-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathPresentation.py
325 lines (257 loc) · 10.5 KB
/
Presentation.py
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
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
# -*- coding: utf-8 -*-
"""
This module presents all the necessary computations for the Grover's Algorithm and Quantum Fourier Transform.
"""
import QuantumCircuit
import numpy as np
import matplotlib.pyplot as plt
import QuantumRegister
import Sparse
def diffuser(circuit):
"""
Creates the diffuser for a given circuit for a Grover's algorithm.
:param circuit: (QuantumCircuit) The quantum circuit that Grover's algorithm should be applied to.
"""
n_qubits = len(circuit.register.Qbits)
# Apply h gates to all qubits
for qbit in range(n_qubits):
circuit.addGate('h', [qbit])
# Apply x gates to all qubits
for qbit in range(n_qubits):
circuit.addGate('x', [qbit])
circuit.ncz([i for i in range(n_qubits)])
# Apply x gates to all qubits
for qbit in range(n_qubits):
circuit.addGate('x', [qbit])
# Apply h gates to all qubits
for qbit in range(n_qubits):
circuit.addGate('h', [qbit])
def Grover_Circuit(n_qubits, measured_bits, plot_results=True):
"""
Constructs a circuit representing Grover's algorithm for a given number of qubits and
bits that we are interested in. Plots measurements after each iteration of the algorithm.
:param n_qubits: (int) Number of qubits in the circuit.
:param measured_bits: (list) list of bits that we are interested in and want to increase the amplitude of.
"""
grover_circuit = QuantumCircuit.QuantumCircuit('Grover', n_qubits)
grover_circuit.addGate('h', [i for i in range(n_qubits)])
repetitions = int(np.pi/4*np.sqrt(2**n_qubits)) - 1
grover_circuit.addmeasure()
# calculate oracle
elements = []
for i in range(2**n_qubits):
if i in measured_bits:
elements.append(Sparse.MatrixElement(i,i,-1))
else: elements.append(Sparse.MatrixElement(i,i,1))
oracle_gate = Sparse.SparseMatrix(2**n_qubits, elements) # Creates a sparseMatrix representation of the oracle
#print(oracle_gate.makedense())
#Add Oracle
grover_circuit.addCustom(0, n_qubits-1, oracle_gate, 'oracle')
#grover_circuit.addmeasure()
#Add diffuser
diffuser(grover_circuit)
grover_circuit.addmeasure()
# Repeat if necessary
for i in range(repetitions):
# Add Oracle
grover_circuit.addCustom(0, n_qubits-1, oracle_gate, 'oracle')
#Add diffuser
diffuser(grover_circuit)
grover_circuit.addmeasure()
#print(np.array(grover_circuit.gates, dtype=object)[:,:6])
#show results
#print(grover_circuit.return_measurements())
final_statevec, measurements = grover_circuit.simulate2()
#for m in measurements[1]:
# print(m)
if plot_results:
# plots the results in a snazzy way
figure, axis = plt.subplots(1, len(measurements[1]))
for j, measurement in enumerate(measurements[1]):
axis[j].bar([i for i in range(measurement.size)], measurement*np.conj(measurement))
axis[j].set_ylim([0,1])
axis[j].set_xlabel("State |N>", fontsize = '13')
if j>0:
axis[j].set_yticklabels("")
#print((results[2][1][j]*np.conj(results[2][1][j])).sum())
axis[0].set_ylabel("Probability", fontsize = '13')
#figure.set_ylabel("Probability of Measuring State")
figure.suptitle("Probability of measuring state N",fontweight='bold', fontsize='15')
plt.show()
print(grover_circuit)
def LazyGroverDemo(n_qubits, measured_bits, plot_results=True):
grover_circuit = QuantumCircuit.QuantumCircuit('Grover', n_qubits)
grover_circuit.addGate('h', [i for i in range(n_qubits)])
repetitions = round(np.pi/4*np.sqrt(2**n_qubits)) - 1
grover_circuit.addmeasure()
# calculate oracle
oracle = Sparse.ColMatrix(2**n_qubits)
for i in range(2**n_qubits):
if i in measured_bits:
oracle[i,i] = -1
else: oracle[i,i] = 1
#print('oracle is:')
#print(oracle)
#Add Oracle
grover_circuit.addCustom(0, n_qubits-1, oracle, 'oracle')
#grover_circuit.addmeasure()
#Add diffuser
diffuser(grover_circuit)
grover_circuit.addmeasure()
# Repeat if necessary
""
for i in range(int(repetitions)):
# Add Oracle
grover_circuit.addCustom(0, n_qubits-1, oracle, 'oracle')
#Add diffuser
diffuser(grover_circuit)
grover_circuit.addmeasure()
""
#print(np.array(grover_circuit.gates, dtype=object)[:,:6])
#show results
#print(grover_circuit.return_measurements())
final_statevec, measurements = grover_circuit.lazysim()
#for m in measurements[1]:
# print(m)
if plot_results:
# plots the results in a snazzy way
figure, axis = plt.subplots(1, len(measurements[1]))
for j, measurement in enumerate(measurements[1]):
axis[j].bar([i for i in range(measurement.size)], measurement*np.conj(measurement))
axis[j].set_ylim([0,1])
axis[j].set_xlabel("State |N>", fontsize = '13')
if j>0:
axis[j].set_yticklabels("")
#print((results[2][1][j]*np.conj(results[2][1][j])).sum())
axis[0].set_ylabel("Probability", fontsize = '13')
#figure.set_ylabel("Probability of Measuring State")
figure.suptitle("Probability of measuring state N",fontweight='bold', fontsize='15')
plt.show()
print(grover_circuit)
def QFT(circuit):
"""
Applies quantum fourier transform to a circuit.
:param circuit: (QuantumCircuit) The quantum circuit to apply the QFT to.
:return: (QuantumCircuit) The same quantum circuit with the QFT applied to it.
"""
n = len(circuit.register.Qbits)
def qft_rotations(circuit, n):
"""
Calculates the roatation gates and hadamards that must be added to the circuit.
:param circuit: (QuantumCircuit) The circuit that the gft will be applied to.
:param n: (int) Number of qubits in the circuit.
"""
if n==0: return circuit
n -= 1
circuit.addGate('h', [n])
for qbit in range(n):
circuit.addBigGate(('cp', qbit, n, np.pi/2**(n-qbit)))
qft_rotations(circuit, n)
def swap_registers(circuit, n):
"""
Uses SWAP gate on the circuit.
:param circuit: (QuantumCircuit) The circuit that the gft will be applied to.
:param n: (int) Number of qubits in the circuit.
:return: (QuantumCircuit) The updated Circuit.
"""
for qbit in range(n//2):
circuit.addBigGate(('swap', qbit, n-qbit-1))
return circuit
def qft(circuit, n):
"""
Performs QFT.
:param circuit: (QuantumCircuit) The circuit that the gft will be applied to.
:param n: (int) Number of qubits in the circuit.
:return: (QuantumCircuit) The updated Circuit.
"""
qft_rotations(circuit, n)
swap_registers(circuit, n)
return circuit
qft(circuit, n)
def qft_dagger(circuit):
"""
Applies an inverse quantum fourier transform to a given circuit.
:param circuit: (QuantumCircuit) A quantum circuit that the inverse qft should be applied to.
:return circuit: (QuantumCircuit) The same circuit, but with an inverse qft applied to it.
"""
n = len(circuit.register.Qbits)
#swap qbits
for qbit in range(n//2):
circuit.swap(qbit, n-qbit-1)
for j in range(n):
for m in range(j):
circuit.cp(m, j, -np.pi/float(2**(j-m)))
circuit.addGate('h', [j])
return circuit
def Ber_Vaz(s):
"""
Creates an example of the Bernstein-Vazirani algorithm.
:param s: (str) String representation of the state that the algorithm should work for.
"""
n=len(s)
bv_circ = QuantumCircuit.QuantumCircuit('Bernstsin-Vazirani Algorithm example', n+1)
# Put ancilla in state |->
bv_circ.addGate('h', [n])
bv_circ.addGate('z', [n])
# Apply hadamard gates before querying the oracle
bv_circ.addGate('h', [i for i in range(n)])
#Apply the inner product oracle
s = s[::-1] # reverse s to match qubit ordering
for q in range(n):
if s[q] == '1': bv_circ.addBigGate(('cn', q, n))
# Apply Hadamard after the oracle
bv_circ.addGate('h', [i for i in range(n)])
bv_circ.show_results()
#bv_circ.register.measure()
short_register = QuantumRegister.QuantumRegister(n)
short_register.setStateVec(bv_circ.register.Statevec.Elements[:2**n])
short_register.measure()
print('Qubits of the Bernstein-Vazirani circuit:')
for i, qbit in enumerate(bv_circ.register.Qbits):
print(f'Qubit {i}')
print(qbit)
print('Shortened register represented by:')
print(short_register)
print('Qubits of the shortened registered')
for i, qbit in enumerate(short_register.Qbits):
print(f'Qubit {i}')
print(qbit)
def qft_example():
"""
Create an example demonstrating quantum fourier transform.
"""
circuit = QuantumCircuit.QuantumCircuit('QFT example 1', 3) # Create circuit
circuit.addGate('x', [2,1]) # Set the state vector to a specific superposition
circuit.addGate('h', [0,2])
circuit.addBigGate(('cn', 0, 2))
circuit.addmeasure()
QFT(circuit)
circuit.addmeasure()
qft_dagger(circuit)
circuit.addmeasure()
results = circuit.run_circuit(return_full=True)
print(np.array(circuit.gates, dtype=object)[:, :12])
for i, measurement in enumerate(results[2][1]):
print(f'Measurement {i}')
print(measurement)
figure, axis = plt.subplots(1, len(results[2][1]))
for j in range(len(results[2][1])):
axis[j].bar([i for i in range(results[2][1][j].size)], results[2][1][j])
axis[j].set_ylim([-1,1])
print((results[2][1][j]*np.conj(results[2][1][j])).sum())
circuit = QuantumCircuit.QuantumCircuit('QFT example 2', 4)
circuit.addGate('x', [2,1])
circuit.addmeasure()
QFT(circuit)
circuit.addmeasure()
qft_dagger(circuit)
circuit.addmeasure()
results = circuit.run_circuit(return_full=True)
for i, measurement in enumerate(results[2][1]):
print(f'Measurement {i}')
print(measurement)
figure, axis = plt.subplots(1, len(results[2][1]))
for j in range(len(results[2][1])):
axis[j].bar([i for i in range(results[2][1][j].size)], results[2][1][j])
axis[j].set_ylim([-1,1])
print((results[2][1][j]*np.conj(results[2][1][j])).sum())