qLDPC 0.4.0__tar.gz → 0.4.2.dev20261004__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.
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/PKG-INFO +26 -13
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/README.md +18 -10
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/pyproject.toml +17 -4
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/pyproject.toml.orig +19 -3
- qldpc-0.4.2.dev20261004/src/qldpc/cache.py +139 -0
- qldpc-0.4.2.dev20261004/src/qldpc/cache_test.py +121 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/benchmarking_test.py +3 -5
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/transversal.py +4 -1
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/transversal_test.py +11 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/code_capacity.py +81 -43
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/code_capacity_test.py +293 -16
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/common.py +185 -147
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/common_test.py +245 -39
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/__init__.py +133 -49
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/error_decoders.py +53 -3
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/error_decoders_test.py +20 -1
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/observable_decoders.py +2 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/capabilities.py +7 -27
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/capabilities_test.py +6 -27
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/common.py +190 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/common_test.py +196 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/__init__.py +3 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/factories.py +58 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/factories_test.py +85 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/legacy.py +492 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/legacy_test.py +454 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/resolution.py +67 -35
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/resolution_test.py +367 -284
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/specs.py +386 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/specs_test.py +573 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/__init__.py +14 -4
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/custom/_lookup_enumeration.py +231 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/custom/_lookup_enumeration_test.py +159 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/composition_test.py +11 -10
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/guf.py +39 -13
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/guf_test.py +36 -39
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/ilp.py +72 -34
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/ilp_test.py +45 -32
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/lookup.py +529 -432
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/lookup_test.py +407 -333
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom_test.py +8 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/dems.py +208 -199
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/dems_test.py +195 -189
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/__init__.py +15 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/frontier.py +329 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/frontier_test.py +289 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/ldpc.py +480 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/ldpc_test.py +221 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/pymatching.py +417 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/pymatching_test.py +220 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/relay_bp.py +339 -43
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/relay_bp_test.py +80 -28
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/tesseract.py +452 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/external/tesseract_test.py +418 -0
- qldpc-0.4.2.dev20261004/src/qldpc/decoders/legacy_api_test.py +233 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/protocols.py +123 -42
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/protocols_test.py +5 -3
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/core.py +43 -85
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/core_test.py +86 -82
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/subgraph.py +4 -6
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/subgraph_test.py +48 -50
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/window.py +10 -14
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/window_test.py +110 -114
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders_test.py +1 -2
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/codes.py +64 -16
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/codes_test.py +33 -6
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/groups.py +2 -2
- qldpc-0.4.0/src/qldpc/cache.py +0 -90
- qldpc-0.4.0/src/qldpc/cache_test.py +0 -60
- qldpc-0.4.0/src/qldpc/decoders/common.py +0 -98
- qldpc-0.4.0/src/qldpc/decoders/common_test.py +0 -88
- qldpc-0.4.0/src/qldpc/decoders/construction/__init__.py +0 -92
- qldpc-0.4.0/src/qldpc/decoders/construction/legacy.py +0 -291
- qldpc-0.4.0/src/qldpc/decoders/construction/legacy_test.py +0 -164
- qldpc-0.4.0/src/qldpc/decoders/construction/specs.py +0 -453
- qldpc-0.4.0/src/qldpc/decoders/construction/specs_test.py +0 -258
- qldpc-0.4.0/src/qldpc/decoders/external/__init__.py +0 -31
- qldpc-0.4.0/src/qldpc/decoders/external/ldpc.py +0 -219
- qldpc-0.4.0/src/qldpc/decoders/external/ldpc_test.py +0 -100
- qldpc-0.4.0/src/qldpc/decoders/external/pymatching.py +0 -267
- qldpc-0.4.0/src/qldpc/decoders/external/pymatching_test.py +0 -111
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/COPYRIGHT +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/LICENSE +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/_util.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/_util_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/_monomials.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/_monomials_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/groups.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/groups_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/linalg.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/linalg_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/ring_array.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/ring_array_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/rings.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/rings_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/wedderburn_artin.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/wedderburn_artin_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/benchmarking.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/bookkeeping.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/bookkeeping_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/common.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/common_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/conftest.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/encoding.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/encoding_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/alpha_syndrome.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/alpha_syndrome_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/memory.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/memory_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/syndrome_measurement.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/syndrome_measurement_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/noise_model.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/noise_model_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/classical.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/classical_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/distance.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/distance_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/monte_carlo.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/monte_carlo_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/quantum.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/quantum_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/conftest.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/observable_decoders_test.py +35 -35
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/conftest.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/composition.py +4 -4
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/_compiler.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/_compiler_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/bridge.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/bridge_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/cheeger.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/cheeger_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/circuit.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/circuit_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/conftest.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/construction.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/construction_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/gadget.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/gadget_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/__init__.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/gap.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/gap_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/groups_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/sqetch.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/external/sqetch_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/math.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/math_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/objects.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/objects_test.py +0 -0
- {qldpc-0.4.0 → qldpc-0.4.2.dev20261004}/src/qldpc/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: qldpc
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.2.dev20261004
|
|
4
4
|
Summary: Tools for constructing and analyzing quantum low density parity check (qLDPC) codes.
|
|
5
5
|
Keywords: LDPC,low density parity check codes,quantum computing,quantum error correction
|
|
6
6
|
Author: Michael A. Perlin
|
|
@@ -32,6 +32,8 @@ Requires-Dist: sinter>=1.16
|
|
|
32
32
|
Requires-Dist: stim>=1.16
|
|
33
33
|
Requires-Dist: sympy>=1.12
|
|
34
34
|
Requires-Dist: tqdm>=4
|
|
35
|
+
Requires-Dist: qldpc[relay-bp] ; extra == 'decoders'
|
|
36
|
+
Requires-Dist: qldpc[tesseract] ; (python_full_version < '3.15' and platform_machine == 'arm64' and sys_platform == 'darwin' and extra == 'decoders') or (python_full_version < '3.15' and platform_machine == 'x86_64' and sys_platform == 'linux' and extra == 'decoders')
|
|
35
37
|
Requires-Dist: checks-superstaq>=0.5.67 ; extra == 'dev'
|
|
36
38
|
Requires-Dist: jupyter>=1.1.1 ; extra == 'dev'
|
|
37
39
|
Requires-Dist: pypandoc-binary>=1.13 ; extra == 'dev'
|
|
@@ -40,22 +42,25 @@ Requires-Dist: pytest>=8 ; extra == 'dev'
|
|
|
40
42
|
Requires-Dist: pytest-randomly>=3.10 ; extra == 'dev'
|
|
41
43
|
Requires-Dist: pytest-socket>=0.7 ; extra == 'dev'
|
|
42
44
|
Requires-Dist: python-lsp-server[all]>=1.14 ; extra == 'dev'
|
|
43
|
-
Requires-Dist: qldpc[docs,gap
|
|
45
|
+
Requires-Dist: qldpc[decoders,docs,gap] ; extra == 'dev'
|
|
44
46
|
Requires-Dist: ipython>=8 ; extra == 'docs'
|
|
45
47
|
Requires-Dist: nbsphinx>=0.9 ; extra == 'docs'
|
|
46
48
|
Requires-Dist: sphinx>=7 ; extra == 'docs'
|
|
47
49
|
Requires-Dist: sphinx-autoapi>=3 ; extra == 'docs'
|
|
48
50
|
Requires-Dist: sphinx-rtd-theme>=2 ; extra == 'docs'
|
|
49
|
-
Requires-Dist: passagemath-gap==10.8.12 ; python_full_version
|
|
51
|
+
Requires-Dist: passagemath-gap==10.8.12 ; python_full_version == '3.13.*' and sys_platform != 'win32' and extra == 'gap'
|
|
50
52
|
Requires-Dist: relay-bp[stim]==0.2.2 ; extra == 'relay-bp'
|
|
53
|
+
Requires-Dist: tesseract-decoder==0.1.1.dev20260910235247 ; python_full_version >= '3.12' and extra == 'tesseract'
|
|
51
54
|
Requires-Python: >=3.11
|
|
52
55
|
Project-URL: Documentation, https://qldpc.readthedocs.io/
|
|
53
56
|
Project-URL: Issues, https://github.com/qLDPCOrg/qLDPC/issues
|
|
54
57
|
Project-URL: Repository, https://github.com/qLDPCOrg/qLDPC
|
|
58
|
+
Provides-Extra: decoders
|
|
55
59
|
Provides-Extra: dev
|
|
56
60
|
Provides-Extra: docs
|
|
57
61
|
Provides-Extra: gap
|
|
58
62
|
Provides-Extra: relay-bp
|
|
63
|
+
Provides-Extra: tesseract
|
|
59
64
|
Description-Content-Type: text/markdown
|
|
60
65
|
|
|
61
66
|
# qLDPC
|
|
@@ -98,14 +103,25 @@ python -m pip install qldpc
|
|
|
98
103
|
|
|
99
104
|
### Optional extras
|
|
100
105
|
|
|
101
|
-
####
|
|
106
|
+
#### Decoders
|
|
102
107
|
|
|
103
|
-
|
|
108
|
+
qLDPC installs `ldpc` and `pymatching` by default, which include a host of decoders.
|
|
109
|
+
Install the optional decoder integrations published as qLDPC extras for your platform with:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
python -m pip install 'qldpc[decoders]'
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
You can also use individual extras to install a specific optional decoder:
|
|
104
116
|
|
|
105
117
|
```bash
|
|
106
118
|
python -m pip install 'qldpc[relay-bp]'
|
|
119
|
+
python -m pip install 'qldpc[tesseract]'
|
|
107
120
|
```
|
|
108
121
|
|
|
122
|
+
The [Tesseract](https://github.com/quantumlib/tesseract-decoder) decoder requires CPython 3.12–3.14 on macOS arm64 or Linux x86-64.
|
|
123
|
+
[Frontier](https://github.com/aleverrier/frontier) is not published on PyPI and is not included in an extra; see the [decoder guide](https://qldpc.readthedocs.io/decoders.html) for availability and build-time installation guidance.
|
|
124
|
+
|
|
109
125
|
#### GAP integration
|
|
110
126
|
|
|
111
127
|
Some algebra tools, code lookups, and distance estimates require [GAP](https://www.gap-system.org).
|
|
@@ -115,10 +131,9 @@ For the recommended in-process integration, install qLDPC with its maintained li
|
|
|
115
131
|
python -m pip install 'qldpc[gap]'
|
|
116
132
|
```
|
|
117
133
|
|
|
118
|
-
qLDPC uses `passagemath-gap` when
|
|
119
|
-
|
|
120
|
-
If
|
|
121
|
-
If that attempt fails and a separate GAP executable is available, qLDPC prints instructions for installing the package for libgap before using the executable instead.
|
|
134
|
+
qLDPC uses `passagemath-gap` when available, so supported GAP operations do not launch a separate process.
|
|
135
|
+
The in-process binding requires Python 3.13 on a non-Windows platform.
|
|
136
|
+
If `passagemath-gap` is unavailable, qLDPC relies on a GAP executable in your `PATH` (possibly obtainable with `conda install -c conda-forge gap`), and a manual copy/paste fallback.
|
|
122
137
|
GAP integration on Windows remains limited; see [issue #294](https://github.com/qLDPCOrg/qLDPC/issues/294).
|
|
123
138
|
|
|
124
139
|
#### `sqetch` distance estimation
|
|
@@ -131,7 +146,7 @@ python -m pip install 'sqetch[gpu] @ git+https://github.com/a7b/yarn.git@e9ce9d0
|
|
|
131
146
|
|
|
132
147
|
This command installs the upstream version of `sqetch` that qLDPC is tested against, with GPU support.
|
|
133
148
|
`sqetch` is not published on PyPI, so it is not available as a qLDPC extra.
|
|
134
|
-
For binary CSS codes, select
|
|
149
|
+
For binary CSS codes, you can select this backend with `code.get_distance_bound(backend="sqetch")`.
|
|
135
150
|
|
|
136
151
|
### Troubleshooting
|
|
137
152
|
|
|
@@ -167,11 +182,9 @@ print("physical qubits:", len(code))
|
|
|
167
182
|
print("logical qubits:", code.dimension)
|
|
168
183
|
```
|
|
169
184
|
|
|
170
|
-
That object is ready for more than a parameter check: inspect its parity checks and logical operators, choose a decoder, estimate a logical error rate, or use it in a memory experiment.
|
|
171
|
-
|
|
172
185
|
### Go deeper: a subsystem lifted-product code
|
|
173
186
|
|
|
174
|
-
|
|
187
|
+
The next example builds a parity-check matrix over the group algebra `GF(2)[C₅]`, lifts each group-ring entry to a 5×5 binary block, and hands the result to `SLPCode`.
|
|
175
188
|
The [subsystem lifted-product construction](https://arxiv.org/abs/2404.18302) produces an 80-qubit CSS subsystem code with 5 logical qubits and 45 gauge qubits.
|
|
176
189
|
|
|
177
190
|
```python
|
|
@@ -38,14 +38,25 @@ python -m pip install qldpc
|
|
|
38
38
|
|
|
39
39
|
### Optional extras
|
|
40
40
|
|
|
41
|
-
####
|
|
41
|
+
#### Decoders
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
qLDPC installs `ldpc` and `pymatching` by default, which include a host of decoders.
|
|
44
|
+
Install the optional decoder integrations published as qLDPC extras for your platform with:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
python -m pip install 'qldpc[decoders]'
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
You can also use individual extras to install a specific optional decoder:
|
|
44
51
|
|
|
45
52
|
```bash
|
|
46
53
|
python -m pip install 'qldpc[relay-bp]'
|
|
54
|
+
python -m pip install 'qldpc[tesseract]'
|
|
47
55
|
```
|
|
48
56
|
|
|
57
|
+
The [Tesseract](https://github.com/quantumlib/tesseract-decoder) decoder requires CPython 3.12–3.14 on macOS arm64 or Linux x86-64.
|
|
58
|
+
[Frontier](https://github.com/aleverrier/frontier) is not published on PyPI and is not included in an extra; see the [decoder guide](https://qldpc.readthedocs.io/decoders.html) for availability and build-time installation guidance.
|
|
59
|
+
|
|
49
60
|
#### GAP integration
|
|
50
61
|
|
|
51
62
|
Some algebra tools, code lookups, and distance estimates require [GAP](https://www.gap-system.org).
|
|
@@ -55,10 +66,9 @@ For the recommended in-process integration, install qLDPC with its maintained li
|
|
|
55
66
|
python -m pip install 'qldpc[gap]'
|
|
56
67
|
```
|
|
57
68
|
|
|
58
|
-
qLDPC uses `passagemath-gap` when
|
|
59
|
-
|
|
60
|
-
If
|
|
61
|
-
If that attempt fails and a separate GAP executable is available, qLDPC prints instructions for installing the package for libgap before using the executable instead.
|
|
69
|
+
qLDPC uses `passagemath-gap` when available, so supported GAP operations do not launch a separate process.
|
|
70
|
+
The in-process binding requires Python 3.13 on a non-Windows platform.
|
|
71
|
+
If `passagemath-gap` is unavailable, qLDPC relies on a GAP executable in your `PATH` (possibly obtainable with `conda install -c conda-forge gap`), and a manual copy/paste fallback.
|
|
62
72
|
GAP integration on Windows remains limited; see [issue #294](https://github.com/qLDPCOrg/qLDPC/issues/294).
|
|
63
73
|
|
|
64
74
|
#### `sqetch` distance estimation
|
|
@@ -71,7 +81,7 @@ python -m pip install 'sqetch[gpu] @ git+https://github.com/a7b/yarn.git@e9ce9d0
|
|
|
71
81
|
|
|
72
82
|
This command installs the upstream version of `sqetch` that qLDPC is tested against, with GPU support.
|
|
73
83
|
`sqetch` is not published on PyPI, so it is not available as a qLDPC extra.
|
|
74
|
-
For binary CSS codes, select
|
|
84
|
+
For binary CSS codes, you can select this backend with `code.get_distance_bound(backend="sqetch")`.
|
|
75
85
|
|
|
76
86
|
### Troubleshooting
|
|
77
87
|
|
|
@@ -107,11 +117,9 @@ print("physical qubits:", len(code))
|
|
|
107
117
|
print("logical qubits:", code.dimension)
|
|
108
118
|
```
|
|
109
119
|
|
|
110
|
-
That object is ready for more than a parameter check: inspect its parity checks and logical operators, choose a decoder, estimate a logical error rate, or use it in a memory experiment.
|
|
111
|
-
|
|
112
120
|
### Go deeper: a subsystem lifted-product code
|
|
113
121
|
|
|
114
|
-
|
|
122
|
+
The next example builds a parity-check matrix over the group algebra `GF(2)[C₅]`, lifts each group-ring entry to a 5×5 binary block, and hands the result to `SLPCode`.
|
|
115
123
|
The [subsystem lifted-product construction](https://arxiv.org/abs/2404.18302) produces an 80-qubit CSS subsystem code with 5 logical qubits and 45 gauge qubits.
|
|
116
124
|
|
|
117
125
|
```python
|
|
@@ -4,7 +4,7 @@ requires = ["uv-build>=0.9.18,<0.13"]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qldpc"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2.dev20261004"
|
|
8
8
|
description = "Tools for constructing and analyzing quantum low density parity check (qLDPC) codes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [
|
|
@@ -53,6 +53,10 @@ name = "Michael A. Perlin"
|
|
|
53
53
|
email = "mika.perlin@gmail.com"
|
|
54
54
|
|
|
55
55
|
[project.optional-dependencies]
|
|
56
|
+
decoders = [
|
|
57
|
+
"qldpc[relay-bp]",
|
|
58
|
+
"qldpc[tesseract]; ((sys_platform=='darwin' and platform_machine=='arm64') or (sys_platform=='linux' and platform_machine=='x86_64')) and python_version<'3.15'",
|
|
59
|
+
]
|
|
56
60
|
dev = [
|
|
57
61
|
"checks-superstaq>=0.5.67",
|
|
58
62
|
"jupyter>=1.1.1",
|
|
@@ -62,7 +66,7 @@ dev = [
|
|
|
62
66
|
"pytest-randomly>=3.10",
|
|
63
67
|
"pytest-socket>=0.7",
|
|
64
68
|
"python-lsp-server[all]>=1.14",
|
|
65
|
-
"qldpc[docs,gap
|
|
69
|
+
"qldpc[decoders,docs,gap]",
|
|
66
70
|
]
|
|
67
71
|
docs = [
|
|
68
72
|
"ipython>=8",
|
|
@@ -71,8 +75,9 @@ docs = [
|
|
|
71
75
|
"sphinx-autoapi>=3",
|
|
72
76
|
"sphinx-rtd-theme>=2",
|
|
73
77
|
]
|
|
74
|
-
gap = ["passagemath-gap==10.8.12; python_version
|
|
78
|
+
gap = ["passagemath-gap==10.8.12; python_version=='3.13' and sys_platform!='win32'"]
|
|
75
79
|
relay-bp = ["relay-bp[stim]==0.2.2"]
|
|
80
|
+
tesseract = ["tesseract-decoder==0.1.1.dev20260910235247; python_version>='3.12'"]
|
|
76
81
|
|
|
77
82
|
[project.urls]
|
|
78
83
|
Documentation = "https://qldpc.readthedocs.io/"
|
|
@@ -88,6 +93,11 @@ ci-lint = [
|
|
|
88
93
|
"checks-superstaq>=0.5.67",
|
|
89
94
|
"pyproject-fmt>=2",
|
|
90
95
|
]
|
|
96
|
+
ci-notebooks = [
|
|
97
|
+
"jupyter>=1.1.1",
|
|
98
|
+
"matplotlib>=3.10",
|
|
99
|
+
"pip>=24",
|
|
100
|
+
]
|
|
91
101
|
ci-test = [
|
|
92
102
|
"checks-superstaq>=0.5.67",
|
|
93
103
|
"pytest>=8",
|
|
@@ -136,7 +146,10 @@ show_error_codes = true
|
|
|
136
146
|
|
|
137
147
|
[tool.pytest.ini_options]
|
|
138
148
|
addopts = "--disable-socket"
|
|
139
|
-
filterwarnings = [
|
|
149
|
+
filterwarnings = [
|
|
150
|
+
"error::DeprecationWarning",
|
|
151
|
+
"ignore:(?s).*The problem is either infeasible or unbounded.*:UserWarning",
|
|
152
|
+
]
|
|
140
153
|
|
|
141
154
|
[tool.coverage.run]
|
|
142
155
|
include = ["./*"]
|
|
@@ -4,7 +4,7 @@ requires = [ "uv-build>=0.9.18,<0.13" ]
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "qldpc"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.2.dev20261004"
|
|
8
8
|
description = "Tools for constructing and analyzing quantum low density parity check (qLDPC) codes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
keywords = [
|
|
@@ -45,6 +45,13 @@ dependencies = [
|
|
|
45
45
|
"sympy>=1.12",
|
|
46
46
|
"tqdm>=4",
|
|
47
47
|
]
|
|
48
|
+
optional-dependencies.decoders = [
|
|
49
|
+
"qldpc[relay-bp]",
|
|
50
|
+
"""\
|
|
51
|
+
qldpc[tesseract]; ((sys_platform=='darwin' and platform_machine=='arm64') or (sys_platform=='linux' and \
|
|
52
|
+
platform_machine=='x86_64')) and python_version<'3.15'\
|
|
53
|
+
""",
|
|
54
|
+
]
|
|
48
55
|
optional-dependencies.dev = [
|
|
49
56
|
"checks-superstaq>=0.5.67",
|
|
50
57
|
"jupyter>=1.1.1",
|
|
@@ -54,7 +61,7 @@ optional-dependencies.dev = [
|
|
|
54
61
|
"pytest-randomly>=3.10", # tests that need reproducibility read its randomly_seed option
|
|
55
62
|
"pytest-socket>=0.7", # implements the --disable-socket in [tool.pytest] addopts below
|
|
56
63
|
"python-lsp-server[all]>=1.14",
|
|
57
|
-
"qldpc[docs,gap
|
|
64
|
+
"qldpc[decoders,docs,gap]",
|
|
58
65
|
]
|
|
59
66
|
optional-dependencies.docs = [
|
|
60
67
|
"ipython>=8", # for nbsphinx's IPython.sphinxext.ipython_console_highlighting extension
|
|
@@ -64,9 +71,12 @@ optional-dependencies.docs = [
|
|
|
64
71
|
"sphinx-rtd-theme>=2",
|
|
65
72
|
]
|
|
66
73
|
optional-dependencies.gap = [
|
|
67
|
-
"passagemath-gap==10.8.12; python_version
|
|
74
|
+
"passagemath-gap==10.8.12; python_version=='3.13' and sys_platform!='win32'",
|
|
68
75
|
]
|
|
69
76
|
optional-dependencies.relay-bp = [ "relay-bp[stim]==0.2.2" ]
|
|
77
|
+
optional-dependencies.tesseract = [
|
|
78
|
+
"tesseract-decoder==0.1.1.dev20260910235247; python_version>='3.12'",
|
|
79
|
+
]
|
|
70
80
|
urls.Documentation = "https://qldpc.readthedocs.io/"
|
|
71
81
|
urls.Issues = "https://github.com/qLDPCOrg/qLDPC/issues"
|
|
72
82
|
urls.Repository = "https://github.com/qLDPCOrg/qLDPC"
|
|
@@ -80,6 +90,11 @@ ci-lint = [
|
|
|
80
90
|
"checks-superstaq>=0.5.67",
|
|
81
91
|
"pyproject-fmt>=2",
|
|
82
92
|
]
|
|
93
|
+
ci-notebooks = [
|
|
94
|
+
"jupyter>=1.1.1",
|
|
95
|
+
"matplotlib>=3.10",
|
|
96
|
+
"pip>=24",
|
|
97
|
+
]
|
|
83
98
|
ci-test = [
|
|
84
99
|
"checks-superstaq>=0.5.67",
|
|
85
100
|
"pytest>=8",
|
|
@@ -126,6 +141,7 @@ show_error_codes = true
|
|
|
126
141
|
[tool.pytest]
|
|
127
142
|
ini_options.addopts = "--disable-socket" # forbid tests from making network calls
|
|
128
143
|
ini_options.filterwarnings = [
|
|
144
|
+
"error::DeprecationWarning", # tests use the modern API
|
|
129
145
|
"ignore:(?s).*The problem is either infeasible or unbounded.*:UserWarning", # from cvxpy
|
|
130
146
|
]
|
|
131
147
|
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Helper function(s) for caching results."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import functools
|
|
8
|
+
import pathlib
|
|
9
|
+
import sys
|
|
10
|
+
import warnings
|
|
11
|
+
from collections.abc import Callable, Hashable
|
|
12
|
+
from typing import Generic, ParamSpec, TypeVar, cast
|
|
13
|
+
|
|
14
|
+
import diskcache
|
|
15
|
+
import platformdirs
|
|
16
|
+
|
|
17
|
+
Params = ParamSpec("Params")
|
|
18
|
+
Result = TypeVar("Result")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CachedFunction(Generic[Params, Result]):
|
|
22
|
+
"""A function whose results are cached to disk.
|
|
23
|
+
|
|
24
|
+
Calling the function retrieves its result from the cache if available, and otherwise computes
|
|
25
|
+
and caches the result. Calling CachedFunction.refresh ignores any existing cache entry,
|
|
26
|
+
recomputes the result, and saves the new result to the cache.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
__wrapped__: Callable[Params, Result]
|
|
30
|
+
|
|
31
|
+
def __init__(
|
|
32
|
+
self,
|
|
33
|
+
function: Callable[Params, Result],
|
|
34
|
+
cache_name: str,
|
|
35
|
+
*,
|
|
36
|
+
cache_dir: pathlib.Path | str | None = None,
|
|
37
|
+
key_func: Callable[..., Hashable] | None = None,
|
|
38
|
+
) -> None:
|
|
39
|
+
self._function = function
|
|
40
|
+
self._cache_name = cache_name
|
|
41
|
+
self._cache_dir = cache_dir
|
|
42
|
+
self._key_func = key_func
|
|
43
|
+
functools.update_wrapper(self, function)
|
|
44
|
+
|
|
45
|
+
def __call__(self, *args: Params.args, **kwargs: Params.kwargs) -> Result:
|
|
46
|
+
"""Retrieve a result from the cache if available, and otherwise compute and cache it."""
|
|
47
|
+
cache = get_disk_cache(self._cache_name, cache_dir=self._cache_dir)
|
|
48
|
+
key = self._get_key(*args, **kwargs)
|
|
49
|
+
if key in cache:
|
|
50
|
+
return cast(Result, cache[key])
|
|
51
|
+
result = self._function(*args, **kwargs)
|
|
52
|
+
cache[key] = result
|
|
53
|
+
return result
|
|
54
|
+
|
|
55
|
+
def refresh(self, *args: Params.args, **kwargs: Params.kwargs) -> Result:
|
|
56
|
+
"""Recompute the result for these arguments and overwrite the cached value."""
|
|
57
|
+
cache = get_disk_cache(self._cache_name, cache_dir=self._cache_dir)
|
|
58
|
+
key = self._get_key(*args, **kwargs)
|
|
59
|
+
result = self._function(*args, **kwargs)
|
|
60
|
+
cache[key] = result
|
|
61
|
+
return result
|
|
62
|
+
|
|
63
|
+
def _get_key(self, *args: Params.args, **kwargs: Params.kwargs) -> Hashable:
|
|
64
|
+
"""Get the cache key for the given arguments."""
|
|
65
|
+
if self._key_func is not None:
|
|
66
|
+
return self._key_func(*args, **kwargs)
|
|
67
|
+
key = args + tuple(kwargs.items())
|
|
68
|
+
return key if len(key) != 1 else key[0] # unpack length-1 tuples
|
|
69
|
+
|
|
70
|
+
def __reduce__(self) -> str:
|
|
71
|
+
"""Pickle by reference to the module-level name of the decorated function."""
|
|
72
|
+
return self._function.__qualname__
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_disk_cache_path(
|
|
76
|
+
cache_name: str, *, cache_dir: pathlib.Path | str | None = None
|
|
77
|
+
) -> pathlib.Path:
|
|
78
|
+
"""Retrieve the path of a cache."""
|
|
79
|
+
cache_dir = cache_dir or pathlib.Path(platformdirs.user_cache_dir()) / "qldpc"
|
|
80
|
+
return pathlib.Path(cache_dir) / cache_name
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def get_disk_cache(
|
|
84
|
+
cache_name: str, *, cache_dir: pathlib.Path | str | None = None
|
|
85
|
+
) -> diskcache.Cache:
|
|
86
|
+
"""Retrieve a dictionary-like cache object."""
|
|
87
|
+
if running_with_pytest():
|
|
88
|
+
return {}
|
|
89
|
+
return diskcache.Cache(get_disk_cache_path(cache_name, cache_dir=cache_dir))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def use_disk_cache(
|
|
93
|
+
cache_name: str,
|
|
94
|
+
*,
|
|
95
|
+
cache_dir: pathlib.Path | str | None = None,
|
|
96
|
+
key_func: Callable[..., Hashable] | None = None,
|
|
97
|
+
) -> Callable[[Callable[Params, Result]], CachedFunction[Params, Result]]:
|
|
98
|
+
"""Decorator to cache results to disk.
|
|
99
|
+
|
|
100
|
+
By default, the cache key is the tuple of positional arguments followed by (keyword, value)
|
|
101
|
+
pairs, unpacked if this tuple has length 1. A custom key_func, called with the same arguments
|
|
102
|
+
as the decorated function, overrides this default.
|
|
103
|
+
|
|
104
|
+
The decorated function has a .refresh method with the same signature, which ignores any
|
|
105
|
+
existing cache entry, recomputes the result, and saves the new result to the cache. Refreshing
|
|
106
|
+
only affects this cache, and does not clear other caches used internally by the function.
|
|
107
|
+
|
|
108
|
+
The decorator is intended for module-level functions: a decorated function is pickled by
|
|
109
|
+
reference to its qualified name, and it does not bind as a method.
|
|
110
|
+
|
|
111
|
+
Disk caching is bypassed when running with pytest.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
def decorator(function: Callable[Params, Result]) -> CachedFunction[Params, Result]:
|
|
115
|
+
return CachedFunction(function, cache_name, cache_dir=cache_dir, key_func=key_func)
|
|
116
|
+
|
|
117
|
+
return decorator
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def running_with_pytest() -> bool:
|
|
121
|
+
"""Are we currently running with pytest?"""
|
|
122
|
+
return "pytest" in sys.modules
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def clear_entry(
|
|
126
|
+
cache_name: str, key: Hashable, *, cache_dir: pathlib.Path | str | None = None
|
|
127
|
+
) -> None:
|
|
128
|
+
"""Clear an entry from a local cache."""
|
|
129
|
+
cache = get_disk_cache(cache_name, cache_dir=cache_dir)
|
|
130
|
+
if key in cache:
|
|
131
|
+
del cache[key]
|
|
132
|
+
else:
|
|
133
|
+
cache_path = get_disk_cache_path(cache_name, cache_dir=cache_dir)
|
|
134
|
+
warnings.warn(
|
|
135
|
+
f"Attempted to delete the entry '{key}' from the cache '{cache_name}'"
|
|
136
|
+
+ ("" if cache_dir is None else f" (located at '{cache_path}')")
|
|
137
|
+
+ ", but this entry does not exist.",
|
|
138
|
+
stacklevel=2,
|
|
139
|
+
)
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
|
|
3
|
+
"""Unit tests for cache.py."""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import copy
|
|
8
|
+
import pathlib
|
|
9
|
+
import pickle
|
|
10
|
+
import unittest.mock
|
|
11
|
+
from collections.abc import Hashable
|
|
12
|
+
|
|
13
|
+
import pytest
|
|
14
|
+
|
|
15
|
+
import qldpc.cache
|
|
16
|
+
import qldpc.external.codes
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_pytest() -> None:
|
|
20
|
+
"""Disk caching is bypassed when running under pytest."""
|
|
21
|
+
assert qldpc.cache.running_with_pytest()
|
|
22
|
+
assert qldpc.cache.get_disk_cache("test") == {}
|
|
23
|
+
|
|
24
|
+
calls = []
|
|
25
|
+
|
|
26
|
+
@qldpc.cache.use_disk_cache("test")
|
|
27
|
+
def test_func(value: int) -> int:
|
|
28
|
+
"""Test docstring."""
|
|
29
|
+
calls.append(value)
|
|
30
|
+
return value
|
|
31
|
+
|
|
32
|
+
assert test_func.__doc__ == "Test docstring."
|
|
33
|
+
assert test_func(1) == test_func(1) == test_func.refresh(1) == 1
|
|
34
|
+
assert calls == [1, 1, 1]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_use_disk_cache(tmp_path: pathlib.Path) -> None:
|
|
38
|
+
"""Cache function outputs."""
|
|
39
|
+
|
|
40
|
+
cache: dict[Hashable, int] = {}
|
|
41
|
+
with (
|
|
42
|
+
unittest.mock.patch("qldpc.cache.running_with_pytest", return_value=False),
|
|
43
|
+
unittest.mock.patch("diskcache.Cache", return_value=cache),
|
|
44
|
+
):
|
|
45
|
+
|
|
46
|
+
@qldpc.cache.use_disk_cache("test_name")
|
|
47
|
+
def get_five(_: str) -> int:
|
|
48
|
+
return 5
|
|
49
|
+
|
|
50
|
+
# use cache to save/retrieve results
|
|
51
|
+
get_five("test_arg") # save results to cache
|
|
52
|
+
assert cache == {"test_arg": 5}
|
|
53
|
+
assert cache["test_arg"] == get_five("test_arg")
|
|
54
|
+
|
|
55
|
+
# post-process inputs to determine the cache key
|
|
56
|
+
@qldpc.cache.use_disk_cache("test_name", key_func=lambda _: None)
|
|
57
|
+
def get_six(_: str) -> int:
|
|
58
|
+
return 6
|
|
59
|
+
|
|
60
|
+
assert get_six("test_arg") == 6
|
|
61
|
+
assert cache == {"test_arg": 5, None: 6}
|
|
62
|
+
|
|
63
|
+
# delete an entry from the cache
|
|
64
|
+
qldpc.cache.clear_entry("test_name", None)
|
|
65
|
+
assert cache == {"test_arg": 5}
|
|
66
|
+
|
|
67
|
+
# raise a warning if trying to delete an entry that does not exist in the cache
|
|
68
|
+
with pytest.warns(UserWarning, match="entry does not exist"):
|
|
69
|
+
qldpc.cache.clear_entry("test_name", "some_key")
|
|
70
|
+
with pytest.warns(UserWarning, match="located at"):
|
|
71
|
+
qldpc.cache.clear_entry("test_name", "some_key", cache_dir=tmp_path)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def test_refresh() -> None:
|
|
75
|
+
"""Refresh cached function outputs."""
|
|
76
|
+
|
|
77
|
+
cache: dict[Hashable, int] = {}
|
|
78
|
+
with (
|
|
79
|
+
unittest.mock.patch("qldpc.cache.running_with_pytest", return_value=False),
|
|
80
|
+
unittest.mock.patch("diskcache.Cache", return_value=cache),
|
|
81
|
+
):
|
|
82
|
+
calls: list[tuple[int, int]] = []
|
|
83
|
+
|
|
84
|
+
@qldpc.cache.use_disk_cache("test_name")
|
|
85
|
+
def count(value: int, *, offset: int = 0) -> int:
|
|
86
|
+
calls.append((value, offset))
|
|
87
|
+
return len(calls)
|
|
88
|
+
|
|
89
|
+
# the first call computes a result, and the second call retrieves it from the cache
|
|
90
|
+
assert count(1) == count(1) == 1
|
|
91
|
+
assert calls == [(1, 0)]
|
|
92
|
+
|
|
93
|
+
# refreshing recomputes the result and overwrites the cache entry
|
|
94
|
+
assert count.refresh(1) == 2
|
|
95
|
+
assert cache == {1: 2}
|
|
96
|
+
assert count(1) == 2
|
|
97
|
+
assert calls == [(1, 0), (1, 0)]
|
|
98
|
+
|
|
99
|
+
# refreshing a missing entry computes and saves a result without warnings
|
|
100
|
+
assert count.refresh(2, offset=3) == 3
|
|
101
|
+
assert cache == {1: 2, (2, ("offset", 3)): 3}
|
|
102
|
+
|
|
103
|
+
# refreshing uses the custom cache key
|
|
104
|
+
@qldpc.cache.use_disk_cache("test_name", key_func=lambda value: f"key-{value}")
|
|
105
|
+
def double(value: int) -> int:
|
|
106
|
+
calls.append((value, 0))
|
|
107
|
+
return 2 * value
|
|
108
|
+
|
|
109
|
+
cache["key-4"] = 0
|
|
110
|
+
assert double(4) == 0
|
|
111
|
+
assert double.refresh(4) == 8
|
|
112
|
+
assert cache["key-4"] == 8
|
|
113
|
+
assert double(4) == 8
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_pickle() -> None:
|
|
117
|
+
"""Cached module-level functions are pickled by reference."""
|
|
118
|
+
function = qldpc.external.codes.get_classical_code
|
|
119
|
+
assert pickle.dumps(function)
|
|
120
|
+
assert copy.deepcopy(function) is function
|
|
121
|
+
assert function.__wrapped__.__name__ == "get_classical_code"
|
|
@@ -176,9 +176,7 @@ def test_erasure_counts_as_a_discard() -> None:
|
|
|
176
176
|
|
|
177
177
|
# every error of weight one is decoded exactly, and every heavier error is erased, so the
|
|
178
178
|
# logical error rate is exactly zero and the discard rate is 1 - 0.8**7 - 7 * 0.2 * 0.8**6
|
|
179
|
-
decoder = decoders.SinterDecoder(
|
|
180
|
-
decoder=decoders.lookup_table(max_weight=1, add_erasure_bit=True)
|
|
181
|
-
)
|
|
179
|
+
decoder = decoders.SinterDecoder(decoder=decoders.lookup(max_weight=1, add_erasure_bit=True))
|
|
182
180
|
logical_error_rate, discard_rate = circuits.get_logical_error_and_discard_rate(
|
|
183
181
|
circuit, sinter_decoder=decoder, num_samples=1000
|
|
184
182
|
)
|
|
@@ -191,9 +189,9 @@ def test_erasure_counts_as_a_discard() -> None:
|
|
|
191
189
|
|
|
192
190
|
def compile_decoder_for_dem(
|
|
193
191
|
self, dem: stim.DetectorErrorModel
|
|
194
|
-
) -> decoders.CompiledSinterDecoder:
|
|
192
|
+
) -> decoders.sinter.CompiledSinterDecoder:
|
|
195
193
|
compiled = super().compile_decoder_for_dem(dem)
|
|
196
|
-
assert isinstance(compiled, decoders.CompiledTrivialDecoder)
|
|
194
|
+
assert isinstance(compiled, decoders.sinter.CompiledTrivialDecoder)
|
|
197
195
|
compiled.packed_observable_size += 2
|
|
198
196
|
return compiled
|
|
199
197
|
|
|
@@ -386,7 +386,10 @@ def _tableaus_are_equivalent_mod_paulis(tableau_1: stim.Tableau, tableau_2: stim
|
|
|
386
386
|
|
|
387
387
|
@cache.use_disk_cache(
|
|
388
388
|
"group_intersection",
|
|
389
|
-
key_func=lambda
|
|
389
|
+
key_func=lambda group_a, group_b: (
|
|
390
|
+
group_a.hashable_generators(),
|
|
391
|
+
group_b.hashable_generators(),
|
|
392
|
+
),
|
|
390
393
|
)
|
|
391
394
|
def _sympy_group_intersection_generators(
|
|
392
395
|
group_a: abstract.Group, group_b: abstract.Group
|
|
@@ -107,6 +107,17 @@ def test_transversal_group_degree() -> None:
|
|
|
107
107
|
assert len(circuits.get_transversal_ops(code, ["H"])) == 0
|
|
108
108
|
|
|
109
109
|
|
|
110
|
+
def test_group_intersection_keywords() -> None:
|
|
111
|
+
"""The cache key of a group intersection supports keyword arguments."""
|
|
112
|
+
group_a = abstract.SymmetricGroup(3)
|
|
113
|
+
group_b = abstract.CyclicGroup(3)
|
|
114
|
+
generators = transversal._sympy_group_intersection_generators(group_a, group_b)
|
|
115
|
+
assert generators # the cyclic group is a nontrivial subgroup of the symmetric group
|
|
116
|
+
assert generators == transversal._sympy_group_intersection_generators(
|
|
117
|
+
group_a=group_a, group_b=group_b
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
110
121
|
def test_finding_circuit(
|
|
111
122
|
pytestconfig: pytest.Config, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str]
|
|
112
123
|
) -> None:
|