pyqrack-cpu 1.61.3__py3-none-manylinux_2_35_x86_64.whl → 1.63.0__py3-none-manylinux_2_35_x86_64.whl
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.
Potentially problematic release.
This version of pyqrack-cpu might be problematic. Click here for more details.
- pyqrack/qrack_ace_backend.py +381 -54
- {pyqrack_cpu-1.61.3.dist-info → pyqrack_cpu-1.63.0.dist-info}/METADATA +1 -1
- {pyqrack_cpu-1.61.3.dist-info → pyqrack_cpu-1.63.0.dist-info}/RECORD +6 -6
- {pyqrack_cpu-1.61.3.dist-info → pyqrack_cpu-1.63.0.dist-info}/LICENSE +0 -0
- {pyqrack_cpu-1.61.3.dist-info → pyqrack_cpu-1.63.0.dist-info}/WHEEL +0 -0
- {pyqrack_cpu-1.61.3.dist-info → pyqrack_cpu-1.63.0.dist-info}/top_level.txt +0 -0
pyqrack/qrack_ace_backend.py
CHANGED
|
@@ -27,6 +27,163 @@ 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, toClone=None):
|
|
34
|
+
# Initial state in "Bloch vector" terms, defaults to |0⟩
|
|
35
|
+
if toClone:
|
|
36
|
+
self.bloch = toClone.bloch.copy()
|
|
37
|
+
else:
|
|
38
|
+
self.reset()
|
|
39
|
+
|
|
40
|
+
def reset(self):
|
|
41
|
+
self.bloch = [0.0, 0.0, 1.0]
|
|
42
|
+
|
|
43
|
+
def h(self):
|
|
44
|
+
# Hadamard: rotate around Y-axis then X-axis (simplified for LHV)
|
|
45
|
+
x, y, z = self.bloch
|
|
46
|
+
self.bloch = [(x + z) / math.sqrt(2), y, (z - x) / math.sqrt(2)]
|
|
47
|
+
|
|
48
|
+
def x(self):
|
|
49
|
+
x, y, z = self.bloch
|
|
50
|
+
self.bloch = [x, y, -z]
|
|
51
|
+
|
|
52
|
+
def y(self):
|
|
53
|
+
x, y, z = self.bloch
|
|
54
|
+
self.bloch = [-x, y, z]
|
|
55
|
+
|
|
56
|
+
def z(self):
|
|
57
|
+
x, y, z = self.bloch
|
|
58
|
+
self.bloch = [x, -y, z]
|
|
59
|
+
|
|
60
|
+
def rx(self, theta):
|
|
61
|
+
# Rotate Bloch vector around X-axis by angle theta
|
|
62
|
+
x, y, z = self.bloch
|
|
63
|
+
cos_theta = math.cos(theta)
|
|
64
|
+
sin_theta = math.sin(theta)
|
|
65
|
+
new_y = cos_theta * y - sin_theta * z
|
|
66
|
+
new_z = sin_theta * y + cos_theta * z
|
|
67
|
+
self.bloch = [x, new_y, new_z]
|
|
68
|
+
|
|
69
|
+
def ry(self, theta):
|
|
70
|
+
# Rotate Bloch vector around Y-axis by angle theta
|
|
71
|
+
x, y, z = self.bloch
|
|
72
|
+
cos_theta = math.cos(theta)
|
|
73
|
+
sin_theta = math.sin(theta)
|
|
74
|
+
new_x = cos_theta * x + sin_theta * z
|
|
75
|
+
new_z = -sin_theta * x + cos_theta * z
|
|
76
|
+
self.bloch = [new_x, y, new_z]
|
|
77
|
+
|
|
78
|
+
def rz(self, theta):
|
|
79
|
+
# Rotate Bloch vector around Z-axis by angle theta (in radians)
|
|
80
|
+
x, y, z = self.bloch
|
|
81
|
+
cos_theta = math.cos(theta)
|
|
82
|
+
sin_theta = math.sin(theta)
|
|
83
|
+
new_x = cos_theta * x - sin_theta * y
|
|
84
|
+
new_y = sin_theta * x + cos_theta * y
|
|
85
|
+
self.bloch = [new_x, new_y, z]
|
|
86
|
+
|
|
87
|
+
def s(self):
|
|
88
|
+
self.rz(math.pi / 2)
|
|
89
|
+
|
|
90
|
+
def adjs(self):
|
|
91
|
+
self.rz(-math.pi / 2)
|
|
92
|
+
|
|
93
|
+
def t(self):
|
|
94
|
+
self.rz(math.pi / 4)
|
|
95
|
+
|
|
96
|
+
def adjt(self):
|
|
97
|
+
self.rz(-math.pi / 4)
|
|
98
|
+
|
|
99
|
+
def u(self, theta, phi, lam):
|
|
100
|
+
# Apply general single-qubit unitary gate
|
|
101
|
+
self.rz(lam)
|
|
102
|
+
self.ry(theta)
|
|
103
|
+
self.rz(phi)
|
|
104
|
+
|
|
105
|
+
# Provided verbatim by Elara (the custom OpenAI GPT):
|
|
106
|
+
def mtrx(self, matrix):
|
|
107
|
+
"""
|
|
108
|
+
Apply a 2x2 unitary matrix to the LHV Bloch vector using only standard math/cmath.
|
|
109
|
+
Matrix format: [a, b, c, d] for [[a, b], [c, d]]
|
|
110
|
+
"""
|
|
111
|
+
a, b, c, d = matrix
|
|
112
|
+
|
|
113
|
+
# Current Bloch vector
|
|
114
|
+
x, y, z = self.bloch
|
|
115
|
+
|
|
116
|
+
# Convert to density matrix ρ = ½ (I + xσx + yσy + zσz)
|
|
117
|
+
rho = [[(1 + z) / 2, (x - 1j * y) / 2], [(x + 1j * y) / 2, (1 - z) / 2]]
|
|
118
|
+
|
|
119
|
+
# Compute U * ρ
|
|
120
|
+
u_rho = [
|
|
121
|
+
[a * rho[0][0] + b * rho[1][0], a * rho[0][1] + b * rho[1][1]],
|
|
122
|
+
[c * rho[0][0] + d * rho[1][0], c * rho[0][1] + d * rho[1][1]],
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
# Compute (U * ρ) * U†
|
|
126
|
+
rho_prime = [
|
|
127
|
+
[
|
|
128
|
+
u_rho[0][0] * a.conjugate() + u_rho[0][1] * b.conjugate(),
|
|
129
|
+
u_rho[0][0] * c.conjugate() + u_rho[0][1] * d.conjugate(),
|
|
130
|
+
],
|
|
131
|
+
[
|
|
132
|
+
u_rho[1][0] * a.conjugate() + u_rho[1][1] * b.conjugate(),
|
|
133
|
+
u_rho[1][0] * c.conjugate() + u_rho[1][1] * d.conjugate(),
|
|
134
|
+
],
|
|
135
|
+
]
|
|
136
|
+
|
|
137
|
+
# Extract Bloch components: Tr(ρ'σi) = 2 * Re[...]
|
|
138
|
+
new_x = 2 * rho_prime[0][1].real + 2 * rho_prime[1][0].real
|
|
139
|
+
new_y = 2 * (rho_prime[0][1].imag - rho_prime[1][0].imag)
|
|
140
|
+
new_z = 2 * rho_prime[0][0].real - 1 # since Tr(ρ') = 1
|
|
141
|
+
|
|
142
|
+
self.bloch = [new_x, new_y, new_z]
|
|
143
|
+
|
|
144
|
+
def prob(self, basis=Pauli.PauliZ):
|
|
145
|
+
"""Sample a classical outcome from the current 'quantum' state"""
|
|
146
|
+
if basis == Pauli.PauliZ:
|
|
147
|
+
prob_1 = (1 - self.bloch[2]) / 2
|
|
148
|
+
elif basis == Pauli.PauliX:
|
|
149
|
+
prob_1 = (1 - self.bloch[0]) / 2
|
|
150
|
+
elif basis == Pauli.PauliY:
|
|
151
|
+
prob_1 = (1 - self.bloch[1]) / 2
|
|
152
|
+
else:
|
|
153
|
+
raise ValueError(f"Unsupported basis: {basis}")
|
|
154
|
+
return prob_1
|
|
155
|
+
|
|
156
|
+
def m(self):
|
|
157
|
+
result = random.random() < self.prob()
|
|
158
|
+
self.reset()
|
|
159
|
+
if result:
|
|
160
|
+
self.x()
|
|
161
|
+
return result
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# Provided by Elara (the custom OpenAI GPT)
|
|
165
|
+
def _cpauli_lhv(prob, targ, axis, anti, theta=math.pi):
|
|
166
|
+
"""
|
|
167
|
+
Apply a 'soft' controlled-Pauli gate: rotate target qubit
|
|
168
|
+
proportionally to control's Z expectation value.
|
|
169
|
+
|
|
170
|
+
theta: full rotation angle if control in |1⟩
|
|
171
|
+
"""
|
|
172
|
+
# Control influence is (1 - ctrl.bloch[2]) / 2 = P(|1⟩)
|
|
173
|
+
# BUT we avoid collapse by using the expectation value:
|
|
174
|
+
control_influence = (1 - prob) if anti else prob
|
|
175
|
+
|
|
176
|
+
effective_theta = control_influence * theta
|
|
177
|
+
|
|
178
|
+
# Apply partial rotation to target qubit:
|
|
179
|
+
if axis == Pauli.PauliX:
|
|
180
|
+
targ.rx(effective_theta)
|
|
181
|
+
elif axis == Pauli.PauliY:
|
|
182
|
+
targ.ry(effective_theta)
|
|
183
|
+
elif axis == Pauli.PauliZ:
|
|
184
|
+
targ.rz(effective_theta)
|
|
185
|
+
|
|
186
|
+
|
|
30
187
|
class QrackAceBackend:
|
|
31
188
|
"""A back end for elided quantum error correction
|
|
32
189
|
|
|
@@ -45,6 +202,7 @@ class QrackAceBackend:
|
|
|
45
202
|
sim(QrackSimulator): Array of simulators corresponding to "patches" between boundary rows.
|
|
46
203
|
long_range_columns(int): How many ideal rows between QEC boundary rows?
|
|
47
204
|
is_transpose(bool): Rows are long if False, columns are long if True
|
|
205
|
+
correction_bias(float): Bias magnitude and direction during pseudo-QEC
|
|
48
206
|
"""
|
|
49
207
|
|
|
50
208
|
def __init__(
|
|
@@ -53,6 +211,7 @@ class QrackAceBackend:
|
|
|
53
211
|
long_range_columns=4,
|
|
54
212
|
long_range_rows=4,
|
|
55
213
|
is_transpose=False,
|
|
214
|
+
correction_bias=0,
|
|
56
215
|
isTensorNetwork=False,
|
|
57
216
|
isSchmidtDecomposeMulti=False,
|
|
58
217
|
isSchmidtDecompose=True,
|
|
@@ -81,6 +240,7 @@ class QrackAceBackend:
|
|
|
81
240
|
self.long_range_columns = long_range_columns
|
|
82
241
|
self.long_range_rows = long_range_rows
|
|
83
242
|
self.is_transpose = is_transpose
|
|
243
|
+
self.correction_bias = correction_bias
|
|
84
244
|
|
|
85
245
|
fppow = 5
|
|
86
246
|
if "QRACK_FPPOW" in os.environ:
|
|
@@ -116,7 +276,7 @@ class QrackAceBackend:
|
|
|
116
276
|
self._is_row_long_range[-1] = False
|
|
117
277
|
sim_count = col_patch_count * row_patch_count
|
|
118
278
|
|
|
119
|
-
self.
|
|
279
|
+
self._qubits = []
|
|
120
280
|
sim_counts = [0] * sim_count
|
|
121
281
|
sim_id = 0
|
|
122
282
|
tot_qubits = 0
|
|
@@ -130,6 +290,14 @@ class QrackAceBackend:
|
|
|
130
290
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
131
291
|
sim_counts[t_sim_id] += 1
|
|
132
292
|
|
|
293
|
+
qubit.append(
|
|
294
|
+
LHVQubit(
|
|
295
|
+
toClone=(
|
|
296
|
+
toClone._qubits[tot_qubits][2] if toClone else None
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
133
301
|
if (not c) and (not r):
|
|
134
302
|
t_sim_id = (sim_id + col_patch_count) % sim_count
|
|
135
303
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
@@ -142,7 +310,7 @@ class QrackAceBackend:
|
|
|
142
310
|
if not c:
|
|
143
311
|
sim_id = (sim_id + 1) % sim_count
|
|
144
312
|
|
|
145
|
-
self.
|
|
313
|
+
self._qubits.append(qubit)
|
|
146
314
|
tot_qubits += 1
|
|
147
315
|
|
|
148
316
|
self.sim = []
|
|
@@ -259,18 +427,36 @@ class QrackAceBackend:
|
|
|
259
427
|
self._qec_x(c)
|
|
260
428
|
|
|
261
429
|
def _unpack(self, lq):
|
|
262
|
-
return self.
|
|
430
|
+
return self._qubits[lq]
|
|
263
431
|
|
|
264
|
-
def
|
|
432
|
+
def _get_qb_lhv_indices(self, hq):
|
|
265
433
|
qb = []
|
|
266
434
|
if len(hq) < 2:
|
|
267
435
|
qb = [0]
|
|
436
|
+
lhv = -1
|
|
268
437
|
elif len(hq) < 4:
|
|
269
438
|
qb = [0, 1]
|
|
439
|
+
lhv = 2
|
|
270
440
|
else:
|
|
271
|
-
qb = [0, 1,
|
|
441
|
+
qb = [0, 1, 3, 4]
|
|
442
|
+
lhv = 2
|
|
443
|
+
|
|
444
|
+
return qb, lhv
|
|
445
|
+
|
|
446
|
+
def _get_lhv_bloch_angles(self, sim):
|
|
447
|
+
# Z axis
|
|
448
|
+
z = 1 - 2 * sim.prob(Pauli.PauliZ)
|
|
449
|
+
|
|
450
|
+
# X axis
|
|
451
|
+
x = 1 - 2 * sim.prob(Pauli.PauliX)
|
|
452
|
+
|
|
453
|
+
# Y axis
|
|
454
|
+
y = 1 - 2 * sim.prob(Pauli.PauliY)
|
|
272
455
|
|
|
273
|
-
|
|
456
|
+
inclination = math.atan2(math.sqrt(x**2 + y**2), z)
|
|
457
|
+
azimuth = math.atan2(y, x)
|
|
458
|
+
|
|
459
|
+
return azimuth, inclination
|
|
274
460
|
|
|
275
461
|
def _get_bloch_angles(self, hq):
|
|
276
462
|
sim = self.sim[hq[0]].clone()
|
|
@@ -297,7 +483,9 @@ class QrackAceBackend:
|
|
|
297
483
|
|
|
298
484
|
return azimuth, inclination
|
|
299
485
|
|
|
300
|
-
def _rotate_to_bloch(
|
|
486
|
+
def _rotate_to_bloch(
|
|
487
|
+
self, hq, delta_azimuth, delta_inclination
|
|
488
|
+
):
|
|
301
489
|
sim = self.sim[hq[0]]
|
|
302
490
|
q = hq[1]
|
|
303
491
|
|
|
@@ -314,96 +502,169 @@ class QrackAceBackend:
|
|
|
314
502
|
|
|
315
503
|
sim.mtrx([m00, m01, m10, m11], q)
|
|
316
504
|
|
|
505
|
+
|
|
506
|
+
def _rotate_lhv_to_bloch(
|
|
507
|
+
self, sim, delta_azimuth, delta_inclination
|
|
508
|
+
):
|
|
509
|
+
# Apply rotation as "Azimuth, Inclination" (AI)
|
|
510
|
+
cosA = math.cos(delta_azimuth)
|
|
511
|
+
sinA = math.sin(delta_azimuth)
|
|
512
|
+
cosI = math.cos(delta_inclination / 2)
|
|
513
|
+
sinI = math.sin(delta_inclination / 2)
|
|
514
|
+
|
|
515
|
+
m00 = complex(cosI, 0)
|
|
516
|
+
m01 = complex(-cosA, sinA) * sinI
|
|
517
|
+
m10 = complex(cosA, sinA) * sinI
|
|
518
|
+
m11 = complex(cosI, 0)
|
|
519
|
+
|
|
520
|
+
sim.mtrx([m00, m01, m10, m11])
|
|
521
|
+
|
|
522
|
+
|
|
317
523
|
def _correct(self, lq, phase=False, skip_rotation=False):
|
|
318
524
|
hq = self._unpack(lq)
|
|
319
525
|
|
|
320
526
|
if len(hq) == 1:
|
|
321
527
|
return
|
|
322
528
|
|
|
323
|
-
qb = self.
|
|
529
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
324
530
|
|
|
325
531
|
if phase:
|
|
326
532
|
for q in qb:
|
|
327
533
|
b = hq[q]
|
|
328
534
|
self.sim[b[0]].h(b[1])
|
|
535
|
+
b = hq[lhv]
|
|
536
|
+
b.h()
|
|
329
537
|
|
|
330
538
|
if len(hq) == 5:
|
|
331
539
|
# RMS
|
|
332
540
|
p = [
|
|
333
541
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
334
542
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
335
|
-
|
|
543
|
+
hq[2].prob(),
|
|
336
544
|
self.sim[hq[3][0]].prob(hq[3][1]),
|
|
545
|
+
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
337
546
|
]
|
|
338
547
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
339
|
-
prms = math.sqrt(
|
|
548
|
+
prms = math.sqrt(
|
|
549
|
+
(p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
|
|
550
|
+
)
|
|
340
551
|
qrms = math.sqrt(
|
|
341
|
-
(
|
|
342
|
-
|
|
552
|
+
(
|
|
553
|
+
(1 - p[0]) ** 2
|
|
554
|
+
+ (1 - p[1]) ** 2
|
|
555
|
+
+ 3 * ((1 - p[2]) ** 2)
|
|
556
|
+
+ (1 - p[3]) ** 2
|
|
557
|
+
+ (1 - p[4]) ** 2
|
|
558
|
+
)
|
|
559
|
+
/ 7
|
|
343
560
|
)
|
|
344
561
|
result = ((prms + (1 - qrms)) / 2) >= 0.5
|
|
345
562
|
syndrome = (
|
|
346
|
-
[1 - p[0], 1 - p[1], 1 - p[2], 1 - p[3]]
|
|
563
|
+
[1 - p[0], 1 - p[1], 1 - p[2], 1 - p[3], 1 - p[4]]
|
|
347
564
|
if result
|
|
348
|
-
else [p[0], p[1], p[2], p[3]]
|
|
565
|
+
else [p[0], p[1], p[2], p[3], p[4]]
|
|
349
566
|
)
|
|
350
|
-
for q in range(
|
|
567
|
+
for q in range(5):
|
|
351
568
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
352
|
-
|
|
569
|
+
if q == 2:
|
|
570
|
+
hq[q].x()
|
|
571
|
+
else:
|
|
572
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
353
573
|
|
|
354
574
|
if not skip_rotation:
|
|
355
575
|
a, i = [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]
|
|
356
576
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
357
577
|
a[1], i[1] = self._get_bloch_angles(hq[1])
|
|
358
|
-
a[2], i[2] = self.
|
|
359
|
-
a[3], i[3] = self._get_bloch_angles(hq[
|
|
578
|
+
a[2], i[2] = self._get_lhv_bloch_angles(hq[2])
|
|
579
|
+
a[3], i[3] = self._get_bloch_angles(hq[3])
|
|
580
|
+
a[4], i[4] = self._get_bloch_angles(hq[4])
|
|
360
581
|
|
|
361
582
|
a_target = 0
|
|
362
583
|
i_target = 0
|
|
363
|
-
for x in range(
|
|
584
|
+
for x in range(5):
|
|
585
|
+
if x == 2:
|
|
586
|
+
continue
|
|
364
587
|
a_target += a[x]
|
|
365
588
|
i_target += i[x]
|
|
366
589
|
|
|
367
|
-
a_target /=
|
|
368
|
-
i_target /=
|
|
369
|
-
for x in range(
|
|
370
|
-
|
|
590
|
+
a_target /= 5
|
|
591
|
+
i_target /= 5
|
|
592
|
+
for x in range(5):
|
|
593
|
+
if x == 2:
|
|
594
|
+
self._rotate_lhv_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
595
|
+
else:
|
|
596
|
+
self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
597
|
+
|
|
598
|
+
self.apply_magnetic_bias([lq], self.correction_bias)
|
|
371
599
|
|
|
372
600
|
else:
|
|
373
601
|
# RMS
|
|
374
602
|
p = [
|
|
375
603
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
376
604
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
605
|
+
hq[2].prob(),
|
|
377
606
|
]
|
|
378
607
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
379
|
-
prms = math.sqrt((p[0] ** 2 + p[1] ** 2) / 3)
|
|
380
|
-
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2) / 3)
|
|
608
|
+
prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
|
|
609
|
+
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
|
|
381
610
|
result = ((prms + (1 - qrms)) / 2) >= 0.5
|
|
382
|
-
syndrome = [1 - p[0], 1 - p[1]] if result else [p[0], p[1]]
|
|
383
|
-
for q in range(
|
|
611
|
+
syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
|
|
612
|
+
for q in range(3):
|
|
384
613
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
385
|
-
|
|
614
|
+
if q == 2:
|
|
615
|
+
hq[q].x()
|
|
616
|
+
else:
|
|
617
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
386
618
|
|
|
387
619
|
if not skip_rotation:
|
|
388
620
|
a, i = [0, 0, 0], [0, 0, 0]
|
|
389
621
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
390
622
|
a[1], i[1] = self._get_bloch_angles(hq[1])
|
|
623
|
+
a[2], i[2] = self._get_lhv_bloch_angles(hq[2])
|
|
391
624
|
|
|
392
625
|
a_target = 0
|
|
393
626
|
i_target = 0
|
|
394
|
-
for x in range(
|
|
627
|
+
for x in range(3):
|
|
628
|
+
if x == 2:
|
|
629
|
+
continue
|
|
395
630
|
a_target += a[x]
|
|
396
631
|
i_target += i[x]
|
|
397
632
|
|
|
398
|
-
a_target /=
|
|
399
|
-
i_target /=
|
|
400
|
-
for x in range(
|
|
401
|
-
|
|
633
|
+
a_target /= 3
|
|
634
|
+
i_target /= 3
|
|
635
|
+
for x in range(3):
|
|
636
|
+
if x == 2:
|
|
637
|
+
self._rotate_lhv_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
638
|
+
else:
|
|
639
|
+
self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
640
|
+
|
|
641
|
+
self.apply_magnetic_bias([lq], self.correction_bias)
|
|
402
642
|
|
|
403
643
|
if phase:
|
|
404
644
|
for q in qb:
|
|
405
645
|
b = hq[q]
|
|
406
646
|
self.sim[b[0]].h(b[1])
|
|
647
|
+
b = hq[lhv]
|
|
648
|
+
b.h()
|
|
649
|
+
|
|
650
|
+
def apply_magnetic_bias(self, q, b):
|
|
651
|
+
if b == 0:
|
|
652
|
+
return
|
|
653
|
+
b = math.exp(b)
|
|
654
|
+
for x in q:
|
|
655
|
+
hq = self._unpack(x)
|
|
656
|
+
for c in range(len(hq)):
|
|
657
|
+
h = hq[c]
|
|
658
|
+
if c == 2:
|
|
659
|
+
a, i = self._get_lhv_bloch_angles(h)
|
|
660
|
+
self._rotate_lhv_to_bloch(
|
|
661
|
+
h, math.atan(math.tan(a) * b) - a, math.atan(math.tan(i) * b) - i
|
|
662
|
+
)
|
|
663
|
+
else:
|
|
664
|
+
a, i = self._get_bloch_angles(h)
|
|
665
|
+
self._rotate_to_bloch(
|
|
666
|
+
h, math.atan(math.tan(a) * b) - a, math.atan(math.tan(i) * b) - i
|
|
667
|
+
)
|
|
407
668
|
|
|
408
669
|
def u(self, lq, th, ph, lm):
|
|
409
670
|
hq = self._unpack(lq)
|
|
@@ -412,12 +673,15 @@ class QrackAceBackend:
|
|
|
412
673
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
413
674
|
return
|
|
414
675
|
|
|
415
|
-
qb = self.
|
|
676
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
416
677
|
|
|
417
678
|
for q in qb:
|
|
418
679
|
b = hq[q]
|
|
419
680
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
420
681
|
|
|
682
|
+
b = hq[lhv]
|
|
683
|
+
b.u(th, ph, lm)
|
|
684
|
+
|
|
421
685
|
self._correct(lq, False, True)
|
|
422
686
|
self._correct(lq, True, False)
|
|
423
687
|
|
|
@@ -428,12 +692,20 @@ class QrackAceBackend:
|
|
|
428
692
|
self.sim[b[0]].r(p, th, b[1])
|
|
429
693
|
return
|
|
430
694
|
|
|
431
|
-
qb = self.
|
|
695
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
432
696
|
|
|
433
697
|
for q in qb:
|
|
434
698
|
b = hq[q]
|
|
435
699
|
self.sim[b[0]].r(p, th, b[1])
|
|
436
700
|
|
|
701
|
+
b = hq[lhv]
|
|
702
|
+
if p == Pauli.PauliX:
|
|
703
|
+
b.rx(th)
|
|
704
|
+
elif p == Pauli.PauliY:
|
|
705
|
+
b.ry(th)
|
|
706
|
+
elif p == Pauli.PauliZ:
|
|
707
|
+
b.rz(th)
|
|
708
|
+
|
|
437
709
|
if p != Pauli.PauliZ:
|
|
438
710
|
self._correct(lq, False, p != Pauli.PauliX)
|
|
439
711
|
if p != Pauli.PauliX:
|
|
@@ -448,12 +720,15 @@ class QrackAceBackend:
|
|
|
448
720
|
|
|
449
721
|
self._correct(lq)
|
|
450
722
|
|
|
451
|
-
qb = self.
|
|
723
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
452
724
|
|
|
453
725
|
for q in qb:
|
|
454
726
|
b = hq[q]
|
|
455
727
|
self.sim[b[0]].h(b[1])
|
|
456
728
|
|
|
729
|
+
b = hq[lhv]
|
|
730
|
+
b.h()
|
|
731
|
+
|
|
457
732
|
self._correct(lq)
|
|
458
733
|
|
|
459
734
|
def s(self, lq):
|
|
@@ -463,12 +738,15 @@ class QrackAceBackend:
|
|
|
463
738
|
self.sim[b[0]].s(b[1])
|
|
464
739
|
return
|
|
465
740
|
|
|
466
|
-
qb = self.
|
|
741
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
467
742
|
|
|
468
743
|
for q in qb:
|
|
469
744
|
b = hq[q]
|
|
470
745
|
self.sim[b[0]].s(b[1])
|
|
471
746
|
|
|
747
|
+
b = hq[lhv]
|
|
748
|
+
b.s()
|
|
749
|
+
|
|
472
750
|
def adjs(self, lq):
|
|
473
751
|
hq = self._unpack(lq)
|
|
474
752
|
if len(hq) < 2:
|
|
@@ -476,12 +754,15 @@ class QrackAceBackend:
|
|
|
476
754
|
self.sim[b[0]].adjs(b[1])
|
|
477
755
|
return
|
|
478
756
|
|
|
479
|
-
qb = self.
|
|
757
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
480
758
|
|
|
481
759
|
for q in qb:
|
|
482
760
|
b = hq[q]
|
|
483
761
|
self.sim[b[0]].adjs(b[1])
|
|
484
762
|
|
|
763
|
+
b = hq[lhv]
|
|
764
|
+
b.adjs()
|
|
765
|
+
|
|
485
766
|
def x(self, lq):
|
|
486
767
|
hq = self._unpack(lq)
|
|
487
768
|
if len(hq) < 2:
|
|
@@ -489,12 +770,15 @@ class QrackAceBackend:
|
|
|
489
770
|
self.sim[b[0]].x(b[1])
|
|
490
771
|
return
|
|
491
772
|
|
|
492
|
-
qb = self.
|
|
773
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
493
774
|
|
|
494
775
|
for q in qb:
|
|
495
776
|
b = hq[q]
|
|
496
777
|
self.sim[b[0]].x(b[1])
|
|
497
778
|
|
|
779
|
+
b = hq[lhv]
|
|
780
|
+
b.x()
|
|
781
|
+
|
|
498
782
|
def y(self, lq):
|
|
499
783
|
hq = self._unpack(lq)
|
|
500
784
|
if len(hq) < 2:
|
|
@@ -502,12 +786,15 @@ class QrackAceBackend:
|
|
|
502
786
|
self.sim[b[0]].y(b[1])
|
|
503
787
|
return
|
|
504
788
|
|
|
505
|
-
qb = self.
|
|
789
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
506
790
|
|
|
507
791
|
for q in qb:
|
|
508
792
|
b = hq[q]
|
|
509
793
|
self.sim[b[0]].y(b[1])
|
|
510
794
|
|
|
795
|
+
b = hq[lhv]
|
|
796
|
+
b.y()
|
|
797
|
+
|
|
511
798
|
def z(self, lq):
|
|
512
799
|
hq = self._unpack(lq)
|
|
513
800
|
if len(hq) < 2:
|
|
@@ -515,12 +802,15 @@ class QrackAceBackend:
|
|
|
515
802
|
self.sim[b[0]].z(b[1])
|
|
516
803
|
return
|
|
517
804
|
|
|
518
|
-
qb = self.
|
|
805
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
519
806
|
|
|
520
807
|
for q in qb:
|
|
521
808
|
b = hq[q]
|
|
522
809
|
self.sim[b[0]].z(b[1])
|
|
523
810
|
|
|
811
|
+
b = hq[lhv]
|
|
812
|
+
b.z()
|
|
813
|
+
|
|
524
814
|
def t(self, lq):
|
|
525
815
|
hq = self._unpack(lq)
|
|
526
816
|
if len(hq) < 2:
|
|
@@ -528,12 +818,15 @@ class QrackAceBackend:
|
|
|
528
818
|
self.sim[b[0]].t(b[1])
|
|
529
819
|
return
|
|
530
820
|
|
|
531
|
-
qb = self.
|
|
821
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
532
822
|
|
|
533
823
|
for q in qb:
|
|
534
824
|
b = hq[q]
|
|
535
825
|
self.sim[b[0]].t(b[1])
|
|
536
826
|
|
|
827
|
+
b = hq[lhv]
|
|
828
|
+
b.t()
|
|
829
|
+
|
|
537
830
|
def adjt(self, lq):
|
|
538
831
|
hq = self._unpack(lq)
|
|
539
832
|
if len(hq) < 2:
|
|
@@ -541,12 +834,15 @@ class QrackAceBackend:
|
|
|
541
834
|
self.sim[b[0]].adjt(b[1])
|
|
542
835
|
return
|
|
543
836
|
|
|
544
|
-
qb = self.
|
|
837
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
545
838
|
|
|
546
839
|
for q in qb:
|
|
547
840
|
b = hq[q]
|
|
548
841
|
self.sim[b[0]].adjt(b[1])
|
|
549
842
|
|
|
843
|
+
b = hq[lhv]
|
|
844
|
+
b.adjt()
|
|
845
|
+
|
|
550
846
|
def _get_gate(self, pauli, anti, sim_id):
|
|
551
847
|
gate = None
|
|
552
848
|
shadow = None
|
|
@@ -587,11 +883,15 @@ class QrackAceBackend:
|
|
|
587
883
|
|
|
588
884
|
return connected, boundary
|
|
589
885
|
|
|
590
|
-
def _apply_coupling(self, pauli, anti, qb1, hq1, qb2, hq2, lq1_lr):
|
|
886
|
+
def _apply_coupling(self, pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr):
|
|
591
887
|
for q1 in qb1:
|
|
888
|
+
if q1 == lhv1:
|
|
889
|
+
continue
|
|
592
890
|
b1 = hq1[q1]
|
|
593
891
|
gate_fn, shadow_fn = self._get_gate(pauli, anti, b1[0])
|
|
594
892
|
for q2 in qb2:
|
|
893
|
+
if q2 == lhv2:
|
|
894
|
+
continue
|
|
595
895
|
b2 = hq2[q2]
|
|
596
896
|
if b1[0] == b2[0]:
|
|
597
897
|
gate_fn([b1[1]], b2[1])
|
|
@@ -616,10 +916,18 @@ class QrackAceBackend:
|
|
|
616
916
|
|
|
617
917
|
self._correct(lq1)
|
|
618
918
|
|
|
619
|
-
qb1 = self.
|
|
620
|
-
qb2 = self.
|
|
919
|
+
qb1, lhv1 = self._get_qb_lhv_indices(hq1)
|
|
920
|
+
qb2, lhv2 = self._get_qb_lhv_indices(hq2)
|
|
621
921
|
# Apply cross coupling on hardware qubits first
|
|
622
|
-
self._apply_coupling(pauli, anti, qb1, hq1, qb2, hq2, lq1_lr)
|
|
922
|
+
self._apply_coupling(pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr)
|
|
923
|
+
# Apply coupling to the local-hidden-variable target
|
|
924
|
+
if lhv2 >= 0:
|
|
925
|
+
_cpauli_lhv(
|
|
926
|
+
hq1[lhv1].prob() if lhv1 >= 0 else self.sim[hq1[0][0]].prob(hq1[0][1]),
|
|
927
|
+
hq2[lhv2],
|
|
928
|
+
pauli,
|
|
929
|
+
anti,
|
|
930
|
+
)
|
|
623
931
|
|
|
624
932
|
self._correct(lq1, True)
|
|
625
933
|
if pauli != Pauli.PauliZ:
|
|
@@ -721,20 +1029,29 @@ class QrackAceBackend:
|
|
|
721
1029
|
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
722
1030
|
]
|
|
723
1031
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
724
|
-
prms = math.sqrt(
|
|
1032
|
+
prms = math.sqrt(
|
|
1033
|
+
(p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
|
|
1034
|
+
)
|
|
725
1035
|
qrms = math.sqrt(
|
|
726
|
-
(
|
|
727
|
-
|
|
1036
|
+
(
|
|
1037
|
+
(1 - p[0]) ** 2
|
|
1038
|
+
+ (1 - p[1]) ** 2
|
|
1039
|
+
+ 3 * ((1 - p[2]) ** 2)
|
|
1040
|
+
+ (1 - p[3]) ** 2
|
|
1041
|
+
+ (1 - p[4]) ** 2
|
|
1042
|
+
)
|
|
1043
|
+
/ 7
|
|
728
1044
|
)
|
|
729
1045
|
else:
|
|
730
1046
|
# RMS
|
|
731
1047
|
p = [
|
|
732
1048
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
733
1049
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
1050
|
+
hq[2].prob(),
|
|
734
1051
|
]
|
|
735
1052
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
736
|
-
prms = math.sqrt((p[0] ** 2 + p[1] ** 2) / 3)
|
|
737
|
-
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2) / 3)
|
|
1053
|
+
prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
|
|
1054
|
+
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
|
|
738
1055
|
|
|
739
1056
|
return (prms + (1 - qrms)) / 2
|
|
740
1057
|
|
|
@@ -747,7 +1064,7 @@ class QrackAceBackend:
|
|
|
747
1064
|
p = self.prob(lq)
|
|
748
1065
|
result = ((p + self._epsilon) >= 1) or (random.random() < p)
|
|
749
1066
|
|
|
750
|
-
qb = self.
|
|
1067
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
751
1068
|
|
|
752
1069
|
for q in qb:
|
|
753
1070
|
b = hq[q]
|
|
@@ -758,6 +1075,11 @@ class QrackAceBackend:
|
|
|
758
1075
|
else:
|
|
759
1076
|
self.sim[b[0]].force_m(b[1], result)
|
|
760
1077
|
|
|
1078
|
+
b = hq[lhv]
|
|
1079
|
+
b.reset()
|
|
1080
|
+
if result:
|
|
1081
|
+
b.x()
|
|
1082
|
+
|
|
761
1083
|
return result
|
|
762
1084
|
|
|
763
1085
|
def force_m(self, lq, result):
|
|
@@ -768,7 +1090,7 @@ class QrackAceBackend:
|
|
|
768
1090
|
|
|
769
1091
|
self._correct(lq)
|
|
770
1092
|
|
|
771
|
-
qb = self.
|
|
1093
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
772
1094
|
|
|
773
1095
|
for q in qb:
|
|
774
1096
|
b = hq[q]
|
|
@@ -779,6 +1101,11 @@ class QrackAceBackend:
|
|
|
779
1101
|
else:
|
|
780
1102
|
self.sim[b[0]].force_m(b[1], result)
|
|
781
1103
|
|
|
1104
|
+
b = hq[1]
|
|
1105
|
+
b.reset()
|
|
1106
|
+
if result:
|
|
1107
|
+
b.x()
|
|
1108
|
+
|
|
782
1109
|
return c
|
|
783
1110
|
|
|
784
1111
|
def m_all(self):
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
pyqrack/__init__.py,sha256=3tBwfCCD-zQjQ2g1EUZdggKdn-3b2uSFTbT7LS0YLaU,785
|
|
2
2
|
pyqrack/neuron_activation_fn.py,sha256=fQTTFfsvwcot_43Vopacot47IV2Rxk8pelUyuzwpXPs,593
|
|
3
3
|
pyqrack/pauli.py,sha256=wg500wDOwdIU4lEVJoMmjtbAdmtakZYzLPjdzC2rwUQ,654
|
|
4
|
-
pyqrack/qrack_ace_backend.py,sha256=
|
|
4
|
+
pyqrack/qrack_ace_backend.py,sha256=l8-Hn-Idcdv4DLuxrrxFS2pJvzcApT3qDxLTN8eBOes,49450
|
|
5
5
|
pyqrack/qrack_circuit.py,sha256=vDCKGbcEHJDFUKprjCpWgit8lXFnMrPimKHURD2_Hj4,19538
|
|
6
6
|
pyqrack/qrack_neuron.py,sha256=UiJdjAGB6usjAGHWSosSFCUUeIkhh3MtZbsaxfsIsNw,9043
|
|
7
7
|
pyqrack/qrack_neuron_torch_layer.py,sha256=Bs5BLC2GFevfSpo_jSJ2AZl-hfDRJmzlGN9pFw1CtoQ,6160
|
|
@@ -16,8 +16,8 @@ pyqrack/qrack_system/qrack_lib/libqrack_pinvoke.so.9.22.0,sha256=uFHqafF8aaXe5tH
|
|
|
16
16
|
pyqrack/stats/__init__.py,sha256=Hla85my2fY_roR9lIjGBVpEG7ySOTMwjWa8D6-kgCnY,276
|
|
17
17
|
pyqrack/stats/load_quantized_data.py,sha256=z12u9F7Nt3P-i44nY1xxvso_klS6WIHS3iqq7R2_lqE,1184
|
|
18
18
|
pyqrack/stats/quantize_by_range.py,sha256=UM0_7jJDdQ7g30cR3UQAxkbzkqrmsy1oUfqg0h11FUY,2270
|
|
19
|
-
pyqrack_cpu-1.
|
|
20
|
-
pyqrack_cpu-1.
|
|
21
|
-
pyqrack_cpu-1.
|
|
22
|
-
pyqrack_cpu-1.
|
|
23
|
-
pyqrack_cpu-1.
|
|
19
|
+
pyqrack_cpu-1.63.0.dist-info/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
|
|
20
|
+
pyqrack_cpu-1.63.0.dist-info/METADATA,sha256=yXeMzjydVCv_3MBmGNOW5v6sQihfjeNhhbVtyOb23Cw,5971
|
|
21
|
+
pyqrack_cpu-1.63.0.dist-info/WHEEL,sha256=AMMNaGlKLEICDqgnxZojk7k8N6wUjQQ3X9tPjxJ2sOc,110
|
|
22
|
+
pyqrack_cpu-1.63.0.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
|
|
23
|
+
pyqrack_cpu-1.63.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|