pyqrack-cuda 1.54.1__tar.gz → 1.54.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyqrack_cuda-1.54.1/pyqrack_cuda.egg-info → pyqrack_cuda-1.54.3}/PKG-INFO +1 -1
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_ace_backend.py +308 -294
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3/pyqrack_cuda.egg-info}/PKG-INFO +1 -1
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/setup.py +1 -1
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/LICENSE +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/MANIFEST.in +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/Makefile +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/README.md +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyproject.toml +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/__init__.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/pauli.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack_cuda.egg-info/SOURCES.txt +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack_cuda.egg-info/dependency_links.txt +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack_cuda.egg-info/not-zip-safe +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack_cuda.egg-info/requires.txt +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/pyqrack_cuda.egg-info/top_level.txt +0 -0
- {pyqrack_cuda-1.54.1 → pyqrack_cuda-1.54.3}/setup.cfg +0 -0
@@ -27,6 +27,115 @@ except ImportError:
|
|
27
27
|
_IS_QISKIT_AER_AVAILABLE = False
|
28
28
|
|
29
29
|
|
30
|
+
# Initial stub and concept produced through conversation with Elara
|
31
|
+
# (the custom OpenAI GPT)
|
32
|
+
class LHVQubit:
|
33
|
+
def __init__(self):
|
34
|
+
# Initial state in "Bloch vector" terms, defaults to |0⟩
|
35
|
+
self.reset()
|
36
|
+
|
37
|
+
def reset(self):
|
38
|
+
self.bloch = [0.0, 0.0, 1.0]
|
39
|
+
|
40
|
+
def h(self):
|
41
|
+
# Hadamard: rotate around Y-axis then X-axis (simplified for LHV)
|
42
|
+
x, y, z = self.bloch
|
43
|
+
self.bloch = [(x + z) / math.sqrt(2), y, (z - x) / math.sqrt(2)]
|
44
|
+
|
45
|
+
def x(self):
|
46
|
+
x, y, z = self.bloch
|
47
|
+
self.bloch = [x, y, -z]
|
48
|
+
|
49
|
+
def z(self):
|
50
|
+
x, y, z = self.bloch
|
51
|
+
self.bloch = [x, -y, z]
|
52
|
+
|
53
|
+
def rx(self, theta):
|
54
|
+
# Rotate Bloch vector around X-axis by angle theta
|
55
|
+
x, y, z = self.bloch
|
56
|
+
cos_theta = math.cos(theta)
|
57
|
+
sin_theta = math.sin(theta)
|
58
|
+
new_y = cos_theta * y - sin_theta * z
|
59
|
+
new_z = sin_theta * y + cos_theta * z
|
60
|
+
self.bloch = [x, new_y, new_z]
|
61
|
+
|
62
|
+
def ry(self, theta):
|
63
|
+
# Rotate Bloch vector around Y-axis by angle theta
|
64
|
+
x, y, z = self.bloch
|
65
|
+
cos_theta = math.cos(theta)
|
66
|
+
sin_theta = math.sin(theta)
|
67
|
+
new_x = cos_theta * x + sin_theta * z
|
68
|
+
new_z = -sin_theta * x + cos_theta * z
|
69
|
+
self.bloch = [new_x, y, new_z]
|
70
|
+
|
71
|
+
def rz(self, theta):
|
72
|
+
# Rotate Bloch vector around Z-axis by angle theta (in radians)
|
73
|
+
x, y, z = self.bloch
|
74
|
+
cos_theta = math.cos(theta)
|
75
|
+
sin_theta = math.sin(theta)
|
76
|
+
new_x = cos_theta * x - sin_theta * y
|
77
|
+
new_y = sin_theta * x + cos_theta * y
|
78
|
+
self.bloch = [new_x, new_y, z]
|
79
|
+
|
80
|
+
def s(self):
|
81
|
+
self.rz(math.pi / 2)
|
82
|
+
|
83
|
+
def adjs(self):
|
84
|
+
self.rz(-math.pi / 2)
|
85
|
+
|
86
|
+
def t(self):
|
87
|
+
self.rz(math.pi / 4)
|
88
|
+
|
89
|
+
def adjt(self):
|
90
|
+
self.rz(-math.pi / 4)
|
91
|
+
|
92
|
+
def u(self, theta, phi, lam):
|
93
|
+
# Apply general single-qubit unitary gate
|
94
|
+
self.rz(lam)
|
95
|
+
self.ry(theta)
|
96
|
+
self.rz(phi)
|
97
|
+
|
98
|
+
def prob(self, basis=Pauli.PauliZ):
|
99
|
+
"""Sample a classical outcome from the current 'quantum' state"""
|
100
|
+
if basis == Pauli.PauliZ:
|
101
|
+
prob_1 = (1 - self.bloch[2]) / 2
|
102
|
+
elif basis == Pauli.PauliX:
|
103
|
+
prob_1 = (1 - self.bloch[0]) / 2
|
104
|
+
elif basis == Pauli.PauliY:
|
105
|
+
prob_1 = (1 - self.bloch[1]) / 2
|
106
|
+
else:
|
107
|
+
raise ValueError(f"Unsupported basis: {basis}")
|
108
|
+
return prob_1
|
109
|
+
|
110
|
+
def m(self):
|
111
|
+
result = random.random() < self.prob()
|
112
|
+
self.reset()
|
113
|
+
if result:
|
114
|
+
self.x()
|
115
|
+
return result
|
116
|
+
|
117
|
+
# Provided by Elara (the custom OpenAI GPT)
|
118
|
+
def _cpauli_lhv(prob, targ, axis, anti, theta=math.pi):
|
119
|
+
"""
|
120
|
+
Apply a 'soft' controlled-Pauli gate: rotate target qubit
|
121
|
+
proportionally to control's Z expectation value.
|
122
|
+
|
123
|
+
theta: full rotation angle if control in |1⟩
|
124
|
+
"""
|
125
|
+
# Control influence is (1 - ctrl.bloch[2]) / 2 = P(|1⟩)
|
126
|
+
# BUT we avoid collapse by using the expectation value:
|
127
|
+
control_influence = (1 - prob) if anti else prob
|
128
|
+
|
129
|
+
effective_theta = control_influence * theta
|
130
|
+
|
131
|
+
# Apply partial rotation to target qubit:
|
132
|
+
if axis == Pauli.PauliX:
|
133
|
+
targ.rx(effective_theta)
|
134
|
+
elif axis == Pauli.PauliY:
|
135
|
+
targ.ry(effective_theta)
|
136
|
+
elif axis == Puali.PauliZ:
|
137
|
+
targ.rz(effective_theta)
|
138
|
+
|
30
139
|
class QrackAceBackend:
|
31
140
|
"""A back end for elided quantum error correction
|
32
141
|
|
@@ -85,8 +194,8 @@ class QrackAceBackend:
|
|
85
194
|
self._is_col_long_range[-1] = False
|
86
195
|
|
87
196
|
self._qubit_dict = {}
|
197
|
+
self._lhv_dict = {}
|
88
198
|
self._hardware_offset = []
|
89
|
-
self._ancilla = [0] * sim_count
|
90
199
|
sim_counts = [0] * sim_count
|
91
200
|
sim_id = 0
|
92
201
|
tot_qubits = 0
|
@@ -97,28 +206,21 @@ class QrackAceBackend:
|
|
97
206
|
self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
|
98
207
|
tot_qubits += 1
|
99
208
|
sim_counts[sim_id] += 1
|
100
|
-
|
101
|
-
for _ in range(2):
|
102
|
-
self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
|
103
|
-
tot_qubits += 1
|
104
|
-
sim_counts[sim_id] += 1
|
105
|
-
sim_id = (sim_id + 1) % sim_count
|
209
|
+
else:
|
106
210
|
self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
|
107
211
|
tot_qubits += 1
|
108
212
|
sim_counts[sim_id] += 1
|
109
|
-
|
213
|
+
|
214
|
+
self._lhv_dict[tot_qubits] = LHVQubit()
|
215
|
+
tot_qubits += 1
|
216
|
+
|
217
|
+
sim_id = (sim_id + 1) % sim_count
|
110
218
|
self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
|
111
219
|
tot_qubits += 1
|
112
220
|
sim_counts[sim_id] += 1
|
113
|
-
sim_id = (sim_id + 1) % sim_count
|
114
|
-
for _ in range(2):
|
115
|
-
self._qubit_dict[tot_qubits] = (sim_id, sim_counts[sim_id])
|
116
|
-
tot_qubits += 1
|
117
|
-
sim_counts[sim_id] += 1
|
118
221
|
|
119
222
|
self.sim = []
|
120
223
|
for i in range(sim_count):
|
121
|
-
self._ancilla[i] = sim_counts[i]
|
122
224
|
sim_counts[i] += 1
|
123
225
|
self.sim.append(
|
124
226
|
toClone.sim[i].clone()
|
@@ -156,8 +258,8 @@ class QrackAceBackend:
|
|
156
258
|
self._col_length, self._row_length = (row_len, col_len) if is_transpose else (col_len, row_len)
|
157
259
|
|
158
260
|
def _ct_pair_prob(self, q1, q2):
|
159
|
-
p1 = self.sim[q1[0]].prob(q1[1])
|
160
|
-
p2 = self.sim[q2[0]].prob(q2[1])
|
261
|
+
p1 = self.sim[q1[0]].prob(q1[1]) if isinstance(q1, tuple) else q1.prob()
|
262
|
+
p2 = self.sim[q2[0]].prob(q2[1]) if isinstance(q2, tuple) else q2.prob()
|
161
263
|
|
162
264
|
if p1 < p2:
|
163
265
|
return p2, q1
|
@@ -167,52 +269,59 @@ class QrackAceBackend:
|
|
167
269
|
def _cz_shadow(self, q1, q2):
|
168
270
|
prob_max, t = self._ct_pair_prob(q1, q2)
|
169
271
|
if prob_max > 0.5:
|
170
|
-
|
272
|
+
if isinstance(t, tuple):
|
273
|
+
self.sim[t[0]].z(t[1])
|
274
|
+
else:
|
275
|
+
t.z()
|
276
|
+
|
277
|
+
def _qec_x(self, c):
|
278
|
+
if isinstance(c, tuple):
|
279
|
+
self.sim[c[0]].x(c[1])
|
280
|
+
else:
|
281
|
+
c.x()
|
282
|
+
|
283
|
+
def _qec_h(self, t):
|
284
|
+
if isinstance(t, tuple):
|
285
|
+
self.sim[t[0]].h(t[1])
|
286
|
+
else:
|
287
|
+
t.h()
|
288
|
+
|
289
|
+
def _qec_s(self, t):
|
290
|
+
if isinstance(t, tuple):
|
291
|
+
self.sim[c[0]].s(t[1])
|
292
|
+
else:
|
293
|
+
t.s()
|
294
|
+
|
295
|
+
def _qec_adjs(self, t):
|
296
|
+
if isinstance(t, tuple):
|
297
|
+
self.sim[c[0]].adjs(t[1])
|
298
|
+
else:
|
299
|
+
t.adjs()
|
171
300
|
|
172
301
|
def _anti_cz_shadow(self, c, t):
|
173
|
-
self.
|
302
|
+
self._qec_x(c)
|
174
303
|
self._cz_shadow(c, t)
|
175
|
-
self.
|
304
|
+
self._qec_x(c)
|
176
305
|
|
177
306
|
def _cx_shadow(self, c, t):
|
178
|
-
self.
|
307
|
+
self._qec_h(t)
|
179
308
|
self._cz_shadow(c, t)
|
180
|
-
self.
|
309
|
+
self._qec_h(t)
|
181
310
|
|
182
311
|
def _anti_cx_shadow(self, c, t):
|
183
|
-
self.
|
312
|
+
self._qec_x(c)
|
184
313
|
self._cx_shadow(c, t)
|
185
|
-
self.
|
314
|
+
self._qec_x(c)
|
186
315
|
|
187
316
|
def _cy_shadow(self, c, t):
|
188
|
-
self.
|
317
|
+
self._qec_adjs(t)
|
189
318
|
self._cx_shadow(c, t)
|
190
|
-
self.
|
319
|
+
self._qec_s(t)
|
191
320
|
|
192
321
|
def _anti_cy_shadow(self, c, t):
|
193
|
-
self.
|
322
|
+
self._qec_x(c)
|
194
323
|
self._cy_shadow(c, t)
|
195
|
-
self.
|
196
|
-
|
197
|
-
def _ccz_shadow(self, c1, q2, q3):
|
198
|
-
self.sim[q2[0]].mcx([q2[1]], q3[1])
|
199
|
-
self.sim[q3[0]].adjt(q3[1])
|
200
|
-
self._cx_shadow(c1, q3)
|
201
|
-
self.sim[q3[0]].t(q3[1])
|
202
|
-
self.sim[q2[0]].mcx([q2[1]], q3[1])
|
203
|
-
self.sim[q3[0]].adjt(q3[1])
|
204
|
-
self._cx_shadow(c1, q3)
|
205
|
-
self.sim[q3[0]].t(q3[1])
|
206
|
-
self.sim[q2[0]].t(q2[1])
|
207
|
-
self._cx_shadow(c1, q2)
|
208
|
-
self.sim[q2[0]].adjt(q2[1])
|
209
|
-
self.sim[c1[0]].t(c1[1])
|
210
|
-
self._cx_shadow(c1, q2)
|
211
|
-
|
212
|
-
def _ccx_shadow(self, c1, q2, t):
|
213
|
-
self.sim[t[0]].h(t[1])
|
214
|
-
self._ccz_shadow(c1, q2, t)
|
215
|
-
self.sim[t[0]].h(t[1])
|
324
|
+
self._qec_x(c)
|
216
325
|
|
217
326
|
def _unpack(self, lq):
|
218
327
|
offset = self._hardware_offset[lq]
|
@@ -222,133 +331,43 @@ class QrackAceBackend:
|
|
222
331
|
|
223
332
|
return [
|
224
333
|
self._qubit_dict[offset],
|
225
|
-
self.
|
334
|
+
self._lhv_dict[offset + 1],
|
226
335
|
self._qubit_dict[offset + 2],
|
227
336
|
]
|
228
337
|
|
229
|
-
def _encode_decode(self, hq):
|
230
|
-
if len(hq) < 2:
|
231
|
-
return
|
232
|
-
if hq[0][0] == hq[1][0]:
|
233
|
-
b = hq[0]
|
234
|
-
else:
|
235
|
-
b = hq[2]
|
236
|
-
self.sim[b[0]].mcx([b[1]], hq[1][1])
|
237
|
-
|
238
338
|
def _correct(self, lq, phase=False):
|
239
|
-
if self._is_col_long_range[lq % self._row_length]:
|
339
|
+
if self._is_col_long_range[lq % self._row_length] or (self._row_length == 2):
|
240
340
|
return
|
241
|
-
# We can't use true syndrome-based error correction,
|
242
|
-
# because one of the qubits in the code is separated.
|
243
|
-
# However, we can get pretty close!
|
244
|
-
shots = 512
|
245
341
|
|
246
|
-
single_bit = 0
|
247
|
-
other_bits = []
|
248
342
|
hq = self._unpack(lq)
|
249
343
|
|
250
344
|
if phase:
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
elif hq[1][0] == hq[2][0]:
|
258
|
-
single_bit = 0
|
259
|
-
other_bits = [1, 2]
|
260
|
-
else:
|
261
|
-
raise RuntimeError("Invalid boundary qubit!")
|
262
|
-
|
263
|
-
ancilla_sim = hq[other_bits[0]][0]
|
264
|
-
ancilla = self._ancilla[ancilla_sim]
|
265
|
-
|
266
|
-
single_bit_value = self.sim[hq[single_bit][0]].prob(hq[single_bit][1])
|
267
|
-
single_bit_polarization = max(single_bit_value, 1 - single_bit_value)
|
268
|
-
|
269
|
-
# Suggestion from Elara (the custom OpenAI GPT):
|
270
|
-
# Create phase parity tie before measurement.
|
271
|
-
self._ccx_shadow(hq[single_bit], hq[other_bits[0]], [ancilla_sim, ancilla])
|
272
|
-
self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
|
273
|
-
self.sim[ancilla_sim].force_m(ancilla, False)
|
274
|
-
|
275
|
-
samples = self.sim[ancilla_sim].measure_shots(
|
276
|
-
[hq[other_bits[0]][1], hq[other_bits[1]][1]], shots
|
277
|
-
)
|
278
|
-
|
279
|
-
syndrome_indices = (
|
280
|
-
[other_bits[1], other_bits[0]]
|
281
|
-
if (single_bit_value >= 0.5)
|
282
|
-
else [other_bits[0], other_bits[1]]
|
283
|
-
)
|
284
|
-
syndrome = [0, 0, 0]
|
285
|
-
values = []
|
286
|
-
for sample in samples:
|
287
|
-
match sample:
|
288
|
-
case 0:
|
289
|
-
value = single_bit_value
|
290
|
-
syndrome[single_bit] += value
|
291
|
-
case 1:
|
292
|
-
value = single_bit_polarization
|
293
|
-
syndrome[syndrome_indices[0]] += value
|
294
|
-
case 2:
|
295
|
-
value = single_bit_polarization
|
296
|
-
syndrome[syndrome_indices[1]] += value
|
297
|
-
case 3:
|
298
|
-
value = 1 - single_bit_value
|
299
|
-
syndrome[single_bit] += value
|
300
|
-
values.append(value)
|
301
|
-
|
302
|
-
# Suggestion from Elara (custom OpenAI GPT):
|
303
|
-
# Compute the standard deviation and only correct if we're outside a confidence interval.
|
304
|
-
# (This helps avoid limit-point over-correction.)
|
305
|
-
syndrome_sum = sum(syndrome)
|
306
|
-
z_score_numer = syndrome_sum - shots / 2
|
307
|
-
z_score = 0
|
308
|
-
if z_score_numer > 0:
|
309
|
-
syndrome_component_mean = syndrome_sum / shots
|
310
|
-
syndrome_total_variance = sum(
|
311
|
-
(value - syndrome_component_mean) ** 2 for value in values
|
312
|
-
)
|
313
|
-
z_score_denom = math.sqrt(syndrome_total_variance)
|
314
|
-
z_score = (
|
315
|
-
math.inf
|
316
|
-
if math.isclose(z_score_denom, 0)
|
317
|
-
else (z_score_numer / z_score_denom)
|
318
|
-
)
|
345
|
+
b = hq[0]
|
346
|
+
self.sim[b[0]].h(b[1])
|
347
|
+
b = hq[1]
|
348
|
+
b.h()
|
349
|
+
b = hq[2]
|
350
|
+
self.sim[b[0]].h(b[1])
|
319
351
|
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
# Form their syndrome.
|
332
|
-
self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
|
333
|
-
self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
|
334
|
-
# Force the syndrome pathological
|
335
|
-
self.sim[ancilla_sim].force_m(ancilla, True)
|
336
|
-
# Reset the ancilla.
|
337
|
-
self.sim[ancilla_sim].x(ancilla)
|
338
|
-
# Correct the bit flip.
|
339
|
-
self.sim[ancilla_sim].x(hq[error_bit][1])
|
340
|
-
|
341
|
-
# There is no error.
|
342
|
-
if force_syndrome:
|
343
|
-
# Form the syndrome of the coherent bits.
|
344
|
-
self.sim[ancilla_sim].mcx([hq[other_bits[0]][1]], ancilla)
|
345
|
-
self.sim[ancilla_sim].mcx([hq[other_bits[1]][1]], ancilla)
|
346
|
-
# Force the syndrome non-pathological.
|
347
|
-
self.sim[ancilla_sim].force_m(ancilla, False)
|
352
|
+
p = [self.sim[hq[0][0]].prob(hq[0][1]), hq[1].prob(), self.sim[hq[2][0]].prob(hq[2][1])]
|
353
|
+
avg = sum(p) / 3
|
354
|
+
result = (avg >= 0.5)
|
355
|
+
syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
|
356
|
+
|
357
|
+
for q in range(3):
|
358
|
+
if syndrome[q] > 0.5:
|
359
|
+
if q == 1:
|
360
|
+
hq[q].x()
|
361
|
+
else:
|
362
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
348
363
|
|
349
364
|
if phase:
|
350
|
-
|
351
|
-
|
365
|
+
b = hq[0]
|
366
|
+
self.sim[b[0]].h(b[1])
|
367
|
+
b = hq[1]
|
368
|
+
b.h()
|
369
|
+
b = hq[2]
|
370
|
+
self.sim[b[0]].h(b[1])
|
352
371
|
|
353
372
|
def u(self, lq, th, ph, lm):
|
354
373
|
hq = self._unpack(lq)
|
@@ -357,28 +376,15 @@ class QrackAceBackend:
|
|
357
376
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
358
377
|
return
|
359
378
|
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
if not math.isclose(ph, -lm) and not math.isclose(abs(ph), math.pi / 2):
|
370
|
-
# Produces/destroys superposition
|
371
|
-
self._encode_decode(hq)
|
372
|
-
b = hq[0]
|
373
|
-
self.sim[b[0]].u(b[1], th, ph, lm)
|
374
|
-
b = hq[2]
|
375
|
-
self.sim[b[0]].u(b[1], th, ph, lm)
|
376
|
-
self._encode_decode(hq)
|
377
|
-
self._correct(lq)
|
378
|
-
else:
|
379
|
-
# Shouldn't produce/destroy superposition
|
380
|
-
for b in hq:
|
381
|
-
self.sim[b[0]].u(b[1], th, ph, lm)
|
379
|
+
b = hq[0]
|
380
|
+
self.sim[b[0]].u(b[1], th, ph, lm)
|
381
|
+
b = hq[1]
|
382
|
+
b.u(th, ph, lm)
|
383
|
+
b = hq[2]
|
384
|
+
self.sim[b[0]].u(b[1], th, ph, lm)
|
385
|
+
|
386
|
+
self._correct(lq, False)
|
387
|
+
self._correct(lq, True)
|
382
388
|
|
383
389
|
def r(self, p, th, lq):
|
384
390
|
hq = self._unpack(lq)
|
@@ -387,23 +393,22 @@ class QrackAceBackend:
|
|
387
393
|
self.sim[b[0]].r(p, th, b[1])
|
388
394
|
return
|
389
395
|
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
self.
|
406
|
-
self._correct(lq)
|
396
|
+
b = hq[0]
|
397
|
+
self.sim[b[0]].r(p, th, b[1])
|
398
|
+
b = hq[1]
|
399
|
+
if p == Pauli.PauliX:
|
400
|
+
b.rx(th)
|
401
|
+
elif p == Pauli.PauliY:
|
402
|
+
b.ry(th)
|
403
|
+
elif p == Pauli.PauliZ:
|
404
|
+
b.rz(th)
|
405
|
+
b = hq[2]
|
406
|
+
self.sim[b[0]].r(p, th, b[1])
|
407
|
+
|
408
|
+
if p != Pauli.PauliZ:
|
409
|
+
self._correct(lq, False)
|
410
|
+
if p != Pauli.PauliX:
|
411
|
+
self._correct(lq, True)
|
407
412
|
|
408
413
|
def h(self, lq):
|
409
414
|
hq = self._unpack(lq)
|
@@ -412,12 +417,13 @@ class QrackAceBackend:
|
|
412
417
|
self.sim[b[0]].h(b[1])
|
413
418
|
return
|
414
419
|
|
415
|
-
self.
|
420
|
+
self._correct(lq)
|
416
421
|
b = hq[0]
|
417
422
|
self.sim[b[0]].h(b[1])
|
423
|
+
b = hq[1]
|
424
|
+
b.h()
|
418
425
|
b = hq[2]
|
419
426
|
self.sim[b[0]].h(b[1])
|
420
|
-
self._encode_decode(hq)
|
421
427
|
self._correct(lq)
|
422
428
|
|
423
429
|
def s(self, lq):
|
@@ -427,8 +433,12 @@ class QrackAceBackend:
|
|
427
433
|
self.sim[b[0]].s(b[1])
|
428
434
|
return
|
429
435
|
|
430
|
-
|
431
|
-
|
436
|
+
b = hq[0]
|
437
|
+
self.sim[b[0]].s(b[1])
|
438
|
+
b = hq[1]
|
439
|
+
b.s()
|
440
|
+
b = hq[2]
|
441
|
+
self.sim[b[0]].s(b[1])
|
432
442
|
|
433
443
|
def adjs(self, lq):
|
434
444
|
hq = self._unpack(lq)
|
@@ -437,8 +447,12 @@ class QrackAceBackend:
|
|
437
447
|
self.sim[b[0]].adjs(b[1])
|
438
448
|
return
|
439
449
|
|
440
|
-
|
441
|
-
|
450
|
+
b = hq[0]
|
451
|
+
self.sim[b[0]].adjs(b[1])
|
452
|
+
b = hq[1]
|
453
|
+
b.adjs()
|
454
|
+
b = hq[2]
|
455
|
+
self.sim[b[0]].adjs(b[1])
|
442
456
|
|
443
457
|
def x(self, lq):
|
444
458
|
hq = self._unpack(lq)
|
@@ -447,8 +461,12 @@ class QrackAceBackend:
|
|
447
461
|
self.sim[b[0]].x(b[1])
|
448
462
|
return
|
449
463
|
|
450
|
-
|
451
|
-
|
464
|
+
b = hq[0]
|
465
|
+
self.sim[b[0]].x(b[1])
|
466
|
+
b = hq[1]
|
467
|
+
b.x()
|
468
|
+
b = hq[2]
|
469
|
+
self.sim[b[0]].x(b[1])
|
452
470
|
|
453
471
|
def y(self, lq):
|
454
472
|
hq = self._unpack(lq)
|
@@ -457,8 +475,12 @@ class QrackAceBackend:
|
|
457
475
|
self.sim[b[0]].y(b[1])
|
458
476
|
return
|
459
477
|
|
460
|
-
|
461
|
-
|
478
|
+
b = hq[0]
|
479
|
+
self.sim[b[0]].y(b[1])
|
480
|
+
b = hq[1]
|
481
|
+
b.y()
|
482
|
+
b = hq[2]
|
483
|
+
self.sim[b[0]].y(b[1])
|
462
484
|
|
463
485
|
def z(self, lq):
|
464
486
|
hq = self._unpack(lq)
|
@@ -467,8 +489,12 @@ class QrackAceBackend:
|
|
467
489
|
self.sim[b[0]].z(b[1])
|
468
490
|
return
|
469
491
|
|
470
|
-
|
471
|
-
|
492
|
+
b = hq[0]
|
493
|
+
self.sim[b[0]].z(b[1])
|
494
|
+
b = hq[1]
|
495
|
+
b.z()
|
496
|
+
b = hq[2]
|
497
|
+
self.sim[b[0]].z(b[1])
|
472
498
|
|
473
499
|
def t(self, lq):
|
474
500
|
hq = self._unpack(lq)
|
@@ -477,8 +503,12 @@ class QrackAceBackend:
|
|
477
503
|
self.sim[b[0]].t(b[1])
|
478
504
|
return
|
479
505
|
|
480
|
-
|
481
|
-
|
506
|
+
b = hq[0]
|
507
|
+
self.sim[b[0]].t(b[1])
|
508
|
+
b = hq[1]
|
509
|
+
b.t()
|
510
|
+
b = hq[2]
|
511
|
+
self.sim[b[0]].t(b[1])
|
482
512
|
|
483
513
|
def adjt(self, lq):
|
484
514
|
hq = self._unpack(lq)
|
@@ -487,8 +517,12 @@ class QrackAceBackend:
|
|
487
517
|
self.sim[b[0]].adjt(b[1])
|
488
518
|
return
|
489
519
|
|
490
|
-
|
491
|
-
|
520
|
+
b = hq[0]
|
521
|
+
self.sim[b[0]].adjt(b[1])
|
522
|
+
b = hq[1]
|
523
|
+
b.adjt()
|
524
|
+
b = hq[2]
|
525
|
+
self.sim[b[0]].adjt(b[1])
|
492
526
|
|
493
527
|
def _get_gate(self, pauli, anti, sim_id):
|
494
528
|
gate = None
|
@@ -541,6 +575,14 @@ class QrackAceBackend:
|
|
541
575
|
shadow(b1, b2)
|
542
576
|
return
|
543
577
|
|
578
|
+
if self._row_length == 2:
|
579
|
+
gate, shadow = self._get_gate(pauli, anti, hq1[2][0])
|
580
|
+
gate([hq1[2][1]], hq2[0][1])
|
581
|
+
gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
|
582
|
+
gate([hq1[0][1]], hq2[2][1])
|
583
|
+
_cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
|
584
|
+
return
|
585
|
+
|
544
586
|
connected_cols = []
|
545
587
|
c = (lq1_col - 1) % self._row_length
|
546
588
|
while self._is_col_long_range[c] and (
|
@@ -564,64 +606,55 @@ class QrackAceBackend:
|
|
564
606
|
|
565
607
|
if (lq2_col in connected_cols) and (connected_cols.index(lq2_col) < boundary):
|
566
608
|
# lq2_col < lq1_col
|
567
|
-
self._encode_decode(hq1)
|
568
|
-
self._encode_decode(hq2)
|
569
609
|
gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
|
570
610
|
if lq1_lr:
|
571
611
|
gate([hq1[0][1]], hq2[2][1])
|
572
612
|
shadow(hq1[0], hq2[0])
|
613
|
+
_cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
|
573
614
|
elif lq2_lr:
|
574
615
|
gate([hq1[0][1]], hq2[0][1])
|
575
616
|
else:
|
576
617
|
gate([hq1[0][1]], hq2[2][1])
|
577
618
|
shadow(hq1[2], hq2[0])
|
578
|
-
|
579
|
-
self._encode_decode(hq1)
|
619
|
+
_cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
|
580
620
|
elif lq2_col in connected_cols:
|
581
621
|
# lq1_col < lq2_col
|
582
|
-
self._encode_decode(hq1)
|
583
|
-
self._encode_decode(hq2)
|
584
622
|
gate, shadow = self._get_gate(pauli, anti, hq2[0][0])
|
585
623
|
if lq1_lr:
|
586
624
|
gate([hq1[0][1]], hq2[0][1])
|
587
625
|
shadow(hq1[0], hq2[2])
|
626
|
+
_cpauli_lhv(self.sim[hq1[0][0]].prob(hq1[0][1]), hq2[1], pauli, anti)
|
588
627
|
elif lq2_lr:
|
589
628
|
gate([hq1[2][1]], hq2[0][1])
|
590
629
|
else:
|
591
630
|
gate([hq1[2][1]], hq2[0][1])
|
592
631
|
shadow(hq1[0], hq2[2])
|
593
|
-
|
594
|
-
self._encode_decode(hq1)
|
632
|
+
_cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
|
595
633
|
elif lq1_col == lq2_col:
|
596
634
|
# Both are in the same boundary column.
|
597
|
-
self._encode_decode(hq1)
|
598
|
-
self._encode_decode(hq2)
|
599
635
|
b = hq1[0]
|
600
636
|
gate, shadow = self._get_gate(pauli, anti, b[0])
|
601
637
|
gate([b[1]], hq2[0][1])
|
638
|
+
_cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
|
602
639
|
b = hq1[2]
|
603
640
|
gate, shadow = self._get_gate(pauli, anti, b[0])
|
604
641
|
gate([b[1]], hq2[2][1])
|
605
|
-
self._encode_decode(hq1)
|
606
|
-
self._encode_decode(hq2)
|
607
642
|
else:
|
608
643
|
# The qubits have no quantum connection.
|
609
644
|
gate, shadow = self._get_gate(pauli, anti, hq1[0][0])
|
610
|
-
self._encode_decode(hq1)
|
611
|
-
self._encode_decode(hq2)
|
612
645
|
shadow(hq1[0], hq2[0])
|
613
646
|
if lq1_lr:
|
614
647
|
shadow(hq1[0], hq2[2])
|
648
|
+
shadow(hq1[0], hq2[1])
|
615
649
|
elif not lq2_lr:
|
650
|
+
_cpauli_lhv(hq1[1].prob(), hq2[1], pauli, anti)
|
616
651
|
shadow(hq1[2], hq2[2])
|
617
|
-
self._encode_decode(hq2)
|
618
|
-
self._encode_decode(hq1)
|
619
652
|
|
620
653
|
self._correct(lq1, True)
|
621
654
|
if pauli != Pauli.PauliZ:
|
622
655
|
self._correct(lq2, False)
|
623
656
|
if pauli != Pauli.PauliX:
|
624
|
-
self.
|
657
|
+
self._corrent(lq2, True)
|
625
658
|
|
626
659
|
def cx(self, lq1, lq2):
|
627
660
|
self._cpauli(lq1, lq2, False, Pauli.PauliX)
|
@@ -700,31 +733,36 @@ class QrackAceBackend:
|
|
700
733
|
self.cz(lq1, lq2)
|
701
734
|
self.swap(lq1, lq2)
|
702
735
|
|
736
|
+
def prob(self, lq):
|
737
|
+
hq = self._unpack(lq)
|
738
|
+
if len(hq) < 2:
|
739
|
+
b = hq[0]
|
740
|
+
return self.sim[b[0]].prob(b[1])
|
741
|
+
|
742
|
+
self._correct(lq)
|
743
|
+
b0 = hq[0]
|
744
|
+
b2 = hq[2]
|
745
|
+
result = (self.sim[b0[0]].prob(b0[1]) + self.sim[b2[0]].prob(b2[1])) / 2
|
746
|
+
|
747
|
+
return result
|
748
|
+
|
703
749
|
def m(self, lq):
|
704
750
|
hq = self._unpack(lq)
|
705
751
|
if len(hq) < 2:
|
706
752
|
b = hq[0]
|
707
753
|
return self.sim[b[0]].m(b[1])
|
708
754
|
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
|
714
|
-
|
715
|
-
|
716
|
-
|
717
|
-
b
|
718
|
-
|
719
|
-
|
720
|
-
syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
|
721
|
-
# The two separable parts of the code are correlated,
|
722
|
-
# but not non-locally, via entanglement.
|
723
|
-
# Collapse the other separable part toward agreement.
|
724
|
-
b = hq[single_bit]
|
725
|
-
syndrome += self.sim[b[0]].force_m(b[1], bool(syndrome))
|
726
|
-
|
727
|
-
return True if (syndrome > 1) else False
|
755
|
+
result = (random.random() < self.prob(lq))
|
756
|
+
b = hq[0]
|
757
|
+
self.sim[b[0]].force_m(b[1], result)
|
758
|
+
b = hq[1]
|
759
|
+
b.reset()
|
760
|
+
if result:
|
761
|
+
b.x()
|
762
|
+
b = hq[2]
|
763
|
+
self.sim[b[0]].force_m(b[1], result)
|
764
|
+
|
765
|
+
return result
|
728
766
|
|
729
767
|
def force_m(self, lq, c):
|
730
768
|
hq = self._unpack(lq)
|
@@ -733,33 +771,28 @@ class QrackAceBackend:
|
|
733
771
|
return self.sim[b[0]].force_m(b[1])
|
734
772
|
|
735
773
|
self._correct(lq)
|
736
|
-
|
737
|
-
|
774
|
+
b = hq[1]
|
775
|
+
b.reset()
|
776
|
+
if c:
|
777
|
+
b.x()
|
778
|
+
b = hq[0]
|
779
|
+
self.sim[b[0]].force_m(b[1], c)
|
780
|
+
b = hq[2]
|
781
|
+
self.sim[b[0]].force_m(b[1], c)
|
738
782
|
|
739
783
|
return c
|
740
784
|
|
741
785
|
def m_all(self):
|
742
|
-
result = 0
|
743
786
|
# Randomize the order of measurement to amortize error.
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
751
|
-
|
752
|
-
|
753
|
-
lq_col = (c + col_offset) % self._row_length
|
754
|
-
lq = lq_row * self._row_length + lq_col
|
755
|
-
if self.m(lq):
|
756
|
-
result |= 1 << lq
|
757
|
-
lq_row += 1
|
758
|
-
if lq_row == self._col_length:
|
759
|
-
continue
|
760
|
-
for c in range(self._row_length):
|
761
|
-
lq_col = ((self._row_length - (c + 1)) + col_offset) % self._row_length
|
762
|
-
lq = lq_row * self._row_length + lq_col
|
787
|
+
result = 0
|
788
|
+
rows = list(range(self._col_length))
|
789
|
+
random.shuffle(rows)
|
790
|
+
for lq_row in rows:
|
791
|
+
row_offset = lq_row * self._row_length
|
792
|
+
cols = list(range(self._row_length))
|
793
|
+
random.shuffle(cols)
|
794
|
+
for lq_col in cols:
|
795
|
+
lq = row_offset + lq_col
|
763
796
|
if self.m(lq):
|
764
797
|
result |= 1 << lq
|
765
798
|
|
@@ -778,25 +811,6 @@ class QrackAceBackend:
|
|
778
811
|
|
779
812
|
return samples
|
780
813
|
|
781
|
-
def prob(self, lq):
|
782
|
-
hq = self._unpack(lq)
|
783
|
-
if len(hq) < 2:
|
784
|
-
b = hq[0]
|
785
|
-
return self.sim[b[0]].prob(b[1])
|
786
|
-
|
787
|
-
self._correct(lq)
|
788
|
-
if hq[0][0] == hq[1][0]:
|
789
|
-
other_bits = [0, 1]
|
790
|
-
else:
|
791
|
-
other_bits = [1, 2]
|
792
|
-
b0 = hq[other_bits[0]]
|
793
|
-
b1 = hq[other_bits[1]]
|
794
|
-
self.sim[b0[0]].mcx([b0[1]], b1[1])
|
795
|
-
result = self.sim[b0[0]].prob(b0[1])
|
796
|
-
self.sim[b0[0]].mcx([b0[1]], b1[1])
|
797
|
-
|
798
|
-
return result
|
799
|
-
|
800
814
|
def _apply_op(self, operation):
|
801
815
|
name = operation.name
|
802
816
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|