-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathSurfaceCodeQuantumSim.py
More file actions
383 lines (329 loc) · 12.8 KB
/
SurfaceCodeQuantumSim.py
File metadata and controls
383 lines (329 loc) · 12.8 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
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
from quantumsim import Circuit
class Q(): # Quantum register naming aliases
@staticmethod
def D1() -> int:
return 0
@staticmethod
def D2() -> int:
return 1
@staticmethod
def D3() -> int:
return 2
@staticmethod
def D4() -> int:
return 3
@staticmethod
def D5() -> int:
return 4
@staticmethod
def D6() -> int:
return 5
@staticmethod
def D7() -> int:
return 6
@staticmethod
def D8() -> int:
return 7
@staticmethod
def D9() -> int:
return 8
@staticmethod
def X1() -> int:
return 9
@staticmethod
def X2() -> int:
return 9
@staticmethod
def X3() -> int:
return 9
@staticmethod
def X4() -> int:
return 9
@staticmethod
def Z1() -> int:
return 9
@staticmethod
def Z2() -> int:
return 9
@staticmethod
def Z3() -> int:
return 9
@staticmethod
def Z4() -> int:
return 9
class C(): # Classical register naming aliases
@staticmethod
def X1() -> int:
return 0
@staticmethod
def X2() -> int:
return 1
@staticmethod
def X3() -> int:
return 2
@staticmethod
def X4() -> int:
return 3
@staticmethod
def Z1() -> int:
return 4
@staticmethod
def Z2() -> int:
return 5
@staticmethod
def Z3() -> int:
return 6
@staticmethod
def Z4() -> int:
return 7
@staticmethod
def D1() -> int:
return 8
@staticmethod
def D2() -> int:
return 9
@staticmethod
def D3() -> int:
return 10
@staticmethod
def D4() -> int:
return 11
@staticmethod
def D5() -> int:
return 12
@staticmethod
def D6() -> int:
return 13
@staticmethod
def D7() -> int:
return 14
@staticmethod
def D8() -> int:
return 15
@staticmethod
def D9() -> int:
return 16
class SurfaceCodePart:
def __init__(self, name: str, startIndexGate: int, totalGates: int):
self.name = name
self.startIndexGate = startIndexGate
self.totalGates = totalGates
def toString(self) -> str:
return self.name + ", starting from index: " + str(self.startIndexGate) + ", total gates: " + str(self.totalGates) + "\n"
"""
This class offers functions for simulating the rotated surface 17 code implementation using QuantumSim
"""
class SurfaceCode:
# Sources used for creating this class:
# https://errorcorrectionzoo.org/c/surface-17#citation-3
# https://arxiv.org/pdf/2303.17211
# http://arxiv.org/pdf/1612.08208
def __init__(self):
# Simulating many qubits is expensive resource wise, the rotated surface 17 code is therefore simulated using only 10 qubits using QuantumSim.
self.qubits = int(10)
# All 17 simulated qubits are eventually measured, therefore 17 classical bits are required.
self.bits = int(17)
# Memory optimization flag, when simulating many qubits (10 or more) this flag should always be set to TRUE
self.save_instructions_flag = True
self.circuit = Circuit(self.qubits, self.bits, self.save_instructions_flag)
self.circuit.classicalBitRegister.create_partition(0, 3, "AncX")
self.circuit.classicalBitRegister.create_partition(4, 7, "AncZ")
self.circuit.classicalBitRegister.create_partition(8, 16, "Data")
# Keeps track of the circuit parts added to the SurfaceCode class
self.parts = []
def has(self, name) -> bool:
for part in self.parts:
if(part.name == name):
return True
return False
def toString(self) -> str:
output = "Surface code circuitry consists of: \n"
for part in self.parts:
output += part.toString()
output += "Total gates in SurfaceCode object: " + str(self.circuit.gates.__len__())
return output
def add_encoder_circuit(self):
if(self.has("Encoder")):
raise Exception("SurfaceCode already has an encoder circuit")
self.parts.append(SurfaceCodePart("Encoder", self.circuit.gates.__len__(), 12))
# Step 1. initialize D2, D4, D6, D8 in Hadamard basis.
self.circuit.hadamard(Q.D2())
self.circuit.hadamard(Q.D4())
self.circuit.hadamard(Q.D6())
self.circuit.hadamard(Q.D8())
# Step 2. Make four different Bell and Greenberger-Horne-Zeilinger states.
self.circuit.cnot(Q.D2(), Q.D1())
self.circuit.cnot(Q.D6(), Q.D3())
self.circuit.cnot(Q.D6(), Q.D5())
self.circuit.cnot(Q.D4(), Q.D5())
self.circuit.cnot(Q.D4(), Q.D7())
self.circuit.cnot(Q.D8(), Q.D9())
# Step 3. Entangle all Bell and Greenberger-Horne-Zeilinger states.
self.circuit.cnot(Q.D3(), Q.D2())
self.circuit.cnot(Q.D7(), Q.D8())
def remove_encoder_circuit(self):
for part in self.parts:
if part.name == "Encoder":
self.circuit.remove_circuit_part(part.startIndexGate, (part.startIndexGate + part.totalGates))
self.parts.remove(part)
return
raise Exception("No encoder exists in SurfaceCode object")
def add_decoder_circuit(self):
if(self.has("Decoder")):
raise Exception("SurfaceCode already has an decoder circuit")
self.parts.append(SurfaceCodePart("Decoder", self.circuit.gates.__len__(), 12))
# Decoding is the opposite of encoding, reverting the encoding steps results in the initiliazed state
# Step 1. Dentangle all Bell and Greenberger-Horne-Zeilinger states.
self.circuit.cnot(Q.D3(), Q.D2())
self.circuit.cnot(Q.D7(), Q.D8())
# Step 2. Dentagle different Bell and Greenberger-Horne-Zeilinger states.
self.circuit.cnot(Q.D2(), Q.D1())
self.circuit.cnot(Q.D6(), Q.D3())
self.circuit.cnot(Q.D6(), Q.D5())
self.circuit.cnot(Q.D4(), Q.D5())
self.circuit.cnot(Q.D4(), Q.D7())
self.circuit.cnot(Q.D8(), Q.D9())
# Step 3. Put D2, D4, D6, D8 out of the Hadamard basis.
self.circuit.hadamard(Q.D2())
self.circuit.hadamard(Q.D4())
self.circuit.hadamard(Q.D6())
self.circuit.hadamard(Q.D8())
def remove_decoder_circuit(self):
for part in self.parts:
if part.name == "Decoder":
self.circuit.remove_circuit_part(part.startIndexGate, (part.startIndexGate + part.totalGates))
self.parts.remove(part)
return
raise Exception("No decoder exists in SurfaceCode object")
def __add_x1_syndrome_extraction(self):
self.circuit.hadamard(Q.X1())
self.circuit.cnot(Q.X1(), Q.D1())
self.circuit.cnot(Q.X1(), Q.D2())
self.circuit.hadamard(Q.X1())
self.circuit.measurement(Q.X1(), C.X1())
self.circuit.reset(Q.X1(), C.X1())
def __add_x2_syndrome_extraction(self):
self.circuit.hadamard(Q.X2())
self.circuit.cnot(Q.X2(), Q.D7())
self.circuit.cnot(Q.X2(), Q.D4())
self.circuit.cnot(Q.X2(), Q.D8())
self.circuit.cnot(Q.X2(), Q.D5())
self.circuit.hadamard(Q.X2())
self.circuit.measurement(Q.X2(), C.X2())
self.circuit.reset(Q.X2(), C.X2())
def __add_x3_syndrome_extraction(self):
self.circuit.hadamard(Q.X3())
self.circuit.cnot(Q.X3(), Q.D5())
self.circuit.cnot(Q.X3(), Q.D2())
self.circuit.cnot(Q.X3(), Q.D6())
self.circuit.cnot(Q.X3(), Q.D3())
self.circuit.hadamard(Q.X3())
self.circuit.measurement(Q.X3(), C.X3())
self.circuit.reset(Q.X3(), C.X3())
def __add_x4_syndrome_extraction(self):
self.circuit.hadamard(Q.X4())
self.circuit.cnot(Q.X4(), Q.D8())
self.circuit.cnot(Q.X4(), Q.D9())
self.circuit.hadamard(Q.X4())
self.circuit.measurement(Q.X4(), C.X4())
self.circuit.reset(Q.X4(), C.X4())
def add_x_stabilizer_syndrome_extraction(self):
self.parts.append(SurfaceCodePart("Stabilizer X syndrome measurement", self.circuit.gates.__len__(), 28))
self.__add_x1_syndrome_extraction()
self.__add_x2_syndrome_extraction()
self.__add_x3_syndrome_extraction()
self.__add_x4_syndrome_extraction()
def __add_z1_syndrome_extraction(self):
self.circuit.cnot(Q.D7(), Q.Z1())
self.circuit.cnot(Q.D4(), Q.Z1())
self.circuit.measurement(Q.Z1(), C.Z1())
self.circuit.reset(Q.Z1(), C.Z1())
def __add_z2_syndrome_extraction(self):
self.circuit.cnot(Q.D4(), Q.Z2())
self.circuit.cnot(Q.D5(), Q.Z2())
self.circuit.cnot(Q.D1(), Q.Z2())
self.circuit.cnot(Q.D2(), Q.Z2())
self.circuit.measurement(Q.Z2(), C.Z2())
self.circuit.reset(Q.Z2(), C.Z2())
def __add_z3_syndrome_extraction(self):
self.circuit.cnot(Q.D8(), Q.Z3())
self.circuit.cnot(Q.D9(), Q.Z3())
self.circuit.cnot(Q.D5(), Q.Z3())
self.circuit.cnot(Q.D6(), Q.Z3())
self.circuit.measurement(Q.Z3(), C.Z3())
self.circuit.reset(Q.Z3(), C.Z3())
def __add_z4_syndrome_extraction(self):
self.circuit.cnot(Q.D6(), Q.Z4())
self.circuit.cnot(Q.D3(), Q.Z4())
self.circuit.measurement(Q.Z4(), C.Z4())
self.circuit.reset(Q.Z4(), C.Z4())
def add_z_stabilizer_syndrome_extraction(self):
self.parts.append(SurfaceCodePart("Stabilizer Z syndrome measurement", self.circuit.gates.__len__(), 28))
self.__add_z1_syndrome_extraction()
self.__add_z2_syndrome_extraction()
self.__add_z3_syndrome_extraction()
self.__add_z4_syndrome_extraction()
def add_bit_flip(self, q: int):
if(q < 0 or q > 8):
raise Exception("q: qubit parameter must be within boundaries 0(D1) and 8(D9)")
self.circuit.pauli_x(q)
def add_phase_flip(self, q: int):
if(q < 0 or q > 8):
raise Exception("q: qubit parameter must be within boundaries 0(D1) and 8(D9)")
self.circuit.pauli_z(q)
def add_recovery_from_syndrome_x_stabilizer(self):
"""
Calculates an appropriate recovery action based on the stabilizer Z syndrome measurement
"""
self.parts.append(SurfaceCodePart("Syndrome x recovery", self.circuit.gates.__len__(), 1))
self.circuit.recovery_phase_flip(0)
def add_recovery_from_syndrome_z_stabilizer(self):
"""
Calculates an appropriate recovery action based on the stabilizer Z syndrome measurement
"""
self.parts.append(SurfaceCodePart("Syndrome z recovery", self.circuit.gates.__len__(), 1))
self.circuit.recovery_bit_flip(4)
def add_measure_all_data_qubits(self):
self.parts.append(SurfaceCodePart("Data qubit measurement", self.circuit.gates.__len__(), 9))
self.circuit.measurement(Q.D1(), C.D1())
self.circuit.measurement(Q.D2(), C.D2())
self.circuit.measurement(Q.D3(), C.D3())
self.circuit.measurement(Q.D4(), C.D4())
self.circuit.measurement(Q.D5(), C.D5())
self.circuit.measurement(Q.D6(), C.D6())
self.circuit.measurement(Q.D7(), C.D7())
self.circuit.measurement(Q.D8(), C.D8())
self.circuit.measurement(Q.D9(), C.D9())
def add_pauli_x_on_all_data_qubits(self):
self.parts.append(SurfaceCodePart("Pauli X on all data qubits", self.circuit.gates.__len__(), 9))
self.circuit.pauli_x(Q.D1())
self.circuit.pauli_x(Q.D2())
self.circuit.pauli_x(Q.D3())
self.circuit.pauli_x(Q.D4())
self.circuit.pauli_x(Q.D5())
self.circuit.pauli_x(Q.D6())
self.circuit.pauli_x(Q.D7())
self.circuit.pauli_x(Q.D8())
self.circuit.pauli_x(Q.D9())
def add_pauli_z_on_all_data_qubits(self):
self.parts.append(SurfaceCodePart("Pauli Z on all data qubits", self.circuit.gates.__len__(), 9))
self.circuit.pauli_z(Q.D1())
self.circuit.pauli_z(Q.D2())
self.circuit.pauli_z(Q.D3())
self.circuit.pauli_z(Q.D4())
self.circuit.pauli_z(Q.D5())
self.circuit.pauli_z(Q.D6())
self.circuit.pauli_z(Q.D7())
self.circuit.pauli_z(Q.D8())
self.circuit.pauli_z(Q.D9())
def add_noisy_pauli_x_on_all_data_qubits(self):
self.parts.append(SurfaceCodePart("Noisy pauli X on all data qubits", self.circuit.gates.__len__(), 9))
self.circuit.noisy_pauli_x(Q.D1(), 0.01)
self.circuit.noisy_pauli_x(Q.D2(), 0.01)
self.circuit.noisy_pauli_x(Q.D3(), 0.01)
self.circuit.noisy_pauli_x(Q.D4(), 0.01)
self.circuit.noisy_pauli_x(Q.D5(), 0.01)
self.circuit.noisy_pauli_x(Q.D6(), 0.01)
self.circuit.noisy_pauli_x(Q.D7(), 0.01)
self.circuit.noisy_pauli_x(Q.D8(), 0.01)
self.circuit.noisy_pauli_x(Q.D9(), 0.01)