-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Added Simon's Algorithm Example #1908
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,339 @@ | ||||||
''' | ||||||
--------------------------------- | ||||||
SIMONS'S ALGORITHM - OVERVIEW | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
--------------------------------- | ||||||
Simon's Algorithm solves the problem of finding a particular value of s when some function f:{0, 1}^n --> {0, 1}^n | ||||||
is inputted into the program that follows this rule: "f(x) = f(y) if and only if x (+) y is in the set {0^n, s}" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
--------------------------------- | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Delete this |
||||||
STEPS OF THE ALGORITHM | ||||||
--------------------------------- | ||||||
1. Begin with two n-qubit registers, each in the |0> state | ||||||
2. Apply a Hadamard transform to the first n-qubit register, therefore creating an even superposition of states | ||||||
3. The oracle (which encodes values of the function) is queried B_f |x>|y> = |x>|y (+) f(x)>, therefore mapping |0^n> --> |f(x)> | ||||||
4. Apply a Hadamard transform to the first n-qubit register | ||||||
--------------------------------- | ||||||
MEASUREMENT | ||||||
--------------------------------- | ||||||
It can be found that for an output string y, then y (dot mod 2) s is always equal to 0, as we can calculate y (dot mod 2) s = 1 occuring with probability = 0 | ||||||
We get a system of eqautions, which we can use to solve for s (provided y_1, ..., y_(n-1) are linearlly independent)! We measure the string y to be the first n-qubit register | ||||||
''' | ||||||
|
||||||
import cirq | ||||||
import random | ||||||
import numpy as np | ||||||
import copy | ||||||
import sympy | ||||||
import itertools | ||||||
|
||||||
# Qubit preparation | ||||||
|
||||||
number_qubits = #Number of qubits | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's this variable set to? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Redundant comment. Don't repeat what the code says word for word. |
||||||
|
||||||
def main(number_qubits): | ||||||
|
||||||
circuit_sampling = number_qubits-1 | ||||||
|
||||||
#Create the qubits which are used within the circuit | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
Always space between the |
||||||
|
||||||
first_qubits = [cirq.GridQubit(i, 0) for i in range(number_qubits)] | ||||||
second_qubits = [cirq.GridQubit(i, 0) for i in range(number_qubits, 2*number_qubits)] | ||||||
|
||||||
the_activator = cirq.GridQubit(2*number_qubits, 0) | ||||||
|
||||||
#Create the qubits that can be used for large-input Toffoli gates | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
ancilla = [] | ||||||
for v in range(2*number_qubits+1, 3*number_qubits): | ||||||
ancilla.append(cirq.GridQubit(v, 0)) | ||||||
|
||||||
#Create the function that is inputted into the algorithm (secret!) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
domain = [] | ||||||
selector = [] | ||||||
co_domain = [] | ||||||
fixed = [] | ||||||
|
||||||
for k in range(0, 2**number_qubits): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
domain.append(k) | ||||||
selector.append(k) | ||||||
co_domain.append(False) | ||||||
fixed.append(k) | ||||||
|
||||||
#Create the "secret string" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
s = domain[random.randint(0, len(domain)-1)] | ||||||
|
||||||
#Create the "secret function" | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
for g in range(0, int((2**number_qubits)/2)): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Extract this entire section about making the secret function into a method called |
||||||
v = random.choice(selector) | ||||||
x = random.choice(domain) | ||||||
co_domain[x] = v | ||||||
co_domain[x^s] = v | ||||||
del selector[selector.index(v)] | ||||||
del domain[domain.index(x)] | ||||||
if (s != 0): | ||||||
del domain[domain.index(x^s)] | ||||||
|
||||||
secret_function = [fixed, co_domain] | ||||||
|
||||||
oracle = make_oracle(ancilla, secret_function, first_qubits, second_qubits, s, the_activator) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use better names than Rename Drop the |
||||||
|
||||||
c = make_simon_circuit(first_qubits, second_qubits, oracle) | ||||||
|
||||||
#Sampling the circuit | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
simulator = cirq.Simulator() | ||||||
result = simulator.run(c, repetitions=number_qubits-1) | ||||||
final = result.histogram(key='y') | ||||||
print("Secret String: "+str(s)) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or f strings |
||||||
print("Secret Function (Domain and Co-Domain): "+str(secret_function)) | ||||||
final = str(result)[str(result).index("y")+2:len(str(result))].split(", ") | ||||||
last = [] | ||||||
for i in range(0, number_qubits-1): | ||||||
holder = [] | ||||||
for j in final: | ||||||
holder.append(int(j[i])) | ||||||
holder.append(0) | ||||||
last.append(holder) | ||||||
|
||||||
print("Results: "+str(last)) | ||||||
return [last, secret_function, s, last] | ||||||
|
||||||
|
||||||
|
||||||
|
||||||
def make_oracle(ancilla, secret_function, first_qubits, second_qubits, s, the_activator): | ||||||
|
||||||
#Hard-code oracle on a case-by-case basis | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
|
||||||
for o in range(0, len(secret_function[0])): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Avoid |
||||||
counter = 0 | ||||||
for j in list(str(format(secret_function[0][o], "0"+str(number_qubits)+"b"))): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This strikes me as "stringly typed programming", where you are unnecessarily encoding the logic into string operations. |
||||||
if (int(j) == 0): | ||||||
yield cirq.X.on(first_qubits[counter]) | ||||||
counter = counter+1 | ||||||
yield apply_n_qubit_tof(ancilla, first_qubits+[the_activator]) | ||||||
counter = 0 | ||||||
for j in list(str(format(secret_function[0][o], "0"+str(number_qubits)+"b"))): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method has a lot of repeated code. Extract the character counting loop into a method. Only initialize the strings you're making for 0 and for 1 a single time. Use |
||||||
if (int(j) == 0): | ||||||
yield cirq.X.on(first_qubits[counter]) | ||||||
counter = counter+1 | ||||||
|
||||||
counter = 0 | ||||||
for j in list(str(format(secret_function[1][o], "0"+str(number_qubits)+"b"))): | ||||||
if (int(j) == 1): | ||||||
yield cirq.CNOT.on(the_activator, second_qubits[counter]) | ||||||
counter = counter+1 | ||||||
|
||||||
counter = 0 | ||||||
for j in list(str(format(secret_function[0][o], "0"+str(number_qubits)+"b"))): | ||||||
if (int(j) == 0): | ||||||
yield cirq.X.on(first_qubits[counter]) | ||||||
counter = counter+1 | ||||||
yield apply_n_qubit_tof(ancilla, first_qubits+[the_activator]) | ||||||
counter = 0 | ||||||
for j in list(str(format(secret_function[0][o], "0"+str(number_qubits)+"b"))): | ||||||
if (int(j) == 0): | ||||||
yield cirq.X.on(first_qubits[counter]) | ||||||
counter = counter+1 | ||||||
|
||||||
def apply_n_qubit_tof(ancilla, args): | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would be fine with using |
||||||
if (len(args) == 3): | ||||||
yield cirq.CCX.on(args[0], args[1], args[2]) | ||||||
|
||||||
else: | ||||||
|
||||||
yield cirq.CCX.on(args[0], args[1], ancilla[0]) | ||||||
for k in range(2, len(args)-1): | ||||||
yield cirq.CCX(args[k], ancilla[k-2], ancilla[k-1]) | ||||||
|
||||||
yield cirq.CNOT.on(ancilla[len(args)-3], args[len(args)-1]) | ||||||
|
||||||
for k in range(len(args)-2, 1, -1): | ||||||
yield cirq.CCX(args[k], ancilla[k-2], ancilla[k-1]) | ||||||
yield cirq.CCX.on(args[0], args[1], ancilla[0]) | ||||||
|
||||||
|
||||||
def make_simon_circuit(first_qubits, second_qubits, oracle): | ||||||
|
||||||
circuit = cirq.Circuit() | ||||||
|
||||||
#Apply the first set of Hadamard gates | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
for i in range(0, number_qubits): | ||||||
circuit.append(cirq.H.on(first_qubits[i])) | ||||||
|
||||||
#Apply the oracle | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
circuit.append(oracle) | ||||||
|
||||||
#Apply the second set of Hadamard gates | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
for i in range(0, number_qubits): | ||||||
circuit.append(cirq.H.on(first_qubits[i])) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
|
||||||
#Perform measurements upon the qubits | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
circuit.append(cirq.measure(*second_qubits, key='x')) | ||||||
circuit.append(cirq.measure(*first_qubits, key='y')) | ||||||
|
||||||
return circuit | ||||||
|
||||||
run = main(number_qubits) | ||||||
matrix_input = run[0] | ||||||
secret_function = run[1] | ||||||
string_secret = run[2] | ||||||
r = run[3] | ||||||
|
||||||
def shuffle_op(matrix, point): | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add comments in what this does. |
||||||
|
||||||
for i in range(0, len(matrix)): | ||||||
if (matrix[i] == [0 for l in range(0, len(matrix[0]))]): | ||||||
raise ValueError("System of equations not linearly independent, try again") | ||||||
for j in range(0, len(matrix)): | ||||||
if (matrix[i] == matrix[j] and i != j): | ||||||
raise ValueError("System of equations not linearly independent, try again") | ||||||
|
||||||
|
||||||
for i in range(1, len(matrix)+1): | ||||||
for c in list(itertools.combinations([y[0:len(matrix)+1] for y in matrix], i)): | ||||||
hol = [] | ||||||
for b in c: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
hol.append(b) | ||||||
calc = [sum(x)%2 for x in zip(*hol)] | ||||||
ha = True | ||||||
for ij in range (0, len(hol)): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ha = any(hol[ik] == hol[ij] and ik != ij
for ij in range(len(hol))
for ik in range(len(hol)) |
||||||
for ik in range (0, len(hol)): | ||||||
if (hol[ik] == hol[ij] and ik != ij): | ||||||
ha = False | ||||||
if (ha == True and calc == [0 for p in range(0, len(calc))]): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
raise ValueError("System of equations not linearly independent, try again") | ||||||
|
||||||
flip = False | ||||||
passage = False | ||||||
|
||||||
for i in range(0, len(matrix)): | ||||||
for j in range(i+1, len(matrix)): | ||||||
if (matrix[i][i] != 0): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Move this condition into the outer loop |
||||||
x = -1*matrix[j][i]/matrix[i][i] | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this block is just |
||||||
iterator = map(lambda y: y*int(x), matrix[i]) | ||||||
new = [sum(z)%2 for z in zip(matrix[j], iterator)] | ||||||
matrix[j] = new | ||||||
|
||||||
|
||||||
for a in range(0, len(matrix)+1): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The 0 in range(0, n) is redundant. Just |
||||||
|
||||||
fml = [] | ||||||
flip = a | ||||||
|
||||||
work = copy.deepcopy(matrix) | ||||||
|
||||||
h = [0 for i in range(0, len(matrix[0])-2)]+[point] | ||||||
h.insert(flip, 1) | ||||||
work.append(h) | ||||||
|
||||||
for j in range(0, len(work[0])-1): | ||||||
temporary = [] | ||||||
for g in range(0, len(work)): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
if (work[g][j] == 1): | ||||||
temporary.append(work[g]) | ||||||
fml.append(temporary) | ||||||
|
||||||
cv = False | ||||||
|
||||||
if ([] not in fml): | ||||||
for element in itertools.product(*fml): | ||||||
|
||||||
if (sorted(work) == sorted(element)): | ||||||
|
||||||
cv = True | ||||||
|
||||||
last_work = copy.deepcopy(list(element)) | ||||||
|
||||||
#Check for linear independence | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
for i in range(0, len(last_work)): | ||||||
if (last_work[i] == [0 for l in range(0, len(last_work[0]))]): | ||||||
cv = False | ||||||
for j in range(0, len(last_work)): | ||||||
if (last_work[i] == last_work[j] and i != j): | ||||||
cv = False | ||||||
|
||||||
|
||||||
for i in range(1, len(last_work)+1): | ||||||
for c in list(itertools.combinations([y[0:len(last_work)] for y in last_work], i)): | ||||||
hol = [] | ||||||
for b in c: | ||||||
hol.append(b) | ||||||
calc = [sum(x)%2 for x in zip(*hol)] | ||||||
ha = True | ||||||
for ij in range (0, len(hol)): | ||||||
for ik in range (0, len(hol)): | ||||||
if (hol[ik] == hol[ij] and ik != ij): | ||||||
ha = False | ||||||
if (ha == True and calc == [0 for p in range(0, len(calc))]): | ||||||
cv = False | ||||||
|
||||||
#Check if the matrix can be reduced | ||||||
|
||||||
for i in range(0, len(last_work)): | ||||||
for j in range(i+1, len(last_work)): | ||||||
if (last_work[i][i] == 0): | ||||||
cv = False | ||||||
else: | ||||||
x = -1*last_work[j][i]/last_work[i][i] | ||||||
iterator = map(lambda y: y*int(x), last_work[i]) | ||||||
new = [sum(z)%2 for z in zip(last_work[j], iterator)] | ||||||
last_work[j] = new | ||||||
|
||||||
if (cv == True): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When you find yourself breaking conditioned on a variable like this is doing, see if you can extract the inner loop into a method. |
||||||
break; | ||||||
|
||||||
|
||||||
if (cv == True): | ||||||
break; | ||||||
|
||||||
matrix = last_work | ||||||
|
||||||
return last_work | ||||||
|
||||||
def construct_solve(matrix_out): | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a comment explaining what this procedure does. |
||||||
final_matrix = matrix_out | ||||||
|
||||||
solution = [] | ||||||
|
||||||
for i in range(len(final_matrix)-1, 0, -1): | ||||||
solution.append(final_matrix[i][len(final_matrix[i])-1]) | ||||||
for j in range(0, i): | ||||||
if (final_matrix[j][i] == 1): | ||||||
final_matrix[j][len(final_matrix[i])-1] = (final_matrix[j][len(final_matrix[i])-1]-final_matrix[i][len(final_matrix[i])-1])%2 | ||||||
solution.append(final_matrix[0][len(final_matrix[i])-1]) | ||||||
|
||||||
solution.reverse() | ||||||
|
||||||
return solution | ||||||
|
||||||
|
||||||
other_matrix_input = copy.deepcopy(matrix_input) | ||||||
|
||||||
first_shot = shuffle_op(matrix_input, 0) | ||||||
try_1 = construct_solve(first_shot) | ||||||
second_shot = shuffle_op(other_matrix_input, 1) | ||||||
try_2 = construct_solve(second_shot) | ||||||
|
||||||
processing1 = ''.join(str(x) for x in try_1) | ||||||
processing2 = ''.join(str(x) for x in try_2) | ||||||
|
||||||
the_last = 0 | ||||||
if (secret_function[1][secret_function[0].index(0)] == secret_function[1][secret_function[0].index(int(processing1, 2))] and secret_function[1][secret_function[0].index(0)] == secret_function[1][secret_function[0].index(int(processing2, 2))]): | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This statement is too long. Create an appropriately named and commented procedure that does this. |
||||||
final = int(processing1, 2) | ||||||
if (int(processing1, 2) == 0): | ||||||
final = int(processing2, 2) | ||||||
print("The secret string is: "+str(final)) | ||||||
the_last = final | ||||||
|
||||||
else: | ||||||
print("The secret string is 0") | ||||||
the_last = 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.