pyqrack-cpu 1.61.3__py3-none-manylinux_2_39_x86_64.whl → 1.63.0__py3-none-manylinux_2_39_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.

@@ -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._qubit_dict = {}
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._qubit_dict[tot_qubits] = qubit
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._qubit_dict[lq]
430
+ return self._qubits[lq]
263
431
 
264
- def _get_qb_indices(self, hq):
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, 2, 3]
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
- return qb
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(self, hq, delta_azimuth, delta_inclination):
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._get_qb_indices(hq)
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
- self.sim[hq[2][0]].prob(hq[2][1]),
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((p[0] ** 2 + p[1] ** 2 + p[2] ** 2 + p[3] ** 2) / 4)
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
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
342
- / 4
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(4):
567
+ for q in range(5):
351
568
  if syndrome[q] > (0.5 + self._epsilon):
352
- self.sim[hq[q][0]].x(hq[q][1])
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._get_bloch_angles(hq[3])
359
- a[3], i[3] = self._get_bloch_angles(hq[4])
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(4):
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 /= 4
368
- i_target /= 4
369
- for x in range(4):
370
- self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
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(2):
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
- self.sim[hq[q][0]].x(hq[q][1])
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(2):
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 /= 2
399
- i_target /= 2
400
- for x in range(2):
401
- self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq1)
620
- qb2 = self._get_qb_indices(hq2)
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((p[0] ** 2 + p[1] ** 2 + p[2] ** 2 + p[3] ** 2) / 4)
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
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
727
- / 4
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu
3
- Version: 1.61.3
3
+ Version: 1.63.0
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -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=1ibcuK68O2hwW8AzzxHLURXcjuQ13HKU-nIVZPLYJIg,39752
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=uoM5svDGvpgOZ3-
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.61.3.dist-info/licenses/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
- pyqrack_cpu-1.61.3.dist-info/METADATA,sha256=Dhg78xsy5KDjjqgltkFb8r8yEZM-g3FlM8kRwqzGAis,6181
21
- pyqrack_cpu-1.61.3.dist-info/WHEEL,sha256=k8EuOMBHdXsN9XSTE5LrpwS4FtdLkoSlyO_7W-lE_zg,109
22
- pyqrack_cpu-1.61.3.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
23
- pyqrack_cpu-1.61.3.dist-info/RECORD,,
19
+ pyqrack_cpu-1.63.0.dist-info/licenses/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
+ pyqrack_cpu-1.63.0.dist-info/METADATA,sha256=7mHJUFeELM8CYIFYX6u0gHC7J95pUpc3i6HGPmCPXmk,6181
21
+ pyqrack_cpu-1.63.0.dist-info/WHEEL,sha256=k8EuOMBHdXsN9XSTE5LrpwS4FtdLkoSlyO_7W-lE_zg,109
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,,