-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathquantum_error_correction.py
389 lines (291 loc) · 11.2 KB
/
quantum_error_correction.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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Feb 16 09:13:39 2021
@author: Abdellah Tounsi
Task 2: Quantum Error Correction
The bit-flip code and the sign-flip code are two very simple circuits able
to detect and fix the bit-flip and the sign-flip errors, respectively.
### Each question is solved by the functions:
1) error_free_case
2) noisy_case
3) corrected_case
4) compare
"""
import numpy as np
import matplotlib.pyplot as plt
from qiskit import(QuantumCircuit, QuantumRegister,
ClassicalRegister,
execute, Aer)
from qiskit.providers.aer import QasmSimulator
from random import random
N_SHOTS = 1000 # Number of experiements
P = 0.5 # Probability of quantum error
PX = 0.5 # Probability that error is NOT gate
PZ = 1 - PX # Probability that error is Z gate
# Launch simulator
simulator = Aer.get_backend('qasm_simulator')
def error_free_case(n_shots=N_SHOTS,
change_basis=False,
verbose=False):
"""
1) Build the following simple circuit to prepare the Bell state:
\psi> = CNOT (H otimes I) |00>
"""
# prepare register of two qubits \00> and two classical bits
circuit = QuantumCircuit(2, 2)
# Apply (H \otimes I) gate
# circuit.h(0)
# Apply CNOT gate on control qubit 0 and target qubit 1
circuit.cx(0, 1)
# Map the quantum measurement to the classical bits
circuit.barrier([0, 1])
if change_basis:
# to measure the state in basis of NOT eigenvectors
circuit.h(0)
circuit.h(1)
circuit.measure([0,1], [0,1])
# Execute the circuit on the qasm simulator
job = execute(circuit, simulator, shots=n_shots)
# Grab results from the job
result = job.result()
# Returns counts
counts = result.get_counts(circuit)
if verbose:
print("\n 1) Error-Free Quantum Circuit Output:",counts)
# Draw the circuit
circuit.draw(output='mpl', filename='Answer_1.png')
return counts
class NoisyQuantumCircuit(QuantumCircuit):
"""
This noisy circuit is a standard quantum circuit that is a list of
instructions bound to some registers. Those instructions include
random bit-flip and sign-flip instructions.
Additional args:
p: float between 0 and 1
Probability of quantum error
px: float between 0 and 1
Probability of quantum error to be bit-flip error (NOT)
pz: float between 0 and 1
Probability of quantum error to be sign-flip error (Z)
"""
def __init__(self, *regs, name_=None, global_phase_=0,
p=0.2, px=0.5):
self.p = p
self.px = px
self.pz = 1 - px
QuantumCircuit.__init__(self, *regs, name=name_,
global_phase=global_phase_)
def error(self, qubit):
random_number = random()
if random_number <= self.p:
if random() <= self.px:
self.x(qubit)
else:
self.z(qubit)
def noisy_case(n_shots=N_SHOTS,
p_=P, px_=PX,
change_basis=False,
verbose=False):
"""
2) Now add, right before the CNOT gate and for each of the two qubits,
an arbitrary “error gate”.
By error gate we mean that with a certain probability (that you can decide
but must be non-zero for all the choices) you have a 1 qubit unitary which
can be either the identity, or the X gate (bit-flip error) or the Z gate
(sign-flip error).
Inputs:
n_shots: int
Number of repetitions of circuit, for sampling.
p: float between 0 and 1
probability of error
px: float between 0 and 1
probability of error to be bit-flip
"""
# prepare register of two qubits \00> and two classical bits
circuit = NoisyQuantumCircuit(2, 2, p=p_, px=px_)
# Apply (H \otimes I) gate
circuit.h(0)
# Generate random quantum error
circuit.barrier([0, 1])
circuit.error(0)
circuit.error(1)
# Apply CNOT gate on control qubit 0 and target qubit 1
circuit.cx(0, 1)
# Map the quantum measurement to the classical bits
circuit.barrier([0, 1])
if change_basis:
# to measure the state in basis of NOT eigenvectors
circuit.h(0)
circuit.h(1)
circuit.measure([0,1], [0,1])
# Execute the circuit on the qasm simulator
job = execute(circuit, simulator, shots=n_shots)
# Grab results from the job
result = job.result()
# Returns counts
counts = result.get_counts(circuit)
if verbose:
print("\n 1) Noisy Quantum Circuit Output:",counts)
# Draw the circuit
circuit.draw(output='mpl', filename='Answer_2.png')
return counts
def corrected_case(n_shots=N_SHOTS,
p_=P, px_=PX,
change_basis=False,
verbose=False):
"""
3) Encode each of the two qubits with a sign-flip or a bit-flip code,
in such a way that all the possible choices for the error gates described
in 2), occurring on the logical qubits, can be detected and fixed.
Motivate your choice. This is the most non-trivial part of the problem,
so do it with a lot of care!
Inputs:
n_shots: int
Number of repetitions of circuit, for sampling.
p: float between 0 and 1
probability of error
px: float between 0 and 1
probability of error to be bit-flip
"""
## prepare register of two qubits \00> and two classical bits
q = QuantumRegister(9 + 9 + 3)
c = ClassicalRegister(1 + 1)
circuit = QuantumCircuit(q, c)
## Encoding
# qubit 0
circuit.cx(q[0], q[3])
circuit.cx(q[0], q[6])
for i in range(3):
circuit.h(q[0 + i * 3])
circuit.cx(q[3 * i], q[3 * i + 1])
circuit.cx(q[3 * i], q[3 * i + 2])
# qubit 1
circuit.cx(q[9], q[12])
circuit.cx(q[9], q[15])
for i in range(3):
circuit.h(q[9 + i * 3])
circuit.cx(q[9 + 3 * i], q[9 + 3 * i + 1])
circuit.cx(q[9 + 3 * i], q[9 + 3 * i + 2])
## Noisy Quantum Operations
circuit.barrier(q)
# Hadamard gate on qubit 0
for i in range(3):
circuit.cx(q[i * 3], q[18 + i])
for i in range(3):
circuit.cz(q[18 + i], q[3 * i])
for i in range(3):
circuit.z(q[i * 3])
# Generate error
err_circuit = NoisyQuantumCircuit(18, name_='Noisy Gate', p=p_, px=px_)
err_circuit.error(5)#int(random() * 9.))
# transform the noisy gate to instruction
err_gate = err_circuit.to_instruction()
qr = []
for i in range(18):
qr.append(q[i])
circuit.append(err_gate, qr)
## Correct Error
circuit.barrier(q)
# Correct bit flip
# qubit 0
for i in range(3):
circuit.cx(q[3 * i], q[3 * i + 1])
circuit.cx(q[3 * i], q[3 * i + 2])
circuit.ccx(q[3 * i + 2], q[3 * i + 1], q[3 * i])
circuit.h(q[3 * i])
# qubit 1
for i in range(3):
circuit.cx(q[9 + 3 * i], q[9 + 3 * i + 1])
circuit.cx(q[9 + 3 * i], q[9 + 3 * i + 2])
circuit.ccx(q[9 + 3 * i + 2], q[9 +3 * i + 1], q[9 + 3 * i])
circuit.h(q[9 + 3 * i])
# Correct phase flip
# qubit 0
circuit.cx(q[0], q[3])
circuit.cx(q[0], q[6])
circuit.ccx(q[6], q[3], q[0])
# qubit 1
circuit.cx(q[0 + 9], q[3 + 9])
circuit.cx(q[0 + 9], q[6 + 9])
circuit.ccx(q[6 + 9], q[3 + 9], q[0 + 9])
# Apply CNOT (0, 1) gate
circuit.barrier(q)
circuit.cx(q[0], q[9])
## Measurment
circuit.barrier(q)
if change_basis:
# to measure the state in basis of NOT eigenvectors
circuit.h(0)
circuit.h(9)
circuit.measure([q[0], q[9]], c)
# Execute the circuit on the qasm simulator
job = execute(circuit, simulator, shots=n_shots)
# Grab results from the job
result = job.result()
# Return counts
counts = result.get_counts(circuit)
if verbose:
print("\n 1) Corrected Noisy Quantum Circuit Output:",counts)
# Draw the circuit
circuit.draw(output='mpl', filename='Answer3.png')
return counts
def compare(n_trials=10, n_shots_=N_SHOTS,
p=P, px=PX):
"""
4) Test your solution by making many measurements over the final state
and testing that the results are in line with the expectations.
Comparison of error-free circuit, noisy circuit and corrected circuit
is performed in basis of Z and X eigenvectors to verify that overall bit-flips
and phase-flips are well corrected.
Parameters
----------
n_trials : int
Number of experiements to be done.. The default is 10.
n_shots_ : int
Number of repetitions of circuit, for sampling. The default is N_SHOTS.
p : float, between 0 and 1
Probability of error The default is P.
px : float, between 0 and 1
Probability of error to be bit-flip. The default is PX.
Returns
-------
dict
Measurment results in basis of Z and X eigenvectors.
"""
## Verify overall bit-flip correction
error_free_results_z = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = error_free_case(n_shots=n_shots_)
for state in result:
error_free_results_z[state] += result[state]
noisy_case_results_z = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = noisy_case(n_shots=n_shots_, p_=p, px_=px)
for state in result:
noisy_case_results_z[state] += result[state]
corrected_case_results_z = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = corrected_case(n_shots=n_shots_, p_=p, px_=px)
for state in result:
corrected_case_results_z[state] += result[state]
## Verify overall phase-flip correction
error_free_results_x = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = error_free_case(n_shots=n_shots_, change_basis=True)
for state in result:
error_free_results_x[state] += result[state]
noisy_case_results_x = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = noisy_case(n_shots=n_shots_, p_=p, px_=px, change_basis=True)
for state in result:
noisy_case_results_x[state] += result[state]
corrected_case_results_x = {'00': 0, '01': 0, '10': 0, '11': 0}
for n in range(n_trials):
result = corrected_case(n_shots=n_shots_, p_=p, px_=px,
change_basis=True)
for state in result:
corrected_case_results_x[state] += result[state]
return {'error_free': (error_free_results_z, error_free_results_x),
'noisy': (noisy_case_results_z, noisy_case_results_x),
'corrected': (corrected_case_results_z, corrected_case_results_x)}