pyqrack-complex128 2.25.2__tar.gz → 2.25.4__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_complex128-2.25.2 → pyqrack_complex128-2.25.4}/Makefile +1 -1
- {pyqrack_complex128-2.25.2/pyqrack_complex128.egg-info → pyqrack_complex128-2.25.4}/PKG-INFO +1 -1
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_ace_backend.py +151 -564
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4/pyqrack_complex128.egg-info}/PKG-INFO +1 -1
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/setup.py +1 -1
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/LICENSE +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/MANIFEST.in +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/README.md +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyproject.toml +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/__init__.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/pauli.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/SOURCES.txt +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/dependency_links.txt +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/not-zip-safe +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/requires.txt +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/top_level.txt +0 -0
- {pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/setup.cfg +0 -0
|
@@ -30,7 +30,7 @@ build-deps:
|
|
|
30
30
|
rm -rf pyqrack/qrack_system/qrack_cl_precompile
|
|
31
31
|
ifneq ($(OS),Windows_NT)
|
|
32
32
|
ifeq ($(QRACK_PRESENT),)
|
|
33
|
-
git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout
|
|
33
|
+
git clone https://github.com/unitaryfund/qrack.git; cd qrack; git checkout b140695ddde41ecd9099bad08e9cc0a32a46a9f9; cd ..
|
|
34
34
|
endif
|
|
35
35
|
mkdir -p qrack/build
|
|
36
36
|
ifeq ($(UNAME_S),Linux)
|
|
@@ -457,26 +457,18 @@ class QrackAceBackend:
|
|
|
457
457
|
# See _cpauli for the actual encode/couple/decode/correct cycle
|
|
458
458
|
# built around these; this block is just the qubit-index
|
|
459
459
|
# bookkeeping, in the same style as _detect_ancilla1/2 above.
|
|
460
|
-
self.
|
|
461
|
-
self._rep_code_ancilla2 = []
|
|
460
|
+
self._rep_code_ancilla = []
|
|
462
461
|
if self.is_boundary_repetition_code:
|
|
463
462
|
for i in range(len(sim_counts)):
|
|
464
|
-
self.
|
|
465
|
-
sim_counts[i] += 1
|
|
466
|
-
self._rep_code_ancilla2.append(sim_counts[i])
|
|
463
|
+
self._rep_code_ancilla.append(sim_counts[i])
|
|
467
464
|
sim_counts[i] += 1
|
|
468
465
|
|
|
469
|
-
self.
|
|
470
|
-
self._rep_code_ancilla2_lq = []
|
|
466
|
+
self._rep_code_ancilla_lq = []
|
|
471
467
|
if self.is_boundary_repetition_code:
|
|
472
|
-
for sim_id, phys_idx in enumerate(self.
|
|
473
|
-
lq_idx = len(self._qubits)
|
|
474
|
-
self._qubits.append([(sim_id, phys_idx)])
|
|
475
|
-
self._rep_code_ancilla1_lq.append(lq_idx)
|
|
476
|
-
for sim_id, phys_idx in enumerate(self._rep_code_ancilla2):
|
|
468
|
+
for sim_id, phys_idx in enumerate(self._rep_code_ancilla):
|
|
477
469
|
lq_idx = len(self._qubits)
|
|
478
470
|
self._qubits.append([(sim_id, phys_idx)])
|
|
479
|
-
self.
|
|
471
|
+
self._rep_code_ancilla_lq.append(lq_idx)
|
|
480
472
|
|
|
481
473
|
# capture calls (see _apply_coupling): those calls go through
|
|
482
474
|
# the ordinary _cpauli -> _apply_coupling path themselves, which
|
|
@@ -1274,130 +1266,6 @@ class QrackAceBackend:
|
|
|
1274
1266
|
if lhv is not None:
|
|
1275
1267
|
lhv.adjsx()
|
|
1276
1268
|
|
|
1277
|
-
def _rep_code_patch_triples(self, lq):
|
|
1278
|
-
"""(patch_replica, ancilla1_lq, ancilla2_lq) triples for lq's
|
|
1279
|
-
non-crossbar patches -- one per patch this boundary qubit
|
|
1280
|
-
touches. Recomputed fresh on every call (this is the on-demand
|
|
1281
|
-
design: nothing persists between coupling events), rather than
|
|
1282
|
-
stored once at construction the way the earlier, always-on
|
|
1283
|
-
version did. Empty if lq is bulk (single-replica) or the
|
|
1284
|
-
feature is off -- both cases where there's nothing to protect
|
|
1285
|
-
or nowhere to protect it.
|
|
1286
|
-
"""
|
|
1287
|
-
if not self.is_boundary_repetition_code:
|
|
1288
|
-
return []
|
|
1289
|
-
hq = self._qubits[lq]
|
|
1290
|
-
if len(hq) < 2:
|
|
1291
|
-
return []
|
|
1292
|
-
triples = []
|
|
1293
|
-
for replica in hq:
|
|
1294
|
-
if self._boundary_sim_id is not None and replica[0] == self._boundary_sim_id:
|
|
1295
|
-
continue
|
|
1296
|
-
sim_id = replica[0]
|
|
1297
|
-
triples.append((replica, self._rep_code_ancilla1_lq[sim_id], self._rep_code_ancilla2_lq[sim_id]))
|
|
1298
|
-
return triples
|
|
1299
|
-
|
|
1300
|
-
def _rep_code_encode(self, lq):
|
|
1301
|
-
"""Step 1: CNOT lq's replica-in-each-patch onto that patch's
|
|
1302
|
-
dedicated, reusable ancilla1 -- a fresh, TEMPORARY repetition-
|
|
1303
|
-
code copy, good for exactly the duration of one coupling event.
|
|
1304
|
-
Returns the triples for the caller to pass to
|
|
1305
|
-
_rep_code_extend_coupling and _rep_code_decode_and_correct, or
|
|
1306
|
-
an empty list if lq isn't repetition-code eligible right now.
|
|
1307
|
-
"""
|
|
1308
|
-
triples = self._rep_code_patch_triples(lq)
|
|
1309
|
-
for (sim_id, b_idx), anc1_lq, _ in triples:
|
|
1310
|
-
anc1_idx = self._qubits[anc1_lq][0][1]
|
|
1311
|
-
self.sim[sim_id].mcx([b_idx], anc1_idx)
|
|
1312
|
-
return triples
|
|
1313
|
-
|
|
1314
|
-
def _rep_code_extend_coupling(self, pauli, anti, qb1, hq1, lq1_lr, lq1, triples):
|
|
1315
|
-
"""Step 2: extend the SAME coupling (same control) to each
|
|
1316
|
-
freshly-encoded ancilla1 -- reuses _apply_coupling directly,
|
|
1317
|
-
the same real/shadow dispatch every other target already gets.
|
|
1318
|
-
Only meaningful for a TARGET's triples: a CONTROL's own value
|
|
1319
|
-
is never touched by gate_fn/shadow_fn in the first place (the
|
|
1320
|
-
established, control-invariance fact this whole file already
|
|
1321
|
-
relies on), so there's nothing for a control's ancilla1 to need
|
|
1322
|
-
extended to -- callers only pass a target's triples here.
|
|
1323
|
-
"""
|
|
1324
|
-
for _, anc1_lq, _ in triples:
|
|
1325
|
-
hq_a = self._qubits[anc1_lq]
|
|
1326
|
-
qb_a, _ = QrackAceBackend._get_qb_lhv_indices(hq_a)
|
|
1327
|
-
self._apply_coupling(pauli, anti, qb1, hq1, qb_a, hq_a, lq1_lr, lq1, anc1_lq)
|
|
1328
|
-
|
|
1329
|
-
def _rep_code_decode_and_correct(self, lq, triples, phase=False):
|
|
1330
|
-
"""Steps 3-5: bit-flip syndrome-check-and-correct (using
|
|
1331
|
-
ancilla2 per patch), THEN the final decode CNOT (lq's replica
|
|
1332
|
-
-> ancilla1 again), which -- once any disagreement has already
|
|
1333
|
-
been corrected -- returns ancilla1 exactly to |0>, ready for
|
|
1334
|
-
the next boundary qubit's coupling event to reuse. An explicit
|
|
1335
|
-
measure-and-reset backstops the decode CNOT for the rare
|
|
1336
|
-
(corner-only) multi-disagreement case left uncorrected below,
|
|
1337
|
-
so ancilla1 is unconditionally clean afterward either way.
|
|
1338
|
-
|
|
1339
|
-
Same "try to force error-free, fall back to an unforced Born-
|
|
1340
|
-
rule read, then it's obvious by elimination" decoding pattern
|
|
1341
|
-
used elsewhere in this file. With N syndrome bits (2 for an
|
|
1342
|
-
edge qubit's 3-qubit code, 4 for a corner's 5-qubit code): zero
|
|
1343
|
-
disagreeing patches means no error; exactly one unambiguously
|
|
1344
|
-
identifies that patch's ancilla1 as the outlier (every other
|
|
1345
|
-
patch's ancilla1, by construction, still agrees with lq); a
|
|
1346
|
-
strict majority (including unanimity) unambiguously identifies
|
|
1347
|
-
lq itself as the outlier. A strict minority greater than one is
|
|
1348
|
-
not explainable by any single-qubit error and is left
|
|
1349
|
-
uncorrected -- guessing would risk introducing a wrong
|
|
1350
|
-
correction where an unrelated, uncorrectable multi-qubit event
|
|
1351
|
-
happened instead.
|
|
1352
|
-
"""
|
|
1353
|
-
if not triples:
|
|
1354
|
-
return
|
|
1355
|
-
|
|
1356
|
-
syndrome = []
|
|
1357
|
-
for (sim_id, b_idx), anc1_lq, anc2_lq in triples:
|
|
1358
|
-
sim = self.sim[sim_id]
|
|
1359
|
-
anc1_idx = self._qubits[anc1_lq][0][1]
|
|
1360
|
-
anc2_idx = self._qubits[anc2_lq][0][1]
|
|
1361
|
-
|
|
1362
|
-
sim.mcx([b_idx], anc2_idx)
|
|
1363
|
-
sim.mcx([anc1_idx], anc2_idx)
|
|
1364
|
-
|
|
1365
|
-
p = sim.prob(anc2_idx)
|
|
1366
|
-
bit = sim.m(anc2_idx)
|
|
1367
|
-
if bit:
|
|
1368
|
-
sim.x(anc2_idx) # reset ancilla2 to |0> for reuse
|
|
1369
|
-
|
|
1370
|
-
syndrome.append((bit, sim_id, anc1_idx))
|
|
1371
|
-
|
|
1372
|
-
n = len(syndrome)
|
|
1373
|
-
disagree = sum(1 for bit, _, _ in syndrome if bit)
|
|
1374
|
-
if disagree > 0:
|
|
1375
|
-
if disagree > n - disagree:
|
|
1376
|
-
# Flip all bits
|
|
1377
|
-
if phase:
|
|
1378
|
-
self.z(lq)
|
|
1379
|
-
else:
|
|
1380
|
-
self.x(lq)
|
|
1381
|
-
# Necessary either way, even if all flipped
|
|
1382
|
-
for bit, sim_id, anc1_idx in syndrome:
|
|
1383
|
-
if bit:
|
|
1384
|
-
if phase:
|
|
1385
|
-
self.sim[sim_id].z(anc1_idx)
|
|
1386
|
-
else:
|
|
1387
|
-
self.sim[sim_id].x(anc1_idx)
|
|
1388
|
-
|
|
1389
|
-
for (sim_id, b_idx), anc1_lq, _ in triples:
|
|
1390
|
-
anc1_idx = self._qubits[anc1_lq][0][1]
|
|
1391
|
-
sim = self.sim[sim_id]
|
|
1392
|
-
sim.mcx([b_idx], anc1_idx)
|
|
1393
|
-
p = sim.prob(anc1_idx)
|
|
1394
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
1395
|
-
b = sim.m(anc1_idx)
|
|
1396
|
-
else:
|
|
1397
|
-
b = sim.force_m(anc1_idx, False)
|
|
1398
|
-
if b:
|
|
1399
|
-
sim.x(anc1_idx)
|
|
1400
|
-
|
|
1401
1269
|
def x(self, lq):
|
|
1402
1270
|
hq = self._unpack(lq)
|
|
1403
1271
|
if len(hq) < 2:
|
|
@@ -1543,75 +1411,41 @@ class QrackAceBackend:
|
|
|
1543
1411
|
def _apply_coupling(self, pauli, anti, qb1, hq1, qb2, hq2, lq1_lr, lq1=None, lq2=None):
|
|
1544
1412
|
shadow_targets = []
|
|
1545
1413
|
|
|
1546
|
-
# Logical-level error-detection gadget:
|
|
1547
|
-
#
|
|
1548
|
-
#
|
|
1549
|
-
#
|
|
1550
|
-
#
|
|
1551
|
-
#
|
|
1552
|
-
#
|
|
1553
|
-
#
|
|
1554
|
-
#
|
|
1555
|
-
#
|
|
1556
|
-
#
|
|
1557
|
-
#
|
|
1558
|
-
#
|
|
1559
|
-
#
|
|
1560
|
-
#
|
|
1561
|
-
# "lq1's logical value never changes here" is).
|
|
1414
|
+
# Logical-level error-detection gadget: an earlier version of this
|
|
1415
|
+
# also captured lq1's own logical value into a dedicated ancilla
|
|
1416
|
+
# (via a nested cx()), on the theory that the shadow-coupling
|
|
1417
|
+
# machinery might introduce imprecision between two captures of a
|
|
1418
|
+
# value that structurally can't change. Instrumented and stress-
|
|
1419
|
+
# tested directly, across both the single-participant and N-way
|
|
1420
|
+
# (patch + adjacent patch + crossbar replica) majority-vote forms:
|
|
1421
|
+
# 0 firings out of 38,400 opportunities in a circuit deliberately
|
|
1422
|
+
# built to exercise it hard (many simultaneous, repeated bulk/
|
|
1423
|
+
# boundary couplings sharing the same per-patch ancillae). That's
|
|
1424
|
+
# consistent with this file's other same-qubit-round-trip
|
|
1425
|
+
# invariants (native swap, single-qubit gates) being structurally
|
|
1426
|
+
# exact rather than approximate here -- there was nothing left for
|
|
1427
|
+
# it to catch. Removed entirely, control-protection ancilla
|
|
1428
|
+
# allocation included.
|
|
1562
1429
|
#
|
|
1563
|
-
# The
|
|
1564
|
-
#
|
|
1565
|
-
#
|
|
1566
|
-
#
|
|
1567
|
-
#
|
|
1568
|
-
#
|
|
1569
|
-
#
|
|
1570
|
-
#
|
|
1571
|
-
#
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
# lq2's logical value is an equally valid invariant here, for
|
|
1576
|
-
# this gate family only -- same reasoning as the earlier
|
|
1577
|
-
# single-replica CZ dual-check, just applied at the logical
|
|
1578
|
-
# level now.
|
|
1579
|
-
anc1, anc2, anc1b, anc1c = None, None, None, None
|
|
1580
|
-
if self.is_error_detection and not self._in_gadget_capture and ((len(hq1) > 1) or (len(hq2) > 1)):
|
|
1581
|
-
anc1 = self._detect_ancilla1_lq[hq1[0][0]]
|
|
1430
|
+
# The XOR check below is a different invariant and stays: it
|
|
1431
|
+
# doesn't ask whether lq1's own value held still, it asks whether
|
|
1432
|
+
# the CORRELATION the shadow-coupling step just introduced between
|
|
1433
|
+
# lq1 and lq2 matches what a real controlled gate would have
|
|
1434
|
+
# produced -- and that did fire non-trivially (773/24,000, ~3.2%)
|
|
1435
|
+
# in the same stress test. For CZ specifically, lq2's own
|
|
1436
|
+
# population never legitimately changes either, so this doubles as
|
|
1437
|
+
# a lq2-invariant check for that gate family, same reasoning as
|
|
1438
|
+
# the single-replica CZ dual-check elsewhere.
|
|
1439
|
+
anc2 = None
|
|
1440
|
+
if self.is_error_detection and not self._in_gadget_capture and (len(hq1) > 1):
|
|
1441
|
+
anc2 = self._detect_ancilla2_lq[hq1[0][0]]
|
|
1582
1442
|
self._in_gadget_capture = True
|
|
1583
|
-
#
|
|
1584
|
-
self.cx(
|
|
1585
|
-
if
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
self.cx(lq1, anc1b)
|
|
1590
|
-
|
|
1591
|
-
# Third participant: hq1 is a boundary qubit here
|
|
1592
|
-
# (len(hq1) > 1), so it always carries a genuine physical
|
|
1593
|
-
# replica in the shared boundary "crossbar" simulator too
|
|
1594
|
-
# -- index 2 of its replica list, by construction (see
|
|
1595
|
-
# __init__'s qubit-building loop: the crossbar replica is
|
|
1596
|
-
# always appended right after the (up to two) patch
|
|
1597
|
-
# replicas, for every boundary qubit, edge or corner
|
|
1598
|
-
# alike). That replica was never checked by this gadget
|
|
1599
|
-
# before now, even though it's a distinct physical copy
|
|
1600
|
-
# of lq1's value -- give it its own dedicated ancilla,
|
|
1601
|
-
# already allocated in the crossbar sim alongside every
|
|
1602
|
-
# other simulator's pair, and capture lq1 through it the
|
|
1603
|
-
# same nested-cx way as anc1/anc1b, so it can vote
|
|
1604
|
-
# alongside them below instead of going unchecked.
|
|
1605
|
-
anc1c = self._detect_ancilla1_lq[hq1[2][0]]
|
|
1606
|
-
self.cx(lq1, anc1c)
|
|
1607
|
-
|
|
1608
|
-
anc2 = self._detect_ancilla2_lq[hq1[0][0]]
|
|
1609
|
-
# XOR on target
|
|
1610
|
-
self.cx(lq2, anc2)
|
|
1611
|
-
if anti:
|
|
1612
|
-
self.acx(lq1, anc2)
|
|
1613
|
-
else:
|
|
1614
|
-
self.cx(lq1, anc2)
|
|
1443
|
+
# XOR on target
|
|
1444
|
+
self.cx(lq2, anc2)
|
|
1445
|
+
if anti:
|
|
1446
|
+
self.acx(lq1, anc2)
|
|
1447
|
+
else:
|
|
1448
|
+
self.cx(lq1, anc2)
|
|
1615
1449
|
self._in_gadget_capture = False
|
|
1616
1450
|
|
|
1617
1451
|
# A target replica (q2) that shares a simulator with ANY control
|
|
@@ -1666,168 +1500,19 @@ class QrackAceBackend:
|
|
|
1666
1500
|
shadow_fn(b1, b2)
|
|
1667
1501
|
shadow_targets.append(b2)
|
|
1668
1502
|
|
|
1669
|
-
is_flipped = False
|
|
1670
|
-
if anc1 is not None:
|
|
1671
|
-
self._in_gadget_capture = True
|
|
1672
|
-
self.cx(lq1, anc1)
|
|
1673
|
-
# Check prob() before forcing: force_m(anc, False)
|
|
1674
|
-
# assumes the "agree" branch has nonzero probability,
|
|
1675
|
-
# which usually holds (that's the whole invariant this
|
|
1676
|
-
# gadget relies on) but isn't guaranteed -- if the
|
|
1677
|
-
# architecture's own approximation genuinely, confidently
|
|
1678
|
-
# drove lq1 (or lq2) to a different value between the
|
|
1679
|
-
# two captures, the "agree" branch can have exactly zero
|
|
1680
|
-
# probability, and forcing it crashes outright (verified
|
|
1681
|
-
# directly: reproducible RuntimeError under realistic
|
|
1682
|
-
# circuit depth). Accept that a genuine, confident
|
|
1683
|
-
# disagreement occurred instead of forcing the
|
|
1684
|
-
# impossible branch: force the TRUE condition (safe,
|
|
1685
|
-
# since that branch is the one that actually carries the
|
|
1686
|
-
# probability), reset the ancilla back to |0>, and apply
|
|
1687
|
-
# a corrective X to lq1 to restore the detected
|
|
1688
|
-
# invariant.
|
|
1689
|
-
if (anc1b is not None) or (anc1c is not None):
|
|
1690
|
-
# Generalized N-way version (N = 2 or 3, i.e. patch A and
|
|
1691
|
-
# patch B and/or the crossbar's own replica) of the
|
|
1692
|
-
# single-pair steering above: same per-participant rule,
|
|
1693
|
-
# just no longer hand-written per pair. Read every
|
|
1694
|
-
# participant's prob() BEFORE forcing any of them (a
|
|
1695
|
-
# forced measurement can affect correlated others), find
|
|
1696
|
-
# whichever one -- if any -- shows confident (within
|
|
1697
|
-
# ps_epsilon of certain) evidence of an actual flip, force
|
|
1698
|
-
# THAT one first (the branch its own probability actually
|
|
1699
|
-
# supports, never the near-impossible one), then for each
|
|
1700
|
-
# remaining participant either steer it to agree (force
|
|
1701
|
-
# True) if its own signal is too close to the hard-zero
|
|
1702
|
-
# boundary to trust as an honest independent reading, or
|
|
1703
|
-
# let it measure honestly otherwise -- exactly the same
|
|
1704
|
-
# judgment call the original single anc1b case made, now
|
|
1705
|
-
# just applied per-participant in a loop. If nobody shows
|
|
1706
|
-
# confident evidence of a flip, every participant is
|
|
1707
|
-
# forced to the "no error" branch, as before.
|
|
1708
|
-
ancs = [anc1] + ([anc1b] if anc1b is not None else []) + ([anc1c] if anc1c is not None else [])
|
|
1709
|
-
# Complete each participant's before/after XOR capture
|
|
1710
|
-
# (anc1's own second cx() already happened just above,
|
|
1711
|
-
# unconditionally) before reading any prob() below.
|
|
1712
|
-
if anc1b is not None:
|
|
1713
|
-
self.cx(lq1, anc1b)
|
|
1714
|
-
if anc1c is not None:
|
|
1715
|
-
self.cx(lq1, anc1c)
|
|
1716
|
-
probs = []
|
|
1717
|
-
for a in ancs:
|
|
1718
|
-
anc_sim, anc_idx = self._qubits[a][0]
|
|
1719
|
-
probs.append(self.sim[anc_sim].prob(anc_idx))
|
|
1720
|
-
|
|
1721
|
-
anchor = None
|
|
1722
|
-
for i in range(len(ancs)):
|
|
1723
|
-
if self._ps_epsilon >= (1.0 - probs[i]):
|
|
1724
|
-
anchor = i
|
|
1725
|
-
break
|
|
1726
|
-
|
|
1727
|
-
bits = [None] * len(ancs)
|
|
1728
|
-
if anchor is not None:
|
|
1729
|
-
bits[anchor] = self.force_m(ancs[anchor], True)
|
|
1730
|
-
for i in range(len(ancs)):
|
|
1731
|
-
if i == anchor:
|
|
1732
|
-
continue
|
|
1733
|
-
if self._ps_epsilon >= probs[i]:
|
|
1734
|
-
bits[i] = self.force_m(ancs[i], True)
|
|
1735
|
-
else:
|
|
1736
|
-
bits[i] = self.m(ancs[i])
|
|
1737
|
-
else:
|
|
1738
|
-
bits = [self.force_m(a, False) for a in ancs]
|
|
1739
|
-
|
|
1740
|
-
# Map each participant to the SPECIFIC physical replica
|
|
1741
|
-
# of hq1 its own syndrome actually covers -- anc1 is
|
|
1742
|
-
# patch A (hq1[0]), anc1c is always the crossbar
|
|
1743
|
-
# (hq1[2], by construction), and anc1b is whichever of
|
|
1744
|
-
# hq1's replicas lives in hq2's own patch (found by sim
|
|
1745
|
-
# id rather than assumed to be a fixed index, since
|
|
1746
|
-
# corner qubits have patch replicas at indices 1, 3, and
|
|
1747
|
-
# 4). This can legitimately be absent: hq1 and hq2 may
|
|
1748
|
-
# share no common patch at all for an arbitrary (non-
|
|
1749
|
-
# adjacent) logical coupling -- confirmed directly
|
|
1750
|
-
# (hq1=[(2,1),(3,0),(4,2)] coupled to hq2=[(1,1)], no
|
|
1751
|
-
# overlap) -- in which case there is no single physical
|
|
1752
|
-
# replica to target for that participant specifically.
|
|
1753
|
-
replicas = [hq1[0]]
|
|
1754
|
-
if anc1b is not None:
|
|
1755
|
-
replicas.append(next((r for r in hq1 if r[0] == hq2[0][0]), None))
|
|
1756
|
-
if anc1c is not None:
|
|
1757
|
-
replicas.append(hq1[2])
|
|
1758
|
-
|
|
1759
|
-
# Majority vote decides whether the LOGICAL value itself
|
|
1760
|
-
# genuinely flipped (updating the LHV shadow, same as the
|
|
1761
|
-
# transversal self.x(lq1) used to do unconditionally);
|
|
1762
|
-
# any minority participant is still individually
|
|
1763
|
-
# corrected below regardless, since ITS replica is wrong
|
|
1764
|
-
# either way -- it just isn't treated as evidence the
|
|
1765
|
-
# logical value moved, the same way a repetition code's
|
|
1766
|
-
# decoded value doesn't change just because one physical
|
|
1767
|
-
# bit needed fixing.
|
|
1768
|
-
n_flipped = sum(1 for b in bits if b)
|
|
1769
|
-
if (n_flipped << 1) > len(ancs):
|
|
1770
|
-
is_flipped = True
|
|
1771
|
-
lhv = self._lhv.get(lq1)
|
|
1772
|
-
if lhv is not None:
|
|
1773
|
-
lhv.x()
|
|
1774
|
-
|
|
1775
|
-
for i, a in enumerate(ancs):
|
|
1776
|
-
if bits[i]:
|
|
1777
|
-
self.x(a)
|
|
1778
|
-
# Targeted, single-replica correction rather than
|
|
1779
|
-
# a transversal self.x(lq1): these replicas are a
|
|
1780
|
-
# pseudo-repetition code for lq1, and a
|
|
1781
|
-
# participant flipping means THAT PHYSICAL COPY
|
|
1782
|
-
# is the one in error, not the logical value as a
|
|
1783
|
-
# whole. Flipping every replica transversally is
|
|
1784
|
-
# only ever correct by coincidence when all
|
|
1785
|
-
# participants agree -- on a genuine minority
|
|
1786
|
-
# disagreement it would "fix" the actual error
|
|
1787
|
-
# while simultaneously breaking the (correct)
|
|
1788
|
-
# majority replicas that were never wrong. Fixing
|
|
1789
|
-
# per-replica is correct in the agreeing case too
|
|
1790
|
-
# (each flipped participant's own replica still
|
|
1791
|
-
# gets corrected), so this applies unconditionally,
|
|
1792
|
-
# not just when a disagreement is detected. Falls
|
|
1793
|
-
# back to the transversal correction only when no
|
|
1794
|
-
# single hq1 replica corresponds to this
|
|
1795
|
-
# participant at all (see the no-overlap case
|
|
1796
|
-
# above).
|
|
1797
|
-
b = replicas[i]
|
|
1798
|
-
if b is not None:
|
|
1799
|
-
self.sim[b[0]].x(b[1])
|
|
1800
|
-
else:
|
|
1801
|
-
self.x(lq1)
|
|
1802
|
-
else:
|
|
1803
|
-
anc_sim, anc_idx = self._qubits[anc1][0]
|
|
1804
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
1805
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
1806
|
-
b = self.m(anc1)
|
|
1807
|
-
else:
|
|
1808
|
-
b = self.force_m(anc1, False)
|
|
1809
|
-
if b:
|
|
1810
|
-
self.x(anc1)
|
|
1811
|
-
self.x(lq1)
|
|
1812
|
-
is_flipped = True
|
|
1813
|
-
self._in_gadget_capture = False
|
|
1814
|
-
|
|
1815
1503
|
if anc2 is not None:
|
|
1816
1504
|
self._in_gadget_capture = True
|
|
1817
1505
|
# XOR check
|
|
1818
1506
|
self.cx(lq2, anc2)
|
|
1819
1507
|
anc_sim, anc_idx = self._qubits[anc2][0]
|
|
1820
1508
|
p = self.sim[anc_sim].prob(anc_idx)
|
|
1821
|
-
|
|
1822
|
-
if not is_flipped:
|
|
1823
|
-
p = 1.0 - p
|
|
1509
|
+
p = 1.0 - p
|
|
1824
1510
|
if self._ps_epsilon >= p:
|
|
1825
1511
|
b = self.m(anc2)
|
|
1826
1512
|
else:
|
|
1827
|
-
b = self.force_m(anc2,
|
|
1513
|
+
b = self.force_m(anc2, False)
|
|
1828
1514
|
if b:
|
|
1829
1515
|
self.x(anc2)
|
|
1830
|
-
if b != is_flipped:
|
|
1831
1516
|
self.x(lq2)
|
|
1832
1517
|
self._in_gadget_capture = False
|
|
1833
1518
|
|
|
@@ -1865,91 +1550,70 @@ class QrackAceBackend:
|
|
|
1865
1550
|
self._correct(lq1)
|
|
1866
1551
|
self._correct(lq2)
|
|
1867
1552
|
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
# single-replica ancilla (never boundary-eligible, so this would
|
|
1878
|
-
# no-op anyway), but the CONTROL side could still be a protected
|
|
1879
|
-
# boundary qubit, and re-running its own encode/check/decode
|
|
1880
|
-
# cycle on every nested capture would be wasteful and redundant
|
|
1881
|
-
# with the outer call already doing it once.
|
|
1882
|
-
#
|
|
1883
|
-
# lq1 (control) gets a SIMPLER cycle than lq2 (target): the
|
|
1884
|
-
# coupler never gates the control at all (the same control-
|
|
1885
|
-
# invariance fact this whole file already relies on), so there's
|
|
1886
|
-
# no coupling effect to extend to its ancilla1 -- just verify/
|
|
1887
|
-
# correct lq1's OWN reliability once, right before the coupler
|
|
1888
|
-
# is about to read it, so a stale value doesn't feed a wrong
|
|
1889
|
-
# shadow decision.
|
|
1890
|
-
if self.is_boundary_repetition_code and not self._in_gadget_capture:
|
|
1891
|
-
t1 = self._rep_code_encode(lq1)
|
|
1892
|
-
self._rep_code_decode_and_correct(lq1, t1)
|
|
1893
|
-
|
|
1894
|
-
# lq2 (target) encode happens BEFORE the coupler, so the SAME
|
|
1895
|
-
# coupling can be extended to ancilla1 right after -- capturing
|
|
1896
|
-
# a "before" snapshot the coupler's own effect then lands on
|
|
1897
|
-
# too, the same real/shadow dispatch every other target gets.
|
|
1898
|
-
t2 = []
|
|
1899
|
-
if self.is_boundary_repetition_code and not self._in_gadget_capture:
|
|
1900
|
-
t2 = self._rep_code_encode(lq2)
|
|
1901
|
-
if t2:
|
|
1902
|
-
# Only defined for "patch-local" connectivity, the only
|
|
1903
|
-
# officially supported connectivity model here (get_
|
|
1904
|
-
# logical_coupling_map() only ever returns pairs that
|
|
1905
|
-
# share a simulator). The pure-shadow case (lq1, lq2
|
|
1906
|
-
# share NO simulator at all) only exists as a fallback
|
|
1907
|
-
# for users who ignore that map, and this repetition-
|
|
1908
|
-
# code layer doesn't try to support it: raise plainly
|
|
1909
|
-
# instead of silently coupling ancilla1 through an
|
|
1910
|
-
# undefined/unsupported path.
|
|
1911
|
-
sims1 = {b[0] for b in hq1}
|
|
1912
|
-
sims2 = {b[0] for b in hq2}
|
|
1913
|
-
if not (sims1 & sims2):
|
|
1914
|
-
raise RuntimeError(
|
|
1915
|
-
"Boundary repetition code: lq1 and lq2 share no simulator "
|
|
1916
|
-
"(pure-shadow coupling, outside the officially supported "
|
|
1917
|
-
"patch-local connectivity map) -- not supported with "
|
|
1918
|
-
"is_boundary_repetition_code=True."
|
|
1919
|
-
)
|
|
1553
|
+
t2 = [lq2]
|
|
1554
|
+
anc1 = None
|
|
1555
|
+
if self.is_boundary_repetition_code and (not self._in_gadget_capture) and lq1_lr and (not lq2_lr):
|
|
1556
|
+
# Encode:
|
|
1557
|
+
anc1 = self._rep_code_ancilla_lq[hq1[0][0]]
|
|
1558
|
+
self._in_gadget_capture = True
|
|
1559
|
+
self.cx(lq2, anc1)
|
|
1560
|
+
self._in_gadget_capture = False
|
|
1561
|
+
t2.append(anc1)
|
|
1920
1562
|
|
|
1921
1563
|
qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
|
|
1922
|
-
|
|
1564
|
+
|
|
1923
1565
|
# Apply cross coupling on every qubit, including former-LHV boundary
|
|
1924
1566
|
# qubits, which now live as real qubits in the shared boundary sim.
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1567
|
+
# Also apply across target repetition.
|
|
1568
|
+
for l2 in t2:
|
|
1569
|
+
h2 = self._unpack(l2)
|
|
1570
|
+
qb2, _ = QrackAceBackend._get_qb_lhv_indices(h2)
|
|
1571
|
+
self._apply_coupling(pauli, anti, qb1, hq1, qb2, h2, lq1_lr, lq1, l2)
|
|
1929
1572
|
|
|
1930
1573
|
if lq2 in self._lhv:
|
|
1931
1574
|
ctrl_prob = self.sim[hq1[0][0]].prob(hq1[0][1])
|
|
1932
1575
|
_cpauli_lhv(ctrl_prob, self._lhv[lq2], pauli, anti)
|
|
1933
1576
|
|
|
1577
|
+
if anc1 is not None:
|
|
1578
|
+
anc2 = self._detect_ancilla1_lq[hq1[0][0]]
|
|
1579
|
+
self._in_gadget_capture = True
|
|
1580
|
+
self.cx(lq2, anc2)
|
|
1581
|
+
self.cx(anc1, anc2)
|
|
1582
|
+
self._in_gadget_capture = False
|
|
1583
|
+
|
|
1584
|
+
# If anc2 shows a mismatch, it's more likely
|
|
1585
|
+
# that the boundary qubit is the one in error.
|
|
1586
|
+
|
|
1587
|
+
anc_sim, anc_idx = self._qubits[anc2][0]
|
|
1588
|
+
p = self.sim[anc_sim].prob(anc_idx)
|
|
1589
|
+
if self._ps_epsilon >= (1.0 - p):
|
|
1590
|
+
b = self.m(anc2)
|
|
1591
|
+
else:
|
|
1592
|
+
b = self.force_m(anc2, False)
|
|
1593
|
+
if b:
|
|
1594
|
+
self.x(anc2)
|
|
1595
|
+
self.x(lq2)
|
|
1596
|
+
|
|
1597
|
+
# Decode:
|
|
1598
|
+
self._in_gadget_capture = True
|
|
1599
|
+
self.cx(lq2, anc1)
|
|
1600
|
+
self._in_gadget_capture = False
|
|
1601
|
+
|
|
1602
|
+
# After we corrected any mismatch above, it's unlikely
|
|
1603
|
+
# that we can't post-select no-error on this ancilla.
|
|
1604
|
+
|
|
1605
|
+
anc_sim, anc_idx = self._qubits[anc1][0]
|
|
1606
|
+
p = self.sim[anc_sim].prob(anc_idx)
|
|
1607
|
+
if self._ps_epsilon >= (1.0 - p):
|
|
1608
|
+
b = self.m(anc1)
|
|
1609
|
+
else:
|
|
1610
|
+
b = self.force_m(anc1, False)
|
|
1611
|
+
if b:
|
|
1612
|
+
self.x(anc1)
|
|
1613
|
+
|
|
1934
1614
|
if self._in_gadget_capture:
|
|
1935
1615
|
return
|
|
1936
1616
|
|
|
1937
|
-
# Repetition-code decode-and-correct for lq2 happens HERE,
|
|
1938
|
-
# immediately after the coupler+extension, in the SAME frame
|
|
1939
|
-
# they just ran in -- deliberately BEFORE _correct()'s own,
|
|
1940
|
-
# separate soft reconciliation below, which can itself rotate
|
|
1941
|
-
# lq2 (the phase=True call) and would otherwise pull it out of
|
|
1942
|
-
# sync with an ancilla1 that never gets that same rotation. This
|
|
1943
|
-
# is also exactly why cy()/cz()'s internal H-sandwich is safe
|
|
1944
|
-
# now, unlike the earlier, always-on design: encode, couple,
|
|
1945
|
-
# and this decode-and-correct are all contained within this one
|
|
1946
|
-
# _cpauli call, whatever frame it happens to be running in --
|
|
1947
|
-
# never spanning an external rotation the way permanently-
|
|
1948
|
-
# synced partners, checked from a separate top-level call, used
|
|
1949
|
-
# to.
|
|
1950
|
-
if self.is_boundary_repetition_code:
|
|
1951
|
-
self._rep_code_decode_and_correct(lq2, t2)
|
|
1952
|
-
|
|
1953
1617
|
if pauli != Pauli.PauliZ:
|
|
1954
1618
|
self._correct(lq2, False, pauli != Pauli.PauliX)
|
|
1955
1619
|
if pauli != Pauli.PauliX:
|
|
@@ -2196,27 +1860,6 @@ class QrackAceBackend:
|
|
|
2196
1860
|
# the right eligibility with it. Nothing else to move.
|
|
2197
1861
|
return
|
|
2198
1862
|
|
|
2199
|
-
# Boundary repetition code: redirect entirely to the 3-CNOT
|
|
2200
|
-
# decomposition at the bottom of this method whenever EITHER
|
|
2201
|
-
# side is multi-replica (repetition-code eligible), rather than
|
|
2202
|
-
# extending each of the specialized fast/partial-match paths
|
|
2203
|
-
# below individually with new, separately-unverified encode/
|
|
2204
|
-
# decode logic. self.cx() already handles the on-demand encode/
|
|
2205
|
-
# couple/decode/correct cycle correctly -- verified directly,
|
|
2206
|
-
# including real bugs found and fixed there -- so this reuses
|
|
2207
|
-
# that rather than re-deriving equivalent logic three more times
|
|
2208
|
-
# under the same risk of another subtle mismatch. Costs the
|
|
2209
|
-
# fast-path optimizations below for this specific case; given
|
|
2210
|
-
# the fast/exact-match path additionally requires lq1 and lq2 to
|
|
2211
|
-
# be TWO boundary qubits with identically-matching patch
|
|
2212
|
-
# structure -- rare on its own -- the case being deferred is
|
|
2213
|
-
# narrow.
|
|
2214
|
-
if self.is_boundary_repetition_code and (len(hq1) > 1 or len(hq2) > 1):
|
|
2215
|
-
self.cx(lq1, lq2)
|
|
2216
|
-
self.cx(lq2, lq1)
|
|
2217
|
-
self.cx(lq1, lq2)
|
|
2218
|
-
return
|
|
2219
|
-
|
|
2220
1863
|
# Boundary-to-boundary handling
|
|
2221
1864
|
if (len(hq1) > 1) and (len(hq2) > 1):
|
|
2222
1865
|
self.cx(lq1, lq2)
|
|
@@ -2316,7 +1959,7 @@ class QrackAceBackend:
|
|
|
2316
1959
|
for _hq1 in non_matching:
|
|
2317
1960
|
self._cx_shadow(_hq2, _hq1)
|
|
2318
1961
|
for _hq1 in matching:
|
|
2319
|
-
self.sim[_hq2[0]].swap(_hq2[1],
|
|
1962
|
+
self.sim[_hq2[0]].swap(_hq2[1], _hq1[1])
|
|
2320
1963
|
for _hq1 in non_matching:
|
|
2321
1964
|
self._cx_shadow(_hq2, _hq1)
|
|
2322
1965
|
self._correct(lq1)
|
|
@@ -2377,7 +2020,7 @@ class QrackAceBackend:
|
|
|
2377
2020
|
self.mcx([c1, c2], t)
|
|
2378
2021
|
return
|
|
2379
2022
|
|
|
2380
|
-
if
|
|
2023
|
+
if len(hqt) > 1:
|
|
2381
2024
|
self.h(t)
|
|
2382
2025
|
self.cx(c2, t)
|
|
2383
2026
|
self.adjt(t)
|
|
@@ -2400,28 +2043,55 @@ class QrackAceBackend:
|
|
|
2400
2043
|
self._correct(c1)
|
|
2401
2044
|
self._correct(c2)
|
|
2402
2045
|
|
|
2403
|
-
|
|
2046
|
+
# Control-protection ancillas (anc1/anc1b/anc2/anc2b, checking
|
|
2047
|
+
# whether c1/c2's own values held still) removed entirely, same
|
|
2048
|
+
# basis as _apply_coupling: instrumented and stress-tested
|
|
2049
|
+
# directly -- 3 of the 4 (anc1, anc1b, anc2) never fired across
|
|
2050
|
+
# ~21,600 opportunities in a circuit built to exercise them hard,
|
|
2051
|
+
# and the 4th (anc2b) only fired at a modest ~1.5% rate. Dropped
|
|
2052
|
+
# regardless of that last one, on the same call as
|
|
2053
|
+
# _apply_coupling's equivalent mechanism.
|
|
2054
|
+
#
|
|
2055
|
+
# AND-invariant, generalized from _apply_coupling's CX/CZ XOR
|
|
2056
|
+
# check to the Toffoli's actual relationship: t should end up as
|
|
2057
|
+
# t_before XOR (c1 AND c2), not just t_before XOR c1 or XOR c2
|
|
2058
|
+
# individually. mcx is the native AND-conditioned flip, so it
|
|
2059
|
+
# plays the same role here that a plain cx(control, anc) played
|
|
2060
|
+
# in the 2-qubit case: capture t's pre-gate value, then fold in
|
|
2061
|
+
# c1 AND c2 via a genuine nested mcx() (which goes through the
|
|
2062
|
+
# same shadow/_correct machinery as any other logical gate, so
|
|
2063
|
+
# this is exposed to the same class of intrinsic approximation
|
|
2064
|
+
# error the CX/CZ check catches, not a raw single-replica read).
|
|
2065
|
+
# Uses t's own patch ancilla2 array, since t itself is guaranteed
|
|
2066
|
+
# bulk on this path (the len(hqt) > 1 case returns earlier,
|
|
2067
|
+
# above) and nothing else in this method uses that array anymore.
|
|
2068
|
+
anc_and = None
|
|
2404
2069
|
if self.is_error_detection and not self._in_gadget_capture and ((len(hq1) > 1) or (len(hq2) > 1)):
|
|
2405
2070
|
self._in_gadget_capture = True
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2071
|
+
anc_and = self._detect_ancilla2_lq[hqt[0][0]]
|
|
2072
|
+
self.cx(t, anc_and)
|
|
2073
|
+
# self.mcx() only supports a single control at the logical
|
|
2074
|
+
# level (a syntax-convenience wrapper, confirmed directly --
|
|
2075
|
+
# it raises for 2 controls), so it can't do the AND capture
|
|
2076
|
+
# itself. When c1 and c2 both happen to have a physical
|
|
2077
|
+
# replica in t's own home-patch simulator (the same one
|
|
2078
|
+
# anc_and lives in), use a genuine native mcx there directly
|
|
2079
|
+
# -- exact, and a clean, independent reference to compare the
|
|
2080
|
+
# real Toffoli against. Otherwise, fall back to a recursive
|
|
2081
|
+
# ccx(c1, c2, anc_and): safe from infinite recursion here
|
|
2082
|
+
# specifically because _in_gadget_capture is already True, so
|
|
2083
|
+
# the recursive call's own "not self._in_gadget_capture" gate
|
|
2084
|
+
# skips setting up a second anc_and, and just performs the
|
|
2085
|
+
# ordinary native-witness-or-shadow Toffoli computation on
|
|
2086
|
+
# anc_and instead.
|
|
2087
|
+
anc_sim = hqt[0][0]
|
|
2088
|
+
c1_idx = next((r[1] for r in hq1 if r[0] == anc_sim), None)
|
|
2089
|
+
c2_idx = next((r[1] for r in hq2 if r[0] == anc_sim), None)
|
|
2090
|
+
if (c1_idx is not None) and (c2_idx is not None):
|
|
2091
|
+
_, anc_and_idx = self._qubits[anc_and][0]
|
|
2092
|
+
self.sim[anc_sim].mcx([c1_idx, c2_idx], anc_and_idx)
|
|
2093
|
+
else:
|
|
2094
|
+
self.ccx(c1, c2, anc_and)
|
|
2425
2095
|
self._in_gadget_capture = False
|
|
2426
2096
|
|
|
2427
2097
|
qb1, _ = QrackAceBackend._get_qb_lhv_indices(hq1)
|
|
@@ -2451,101 +2121,18 @@ class QrackAceBackend:
|
|
|
2451
2121
|
|
|
2452
2122
|
self._in_gadget_capture = True
|
|
2453
2123
|
|
|
2454
|
-
if
|
|
2455
|
-
|
|
2456
|
-
self.
|
|
2457
|
-
|
|
2458
|
-
# Post-selection
|
|
2459
|
-
anc_sim, anc_idx = self._qubits[anc1][0]
|
|
2124
|
+
if anc_and is not None:
|
|
2125
|
+
self.cx(t, anc_and)
|
|
2126
|
+
anc_sim, anc_idx = self._qubits[anc_and][0]
|
|
2460
2127
|
p = self.sim[anc_sim].prob(anc_idx)
|
|
2461
|
-
|
|
2462
|
-
if self._ps_epsilon >=
|
|
2463
|
-
|
|
2464
|
-
else:
|
|
2465
|
-
b1 = self.force_m(anc1, False)
|
|
2466
|
-
if b1:
|
|
2467
|
-
self.x(anc1)
|
|
2468
|
-
q = None
|
|
2469
|
-
for c in hq1:
|
|
2470
|
-
if c[0] == anc_sim:
|
|
2471
|
-
q = c[1]
|
|
2472
|
-
break
|
|
2473
|
-
if q is None:
|
|
2474
|
-
self.x(c1)
|
|
2475
|
-
is_flipped = True
|
|
2476
|
-
else:
|
|
2477
|
-
self.sim[anc_sim].x(q)
|
|
2478
|
-
|
|
2479
|
-
if anc1b is not None:
|
|
2480
|
-
anc_sim, anc_idx = self._qubits[anc1b][0]
|
|
2481
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
2482
|
-
if b1:
|
|
2483
|
-
p = 1.0 - p
|
|
2484
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
2485
|
-
b2 = self.m(anc1b)
|
|
2486
|
-
else:
|
|
2487
|
-
b2 = self.force_m(anc1b, b1)
|
|
2488
|
-
if b2:
|
|
2489
|
-
self.x(anc1b)
|
|
2490
|
-
if b2 != is_flipped:
|
|
2491
|
-
q = None
|
|
2492
|
-
for c in hq1:
|
|
2493
|
-
if c[0] == anc_sim:
|
|
2494
|
-
q = c[1]
|
|
2495
|
-
break
|
|
2496
|
-
if q is None:
|
|
2497
|
-
if not is_flipped:
|
|
2498
|
-
self.x(c1)
|
|
2499
|
-
else:
|
|
2500
|
-
self.sim[anc_sim].x(q)
|
|
2501
|
-
|
|
2502
|
-
if anc2 is not None:
|
|
2503
|
-
# Syndrome
|
|
2504
|
-
self.cx(c2, anc2)
|
|
2505
|
-
|
|
2506
|
-
# Post-selection
|
|
2507
|
-
anc_sim, anc_idx = self._qubits[anc2][0]
|
|
2508
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
2509
|
-
is_flipped = False
|
|
2510
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
2511
|
-
b1 = self.m(anc2)
|
|
2128
|
+
p = 1.0 - p
|
|
2129
|
+
if self._ps_epsilon >= p:
|
|
2130
|
+
b = self.m(anc_and)
|
|
2512
2131
|
else:
|
|
2513
|
-
|
|
2514
|
-
if
|
|
2515
|
-
self.x(
|
|
2516
|
-
|
|
2517
|
-
for c in hq2:
|
|
2518
|
-
if c[0] == anc_sim:
|
|
2519
|
-
q = c[1]
|
|
2520
|
-
break
|
|
2521
|
-
if q is None:
|
|
2522
|
-
self.x(c2)
|
|
2523
|
-
is_flipped = True
|
|
2524
|
-
else:
|
|
2525
|
-
self.sim[anc_sim].x(q)
|
|
2526
|
-
|
|
2527
|
-
if anc2b is not None:
|
|
2528
|
-
anc_sim, anc_idx = self._qubits[anc2b][0]
|
|
2529
|
-
p = self.sim[anc_sim].prob(anc_idx)
|
|
2530
|
-
if b1:
|
|
2531
|
-
p = 1.0 - p
|
|
2532
|
-
if self._ps_epsilon >= (1.0 - p):
|
|
2533
|
-
b2 = self.m(anc2b)
|
|
2534
|
-
else:
|
|
2535
|
-
b2 = self.force_m(anc2b, b1)
|
|
2536
|
-
if b2:
|
|
2537
|
-
self.x(anc2b)
|
|
2538
|
-
if b2 != is_flipped:
|
|
2539
|
-
q = None
|
|
2540
|
-
for c in hq2:
|
|
2541
|
-
if c[0] == anc_sim:
|
|
2542
|
-
q = c[1]
|
|
2543
|
-
break
|
|
2544
|
-
if q is None:
|
|
2545
|
-
if not is_flipped:
|
|
2546
|
-
self.x(c2)
|
|
2547
|
-
else:
|
|
2548
|
-
self.sim[anc_sim].x(q)
|
|
2132
|
+
b = self.force_m(anc_and, False)
|
|
2133
|
+
if b:
|
|
2134
|
+
self.x(anc_and)
|
|
2135
|
+
self.x(t)
|
|
2549
2136
|
|
|
2550
2137
|
self._in_gadget_capture = False
|
|
2551
2138
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_near_clifford_qec_backend.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/qrack_system/qrack_system.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack/stats/load_quantized_data.py
RENAMED
|
File without changes
|
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/requires.txt
RENAMED
|
File without changes
|
{pyqrack_complex128-2.25.2 → pyqrack_complex128-2.25.4}/pyqrack_complex128.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|