Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
339 changes: 339 additions & 0 deletions examples/simon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,339 @@
'''
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
'''
'''Demonstrates Simon's algorithm.

---------------------------------
SIMONS'S ALGORITHM - OVERVIEW
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
SIMONS'S ALGORITHM - OVERVIEW

---------------------------------
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}"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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}"
is inputted into the program that follows this rule: "f(x) = f(y) if and only if x xor y is in the set {0^n, s}"

---------------------------------
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Delete this STEPS section and replace it with a reference to Simon's Paper: https://epubs.siam.org/doi/10.1137/S0097539796298637

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's this variable set to?

Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Create the qubits which are used within the circuit
# Create the qubits which are used within the circuit

Always space between the # and the comment.


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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Create the qubits that can be used for large-input Toffoli gates
# Create the qubits that can be used for large-input Toffoli gates

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!)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Create the function that is inputted into the algorithm (secret!)
# Create the function that is inputted into the algorithm (secret!)


domain = []
selector = []
co_domain = []
fixed = []

for k in range(0, 2**number_qubits):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

domain = list(range(2**number_qubits))
selector = list(domain)
fixed = list(domain)
co_domain = [False] * 2**number_qubits

domain.append(k)
selector.append(k)
co_domain.append(False)
fixed.append(k)

#Create the "secret string"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Create the "secret string"
# Create the "secret string"

s = domain[random.randint(0, len(domain)-1)]

#Create the "secret function"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Create the "secret function"
# Create the "secret function"

for g in range(0, int((2**number_qubits)/2)):
Copy link
Contributor

Choose a reason for hiding this comment

The 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 create_secret_function

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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use better names than first_qubits and second_qubits. Refer to their role in the algorithm.

Rename s to a word.

Drop the the in the_activator.


c = make_simon_circuit(first_qubits, second_qubits, oracle)

#Sampling the circuit
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Sampling the circuit
# Sampling the circuit


simulator = cirq.Simulator()
result = simulator.run(c, repetitions=number_qubits-1)
final = result.histogram(key='y')
print("Secret String: "+str(s))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use format() when printing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or f strings f"like this {variable_name}"

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Hard-code oracle on a case-by-case basis
# Hard-code oracle on a case-by-case basis



for o in range(0, len(secret_function[0])):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid o since it looks like zero.

counter = 0
for j in list(str(format(secret_function[0][o], "0"+str(number_qubits)+"b"))):
Copy link
Contributor

Choose a reason for hiding this comment

The 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"))):
Copy link
Contributor

Choose a reason for hiding this comment

The 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 cirq.X.on_each(first_qubits[:count]) instead of mixing the counting with the gate making.

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):

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would be fine with using X(target).controlled_by(*controls) instead of this method. It will nicely simplify the example.

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Apply the first set of Hadamard gates
# Apply the first set of Hadamard gates


for i in range(0, number_qubits):
circuit.append(cirq.H.on(first_qubits[i]))

#Apply the oracle
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Apply the oracle
# Apply the oracle


circuit.append(oracle)

#Apply the second set of Hadamard gates
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Apply the second set of Hadamard gates
# Apply the second set of Hadamard gates


for i in range(0, number_qubits):
circuit.append(cirq.H.on(first_qubits[i]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

circuit.append(cirq.H.on_each(*first_qubits[:number_qubits]))


#Perform measurements upon the qubits
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Perform measurements upon the qubits
# Perform measurements upon the qubits


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):

Copy link
Collaborator

Choose a reason for hiding this comment

The 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hol = list(c)

hol.append(b)
calc = [sum(x)%2 for x in zip(*hol)]
ha = True
for ij in range (0, len(hol)):
Copy link
Contributor

Choose a reason for hiding this comment

The 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))]):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if ha and calc == [0] * len(calc):

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):
Copy link
Contributor

Choose a reason for hiding this comment

The 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]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this block is just matrix[j] -= matrix[i] * int(matrix[j][i] / matrix[i][i]), assuming matrix is a numpy array.

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):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 0 in range(0, n) is redundant. Just range(n).


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)):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

temporary = [e for e in work if e[j] == 1]

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
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
#Check for linear independence
# Check for linear independence


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):
Copy link
Contributor

Choose a reason for hiding this comment

The 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):

Copy link
Collaborator

Choose a reason for hiding this comment

The 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))]):
Copy link
Collaborator

Choose a reason for hiding this comment

The 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