pyqrack-cpu 1.62.0__py3-none-manylinux_2_35_x86_64.whl → 1.64.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 +375 -63
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.64.0.dist-info}/METADATA +1 -1
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.64.0.dist-info}/RECORD +6 -6
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.64.0.dist-info}/LICENSE +0 -0
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.64.0.dist-info}/WHEEL +0 -0
- {pyqrack_cpu-1.62.0.dist-info → pyqrack_cpu-1.64.0.dist-info}/top_level.txt +0 -0
pyqrack/qrack_ace_backend.py
CHANGED
|
@@ -27,6 +27,169 @@ 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
|
+
p = math.sqrt(new_x**2 + new_y**2 + new_z**2)
|
|
143
|
+
|
|
144
|
+
new_x /= p
|
|
145
|
+
new_y /= p
|
|
146
|
+
new_z /= p
|
|
147
|
+
|
|
148
|
+
self.bloch = [new_x, new_y, new_z]
|
|
149
|
+
|
|
150
|
+
def prob(self, basis=Pauli.PauliZ):
|
|
151
|
+
"""Sample a classical outcome from the current 'quantum' state"""
|
|
152
|
+
if basis == Pauli.PauliZ:
|
|
153
|
+
prob_1 = (1 - self.bloch[2]) / 2
|
|
154
|
+
elif basis == Pauli.PauliX:
|
|
155
|
+
prob_1 = (1 - self.bloch[0]) / 2
|
|
156
|
+
elif basis == Pauli.PauliY:
|
|
157
|
+
prob_1 = (1 - self.bloch[1]) / 2
|
|
158
|
+
else:
|
|
159
|
+
raise ValueError(f"Unsupported basis: {basis}")
|
|
160
|
+
return prob_1
|
|
161
|
+
|
|
162
|
+
def m(self):
|
|
163
|
+
result = random.random() < self.prob()
|
|
164
|
+
self.reset()
|
|
165
|
+
if result:
|
|
166
|
+
self.x()
|
|
167
|
+
return result
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# Provided by Elara (the custom OpenAI GPT)
|
|
171
|
+
def _cpauli_lhv(prob, targ, axis, anti, theta=math.pi):
|
|
172
|
+
"""
|
|
173
|
+
Apply a 'soft' controlled-Pauli gate: rotate target qubit
|
|
174
|
+
proportionally to control's Z expectation value.
|
|
175
|
+
|
|
176
|
+
theta: full rotation angle if control in |1⟩
|
|
177
|
+
"""
|
|
178
|
+
# Control influence is (1 - ctrl.bloch[2]) / 2 = P(|1⟩)
|
|
179
|
+
# BUT we avoid collapse by using the expectation value:
|
|
180
|
+
control_influence = (1 - prob) if anti else prob
|
|
181
|
+
|
|
182
|
+
effective_theta = control_influence * theta
|
|
183
|
+
|
|
184
|
+
# Apply partial rotation to target qubit:
|
|
185
|
+
if axis == Pauli.PauliX:
|
|
186
|
+
targ.rx(effective_theta)
|
|
187
|
+
elif axis == Pauli.PauliY:
|
|
188
|
+
targ.ry(effective_theta)
|
|
189
|
+
elif axis == Pauli.PauliZ:
|
|
190
|
+
targ.rz(effective_theta)
|
|
191
|
+
|
|
192
|
+
|
|
30
193
|
class QrackAceBackend:
|
|
31
194
|
"""A back end for elided quantum error correction
|
|
32
195
|
|
|
@@ -45,7 +208,6 @@ class QrackAceBackend:
|
|
|
45
208
|
sim(QrackSimulator): Array of simulators corresponding to "patches" between boundary rows.
|
|
46
209
|
long_range_columns(int): How many ideal rows between QEC boundary rows?
|
|
47
210
|
is_transpose(bool): Rows are long if False, columns are long if True
|
|
48
|
-
correction_bias(float): Bias magnitude and direction during pseudo-QEC
|
|
49
211
|
"""
|
|
50
212
|
|
|
51
213
|
def __init__(
|
|
@@ -54,7 +216,6 @@ class QrackAceBackend:
|
|
|
54
216
|
long_range_columns=4,
|
|
55
217
|
long_range_rows=4,
|
|
56
218
|
is_transpose=False,
|
|
57
|
-
correction_bias=0,
|
|
58
219
|
isTensorNetwork=False,
|
|
59
220
|
isSchmidtDecomposeMulti=False,
|
|
60
221
|
isSchmidtDecompose=True,
|
|
@@ -122,6 +283,7 @@ class QrackAceBackend:
|
|
|
122
283
|
self._qubits = []
|
|
123
284
|
sim_counts = [0] * sim_count
|
|
124
285
|
sim_id = 0
|
|
286
|
+
tot_qubits = 0
|
|
125
287
|
for r in self._is_row_long_range:
|
|
126
288
|
for c in self._is_col_long_range:
|
|
127
289
|
qubit = [(sim_id, sim_counts[sim_id])]
|
|
@@ -132,6 +294,14 @@ class QrackAceBackend:
|
|
|
132
294
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
133
295
|
sim_counts[t_sim_id] += 1
|
|
134
296
|
|
|
297
|
+
qubit.append(
|
|
298
|
+
LHVQubit(
|
|
299
|
+
toClone=(
|
|
300
|
+
toClone._qubits[tot_qubits][2] if toClone else None
|
|
301
|
+
)
|
|
302
|
+
)
|
|
303
|
+
)
|
|
304
|
+
|
|
135
305
|
if (not c) and (not r):
|
|
136
306
|
t_sim_id = (sim_id + col_patch_count) % sim_count
|
|
137
307
|
qubit.append((t_sim_id, sim_counts[t_sim_id]))
|
|
@@ -145,6 +315,7 @@ class QrackAceBackend:
|
|
|
145
315
|
sim_id = (sim_id + 1) % sim_count
|
|
146
316
|
|
|
147
317
|
self._qubits.append(qubit)
|
|
318
|
+
tot_qubits += 1
|
|
148
319
|
|
|
149
320
|
self.sim = []
|
|
150
321
|
for i in range(sim_count):
|
|
@@ -262,16 +433,34 @@ class QrackAceBackend:
|
|
|
262
433
|
def _unpack(self, lq):
|
|
263
434
|
return self._qubits[lq]
|
|
264
435
|
|
|
265
|
-
def
|
|
436
|
+
def _get_qb_lhv_indices(self, hq):
|
|
266
437
|
qb = []
|
|
267
438
|
if len(hq) < 2:
|
|
268
439
|
qb = [0]
|
|
440
|
+
lhv = -1
|
|
269
441
|
elif len(hq) < 4:
|
|
270
442
|
qb = [0, 1]
|
|
443
|
+
lhv = 2
|
|
271
444
|
else:
|
|
272
|
-
qb = [0, 1,
|
|
445
|
+
qb = [0, 1, 3, 4]
|
|
446
|
+
lhv = 2
|
|
273
447
|
|
|
274
|
-
return qb
|
|
448
|
+
return qb, lhv
|
|
449
|
+
|
|
450
|
+
def _get_lhv_bloch_angles(self, sim):
|
|
451
|
+
# Z axis
|
|
452
|
+
z = sim.bloch[2]
|
|
453
|
+
|
|
454
|
+
# X axis
|
|
455
|
+
x = sim.bloch[0]
|
|
456
|
+
|
|
457
|
+
# Y axis
|
|
458
|
+
y = sim.bloch[1]
|
|
459
|
+
|
|
460
|
+
inclination = math.atan2(math.sqrt(x**2 + y**2), z)
|
|
461
|
+
azimuth = math.atan2(y, x)
|
|
462
|
+
|
|
463
|
+
return azimuth, inclination
|
|
275
464
|
|
|
276
465
|
def _get_bloch_angles(self, hq):
|
|
277
466
|
sim = self.sim[hq[0]].clone()
|
|
@@ -315,100 +504,146 @@ class QrackAceBackend:
|
|
|
315
504
|
|
|
316
505
|
sim.mtrx([m00, m01, m10, m11], q)
|
|
317
506
|
|
|
507
|
+
def _rotate_lhv_to_bloch(self, sim, delta_azimuth, delta_inclination):
|
|
508
|
+
# Apply rotation as "Azimuth, Inclination" (AI)
|
|
509
|
+
cosA = math.cos(delta_azimuth)
|
|
510
|
+
sinA = math.sin(delta_azimuth)
|
|
511
|
+
cosI = math.cos(delta_inclination / 2)
|
|
512
|
+
sinI = math.sin(delta_inclination / 2)
|
|
513
|
+
|
|
514
|
+
m00 = complex(cosI, 0)
|
|
515
|
+
m01 = complex(-cosA, sinA) * sinI
|
|
516
|
+
m10 = complex(cosA, sinA) * sinI
|
|
517
|
+
m11 = complex(cosI, 0)
|
|
518
|
+
|
|
519
|
+
sim.mtrx([m00, m01, m10, m11])
|
|
520
|
+
|
|
318
521
|
def _correct(self, lq, phase=False, skip_rotation=False):
|
|
319
522
|
hq = self._unpack(lq)
|
|
320
523
|
|
|
321
524
|
if len(hq) == 1:
|
|
322
525
|
return
|
|
323
526
|
|
|
324
|
-
qb = self.
|
|
527
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
325
528
|
|
|
326
529
|
if phase:
|
|
327
530
|
for q in qb:
|
|
328
531
|
b = hq[q]
|
|
329
532
|
self.sim[b[0]].h(b[1])
|
|
533
|
+
b = hq[lhv]
|
|
534
|
+
b.h()
|
|
330
535
|
|
|
331
536
|
if len(hq) == 5:
|
|
332
537
|
# RMS
|
|
333
538
|
p = [
|
|
334
539
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
335
540
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
336
|
-
|
|
541
|
+
hq[2].prob(),
|
|
337
542
|
self.sim[hq[3][0]].prob(hq[3][1]),
|
|
543
|
+
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
338
544
|
]
|
|
339
545
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
340
|
-
prms = math.sqrt(
|
|
546
|
+
prms = math.sqrt(
|
|
547
|
+
(p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
|
|
548
|
+
)
|
|
341
549
|
qrms = math.sqrt(
|
|
342
|
-
(
|
|
343
|
-
|
|
550
|
+
(
|
|
551
|
+
(1 - p[0]) ** 2
|
|
552
|
+
+ (1 - p[1]) ** 2
|
|
553
|
+
+ 3 * ((1 - p[2]) ** 2)
|
|
554
|
+
+ (1 - p[3]) ** 2
|
|
555
|
+
+ (1 - p[4]) ** 2
|
|
556
|
+
)
|
|
557
|
+
/ 7
|
|
344
558
|
)
|
|
345
559
|
result = ((prms + (1 - qrms)) / 2) >= 0.5
|
|
346
560
|
syndrome = (
|
|
347
|
-
[1 - p[0], 1 - p[1], 1 - p[2], 1 - p[3]]
|
|
561
|
+
[1 - p[0], 1 - p[1], 1 - p[2], 1 - p[3], 1 - p[4]]
|
|
348
562
|
if result
|
|
349
|
-
else [p[0], p[1], p[2], p[3]]
|
|
563
|
+
else [p[0], p[1], p[2], p[3], p[4]]
|
|
350
564
|
)
|
|
351
|
-
for q in range(
|
|
565
|
+
for q in range(5):
|
|
352
566
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
353
|
-
|
|
567
|
+
if q == 2:
|
|
568
|
+
hq[q].x()
|
|
569
|
+
else:
|
|
570
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
354
571
|
|
|
355
572
|
if not skip_rotation:
|
|
356
|
-
a, i = [0, 0, 0, 0], [0, 0, 0, 0]
|
|
573
|
+
a, i = [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]
|
|
357
574
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
358
575
|
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[
|
|
576
|
+
a[2], i[2] = self._get_lhv_bloch_angles(hq[2])
|
|
577
|
+
a[3], i[3] = self._get_bloch_angles(hq[3])
|
|
578
|
+
a[4], i[4] = self._get_bloch_angles(hq[4])
|
|
361
579
|
|
|
362
580
|
a_target = 0
|
|
363
581
|
i_target = 0
|
|
364
|
-
for x in range(
|
|
582
|
+
for x in range(5):
|
|
583
|
+
if x == 2:
|
|
584
|
+
continue
|
|
365
585
|
a_target += a[x]
|
|
366
586
|
i_target += i[x]
|
|
367
587
|
|
|
368
|
-
a_target /=
|
|
369
|
-
i_target /=
|
|
370
|
-
for x in range(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
588
|
+
a_target /= 5
|
|
589
|
+
i_target /= 5
|
|
590
|
+
for x in range(5):
|
|
591
|
+
if x == 2:
|
|
592
|
+
self._rotate_lhv_to_bloch(
|
|
593
|
+
hq[x], a_target - a[x], i_target - i[x]
|
|
594
|
+
)
|
|
595
|
+
else:
|
|
596
|
+
self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
374
597
|
|
|
375
598
|
else:
|
|
376
599
|
# RMS
|
|
377
600
|
p = [
|
|
378
601
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
379
602
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
603
|
+
hq[2].prob(),
|
|
380
604
|
]
|
|
381
605
|
# 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)
|
|
606
|
+
prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
|
|
607
|
+
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
|
|
384
608
|
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(
|
|
609
|
+
syndrome = [1 - p[0], 1 - p[1], 1 - p[2]] if result else [p[0], p[1], p[2]]
|
|
610
|
+
for q in range(3):
|
|
387
611
|
if syndrome[q] > (0.5 + self._epsilon):
|
|
388
|
-
|
|
612
|
+
if q == 2:
|
|
613
|
+
hq[q].x()
|
|
614
|
+
else:
|
|
615
|
+
self.sim[hq[q][0]].x(hq[q][1])
|
|
389
616
|
|
|
390
617
|
if not skip_rotation:
|
|
391
|
-
a, i = [0, 0], [0, 0]
|
|
618
|
+
a, i = [0, 0, 0], [0, 0, 0]
|
|
392
619
|
a[0], i[0] = self._get_bloch_angles(hq[0])
|
|
393
620
|
a[1], i[1] = self._get_bloch_angles(hq[1])
|
|
621
|
+
a[2], i[2] = self._get_lhv_bloch_angles(hq[2])
|
|
394
622
|
|
|
395
623
|
a_target = 0
|
|
396
624
|
i_target = 0
|
|
397
|
-
for x in range(
|
|
625
|
+
for x in range(3):
|
|
626
|
+
if x == 2:
|
|
627
|
+
continue
|
|
398
628
|
a_target += a[x]
|
|
399
629
|
i_target += i[x]
|
|
400
630
|
|
|
401
|
-
a_target /=
|
|
402
|
-
i_target /=
|
|
403
|
-
for x in range(
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
631
|
+
a_target /= 3
|
|
632
|
+
i_target /= 3
|
|
633
|
+
for x in range(3):
|
|
634
|
+
if x == 2:
|
|
635
|
+
self._rotate_lhv_to_bloch(
|
|
636
|
+
hq[x], a_target - a[x], i_target - i[x]
|
|
637
|
+
)
|
|
638
|
+
else:
|
|
639
|
+
self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
|
|
407
640
|
|
|
408
641
|
if phase:
|
|
409
642
|
for q in qb:
|
|
410
643
|
b = hq[q]
|
|
411
644
|
self.sim[b[0]].h(b[1])
|
|
645
|
+
b = hq[lhv]
|
|
646
|
+
b.h()
|
|
412
647
|
|
|
413
648
|
def apply_magnetic_bias(self, q, b):
|
|
414
649
|
if b == 0:
|
|
@@ -416,11 +651,22 @@ class QrackAceBackend:
|
|
|
416
651
|
b = math.exp(b)
|
|
417
652
|
for x in q:
|
|
418
653
|
hq = self._unpack(x)
|
|
419
|
-
for
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
654
|
+
for c in range(len(hq)):
|
|
655
|
+
h = hq[c]
|
|
656
|
+
if c == 2:
|
|
657
|
+
a, i = self._get_lhv_bloch_angles(h)
|
|
658
|
+
self._rotate_lhv_to_bloch(
|
|
659
|
+
h,
|
|
660
|
+
math.atan(math.tan(a) * b) - a,
|
|
661
|
+
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,
|
|
667
|
+
math.atan(math.tan(a) * b) - a,
|
|
668
|
+
math.atan(math.tan(i) * b) - i,
|
|
669
|
+
)
|
|
424
670
|
|
|
425
671
|
def u(self, lq, th, ph, lm):
|
|
426
672
|
hq = self._unpack(lq)
|
|
@@ -429,12 +675,15 @@ class QrackAceBackend:
|
|
|
429
675
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
430
676
|
return
|
|
431
677
|
|
|
432
|
-
qb = self.
|
|
678
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
433
679
|
|
|
434
680
|
for q in qb:
|
|
435
681
|
b = hq[q]
|
|
436
682
|
self.sim[b[0]].u(b[1], th, ph, lm)
|
|
437
683
|
|
|
684
|
+
b = hq[lhv]
|
|
685
|
+
b.u(th, ph, lm)
|
|
686
|
+
|
|
438
687
|
self._correct(lq, False, True)
|
|
439
688
|
self._correct(lq, True, False)
|
|
440
689
|
|
|
@@ -445,12 +694,20 @@ class QrackAceBackend:
|
|
|
445
694
|
self.sim[b[0]].r(p, th, b[1])
|
|
446
695
|
return
|
|
447
696
|
|
|
448
|
-
qb = self.
|
|
697
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
449
698
|
|
|
450
699
|
for q in qb:
|
|
451
700
|
b = hq[q]
|
|
452
701
|
self.sim[b[0]].r(p, th, b[1])
|
|
453
702
|
|
|
703
|
+
b = hq[lhv]
|
|
704
|
+
if p == Pauli.PauliX:
|
|
705
|
+
b.rx(th)
|
|
706
|
+
elif p == Pauli.PauliY:
|
|
707
|
+
b.ry(th)
|
|
708
|
+
elif p == Pauli.PauliZ:
|
|
709
|
+
b.rz(th)
|
|
710
|
+
|
|
454
711
|
if p != Pauli.PauliZ:
|
|
455
712
|
self._correct(lq, False, p != Pauli.PauliX)
|
|
456
713
|
if p != Pauli.PauliX:
|
|
@@ -465,12 +722,15 @@ class QrackAceBackend:
|
|
|
465
722
|
|
|
466
723
|
self._correct(lq)
|
|
467
724
|
|
|
468
|
-
qb = self.
|
|
725
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
469
726
|
|
|
470
727
|
for q in qb:
|
|
471
728
|
b = hq[q]
|
|
472
729
|
self.sim[b[0]].h(b[1])
|
|
473
730
|
|
|
731
|
+
b = hq[lhv]
|
|
732
|
+
b.h()
|
|
733
|
+
|
|
474
734
|
self._correct(lq)
|
|
475
735
|
|
|
476
736
|
def s(self, lq):
|
|
@@ -480,12 +740,15 @@ class QrackAceBackend:
|
|
|
480
740
|
self.sim[b[0]].s(b[1])
|
|
481
741
|
return
|
|
482
742
|
|
|
483
|
-
qb = self.
|
|
743
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
484
744
|
|
|
485
745
|
for q in qb:
|
|
486
746
|
b = hq[q]
|
|
487
747
|
self.sim[b[0]].s(b[1])
|
|
488
748
|
|
|
749
|
+
b = hq[lhv]
|
|
750
|
+
b.s()
|
|
751
|
+
|
|
489
752
|
def adjs(self, lq):
|
|
490
753
|
hq = self._unpack(lq)
|
|
491
754
|
if len(hq) < 2:
|
|
@@ -493,12 +756,15 @@ class QrackAceBackend:
|
|
|
493
756
|
self.sim[b[0]].adjs(b[1])
|
|
494
757
|
return
|
|
495
758
|
|
|
496
|
-
qb = self.
|
|
759
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
497
760
|
|
|
498
761
|
for q in qb:
|
|
499
762
|
b = hq[q]
|
|
500
763
|
self.sim[b[0]].adjs(b[1])
|
|
501
764
|
|
|
765
|
+
b = hq[lhv]
|
|
766
|
+
b.adjs()
|
|
767
|
+
|
|
502
768
|
def x(self, lq):
|
|
503
769
|
hq = self._unpack(lq)
|
|
504
770
|
if len(hq) < 2:
|
|
@@ -506,12 +772,15 @@ class QrackAceBackend:
|
|
|
506
772
|
self.sim[b[0]].x(b[1])
|
|
507
773
|
return
|
|
508
774
|
|
|
509
|
-
qb = self.
|
|
775
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
510
776
|
|
|
511
777
|
for q in qb:
|
|
512
778
|
b = hq[q]
|
|
513
779
|
self.sim[b[0]].x(b[1])
|
|
514
780
|
|
|
781
|
+
b = hq[lhv]
|
|
782
|
+
b.x()
|
|
783
|
+
|
|
515
784
|
def y(self, lq):
|
|
516
785
|
hq = self._unpack(lq)
|
|
517
786
|
if len(hq) < 2:
|
|
@@ -519,12 +788,15 @@ class QrackAceBackend:
|
|
|
519
788
|
self.sim[b[0]].y(b[1])
|
|
520
789
|
return
|
|
521
790
|
|
|
522
|
-
qb = self.
|
|
791
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
523
792
|
|
|
524
793
|
for q in qb:
|
|
525
794
|
b = hq[q]
|
|
526
795
|
self.sim[b[0]].y(b[1])
|
|
527
796
|
|
|
797
|
+
b = hq[lhv]
|
|
798
|
+
b.y()
|
|
799
|
+
|
|
528
800
|
def z(self, lq):
|
|
529
801
|
hq = self._unpack(lq)
|
|
530
802
|
if len(hq) < 2:
|
|
@@ -532,12 +804,15 @@ class QrackAceBackend:
|
|
|
532
804
|
self.sim[b[0]].z(b[1])
|
|
533
805
|
return
|
|
534
806
|
|
|
535
|
-
qb = self.
|
|
807
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
536
808
|
|
|
537
809
|
for q in qb:
|
|
538
810
|
b = hq[q]
|
|
539
811
|
self.sim[b[0]].z(b[1])
|
|
540
812
|
|
|
813
|
+
b = hq[lhv]
|
|
814
|
+
b.z()
|
|
815
|
+
|
|
541
816
|
def t(self, lq):
|
|
542
817
|
hq = self._unpack(lq)
|
|
543
818
|
if len(hq) < 2:
|
|
@@ -545,12 +820,15 @@ class QrackAceBackend:
|
|
|
545
820
|
self.sim[b[0]].t(b[1])
|
|
546
821
|
return
|
|
547
822
|
|
|
548
|
-
qb = self.
|
|
823
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
549
824
|
|
|
550
825
|
for q in qb:
|
|
551
826
|
b = hq[q]
|
|
552
827
|
self.sim[b[0]].t(b[1])
|
|
553
828
|
|
|
829
|
+
b = hq[lhv]
|
|
830
|
+
b.t()
|
|
831
|
+
|
|
554
832
|
def adjt(self, lq):
|
|
555
833
|
hq = self._unpack(lq)
|
|
556
834
|
if len(hq) < 2:
|
|
@@ -558,12 +836,15 @@ class QrackAceBackend:
|
|
|
558
836
|
self.sim[b[0]].adjt(b[1])
|
|
559
837
|
return
|
|
560
838
|
|
|
561
|
-
qb = self.
|
|
839
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
562
840
|
|
|
563
841
|
for q in qb:
|
|
564
842
|
b = hq[q]
|
|
565
843
|
self.sim[b[0]].adjt(b[1])
|
|
566
844
|
|
|
845
|
+
b = hq[lhv]
|
|
846
|
+
b.adjt()
|
|
847
|
+
|
|
567
848
|
def _get_gate(self, pauli, anti, sim_id):
|
|
568
849
|
gate = None
|
|
569
850
|
shadow = None
|
|
@@ -604,11 +885,15 @@ class QrackAceBackend:
|
|
|
604
885
|
|
|
605
886
|
return connected, boundary
|
|
606
887
|
|
|
607
|
-
def _apply_coupling(self, pauli, anti, qb1, hq1, qb2, hq2, lq1_lr):
|
|
888
|
+
def _apply_coupling(self, pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr):
|
|
608
889
|
for q1 in qb1:
|
|
890
|
+
if q1 == lhv1:
|
|
891
|
+
continue
|
|
609
892
|
b1 = hq1[q1]
|
|
610
893
|
gate_fn, shadow_fn = self._get_gate(pauli, anti, b1[0])
|
|
611
894
|
for q2 in qb2:
|
|
895
|
+
if q2 == lhv2:
|
|
896
|
+
continue
|
|
612
897
|
b2 = hq2[q2]
|
|
613
898
|
if b1[0] == b2[0]:
|
|
614
899
|
gate_fn([b1[1]], b2[1])
|
|
@@ -633,10 +918,18 @@ class QrackAceBackend:
|
|
|
633
918
|
|
|
634
919
|
self._correct(lq1)
|
|
635
920
|
|
|
636
|
-
qb1 = self.
|
|
637
|
-
qb2 = self.
|
|
921
|
+
qb1, lhv1 = self._get_qb_lhv_indices(hq1)
|
|
922
|
+
qb2, lhv2 = self._get_qb_lhv_indices(hq2)
|
|
638
923
|
# Apply cross coupling on hardware qubits first
|
|
639
|
-
self._apply_coupling(pauli, anti, qb1, hq1, qb2, hq2, lq1_lr)
|
|
924
|
+
self._apply_coupling(pauli, anti, qb1, lhv1, hq1, qb2, lhv2, hq2, lq1_lr)
|
|
925
|
+
# Apply coupling to the local-hidden-variable target
|
|
926
|
+
if lhv2 >= 0:
|
|
927
|
+
_cpauli_lhv(
|
|
928
|
+
hq1[lhv1].prob() if lhv1 >= 0 else self.sim[hq1[0][0]].prob(hq1[0][1]),
|
|
929
|
+
hq2[lhv2],
|
|
930
|
+
pauli,
|
|
931
|
+
anti,
|
|
932
|
+
)
|
|
640
933
|
|
|
641
934
|
self._correct(lq1, True)
|
|
642
935
|
if pauli != Pauli.PauliZ:
|
|
@@ -738,20 +1031,29 @@ class QrackAceBackend:
|
|
|
738
1031
|
self.sim[hq[4][0]].prob(hq[4][1]),
|
|
739
1032
|
]
|
|
740
1033
|
# Balancing suggestion from Elara (the custom OpenAI GPT)
|
|
741
|
-
prms = math.sqrt(
|
|
1034
|
+
prms = math.sqrt(
|
|
1035
|
+
(p[0] ** 2 + p[1] ** 2 + 3 * (p[2] ** 2) + p[3] ** 2 + p[4] ** 2) / 7
|
|
1036
|
+
)
|
|
742
1037
|
qrms = math.sqrt(
|
|
743
|
-
(
|
|
744
|
-
|
|
1038
|
+
(
|
|
1039
|
+
(1 - p[0]) ** 2
|
|
1040
|
+
+ (1 - p[1]) ** 2
|
|
1041
|
+
+ 3 * ((1 - p[2]) ** 2)
|
|
1042
|
+
+ (1 - p[3]) ** 2
|
|
1043
|
+
+ (1 - p[4]) ** 2
|
|
1044
|
+
)
|
|
1045
|
+
/ 7
|
|
745
1046
|
)
|
|
746
1047
|
else:
|
|
747
1048
|
# RMS
|
|
748
1049
|
p = [
|
|
749
1050
|
self.sim[hq[0][0]].prob(hq[0][1]),
|
|
750
1051
|
self.sim[hq[1][0]].prob(hq[1][1]),
|
|
1052
|
+
hq[2].prob(),
|
|
751
1053
|
]
|
|
752
1054
|
# 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)
|
|
1055
|
+
prms = math.sqrt((p[0] ** 2 + p[1] ** 2 + p[2] ** 2) / 3)
|
|
1056
|
+
qrms = math.sqrt(((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2) / 3)
|
|
755
1057
|
|
|
756
1058
|
return (prms + (1 - qrms)) / 2
|
|
757
1059
|
|
|
@@ -764,7 +1066,7 @@ class QrackAceBackend:
|
|
|
764
1066
|
p = self.prob(lq)
|
|
765
1067
|
result = ((p + self._epsilon) >= 1) or (random.random() < p)
|
|
766
1068
|
|
|
767
|
-
qb = self.
|
|
1069
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
768
1070
|
|
|
769
1071
|
for q in qb:
|
|
770
1072
|
b = hq[q]
|
|
@@ -775,6 +1077,11 @@ class QrackAceBackend:
|
|
|
775
1077
|
else:
|
|
776
1078
|
self.sim[b[0]].force_m(b[1], result)
|
|
777
1079
|
|
|
1080
|
+
b = hq[lhv]
|
|
1081
|
+
b.reset()
|
|
1082
|
+
if result:
|
|
1083
|
+
b.x()
|
|
1084
|
+
|
|
778
1085
|
return result
|
|
779
1086
|
|
|
780
1087
|
def force_m(self, lq, result):
|
|
@@ -785,7 +1092,7 @@ class QrackAceBackend:
|
|
|
785
1092
|
|
|
786
1093
|
self._correct(lq)
|
|
787
1094
|
|
|
788
|
-
qb = self.
|
|
1095
|
+
qb, lhv = self._get_qb_lhv_indices(hq)
|
|
789
1096
|
|
|
790
1097
|
for q in qb:
|
|
791
1098
|
b = hq[q]
|
|
@@ -796,6 +1103,11 @@ class QrackAceBackend:
|
|
|
796
1103
|
else:
|
|
797
1104
|
self.sim[b[0]].force_m(b[1], result)
|
|
798
1105
|
|
|
1106
|
+
b = hq[1]
|
|
1107
|
+
b.reset()
|
|
1108
|
+
if result:
|
|
1109
|
+
b.x()
|
|
1110
|
+
|
|
799
1111
|
return c
|
|
800
1112
|
|
|
801
1113
|
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=9p90dJXqFwc-hjclIexXZzNv-zYTWyg6vk9qYMjifXY,49439
|
|
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.64.0.dist-info/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
|
|
20
|
+
pyqrack_cpu-1.64.0.dist-info/METADATA,sha256=QyekUucHm8nlPEFmdeSqh0ciaU8wodx9HVp9w8K9SWM,5971
|
|
21
|
+
pyqrack_cpu-1.64.0.dist-info/WHEEL,sha256=AMMNaGlKLEICDqgnxZojk7k8N6wUjQQ3X9tPjxJ2sOc,110
|
|
22
|
+
pyqrack_cpu-1.64.0.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
|
|
23
|
+
pyqrack_cpu-1.64.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|