-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgrovers.py
More file actions
205 lines (166 loc) · 6.34 KB
/
Copy pathgrovers.py
File metadata and controls
205 lines (166 loc) · 6.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import operations as op
import register as re
import quantum_states as qs
import numpy as np
import matplotlib.pyplot as plt
import time
def Oracle(nq, s, Sparse = False):
""" Function that dynamically creates the oracle gate
Using deterministic algorithm for target state s, with number of qubits nq,
function creates a matrix made up from lower-level gates which serves as
the oracle gate when applied to the state vector of the quantum register.
Parameters
----------
nq : int
Number of qubits per register.
s : int
Denary representation of target state.
Returns
-------
numpy array or sp.Sparse
returns an nq-dimensional numpy array which can be applied as an Oracle
gate on a quantum register
"""
#takes binary representation of state and formats it to construct gate
Tr = bin(s)[2:].zfill(nq)
Neg = "" #Stores the code for the first
#and last layers (i.e. for |0> we get all 'XX')
for i in Tr:
if i == '0':
Neg+="X"
else:
Neg+="I"
#Constructs the matrices representing the leftmost and rightmost operations
L = op.constructGate(Neg, Sparse)
#Constructs the nq-dimansional CNOT gate (middle layer)
Z = op.constructGate(f"{nq}Z", Sparse) #a code as '3X', means a controlled X gate acting on the smallest qubit (essentially a Toffoli in this case).
return op.matrixProduct(op.matrixProduct(L, Z), L)
def Hadamard(nq, Sparse = False):
""" Constructs the paralel nq-Hadamard gate (that is to be applied to all qubits)
Parameters
----------
nq : int
Number of qubits in register.
Returns
-------
numpy array or sp.Sparse
returns an nq-dimensional numpy array which can be applied as a Hadamard
gate on a quantum register
"""
H = op.constructGate('H'*nq, Sparse)
return H
def Diffuser(nq, Sparse = False):
""" Returns the Grover's Diffusion operator for # of qubits nq
Parameters
----------
nq : int
Number of qubits in register.
Returns
-------
numpy array or sp.Sparse
returns an nq-dimensional numpy array which can be applied as a Diffuser
gate on a quantum register
"""
L = op.constructGate("X"*nq, Sparse) #Constructs the matrices representing the leftmost and rightmost operations
Z = op.constructGate(f"{nq}Z", Sparse) #Constructs the nq-dimansional CNOT gate (middle layer)
return op.matrixProduct(op.matrixProduct(L, Z), L)
def Grovers(nq, s, cOut, Sparse = False):
""" Actual function running grover's algorithm.
Capable of adapting gates dynamically depending on the target state and number of
qubits selected by user.
Parameters
----------
nq : type
Description of parameter `nq`.
s : type
Denary representation of state.
Returns
-------
register.Register, int
The custom register object starts as a uniform superposition of States
which is then put through Grover's algorithm 'it' times and the heavily
weighted (towards target state) register is returned at the end.
Dt is the time interval that it took to run Grovers 'it' times on
register.
"""
if cOut:
print('\n'+"-------Making gates------:")
print("Making Hadamard")
H = Hadamard(nq, Sparse)
if cOut:
print("Making Oracle")
Orac = Oracle(nq, s, Sparse)
if cOut:
print("Making Diffusion Operator" + '\n')
Diff = Diffuser(nq, Sparse)
#Initialising Register in a uniform superposition
if cOut:
print("-------Initialising Register-------" + '\n')
#pass an n-qbit determinate state 0
R = re.Register(qs.State((0,nq)))
start_time = time.time()
#n-dimensional hadamard creates uniform superposition of states up until state(2**nq)
R.applyGate(H, Sparse)
#Iterating -it times (most accurate order of iteration, ussually simply quoted as root(n))
it = int(np.pi/(4*np.arcsin(1/np.sqrt(2**nq))))
if cOut:
print('\n'+ f"Running Grover's, {it} times:")
for i in range(it):
R.applyGate(Orac, Sparse)
R.applyGate(H, Sparse)
R.applyGate(Diff, Sparse)
R.applyGate(H, Sparse)
Dt = time.time() - start_time
return R, Dt
def FrequencyPlot(freq, States):
"""Plots a graph of each basis state and how many times it was observed.
Times selected is analogous to probability of being the 'correct' target state.
Parameters
----------
freq : list of int
Number of occurances for each state.
States : list of strings
List containing each possible state, in dirac notation.
"""
xaxis = list(range(0,len(States)))
plt.bar(xaxis,freq, tick_label=States)
plt.ylabel("Frequency of Occurance")
plt.xlabel("Basis States")
plt.xticks(rotation=90)
plt.title(f"Frequency of Occurance for Each Basis State")
for i in range (0, len(States)):
plt.annotate(freq[i], xy=(i, freq[i]), ha='center', va='bottom')
plt.savefig('Plot of Frequencies')
plt.show()
def Observe_System(R, k, nq):
""" Observe the register R, k times.
This simulates the "Uncertainty" in the outcome of the observation.
As the state of the system before observing it, is definite, we don't need
to run Grover's each time. Just simulate the final measurement using
the register.measure() method, that implements a Monte-Carlo approach to choose
which state the system is going to "collapse" to when measuring it.
Calls for plot of measurements at the end.
Parameters
----------
R : register.Register
Custom register object.
k : int
Number of times Grover's will be "ran". Grover's doesn't actually have to be ran
k times, as the final amplitudes are definite, for a set target state
nq : int
Number of quibits used to represent each state.
"""
Obs = []
States = [f"|{bin(i)[2:].zfill(nq)}>" for i in range(2**nq)]
freq = []
for i in range(k):
Obs.append(R.measure())
for s in States:
freq.append(Obs.count(s))
freq = np.array(freq)/k
print('\n' + f"# of Occurences of each state after measuring the system {k} times:")
for i in range(len(freq)):
print(f"{States[i]}: {freq[i]}")
if nq <= 5:
FrequencyPlot(freq, States)
return (max(freq))