pyqrack-cpu 1.62.0__py3-none-manylinux_2_39_x86_64.whl → 1.64.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,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 _get_qb_indices(self, hq):
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, 2, 3]
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._get_qb_indices(hq)
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
- self.sim[hq[2][0]].prob(hq[2][1]),
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((p[0] ** 2 + p[1] ** 2 + p[2] ** 2 + p[3] ** 2) / 4)
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
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
343
- / 4
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(4):
565
+ for q in range(5):
352
566
  if syndrome[q] > (0.5 + self._epsilon):
353
- self.sim[hq[q][0]].x(hq[q][1])
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._get_bloch_angles(hq[3])
360
- a[3], i[3] = self._get_bloch_angles(hq[4])
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(4):
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 /= 4
369
- i_target /= 4
370
- for x in range(4):
371
- self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
372
-
373
- self.apply_magnetic_bias([lq], self.correction_bias)
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(2):
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
- self.sim[hq[q][0]].x(hq[q][1])
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(2):
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 /= 2
402
- i_target /= 2
403
- for x in range(2):
404
- self._rotate_to_bloch(hq[x], a_target - a[x], i_target - i[x])
405
-
406
- self.apply_magnetic_bias([lq], self.correction_bias)
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 h in hq:
420
- a, i = self._get_bloch_angles(h)
421
- self._rotate_to_bloch(
422
- h, math.atan(math.tan(a) * b) - a, math.atan(math.tan(i) * b) - i
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq1)
637
- qb2 = self._get_qb_indices(hq2)
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((p[0] ** 2 + p[1] ** 2 + p[2] ** 2 + p[3] ** 2) / 4)
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
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
744
- / 4
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu
3
- Version: 1.62.0
3
+ Version: 1.64.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=SLmRwJmUAzV0BWoZJFskMYl21Kg9VXjQnoaAb4g6eDk,40334
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=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.62.0.dist-info/licenses/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
- pyqrack_cpu-1.62.0.dist-info/METADATA,sha256=QCxD2p5WskO1aqEvfMM-zUhy549OX5gxlRdk15JKdAs,6181
21
- pyqrack_cpu-1.62.0.dist-info/WHEEL,sha256=k8EuOMBHdXsN9XSTE5LrpwS4FtdLkoSlyO_7W-lE_zg,109
22
- pyqrack_cpu-1.62.0.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
23
- pyqrack_cpu-1.62.0.dist-info/RECORD,,
19
+ pyqrack_cpu-1.64.0.dist-info/licenses/LICENSE,sha256=HxB-7SaWTuewAk1nz-3_3FUD6QhgX73kNT_taKVUTq8,1069
20
+ pyqrack_cpu-1.64.0.dist-info/METADATA,sha256=Ik6swbbVDOBsG5uSiE_3s3Zh_wyA2y5GET3Nfz6O7OU,6181
21
+ pyqrack_cpu-1.64.0.dist-info/WHEEL,sha256=k8EuOMBHdXsN9XSTE5LrpwS4FtdLkoSlyO_7W-lE_zg,109
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,,