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.
- {pyqrack_cpu-2.26.5/pyqrack_cpu.egg-info → pyqrack_cpu-2.26.6}/PKG-INFO +1 -1
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_ace_backend.py +43 -125
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6/pyqrack_cpu.egg-info}/PKG-INFO +1 -1
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/setup.py +1 -1
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/LICENSE +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/MANIFEST.in +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/Makefile +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/README.md +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyproject.toml +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/__init__.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/pauli.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/SOURCES.txt +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/dependency_links.txt +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/not-zip-safe +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/requires.txt +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/pyqrack_cpu.egg-info/top_level.txt +0 -0
- {pyqrack_cpu-2.26.5 → pyqrack_cpu-2.26.6}/setup.cfg +0 -0
|
@@ -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):
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1420
|
+
anc = self._detect_ancilla_lq[anc_sim]
|
|
1429
1421
|
self._in_gadget_capture = True
|
|
1430
1422
|
# XOR on target
|
|
1431
|
-
self.cx(lq2,
|
|
1423
|
+
self.cx(lq2, anc)
|
|
1432
1424
|
if anti:
|
|
1433
|
-
self.acx(lq1,
|
|
1425
|
+
self.acx(lq1, anc)
|
|
1434
1426
|
else:
|
|
1435
|
-
self.cx(lq1,
|
|
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
|
|
1492
|
+
if anc is not None:
|
|
1501
1493
|
self._in_gadget_capture = True
|
|
1502
1494
|
# XOR check
|
|
1503
|
-
self.cx(lq2,
|
|
1504
|
-
anc_sim, anc_idx = self._qubits[
|
|
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(
|
|
1500
|
+
b = self.m(anc)
|
|
1509
1501
|
else:
|
|
1510
|
-
b = self.force_m(
|
|
1502
|
+
b = self.force_m(anc, False)
|
|
1511
1503
|
if b:
|
|
1512
|
-
self.x(
|
|
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
|
-
|
|
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
|
-
|
|
1548
|
+
anc = self._rep_code_ancilla_lq[anc_sim]
|
|
1557
1549
|
self._in_gadget_capture = True
|
|
1558
|
-
self.cx(lq2,
|
|
1550
|
+
self.cx(lq2, anc)
|
|
1559
1551
|
self._in_gadget_capture = False
|
|
1560
|
-
t2.append(
|
|
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
|
|
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,
|
|
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[
|
|
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(
|
|
1580
|
+
b = self.m(anc)
|
|
1608
1581
|
else:
|
|
1609
|
-
b = self.force_m(
|
|
1582
|
+
b = self.force_m(anc, False)
|
|
1610
1583
|
if b:
|
|
1611
|
-
self.x(
|
|
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)
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
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.
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|