pyqrack-complex128 2.26.4__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_complex128-2.26.4/pyqrack_complex128.egg-info → pyqrack_complex128-2.26.6}/PKG-INFO +1 -1
  2. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_ace_backend.py +53 -138
  3. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6/pyqrack_complex128.egg-info}/PKG-INFO +1 -1
  4. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/setup.py +1 -1
  5. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/LICENSE +0 -0
  6. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/MANIFEST.in +0 -0
  7. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/Makefile +0 -0
  8. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/README.md +0 -0
  9. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyproject.toml +0 -0
  10. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/__init__.py +0 -0
  11. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/neuron_activation_fn.py +0 -0
  12. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/pauli.py +0 -0
  13. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_circuit.py +0 -0
  14. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
  15. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_neuron.py +0 -0
  16. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_neuron_torch_layer.py +0 -0
  17. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_simulator.py +0 -0
  18. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_stabilizer.py +0 -0
  19. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_system/__init__.py +0 -0
  20. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/qrack_system/qrack_system.py +0 -0
  21. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/quimb_circuit_type.py +0 -0
  22. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/stats/__init__.py +0 -0
  23. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/stats/load_quantized_data.py +0 -0
  24. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack/stats/quantize_by_range.py +0 -0
  25. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack_complex128.egg-info/SOURCES.txt +0 -0
  26. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack_complex128.egg-info/dependency_links.txt +0 -0
  27. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack_complex128.egg-info/not-zip-safe +0 -0
  28. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack_complex128.egg-info/requires.txt +0 -0
  29. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/pyqrack_complex128.egg-info/top_level.txt +0 -0
  30. {pyqrack_complex128-2.26.4 → pyqrack_complex128-2.26.6}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-complex128
3
- Version: 2.26.4
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)):
@@ -1389,7 +1381,6 @@ class QrackAceBackend:
1389
1381
  sim_id, idx1 = hq1[i]
1390
1382
  gate_fn, shadow_fn = self._get_gate(pauli, anti, sim_id)
1391
1383
  _, idx2 = hq2[i]
1392
- self.sim[sim_id].swap(idx1, idx2)
1393
1384
  gate_fn([idx1], idx2)
1394
1385
  return
1395
1386
 
@@ -1418,7 +1409,7 @@ class QrackAceBackend:
1418
1409
  # population never legitimately changes either, so this doubles as
1419
1410
  # a lq2-invariant check for that gate family, same reasoning as
1420
1411
  # the single-replica CZ dual-check elsewhere.
1421
- anc2 = None
1412
+ anc = None
1422
1413
  if self.is_error_detection and (not self._in_gadget_capture) and (len(hq2) > 1):
1423
1414
  anc_sim = hq1[0][0]
1424
1415
  t_sims = {y[0] for y in hq2 if y[0] >= 0}
@@ -1426,16 +1417,20 @@ class QrackAceBackend:
1426
1417
  if x[0] in t_sims:
1427
1418
  anc_sim = x[0]
1428
1419
  break
1429
- anc2 = self._detect_ancilla2_lq[anc_sim]
1420
+ anc = self._detect_ancilla_lq[anc_sim]
1430
1421
  self._in_gadget_capture = True
1431
1422
  # XOR on target
1432
- self.cx(lq2, anc2)
1423
+ self.cx(lq2, anc)
1433
1424
  if anti:
1434
- self.acx(lq1, anc2)
1425
+ self.acx(lq1, anc)
1435
1426
  else:
1436
- self.cx(lq1, anc2)
1427
+ self.cx(lq1, anc)
1437
1428
  self._in_gadget_capture = False
1438
1429
 
1430
+ if not self._in_gadget_capture:
1431
+ self._correct(lq1)
1432
+ self._correct(lq2)
1433
+
1439
1434
  # A target replica (q2) that shares a simulator with ANY control
1440
1435
  # replica in qb1 is going to receive a REAL, exact gate from that
1441
1436
  # control at some point in the loop below (the `b1[0] == b2[0]`
@@ -1488,19 +1483,25 @@ class QrackAceBackend:
1488
1483
  shadow_fn(b1, b2)
1489
1484
  shadow_targets.append(b2)
1490
1485
 
1491
- if anc2 is not None:
1486
+ if not self._in_gadget_capture:
1487
+ if pauli != Pauli.PauliZ:
1488
+ self._correct(lq2, False, pauli != Pauli.PauliX)
1489
+ if pauli != Pauli.PauliX:
1490
+ self._correct(lq2, True)
1491
+
1492
+ if anc is not None:
1492
1493
  self._in_gadget_capture = True
1493
1494
  # XOR check
1494
- self.cx(lq2, anc2)
1495
- anc_sim, anc_idx = self._qubits[anc2][0]
1495
+ self.cx(lq2, anc)
1496
+ anc_sim, anc_idx = self._qubits[anc][0]
1496
1497
  p = self.sim[anc_sim].prob(anc_idx)
1497
1498
  p = 1.0 - p
1498
1499
  if self._ps_epsilon >= p:
1499
- b = self.m(anc2)
1500
+ b = self.m(anc)
1500
1501
  else:
1501
- b = self.force_m(anc2, False)
1502
+ b = self.force_m(anc, False)
1502
1503
  if b:
1503
- self.x(anc2)
1504
+ self.x(anc)
1504
1505
  self.x(lq2)
1505
1506
  self._in_gadget_capture = False
1506
1507
 
@@ -1534,12 +1535,8 @@ class QrackAceBackend:
1534
1535
  lq1_lr = len(hq1) == 1
1535
1536
  lq2_lr = len(hq2) == 1
1536
1537
 
1537
- if not self._in_gadget_capture:
1538
- self._correct(lq1)
1539
- self._correct(lq2)
1540
-
1541
1538
  t2 = [lq2]
1542
- anc1 = None
1539
+ anc, anc_sim = None, None
1543
1540
  if self.is_boundary_repetition_code and (not self._in_gadget_capture) and lq1_lr and (not lq2_lr):
1544
1541
  # Encode:
1545
1542
  anc_sim = hq1[0][0]
@@ -1548,11 +1545,11 @@ class QrackAceBackend:
1548
1545
  if x[0] in t_sims:
1549
1546
  anc_sim = x[0]
1550
1547
  break
1551
- anc1 = self._detect_ancilla2_lq[anc_sim]
1548
+ anc = self._rep_code_ancilla_lq[anc_sim]
1552
1549
  self._in_gadget_capture = True
1553
- self.cx(lq2, anc1)
1550
+ self.cx(lq2, anc)
1554
1551
  self._in_gadget_capture = False
1555
- t2.append(anc1)
1552
+ t2.append(anc)
1556
1553
 
1557
1554
  qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
1558
1555
 
@@ -1568,50 +1565,24 @@ class QrackAceBackend:
1568
1565
  ctrl_prob = self.sim[hq1[0][0]].prob(hq1[0][1])
1569
1566
  _cpauli_lhv(ctrl_prob, self._lhv[lq2], pauli, anti)
1570
1567
 
1571
- if anc1 is not None:
1572
- anc2 = self._detect_ancilla1_lq[hq1[0][0]]
1573
- self._in_gadget_capture = True
1574
- self.cx(lq2, anc2)
1575
- self.cx(anc1, anc2)
1576
- self._in_gadget_capture = False
1577
-
1578
- # If anc2 shows a mismatch, it's more likely
1579
- # that the boundary qubit is the one in error.
1580
-
1581
- anc_sim, anc_idx = self._qubits[anc2][0]
1582
- p = self.sim[anc_sim].prob(anc_idx)
1583
- if self._ps_epsilon >= (1.0 - p):
1584
- b = self.m(anc2)
1585
- else:
1586
- b = self.force_m(anc2, False)
1587
- if b:
1588
- self.x(anc2)
1589
- self.x(lq2)
1590
-
1568
+ if anc is not None:
1591
1569
  # Decode:
1592
1570
  self._in_gadget_capture = True
1593
- self.cx(lq2, anc1)
1571
+ self.cx(lq2, anc)
1594
1572
  self._in_gadget_capture = False
1595
1573
 
1596
1574
  # After we corrected any mismatch above, it's unlikely
1597
1575
  # that we can't post-select no-error on this ancilla.
1598
1576
 
1599
- anc_sim, anc_idx = self._qubits[anc1][0]
1577
+ anc_sim, anc_idx = self._qubits[anc][0]
1600
1578
  p = self.sim[anc_sim].prob(anc_idx)
1601
1579
  if self._ps_epsilon >= (1.0 - p):
1602
- b = self.m(anc1)
1580
+ b = self.m(anc)
1603
1581
  else:
1604
- b = self.force_m(anc1, False)
1582
+ b = self.force_m(anc, False)
1605
1583
  if b:
1606
- self.x(anc1)
1607
-
1608
- if self._in_gadget_capture:
1609
- return
1610
-
1611
- if pauli != Pauli.PauliZ:
1612
- self._correct(lq2, False, pauli != Pauli.PauliX)
1613
- if pauli != Pauli.PauliX:
1614
- self._correct(lq2, True)
1584
+ self.x(anc)
1585
+ self.x(lq2)
1615
1586
 
1616
1587
  def cx(self, lq1, lq2):
1617
1588
  self._cpauli(lq1, lq2, False, Pauli.PauliX)
@@ -1891,27 +1862,17 @@ class QrackAceBackend:
1891
1862
  return
1892
1863
 
1893
1864
  # Boundary-to-boundary handling
1894
- if (len(hq1) > 1) and (len(hq2) > 1):
1895
- self.cx(lq1, lq2)
1896
- self.cx(lq2, lq1)
1897
- 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)
1898
1874
  return
1899
1875
 
1900
- anc1, anc2 = None, None
1901
- if self.is_error_detection and not self._in_gadget_capture and ((len(hq1) > 1) or (len(hq2) > 1)):
1902
- t_sims = {y[0] for y in hq2 if y[0] >= 0}
1903
- anc_sim = hq1[0][0]
1904
- for x in hq1:
1905
- if x[0] in t_sims:
1906
- anc_sim = x[0]
1907
- break
1908
- anc1 = self._detect_ancilla1_lq[anc_sim]
1909
- anc2 = self._detect_ancilla2_lq[anc_sim]
1910
- self._in_gadget_capture = True
1911
- self.cx(lq1, anc1)
1912
- self.cx(lq2, anc2)
1913
- self._in_gadget_capture = False
1914
-
1915
1876
  self._correct(lq1)
1916
1877
  self._correct(lq2)
1917
1878
 
@@ -1969,30 +1930,6 @@ class QrackAceBackend:
1969
1930
  self.cx(lq2, lq1)
1970
1931
  self.cx(lq1, lq2)
1971
1932
 
1972
- if anc1 is not None:
1973
- self._in_gadget_capture = True
1974
- self.cx(lq1, anc2)
1975
- self.cx(lq2, anc1)
1976
- anc_sim, anc_idx = self._qubits[anc2][0]
1977
- p = self.sim[anc_sim].prob(anc_idx)
1978
- if self._ps_epsilon >= (1.0 - p):
1979
- b = self.m(anc2)
1980
- else:
1981
- b = self.force_m(anc2, False)
1982
- if b:
1983
- self.x(anc2)
1984
- self.x(lq1)
1985
- anc_sim, anc_idx = self._qubits[anc1][0]
1986
- p = self.sim[anc_sim].prob(anc_idx)
1987
- if self._ps_epsilon >= (1.0 - p):
1988
- b = self.m(anc1)
1989
- else:
1990
- b = self.force_m(anc1, False)
1991
- if b:
1992
- self.x(anc1)
1993
- self.x(lq2)
1994
- self._in_gadget_capture = False
1995
-
1996
1933
  def iswap(self, lq1, lq2):
1997
1934
  self.swap(lq1, lq2)
1998
1935
  self.cz(lq1, lq2)
@@ -2014,15 +1951,6 @@ class QrackAceBackend:
2014
1951
  self.mcx([c1, c2], t)
2015
1952
  return
2016
1953
 
2017
- # Control-protection ancillas (anc1/anc1b/anc2/anc2b, checking
2018
- # whether c1/c2's own values held still) removed entirely, same
2019
- # basis as _apply_coupling: instrumented and stress-tested
2020
- # directly -- 3 of the 4 (anc1, anc1b, anc2) never fired across
2021
- # ~21,600 opportunities in a circuit built to exercise them hard,
2022
- # and the 4th (anc2b) only fired at a modest ~1.5% rate. Dropped
2023
- # regardless of that last one, on the same call as
2024
- # _apply_coupling's equivalent mechanism.
2025
- #
2026
1954
  # AND-invariant, generalized from _apply_coupling's CX/CZ XOR
2027
1955
  # check to the Toffoli's actual relationship: t should end up as
2028
1956
  # t_before XOR (c1 AND c2), not just t_before XOR c1 or XOR c2
@@ -2054,7 +1982,7 @@ class QrackAceBackend:
2054
1982
  if found:
2055
1983
  break
2056
1984
  self._in_gadget_capture = True
2057
- anc_and = self._detect_ancilla2_lq[anc_sim]
1985
+ anc_and = self._detect_ancilla_lq[anc_sim]
2058
1986
  self.cx(t, anc_and)
2059
1987
  # self.mcx() only supports a single control at the logical
2060
1988
  # level (a syntax-convenience wrapper, confirmed directly --
@@ -3047,17 +2975,11 @@ class QrackAceBackend:
3047
2975
  sims_b = [qb[0] for qb in self._qubits[b]]
3048
2976
  has_match = any(s in sims_b for s in sims_a)
3049
2977
 
3050
- # Mirrors swap()'s own guard: the sandwich path (and its
3051
- # Z-type error model) only applies when a matching replica
3052
- # actually anchors it. A simple/simple pair with no shared
3053
- # sim routes through the general 3-CNOT fallback in swap()
3054
- # itself now (verified: the sandwich path gave 100/100 wrong
3055
- # deterministic results there), so the noise model needs the
3056
- # same condition to stay consistent with what the gate does.
3057
2978
  if (is_a_simple != is_b_simple) and has_match:
3058
2979
  p_net_swap = 2 * p * (1 - p)
3059
2980
  if self.is_error_detection:
3060
2981
  p_net_swap *= d
2982
+ p_net_swap *= p_net_swap
3061
2983
  if is_a_simple:
3062
2984
  # a is bulk, b is boundary -> error lands on b
3063
2985
  noise_model.add_quantum_error(
@@ -3099,15 +3021,6 @@ class QrackAceBackend:
3099
3021
  if (is_a_simple != is_b_simple) and has_match:
3100
3022
  p_cz = 1 - sp * p # same per-call formula as standalone cz
3101
3023
  if self.is_error_detection and len(self._qubits[a]) > 1:
3102
- # this cz component is routed through cx() when
3103
- # is_error_detection is on (see cz()'s own wrapper),
3104
- # so it's the SAME kind of shadow-driven term, gated
3105
- # the SAME way, as the main cx/cy/cz loop above (see
3106
- # that comment for the mechanism); p_net_swap was
3107
- # already damped above, independently, since it comes
3108
- # from a different gate (the now-protected native
3109
- # swap, not
3110
- # this cz component).
3111
3024
  p_cz *= d
3112
3025
  p_net_iswap = p_net_swap * (1 - p_cz) + p_cz * (1 - p_net_swap)
3113
3026
  if is_a_simple:
@@ -3122,3 +3035,5 @@ class QrackAceBackend:
3122
3035
  )
3123
3036
 
3124
3037
  return noise_model
3038
+
3039
+ return noise_model
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyqrack-complex128
3
- Version: 2.26.4
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.4"
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')