pyqrack-cpu 2.26.5__tar.gz → 2.26.6__tar.gz

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.
Files changed (30) hide show
  1. {pyqrack_cpu-2.26.5/pyqrack_cpu.egg-info → pyqrack_cpu-2.26.6}/PKG-INFO +1 -1
  2. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_ace_backend.py +43 -125
  3. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6/pyqrack_cpu.egg-info}/PKG-INFO +1 -1
  4. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/setup.py +1 -1
  5. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/LICENSE +0 -0
  6. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/MANIFEST.in +0 -0
  7. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/Makefile +0 -0
  8. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/README.md +0 -0
  9. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyproject.toml +0 -0
  10. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/__init__.py +0 -0
  11. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/neuron_activation_fn.py +0 -0
  12. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/pauli.py +0 -0
  13. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_circuit.py +0 -0
  14. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
  15. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_neuron.py +0 -0
  16. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  17. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_simulator.py +0 -0
  18. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_stabilizer.py +0 -0
  19. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_system/__init__.py +0 -0
  20. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_system/qrack_system.py +0 -0
  21. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/quimb_circuit_type.py +0 -0
  22. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/__init__.py +0 -0
  23. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/load_quantized_data.py +0 -0
  24. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/quantize_by_range.py +0 -0
  25. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/SOURCES.txt +0 -0
  26. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/dependency_links.txt +0 -0
  27. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/not-zip-safe +0 -0
  28. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/requires.txt +0 -0
  29. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/top_level.txt +0 -0
  30. {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu
3
- Version: 2.26.5
3
+ Version: 2.26.6
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -385,20 +385,17 @@ class QrackAceBackend:
385
385
  tot_qubits += 1
386
386
 
387
387
  # Error-detection gadget (IBM-style detect-and-post-select, not
388
- # correction): two shared ancillae per simulator -- one per patch,
388
+ # correction): one shared ancilla per simulator -- one per patch,
389
389
  # "a single ancilla per patch, overall" -- reused across every real
390
390
  # (same-simulator) coupling
391
391
  # gate that touches it, never allocated per-gate or per-qubit.
392
392
  # See _apply_coupling for the actual gadget; this block only does
393
393
  # the qubit-index bookkeeping, in the same style as every other
394
394
  # per-simulator allocation above.
395
- self._detect_ancilla1 = []
396
- self._detect_ancilla2 = []
395
+ self._detect_ancilla = []
397
396
  if self.is_error_detection:
398
397
  for i in range(len(sim_counts)):
399
- self._detect_ancilla1.append(sim_counts[i])
400
- sim_counts[i] += 1
401
- self._detect_ancilla2.append(sim_counts[i])
398
+ self._detect_ancilla.append(sim_counts[i])
402
399
  sim_counts[i] += 1
403
400
 
404
401
  # Logical-qubit wrapper around each patch's detection ancilla,
@@ -419,30 +416,25 @@ class QrackAceBackend:
419
416
  # specific physical replica directly (an earlier version of
420
417
  # this) isn't a valid invariant, since _correct() can
421
418
  # legitimately need to change any single replica's own value.
422
- self._detect_ancilla1_lq = []
423
- self._detect_ancilla2_lq = []
419
+ self._detect_ancilla_lq = []
424
420
  if self.is_error_detection:
425
- for sim_id, phys_idx in enumerate(self._detect_ancilla1):
426
- lq_idx = len(self._qubits)
427
- self._qubits.append([(sim_id, phys_idx)])
428
- self._detect_ancilla1_lq.append(lq_idx)
429
- for sim_id, phys_idx in enumerate(self._detect_ancilla2):
421
+ for sim_id, phys_idx in enumerate(self._detect_ancilla):
430
422
  lq_idx = len(self._qubits)
431
423
  self._qubits.append([(sim_id, phys_idx)])
432
- self._detect_ancilla2_lq.append(lq_idx)
424
+ self._detect_ancilla_lq.append(lq_idx)
433
425
  # Boundary repetition code, on-demand design: only the couplers
434
426
  # are actually noisy here -- single-qubit gates are already
435
427
  # exactly transversal, per-replica, with zero error, so there's
436
428
  # no need to maintain a permanently-synced repetition code across
437
429
  # them at all. Instead: TWO dedicated, reusable scratch ancillae
438
- # per patch (separate from _detect_ancilla1/2 above, to avoid any
430
+ # per patch (separate from _detect_ancilla above, to avoid any
439
431
  # contention between this mechanism and that one within the same
440
432
  # coupling event), allocated ONCE, and reused across every
441
433
  # boundary qubit that patch ever protects -- not one allocation
442
434
  # per boundary qubit the way the earlier, always-on version did.
443
435
  # See _cpauli for the actual encode/couple/decode/correct cycle
444
436
  # built around these; this block is just the qubit-index
445
- # bookkeeping, in the same style as _detect_ancilla1/2 above.
437
+ # bookkeeping, in the same style as _detect_ancilla above.
446
438
  self._rep_code_ancilla = []
447
439
  if self.is_boundary_repetition_code:
448
440
  for i in range(len(sim_counts)):
@@ -1417,7 +1409,7 @@ class QrackAceBackend:
1417
1409
  # population never legitimately changes either, so this doubles as
1418
1410
  # a lq2-invariant check for that gate family, same reasoning as
1419
1411
  # the single-replica CZ dual-check elsewhere.
1420
- anc2 = None
1412
+ anc = None
1421
1413
  if self.is_error_detection and (not self._in_gadget_capture) and (len(hq2) > 1):
1422
1414
  anc_sim = hq1[0][0]
1423
1415
  t_sims = {y[0] for y in hq2 if y[0] >= 0}
@@ -1425,14 +1417,14 @@ class QrackAceBackend:
1425
1417
  if x[0] in t_sims:
1426
1418
  anc_sim = x[0]
1427
1419
  break
1428
- anc2 = self._detect_ancilla2_lq[anc_sim]
1420
+ anc = self._detect_ancilla_lq[anc_sim]
1429
1421
  self._in_gadget_capture = True
1430
1422
  # XOR on target
1431
- self.cx(lq2, anc2)
1423
+ self.cx(lq2, anc)
1432
1424
  if anti:
1433
- self.acx(lq1, anc2)
1425
+ self.acx(lq1, anc)
1434
1426
  else:
1435
- self.cx(lq1, anc2)
1427
+ self.cx(lq1, anc)
1436
1428
  self._in_gadget_capture = False
1437
1429
 
1438
1430
  if not self._in_gadget_capture:
@@ -1497,19 +1489,19 @@ class QrackAceBackend:
1497
1489
  if pauli != Pauli.PauliX:
1498
1490
  self._correct(lq2, True)
1499
1491
 
1500
- if anc2 is not None:
1492
+ if anc is not None:
1501
1493
  self._in_gadget_capture = True
1502
1494
  # XOR check
1503
- self.cx(lq2, anc2)
1504
- anc_sim, anc_idx = self._qubits[anc2][0]
1495
+ self.cx(lq2, anc)
1496
+ anc_sim, anc_idx = self._qubits[anc][0]
1505
1497
  p = self.sim[anc_sim].prob(anc_idx)
1506
1498
  p = 1.0 - p
1507
1499
  if self._ps_epsilon >= p:
1508
- b = self.m(anc2)
1500
+ b = self.m(anc)
1509
1501
  else:
1510
- b = self.force_m(anc2, False)
1502
+ b = self.force_m(anc, False)
1511
1503
  if b:
1512
- self.x(anc2)
1504
+ self.x(anc)
1513
1505
  self.x(lq2)
1514
1506
  self._in_gadget_capture = False
1515
1507
 
@@ -1544,7 +1536,7 @@ class QrackAceBackend:
1544
1536
  lq2_lr = len(hq2) == 1
1545
1537
 
1546
1538
  t2 = [lq2]
1547
- anc1 = None
1539
+ anc, anc_sim = None, None
1548
1540
  if self.is_boundary_repetition_code and (not self._in_gadget_capture) and lq1_lr and (not lq2_lr):
1549
1541
  # Encode:
1550
1542
  anc_sim = hq1[0][0]
@@ -1553,11 +1545,11 @@ class QrackAceBackend:
1553
1545
  if x[0] in t_sims:
1554
1546
  anc_sim = x[0]
1555
1547
  break
1556
- anc1 = self._detect_ancilla2_lq[anc_sim]
1548
+ anc = self._rep_code_ancilla_lq[anc_sim]
1557
1549
  self._in_gadget_capture = True
1558
- self.cx(lq2, anc1)
1550
+ self.cx(lq2, anc)
1559
1551
  self._in_gadget_capture = False
1560
- t2.append(anc1)
1552
+ t2.append(anc)
1561
1553
 
1562
1554
  qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
1563
1555
 
@@ -1573,42 +1565,24 @@ class QrackAceBackend:
1573
1565
  ctrl_prob = self.sim[hq1[0][0]].prob(hq1[0][1])
1574
1566
  _cpauli_lhv(ctrl_prob, self._lhv[lq2], pauli, anti)
1575
1567
 
1576
- if anc1 is not None:
1577
- anc2 = self._detect_ancilla1_lq[hq1[0][0]]
1578
- self._in_gadget_capture = True
1579
- self.cx(lq2, anc2)
1580
- self.cx(anc1, anc2)
1581
- self._in_gadget_capture = False
1582
-
1583
- # If anc2 shows a mismatch, it's more likely
1584
- # that the boundary qubit is the one in error.
1585
-
1586
- anc_sim, anc_idx = self._qubits[anc2][0]
1587
- p = self.sim[anc_sim].prob(anc_idx)
1588
- if self._ps_epsilon >= (1.0 - p):
1589
- b = self.m(anc2)
1590
- else:
1591
- b = self.force_m(anc2, False)
1592
- if b:
1593
- self.x(anc2)
1594
- self.x(lq2)
1595
-
1568
+ if anc is not None:
1596
1569
  # Decode:
1597
1570
  self._in_gadget_capture = True
1598
- self.cx(lq2, anc1)
1571
+ self.cx(lq2, anc)
1599
1572
  self._in_gadget_capture = False
1600
1573
 
1601
1574
  # After we corrected any mismatch above, it's unlikely
1602
1575
  # that we can't post-select no-error on this ancilla.
1603
1576
 
1604
- anc_sim, anc_idx = self._qubits[anc1][0]
1577
+ anc_sim, anc_idx = self._qubits[anc][0]
1605
1578
  p = self.sim[anc_sim].prob(anc_idx)
1606
1579
  if self._ps_epsilon >= (1.0 - p):
1607
- b = self.m(anc1)
1580
+ b = self.m(anc)
1608
1581
  else:
1609
- b = self.force_m(anc1, False)
1582
+ b = self.force_m(anc, False)
1610
1583
  if b:
1611
- self.x(anc1)
1584
+ self.x(anc)
1585
+ self.x(lq2)
1612
1586
 
1613
1587
  def cx(self, lq1, lq2):
1614
1588
  self._cpauli(lq1, lq2, False, Pauli.PauliX)
@@ -1888,27 +1862,17 @@ class QrackAceBackend:
1888
1862
  return
1889
1863
 
1890
1864
  # Boundary-to-boundary handling
1891
- if (len(hq1) > 1) and (len(hq2) > 1):
1892
- self.cx(lq1, lq2)
1893
- self.cx(lq2, lq1)
1894
- self.cx(lq1, lq2)
1865
+ if self.is_error_detection and ((len(hq1) > 1) or (len(hq2) > 1)):
1866
+ if len(hq1) > 1:
1867
+ self.cx(lq1, lq2)
1868
+ self.cx(lq2, lq1)
1869
+ self.cx(lq1, lq2)
1870
+ else:
1871
+ self.cx(lq2, lq1)
1872
+ self.cx(lq1, lq2)
1873
+ self.cx(lq2, lq1)
1895
1874
  return
1896
1875
 
1897
- anc1, anc2 = None, None
1898
- if self.is_error_detection and not self._in_gadget_capture and ((len(hq1) > 1) or (len(hq2) > 1)):
1899
- t_sims = {y[0] for y in hq2 if y[0] >= 0}
1900
- anc_sim = hq1[0][0]
1901
- for x in hq1:
1902
- if x[0] in t_sims:
1903
- anc_sim = x[0]
1904
- break
1905
- anc1 = self._detect_ancilla1_lq[anc_sim]
1906
- anc2 = self._detect_ancilla2_lq[anc_sim]
1907
- self._in_gadget_capture = True
1908
- self.cx(lq1, anc1)
1909
- self.cx(lq2, anc2)
1910
- self._in_gadget_capture = False
1911
-
1912
1876
  self._correct(lq1)
1913
1877
  self._correct(lq2)
1914
1878
 
@@ -1966,30 +1930,6 @@ class QrackAceBackend:
1966
1930
  self.cx(lq2, lq1)
1967
1931
  self.cx(lq1, lq2)
1968
1932
 
1969
- if anc1 is not None:
1970
- self._in_gadget_capture = True
1971
- self.cx(lq1, anc2)
1972
- self.cx(lq2, anc1)
1973
- anc_sim, anc_idx = self._qubits[anc2][0]
1974
- p = self.sim[anc_sim].prob(anc_idx)
1975
- if self._ps_epsilon >= (1.0 - p):
1976
- b = self.m(anc2)
1977
- else:
1978
- b = self.force_m(anc2, False)
1979
- if b:
1980
- self.x(anc2)
1981
- self.x(lq1)
1982
- anc_sim, anc_idx = self._qubits[anc1][0]
1983
- p = self.sim[anc_sim].prob(anc_idx)
1984
- if self._ps_epsilon >= (1.0 - p):
1985
- b = self.m(anc1)
1986
- else:
1987
- b = self.force_m(anc1, False)
1988
- if b:
1989
- self.x(anc1)
1990
- self.x(lq2)
1991
- self._in_gadget_capture = False
1992
-
1993
1933
  def iswap(self, lq1, lq2):
1994
1934
  self.swap(lq1, lq2)
1995
1935
  self.cz(lq1, lq2)
@@ -2011,15 +1951,6 @@ class QrackAceBackend:
2011
1951
  self.mcx([c1, c2], t)
2012
1952
  return
2013
1953
 
2014
- # Control-protection ancillas (anc1/anc1b/anc2/anc2b, checking
2015
- # whether c1/c2's own values held still) removed entirely, same
2016
- # basis as _apply_coupling: instrumented and stress-tested
2017
- # directly -- 3 of the 4 (anc1, anc1b, anc2) never fired across
2018
- # ~21,600 opportunities in a circuit built to exercise them hard,
2019
- # and the 4th (anc2b) only fired at a modest ~1.5% rate. Dropped
2020
- # regardless of that last one, on the same call as
2021
- # _apply_coupling's equivalent mechanism.
2022
- #
2023
1954
  # AND-invariant, generalized from _apply_coupling's CX/CZ XOR
2024
1955
  # check to the Toffoli's actual relationship: t should end up as
2025
1956
  # t_before XOR (c1 AND c2), not just t_before XOR c1 or XOR c2
@@ -2051,7 +1982,7 @@ class QrackAceBackend:
2051
1982
  if found:
2052
1983
  break
2053
1984
  self._in_gadget_capture = True
2054
- anc_and = self._detect_ancilla2_lq[anc_sim]
1985
+ anc_and = self._detect_ancilla_lq[anc_sim]
2055
1986
  self.cx(t, anc_and)
2056
1987
  # self.mcx() only supports a single control at the logical
2057
1988
  # level (a syntax-convenience wrapper, confirmed directly --
@@ -3044,17 +2975,11 @@ class QrackAceBackend:
3044
2975
  sims_b = [qb[0] for qb in self._qubits[b]]
3045
2976
  has_match = any(s in sims_b for s in sims_a)
3046
2977
 
3047
- # Mirrors swap()'s own guard: the sandwich path (and its
3048
- # Z-type error model) only applies when a matching replica
3049
- # actually anchors it. A simple/simple pair with no shared
3050
- # sim routes through the general 3-CNOT fallback in swap()
3051
- # itself now (verified: the sandwich path gave 100/100 wrong
3052
- # deterministic results there), so the noise model needs the
3053
- # same condition to stay consistent with what the gate does.
3054
2978
  if (is_a_simple != is_b_simple) and has_match:
3055
2979
  p_net_swap = 2 * p * (1 - p)
3056
2980
  if self.is_error_detection:
3057
2981
  p_net_swap *= d
2982
+ p_net_swap *= p_net_swap
3058
2983
  if is_a_simple:
3059
2984
  # a is bulk, b is boundary -> error lands on b
3060
2985
  noise_model.add_quantum_error(
@@ -3096,15 +3021,6 @@ class QrackAceBackend:
3096
3021
  if (is_a_simple != is_b_simple) and has_match:
3097
3022
  p_cz = 1 - sp * p # same per-call formula as standalone cz
3098
3023
  if self.is_error_detection and len(self._qubits[a]) > 1:
3099
- # this cz component is routed through cx() when
3100
- # is_error_detection is on (see cz()'s own wrapper),
3101
- # so it's the SAME kind of shadow-driven term, gated
3102
- # the SAME way, as the main cx/cy/cz loop above (see
3103
- # that comment for the mechanism); p_net_swap was
3104
- # already damped above, independently, since it comes
3105
- # from a different gate (the now-protected native
3106
- # swap, not
3107
- # this cz component).
3108
3024
  p_cz *= d
3109
3025
  p_net_iswap = p_net_swap * (1 - p_cz) + p_cz * (1 - p_net_swap)
3110
3026
  if is_a_simple:
@@ -3119,3 +3035,5 @@ class QrackAceBackend:
3119
3035
  )
3120
3036
 
3121
3037
  return noise_model
3038
+
3039
+ return noise_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-cpu
3
- Version: 2.26.5
3
+ Version: 2.26.6
4
4
  Summary: pyqrack - Pure Python vm6502q/qrack Wrapper
5
5
  Home-page: https://github.com/vm6502q/pyqrack
6
6
  Author: Daniel Strano
@@ -7,7 +7,7 @@ from setuptools import setup
7
7
  from setuptools.command.build_py import build_py
8
8
 
9
9
 
10
- VERSION = "2.26.5"
10
+ VERSION = "2.26.6"
11
11
 
12
12
  # Read long description from README.
13
13
  README_PATH = os.path.join(os.path.abspath(os.path.dirname(__file__)), 'README.md')
File without changes
File without changes
File without changes
File without changes
File without changes