pyqrack-cpu-complex128 2.26.0__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_complex128-2.26.0/pyqrack_cpu_complex128.egg-info → pyqrack_cpu_complex128-2.26.6}/PKG-INFO +1 -1
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_ace_backend.py +114 -194
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6/pyqrack_cpu_complex128.egg-info}/PKG-INFO +1 -1
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/setup.py +1 -1
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/LICENSE +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/MANIFEST.in +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/Makefile +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/README.md +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyproject.toml +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/pauli.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack_cpu_complex128.egg-info/SOURCES.txt +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack_cpu_complex128.egg-info/dependency_links.txt +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack_cpu_complex128.egg-info/not-zip-safe +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack_cpu_complex128.egg-info/requires.txt +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack_cpu_complex128.egg-info/top_level.txt +0 -0
- {pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/setup.cfg +0 -0
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_ace_backend.py
RENAMED
|
@@ -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)):
|
|
@@ -1384,6 +1376,14 @@ class QrackAceBackend:
|
|
|
1384
1376
|
def _apply_coupling(self, pauli, anti, qb1, hq1, qb2, hq2, lq1_lr, lq1=None, lq2=None):
|
|
1385
1377
|
shadow_targets = []
|
|
1386
1378
|
|
|
1379
|
+
if len(hq1) == len(hq2) and all(hq1[i][0] == hq2[i][0] for i in range(len(hq1))):
|
|
1380
|
+
for i in range(len(hq1)):
|
|
1381
|
+
sim_id, idx1 = hq1[i]
|
|
1382
|
+
gate_fn, shadow_fn = self._get_gate(pauli, anti, sim_id)
|
|
1383
|
+
_, idx2 = hq2[i]
|
|
1384
|
+
gate_fn([idx1], idx2)
|
|
1385
|
+
return
|
|
1386
|
+
|
|
1387
1387
|
# Logical-level error-detection gadget: an earlier version of this
|
|
1388
1388
|
# also captured lq1's own logical value into a dedicated ancilla
|
|
1389
1389
|
# (via a nested cx()), on the theory that the shadow-coupling
|
|
@@ -1409,24 +1409,28 @@ class QrackAceBackend:
|
|
|
1409
1409
|
# population never legitimately changes either, so this doubles as
|
|
1410
1410
|
# a lq2-invariant check for that gate family, same reasoning as
|
|
1411
1411
|
# the single-replica CZ dual-check elsewhere.
|
|
1412
|
-
|
|
1413
|
-
if self.is_error_detection and not self._in_gadget_capture and (len(
|
|
1412
|
+
anc = None
|
|
1413
|
+
if self.is_error_detection and (not self._in_gadget_capture) and (len(hq2) > 1):
|
|
1414
1414
|
anc_sim = hq1[0][0]
|
|
1415
1415
|
t_sims = {y[0] for y in hq2 if y[0] >= 0}
|
|
1416
1416
|
for x in hq1:
|
|
1417
1417
|
if x[0] in t_sims:
|
|
1418
1418
|
anc_sim = x[0]
|
|
1419
1419
|
break
|
|
1420
|
-
|
|
1420
|
+
anc = self._detect_ancilla_lq[anc_sim]
|
|
1421
1421
|
self._in_gadget_capture = True
|
|
1422
1422
|
# XOR on target
|
|
1423
|
-
self.cx(lq2,
|
|
1423
|
+
self.cx(lq2, anc)
|
|
1424
1424
|
if anti:
|
|
1425
|
-
self.acx(lq1,
|
|
1425
|
+
self.acx(lq1, anc)
|
|
1426
1426
|
else:
|
|
1427
|
-
self.cx(lq1,
|
|
1427
|
+
self.cx(lq1, anc)
|
|
1428
1428
|
self._in_gadget_capture = False
|
|
1429
1429
|
|
|
1430
|
+
if not self._in_gadget_capture:
|
|
1431
|
+
self._correct(lq1)
|
|
1432
|
+
self._correct(lq2)
|
|
1433
|
+
|
|
1430
1434
|
# A target replica (q2) that shares a simulator with ANY control
|
|
1431
1435
|
# replica in qb1 is going to receive a REAL, exact gate from that
|
|
1432
1436
|
# control at some point in the loop below (the `b1[0] == b2[0]`
|
|
@@ -1479,19 +1483,25 @@ class QrackAceBackend:
|
|
|
1479
1483
|
shadow_fn(b1, b2)
|
|
1480
1484
|
shadow_targets.append(b2)
|
|
1481
1485
|
|
|
1482
|
-
if
|
|
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:
|
|
1483
1493
|
self._in_gadget_capture = True
|
|
1484
1494
|
# XOR check
|
|
1485
|
-
self.cx(lq2,
|
|
1486
|
-
anc_sim, anc_idx = self._qubits[
|
|
1495
|
+
self.cx(lq2, anc)
|
|
1496
|
+
anc_sim, anc_idx = self._qubits[anc][0]
|
|
1487
1497
|
p = self.sim[anc_sim].prob(anc_idx)
|
|
1488
1498
|
p = 1.0 - p
|
|
1489
1499
|
if self._ps_epsilon >= p:
|
|
1490
|
-
b = self.m(
|
|
1500
|
+
b = self.m(anc)
|
|
1491
1501
|
else:
|
|
1492
|
-
b = self.force_m(
|
|
1502
|
+
b = self.force_m(anc, False)
|
|
1493
1503
|
if b:
|
|
1494
|
-
self.x(
|
|
1504
|
+
self.x(anc)
|
|
1495
1505
|
self.x(lq2)
|
|
1496
1506
|
self._in_gadget_capture = False
|
|
1497
1507
|
|
|
@@ -1525,19 +1535,21 @@ class QrackAceBackend:
|
|
|
1525
1535
|
lq1_lr = len(hq1) == 1
|
|
1526
1536
|
lq2_lr = len(hq2) == 1
|
|
1527
1537
|
|
|
1528
|
-
if not self._in_gadget_capture:
|
|
1529
|
-
self._correct(lq1)
|
|
1530
|
-
self._correct(lq2)
|
|
1531
|
-
|
|
1532
1538
|
t2 = [lq2]
|
|
1533
|
-
|
|
1539
|
+
anc, anc_sim = None, None
|
|
1534
1540
|
if self.is_boundary_repetition_code and (not self._in_gadget_capture) and lq1_lr and (not lq2_lr):
|
|
1535
1541
|
# Encode:
|
|
1536
|
-
|
|
1542
|
+
anc_sim = hq1[0][0]
|
|
1543
|
+
t_sims = {y[0] for y in hq2 if y[0] >= 0}
|
|
1544
|
+
for x in hq1:
|
|
1545
|
+
if x[0] in t_sims:
|
|
1546
|
+
anc_sim = x[0]
|
|
1547
|
+
break
|
|
1548
|
+
anc = self._rep_code_ancilla_lq[anc_sim]
|
|
1537
1549
|
self._in_gadget_capture = True
|
|
1538
|
-
self.cx(lq2,
|
|
1550
|
+
self.cx(lq2, anc)
|
|
1539
1551
|
self._in_gadget_capture = False
|
|
1540
|
-
t2.append(
|
|
1552
|
+
t2.append(anc)
|
|
1541
1553
|
|
|
1542
1554
|
qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
|
|
1543
1555
|
|
|
@@ -1553,50 +1565,24 @@ class QrackAceBackend:
|
|
|
1553
1565
|
ctrl_prob = self.sim[hq1[0][0]].prob(hq1[0][1])
|
|
1554
1566
|
_cpauli_lhv(ctrl_prob, self._lhv[lq2], pauli, anti)
|
|
1555
1567
|
|
|
1556
|
-
if
|
|
1557
|
-
anc2 = self._detect_ancilla1_lq[hq1[0][0]]
|
|
1558
|
-
self._in_gadget_capture = True
|
|
1559
|
-
self.cx(lq2, anc2)
|
|
1560
|
-
self.cx(anc1, anc2)
|
|
1561
|
-
self._in_gadget_capture = False
|
|
1562
|
-
|
|
1563
|
-
# If anc2 shows a mismatch, it's more likely
|
|
1564
|
-
# that the boundary qubit is the one in error.
|
|
1565
|
-
|
|
1566
|
-
anc_sim, anc_idx = self._qubits[anc2][0]
|
|
1567
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
1568
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
1569
|
-
b = self.m(anc2)
|
|
1570
|
-
else:
|
|
1571
|
-
b = self.force_m(anc2, False)
|
|
1572
|
-
if b:
|
|
1573
|
-
self.x(anc2)
|
|
1574
|
-
self.x(lq2)
|
|
1575
|
-
|
|
1568
|
+
if anc is not None:
|
|
1576
1569
|
# Decode:
|
|
1577
1570
|
self._in_gadget_capture = True
|
|
1578
|
-
self.cx(lq2,
|
|
1571
|
+
self.cx(lq2, anc)
|
|
1579
1572
|
self._in_gadget_capture = False
|
|
1580
1573
|
|
|
1581
1574
|
# After we corrected any mismatch above, it's unlikely
|
|
1582
1575
|
# that we can't post-select no-error on this ancilla.
|
|
1583
1576
|
|
|
1584
|
-
anc_sim, anc_idx = self._qubits[
|
|
1577
|
+
anc_sim, anc_idx = self._qubits[anc][0]
|
|
1585
1578
|
p = self.sim[anc_sim].prob(anc_idx)
|
|
1586
1579
|
if self._ps_epsilon >= (1.0 - p):
|
|
1587
|
-
b = self.m(
|
|
1580
|
+
b = self.m(anc)
|
|
1588
1581
|
else:
|
|
1589
|
-
b = self.force_m(
|
|
1582
|
+
b = self.force_m(anc, False)
|
|
1590
1583
|
if b:
|
|
1591
|
-
self.x(
|
|
1592
|
-
|
|
1593
|
-
if self._in_gadget_capture:
|
|
1594
|
-
return
|
|
1595
|
-
|
|
1596
|
-
if pauli != Pauli.PauliZ:
|
|
1597
|
-
self._correct(lq2, False, pauli != Pauli.PauliX)
|
|
1598
|
-
if pauli != Pauli.PauliX:
|
|
1599
|
-
self._correct(lq2, True)
|
|
1584
|
+
self.x(anc)
|
|
1585
|
+
self.x(lq2)
|
|
1600
1586
|
|
|
1601
1587
|
def cx(self, lq1, lq2):
|
|
1602
1588
|
self._cpauli(lq1, lq2, False, Pauli.PauliX)
|
|
@@ -1810,42 +1796,6 @@ class QrackAceBackend:
|
|
|
1810
1796
|
hq1 = self._unpack(lq1)
|
|
1811
1797
|
hq2 = self._unpack(lq2)
|
|
1812
1798
|
|
|
1813
|
-
sims1 = {r[0] for r in hq1}
|
|
1814
|
-
sims2 = {r[0] for r in hq2}
|
|
1815
|
-
|
|
1816
|
-
if sims1.isdisjoint(sims2):
|
|
1817
|
-
self._correct(lq1)
|
|
1818
|
-
self._correct(lq2)
|
|
1819
|
-
|
|
1820
|
-
# Once fully resolved, the swap itself is nothing but a
|
|
1821
|
-
# reference exchange: self._qubits[lq] is a reference to the
|
|
1822
|
-
# replica-list object, so swapping which index points to
|
|
1823
|
-
# which object IS the swap -- no separate content to copy.
|
|
1824
|
-
self._qubits[lq1], self._qubits[lq2] = self._qubits[lq2], self._qubits[lq1]
|
|
1825
|
-
|
|
1826
|
-
lhv1 = self._lhv.pop(lq1, None)
|
|
1827
|
-
lhv2 = self._lhv.pop(lq2, None)
|
|
1828
|
-
if lhv2 is not None:
|
|
1829
|
-
self._lhv[lq1] = lhv2
|
|
1830
|
-
if lhv1 is not None:
|
|
1831
|
-
self._lhv[lq2] = lhv1
|
|
1832
|
-
|
|
1833
|
-
# Boundary repetition code needs no bookkeeping here, unlike
|
|
1834
|
-
# self._qubits/self._lhv just above: it's on-demand now, not
|
|
1835
|
-
# a permanent, per-logical-index dict of pre-allocated
|
|
1836
|
-
# partners -- eligibility is just len(self._qubits[lq]) > 1,
|
|
1837
|
-
# computed fresh on every coupling event, so the reference
|
|
1838
|
-
# swap of self._qubits above already, automatically carries
|
|
1839
|
-
# the right eligibility with it. Nothing else to move.
|
|
1840
|
-
return
|
|
1841
|
-
|
|
1842
|
-
# Boundary-to-boundary handling
|
|
1843
|
-
if (len(hq1) > 1) and (len(hq2) > 1):
|
|
1844
|
-
self.cx(lq1, lq2)
|
|
1845
|
-
self.cx(lq2, lq1)
|
|
1846
|
-
self.cx(lq1, lq2)
|
|
1847
|
-
return
|
|
1848
|
-
|
|
1849
1799
|
# Fast/exact path: every replica of lq1 lines up, position-for-
|
|
1850
1800
|
# position, with the corresponding replica of lq2 on the SAME
|
|
1851
1801
|
# underlying simulator. A native swap() there is pure index
|
|
@@ -1872,8 +1822,6 @@ class QrackAceBackend:
|
|
|
1872
1822
|
# two sides can land on different simulators (the partial-match
|
|
1873
1823
|
# branches below) needs separate, careful treatment -- not
|
|
1874
1824
|
# reused as-is from _cpauli's helper.
|
|
1875
|
-
self._correct(lq1)
|
|
1876
|
-
self._correct(lq2)
|
|
1877
1825
|
|
|
1878
1826
|
if len(hq1) == len(hq2) and all(hq1[i][0] == hq2[i][0] for i in range(len(hq1))):
|
|
1879
1827
|
for i in range(len(hq1)):
|
|
@@ -1887,20 +1835,46 @@ class QrackAceBackend:
|
|
|
1887
1835
|
# with or without this call).
|
|
1888
1836
|
return
|
|
1889
1837
|
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
self.
|
|
1901
|
-
self.
|
|
1902
|
-
|
|
1903
|
-
|
|
1838
|
+
sims1 = {r[0] for r in hq1}
|
|
1839
|
+
sims2 = {r[0] for r in hq2}
|
|
1840
|
+
|
|
1841
|
+
if sims1.isdisjoint(sims2):
|
|
1842
|
+
# Once fully resolved, the swap itself is nothing but a
|
|
1843
|
+
# reference exchange: self._qubits[lq] is a reference to the
|
|
1844
|
+
# replica-list object, so swapping which index points to
|
|
1845
|
+
# which object IS the swap -- no separate content to copy.
|
|
1846
|
+
self._qubits[lq1], self._qubits[lq2] = self._qubits[lq2], self._qubits[lq1]
|
|
1847
|
+
|
|
1848
|
+
lhv1 = self._lhv.pop(lq1, None)
|
|
1849
|
+
lhv2 = self._lhv.pop(lq2, None)
|
|
1850
|
+
if lhv2 is not None:
|
|
1851
|
+
self._lhv[lq1] = lhv2
|
|
1852
|
+
if lhv1 is not None:
|
|
1853
|
+
self._lhv[lq2] = lhv1
|
|
1854
|
+
|
|
1855
|
+
# Boundary repetition code needs no bookkeeping here, unlike
|
|
1856
|
+
# self._qubits/self._lhv just above: it's on-demand now, not
|
|
1857
|
+
# a permanent, per-logical-index dict of pre-allocated
|
|
1858
|
+
# partners -- eligibility is just len(self._qubits[lq]) > 1,
|
|
1859
|
+
# computed fresh on every coupling event, so the reference
|
|
1860
|
+
# swap of self._qubits above already, automatically carries
|
|
1861
|
+
# the right eligibility with it. Nothing else to move.
|
|
1862
|
+
return
|
|
1863
|
+
|
|
1864
|
+
# Boundary-to-boundary handling
|
|
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)
|
|
1874
|
+
return
|
|
1875
|
+
|
|
1876
|
+
self._correct(lq1)
|
|
1877
|
+
self._correct(lq2)
|
|
1904
1878
|
|
|
1905
1879
|
# Partial-match cases: one side is a simple (single-replica) qubit,
|
|
1906
1880
|
# the other has multiple replicas. For the multi-replica side's
|
|
@@ -1956,30 +1930,6 @@ class QrackAceBackend:
|
|
|
1956
1930
|
self.cx(lq2, lq1)
|
|
1957
1931
|
self.cx(lq1, lq2)
|
|
1958
1932
|
|
|
1959
|
-
if anc1 is not None:
|
|
1960
|
-
self._in_gadget_capture = True
|
|
1961
|
-
self.cx(lq1, anc2)
|
|
1962
|
-
self.cx(lq2, anc1)
|
|
1963
|
-
anc_sim, anc_idx = self._qubits[anc2][0]
|
|
1964
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
1965
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
1966
|
-
b = self.m(anc2)
|
|
1967
|
-
else:
|
|
1968
|
-
b = self.force_m(anc2, False)
|
|
1969
|
-
if b:
|
|
1970
|
-
self.x(anc2)
|
|
1971
|
-
self.x(lq1)
|
|
1972
|
-
anc_sim, anc_idx = self._qubits[anc1][0]
|
|
1973
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
1974
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
1975
|
-
b = self.m(anc1)
|
|
1976
|
-
else:
|
|
1977
|
-
b = self.force_m(anc1, False)
|
|
1978
|
-
if b:
|
|
1979
|
-
self.x(anc1)
|
|
1980
|
-
self.x(lq2)
|
|
1981
|
-
self._in_gadget_capture = False
|
|
1982
|
-
|
|
1983
1933
|
def iswap(self, lq1, lq2):
|
|
1984
1934
|
self.swap(lq1, lq2)
|
|
1985
1935
|
self.cz(lq1, lq2)
|
|
@@ -2001,19 +1951,6 @@ class QrackAceBackend:
|
|
|
2001
1951
|
self.mcx([c1, c2], t)
|
|
2002
1952
|
return
|
|
2003
1953
|
|
|
2004
|
-
if not self._in_gadget_capture:
|
|
2005
|
-
self._correct(c1)
|
|
2006
|
-
self._correct(c2)
|
|
2007
|
-
|
|
2008
|
-
# Control-protection ancillas (anc1/anc1b/anc2/anc2b, checking
|
|
2009
|
-
# whether c1/c2's own values held still) removed entirely, same
|
|
2010
|
-
# basis as _apply_coupling: instrumented and stress-tested
|
|
2011
|
-
# directly -- 3 of the 4 (anc1, anc1b, anc2) never fired across
|
|
2012
|
-
# ~21,600 opportunities in a circuit built to exercise them hard,
|
|
2013
|
-
# and the 4th (anc2b) only fired at a modest ~1.5% rate. Dropped
|
|
2014
|
-
# regardless of that last one, on the same call as
|
|
2015
|
-
# _apply_coupling's equivalent mechanism.
|
|
2016
|
-
#
|
|
2017
1954
|
# AND-invariant, generalized from _apply_coupling's CX/CZ XOR
|
|
2018
1955
|
# check to the Toffoli's actual relationship: t should end up as
|
|
2019
1956
|
# t_before XOR (c1 AND c2), not just t_before XOR c1 or XOR c2
|
|
@@ -2028,7 +1965,7 @@ class QrackAceBackend:
|
|
|
2028
1965
|
# bulk on this path (the len(hqt) > 1 case returns earlier,
|
|
2029
1966
|
# above) and nothing else in this method uses that array anymore.
|
|
2030
1967
|
anc_and = None
|
|
2031
|
-
if self.is_error_detection and not self._in_gadget_capture and (
|
|
1968
|
+
if self.is_error_detection and not self._in_gadget_capture and (len(hqt) > 1):
|
|
2032
1969
|
found = False
|
|
2033
1970
|
anc_sim = hq1[0][0]
|
|
2034
1971
|
t_sims = {y[0] for y in hqt if y[0] >= 0}
|
|
@@ -2045,7 +1982,7 @@ class QrackAceBackend:
|
|
|
2045
1982
|
if found:
|
|
2046
1983
|
break
|
|
2047
1984
|
self._in_gadget_capture = True
|
|
2048
|
-
anc_and = self.
|
|
1985
|
+
anc_and = self._detect_ancilla_lq[anc_sim]
|
|
2049
1986
|
self.cx(t, anc_and)
|
|
2050
1987
|
# self.mcx() only supports a single control at the logical
|
|
2051
1988
|
# level (a syntax-convenience wrapper, confirmed directly --
|
|
@@ -2070,6 +2007,9 @@ class QrackAceBackend:
|
|
|
2070
2007
|
self.ccx(c1, c2, anc_and)
|
|
2071
2008
|
self._in_gadget_capture = False
|
|
2072
2009
|
|
|
2010
|
+
self._correct(c1)
|
|
2011
|
+
self._correct(c2)
|
|
2012
|
+
|
|
2073
2013
|
qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
|
|
2074
2014
|
qb2, _ = QrackAceBackend._get_qb_lhv_indices(hq2)
|
|
2075
2015
|
qbt, _ = QrackAceBackend._get_qb_lhv_indices(hqt)
|
|
@@ -2136,6 +2076,8 @@ class QrackAceBackend:
|
|
|
2136
2076
|
for (b1, b2, bt) in witnesses:
|
|
2137
2077
|
self.sim[b1[0]].mcx([b1[1], b2[1]], bt[1])
|
|
2138
2078
|
|
|
2079
|
+
self._correct(t)
|
|
2080
|
+
|
|
2139
2081
|
self._in_gadget_capture = True
|
|
2140
2082
|
|
|
2141
2083
|
if anc_and is not None:
|
|
@@ -2153,9 +2095,6 @@ class QrackAceBackend:
|
|
|
2153
2095
|
|
|
2154
2096
|
self._in_gadget_capture = False
|
|
2155
2097
|
|
|
2156
|
-
if not self._in_gadget_capture:
|
|
2157
|
-
self._correct(t)
|
|
2158
|
-
|
|
2159
2098
|
def ccz(self, c1, c2, t):
|
|
2160
2099
|
self.h(t)
|
|
2161
2100
|
self.ccx(c1, c2, t)
|
|
@@ -2868,7 +2807,7 @@ class QrackAceBackend:
|
|
|
2868
2807
|
return tgt
|
|
2869
2808
|
|
|
2870
2809
|
# Designed by Dan, Elara (ChatGPT), and (Anthropic) Claude:
|
|
2871
|
-
def create_noise_model(self, x=0.
|
|
2810
|
+
def create_noise_model(self, x=0.25, y=0.25):
|
|
2872
2811
|
if not _IS_QISKIT_AER_AVAILABLE:
|
|
2873
2812
|
raise RuntimeError(
|
|
2874
2813
|
"Before trying to run_qiskit_circuit() with QrackAceBackend, you must install Qiskit Aer!"
|
|
@@ -2876,17 +2815,10 @@ class QrackAceBackend:
|
|
|
2876
2815
|
noise_model = NoiseModel()
|
|
2877
2816
|
|
|
2878
2817
|
def _uncommon_sim_fraction(lq1, lq2):
|
|
2879
|
-
sims1 =
|
|
2880
|
-
sims2 =
|
|
2881
|
-
|
|
2882
|
-
|
|
2883
|
-
if s not in sims2:
|
|
2884
|
-
n += 1
|
|
2885
|
-
for s in sims2:
|
|
2886
|
-
if s not in sims1:
|
|
2887
|
-
n += 1
|
|
2888
|
-
|
|
2889
|
-
return n / (len(sims1) + len(sims2))
|
|
2818
|
+
sims1 = {qb[0] for qb in self._qubits[lq1]}
|
|
2819
|
+
sims2 = {qb[0] for qb in self._qubits[lq2]}
|
|
2820
|
+
|
|
2821
|
+
return len(sims1 ^ sims2) / len(sims1.union(sims2))
|
|
2890
2822
|
|
|
2891
2823
|
coupling_map = self.get_logical_coupling_map()
|
|
2892
2824
|
|
|
@@ -2942,6 +2874,7 @@ class QrackAceBackend:
|
|
|
2942
2874
|
|
|
2943
2875
|
sp = (1 - self._sdrp / 2) ** c
|
|
2944
2876
|
p = (1 - x) ** u
|
|
2877
|
+
d = 1 - ((1 - y) ** u)
|
|
2945
2878
|
|
|
2946
2879
|
# cx/cy/cz: single-qubit, TARGET-only error, with the gate's
|
|
2947
2880
|
# own generator as the Pauli type -- derived directly from
|
|
@@ -2966,7 +2899,7 @@ class QrackAceBackend:
|
|
|
2966
2899
|
# anc2 verifying b) are structurally live exactly when a
|
|
2967
2900
|
# itself is boundary -- see the comment above this loop.
|
|
2968
2901
|
# Damp accordingly.
|
|
2969
|
-
p_shadow_ccz *=
|
|
2902
|
+
p_shadow_ccz *= d
|
|
2970
2903
|
noise_model.add_quantum_error(
|
|
2971
2904
|
pauli_error([("ZI", p_shadow_ccz), ("II", 1 - p_shadow_ccz)]), "cz", [a, b]
|
|
2972
2905
|
)
|
|
@@ -3042,17 +2975,11 @@ class QrackAceBackend:
|
|
|
3042
2975
|
sims_b = [qb[0] for qb in self._qubits[b]]
|
|
3043
2976
|
has_match = any(s in sims_b for s in sims_a)
|
|
3044
2977
|
|
|
3045
|
-
# Mirrors swap()'s own guard: the sandwich path (and its
|
|
3046
|
-
# Z-type error model) only applies when a matching replica
|
|
3047
|
-
# actually anchors it. A simple/simple pair with no shared
|
|
3048
|
-
# sim routes through the general 3-CNOT fallback in swap()
|
|
3049
|
-
# itself now (verified: the sandwich path gave 100/100 wrong
|
|
3050
|
-
# deterministic results there), so the noise model needs the
|
|
3051
|
-
# same condition to stay consistent with what the gate does.
|
|
3052
2978
|
if (is_a_simple != is_b_simple) and has_match:
|
|
3053
2979
|
p_net_swap = 2 * p * (1 - p)
|
|
3054
2980
|
if self.is_error_detection:
|
|
3055
|
-
p_net_swap *=
|
|
2981
|
+
p_net_swap *= d
|
|
2982
|
+
p_net_swap *= p_net_swap
|
|
3056
2983
|
if is_a_simple:
|
|
3057
2984
|
# a is bulk, b is boundary -> error lands on b
|
|
3058
2985
|
noise_model.add_quantum_error(
|
|
@@ -3094,16 +3021,7 @@ class QrackAceBackend:
|
|
|
3094
3021
|
if (is_a_simple != is_b_simple) and has_match:
|
|
3095
3022
|
p_cz = 1 - sp * p # same per-call formula as standalone cz
|
|
3096
3023
|
if self.is_error_detection and len(self._qubits[a]) > 1:
|
|
3097
|
-
|
|
3098
|
-
# is_error_detection is on (see cz()'s own wrapper),
|
|
3099
|
-
# so it's the SAME kind of shadow-driven term, gated
|
|
3100
|
-
# the SAME way, as the main cx/cy/cz loop above (see
|
|
3101
|
-
# that comment for the mechanism); p_net_swap was
|
|
3102
|
-
# already damped above, independently, since it comes
|
|
3103
|
-
# from a different gate (the now-protected native
|
|
3104
|
-
# swap, not
|
|
3105
|
-
# this cz component).
|
|
3106
|
-
p_cz *= y
|
|
3024
|
+
p_cz *= d
|
|
3107
3025
|
p_net_iswap = p_net_swap * (1 - p_cz) + p_cz * (1 - p_net_swap)
|
|
3108
3026
|
if is_a_simple:
|
|
3109
3027
|
terms = [("II", 1 - p_net_iswap), ("ZI", p_net_iswap)]
|
|
@@ -3117,3 +3035,5 @@ class QrackAceBackend:
|
|
|
3117
3035
|
)
|
|
3118
3036
|
|
|
3119
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
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/neuron_activation_fn.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_neuron_torch_layer.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_system/__init__.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/qrack_system/qrack_system.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/quimb_circuit_type.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/stats/load_quantized_data.py
RENAMED
|
File without changes
|
{pyqrack_cpu_complex128-2.26.0 → pyqrack_cpu_complex128-2.26.6}/pyqrack/stats/quantize_by_range.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|