dense-evolution 8.3.0__tar.gz → 8.3.1__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 (170) hide show
  1. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/PKG-INFO +4 -2
  2. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/README.md +3 -1
  3. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/__init__.py +13 -3
  4. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/__init__.py +10 -0
  5. dense_evolution-8.3.1/dense_evolution/circuits/arithmetic.py +241 -0
  6. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/parser.py +69 -6
  7. dense_evolution-8.3.1/dense_evolution/circuits/postselect.py +59 -0
  8. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/config.py +13 -0
  9. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/__init__.py +4 -4
  10. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/kl_divergence.py +3 -1
  11. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/magic_entropy.py +3 -1
  12. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/magic_entropy_shadows.py +5 -0
  13. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/renyi.py +3 -1
  14. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/stabilizer_renyi_entropy.py +3 -1
  15. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/zne.py +123 -7
  16. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/PKG-INFO +4 -2
  17. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/SOURCES.txt +2 -0
  18. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/pyproject.toml +1 -1
  19. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/autodiff.py +0 -0
  20. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/__init__.py +0 -0
  21. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/__init__.py +0 -0
  22. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/_engine_imports.py +0 -0
  23. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/circuit_chunker.py +0 -0
  24. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/core.py +0 -0
  25. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/disk_overflow.py +0 -0
  26. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/geometry.py +0 -0
  27. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/guard.py +0 -0
  28. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/chunk/kernels.py +0 -0
  29. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/mps.py +0 -0
  30. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/backends/statevector.py +0 -0
  31. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/chunk.py +0 -0
  32. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/compiler.py +0 -0
  33. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/diagram.py +0 -0
  34. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/gates.py +0 -0
  35. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/qft.py +0 -0
  36. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/random_circuit.py +0 -0
  37. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/registry.py +0 -0
  38. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/topology.py +0 -0
  39. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/trotter.py +0 -0
  40. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/circuits/uccsd.py +0 -0
  41. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/cli.py +0 -0
  42. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/compiler.py +0 -0
  43. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/drawing.py +0 -0
  44. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/entropy.py +0 -0
  45. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/fermions.py +0 -0
  46. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/gates.py +0 -0
  47. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/harrison_tb.py +0 -0
  48. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/healing.py +0 -0
  49. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/interop/__init__.py +0 -0
  50. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/interop/qiskit_pennylane.py +0 -0
  51. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/measurement.py +0 -0
  52. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mitigation/healing.py +0 -0
  53. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/mps.py +0 -0
  54. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/__init__.py +0 -0
  55. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/assembly.py +0 -0
  56. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/basis.py +0 -0
  57. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/boys.py +0 -0
  58. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/bridge.py +0 -0
  59. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/cartesian.py +0 -0
  60. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/coulomb.py +0 -0
  61. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/differentiable.py +0 -0
  62. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/gaussians.py +0 -0
  63. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/kinetic.py +0 -0
  64. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/libcint_bridge.py +0 -0
  65. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/overlap.py +0 -0
  66. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/native_hf/scf.py +0 -0
  67. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/__init__.py +0 -0
  68. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/coherent_attack.py +0 -0
  69. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/cosmic_ray.py +0 -0
  70. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/density_matrix_channels.py +0 -0
  71. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/differentiable.py +0 -0
  72. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/__init__.py +0 -0
  73. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/amplitude_damping.py +0 -0
  74. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/bitflip.py +0 -0
  75. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/combined.py +0 -0
  76. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/depolarizing.py +0 -0
  77. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/ideal.py +0 -0
  78. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus/phaseflip.py +0 -0
  79. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/kraus_channels.py +0 -0
  80. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/oscillating.py +0 -0
  81. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/noise/pink.py +0 -0
  82. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/observables.py +0 -0
  83. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/parser.py +0 -0
  84. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/__init__.py +0 -0
  85. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/entropy.py +0 -0
  86. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/fermions.py +0 -0
  87. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/observables.py +0 -0
  88. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/qec.py +0 -0
  89. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/spectral.py +0 -0
  90. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/physics/states.py +0 -0
  91. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/protocols/__init__.py +0 -0
  92. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/protocols/bb84.py +0 -0
  93. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/protocols/di_qkd_ghz.py +0 -0
  94. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/protocols/dicka_protocol2.py +0 -0
  95. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qec.py +0 -0
  96. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qft.py +0 -0
  97. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qmmm/__init__.py +0 -0
  98. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qmmm/ase_bridge.py +0 -0
  99. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qmmm/forces.py +0 -0
  100. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qmmm/propagation.py +0 -0
  101. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/qmmm/region.py +0 -0
  102. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/random_circuit.py +0 -0
  103. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/registry.py +0 -0
  104. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/simulator.py +0 -0
  105. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/solvers/__init__.py +0 -0
  106. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/solvers/autodiff.py +0 -0
  107. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/solvers/harrison_tb.py +0 -0
  108. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/solvers/vhd_tb.py +0 -0
  109. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/states.py +0 -0
  110. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/topology.py +0 -0
  111. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/trotter.py +0 -0
  112. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/utils/__init__.py +0 -0
  113. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/utils/drawing.py +0 -0
  114. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/utils/mass_decomposition.py +0 -0
  115. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/utils/measurement.py +0 -0
  116. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution/vhd_tb.py +0 -0
  117. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/dependency_links.txt +0 -0
  118. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/entry_points.txt +0 -0
  119. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/requires.txt +0 -0
  120. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/dense_evolution.egg-info/top_level.txt +0 -0
  121. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/license.md +0 -0
  122. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/research/local_site/__init__.py +0 -0
  123. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/research/local_site/app/__init__.py +0 -0
  124. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/research/local_site/app/server.py +0 -0
  125. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/setup.cfg +0 -0
  126. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/__init__.py +0 -0
  127. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/_gate_tables.py +0 -0
  128. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/band_structure.py +0 -0
  129. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/circuit_builder_component.py +0 -0
  130. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/circuit_diagram.py +0 -0
  131. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/crypto_protocols.py +0 -0
  132. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/engine.py +0 -0
  133. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/graphical_builder.py +0 -0
  134. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/hamiltonians.py +0 -0
  135. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/mass_decomposition_tool.py +0 -0
  136. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/mitigation.py +0 -0
  137. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/native_hf_diagnostics.py +0 -0
  138. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/noise_tools.py +0 -0
  139. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/qasm_library.py +0 -0
  140. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/qmmm.py +0 -0
  141. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/rag_tool.py +0 -0
  142. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/state_visuals.py +0 -0
  143. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/system_limits.py +0 -0
  144. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/vector_healing.py +0 -0
  145. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/visuals.py +0 -0
  146. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/vqe.py +0 -0
  147. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/dashboard/core/wormhole.py +0 -0
  148. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/ia_utils/__init__.py +0 -0
  149. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/ia_utils/adversarial_vector_attack.py +0 -0
  150. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/ia_utils/rag.py +0 -0
  151. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/ia_utils/vector_healing.py +0 -0
  152. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/__init__.py +0 -0
  153. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/client.py +0 -0
  154. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/config.py +0 -0
  155. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/models.py +0 -0
  156. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/molecules.py +0 -0
  157. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/server.py +0 -0
  158. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/__init__.py +0 -0
  159. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/chemistry_tools.py +0 -0
  160. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/circuit_tools.py +0 -0
  161. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/crypto_tools.py +0 -0
  162. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/mitigation_tools.py +0 -0
  163. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/noise_tools.py +0 -0
  164. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/retrieval_tools.py +0 -0
  165. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/system_tools.py +0 -0
  166. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/tools/wormhole_tools.py +0 -0
  167. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/utils/__init__.py +0 -0
  168. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/utils/cache.py +0 -0
  169. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/utils/images.py +0 -0
  170. {dense_evolution-8.3.0 → dense_evolution-8.3.1}/tools/mcp_server/utils/truncation.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dense-evolution
3
- Version: 8.3.0
3
+ Version: 8.3.1
4
4
  Summary: High-performance quantum simulation toolkit -- Statevector/MPS engines with JIT compilation, noise modeling, VQE, QEC, quantum chemistry, and agent-native tooling
5
5
  Author-email: Salvatore Pennacchio <jtatopenn@libero.it>
6
6
  License: Business Source License 1.1
@@ -300,9 +300,11 @@ sim.run_chunk(circuit.to_tuples(), chunk_size_gates=500)
300
300
 
301
301
  - **JIT-fused statevector engine.** No Kronecker-product overhead — stride-sliced linear kernel fusion compiled through JAX XLA, real GPU/TPU dispatch with no code change. [Simulator](https://tatopenn-cell.github.io/Dense-Evolution/api/simulator/) · [MPS backend](https://tatopenn-cell.github.io/Dense-Evolution/api/mps/) for low-entanglement circuits at scale.
302
302
  - **Anti-OOM `Chunk` engine.** Circuits too large for one array, split dynamically and sized off the real compute device's own free memory — with disk-backed overflow past even that ceiling. [Chunk guide](https://tatopenn-cell.github.io/Dense-Evolution/api/chunk/).
303
- - **Real noise, real mitigation.** Stochastic Kraus channels, real-device noise imported from Qiskit backends, and Zero-Noise Extrapolation to correct for it. [Noise](https://tatopenn-cell.github.io/Dense-Evolution/api/noise/) · [Mitigation](https://tatopenn-cell.github.io/Dense-Evolution/api/mitigation/) · [what noise/mitigation/healing each mean](https://tatopenn-cell.github.io/Dense-Evolution/concepts/).
303
+ - **Real noise, real mitigation.** Stochastic Kraus channels, real-device noise imported from Qiskit backends, and Zero-Noise Extrapolation, with the shot budget split optimally across noise factors and a single-qubit Bloch-vector variant that always returns a valid state, to correct for it. [Noise](https://tatopenn-cell.github.io/Dense-Evolution/api/noise/) · [Mitigation](https://tatopenn-cell.github.io/Dense-Evolution/api/mitigation/) · [what noise/mitigation/healing each mean](https://tatopenn-cell.github.io/Dense-Evolution/concepts/).
304
304
  - **Differentiable VQE, from scratch.** `circuit_to_energy_fn` is the same JAX-differentiable engine real molecular VQE runs on — real Hartree-Fock Hamiltonians, UCCSD/hardware-efficient ansätze, Adam optimization. [Autodiff](https://tatopenn-cell.github.io/Dense-Evolution/api/autodiff/).
305
305
  - **libcint integrals on Windows, macOS and Linux.** `pip install dense-evolution` ships libcint compiled for each system: the full two-electron tensor of ethanol in 6-31G* in 0.32 s. [native_hf](https://tatopenn-cell.github.io/Dense-Evolution/api/native_hf/).
306
+ - **Reversible arithmetic.** Register adders, subtraction, comparisons and modular arithmetic up to modular exponentiation, applied as exact basis permutations and checked against the Vedral, Cuccaro, Draper and Beauregard circuits. [Arithmetic](https://tatopenn-cell.github.io/Dense-Evolution/api/arithmetic/).
307
+ - **Postselection.** Keep the branch where a qubit is found in `|0>`, `|1>`, `|±>` or `|±i>`, with its probability. [Postselection](https://tatopenn-cell.github.io/Dense-Evolution/api/postselect/).
306
308
  - **OpenQASM 2.0/3.0, both directions.** A real parser, plus Qiskit/PennyLane interop bridges. [QASM Parser](https://tatopenn-cell.github.io/Dense-Evolution/api/parser/) · [Interop](https://tatopenn-cell.github.io/Dense-Evolution/api/interop/).
307
309
  - **Code-agnostic QEC decoding**, Majorana/Jordan-Wigner fermion mapping, from-scratch Hartree-Fock for elements outside PennyLane's own basis set, a traversable-wormhole-inspired teleportation protocol, and real QM/MM region partitioning (ring-safe BFS, Diffuse2Seg-derived relevance propagation) — see the [full API reference](https://tatopenn-cell.github.io/Dense-Evolution/api/) for all of it.
308
310
 
@@ -200,9 +200,11 @@ sim.run_chunk(circuit.to_tuples(), chunk_size_gates=500)
200
200
 
201
201
  - **JIT-fused statevector engine.** No Kronecker-product overhead — stride-sliced linear kernel fusion compiled through JAX XLA, real GPU/TPU dispatch with no code change. [Simulator](https://tatopenn-cell.github.io/Dense-Evolution/api/simulator/) · [MPS backend](https://tatopenn-cell.github.io/Dense-Evolution/api/mps/) for low-entanglement circuits at scale.
202
202
  - **Anti-OOM `Chunk` engine.** Circuits too large for one array, split dynamically and sized off the real compute device's own free memory — with disk-backed overflow past even that ceiling. [Chunk guide](https://tatopenn-cell.github.io/Dense-Evolution/api/chunk/).
203
- - **Real noise, real mitigation.** Stochastic Kraus channels, real-device noise imported from Qiskit backends, and Zero-Noise Extrapolation to correct for it. [Noise](https://tatopenn-cell.github.io/Dense-Evolution/api/noise/) · [Mitigation](https://tatopenn-cell.github.io/Dense-Evolution/api/mitigation/) · [what noise/mitigation/healing each mean](https://tatopenn-cell.github.io/Dense-Evolution/concepts/).
203
+ - **Real noise, real mitigation.** Stochastic Kraus channels, real-device noise imported from Qiskit backends, and Zero-Noise Extrapolation, with the shot budget split optimally across noise factors and a single-qubit Bloch-vector variant that always returns a valid state, to correct for it. [Noise](https://tatopenn-cell.github.io/Dense-Evolution/api/noise/) · [Mitigation](https://tatopenn-cell.github.io/Dense-Evolution/api/mitigation/) · [what noise/mitigation/healing each mean](https://tatopenn-cell.github.io/Dense-Evolution/concepts/).
204
204
  - **Differentiable VQE, from scratch.** `circuit_to_energy_fn` is the same JAX-differentiable engine real molecular VQE runs on — real Hartree-Fock Hamiltonians, UCCSD/hardware-efficient ansätze, Adam optimization. [Autodiff](https://tatopenn-cell.github.io/Dense-Evolution/api/autodiff/).
205
205
  - **libcint integrals on Windows, macOS and Linux.** `pip install dense-evolution` ships libcint compiled for each system: the full two-electron tensor of ethanol in 6-31G* in 0.32 s. [native_hf](https://tatopenn-cell.github.io/Dense-Evolution/api/native_hf/).
206
+ - **Reversible arithmetic.** Register adders, subtraction, comparisons and modular arithmetic up to modular exponentiation, applied as exact basis permutations and checked against the Vedral, Cuccaro, Draper and Beauregard circuits. [Arithmetic](https://tatopenn-cell.github.io/Dense-Evolution/api/arithmetic/).
207
+ - **Postselection.** Keep the branch where a qubit is found in `|0>`, `|1>`, `|±>` or `|±i>`, with its probability. [Postselection](https://tatopenn-cell.github.io/Dense-Evolution/api/postselect/).
206
208
  - **OpenQASM 2.0/3.0, both directions.** A real parser, plus Qiskit/PennyLane interop bridges. [QASM Parser](https://tatopenn-cell.github.io/Dense-Evolution/api/parser/) · [Interop](https://tatopenn-cell.github.io/Dense-Evolution/api/interop/).
207
209
  - **Code-agnostic QEC decoding**, Majorana/Jordan-Wigner fermion mapping, from-scratch Hartree-Fock for elements outside PennyLane's own basis set, a traversable-wormhole-inspired teleportation protocol, and real QM/MM region partitioning (ring-safe BFS, Diffuse2Seg-derived relevance propagation) — see the [full API reference](https://tatopenn-cell.github.io/Dense-Evolution/api/) for all of it.
208
210
 
@@ -31,7 +31,7 @@ from .backends.mps import MPSSimulator
31
31
  from .mitigation.zne import (richardson_extrapolate, richardson_amplification_factor,
32
32
  zero_noise_extrapolation, polynomial_extrapolate,
33
33
  bounded_exponential_extrapolate,
34
- project_to_physical, uhlmann_fidelity, zne_density_matrix,
34
+ project_to_physical, bloch_zne, bloch_zne_jit, uhlmann_fidelity, zne_density_matrix,
35
35
  jsd_predictive_zne_density_matrix,
36
36
  coherence_predictive_zne_density_matrix,
37
37
  classically_augmented_zne_phaseflip,
@@ -45,6 +45,12 @@ from .physics.observables import (pauli_expectation, pauli_sum_expectation, paul
45
45
  from .physics.states import ghz_state
46
46
  from .utils.measurement import sample_counts, statevector_fidelity
47
47
  from .circuits.qft import qft
48
+ from .circuits.arithmetic import (
49
+ add_registers, subtract_registers, add_constant,
50
+ compare_registers, compare_constant,
51
+ add_registers_mod, add_constant_mod, multiply_add_mod, multiply_mod, power_mod,
52
+ )
53
+ from .circuits.postselect import postselect
48
54
  from .circuits.random_circuit import random_circuit
49
55
  from .utils.drawing import draw_circuit
50
56
  from .solvers.harrison_tb import (ELEMENTS as HARRISON_ELEMENTS, ETA as HARRISON_ETA,
@@ -62,7 +68,7 @@ from .physics.qec import (pauli_commutes, compute_syndrome, erasure_aware_decode
62
68
  peeling_decode, union_find_decode, matching_erasure_decode,
63
69
  estimate_edge_probabilities_from_detection_events)
64
70
 
65
- __version__ = "8.3.0"
71
+ __version__ = "8.3.1"
66
72
 
67
73
  __all__ = [
68
74
  "__version__",
@@ -75,6 +81,10 @@ __all__ = [
75
81
  "NoiseModel", "NoiseSpec", "QuantumHardwareRegistry",
76
82
  "GATES", "PARAMETRIC_GATES", "GATE_IDS",
77
83
  "entangling_layer", "qft",
84
+ "add_registers", "subtract_registers", "add_constant",
85
+ "compare_registers", "compare_constant",
86
+ "add_registers_mod", "add_constant_mod", "multiply_add_mod", "multiply_mod", "power_mod",
87
+ "postselect",
78
88
  "pauli_rotation_ops", "trotter_evolve_ops", "continuous_pulse_evolve",
79
89
  "continuous_dissipative_evolve",
80
90
  "find_excitations", "single_excitation_ops", "double_excitation_ops",
@@ -91,7 +101,7 @@ __all__ = [
91
101
  "richardson_extrapolate", "richardson_amplification_factor",
92
102
  "zero_noise_extrapolation", "polynomial_extrapolate",
93
103
  "bounded_exponential_extrapolate",
94
- "project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
104
+ "project_to_physical", "bloch_zne", "bloch_zne_jit", "uhlmann_fidelity", "zne_density_matrix",
95
105
  "jsd_predictive_zne_density_matrix", "coherence_predictive_zne_density_matrix",
96
106
  "classically_augmented_zne_phaseflip",
97
107
  "global_depolarizing_channel", "amplitude_damping_channel", "phaseflip_channel_exact",
@@ -5,6 +5,12 @@ from .compiler import QuantumTranspiler
5
5
  from .registry import HAS_JAX, NoiseModel, NoiseSpec, QuantumHardwareRegistry
6
6
  from .topology import entangling_layer, VALID_PATTERNS
7
7
  from .qft import qft
8
+ from .arithmetic import (
9
+ add_registers, subtract_registers, add_constant,
10
+ compare_registers, compare_constant,
11
+ add_registers_mod, add_constant_mod, multiply_add_mod, multiply_mod, power_mod,
12
+ )
13
+ from .postselect import postselect
8
14
  from .random_circuit import random_circuit
9
15
  from .trotter import pauli_rotation_ops, trotter_evolve_ops
10
16
 
@@ -15,6 +21,10 @@ __all__ = [
15
21
  "HAS_JAX", "NoiseModel", "NoiseSpec", "QuantumHardwareRegistry",
16
22
  "entangling_layer", "VALID_PATTERNS",
17
23
  "qft",
24
+ "add_registers", "subtract_registers", "add_constant",
25
+ "compare_registers", "compare_constant",
26
+ "add_registers_mod", "add_constant_mod", "multiply_add_mod", "multiply_mod", "power_mod",
27
+ "postselect",
18
28
  "random_circuit",
19
29
  "pauli_rotation_ops", "trotter_evolve_ops",
20
30
  ]
@@ -0,0 +1,241 @@
1
+ """
2
+ Reversible arithmetic on qubit registers, applied to a statevector.
3
+
4
+ Every operation here is a bijection f on computational basis states, so its
5
+ unitary is the permutation U|x> = |f(x)> (Vedral, Barenco, Ekert,
6
+ quant-ph/9511018, Sect. II, Eqs. 2-3). It is applied directly as an index
7
+ permutation of the statevector, which is exact and costs one gather over the
8
+ 2^n amplitudes. The gate-level circuits from the papers (Cuccaro et al.
9
+ quant-ph/0410184 ripple-carry adder, Draper quant-ph/0008033 transform
10
+ adder) are the references the tests compare against.
11
+
12
+ A register is a list of qubit indices, least significant bit first. Qubit 0
13
+ is the most significant bit of the statevector index, as everywhere in the
14
+ engine.
15
+ """
16
+ import numpy as np
17
+
18
+ from ..config import ensure_x64
19
+ from .registry import HAS_JAX
20
+
21
+ if HAS_JAX:
22
+ import jax.numpy as jnp
23
+ xp = jnp
24
+ else:
25
+ xp = np
26
+
27
+ __all__ = ['add_registers', 'subtract_registers', 'add_constant',
28
+ 'compare_registers', 'compare_constant',
29
+ 'add_registers_mod', 'add_constant_mod', 'multiply_add_mod',
30
+ 'multiply_mod', 'power_mod']
31
+
32
+ _COMPARISONS = {
33
+ '<': np.less, '>': np.greater, '<=': np.less_equal,
34
+ '>=': np.greater_equal, '==': np.equal, '!=': np.not_equal,
35
+ }
36
+
37
+
38
+ def _register_values(n_qubits, register):
39
+ idx = np.arange(2 ** n_qubits)
40
+ value = np.zeros_like(idx)
41
+ for k, q in enumerate(register):
42
+ value |= ((idx >> (n_qubits - 1 - q)) & 1) << k
43
+ return idx, value
44
+
45
+
46
+ def _with_register(n_qubits, idx, register, value):
47
+ out = idx.copy()
48
+ for k, q in enumerate(register):
49
+ shift = n_qubits - 1 - q
50
+ out = (out & ~(1 << shift)) | (((value >> k) & 1) << shift)
51
+ return out
52
+
53
+
54
+ def _check(n_qubits, *registers):
55
+ used = [q for r in registers for q in r]
56
+ if len(set(used)) != len(used):
57
+ raise ValueError("registers must not share qubits")
58
+ if any(q < 0 or q >= n_qubits for q in used):
59
+ raise ValueError(f"qubit index out of range for {n_qubits} qubits")
60
+ if any(len(r) == 0 for r in registers):
61
+ raise ValueError("registers must not be empty")
62
+
63
+
64
+ def _permute(sv, target):
65
+ if xp is np:
66
+ sv = np.asarray(sv)
67
+ out = np.zeros_like(sv)
68
+ out[target] = sv
69
+ return out
70
+ ensure_x64()
71
+ sv = xp.asarray(sv)
72
+ return xp.zeros_like(sv).at[target].set(sv)
73
+
74
+
75
+ def add_registers(sv, n_qubits, a, b):
76
+ """
77
+ |a, b> -> |a, (a + b) mod 2^len(b)>.
78
+
79
+ With len(b) = len(a) + 1 the sum never overflows (Vedral et al.,
80
+ Sect. III.A, Eq. 9); with len(b) = len(a) it is addition modulo 2^n
81
+ (Cuccaro et al., Sect. 4.1).
82
+ """
83
+ _check(n_qubits, a, b)
84
+ idx, va = _register_values(n_qubits, a)
85
+ _, vb = _register_values(n_qubits, b)
86
+ target = _with_register(n_qubits, idx, b, (va + vb) % (1 << len(b)))
87
+ return _permute(sv, target)
88
+
89
+
90
+ def subtract_registers(sv, n_qubits, a, b):
91
+ """
92
+ |a, b> -> |a, (b - a) mod 2^len(b)>, the inverse of add_registers.
93
+
94
+ If b < a the result is 2^len(b) - (a - b) and, with len(b) = len(a) + 1,
95
+ its most significant qubit is 1 (Vedral et al., Sect. III.A).
96
+ """
97
+ _check(n_qubits, a, b)
98
+ idx, va = _register_values(n_qubits, a)
99
+ _, vb = _register_values(n_qubits, b)
100
+ target = _with_register(n_qubits, idx, b, (vb - va) % (1 << len(b)))
101
+ return _permute(sv, target)
102
+
103
+
104
+ def add_constant(sv, n_qubits, b, c):
105
+ """
106
+ |b> -> |(b + c) mod 2^len(b)> for a classical integer c.
107
+
108
+ c = 1 is increment, c = -1 decrement. Same operation as Draper's
109
+ transform adder with classical addend (Sect. 5) and Beauregard's
110
+ phiADD(c) (quant-ph/0205095, Sect. 2.1).
111
+ """
112
+ _check(n_qubits, b)
113
+ idx, vb = _register_values(n_qubits, b)
114
+ target = _with_register(n_qubits, idx, b, (vb + int(c)) % (1 << len(b)))
115
+ return _permute(sv, target)
116
+
117
+
118
+ def _flip_if(n_qubits, idx, out, condition):
119
+ return idx ^ (condition.astype(idx.dtype) << (n_qubits - 1 - out))
120
+
121
+
122
+ def _comparison(op):
123
+ if op not in _COMPARISONS:
124
+ raise ValueError(f"op must be one of {sorted(_COMPARISONS)}, got {op!r}")
125
+ return _COMPARISONS[op]
126
+
127
+
128
+ def compare_registers(sv, n_qubits, a, b, out, op='<'):
129
+ """
130
+ |a, b, z> -> |a, b, z XOR [a op b]>, inputs unchanged.
131
+
132
+ op = '<' is the comparator of Cuccaro et al. (Sect. 4.3): the high bit of
133
+ a - b, which is 1 if and only if a < b. The other comparisons follow by
134
+ swapping the operands and negating the output qubit.
135
+ """
136
+ _check(n_qubits, a, b, [out])
137
+ idx, va = _register_values(n_qubits, a)
138
+ _, vb = _register_values(n_qubits, b)
139
+ return _permute(sv, _flip_if(n_qubits, idx, out, _comparison(op)(va, vb)))
140
+
141
+
142
+ def compare_constant(sv, n_qubits, b, c, out, op='<'):
143
+ """
144
+ |b, z> -> |b, z XOR [b op c]> for a classical integer c.
145
+
146
+ op = '<' is the most significant qubit after the reverse phiADD(c) of
147
+ Beauregard (quant-ph/0205095, Sect. 2.1, Fig. 4).
148
+ """
149
+ _check(n_qubits, b, [out])
150
+ idx, vb = _register_values(n_qubits, b)
151
+ return _permute(sv, _flip_if(n_qubits, idx, out, _comparison(op)(vb, int(c))))
152
+
153
+
154
+ def _active(n_qubits, idx, controls):
155
+ on = np.ones(idx.shape, dtype=bool)
156
+ for q in controls:
157
+ on &= ((idx >> (n_qubits - 1 - q)) & 1).astype(bool)
158
+ return on
159
+
160
+
161
+ def _check_modulus(N, *registers):
162
+ if N < 1 or any(N > (1 << len(r)) for r in registers):
163
+ raise ValueError(f"modulus N={N} must be >= 1 and fit in the register")
164
+
165
+
166
+ def add_registers_mod(sv, n_qubits, a, b, N):
167
+ """
168
+ |a, b> -> |a, (a + b) mod N> for 0 <= a, b < N (Vedral et al., Sect. III.B,
169
+ Eq. 10). Basis states with a >= N or b >= N are left unchanged, which
170
+ keeps the map a permutation.
171
+ """
172
+ _check(n_qubits, a, b)
173
+ _check_modulus(N, a, b)
174
+ idx, va = _register_values(n_qubits, a)
175
+ _, vb = _register_values(n_qubits, b)
176
+ ok = (va < N) & (vb < N)
177
+ return _permute(sv, _with_register(n_qubits, idx, b, np.where(ok, (va + vb) % N, vb)))
178
+
179
+
180
+ def add_constant_mod(sv, n_qubits, b, c, N, controls=()):
181
+ """
182
+ |b> -> |(b + c) mod N> for 0 <= b < N, applied only where every control
183
+ qubit is 1: the doubly controlled phiADD(c)MOD(N) of Beauregard
184
+ (quant-ph/0205095, Sect. 2.2, Fig. 5). Basis states with b >= N are left
185
+ unchanged.
186
+ """
187
+ _check(n_qubits, b, *([list(controls)] if controls else []))
188
+ _check_modulus(N, b)
189
+ idx, vb = _register_values(n_qubits, b)
190
+ ok = _active(n_qubits, idx, controls) & (vb < N)
191
+ return _permute(sv, _with_register(n_qubits, idx, b, np.where(ok, (vb + int(c)) % N, vb)))
192
+
193
+
194
+ def multiply_add_mod(sv, n_qubits, x, b, a, N, controls=()):
195
+ """
196
+ |x, b> -> |x, (b + a*x) mod N> for 0 <= b < N, applied only where every
197
+ control qubit is 1: CMULT(a)MOD(N) of Beauregard (Sect. 2.3, Fig. 6),
198
+ built in the paper from n controlled modular additions of 2^i*a mod N
199
+ (Vedral et al., Sect. III.C). Basis states with b >= N are left unchanged.
200
+ """
201
+ _check(n_qubits, x, b, *([list(controls)] if controls else []))
202
+ _check_modulus(N, b)
203
+ idx, vx = _register_values(n_qubits, x)
204
+ _, vb = _register_values(n_qubits, b)
205
+ ok = _active(n_qubits, idx, controls) & (vb < N)
206
+ new = (vb + (int(a) % N) * (vx % N)) % N
207
+ return _permute(sv, _with_register(n_qubits, idx, b, np.where(ok, new, vb)))
208
+
209
+
210
+ def multiply_mod(sv, n_qubits, x, a, N, controls=()):
211
+ """
212
+ |x> -> |a*x mod N> in place for 0 <= x < N and gcd(a, N) = 1, applied only
213
+ where every control qubit is 1 (Vedral et al., Sect. II, Eqs. 4-6;
214
+ Beauregard, Sect. 2.3, Fig. 7). With N = 2^len(x) and odd a this is plain
215
+ multiplication modulo 2^n. Basis states with x >= N are left unchanged.
216
+ """
217
+ _check(n_qubits, x, *([list(controls)] if controls else []))
218
+ _check_modulus(N, x)
219
+ if np.gcd(int(a), N) != 1:
220
+ raise ValueError(f"a={a} and N={N} must be coprime for an in-place multiplication")
221
+ idx, vx = _register_values(n_qubits, x)
222
+ ok = _active(n_qubits, idx, controls) & (vx < N)
223
+ return _permute(sv, _with_register(n_qubits, idx, x, np.where(ok, (int(a) * vx) % N, vx)))
224
+
225
+
226
+ def power_mod(sv, n_qubits, x, y, a, N):
227
+ """
228
+ |x, y> -> |x, y * a^x mod N> for 0 <= y < N and gcd(a, N) = 1. With y = 1
229
+ this is the modular exponentiation |x, 1> -> |x, a^x mod N> of Shor's
230
+ algorithm (Vedral et al., Eq. 1 and Sect. III.D). Basis states with
231
+ y >= N are left unchanged.
232
+ """
233
+ _check(n_qubits, x, y)
234
+ _check_modulus(N, y)
235
+ if np.gcd(int(a), N) != 1:
236
+ raise ValueError(f"a={a} and N={N} must be coprime")
237
+ idx, vx = _register_values(n_qubits, x)
238
+ _, vy = _register_values(n_qubits, y)
239
+ powers = np.array([pow(int(a), int(e), N) for e in range(1 << len(x))])
240
+ ok = vy < N
241
+ return _permute(sv, _with_register(n_qubits, idx, y, np.where(ok, (vy * powers[vx]) % N, vy)))
@@ -260,12 +260,9 @@ class QASMParser:
260
260
  - `for` loops with unresolvable bounds, and all `if`/`while`/`def`
261
261
  blocks (no static execution — would need runtime classical bit
262
262
  state), are simply removed, leaving the rest of the source intact.
263
- - `gate` definitions are removed too — their body uses the gate's
264
- own formal parameter names, not real qubit indices, so it can't
265
- be executed directly; a later call site referencing that gate
266
- name still falls through as an unrecognized gate (silent no-op,
267
- same as any other unknown gate name elsewhere in this codebase),
268
- but no longer corrupts the qubit/statement that follows it.
263
+ - `gate` definitions never reach this loop: parse() lifts them out
264
+ first with _extract_gate_definitions and expands every call site
265
+ into the definition's body (_expand_gate_call).
269
266
 
270
267
  Runs as a search/replace loop rather than recursion: after an outer
271
268
  block is unrolled, any inner (nested) blocks are duplicated as raw
@@ -303,6 +300,70 @@ class QASMParser:
303
300
  s = s[:m.start()] + replacement + s[close_brace + 1:]
304
301
  return s
305
302
 
303
+ _RE_GATE_DEF = re.compile(r'\bgate\s+([a-zA-Z_]\w*)\s*(?:\(([^)]*)\))?\s*([^{]*)\{')
304
+
305
+ def _extract_gate_definitions(self, s: str) -> Tuple[str, Dict]:
306
+ """
307
+ Remove every `gate NAME(params) args { body }` definition from the
308
+ source and return it with a table NAME -> (params, args, body).
309
+
310
+ OpenQASM 2.0 defines new gates as a body of earlier gates acting on
311
+ formal arguments; exporters such as qiskit.qasm2.dumps write
312
+ composite and arithmetic circuits this way (e.g. gate_MAJ inside
313
+ CDKMRippleCarryAdder), so call sites must be expanded, not dropped.
314
+ """
315
+ defs: Dict[str, Tuple[List[str], List[str], str]] = {}
316
+ while True:
317
+ m = self._RE_GATE_DEF.search(s)
318
+ if not m:
319
+ return s, defs
320
+ close = self._find_matching_brace(s, m.end() - 1)
321
+ if close is None:
322
+ return s, defs
323
+ params = [p.strip() for p in (m.group(2) or '').split(',') if p.strip()]
324
+ args = [a.strip() for a in m.group(3).split(',') if a.strip()]
325
+ defs[m.group(1).lower()] = (params, args, s[m.end():close])
326
+ s = s[:m.start()] + s[close + 1:]
327
+
328
+ def _expand_gate_call(self, instr: str, defs: Dict, depth: int = 0) -> List[str]:
329
+ """
330
+ Replace a call to a user-defined gate by its body, with formal
331
+ arguments and parameters substituted, recursively for nested
332
+ definitions. Anything else is returned unchanged.
333
+ """
334
+ instr = re.sub(r'\s+', ' ', instr).strip()
335
+ head = re.split(r'[\s(]', instr, 1)[0].lower()
336
+ if head not in defs:
337
+ return [instr]
338
+ if depth > 64:
339
+ raise ValueError(f"gate definitions nested too deeply at '{head}'")
340
+ params, args, body = defs[head]
341
+ rest = instr[len(head):].strip()
342
+ actual_params: List[str] = []
343
+ if rest.startswith('('):
344
+ depth_p, close = 0, -1
345
+ for i, ch in enumerate(rest):
346
+ depth_p += (ch == '(') - (ch == ')')
347
+ if depth_p == 0:
348
+ close = i
349
+ break
350
+ actual_params = [t.strip() for t in self._split_params(rest[1:close])]
351
+ rest = rest[close + 1:]
352
+ actual_args = [a.strip() for a in rest.split(',') if a.strip()]
353
+ if len(actual_args) != len(args) or len(actual_params) != len(params):
354
+ raise ValueError(f"gate '{head}' expects {len(params)} parameters and "
355
+ f"{len(args)} qubits, got '{instr}'")
356
+ table = dict(zip(args, actual_args))
357
+ table.update({p: f'({v})' for p, v in zip(params, actual_params)})
358
+ if table:
359
+ names = re.compile(r'\b(' + '|'.join(re.escape(k) for k in table) + r')\b')
360
+ body = names.sub(lambda mm: table[mm.group(1)], body)
361
+ out: List[str] = []
362
+ for st in body.split(';'):
363
+ if st.strip():
364
+ out.extend(self._expand_gate_call(st, defs, depth + 1))
365
+ return out
366
+
306
367
  def parse(self, qasm_str: str) -> QASMCircuit:
307
368
  """
308
369
  Parse an OpenQASM 2.0 or 3.0 string into a QASMCircuit.
@@ -337,10 +398,12 @@ class QASMParser:
337
398
  # Must run before the ';'-split below: brace-delimited blocks are
338
399
  # not single ';'-terminated statements, and left alone they corrupt
339
400
  # whatever real statement follows them on the same line.
401
+ cleaned, gate_defs = self._extract_gate_definitions(cleaned)
340
402
  cleaned = self._process_block_constructs(cleaned)
341
403
 
342
404
  # ── split into statements ─────────────────────────────────────
343
405
  statements = [s.strip() for s in cleaned.split(';') if s.strip()]
406
+ statements = [e for st in statements for e in self._expand_gate_call(st, gate_defs)]
344
407
 
345
408
  for instr in statements:
346
409
  # collapse internal whitespace runs to a single space
@@ -0,0 +1,59 @@
1
+ """
2
+ Postselection: keep only the branch of the state in which a qubit is found in a
3
+ chosen state, then renormalise.
4
+
5
+ This is the operation in the definition of PostBQP (Aaronson,
6
+ quant-ph/0412187, Definition 1): the computation is conditioned on a
7
+ measurement outcome that has nonzero probability. It is not unitary, so it is
8
+ applied to the statevector directly, as the projector |psi><psi| on the
9
+ qubit followed by renormalisation, and the success probability is returned
10
+ with the new state.
11
+ """
12
+ import numpy as np
13
+
14
+ from ..config import ensure_x64
15
+ from .registry import HAS_JAX
16
+
17
+ if HAS_JAX:
18
+ import jax.numpy as jnp
19
+ xp = jnp
20
+ else:
21
+ xp = np
22
+
23
+ __all__ = ['postselect', 'POSTSELECT_STATES']
24
+
25
+ _R = 1 / np.sqrt(2)
26
+ POSTSELECT_STATES = {
27
+ '0': np.array([1, 0], dtype=np.complex128),
28
+ '1': np.array([0, 1], dtype=np.complex128),
29
+ '+': np.array([_R, _R], dtype=np.complex128),
30
+ '-': np.array([_R, -_R], dtype=np.complex128),
31
+ '+i': np.array([_R, 1j * _R], dtype=np.complex128),
32
+ '-i': np.array([_R, -1j * _R], dtype=np.complex128),
33
+ }
34
+
35
+
36
+ def postselect(sv, n_qubits, qubit, state='1'):
37
+ """
38
+ Project `qubit` onto `state` and renormalise.
39
+
40
+ `state` is one of '0', '1', '+', '-', '+i', '-i'. Returns
41
+ `(new_sv, probability)`, where `probability` is the chance of that outcome
42
+ before postselection. Raises ValueError if the probability is zero, since
43
+ postselection is only defined for outcomes that can occur (Aaronson,
44
+ Definition 1, condition (i)).
45
+ """
46
+ if state not in POSTSELECT_STATES:
47
+ raise ValueError(f"state must be one of {list(POSTSELECT_STATES)}, got {state!r}")
48
+ if not 0 <= qubit < n_qubits:
49
+ raise ValueError(f"qubit index out of range for {n_qubits} qubits")
50
+ if HAS_JAX:
51
+ ensure_x64()
52
+ psi = xp.asarray(POSTSELECT_STATES[state])
53
+ t = xp.asarray(sv).reshape(2 ** qubit, 2, 2 ** (n_qubits - qubit - 1))
54
+ overlap = xp.einsum('j,ijk->ik', xp.conj(psi), t)
55
+ probability = float(xp.sum(xp.abs(overlap) ** 2))
56
+ if probability <= 1e-15:
57
+ raise ValueError(f"outcome {state!r} on qubit {qubit} has zero probability")
58
+ projected = xp.einsum('j,ik->ijk', psi, overlap).reshape(-1)
59
+ return projected / np.sqrt(probability), probability
@@ -19,6 +19,8 @@ QuantumHardwareRegistry.__init__, circuit_to_energy_fn); set_precision()
19
19
  is the public opt-out for a caller who wants to choose precision
20
20
  explicitly, before constructing anything, and have that choice stick.
21
21
  """
22
+ import functools
23
+
22
24
  import jax
23
25
 
24
26
  _explicit = False
@@ -47,3 +49,14 @@ def ensure_x64() -> None:
47
49
  precision -- never at import time."""
48
50
  if not _explicit:
49
51
  jax.config.update("jax_enable_x64", True)
52
+
53
+
54
+ def with_x64(fn):
55
+ """Wrap `fn` so ensure_x64() runs before every call -- for public entry
56
+ points that build JAX arrays from user input, which JAX would otherwise
57
+ silently truncate to complex64/float32 if nothing else enabled x64 first."""
58
+ @functools.wraps(fn)
59
+ def wrapper(*args, **kwargs):
60
+ ensure_x64()
61
+ return fn(*args, **kwargs)
62
+ return wrapper
@@ -8,10 +8,10 @@ snapshots (magic_entropy_shadows), a multi-qubit per-state magic monotone
8
8
  that module's own docstring for the distinction), and the classical
9
9
  Kullback-Leibler divergence over probability distributions (kl_divergence)."""
10
10
  from .zne import (
11
- richardson_extrapolate, richardson_amplification_factor,
11
+ richardson_extrapolate, richardson_amplification_factor, zne_shot_allocation,
12
12
  zero_noise_extrapolation, polynomial_extrapolate,
13
13
  bounded_exponential_extrapolate,
14
- project_to_physical, uhlmann_fidelity, zne_density_matrix,
14
+ project_to_physical, bloch_zne, bloch_zne_jit, uhlmann_fidelity, zne_density_matrix,
15
15
  jsd_predictive_zne_density_matrix,
16
16
  coherence_predictive_zne_density_matrix,
17
17
  classically_augmented_zne_phaseflip,
@@ -33,10 +33,10 @@ from .magic_entropy_shadows import (
33
33
  from .kl_divergence import kl_divergence, kl_divergence_jit
34
34
 
35
35
  __all__ = [
36
- "richardson_extrapolate", "richardson_amplification_factor",
36
+ "richardson_extrapolate", "richardson_amplification_factor", "zne_shot_allocation",
37
37
  "zero_noise_extrapolation", "polynomial_extrapolate",
38
38
  "bounded_exponential_extrapolate",
39
- "project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
39
+ "project_to_physical", "bloch_zne", "bloch_zne_jit", "uhlmann_fidelity", "zne_density_matrix",
40
40
  "jsd_predictive_zne_density_matrix",
41
41
  "coherence_predictive_zne_density_matrix",
42
42
  "classically_augmented_zne_phaseflip",
@@ -41,6 +41,7 @@ Additive, not a replacement for the already-validated healing pipeline.
41
41
  """
42
42
  import jax
43
43
  import jax.numpy as jnp
44
+ from ..config import with_x64
44
45
 
45
46
  __all__ = ["kl_divergence", "kl_divergence_jit"]
46
47
 
@@ -69,6 +70,7 @@ def _kl_divergence_core(p: jnp.ndarray, q: jnp.ndarray) -> jnp.ndarray:
69
70
  return jnp.where(is_support_violation, jnp.inf, finite_result)
70
71
 
71
72
 
73
+ @with_x64
72
74
  def kl_divergence(p: jnp.ndarray, q: jnp.ndarray) -> float:
73
75
  """Classical Kullback-Leibler divergence D_KL(p||q), in bits.
74
76
 
@@ -88,6 +90,6 @@ def kl_divergence(p: jnp.ndarray, q: jnp.ndarray) -> float:
88
90
  return float(_kl_divergence_core(p, q))
89
91
 
90
92
 
91
- kl_divergence_jit = jax.jit(_kl_divergence_core)
93
+ kl_divergence_jit = with_x64(jax.jit(_kl_divergence_core))
92
94
  """`jax.jit`-compiled entry point for `kl_divergence`. `p`/`q` must already
93
95
  be `float64` arrays. Returns a jnp scalar, not a Python `float`."""
@@ -45,6 +45,7 @@ import functools
45
45
  import jax
46
46
  import jax.numpy as jnp
47
47
  import numpy as np
48
+ from ..config import with_x64
48
49
 
49
50
  __all__ = ["magic_entropy", "magic_entropy_jit"]
50
51
 
@@ -130,6 +131,7 @@ def _magic_entropy_core(rho: jnp.ndarray, eps: float = 1e-12) -> jnp.ndarray:
130
131
  return -jnp.sum(safe_ev * jnp.log2(safe_ev))
131
132
 
132
133
 
134
+ @with_x64
133
135
  def magic_entropy(rho: jnp.ndarray) -> float:
134
136
  """Magic entropy of a single-qubit density matrix `rho` (2x2), in bits
135
137
  (log2) -- NOTE this differs from
@@ -157,7 +159,7 @@ def magic_entropy(rho: jnp.ndarray) -> float:
157
159
  return float(_magic_entropy_core(rho))
158
160
 
159
161
 
160
- magic_entropy_jit = jax.jit(_magic_entropy_core)
162
+ magic_entropy_jit = with_x64(jax.jit(_magic_entropy_core))
161
163
  """`jax.jit`-compiled entry point for `magic_entropy`. `rho` must already
162
164
  be `complex128`. Returns a jnp scalar, not a Python `float` -- call
163
165
  `float(...)` yourself if you need one outside a jitted context."""
@@ -36,6 +36,7 @@ import jax.numpy as jnp
36
36
  import numpy as np
37
37
 
38
38
  from .magic_entropy import _key_unitary_k3
39
+ from ..config import with_x64
39
40
 
40
41
  __all__ = [
41
42
  "sample_classical_shadow", "magic_entropy_from_shadows",
@@ -56,6 +57,7 @@ def _basis_u() -> jnp.ndarray:
56
57
  return jnp.stack([h, h @ sdag, jnp.eye(2, dtype=jnp.complex128)])
57
58
 
58
59
 
60
+ @with_x64
59
61
  def sample_classical_shadow(rho: jnp.ndarray, n_snapshots: int, seed: int = 0) -> jnp.ndarray:
60
62
  """Simulates the real single-qubit random-Pauli classical-shadow
61
63
  measurement protocol against a known `rho` (2x2, pure or mixed): for
@@ -149,6 +151,7 @@ def _median_of_means(values: np.ndarray, n_groups: int) -> float:
149
151
  return float(np.median(group_means))
150
152
 
151
153
 
154
+ @with_x64
152
155
  def magic_entropy_from_shadows(shadow_snapshots: jnp.ndarray, n_groups: int = 20) -> float:
153
156
  """Estimates `magic_entropy(rho)` from classical shadow snapshots of
154
157
  `rho` (from `sample_classical_shadow`, or real hardware measurement
@@ -208,6 +211,7 @@ _FIT_C = 11.751
208
211
  _FIT_P = 0.546
209
212
 
210
213
 
214
+ @with_x64
211
215
  def approx_shadow_std(n_snapshots: int) -> float:
212
216
  """Rough approximate standard deviation (bits) of
213
217
  `magic_entropy_from_shadows`'s estimate at a given snapshot count,
@@ -225,6 +229,7 @@ def approx_shadow_std(n_snapshots: int) -> float:
225
229
  return _FIT_C / float(n_snapshots) ** _FIT_P
226
230
 
227
231
 
232
+ @with_x64
228
233
  def fit_shadow_sample_complexity(rho: jnp.ndarray, exact_value: float, n_snapshots_list, n_trials: int, seed_base: int = 0):
229
234
  """Empirically measures `magic_entropy_from_shadows`'s standard
230
235
  deviation across `n_trials` independent shadow samplings at each