pyqrack-cpu 2.18.0__tar.gz → 2.18.2__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.18.0/pyqrack_cpu.egg-info → pyqrack_cpu-2.18.2}/PKG-INFO +1 -1
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_ace_backend.py +203 -45
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2/pyqrack_cpu.egg-info}/PKG-INFO +1 -1
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/setup.py +1 -1
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/LICENSE +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/MANIFEST.in +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/Makefile +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/README.md +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyproject.toml +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/__init__.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/pauli.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack_cpu.egg-info/SOURCES.txt +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack_cpu.egg-info/dependency_links.txt +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack_cpu.egg-info/not-zip-safe +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack_cpu.egg-info/requires.txt +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/pyqrack_cpu.egg-info/top_level.txt +0 -0
- {pyqrack_cpu-2.18.0 → pyqrack_cpu-2.18.2}/setup.cfg +0 -0
|
@@ -631,10 +631,55 @@ class QrackAceBackend:
|
|
|
631
631
|
else:
|
|
632
632
|
t.adjs()
|
|
633
633
|
|
|
634
|
-
def
|
|
634
|
+
def _detect_swap(self, sim_id, idx1, idx2):
|
|
635
|
+
"""Same detect-and-post-select gadget as _apply_coupling's real
|
|
636
|
+
gate branch, generalized to SWAP's own invariant instead of
|
|
637
|
+
CNOT's. SWAP exchanges two qubits' values, but their XOR
|
|
638
|
+
(parity) is exactly conserved in the noiseless case:
|
|
639
|
+
idx1_before ^ idx2_before == idx1_after ^ idx2_after, regardless
|
|
640
|
+
of either qubit's individual value -- captured the same way,
|
|
641
|
+
just with two capture-CNOTs (one per qubit) into the shared
|
|
642
|
+
per-simulator ancilla instead of one.
|
|
643
|
+
"""
|
|
644
|
+
sim = self.sim[sim_id]
|
|
645
|
+
if not self.is_error_detection:
|
|
646
|
+
sim.swap(idx1, idx2)
|
|
647
|
+
return
|
|
648
|
+
anc = self._detect_ancilla[sim_id]
|
|
649
|
+
sim.mcx([idx1], anc)
|
|
650
|
+
sim.mcx([idx2], anc)
|
|
651
|
+
sim.swap(idx1, idx2)
|
|
652
|
+
sim.mcx([idx1], anc)
|
|
653
|
+
sim.mcx([idx2], anc)
|
|
654
|
+
sim.force_m(anc, False)
|
|
655
|
+
|
|
656
|
+
def _anti_shadow_wrap(self, c, t, middle_fn):
|
|
657
|
+
"""Shared gadget for _anti_c{x,y,z}_shadow: all three bracket
|
|
658
|
+
their genuinely cross-sim shadow call with two _qec_x(c) calls,
|
|
659
|
+
and nothing else ever touches c in between -- the shadow call
|
|
660
|
+
underneath only ever reads c via .prob(), never gates it (same
|
|
661
|
+
property _apply_coupling's real branch relies on for its own
|
|
662
|
+
control-invariance check). So X;X on c should net to identity,
|
|
663
|
+
in the noiseless case, regardless of c's current value or basis
|
|
664
|
+
-- exactly as protectable as the CNOT-sandwich case, just with
|
|
665
|
+
X instead of CNOT as the bracketing gate.
|
|
666
|
+
"""
|
|
667
|
+
if not (self.is_error_detection and isinstance(c, tuple)):
|
|
668
|
+
self._qec_x(c)
|
|
669
|
+
middle_fn(c, t)
|
|
670
|
+
self._qec_x(c)
|
|
671
|
+
return
|
|
672
|
+
anc = self._detect_ancilla[c[0]]
|
|
673
|
+
sim = self.sim[c[0]]
|
|
674
|
+
sim.mcx([c[1]], anc)
|
|
635
675
|
self._qec_x(c)
|
|
636
|
-
|
|
676
|
+
middle_fn(c, t)
|
|
637
677
|
self._qec_x(c)
|
|
678
|
+
sim.mcx([c[1]], anc)
|
|
679
|
+
sim.force_m(anc, False)
|
|
680
|
+
|
|
681
|
+
def _anti_cz_shadow(self, c, t):
|
|
682
|
+
self._anti_shadow_wrap(c, t, self._cz_shadow)
|
|
638
683
|
|
|
639
684
|
def _cx_shadow(self, c, t):
|
|
640
685
|
self._qec_h(t)
|
|
@@ -642,9 +687,7 @@ class QrackAceBackend:
|
|
|
642
687
|
self._qec_h(t)
|
|
643
688
|
|
|
644
689
|
def _anti_cx_shadow(self, c, t):
|
|
645
|
-
self.
|
|
646
|
-
self._cx_shadow(c, t)
|
|
647
|
-
self._qec_x(c)
|
|
690
|
+
self._anti_shadow_wrap(c, t, self._cx_shadow)
|
|
648
691
|
|
|
649
692
|
def _cy_shadow(self, c, t):
|
|
650
693
|
self._qec_adjs(t)
|
|
@@ -652,9 +695,7 @@ class QrackAceBackend:
|
|
|
652
695
|
self._qec_s(t)
|
|
653
696
|
|
|
654
697
|
def _anti_cy_shadow(self, c, t):
|
|
655
|
-
self.
|
|
656
|
-
self._cy_shadow(c, t)
|
|
657
|
-
self._qec_x(c)
|
|
698
|
+
self._anti_shadow_wrap(c, t, self._cy_shadow)
|
|
658
699
|
|
|
659
700
|
def _unpack(self, lq):
|
|
660
701
|
return self._qubits[lq]
|
|
@@ -1415,44 +1456,78 @@ class QrackAceBackend:
|
|
|
1415
1456
|
b2 = hq2[q2]
|
|
1416
1457
|
if b1[0] == b2[0]:
|
|
1417
1458
|
if self.is_error_detection:
|
|
1418
|
-
#
|
|
1419
|
-
# post-
|
|
1420
|
-
#
|
|
1421
|
-
#
|
|
1422
|
-
#
|
|
1423
|
-
#
|
|
1424
|
-
#
|
|
1425
|
-
#
|
|
1426
|
-
#
|
|
1427
|
-
#
|
|
1428
|
-
#
|
|
1429
|
-
#
|
|
1430
|
-
#
|
|
1431
|
-
#
|
|
1432
|
-
#
|
|
1433
|
-
#
|
|
1434
|
-
#
|
|
1435
|
-
# --
|
|
1436
|
-
#
|
|
1437
|
-
# repeat, done in one step because the simulator
|
|
1438
|
-
# has the full state to project, not just samples
|
|
1439
|
-
# from it. The same ancilla is reused for every
|
|
1440
|
-
# gate touching this simulator: each round-trip is
|
|
1441
|
-
# self-contained and leaves the ancilla at |0>
|
|
1442
|
-
# again whenever nothing was detected, so there's
|
|
1443
|
-
# no cross-gate bookkeeping needed.
|
|
1459
|
+
# Single-patch, not multi-patch: force_m's
|
|
1460
|
+
# post-selection isn't local to the one
|
|
1461
|
+
# simulator it's physically called on -- the
|
|
1462
|
+
# projection it performs reflects the "lab
|
|
1463
|
+
# frame" ground truth for whatever it's checking,
|
|
1464
|
+
# not an independent per-patch fact. Since every
|
|
1465
|
+
# replica of a boundary qubit is meant to
|
|
1466
|
+
# represent the SAME logical value, checking that
|
|
1467
|
+
# invariant via any one patch that has a real
|
|
1468
|
+
# gate touching it already captures the full
|
|
1469
|
+
# syndrome -- separately re-checking lq1's other
|
|
1470
|
+
# replicas in their own patches doesn't add new
|
|
1471
|
+
# independent information, it just adds their
|
|
1472
|
+
# own extra gate exposure for no additional
|
|
1473
|
+
# benefit. So: XOR b1's value into ITS OWN
|
|
1474
|
+
# patch's ancilla immediately before the gate,
|
|
1475
|
+
# XOR it in again immediately after, force_m that
|
|
1476
|
+
# one ancilla -- b1 only, not every replica of
|
|
1477
|
+
# lq1.
|
|
1444
1478
|
#
|
|
1445
|
-
#
|
|
1446
|
-
#
|
|
1447
|
-
#
|
|
1448
|
-
#
|
|
1449
|
-
#
|
|
1450
|
-
#
|
|
1479
|
+
# Also worth being explicit about: what this
|
|
1480
|
+
# catches is NOT the same thing as the `noise=`
|
|
1481
|
+
# parameter's injected depolarizing/Pauli channel.
|
|
1482
|
+
# It's this architecture's own INTRINSIC error --
|
|
1483
|
+
# the approximation inherent in the elided
|
|
1484
|
+
# shadow-coupling / replica-consistency scheme,
|
|
1485
|
+
# present even at noise=0. Testing this gadget
|
|
1486
|
+
# against `noise=p` instead measures something
|
|
1487
|
+
# different (and actively penalizes it, since the
|
|
1488
|
+
# sandwiching CNOTs are then subject to that same
|
|
1489
|
+
# injected noise too) -- see conversation record.
|
|
1490
|
+
#
|
|
1491
|
+
# Scope: still only the mcx/mcy/mcz path
|
|
1492
|
+
# (_cpauli / _apply_coupling); swap/iswap/etc.
|
|
1493
|
+
# get their own dedicated gadget (_detect_swap),
|
|
1494
|
+
# and the anti-controlled shadow variants get
|
|
1495
|
+
# theirs (_anti_shadow_wrap) -- see those.
|
|
1496
|
+
#
|
|
1497
|
+
# CZ specifically also checks b2, not just b1:
|
|
1498
|
+
# unlike CX/CY, where the target's Z-population
|
|
1499
|
+
# legitimately, intentionally changes (that IS
|
|
1500
|
+
# the entangling operation -- not an error), CZ
|
|
1501
|
+
# is diagonal in the computational basis and
|
|
1502
|
+
# NEVER changes either qubit's Z-population, only
|
|
1503
|
+
# a relative phase. So b2 is an equally valid
|
|
1504
|
+
# invariance check here, for this gate family
|
|
1505
|
+
# only. Combined as one XOR parity (b1^b2), same
|
|
1506
|
+
# pattern as the SWAP gadget: catches either
|
|
1507
|
+
# participant's first-order (O(p)) disturbance,
|
|
1508
|
+
# only misses the second-order (O(p^2)) case
|
|
1509
|
+
# where both are disturbed simultaneously in a
|
|
1510
|
+
# way that cancels in the combined parity.
|
|
1511
|
+
# (Considered and rejected checking b1 in BOTH
|
|
1512
|
+
# Z and X bases instead, to catch phase-type
|
|
1513
|
+
# errors on the control too -- doesn't work: CX/CY
|
|
1514
|
+
# genuinely, intentionally entangle control with
|
|
1515
|
+
# target, which legitimately degrades the
|
|
1516
|
+
# control's own X/Y-basis coherence as correct
|
|
1517
|
+
# gate behavior, not error. A detector for that
|
|
1518
|
+
# would flag every successful entangling gate and
|
|
1519
|
+
# destroy the entanglement the gate was supposed
|
|
1520
|
+
# to create.)
|
|
1521
|
+
check_b2_too = pauli == Pauli.PauliZ
|
|
1451
1522
|
anc = self._detect_ancilla[b1[0]]
|
|
1452
1523
|
sim = self.sim[b1[0]]
|
|
1453
1524
|
sim.mcx([b1[1]], anc)
|
|
1525
|
+
if check_b2_too:
|
|
1526
|
+
sim.mcx([b2[1]], anc)
|
|
1454
1527
|
gate_fn([b1[1]], b2[1])
|
|
1455
1528
|
sim.mcx([b1[1]], anc)
|
|
1529
|
+
if check_b2_too:
|
|
1530
|
+
sim.mcx([b2[1]], anc)
|
|
1456
1531
|
sim.force_m(anc, False)
|
|
1457
1532
|
else:
|
|
1458
1533
|
gate_fn([b1[1]], b2[1])
|
|
@@ -1677,7 +1752,7 @@ class QrackAceBackend:
|
|
|
1677
1752
|
for i in range(len(hq1)):
|
|
1678
1753
|
sim_id, idx1 = hq1[i]
|
|
1679
1754
|
_, idx2 = hq2[i]
|
|
1680
|
-
self.
|
|
1755
|
+
self._detect_swap(sim_id, idx1, idx2)
|
|
1681
1756
|
# No post-swap correction: this branch is a native index
|
|
1682
1757
|
# relabeling applied identically to every matching replica
|
|
1683
1758
|
# pair, with zero Schmidt-truncation opportunity -- verified
|
|
@@ -1706,7 +1781,7 @@ class QrackAceBackend:
|
|
|
1706
1781
|
for _hq2 in non_matching:
|
|
1707
1782
|
self._cx_shadow(_hq1, _hq2)
|
|
1708
1783
|
for _hq2 in matching:
|
|
1709
|
-
self.
|
|
1784
|
+
self._detect_swap(_hq1[0], _hq1[1], _hq2[1])
|
|
1710
1785
|
for _hq2 in non_matching:
|
|
1711
1786
|
self._cx_shadow(_hq1, _hq2)
|
|
1712
1787
|
# No correction on lq1 (bulk, single-replica): already a
|
|
@@ -1725,7 +1800,7 @@ class QrackAceBackend:
|
|
|
1725
1800
|
for _hq1 in non_matching:
|
|
1726
1801
|
self._cx_shadow(_hq2, _hq1)
|
|
1727
1802
|
for _hq1 in matching:
|
|
1728
|
-
self.
|
|
1803
|
+
self._detect_swap(_hq2[0], _hq2[1], _hq1[1])
|
|
1729
1804
|
for _hq1 in non_matching:
|
|
1730
1805
|
self._cx_shadow(_hq2, _hq1)
|
|
1731
1806
|
self._correct(lq1)
|
|
@@ -2334,7 +2409,7 @@ class QrackAceBackend:
|
|
|
2334
2409
|
return self._coupling_map
|
|
2335
2410
|
|
|
2336
2411
|
# Designed by Dan, Elara (ChatGPT), and (Anthropic) Claude:
|
|
2337
|
-
def create_noise_model(self, x=0.5):
|
|
2412
|
+
def create_noise_model(self, x=0.5, y=0.5):
|
|
2338
2413
|
if not _IS_QISKIT_AER_AVAILABLE:
|
|
2339
2414
|
raise RuntimeError(
|
|
2340
2415
|
"Before trying to run_qiskit_circuit() with QrackAceBackend, you must install Qiskit Aer!"
|
|
@@ -2354,7 +2429,58 @@ class QrackAceBackend:
|
|
|
2354
2429
|
|
|
2355
2430
|
return n / (len(sims1) + len(sims2))
|
|
2356
2431
|
|
|
2357
|
-
|
|
2432
|
+
coupling_map = self.get_logical_coupling_map()
|
|
2433
|
+
|
|
2434
|
+
# Error-detection damping (cx/cy/cz only -- see below for why not
|
|
2435
|
+
# swap/iswap): a qubit only ever gets the gadget's benefit when it
|
|
2436
|
+
# actually plays CONTROL in some real (u=0) gate somewhere in the
|
|
2437
|
+
# coupling map -- that's what gives its replica a chance to be
|
|
2438
|
+
# swept clean via _apply_coupling's capture-sandwich-force_m
|
|
2439
|
+
# before any shadow decision reads its .prob(). This is a
|
|
2440
|
+
# structural fact about each qubit individually, computed once
|
|
2441
|
+
# here, not a property of the circuit or a global constant --
|
|
2442
|
+
# which is exactly why this can't be reproduced by a user just
|
|
2443
|
+
# passing a smaller x: x scales every pair uniformly, and this
|
|
2444
|
+
# doesn't apply uniformly, either across qubits (only ones with a
|
|
2445
|
+
# real-gate opportunity qualify) or across gate types (see below).
|
|
2446
|
+
#
|
|
2447
|
+
# Both sides of a real (u=0) pair get added, not just the control:
|
|
2448
|
+
# CZ's own gadget check now covers its target too (CZ is diagonal
|
|
2449
|
+
# -- unlike CX/CY, it never changes either qubit's Z-population,
|
|
2450
|
+
# so the target is an equally valid invariance check there, see
|
|
2451
|
+
# _apply_coupling). The coupling map doesn't distinguish which
|
|
2452
|
+
# gate type a future circuit will actually use for a given pair,
|
|
2453
|
+
# so this necessarily overstates the CX/CY case slightly (where
|
|
2454
|
+
# only the control gets swept) -- an approximation on top of an
|
|
2455
|
+
# already-approximate placeholder, not a new source of precision
|
|
2456
|
+
# lost.
|
|
2457
|
+
has_real_partner = set()
|
|
2458
|
+
if self.is_error_detection:
|
|
2459
|
+
for lq1, lq2 in coupling_map:
|
|
2460
|
+
if _uncommon_sim_fraction(lq1, lq2) == 0:
|
|
2461
|
+
has_real_partner.add(lq1)
|
|
2462
|
+
has_real_partner.add(lq2)
|
|
2463
|
+
|
|
2464
|
+
# This factor is a theory-derived placeholder, not an empirical
|
|
2465
|
+
# fit -- deliberately, per instruction not to chase a regression
|
|
2466
|
+
# right now. It reflects that the gadget's control-invariance
|
|
2467
|
+
# check is a Z-basis check: it exactly catches the X/Y-type
|
|
2468
|
+
# disturbances that would degrade a Z-basis .prob() reading (the
|
|
2469
|
+
# only thing _cz_shadow's decision actually depends on), while a
|
|
2470
|
+
# Z-type disturbance on the control doesn't degrade that reading
|
|
2471
|
+
# in the first place and so was never a threat to the shadow
|
|
2472
|
+
# decision to begin with. That argument alone would suggest
|
|
2473
|
+
# damping close to the full X/Y-type share of the channel -- but
|
|
2474
|
+
# this is a STRUCTURAL possibility (the control CAN be swept
|
|
2475
|
+
# clean somewhere in the map), not a per-execution guarantee that
|
|
2476
|
+
# it WAS, before any specific shadow read in a specific circuit.
|
|
2477
|
+
# 0.5 is a deliberately conservative middle estimate given that
|
|
2478
|
+
# gap, not a derived bound; replace with a fitted value once (or
|
|
2479
|
+
# if) empirical calibration against real XEB data happens.
|
|
2480
|
+
# _ERROR_DETECTION_DAMPING = 0.5
|
|
2481
|
+
# (Use "y" instead.)
|
|
2482
|
+
|
|
2483
|
+
for a, b in coupling_map:
|
|
2358
2484
|
u = _uncommon_sim_fraction(a, b)
|
|
2359
2485
|
c = 1 - u
|
|
2360
2486
|
|
|
@@ -2377,6 +2503,14 @@ class QrackAceBackend:
|
|
|
2377
2503
|
# Pauli-string convention is big-endian relative to the qubit
|
|
2378
2504
|
# list: the LAST qubit corresponds to the LEFTMOST character).
|
|
2379
2505
|
p_shadow_ccz = 1 - sp * p
|
|
2506
|
+
if a in has_real_partner:
|
|
2507
|
+
# a is the control for this pair (_cpauli(lq1=a, lq2=b,
|
|
2508
|
+
# ...) is always called with a as the coupling source),
|
|
2509
|
+
# and a has at least one real-gate opportunity elsewhere
|
|
2510
|
+
# in the map -- so its .prob() reading, which THIS
|
|
2511
|
+
# shadow decision partly depends on, has a structural
|
|
2512
|
+
# chance of having been kept clean. Damp accordingly.
|
|
2513
|
+
p_shadow_ccz *= y
|
|
2380
2514
|
noise_model.add_quantum_error(
|
|
2381
2515
|
pauli_error([("ZI", p_shadow_ccz), ("II", 1 - p_shadow_ccz)]), "cz", [a, b]
|
|
2382
2516
|
)
|
|
@@ -2429,6 +2563,22 @@ class QrackAceBackend:
|
|
|
2429
2563
|
# same condition to stay consistent with what the gate does.
|
|
2430
2564
|
if (is_a_simple != is_b_simple) and has_match:
|
|
2431
2565
|
p_net_swap = 2 * p * (1 - p)
|
|
2566
|
+
# swap() now routes its own matching-replica gate through
|
|
2567
|
+
# _detect_swap (a genuine same-simulator, parity-invariant
|
|
2568
|
+
# gadget: idx1^idx2 is exactly conserved across a SWAP,
|
|
2569
|
+
# the same way a CNOT's control is conserved -- see
|
|
2570
|
+
# _detect_swap), so this IS damped now, unlike the stale
|
|
2571
|
+
# "swap/iswap deliberately not damped" note this replaced.
|
|
2572
|
+
# Gate on whichever side bears the error (the boundary
|
|
2573
|
+
# qubit), using the same has_real_partner structural
|
|
2574
|
+
# proxy as the cx/cy/cz case above -- the matching-replica
|
|
2575
|
+
# branch needs the same "shares a simulator with
|
|
2576
|
+
# something else" precondition _uncommon_sim_fraction==0
|
|
2577
|
+
# already captures, so reusing that set here keeps this
|
|
2578
|
+
# consistent rather than re-deriving a parallel condition.
|
|
2579
|
+
boundary_qubit = b if is_a_simple else a
|
|
2580
|
+
if boundary_qubit in has_real_partner:
|
|
2581
|
+
p_net_swap *= y
|
|
2432
2582
|
if is_a_simple:
|
|
2433
2583
|
# a is bulk, b is boundary -> error lands on b
|
|
2434
2584
|
noise_model.add_quantum_error(
|
|
@@ -2460,6 +2610,14 @@ class QrackAceBackend:
|
|
|
2460
2610
|
# previous formula.
|
|
2461
2611
|
if (is_a_simple != is_b_simple) and has_match:
|
|
2462
2612
|
p_cz = 1 - sp * p # same per-call formula as standalone cz
|
|
2613
|
+
if boundary_qubit in has_real_partner:
|
|
2614
|
+
# this cz component is the SAME kind of shadow-driven
|
|
2615
|
+
# term as the main cx/cy/cz loop above (see that
|
|
2616
|
+
# comment for the mechanism); p_net_swap was already
|
|
2617
|
+
# damped above, independently, since it comes from a
|
|
2618
|
+
# different gate (the now-protected native swap, not
|
|
2619
|
+
# this cz component).
|
|
2620
|
+
p_cz *= y
|
|
2463
2621
|
p_i = (1 - p_net_swap) * (1 - p_cz)
|
|
2464
2622
|
p_x = p_net_swap * (1 - p_cz)
|
|
2465
2623
|
p_z = (1 - p_net_swap) * p_cz
|
|
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
|