qLDPC 0.4.1__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.
Files changed (145) hide show
  1. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/PKG-INFO +3 -2
  2. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/README.md +2 -1
  3. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/pyproject.toml +10 -2
  4. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/pyproject.toml.orig +7 -1
  5. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/benchmarking_test.py +2 -2
  6. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/code_capacity.py +77 -51
  7. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/code_capacity_test.py +281 -14
  8. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/common.py +52 -111
  9. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/common_test.py +131 -14
  10. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/__init__.py +101 -24
  11. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/error_decoders.py +51 -3
  12. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/error_decoders_test.py +20 -1
  13. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/capabilities.py +7 -27
  14. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/capabilities_test.py +5 -26
  15. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/common.py +14 -11
  16. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/common_test.py +11 -11
  17. qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/factories.py +58 -0
  18. qldpc-0.4.2.dev20261004/src/qldpc/decoders/construction/factories_test.py +85 -0
  19. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/legacy.py +63 -81
  20. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/legacy_test.py +103 -32
  21. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/resolution.py +67 -17
  22. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/resolution_test.py +109 -22
  23. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/specs.py +55 -124
  24. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/specs_test.py +60 -60
  25. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/__init__.py +14 -4
  26. qldpc-0.4.2.dev20261004/src/qldpc/decoders/custom/_lookup_enumeration.py +231 -0
  27. qldpc-0.4.2.dev20261004/src/qldpc/decoders/custom/_lookup_enumeration_test.py +159 -0
  28. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/composition_test.py +9 -9
  29. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/guf.py +16 -8
  30. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/guf_test.py +4 -4
  31. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/ilp.py +65 -39
  32. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/ilp_test.py +21 -10
  33. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/lookup.py +91 -281
  34. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/lookup_test.py +110 -94
  35. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom_test.py +8 -0
  36. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/dems_test.py +14 -8
  37. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/frontier.py +53 -11
  38. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/frontier_test.py +28 -7
  39. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/ldpc.py +110 -30
  40. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/ldpc_test.py +30 -2
  41. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/pymatching.py +84 -13
  42. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/pymatching_test.py +17 -0
  43. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/relay_bp.py +154 -20
  44. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/relay_bp_test.py +29 -9
  45. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/tesseract.py +80 -23
  46. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/tesseract_test.py +22 -6
  47. qldpc-0.4.2.dev20261004/src/qldpc/decoders/legacy_api_test.py +233 -0
  48. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/protocols.py +77 -4
  49. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/protocols_test.py +5 -3
  50. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/core.py +9 -52
  51. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/core_test.py +33 -24
  52. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/subgraph.py +4 -6
  53. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/subgraph_test.py +1 -1
  54. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/window.py +10 -14
  55. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/window_test.py +1 -1
  56. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/COPYRIGHT +0 -0
  57. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/LICENSE +0 -0
  58. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/__init__.py +0 -0
  59. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/_util.py +0 -0
  60. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/_util_test.py +0 -0
  61. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/__init__.py +0 -0
  62. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/_monomials.py +0 -0
  63. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/_monomials_test.py +0 -0
  64. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/groups.py +0 -0
  65. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/groups_test.py +0 -0
  66. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/linalg.py +0 -0
  67. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/linalg_test.py +0 -0
  68. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/ring_array.py +0 -0
  69. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/ring_array_test.py +0 -0
  70. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/rings.py +0 -0
  71. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/rings_test.py +0 -0
  72. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/wedderburn_artin.py +0 -0
  73. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/abstract/wedderburn_artin_test.py +0 -0
  74. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/cache.py +0 -0
  75. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/cache_test.py +0 -0
  76. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/__init__.py +0 -0
  77. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/benchmarking.py +0 -0
  78. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/bookkeeping.py +0 -0
  79. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/bookkeeping_test.py +0 -0
  80. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/common.py +0 -0
  81. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/common_test.py +0 -0
  82. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/conftest.py +0 -0
  83. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/encoding.py +0 -0
  84. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/encoding_test.py +0 -0
  85. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/__init__.py +0 -0
  86. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/alpha_syndrome.py +0 -0
  87. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/alpha_syndrome_test.py +0 -0
  88. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/memory.py +0 -0
  89. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/memory_test.py +0 -0
  90. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/syndrome_measurement.py +0 -0
  91. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/memory/syndrome_measurement_test.py +0 -0
  92. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/noise_model.py +0 -0
  93. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/noise_model_test.py +0 -0
  94. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/transversal.py +0 -0
  95. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/circuits/transversal_test.py +0 -0
  96. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/__init__.py +0 -0
  97. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/classical.py +0 -0
  98. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/classical_test.py +0 -0
  99. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/distance.py +0 -0
  100. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/distance_test.py +0 -0
  101. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/monte_carlo.py +0 -0
  102. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/monte_carlo_test.py +0 -0
  103. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/quantum.py +0 -0
  104. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/codes/quantum_test.py +0 -0
  105. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/conftest.py +0 -0
  106. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/__init__.py +0 -0
  107. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/observable_decoders.py +0 -0
  108. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/adapters/observable_decoders_test.py +0 -0
  109. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/conftest.py +0 -0
  110. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/construction/__init__.py +0 -0
  111. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/custom/composition.py +0 -0
  112. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/dems.py +0 -0
  113. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/external/__init__.py +0 -0
  114. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter/__init__.py +0 -0
  115. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders/sinter_test.py +0 -0
  116. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/decoders_test.py +0 -0
  117. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/__init__.py +0 -0
  118. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/__init__.py +0 -0
  119. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/_compiler.py +0 -0
  120. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/_compiler_test.py +0 -0
  121. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/bridge.py +0 -0
  122. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/bridge_test.py +0 -0
  123. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/cheeger.py +0 -0
  124. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/cheeger_test.py +0 -0
  125. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/circuit.py +0 -0
  126. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/circuit_test.py +0 -0
  127. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/conftest.py +0 -0
  128. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/construction.py +0 -0
  129. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/construction_test.py +0 -0
  130. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/gadget.py +0 -0
  131. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/experimental/surgery/gadget_test.py +0 -0
  132. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/__init__.py +0 -0
  133. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/codes.py +0 -0
  134. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/codes_test.py +0 -0
  135. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/gap.py +0 -0
  136. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/gap_test.py +0 -0
  137. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/groups.py +0 -0
  138. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/groups_test.py +0 -0
  139. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/sqetch.py +0 -0
  140. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/external/sqetch_test.py +0 -0
  141. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/math.py +0 -0
  142. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/math_test.py +0 -0
  143. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/objects.py +0 -0
  144. {qldpc-0.4.1 → qldpc-0.4.2.dev20261004}/src/qldpc/objects_test.py +0 -0
  145. {qldpc-0.4.1 → 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.1
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
@@ -106,7 +106,7 @@ python -m pip install qldpc
106
106
  #### Decoders
107
107
 
108
108
  qLDPC installs `ldpc` and `pymatching` by default, which include a host of decoders.
109
- You can install every decoder available in `qldpc` for your platform with:
109
+ Install the optional decoder integrations published as qLDPC extras for your platform with:
110
110
 
111
111
  ```bash
112
112
  python -m pip install 'qldpc[decoders]'
@@ -120,6 +120,7 @@ python -m pip install 'qldpc[tesseract]'
120
120
  ```
121
121
 
122
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.
123
124
 
124
125
  #### GAP integration
125
126
 
@@ -41,7 +41,7 @@ python -m pip install qldpc
41
41
  #### Decoders
42
42
 
43
43
  qLDPC installs `ldpc` and `pymatching` by default, which include a host of decoders.
44
- You can install every decoder available in `qldpc` for your platform with:
44
+ Install the optional decoder integrations published as qLDPC extras for your platform with:
45
45
 
46
46
  ```bash
47
47
  python -m pip install 'qldpc[decoders]'
@@ -55,6 +55,7 @@ python -m pip install 'qldpc[tesseract]'
55
55
  ```
56
56
 
57
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.
58
59
 
59
60
  #### GAP integration
60
61
 
@@ -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.1"
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 = [
@@ -93,6 +93,11 @@ ci-lint = [
93
93
  "checks-superstaq>=0.5.67",
94
94
  "pyproject-fmt>=2",
95
95
  ]
96
+ ci-notebooks = [
97
+ "jupyter>=1.1.1",
98
+ "matplotlib>=3.10",
99
+ "pip>=24",
100
+ ]
96
101
  ci-test = [
97
102
  "checks-superstaq>=0.5.67",
98
103
  "pytest>=8",
@@ -141,7 +146,10 @@ show_error_codes = true
141
146
 
142
147
  [tool.pytest.ini_options]
143
148
  addopts = "--disable-socket"
144
- filterwarnings = ["ignore:(?s).*The problem is either infeasible or unbounded.*:UserWarning"]
149
+ filterwarnings = [
150
+ "error::DeprecationWarning",
151
+ "ignore:(?s).*The problem is either infeasible or unbounded.*:UserWarning",
152
+ ]
145
153
 
146
154
  [tool.coverage.run]
147
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.1"
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 = [
@@ -90,6 +90,11 @@ ci-lint = [
90
90
  "checks-superstaq>=0.5.67",
91
91
  "pyproject-fmt>=2",
92
92
  ]
93
+ ci-notebooks = [
94
+ "jupyter>=1.1.1",
95
+ "matplotlib>=3.10",
96
+ "pip>=24",
97
+ ]
93
98
  ci-test = [
94
99
  "checks-superstaq>=0.5.67",
95
100
  "pytest>=8",
@@ -136,6 +141,7 @@ show_error_codes = true
136
141
  [tool.pytest]
137
142
  ini_options.addopts = "--disable-socket" # forbid tests from making network calls
138
143
  ini_options.filterwarnings = [
144
+ "error::DeprecationWarning", # tests use the modern API
139
145
  "ignore:(?s).*The problem is either infeasible or unbounded.*:UserWarning", # from cvxpy
140
146
  ]
141
147
 
@@ -189,9 +189,9 @@ def test_erasure_counts_as_a_discard() -> None:
189
189
 
190
190
  def compile_decoder_for_dem(
191
191
  self, dem: stim.DetectorErrorModel
192
- ) -> decoders.CompiledSinterDecoder:
192
+ ) -> decoders.sinter.CompiledSinterDecoder:
193
193
  compiled = super().compile_decoder_for_dem(dem)
194
- assert isinstance(compiled, decoders.CompiledTrivialDecoder)
194
+ assert isinstance(compiled, decoders.sinter.CompiledTrivialDecoder)
195
195
  compiled.packed_observable_size += 2
196
196
  return compiled
197
197
 
@@ -10,8 +10,8 @@ by converting the physical errors that it infers into observable values.
10
10
  from __future__ import annotations
11
11
 
12
12
  import dataclasses
13
+ import warnings
13
14
  from collections.abc import Mapping
14
- from typing import cast
15
15
 
16
16
  import galois
17
17
  import numpy as np
@@ -19,6 +19,7 @@ import numpy.typing as npt
19
19
  import scipy.sparse
20
20
  import stim
21
21
 
22
+ from qldpc._util import get_external_caller_stacklevel
22
23
  from qldpc.decoders.adapters.observable_decoders import (
23
24
  BitPackedObservableDecoder,
24
25
  ErrorsToFieldObservablesDecoder,
@@ -26,21 +27,21 @@ from qldpc.decoders.adapters.observable_decoders import (
26
27
  )
27
28
  from qldpc.decoders.capabilities import (
28
29
  compiles_for_dem,
29
- constructs_observable_decoder,
30
30
  is_prebuilt_observable_decoder,
31
31
  )
32
- from qldpc.decoders.common import PLACEHOLDER_ERROR_RATE
33
- from qldpc.decoders.construction.legacy import resolve_decoder
34
- from qldpc.decoders.construction.resolution import reject_prebuilt_decoder
32
+ from qldpc.decoders.common import PLACEHOLDER_ERROR_RATE, ObservableDecodingFallbackWarning
33
+ from qldpc.decoders.construction.factories import DEMDecoderFactory
34
+ from qldpc.decoders.construction.resolution import _resolve_error_decoder, reject_prebuilt_decoder
35
35
  from qldpc.decoders.construction.specs import (
36
36
  DecoderInput,
37
37
  DecoderSpec,
38
- ObservableDecoderConstructor,
39
38
  )
40
39
  from qldpc.decoders.dems import DetectorErrorModelArrays
41
40
  from qldpc.decoders.protocols import ErrorDecoder, ObservableDecoder
42
41
  from qldpc.decoders.sinter.core import CompiledSinterDecoder
43
42
 
43
+ _MAX_SPEC_REPR_LENGTH = 80 # truncate long option values in warnings
44
+
44
45
 
45
46
  def get_code_capacity_dem(
46
47
  syndrome_matrix: galois.FieldArray,
@@ -69,8 +70,8 @@ def get_code_capacity_dem(
69
70
  field = type(syndrome_matrix)
70
71
  if getattr(field, "order", 2) != 2:
71
72
  raise ValueError(
72
- "A Sinter-style decoder is compiled for a Stim detector error model, which is binary, so"
73
- f" it cannot decode a code over {field.name}. Pass decoder settings such as"
73
+ "An observable decoder built from a Stim detector error model requires a binary code, so"
74
+ f" it cannot decode a code over {field.name}. Pass a decoder specification such as"
74
75
  " decoders.guf(), or a prebuilt observable decoder (such as an ObservableLookupDecoder)"
75
76
  " built for this code, instead"
76
77
  )
@@ -231,44 +232,32 @@ def get_code_capacity_decoder(
231
232
  ) -> CodeCapacityDecoder:
232
233
  """Build an observable decoder for one sector of a code-capacity experiment.
233
234
 
234
- Code-capacity sampling decodes the syndrome ``syndrome_matrix @ error`` of each sampled error to
235
- predict its observable values ``observable_matrix @ error``. The decoder input is resolved into
236
- an observable decoder as follows:
237
-
238
- - A Sinter-style decoder (an object with a compile_decoder_for_dem method, such as a
239
- decoders.SinterDecoder), a constructor explicitly declared to return an observable decoder,
240
- or decoder settings that cannot infer errors (such as decoders.frontier(...)), is built for
241
- the detector error model that get_code_capacity_dem constructs. This requires the matrices
242
- to be binary.
243
- - A prebuilt observable decoder (an object with a decode_observables method, or a compiled
244
- Sinter decoder with a decode_shots_bit_packed method, that is not also an error decoder) is
245
- used as is. It must predict the observable values ``observable_matrix @ error`` from the
246
- syndrome ``syndrome_matrix @ error``.
247
- - Anything else (None, decoder settings, a constructor, or a prebuilt error decoder, together
248
- with any deprecated decoder_args) builds an error decoder for syndrome_matrix, exactly as
249
- qldpc.decoders.resolve_decoder does. The observable values of the errors that it infers are
250
- its predictions. A decoder that is both an error decoder and an observable decoder, such as a
251
- RelayBPDecoder, is used as an error decoder.
252
-
253
- A DecoderSpec with ``infers_errors=True`` builds an error decoder. A DecoderSpec with
254
- ``infers_errors=False``, a Sinter-style decoder, or an observable-decoder constructor builds an
255
- observable decoder from the code-capacity detector error model. A prebuilt observable decoder
256
- is used directly.
235
+ Code-capacity sampling predicts ``observable_matrix @ error`` from the syndrome
236
+ ``syndrome_matrix @ error``. Pass a decoder specification such as ``decoders.bp_osd(...)``
237
+ as ``decoder=``.
238
+
239
+ For a binary code, a specification with native observable support is first built as an
240
+ observable decoder for the code-capacity detector error model. If that build raises a
241
+ ValueError, for example because the specification sets matrix-only options or the backend
242
+ rejects the model's structure, an ObservableDecodingFallbackWarning is emitted and an error
243
+ decoder is built for syndrome_matrix instead. Wrap a specification as
244
+ ``decoders.from_matrix(spec.build)`` to select error decoding explicitly.
257
245
 
258
246
  Args:
259
247
  syndrome_matrix: The matrix that maps an error to its syndrome.
260
248
  observable_matrix: The matrix that maps an error to its observable values, or None if every
261
249
  error location is itself an observable.
262
- decoder: The decoder input.
250
+ decoder: A decoder specification for this sector, or None for the default decoder.
263
251
  decoder_args: Deprecated keyword-based decoder options, which build an error decoder.
264
252
  dem_errors: The errors of the error mechanisms of the detector error model for which a
265
253
  Sinter-style decoder is compiled, as columns of a matrix. Defaults to the identity
266
254
  matrix, making each error location an error mechanism.
267
255
  symplectic_dem_errors: Whether the detector error model has one X, Z, and Y mechanism per
268
256
  qudit. Cannot be combined with dem_errors.
269
- dem_error_weights: Relative probabilities for the detector error model's error mechanisms.
270
- These are scaled by a fixed placeholder error rate, so decoder decisions do not change
271
- when the returned estimator is evaluated at different physical error rates.
257
+ dem_error_weights: Relative probabilities for the detector error model's error mechanisms
258
+ when building a native observable decoder or compiling a Sinter-style decoder. These
259
+ are scaled by a fixed placeholder error rate, so decoder decisions do not change when
260
+ the returned estimator is evaluated at different physical error rates.
272
261
  prebuilt_rejection_reason: If not None, reject a prebuilt (error or observable) decoder,
273
262
  with this reason; see help(qldpc.decoders.reject_prebuilt_decoder). A Sinter-style
274
263
  decoder is compiled here, so it is not rejected.
@@ -310,8 +299,7 @@ def get_code_capacity_decoder(
310
299
  require_dimensions=True,
311
300
  )
312
301
 
313
- observable_spec = isinstance(decoder, DecoderSpec) and not decoder.infers_errors
314
- if not decoder_args and (observable_spec or constructs_observable_decoder(decoder)):
302
+ if not decoder_args and isinstance(decoder, DEMDecoderFactory):
315
303
  dem = get_code_capacity_dem(
316
304
  syndrome_matrix,
317
305
  observable_matrix,
@@ -319,26 +307,64 @@ def get_code_capacity_decoder(
319
307
  symplectic_errors=symplectic_dem_errors,
320
308
  error_probs=dem_error_probs,
321
309
  )
322
- if isinstance(decoder, DecoderSpec):
323
- observable_decoder = decoder.build_observable_decoder(dem)
324
- source = "A decoder spec"
325
- else:
326
- observable_decoder = cast(ObservableDecoderConstructor, decoder)(dem)
327
- source = "An observable decoder constructor"
328
310
  return _get_observable_code_capacity_decoder(
329
- observable_decoder,
311
+ decoder.build(dem),
330
312
  syndrome_matrix,
331
313
  observable_matrix,
332
- source,
314
+ "A from_dem factory",
333
315
  require_dimensions=False,
334
316
  )
335
317
 
336
- error_decoder = resolve_decoder(
337
- syndrome_matrix,
338
- decoder, # type:ignore[arg-type]
339
- decoder_args,
340
- warn_deprecated=warn_deprecated,
341
- )
318
+ native_error: Exception | None = None
319
+ if not decoder_args and isinstance(decoder, DecoderSpec):
320
+ is_binary = getattr(type(syndrome_matrix), "order", 2) == 2
321
+ if not decoder.infers_errors or (is_binary and decoder.predicts_observables_natively):
322
+ dem = get_code_capacity_dem(
323
+ syndrome_matrix,
324
+ observable_matrix,
325
+ dem_errors,
326
+ symplectic_errors=symplectic_dem_errors,
327
+ error_probs=dem_error_probs,
328
+ )
329
+ if not decoder.infers_errors:
330
+ # an observable-only specification has nothing to fall back to
331
+ observable_decoder = decoder.build_observable_decoder(dem)
332
+ else:
333
+ try:
334
+ observable_decoder = decoder.build_observable_decoder(dem)
335
+ except ValueError as error:
336
+ native_error = error
337
+ if native_error is None:
338
+ return _get_observable_code_capacity_decoder(
339
+ observable_decoder,
340
+ syndrome_matrix,
341
+ observable_matrix,
342
+ "A decoder specification",
343
+ require_dimensions=False,
344
+ )
345
+ spec_repr = repr(decoder)
346
+ if len(spec_repr) > _MAX_SPEC_REPR_LENGTH:
347
+ spec_repr = spec_repr[: _MAX_SPEC_REPR_LENGTH - 4] + "...)"
348
+ warnings.warn(
349
+ f"{spec_repr} could not build an observable decoder for this code-capacity"
350
+ f" detector error model ({type(native_error).__name__}: {native_error}), so qLDPC"
351
+ " is decoding errors with the syndrome matrix instead. Pass"
352
+ " decoder=decoders.from_matrix(spec.build) to select error decoding explicitly",
353
+ ObservableDecodingFallbackWarning,
354
+ stacklevel=get_external_caller_stacklevel(),
355
+ )
356
+
357
+ try:
358
+ error_decoder = _resolve_error_decoder(
359
+ syndrome_matrix,
360
+ decoder, # type:ignore[arg-type]
361
+ decoder_args,
362
+ warn_deprecated=warn_deprecated,
363
+ )
364
+ except ValueError as error:
365
+ if native_error is not None:
366
+ raise error from native_error
367
+ raise
342
368
  return CodeCapacityDecoder.from_error_decoder(error_decoder, syndrome_matrix, observable_matrix)
343
369
 
344
370