pyqrack-cpu 1.62.0__py3-none-win_amd64.whl → 1.63.0__py3-none-win_amd64.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
 
@@ -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 _get_qb_indices(self, hq):
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, 2, 3]
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(self, hq, delta_azimuth, delta_inclination):
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._get_qb_indices(hq)
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
- self.sim[hq[2][0]].prob(hq[2][1]),
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((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
+ )
341
551
  qrms = math.sqrt(
342
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
343
- / 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
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(4):
567
+ for q in range(5):
352
568
  if syndrome[q] > (0.5 + self._epsilon):
353
- 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])
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._get_bloch_angles(hq[3])
360
- 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])
361
581
 
362
582
  a_target = 0
363
583
  i_target = 0
364
- for x in range(4):
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 /= 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])
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(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):
387
613
  if syndrome[q] > (0.5 + self._epsilon):
388
- 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])
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(2):
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 /= 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])
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 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
- )
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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._get_qb_indices(hq1)
637
- qb2 = self._get_qb_indices(hq2)
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((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
+ )
742
1035
  qrms = math.sqrt(
743
- ((1 - p[0]) ** 2 + (1 - p[1]) ** 2 + (1 - p[2]) ** 2 + (1 - p[3]) ** 2)
744
- / 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
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._get_qb_indices(hq)
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._get_qb_indices(hq)
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,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pyqrack-cpu
3
- Version: 1.62.0
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=9fGCYdEUg_AsENCDRylbrZvUMVbUajRTn3CyuFGuFyM,805
2
2
  pyqrack/neuron_activation_fn.py,sha256=GOqcCkiEB60jCojTrcuHuZMDP5aTOy0adtR8SY_2EZc,614
3
3
  pyqrack/pauli.py,sha256=TUm1SN_HLz3eMW9gD_eg-IYXcMCyr36mYSytq_ExZIU,673
4
- pyqrack/qrack_ace_backend.py,sha256=De8Qp8XPdbjQB6oWdCTF0xJEHDKWzCdWZwwKBgzBJ8s,41567
4
+ pyqrack/qrack_ace_backend.py,sha256=AoJsf3jco3AL76yaGyq9_Krd7RK8CS1mziY4VI1Iq-8,50993
5
5
  pyqrack/qrack_circuit.py,sha256=QK2dtPYurdXuw-efVq7lYz40_480NxCejXhOVwBGkXs,20122
6
6
  pyqrack/qrack_neuron.py,sha256=a3F0hduVvXZi9aXsY0du5hBFXpMq_R5UHHDOv2-YtFM,9305
7
7
  pyqrack/qrack_neuron_torch_layer.py,sha256=OhNSldzaqLaMoNBkin68j8QWOOiuZCQJDZPgSDRI2Fk,6330
@@ -10,12 +10,12 @@ pyqrack/qrack_stabilizer.py,sha256=AJe7dfFcxFKyig3tjWXw0UKhXer5Wl9QNvjNNqlOL5M,2
10
10
  pyqrack/quimb_circuit_type.py,sha256=iC0CCpZBGhziFC8-uBCH43Mi29uvVUrtBG6W9YBlyps,638
11
11
  pyqrack/qrack_system/__init__.py,sha256=PUterej-xpA4BqFmiBrQCMeTQlsRf-K8Dxnwp-iVvUQ,343
12
12
  pyqrack/qrack_system/qrack_system.py,sha256=73m6cDyaE-wY0w4uFqdpngDOMoYJWOnzjitF8mD0Vb4,44281
13
- pyqrack/qrack_system/qrack_lib/qrack_pinvoke.dll,sha256=vTCBZJCdjB3dhjPQ-4LlKjfVftwfvOxeyxOrngHsTAM,1769472
13
+ pyqrack/qrack_system/qrack_lib/qrack_pinvoke.dll,sha256=MJ8ekdBYl-oWL_zqXCUUnqsIC58gVCioaAQXBRu6nHE,1769472
14
14
  pyqrack/stats/__init__.py,sha256=hI715MGW7D4mDYhUFpRI4ZLsynYDO4tN-rjsuuYbG6Q,282
15
15
  pyqrack/stats/load_quantized_data.py,sha256=_1w9BPrZNreP0wOAyaAZHdEGKoGiI7tMeFD9P3eyJC0,1219
16
16
  pyqrack/stats/quantize_by_range.py,sha256=0eBIqByIxa4vfm4fQGZLAMGR9y8raxde3e5rvUWJ_dQ,2326
17
- pyqrack_cpu-1.62.0.dist-info/LICENSE,sha256=IdAVedmFOPQtHi_XeEI9OhJwUuwlT6tCJwrT55zAn3w,1090
18
- pyqrack_cpu-1.62.0.dist-info/METADATA,sha256=3ajK7DWjdFrv0uU33J9NTb9y9U0fDpXhHy1NJVXbFr4,6035
19
- pyqrack_cpu-1.62.0.dist-info/WHEEL,sha256=JMWfR_Dj7ISokcwe0cBhCfK6JKnIi-ZX11L6d_ntt6o,98
20
- pyqrack_cpu-1.62.0.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
21
- pyqrack_cpu-1.62.0.dist-info/RECORD,,
17
+ pyqrack_cpu-1.63.0.dist-info/LICENSE,sha256=IdAVedmFOPQtHi_XeEI9OhJwUuwlT6tCJwrT55zAn3w,1090
18
+ pyqrack_cpu-1.63.0.dist-info/METADATA,sha256=MNvWVRIfPETjhLilbWiLcwuN4YXJVmqBkLnAf5txrAU,6035
19
+ pyqrack_cpu-1.63.0.dist-info/WHEEL,sha256=JMWfR_Dj7ISokcwe0cBhCfK6JKnIi-ZX11L6d_ntt6o,98
20
+ pyqrack_cpu-1.63.0.dist-info/top_level.txt,sha256=YE_3q9JTGRLMilNg2tGP1y7uU-Dx8PDao2OhwoIbv8E,8
21
+ pyqrack_cpu-1.63.0.dist-info/RECORD,,