Is there an algorithm for multi-level logic minimization? - algorithm

I need an algorithm that, when given any number of boolean expressions, with any number of variables, can do multi-level logic minimization to give a set of boolean functions.
Wikipedia briefly mentions multi-level representations and gives an example, but doesn't explain how to do it, and I can't find it anywhere else either.
Edit: to clarify, it needs to work on systems with multiple outputs, merging parts of the outputs' boolean expressions to minimize the number of required logic gates.
Wikipedia gives the following example:
F1 = AB + AC + AD
F2 = A`B + A`C + A`E
A functionally equivalent multilevel representation can be:
P = B + C
F1 = AP + AD
F2 = A`P + A`E
This reduces the number of logic gates required by reusing B + C.
I'm looking for an algorithm to do this with any number of inputs and outputs and produce the a functionally equivalent multilevel representation with the minimum possible number of logic gates. Apologies if any of my terminology was/is off.

I believe what you want is Quine-McCluskey algorithm, which has an exponential complexity. The idea is to generate a truth table and combine minterms. The linked wikipedia provides a clear explanation on how the algorithm works.

Related

One question about circuit realization of Shor's algorithm in quantum computing

I am studying the quantum circuit realization of Shor's algorithm about factoring 15 into product of prime numbers using the python package Qiskit. See this website for details.
My question is related to the realization of U-gate in this website. In this website, the realization of U-gate is given in the form
def c_amod15(a, power):
"""Controlled multiplication by a mod 15"""
if a not in [2,7,8,11,13]:
raise ValueError("'a' must be 2,7,8,11 or 13")
U = QuantumCircuit(4)
for iteration in range(power):
if a in [2,13]:
U.swap(0,1)
U.swap(1,2)
U.swap(2,3)
if a in [7,8]:
U.swap(2,3)
U.swap(1,2)
U.swap(0,1)
if a == 11:
U.swap(1,3)
U.swap(0,2)
if a in [7,11,13]:
for q in range(4):
U.x(q)
U = U.to_gate()
U.name = "%i^%i mod 15" % (a, power)
c_U = U.control()
return c_U
My question is that why this U-gate is engineered in such way by swapping qbits. How exactly the value of 'a' will affect the swapping scheme? What if I want to factor 33? How should I change this swapping scheme to factor 33?
The value of a is part of the phase estimation part of Shor's algorithm, where the operation
|y> -> |ay mod N>
is applied. So a influences the arithmetic operation and depending on how you implement the modular multiplication influences the final circuit differently.
The Qiskit textbook implementation seems to only support special values of a, but the software package itself has the general code for all values of a: https://github.com/Qiskit/qiskit-terra/blob/main/qiskit/algorithms/factorizers/shor.py
That code uses the Fourier transform to do the multiplication, so a will influence the phase shifts applied after the Fourier transform. Qiskit's implementation is based on this paper where you can find more information.
To try and answer your question in the comments:
I guess my question is why swapping register bits can give us a gate realizing order finding algorithms.
One way to think of Shor's algorithm is it takes as input:
A circuit* U, and
a starting state |ψ⟩
Shor's algorithm tells us the period of that circuit, i.e. the number of times you need to repeat U to get back to your initial input. We then use a classical algorithm to map factoring to this problem by setting U|y⟩≡|ay mod N⟩ and |ψ⟩=|1⟩.
You can confirm through simulations that the circuit in the Qiskit Textbook has that property, although it doesn't give a method of generating that circuit (I imagine it was educated guessing similar to this answer but you'll need to read that paper in the other answer for a general method).
If we already know the answer using the algorithm, then we could just find any old circuit with the correct period and plug that in. E.g. a single swap gate acting on |1⟩ has period 2. Although this doesn't really count as "doing Shor's algorithm", it's often used to demonstrate the algorithm works 1 2.
*To make the algorithm efficient, the input is really "an efficient way to make circuits for U^(2^x)". Fortunately, we know how to do this for the circuits needed for factoring, but the Qiskit textbook just repeats U inefficiently for sake of demonstration.

How to refer to "equivalent" algorithms

This is a bit of a "soft question", so if this is not the appropriate place to post, please let me know.
Essentially I'm wondering how to talk about algorithms which are "equivalent" in some sense but "different" in others.
Here is a toy example. Suppose we are given a list of numbers list of length n. Two simple ways to add up the numbers in the list are given below. Obviously these methods are exactly the same in exact arithmetic, but in floating point arithmetic might give different results.
add_list_1(list,n):
sum = 0
for i=1,2,...,n:
sum += list[i]
return sum
add_list_2(list,n):
sum = 0
for i=n,...,2,1:
sum += list[i]
return sum
This is a very common thing to happen with numerical algorithms, with Gram-Schmidt vs Modified Gram Schmidt being perhaps the most well known example.
The wikipedia page for algorithms mentions "high level description", "implementation description", and "formal description".
Obviously, the implementation and formal descriptions vary, but a high level description such as "add up the list" is the same for both.
Are these different algorithms, different implementations of the same algorithm, or something else entirely? How would you describe algorithms where the high level level description is the same but the implementation is different when talking about them?
The following definition can be found on the Info for the algorithm tag.
An algorithm is a set of ordered instructions based on a formal language with the following conditions:
Finite. The number of instructions must be finite.
Executable. All instructions must be executable in some language-dependent way, in a finite amount of time.
Considering especially
set of ordered instructions based on a formal language
What this tells us is that the order of the instructions matter. While the outcome of two different algorithms might be the same, it does not imply that the algorithms are the same.
Your example of Gram-Schmidt vs. Modified Gram-Schmidt is an interesting one. Looking at the structure of each algorithm as defined here, these are indeed different algorithms, even on a high level description. The steps are in different orders.
One important distinction you need to make is between a set of instructions and the output set. Here you can find a description of three shortest path algorithms. The set of possible results based on input is the same but they are three very distinct algorithms. And they also have three completely different high level descriptions. To someone who does not care about that though these "do the same" (almost hurts me to write this) and are equivalent.
Another important distinction is the similarity of steps between to algorithms. Let's take your example and write it in a bit more formal notation:
procedure 1 (list, n):
let sum = 0
for i = 1 : n
sum = sum + list[i]
end for
sum //using implicit return
procedure 2 (list, n):
let sum = 0
for i = n : 1
sum = sum + list[i]
end for
sum //using implicit return
These two pieces of code have the same set of results but the instructions seem differently ordered. Still this is not true on a high level. It depends on how you formalise the procedures. Loops are one of those things that if we reduce them to indices they change our procedure. In this particular case though (as already pointed out in the comments), we can essentially substitute the loop for a more formalised for each loop.
procedure 3 (list):
let sum = 0
for each element in list
sum = sum + element
end for
sum
procedure 3 now does the same things as procedure 1 and procedure 2, their result is the same but the instructions again seem different. So the procedures are equivalent algorithms but not the same on the implementation level. They are not the same since the order in which the instructions for summing are executed is different for procedure 1 and procedure 2 and completely ignored in procedure 3 (it depends on your implementation of for each!).
This is where the concepts of a high level description comes in. It is the same for all three algorithms as you already pointed out. The following is from the Wikipedia article you are referring to.
1 High-level description
"...prose to describe an algorithm, ignoring the implementation details. At this level, we do not need to mention how the machine manages its tape or head."
2 Implementation description
"...prose used to define the way the Turing machine uses its head and the way that it stores data on its tape. At this level, we do not give details of states or transition function."
3 Formal description
Most detailed, "lowest level", gives the Turing machine's "state table".
Keeping this in mind your question really depends on the context it is posed in. All three procedures on a high level are the same:
1. Let sum = 0
2. For every element in list add the element to sum
3. Return sum
We do not care how we go through the list or how we sum, just that we do.
On the implementation level we already see a divergence. The procedures move differently over the "tape" but store the information in the same way. While procedure 1 moves "right" on the tape from a starting position, procedure 2 moves "left" on the tape from the "end" (careful with this because there is no such thing in a TM, it has to be defined with a different state, which we do not use in this level).
procedure 3, well it is not defined well enough to make that distinction.
On the low level we need to be very precise. I am not going down to the level of a TM state table thus please accept this rather informal procedure description.
procedure 1:
1. Move right until you hit an unmarked integer or the "end"
//In an actual TM this would not work, just for simplification I am using ints
1.e. If you hit the end terminate //(i = n)
2. Record value //(sum += list[i]) (of course this is a lot longer in an actual TM)
3. Go back until you find the first marked number
4. Go to 1.
procedure 2 would be the reverse on instructions 1. and 3., thus they are not the same.
But on these different levels are these procedures equivalent? According to Merriam Webster, I'd say they are on all levels. Their "value" or better their "output" is the same for the same input**. The issue with the communication is that these algorithms, like you already stated in your question return the same making them equivalent but not the same.
You referring to **floating point inaccuracy implies implementation level, on which the two algorithms are already different. As a mathematical model we do not have to worry about floating point inaccuracy because there is no such thing in mathematics (mathematicians live in a "perfect" world).
These algorithms are the different implementation level descriptions of the same high level description. Thus, I would refer to different implementations of the same high level algorithm since the idea is the same.
The last important distinction is the further formalisation of an algorithm by assigning it to a set for its complexity (as pointed out perfectly in the comments by #jdehesa). If you just use big omicron, well... your sets are going to be huge and make more algorithms "equivalent". This is because both merge sort and bubble sort are both members of the set O(n^2) for their time complexity (very unprecise but n^2 is an upper bound for both). Obviously bubble sort is not in O(n*log[2](n)) but this description does not specify that. If we use big theta then bubble and merge sort are not in the same set anymore, context matters. There is more to describing an algorithm than just its steps and that is one more way you can keep in mind to distinguish algorithms.
To sum up: it depends on context, especially who you are talking to. If you are comparing algorithms, make sure that you specify the level you are doing it on. To an amateur saying "add up the list" will be good enough, for your docs use a high level description, when explaining your code explain your implementation of the above high level, and when you really need to formalise your idea before putting it in code use a formal description. Latter will also allow you to prove that your program executes correctly. Of course, nowadays you do not have to write all the states of the underlying TM anymore. When you describe your algorithms, do it in the appropriate form for the setting. And if you have two different implementations of the same high level algorithm just point out the differences on the implementation level (direction of traversal, implementation of summing, format of return values etc.).
I guess, you could call it an ambiguous algorithm. Although this term may not be well defined in literature, consider your example on adding the list of elements.
It could be defined as
1. Initialize sum to zero
2. Add elements in the list to sum one by one.
3. return the sum
The second part is ambiguous, you can add them in any order as its not defined in the algorithm statement and the sum may change in floating point arithematic
One good example I came across: cornell lecture slide. That messy sandwich example is golden.
You could read what the term Ambiguity gererally refers to here wiki, Its applied in various contexts including computer science algorithms.
You may be referring to algorithms that, at least at the surface, perform the same underlying task, but have different levels of numerical stability ("robustness"). Two examples of this may be—
calculating mean and variance (where the so-called "Welford algorithm" is more numerically stable than the naive approach), and
solving a quadratic equation (with many formulas with different "robustness" to choose from).
"Equivalent" algorithms may also include algorithms that are not deterministic, or not consistent between computer systems, or both; for example, due to differences in implementation of floating-point numbers and/or floating-point math, or in the order in which parallel operations finish. This is especially problematic for applications that care about repeatable "random" number generation.

Summations - Closed Form - Where to Start

I am struggling to understand basics as it related to forming a closed form expression from a summation. I understand the goal at hand, but do not understand the process for which to follow in order to accomplish the goal.
Find a closed form for the sum k+2k+3k+...+K^2. Prove your claim
My first approach was to turn it into a recurrence relation, which did not work cleanly. After that I would attempt to turn from a recurrence relation into a closed form, but I am unsuccessful in getting there.
Does anyone know of a strong approach for solving such problems? Or any simplistic tutorials that can be provided? The material I find online does not help, and causes further confusion.
Thanks
No one gave the mathematical approach, so I am adding the mathematical approach to this AP problem.
Given series is 1k + 2k + 3k + .... + k.k(OR k^2)
Therefore, it means that there are altogether k terms together in the given series.
Next, as here all the consecutive terms are greater than the previous term by a constant common difference,i.e., k.
So, this is an Arithmetic Progression.
Now, to calculate the general summation, the formula is given by :-
S(n) = n/2{a(1)+a(n)}
where,S(n) is the summation of series upto n terms
n is the number of terms in the series,
a(1) is the first term of the series, and
a(n) is the last(n th) term of the series.
Here,fitting the terms of the given series into the summation formula, we get :-
S(n) = k/2{1k + k.k} = (k/2){k+k^2) = [(k^2)/2 + (k^3)/2]*.
If you are interested in a general algorithm to compute sums like these (and more complicated ones) I can't recommend the book A=B enough.
The authors have been so kind to make the pdf freely available:
http://www.math.upenn.edu/~wilf/AeqB.html
Enjoy!
Asad has explained a mathematical approach in the comments to solving this.
If you are interested in a programming approach that works for more complicated expressions, then you can use Sympy in Python.
For example:
import sympy
x,k = sympy.symbols('x k')
print sympy.sum(x*k,(x,1,k))
prints:
k*(k/2 + k**2/2)

Display the exact form of quadratic equations' roots

I notice that almost all of new calculators are able to display the roots of quadratic equations in exact form. For example:
x^2-16x+14=0
x1=8+5sqrt2
x2=8-5sqrt2
What algorithm could I use to achieve that? I've been searching around but I found no results related to this problem
Assuming your quadratic equation is in the form
y = ax^2+bx+c
you get the two roots by
x_1,x_2 = ( -b +- sqrt(b^2-4ac)) / 2a
when for one you use the + between b and the square root, and for the other the -.
If you want to take something out of the square root, just compute the factors of the argument and take out the ones with exponent greater than 2.
By the way, the two root you posted are wrong.
The “algorithm” is exactly the same as on paper. Depending on the programming language, it may start with int delta = b*b - 4*a*c;.
You may want to define a datatype of terms and simplifications on them, though, in case the coefficients of the equation are not simply integer but themselves solutions of previous equations. If this is the sort of thing you are after, look up “symbolic computation”. Some languages are better for this purpose than others. I expect that elementary versions of what you are asking is actually used as an example in some ML tutorials, for instance (see chapter 9).

Given a integer number, find the smallest function that given it

I have a very large positive integer number (million digits). I need represent it with the smallest possible function, this number is variable, it means, I need an algorithm that generates the smallest possible function to get the given number.
Example: For the number 29512665430652752148753480226197736314359272517043832886063884637676943433478020332709411004889 the algorithm must return "9^99". It must be able to analyze numbers and always return a math function that represent the number. Example the number 21847450052839212624230656502990235142567050104912751880812823948662932355202 must return "9^5^16+1".
Heard of Kolmogorov complexity?
To answer your question: unless you restrict yourself to some specific set of functions, it's impossible.
EDIT: Even in your example, how do you know that the shortest representation of 21​847​450​052​839​212​624​230​656​502​990​235​142​567​050​104​912​751​880​812​823​948​662​932​355​202 is actually 9^5^16+1? Isn't it a quite hard to prove even in this specific case?
If you restrict yourself to some set of functions then you can use the following algorithm:
For i = 1 to n
enumerate all strings s of length i
if s represents a valid expression according to rules chosen a priori,
and evaluates to the number in the input,
return s
It is guaranteed to halt because on the last iteration of the outer loop (i = n) you will get eventually to a string contains the input verbatim.
Of course, this is not very efficient. Specifically O(bn) where n is the length of the input and b is the size of the alphabet.
Expanding on #ybungalobill's terse answer, your function is equivalent to a function that computes the Kolmogorov complexity of an arbitrary string. (The equivalence is obvious if you treat each digit of your very large numbers as characters, and the numbers as sequences of characters.)
According to the Wikipedia page on Kolmogorov complexity, the K(s) function that gives the complexity of a string s is not a computable function. (The page includes a proof.)
In other words, the algorithm you want simply does not exist.
#BlueRaja - Danny Pflughoeft: yes, it is. I'm trying to create some compression that uses this algorithm, but by the way this is impossible.
That's because it's technically impossible to compress arbitrary data, for the same reason, but that doesn't stop us from doing it :)
There are much better ways of compressing data, however. Take a look at, for instance, LZ. It is so ubiquitous that you can almost certainly find a library to do the compression for you, regardless of what language you're writing in. DEFLATE is another popular one.
Hope that helps!
If you're not looking for optimality, just a reasonably good job, then there are a bunch of heuristics you can use. For example, try to decompose n using all of the following
n = a^k + b
for k = 2, 3, ..., log n, and pick the one with the smallest a + b, say. You can compute a and b using a = floor(n^(1/k)) and b = n-a^k. Then recurse on a and b.
Of course, this uses only exponentiation and addition to find a good compression. If you allow subtraction as well, use a=round(n^(1/k)) instead and let b be negative.
Allowing multiplication as well makes it quite a bit harder because you would probably need to factor n.

Resources