pyqrack-cpu 1.62.0__py3-none-macosx_13_0_x86_64.whl → 1.63.0__py3-none-macosx_13_0_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.
- pyqrack/qrack_ace_backend.py +368 -58
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.63.0.dist-info}/METADATA +1 -1
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.63.0.dist-info}/RECORD +6 -6
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.63.0.dist-info}/LICENSE +0 -0
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.63.0.dist-info}/WHEEL +0 -0
- {pyqrack_cpu-1.62.0.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
|
|
|
@@ -122,6 +279,7 @@ class QrackAceBackend:
|
|
|
122
279
|
self._qubits = []
|
|
123
280
|
sim_counts = [0] * sim_count
|
|
124
281
|
sim_id = 0
|
|
282
|
+
tot_qubits = 0
|
|
125
283
|
for r in self._is_row_long_range:
|
|
126
284
|
for c in self._is_col_long_range:
|
|
127
285
|
qubit = [(sim_id, sim_counts[sim_id])]
|
|
@@ -132,6 +290,14 @@ class QrackAceBackend:
|
|
|
132
290
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
133
291
|
sim_counts[t_sim_id] += 1
|
|
134
292
|
|
|
293
|
+
qubit.append(
|
|
294
|
+
LHVQubit(
|
|
295
|
+
toClone=(
|
|
296
|
+
toClone._qubits[tot_qubits][2] if toClone else None
|
|
297
|
+
)
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
135
301
|
if (not c) and (not r):
|
|
136
302
|
t_sim_id = (sim_id + col_patch_count) % sim_count
|
|
137
303
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
@@ -145,6 +311,7 @@ class QrackAceBackend:
|
|
|
145
311
|
sim_id = (sim_id + 1) % sim_count
|
|
146
312
|
|
|
147
313
|
self._qubits.append(qubit)
|
|
314
|
+
tot_qubits += 1
|
|
148
315
|
|
|
149
316
|
self.sim = []
|
|
150
317
|
for i in range(sim_count):
|
|
@@ -262,16 +429,34 @@ class QrackAceBackend:
|
|
|
262
429
|
def _unpack(self, lq):
|
|
263
430
|
return self._qubits[lq]
|
|
264
431
|
|
|
265
|
-
def
|
|
432
|
+
def _get_qb_lhv_indices(self, hq):
|
|
266
433
|
qb = []
|
|
267
434
|
if len(hq) < 2:
|
|
268
435
|
qb = [0]
|
|
436
|
+
lhv = -1
|
|
269
437
|
elif len(hq) < 4:
|
|
270
438
|
qb = [0, 1]
|
|
439
|
+
lhv = 2
|
|
271
440
|
else:
|
|
272
|
-
qb = [0, 1,
|
|
441
|
+
qb = [0, 1, 3, 4]
|
|
442
|
+
lhv = 2
|
|
273
443
|
|
|
274
|
-
return qb
|
|
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)
|
|
455
|
+
|
|
456
|
+
inclination = math.atan2(math.sqrt(x**2 + y**2), z)
|
|
457
|
+
azimuth = math.atan2(y, x)
|
|
458
|
+
|
|
459
|
+
return azimuth, inclination
|
|
275
460
|
|
|
276
461
|
def _get_bloch_angles(self, hq):
|
|
277
462
|
sim = self.sim[hq[0]].clone()
|
|
@@ -298,7 +483,9 @@ class QrackAceBackend:
|
|
|
298
483
|
|
|
299
484
|
return azimuth, inclination
|
|
300
485
|
|
|
301
|
-
def _rotate_to_bloch(
|
|
486
|
+
def _rotate_to_bloch(
|
|
487
|
+
self, hq, delta_azimuth, delta_inclination
|
|
488
|
+
):
|
|
302
489
|
sim = self.sim[hq[0]]
|
|
303
490
|
q = hq[1]
|
|
304
491
|
|
|
@@ -315,60 +502,98 @@ class QrackAceBackend:
|
|
|
315
502
|
|
|
316
503
|
sim.mtrx([m00, m01, m10, m11], q)
|
|
317
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
|
+
|
|
318
523
|
def _correct(self, lq, phase=False, skip_rotation=False):
|
|
319
524
|
hq = self._unpack(lq)
|
|
320
525
|
|
|
321
526
|
if len(hq) == 1:
|
|
322
527
|
return
|
|
323
528
|
|
|
324
|
-
qb = self.
|
|
529
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
325
530
|
|
|
326
531
|
if phase:
|
|
327
532
|
for q in qb:
|
|
328
533
|
b = hq[q]
|
|
329
534
|
self.sim[b[0]].h(b[1])
|
|
535
|
+
b = hq[lhv]
|
|
536
|
+
b.h()
|
|
330
537
|
|
|
331
538
|
if len(hq) == 5:
|
|
332
539
|
# RMS
|
|
333
540
|
p = [
|
|
334
541
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
335
542
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
336
|
-
|
|
543
|
+
hq[2].prob(),
|
|
337
544
|
self.sim[hq[3][0]].prob(hq[3][1]),
|
|
545
|
+
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
338
546
|
]
|
|
339
547
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
340
|
-
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
|
+
)
|
|
341
551
|
qrms = math.sqrt(
|
|
342
|
-
(
|
|
343
|
-
|
|
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
|
|
344
560
|
)
|
|
345
561
|
result = ((prms + (1 - qrms)) / 2) >= 0.5
|
|
346
562
|
syndrome = (
|
|
347
|
-
[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]]
|
|
348
564
|
if result
|
|
349
|
-
else [p[0], p[1], p[2], p[3]]
|
|
565
|
+
else [p[0], p[1], p[2], p[3], p[4]]
|
|
350
566
|
)
|
|
351
|
-
for q in range(
|
|
567
|
+
for q in range(5):
|
|
352
568
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
353
|
-
|
|
569
|
+
if q == 2:
|
|
570
|
+
hq[q].x()
|
|
571
|
+
else:
|
|
572
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
354
573
|
|
|
355
574
|
if not skip_rotation:
|
|
356
|
-
a, i = [0, 0, 0, 0], [0, 0, 0, 0]
|
|
575
|
+
a, i = [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]
|
|
357
576
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
358
577
|
a[1], i[1] = self._get_bloch_angles(hq[1])
|
|
359
|
-
a[2], i[2] = self.
|
|
360
|
-
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])
|
|
361
581
|
|
|
362
582
|
a_target = 0
|
|
363
583
|
i_target = 0
|
|
364
|
-
for x in range(
|
|
584
|
+
for x in range(5):
|
|
585
|
+
if x == 2:
|
|
586
|
+
continue
|
|
365
587
|
a_target += a[x]
|
|
366
588
|
i_target += i[x]
|
|
367
589
|
|
|
368
|
-
a_target /=
|
|
369
|
-
i_target /=
|
|
370
|
-
for x in range(
|
|
371
|
-
|
|
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])
|
|
372
597
|
|
|
373
598
|
self.apply_magnetic_bias([lq], self.correction_bias)
|
|
374
599
|
|
|
@@ -377,31 +602,41 @@ class QrackAceBackend:
|
|
|
377
602
|
p = [
|
|
378
603
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
379
604
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
605
|
+
hq[2].prob(),
|
|
380
606
|
]
|
|
381
607
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
382
|
-
prms = math.sqrt((p[0] ** 2 + p[1] ** 2) / 3)
|
|
383
|
-
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)
|
|
384
610
|
result = ((prms + (1 - qrms)) / 2) >= 0.5
|
|
385
|
-
syndrome = [1 - p[0], 1 - p[1]] if result else [p[0], p[1]]
|
|
386
|
-
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):
|
|
387
613
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
388
|
-
|
|
614
|
+
if q == 2:
|
|
615
|
+
hq[q].x()
|
|
616
|
+
else:
|
|
617
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
389
618
|
|
|
390
619
|
if not skip_rotation:
|
|
391
|
-
a, i = [0, 0], [0, 0]
|
|
620
|
+
a, i = [0, 0, 0], [0, 0, 0]
|
|
392
621
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
393
622
|
a[1], i[1] = self._get_bloch_angles(hq[1])
|
|
623
|
+
a[2], i[2] = self._get_lhv_bloch_angles(hq[2])
|
|
394
624
|
|
|
395
625
|
a_target = 0
|
|
396
626
|
i_target = 0
|
|
397
|
-
for x in range(
|
|
627
|
+
for x in range(3):
|
|
628
|
+
if x == 2:
|
|
629
|
+
continue
|
|
398
630
|
a_target += a[x]
|
|
399
631
|
i_target += i[x]
|
|
400
632
|
|
|
401
|
-
a_target /=
|
|
402
|
-
i_target /=
|
|
403
|
-
for x in range(
|
|
404
|
-
|
|
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])
|
|
405
640
|
|
|
406
641
|
self.apply_magnetic_bias([lq], self.correction_bias)
|
|
407
642
|
|
|
@@ -409,6 +644,8 @@ class QrackAceBackend:
|
|
|
409
644
|
for q in qb:
|
|
410
645
|
b = hq[q]
|
|
411
646
|
self.sim[b[0]].h(b[1])
|
|
647
|
+
b = hq[lhv]
|
|
648
|
+
b.h()
|
|
412
649
|
|
|
413
650
|
def apply_magnetic_bias(self, q, b):
|
|
414
651
|
if b == 0:
|
|
@@ -416,11 +653,18 @@ class QrackAceBackend:
|
|
|
416
653
|
b = math.exp(b)
|
|
417
654
|
for x in q:
|
|
418
655
|
hq = self._unpack(x)
|
|
419
|
-
for
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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
|
+
)
|
|
424
668
|
|
|
425
669
|
def u(self, lq, th, ph, lm):
|
|
426
670
|
hq = self._unpack(lq)
|
|
@@ -429,12 +673,15 @@ class QrackAceBackend:
|
|
|
429
673
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
430
674
|
return
|
|
431
675
|
|
|
432
|
-
qb = self.
|
|
676
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
433
677
|
|
|
434
678
|
for q in qb:
|
|
435
679
|
b = hq[q]
|
|
436
680
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
437
681
|
|
|
682
|
+
b = hq[lhv]
|
|
683
|
+
b.u(th, ph, lm)
|
|
684
|
+
|
|
438
685
|
self._correct(lq, False, True)
|
|
439
686
|
self._correct(lq, True, False)
|
|
440
687
|
|
|
@@ -445,12 +692,20 @@ class QrackAceBackend:
|
|
|
445
692
|
self.sim[b[0]].r(p, th, b[1])
|
|
446
693
|
return
|
|
447
694
|
|
|
448
|
-
qb = self.
|
|
695
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
449
696
|
|
|
450
697
|
for q in qb:
|
|
451
698
|
b = hq[q]
|
|
452
699
|
self.sim[b[0]].r(p, th, b[1])
|
|
453
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
|
+
|
|
454
709
|
if p != Pauli.PauliZ:
|
|
455
710
|
self._correct(lq, False, p != Pauli.PauliX)
|
|
456
711
|
if p != Pauli.PauliX:
|
|
@@ -465,12 +720,15 @@ class QrackAceBackend:
|
|
|
465
720
|
|
|
466
721
|
self._correct(lq)
|
|
467
722
|
|
|
468
|
-
qb = self.
|
|
723
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
469
724
|
|
|
470
725
|
for q in qb:
|
|
471
726
|
b = hq[q]
|
|
472
727
|
self.sim[b[0]].h(b[1])
|
|
473
728
|
|
|
729
|
+
b = hq[lhv]
|
|
730
|
+
b.h()
|
|
731
|
+
|
|
474
732
|
self._correct(lq)
|
|
475
733
|
|
|
476
734
|
def s(self, lq):
|
|
@@ -480,12 +738,15 @@ class QrackAceBackend:
|
|
|
480
738
|
self.sim[b[0]].s(b[1])
|
|
481
739
|
return
|
|
482
740
|
|
|
483
|
-
qb = self.
|
|
741
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
484
742
|
|
|
485
743
|
for q in qb:
|
|
486
744
|
b = hq[q]
|
|
487
745
|
self.sim[b[0]].s(b[1])
|
|
488
746
|
|
|
747
|
+
b = hq[lhv]
|
|
748
|
+
b.s()
|
|
749
|
+
|
|
489
750
|
def adjs(self, lq):
|
|
490
751
|
hq = self._unpack(lq)
|
|
491
752
|
if len(hq) < 2:
|
|
@@ -493,12 +754,15 @@ class QrackAceBackend:
|
|
|
493
754
|
self.sim[b[0]].adjs(b[1])
|
|
494
755
|
return
|
|
495
756
|
|
|
496
|
-
qb = self.
|
|
757
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
497
758
|
|
|
498
759
|
for q in qb:
|
|
499
760
|
b = hq[q]
|
|
500
761
|
self.sim[b[0]].adjs(b[1])
|
|
501
762
|
|
|
763
|
+
b = hq[lhv]
|
|
764
|
+
b.adjs()
|
|
765
|
+
|
|
502
766
|
def x(self, lq):
|
|
503
767
|
hq = self._unpack(lq)
|
|
504
768
|
if len(hq) < 2:
|
|
@@ -506,12 +770,15 @@ class QrackAceBackend:
|
|
|
506
770
|
self.sim[b[0]].x(b[1])
|
|
507
771
|
return
|
|
508
772
|
|
|
509
|
-
qb = self.
|
|
773
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
510
774
|
|
|
511
775
|
for q in qb:
|
|
512
776
|
b = hq[q]
|
|
513
777
|
self.sim[b[0]].x(b[1])
|
|
514
778
|
|
|
779
|
+
b = hq[lhv]
|
|
780
|
+
b.x()
|
|
781
|
+
|
|
515
782
|
def y(self, lq):
|
|
516
783
|
hq = self._unpack(lq)
|
|
517
784
|
if len(hq) < 2:
|
|
@@ -519,12 +786,15 @@ class QrackAceBackend:
|
|
|
519
786
|
self.sim[b[0]].y(b[1])
|
|
520
787
|
return
|
|
521
788
|
|
|
522
|
-
qb = self.
|
|
789
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
523
790
|
|
|
524
791
|
for q in qb:
|
|
525
792
|
b = hq[q]
|
|
526
793
|
self.sim[b[0]].y(b[1])
|
|
527
794
|
|
|
795
|
+
b = hq[lhv]
|
|
796
|
+
b.y()
|
|
797
|
+
|
|
528
798
|
def z(self, lq):
|
|
529
799
|
hq = self._unpack(lq)
|
|
530
800
|
if len(hq) < 2:
|
|
@@ -532,12 +802,15 @@ class QrackAceBackend:
|
|
|
532
802
|
self.sim[b[0]].z(b[1])
|
|
533
803
|
return
|
|
534
804
|
|
|
535
|
-
qb = self.
|
|
805
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
536
806
|
|
|
537
807
|
for q in qb:
|
|
538
808
|
b = hq[q]
|
|
539
809
|
self.sim[b[0]].z(b[1])
|
|
540
810
|
|
|
811
|
+
b = hq[lhv]
|
|
812
|
+
b.z()
|
|
813
|
+
|
|
541
814
|
def t(self, lq):
|
|
542
815
|
hq = self._unpack(lq)
|
|
543
816
|
if len(hq) < 2:
|
|
@@ -545,12 +818,15 @@ class QrackAceBackend:
|
|
|
545
818
|
self.sim[b[0]].t(b[1])
|
|
546
819
|
return
|
|
547
820
|
|
|
548
|
-
qb = self.
|
|
821
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
549
822
|
|
|
550
823
|
for q in qb:
|
|
551
824
|
b = hq[q]
|
|
552
825
|
self.sim[b[0]].t(b[1])
|
|
553
826
|
|
|
827
|
+
b = hq[lhv]
|
|
828
|
+
b.t()
|
|
829
|
+
|
|
554
830
|
def adjt(self, lq):
|
|
555
831
|
hq = self._unpack(lq)
|
|
556
832
|
if len(hq) < 2:
|
|
@@ -558,12 +834,15 @@ class QrackAceBackend:
|
|
|
558
834
|
self.sim[b[0]].adjt(b[1])
|
|
559
835
|
return
|
|
560
836
|
|
|
561
|
-
qb = self.
|
|
837
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
562
838
|
|
|
563
839
|
for q in qb:
|
|
564
840
|
b = hq[q]
|
|
565
841
|
self.sim[b[0]].adjt(b[1])
|
|
566
842
|
|
|
843
|
+
b = hq[lhv]
|
|
844
|
+
b.adjt()
|
|
845
|
+
|
|
567
846
|
def _get_gate(self, pauli, anti, sim_id):
|
|
568
847
|
gate = None
|
|
569
848
|
shadow = None
|
|
@@ -604,11 +883,15 @@ class QrackAceBackend:
|
|
|
604
883
|
|
|
605
884
|
return connected, boundary
|
|
606
885
|
|
|
607
|
-
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):
|
|
608
887
|
for q1 in qb1:
|
|
888
|
+
if q1 == lhv1:
|
|
889
|
+
continue
|
|
609
890
|
b1 = hq1[q1]
|
|
610
891
|
gate_fn, shadow_fn = self._get_gate(pauli, anti, b1[0])
|
|
611
892
|
for q2 in qb2:
|
|
893
|
+
if q2 == lhv2:
|
|
894
|
+
continue
|
|
612
895
|
b2 = hq2[q2]
|
|
613
896
|
if b1[0] == b2[0]:
|
|
614
897
|
gate_fn([b1[1]], b2[1])
|
|
@@ -633,10 +916,18 @@ class QrackAceBackend:
|
|
|
633
916
|
|
|
634
917
|
self._correct(lq1)
|
|
635
918
|
|
|
636
|
-
qb1 = self.
|
|
637
|
-
qb2 = self.
|
|
919
|
+
qb1, lhv1 = self._get_qb_lhv_indices(hq1)
|
|
920
|
+
qb2, lhv2 = self._get_qb_lhv_indices(hq2)
|
|
638
921
|
# Apply cross coupling on hardware qubits first
|
|
639
|
-
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
|
+
)
|
|
640
931
|
|
|
641
932
|
self._correct(lq1, True)
|
|
642
933
|
if pauli != Pauli.PauliZ:
|
|
@@ -738,20 +1029,29 @@ class QrackAceBackend:
|
|
|
738
1029
|
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
739
1030
|
]
|
|
740
1031
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
741
|
-
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
|
+
)
|
|
742
1035
|
qrms = math.sqrt(
|
|
743
|
-
(
|
|
744
|
-
|
|
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
|
|
745
1044
|
)
|
|
746
1045
|
else:
|
|
747
1046
|
# RMS
|
|
748
1047
|
p = [
|
|
749
1048
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
750
1049
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
1050
|
+
hq[2].prob(),
|
|
751
1051
|
]
|
|
752
1052
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
753
|
-
prms = math.sqrt((p[0] ** 2 + p[1] ** 2) / 3)
|
|
754
|
-
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)
|
|
755
1055
|
|
|
756
1056
|
return (prms + (1 - qrms)) / 2
|
|
757
1057
|
|
|
@@ -764,7 +1064,7 @@ class QrackAceBackend:
|
|
|
764
1064
|
p = self.prob(lq)
|
|
765
1065
|
result = ((p + self._epsilon) >= 1) or (random.random() < p)
|
|
766
1066
|
|
|
767
|
-
qb = self.
|
|
1067
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
768
1068
|
|
|
769
1069
|
for q in qb:
|
|
770
1070
|
b = hq[q]
|
|
@@ -775,6 +1075,11 @@ class QrackAceBackend:
|
|
|
775
1075
|
else:
|
|
776
1076
|
self.sim[b[0]].force_m(b[1], result)
|
|
777
1077
|
|
|
1078
|
+
b = hq[lhv]
|
|
1079
|
+
b.reset()
|
|
1080
|
+
if result:
|
|
1081
|
+
b.x()
|
|
1082
|
+
|
|
778
1083
|
return result
|
|
779
1084
|
|
|
780
1085
|
def force_m(self, lq, result):
|
|
@@ -785,7 +1090,7 @@ class QrackAceBackend:
|
|
|
785
1090
|
|
|
786
1091
|
self._correct(lq)
|
|
787
1092
|
|
|
788
|
-
qb = self.
|
|
1093
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
789
1094
|
|
|
790
1095
|
for q in qb:
|
|
791
1096
|
b = hq[q]
|
|
@@ -796,6 +1101,11 @@ class QrackAceBackend:
|
|
|
796
1101
|
else:
|
|
797
1102
|
self.sim[b[0]].force_m(b[1], result)
|
|
798
1103
|
|
|
1104
|
+
b = hq[1]
|
|
1105
|
+
b.reset()
|
|
1106
|
+
if result:
|
|
1107
|
+
b.x()
|
|
1108
|
+
|
|
799
1109
|
return c
|
|
800
1110
|
|
|
801
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.dylib,sha256=Cdb_wa2cG_V5lzswBH4
|
|
|
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=YHK3aHrd21RTX1p1pt-uiV5KvB3bHD9czcpkMtYUhIc,5951
|
|
21
|
+
pyqrack_cpu-1.63.0.dist-info/WHEEL,sha256=nZx8s83OrgdDmpcWX-8FgI0sEAjdQimt4SdYsdcCaC8,107
|
|
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
|