pyqrack-cpu 2.17.2__tar.gz → 2.18.0__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.17.2 → pyqrack_cpu-2.18.0}/Makefile +1 -1
- {pyqrack_cpu-2.17.2/pyqrack_cpu.egg-info → pyqrack_cpu-2.18.0}/PKG-INFO +1 -1
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_ace_backend.py +77 -1
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0/pyqrack_cpu.egg-info}/PKG-INFO +1 -1
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/setup.py +1 -1
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/LICENSE +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/MANIFEST.in +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/README.md +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyproject.toml +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/__init__.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/neuron_activation_fn.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/pauli.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_circuit.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_near_clifford_qec_backend.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_neuron.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_neuron_torch_layer.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_simulator.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_stabilizer.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_system/__init__.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/qrack_system/qrack_system.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/quimb_circuit_type.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/stats/__init__.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/stats/load_quantized_data.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack/stats/quantize_by_range.py +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack_cpu.egg-info/SOURCES.txt +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack_cpu.egg-info/dependency_links.txt +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack_cpu.egg-info/not-zip-safe +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack_cpu.egg-info/requires.txt +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/pyqrack_cpu.egg-info/top_level.txt +0 -0
- {pyqrack_cpu-2.17.2 → pyqrack_cpu-2.18.0}/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 b51b54d8512cd3d57d0eb395556d40b14c35cc96; cd ..
|
|
34
34
|
endif
|
|
35
35
|
mkdir -p qrack/build
|
|
36
36
|
ifeq ($(UNAME_S),Linux)
|
|
@@ -242,6 +242,7 @@ class QrackAceBackend:
|
|
|
242
242
|
history_window=0,
|
|
243
243
|
is_torus=True,
|
|
244
244
|
is_1d_chain=False,
|
|
245
|
+
is_error_detection=True,
|
|
245
246
|
to_clone=None,
|
|
246
247
|
):
|
|
247
248
|
if to_clone:
|
|
@@ -252,6 +253,7 @@ class QrackAceBackend:
|
|
|
252
253
|
history_window = to_clone.history_window
|
|
253
254
|
is_torus = to_clone.is_torus
|
|
254
255
|
is_1d_chain = to_clone.is_1d_chain
|
|
256
|
+
is_error_detection = to_clone.is_error_detection
|
|
255
257
|
if qubit_count < 0:
|
|
256
258
|
qubit_count = 0
|
|
257
259
|
if long_range_columns < 0:
|
|
@@ -266,6 +268,7 @@ class QrackAceBackend:
|
|
|
266
268
|
self.is_transpose = is_transpose
|
|
267
269
|
self.history_window = history_window
|
|
268
270
|
self.is_torus = is_torus
|
|
271
|
+
self.is_error_detection = is_error_detection
|
|
269
272
|
|
|
270
273
|
fppow = 5
|
|
271
274
|
if "QRACK_FPPOW" in os.environ:
|
|
@@ -402,6 +405,20 @@ class QrackAceBackend:
|
|
|
402
405
|
if has_boundary:
|
|
403
406
|
sim_counts.append(boundary_count)
|
|
404
407
|
|
|
408
|
+
# Error-detection gadget (IBM-style detect-and-post-select, not
|
|
409
|
+
# correction): one shared ancilla per simulator -- every patch AND
|
|
410
|
+
# the boundary crossbar sim alike, "a single ancilla per patch,
|
|
411
|
+
# overall" -- reused across every real (same-simulator) coupling
|
|
412
|
+
# gate that touches it, never allocated per-gate or per-qubit.
|
|
413
|
+
# See _apply_coupling for the actual gadget; this block only does
|
|
414
|
+
# the qubit-index bookkeeping, in the same style as every other
|
|
415
|
+
# per-simulator allocation above.
|
|
416
|
+
self._detect_ancilla = []
|
|
417
|
+
if self.is_error_detection:
|
|
418
|
+
for i in range(len(sim_counts)):
|
|
419
|
+
self._detect_ancilla.append(sim_counts[i])
|
|
420
|
+
sim_counts[i] += 1
|
|
421
|
+
|
|
405
422
|
if "QRACK_QUNIT_SEPARABILITY_THRESHOLD" in os.environ:
|
|
406
423
|
self._sdrp = min(1, float(os.environ["QRACK_QUNIT_SEPARABILITY_THRESHOLD"]))
|
|
407
424
|
else:
|
|
@@ -1397,7 +1414,48 @@ class QrackAceBackend:
|
|
|
1397
1414
|
for q2 in qb2:
|
|
1398
1415
|
b2 = hq2[q2]
|
|
1399
1416
|
if b1[0] == b2[0]:
|
|
1400
|
-
|
|
1417
|
+
if self.is_error_detection:
|
|
1418
|
+
# Error-DETECTION gadget (not correction), directly
|
|
1419
|
+
# post-selected via force_m rather than requiring
|
|
1420
|
+
# real repeated shots: b1 is always the CONTROL of
|
|
1421
|
+
# this gate (by construction -- gate_fn is always
|
|
1422
|
+
# called as gate_fn([b1[1]], b2[1])), and a
|
|
1423
|
+
# control's Z value is exactly invariant under a
|
|
1424
|
+
# controlled-Pauli gate in the noiseless case,
|
|
1425
|
+
# regardless of anti/regular control convention or
|
|
1426
|
+
# which Pauli. So: XOR b1's value into the patch's
|
|
1427
|
+
# shared ancilla immediately before the gate, XOR
|
|
1428
|
+
# it in again immediately after -- if nothing
|
|
1429
|
+
# disturbed b1 (no injected noise on the gate
|
|
1430
|
+
# itself or on either sandwiching CNOT), those two
|
|
1431
|
+
# XORs exactly cancel and the ancilla is back to
|
|
1432
|
+
# |0>. force_m(ancilla, False) then directly
|
|
1433
|
+
# projects the whole simulated state onto that
|
|
1434
|
+
# "no disturbance detected" branch and renormalizes
|
|
1435
|
+
# -- this is the exact numerical equivalent of what
|
|
1436
|
+
# a real experiment gets from measure + discard +
|
|
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.
|
|
1444
|
+
#
|
|
1445
|
+
# Scope: this only covers the mcx/mcy/mcz path
|
|
1446
|
+
# (_cpauli / _apply_coupling). swap/iswap/pswap/etc.
|
|
1447
|
+
# exchange both qubits' values rather than leaving
|
|
1448
|
+
# either invariant, so this specific gadget doesn't
|
|
1449
|
+
# apply to them directly -- left as a documented
|
|
1450
|
+
# gap for a later pass, not silently unhandled.
|
|
1451
|
+
anc = self._detect_ancilla[b1[0]]
|
|
1452
|
+
sim = self.sim[b1[0]]
|
|
1453
|
+
sim.mcx([b1[1]], anc)
|
|
1454
|
+
gate_fn([b1[1]], b2[1])
|
|
1455
|
+
sim.mcx([b1[1]], anc)
|
|
1456
|
+
sim.force_m(anc, False)
|
|
1457
|
+
else:
|
|
1458
|
+
gate_fn([b1[1]], b2[1])
|
|
1401
1459
|
elif lq1_lr or (b1[1] == b2[1]) or ((len(qb1) == 2) and (b1[1] == (b2[1] & 1))):
|
|
1402
1460
|
shadow_fn(b1, b2)
|
|
1403
1461
|
shadow_targets.append(b2)
|
|
@@ -1428,6 +1486,24 @@ class QrackAceBackend:
|
|
|
1428
1486
|
hist.append(entry)
|
|
1429
1487
|
self._coupling_history_rev[lq2] = lq1
|
|
1430
1488
|
|
|
1489
|
+
# A "coupled twirling" scheme was tried here -- after every real
|
|
1490
|
+
# inter-logical-qubit coupling gate, SWAP which physical qubit is
|
|
1491
|
+
# registered as hq[side] with one of its level-0 scratch qubits, to
|
|
1492
|
+
# spread the extra gate/noise exposure that coupling gates put on
|
|
1493
|
+
# hq[side] (and never on its scratch pair) around the triple instead
|
|
1494
|
+
# of always landing on the same physical qubit. Measured head-to-head
|
|
1495
|
+
# against scheduled-only correction across a repeated-coupling
|
|
1496
|
+
# scenario and multiple noise levels: it made the logical error rate
|
|
1497
|
+
# WORSE at every noise level tested, roughly 2x at the low end --
|
|
1498
|
+
# each twirl's own 3 extra CNOTs cost more than the exposure
|
|
1499
|
+
# asymmetry it was meant to fix, and scheduled-only correction
|
|
1500
|
+
# already handles repeated real coupling gates well on its own (most
|
|
1501
|
+
# circuits don't leave a boundary qubit persistently entangled with
|
|
1502
|
+
# something external all the way into readout the way the theory
|
|
1503
|
+
# worried about). Removed rather than kept as an opt-in: it has no
|
|
1504
|
+
# regime found so far where it beats scheduled-only, so there's
|
|
1505
|
+
# nothing to gate it behind.
|
|
1506
|
+
|
|
1431
1507
|
def _cpauli(self, lq1, lq2, anti, pauli):
|
|
1432
1508
|
lq1_row = lq1 // self._row_length
|
|
1433
1509
|
lq1_col = lq1 % self._row_length
|
|
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
|