dense-evolution 8.3.0__py3-none-win_amd64.whl

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 (165) hide show
  1. dashboard_core/__init__.py +115 -0
  2. dashboard_core/_gate_tables.py +30 -0
  3. dashboard_core/band_structure.py +71 -0
  4. dashboard_core/circuit_builder_component.py +232 -0
  5. dashboard_core/circuit_diagram.py +216 -0
  6. dashboard_core/crypto_protocols.py +77 -0
  7. dashboard_core/engine.py +326 -0
  8. dashboard_core/graphical_builder.py +114 -0
  9. dashboard_core/hamiltonians.py +593 -0
  10. dashboard_core/mass_decomposition_tool.py +47 -0
  11. dashboard_core/mitigation.py +343 -0
  12. dashboard_core/native_hf_diagnostics.py +62 -0
  13. dashboard_core/noise_tools.py +125 -0
  14. dashboard_core/qasm_library.py +233 -0
  15. dashboard_core/qmmm.py +16 -0
  16. dashboard_core/rag_tool.py +45 -0
  17. dashboard_core/state_visuals.py +288 -0
  18. dashboard_core/system_limits.py +60 -0
  19. dashboard_core/vector_healing.py +102 -0
  20. dashboard_core/visuals.py +158 -0
  21. dashboard_core/vqe.py +533 -0
  22. dashboard_core/wormhole.py +580 -0
  23. dense_evolution/__init__.py +114 -0
  24. dense_evolution/autodiff.py +10 -0
  25. dense_evolution/backends/__init__.py +5 -0
  26. dense_evolution/backends/chunk/__init__.py +37 -0
  27. dense_evolution/backends/chunk/_engine_imports.py +57 -0
  28. dense_evolution/backends/chunk/circuit_chunker.py +55 -0
  29. dense_evolution/backends/chunk/core.py +432 -0
  30. dense_evolution/backends/chunk/disk_overflow.py +232 -0
  31. dense_evolution/backends/chunk/geometry.py +95 -0
  32. dense_evolution/backends/chunk/guard.py +190 -0
  33. dense_evolution/backends/chunk/kernels.py +531 -0
  34. dense_evolution/backends/mps.py +1569 -0
  35. dense_evolution/backends/statevector.py +616 -0
  36. dense_evolution/chunk.py +25 -0
  37. dense_evolution/circuits/__init__.py +20 -0
  38. dense_evolution/circuits/compiler.py +488 -0
  39. dense_evolution/circuits/diagram.py +94 -0
  40. dense_evolution/circuits/gates.py +91 -0
  41. dense_evolution/circuits/parser.py +632 -0
  42. dense_evolution/circuits/qft.py +66 -0
  43. dense_evolution/circuits/random_circuit.py +85 -0
  44. dense_evolution/circuits/registry.py +74 -0
  45. dense_evolution/circuits/topology.py +79 -0
  46. dense_evolution/circuits/trotter.py +265 -0
  47. dense_evolution/circuits/uccsd.py +275 -0
  48. dense_evolution/cli.py +199 -0
  49. dense_evolution/compiler.py +9 -0
  50. dense_evolution/config.py +49 -0
  51. dense_evolution/drawing.py +10 -0
  52. dense_evolution/entropy.py +9 -0
  53. dense_evolution/fermions.py +9 -0
  54. dense_evolution/gates.py +9 -0
  55. dense_evolution/harrison_tb.py +16 -0
  56. dense_evolution/healing.py +18 -0
  57. dense_evolution/interop/__init__.py +18 -0
  58. dense_evolution/interop/qiskit_pennylane.py +406 -0
  59. dense_evolution/measurement.py +10 -0
  60. dense_evolution/mitigation/__init__.py +54 -0
  61. dense_evolution/mitigation/healing.py +215 -0
  62. dense_evolution/mitigation/kl_divergence.py +93 -0
  63. dense_evolution/mitigation/magic_entropy.py +163 -0
  64. dense_evolution/mitigation/magic_entropy_shadows.py +262 -0
  65. dense_evolution/mitigation/renyi.py +168 -0
  66. dense_evolution/mitigation/stabilizer_renyi_entropy.py +103 -0
  67. dense_evolution/mitigation/zne.py +990 -0
  68. dense_evolution/mps.py +9 -0
  69. dense_evolution/native_hf/__init__.py +26 -0
  70. dense_evolution/native_hf/_libcint/LICENSE-libcint +10 -0
  71. dense_evolution/native_hf/_libcint/libdecint.dll +0 -0
  72. dense_evolution/native_hf/assembly.py +304 -0
  73. dense_evolution/native_hf/basis.py +117 -0
  74. dense_evolution/native_hf/boys.py +35 -0
  75. dense_evolution/native_hf/bridge.py +112 -0
  76. dense_evolution/native_hf/cartesian.py +64 -0
  77. dense_evolution/native_hf/coulomb.py +196 -0
  78. dense_evolution/native_hf/differentiable.py +53 -0
  79. dense_evolution/native_hf/gaussians.py +79 -0
  80. dense_evolution/native_hf/kinetic.py +52 -0
  81. dense_evolution/native_hf/libcint_bridge.py +167 -0
  82. dense_evolution/native_hf/overlap.py +91 -0
  83. dense_evolution/native_hf/scf.py +404 -0
  84. dense_evolution/noise/__init__.py +79 -0
  85. dense_evolution/noise/coherent_attack.py +264 -0
  86. dense_evolution/noise/cosmic_ray.py +61 -0
  87. dense_evolution/noise/density_matrix_channels.py +78 -0
  88. dense_evolution/noise/differentiable.py +66 -0
  89. dense_evolution/noise/kraus/__init__.py +6 -0
  90. dense_evolution/noise/kraus/amplitude_damping.py +47 -0
  91. dense_evolution/noise/kraus/bitflip.py +22 -0
  92. dense_evolution/noise/kraus/combined.py +16 -0
  93. dense_evolution/noise/kraus/depolarizing.py +47 -0
  94. dense_evolution/noise/kraus/ideal.py +10 -0
  95. dense_evolution/noise/kraus/phaseflip.py +21 -0
  96. dense_evolution/noise/kraus_channels.py +285 -0
  97. dense_evolution/noise/oscillating.py +32 -0
  98. dense_evolution/noise/pink.py +80 -0
  99. dense_evolution/observables.py +11 -0
  100. dense_evolution/parser.py +9 -0
  101. dense_evolution/physics/__init__.py +27 -0
  102. dense_evolution/physics/entropy.py +161 -0
  103. dense_evolution/physics/fermions.py +322 -0
  104. dense_evolution/physics/observables.py +523 -0
  105. dense_evolution/physics/qec.py +1113 -0
  106. dense_evolution/physics/spectral.py +143 -0
  107. dense_evolution/physics/states.py +43 -0
  108. dense_evolution/protocols/__init__.py +27 -0
  109. dense_evolution/protocols/bb84.py +133 -0
  110. dense_evolution/protocols/di_qkd_ghz.py +199 -0
  111. dense_evolution/protocols/dicka_protocol2.py +124 -0
  112. dense_evolution/qec.py +20 -0
  113. dense_evolution/qft.py +9 -0
  114. dense_evolution/qmmm/__init__.py +13 -0
  115. dense_evolution/qmmm/ase_bridge.py +97 -0
  116. dense_evolution/qmmm/forces.py +388 -0
  117. dense_evolution/qmmm/propagation.py +80 -0
  118. dense_evolution/qmmm/region.py +137 -0
  119. dense_evolution/random_circuit.py +15 -0
  120. dense_evolution/registry.py +9 -0
  121. dense_evolution/simulator.py +10 -0
  122. dense_evolution/solvers/__init__.py +19 -0
  123. dense_evolution/solvers/autodiff.py +169 -0
  124. dense_evolution/solvers/harrison_tb.py +189 -0
  125. dense_evolution/solvers/vhd_tb.py +187 -0
  126. dense_evolution/states.py +9 -0
  127. dense_evolution/topology.py +9 -0
  128. dense_evolution/trotter.py +9 -0
  129. dense_evolution/utils/__init__.py +13 -0
  130. dense_evolution/utils/drawing.py +101 -0
  131. dense_evolution/utils/mass_decomposition.py +246 -0
  132. dense_evolution/utils/measurement.py +94 -0
  133. dense_evolution/vhd_tb.py +16 -0
  134. dense_evolution-8.3.0.dist-info/METADATA +366 -0
  135. dense_evolution-8.3.0.dist-info/RECORD +165 -0
  136. dense_evolution-8.3.0.dist-info/WHEEL +5 -0
  137. dense_evolution-8.3.0.dist-info/entry_points.txt +2 -0
  138. dense_evolution-8.3.0.dist-info/licenses/license.md +58 -0
  139. dense_evolution-8.3.0.dist-info/top_level.txt +5 -0
  140. ia_utils/__init__.py +0 -0
  141. ia_utils/adversarial_vector_attack.py +196 -0
  142. ia_utils/rag.py +288 -0
  143. ia_utils/vector_healing.py +399 -0
  144. local_site/__init__.py +0 -0
  145. local_site/app/__init__.py +0 -0
  146. local_site/app/server.py +1009 -0
  147. mcp_server/__init__.py +0 -0
  148. mcp_server/client.py +324 -0
  149. mcp_server/config.py +32 -0
  150. mcp_server/models.py +347 -0
  151. mcp_server/molecules.py +71 -0
  152. mcp_server/server.py +119 -0
  153. mcp_server/tools/__init__.py +0 -0
  154. mcp_server/tools/chemistry_tools.py +225 -0
  155. mcp_server/tools/circuit_tools.py +83 -0
  156. mcp_server/tools/crypto_tools.py +66 -0
  157. mcp_server/tools/mitigation_tools.py +81 -0
  158. mcp_server/tools/noise_tools.py +60 -0
  159. mcp_server/tools/retrieval_tools.py +44 -0
  160. mcp_server/tools/system_tools.py +149 -0
  161. mcp_server/tools/wormhole_tools.py +142 -0
  162. mcp_server/utils/__init__.py +0 -0
  163. mcp_server/utils/cache.py +55 -0
  164. mcp_server/utils/images.py +67 -0
  165. mcp_server/utils/truncation.py +38 -0
@@ -0,0 +1,406 @@
1
+ """Qiskit / PennyLane interop bridges (`from_qiskit`, `from_pennylane`,
2
+ `run_qiskit_circuit`, `run_pennylane_circuit`), a real-calibration noise
3
+ bridge (`noise_model_from_qiskit_backend`), and a Clifford-only STIM
4
+ bridge (`to_stim`). All circuit bridges go through OpenQASM 2.0
5
+ (`qiskit.qasm2.dumps` / `qml.to_openqasm`) and this library's own
6
+ `QASMParser` -- not bespoke gate-by-gate translators, so gate coverage
7
+ matches whatever the parser/simulator already support.
8
+
9
+ **Bit-order.** Qiskit indexes probability/statevector arrays
10
+ little-endian (qubit 0 = least significant bit); Dense-Evolution indexes
11
+ MSB-first everywhere (`phys = n_qubits - 1 - qubit`, the same convention
12
+ `apply_gate_1q`/`apply_gate_2q`/`measure`/`run_circuit_jit` use).
13
+ `run_qiskit_circuit` reorders its output into Qiskit's own convention
14
+ (`_to_qiskit_bit_order`, a plain bit-reversal permutation, verified
15
+ against `Statevector.from_instruction(...).probabilities()` on an
16
+ asymmetric circuit) so it's directly comparable to
17
+ `Statevector(circuit).probabilities()`. PennyLane's own wire convention
18
+ already matches Dense-Evolution's MSB-first indexing natively --
19
+ `run_pennylane_circuit` does **not** reorder, on purpose; verified
20
+ directly that the two frameworks genuinely need different treatment
21
+ here, not just "symmetric for simplicity."
22
+
23
+ **Known limits**, inherited from the QASM2 bridge (not something this
24
+ layer works around):
25
+
26
+ - No classical control flow -- `if`/`while` and mid-circuit-measurement-
27
+ conditioned gates are parsed out, not executed (same limitation as
28
+ native QASM3 circuits).
29
+ - No expansion of composite/custom gates. A Qiskit call like `mcx` with
30
+ 3+ controls gets exported as a named `gate mcx { ... }` definition;
31
+ the definition parses cleanly but the gate itself isn't a primitive
32
+ this simulator knows how to execute, so a call to it is a silent
33
+ no-op -- same as referencing any unrecognized gate name elsewhere.
34
+ Stick to the gates this simulator actually implements for results you
35
+ can trust.
36
+ - Only a plugin/backend-free bridge for circuit *execution* -- no
37
+ `qiskit.providers.BackendV2` or PennyLane `Device` registration, so
38
+ callers still invoke `run_qiskit_circuit`/`run_pennylane_circuit`
39
+ explicitly rather than pointing existing framework code at a new
40
+ backend/device string. (`noise_model_from_qiskit_backend` reads a
41
+ `BackendV2`'s calibration data -- that's data extraction only, not
42
+ backend/provider registration.)
43
+ - **`run_qiskit_circuit`/`run_pennylane_circuit` are not differentiable.**
44
+ `from_pennylane`/`from_qiskit` materialize every gate parameter into a
45
+ plain Python `float` inside the QASM text before parsing -- the value
46
+ leaves the JAX trace entirely. `jax.grad` through `run_pennylane_circuit`
47
+ does **not** raise: it silently returns `0.0`, which looks like
48
+ "already converged" rather than "not wired up" (verified directly).
49
+ For a real gradient, use `circuit_to_energy_fn` instead -- pass it the
50
+ `QASMCircuit` that `from_qiskit`/`from_pennylane` returns, before that
51
+ float-baking happens, and `jax.grad` works correctly.
52
+ """
53
+ import sys
54
+ import warnings
55
+ from typing import Optional, Tuple
56
+ import numpy as np
57
+
58
+ from ..circuits.parser import QASMParser, QASMCircuit
59
+ from ..backends.statevector import DenseSVSimulator
60
+
61
+ try:
62
+ import qiskit.qasm2 as _qasm2
63
+ HAS_QISKIT = True
64
+ except ImportError:
65
+ HAS_QISKIT = False
66
+
67
+ try:
68
+ import pennylane as qml
69
+ HAS_PENNYLANE = True
70
+ except ImportError:
71
+ HAS_PENNYLANE = False
72
+
73
+ try:
74
+ import stim
75
+ HAS_STIM = True
76
+ except ImportError:
77
+ HAS_STIM = False
78
+
79
+ _macos_qiskit_warning_shown = False
80
+
81
+
82
+ def _require_qiskit():
83
+ if not HAS_QISKIT:
84
+ raise ImportError(
85
+ "Qiskit interop requires the 'qiskit' package. "
86
+ "Install it with: pip install dense-evolution[qiskit]")
87
+ global _macos_qiskit_warning_shown
88
+ if sys.platform == 'darwin' and not _macos_qiskit_warning_shown:
89
+ _macos_qiskit_warning_shown = True
90
+ warnings.warn(
91
+ "Qiskit's own QuantumCircuit.__init__ is known to segfault the "
92
+ "whole process on macOS/arm64 (reproduced on GitHub Actions "
93
+ "arm64 runners, Python 3.10-3.12) -- this is an upstream Qiskit "
94
+ "bug, not something dense-evolution can fix from its side. If "
95
+ "you hit a crash, consider the PennyLane bridge instead "
96
+ "(pip install dense-evolution[pennylane]), which does not "
97
+ "construct Qiskit objects and has no known issue of this kind.",
98
+ RuntimeWarning, stacklevel=2)
99
+
100
+
101
+ def _require_pennylane():
102
+ if not HAS_PENNYLANE:
103
+ raise ImportError(
104
+ "PennyLane interop requires the 'pennylane' package. "
105
+ "Install it with: pip install dense-evolution[pennylane]")
106
+
107
+
108
+ def _require_stim():
109
+ if not HAS_STIM:
110
+ raise ImportError(
111
+ "STIM interop requires the 'stim' package. "
112
+ "Install it with: pip install dense-evolution[stim]")
113
+
114
+
115
+ def _to_qiskit_bit_order(probs: np.ndarray, n_qubits: int) -> np.ndarray:
116
+ """
117
+ Reindex a probability array from Dense-Evolution's native MSB-first
118
+ convention (qubit 0 = most significant bit of the index, the same
119
+ convention as apply_gate_1q/apply_gate_2q/measure/beast-mode) to
120
+ Qiskit's little-endian convention (qubit 0 = least significant bit),
121
+ so the result is directly comparable to Statevector(...).probabilities().
122
+
123
+ A plain bit-reversal permutation of the index — verified against
124
+ Statevector.from_instruction(...).probabilities() on an asymmetric
125
+ circuit (exact match only after this reversal, not before).
126
+
127
+ Vectorized bit-reversal (prog.txt point 3): loops over n_qubits bit
128
+ positions, not over the 2**n_qubits indices themselves like the
129
+ previous `format(i, ...)[::-1]`-per-index Python loop did.
130
+ """
131
+ idx = np.arange(2 ** n_qubits)
132
+ perm = np.zeros_like(idx)
133
+ for b in range(n_qubits):
134
+ perm |= ((idx >> b) & 1) << (n_qubits - 1 - b)
135
+ return probs[perm]
136
+
137
+
138
+ def from_qiskit(circuit) -> QASMCircuit:
139
+ """Convert a Qiskit QuantumCircuit into a QASMCircuit via OpenQASM 2.0
140
+ (qiskit.qasm2.dumps), reusing the existing QASMParser rather than a
141
+ bespoke gate-by-gate translator."""
142
+ _require_qiskit()
143
+ qasm_str = _qasm2.dumps(circuit)
144
+ return QASMParser().parse(qasm_str)
145
+
146
+
147
+ def _sorted_wires(wires):
148
+ """Best-effort ascending order for a wire sequence. Falls back to the
149
+ original order if the labels aren't mutually comparable (e.g. mixed
150
+ str/int wire names) — better to fall back to the old touch-order
151
+ behavior than to crash on an exotic device's wire labels."""
152
+ try:
153
+ return sorted(wires)
154
+ except TypeError:
155
+ return list(wires)
156
+
157
+
158
+ def from_pennylane(circuit, *args, **kwargs) -> QASMCircuit:
159
+ """Convert a PennyLane QNode or QuantumTape/QuantumScript into a
160
+ QASMCircuit via OpenQASM 2.0, reusing the existing QASMParser.
161
+
162
+ PennyLane's own serialization API for a bare tape has changed across
163
+ versions in an incompatible way (verified directly against both):
164
+ - >=~0.43 (Python 3.11+ only): qml.to_openqasm(tape) returns the
165
+ QASM string directly; QuantumTape/QuantumScript no longer has a
166
+ to_openqasm() method at all.
167
+ - <=0.42.x (still installed on Python 3.10, where newer PennyLane
168
+ isn't available): qml.to_openqasm(tape) does NOT special-case a
169
+ bare tape — it returns a QNode-oriented wrapper that crashes with
170
+ AttributeError ('QuantumTape' object has no attribute 'func') if
171
+ called on one. The tape's own tape.to_openqasm() method is what
172
+ works there instead.
173
+ So: a bare tape/QuantumScript uses its own to_openqasm() method when
174
+ present (old API), otherwise falls through to the top-level
175
+ qml.to_openqasm() (new API). A QNode (not a QuantumScript instance)
176
+ always uses the top-level function, which returns a wrapper that must
177
+ be called with the QNode's own arguments — consistent across both
178
+ versions, this path was never the one that broke.
179
+
180
+ WIRE ORDER: by default, both PennyLane APIs number the exported QASM
181
+ qubits in the order wires are FIRST TOUCHED in the circuit, not by
182
+ their actual wire index — e.g. `qml.PauliX(wires=2)` followed by
183
+ `qml.CNOT(wires=[2, 1])` becomes `x q[0]; cx q[0],q[1];` in the
184
+ default export, silently renumbering wire 2 -> q[0] and wire 1 -> q[1].
185
+ Verified directly: this produced a topologically different circuit
186
+ from the one PennyLane itself executes whenever wires aren't touched
187
+ in ascending order (a QASMParser-based bridge has no way to recover
188
+ the true mapping after the fact — the touch-order renumbering has
189
+ already happened by the time QASM text exists). Both APIs accept an
190
+ explicit `wires=` argument that forces the true wire order into the
191
+ export instead — used here for both the QNode path (the device's own
192
+ declared wire order) and the tape path (the tape's own wires, sorted
193
+ ascending, since a bare tape has no device to ask).
194
+ """
195
+ _require_pennylane()
196
+ if isinstance(circuit, qml.tape.QuantumScript):
197
+ wires = _sorted_wires(circuit.wires)
198
+ if hasattr(circuit, 'to_openqasm'):
199
+ qasm_str = circuit.to_openqasm(wires=wires, measure_all=False)
200
+ else:
201
+ qasm_str = qml.to_openqasm(circuit, wires=wires, measure_all=False)
202
+ else:
203
+ device = getattr(circuit, 'device', None)
204
+ wires = device.wires if device is not None else None
205
+ result = qml.to_openqasm(circuit, wires=wires, measure_all=False)
206
+ qasm_str = result if isinstance(result, str) else result(*args, **kwargs)
207
+ return QASMParser().parse(qasm_str)
208
+
209
+
210
+ def run_qiskit_circuit(
211
+ circuit,
212
+ use_float32: bool = True,
213
+ sim: Optional[DenseSVSimulator] = None,
214
+ ) -> Tuple[DenseSVSimulator, np.ndarray]:
215
+ """Run a Qiskit QuantumCircuit on DenseSVSimulator. Returns
216
+ (sim, probabilities) with probabilities reordered into Qiskit's own
217
+ little-endian bit convention, so they compare directly against
218
+ Statevector(circuit).probabilities() — see _to_qiskit_bit_order."""
219
+ circ = from_qiskit(circuit)
220
+ if sim is None:
221
+ sim = DenseSVSimulator(n_qubits=circ.n_qubits, use_float32=use_float32)
222
+ sim.run_circuit(circ.to_tuples())
223
+ probs = np.asarray(sim.get_probabilities())
224
+ return sim, _to_qiskit_bit_order(probs, circ.n_qubits)
225
+
226
+
227
+ _DEFAULT_CALIBRATION_SKIP_GATES = frozenset({'measure'})
228
+
229
+
230
+ def noise_model_from_qiskit_backend(
231
+ backend,
232
+ circuit=None,
233
+ skip_gates=_DEFAULT_CALIBRATION_SKIP_GATES,
234
+ ) -> list:
235
+ """Build a Dense-Evolution-native noise specification from a Qiskit
236
+ BackendV2's own calibration data (backend.target) -- works for both
237
+ real backends and fake/mock backends carrying a real historical
238
+ calibration snapshot (e.g.
239
+ qiskit_ibm_runtime.fake_provider.FakeSherbrooke), so a simulation can
240
+ use the device's actual measured per-qubit/per-gate error rates
241
+ instead of an idealized channel.
242
+
243
+ Returns a list of dicts, each directly usable as the model/p/qubits
244
+ arguments to NoiseModel.apply_to_sv:
245
+ [{'gate': 'sx', 'qubits': [3], 'model': 'depolarizing', 'p': 0.00029},
246
+ {'gate': 'ecr', 'qubits': [1, 0], 'model': 'depolarizing', 'p': 0.0075}, ...]
247
+ One entry per unique (gate, qubit-target) pair found in the
248
+ calibration data -- never duplicated by how many times a gate occurs
249
+ in any one circuit.
250
+
251
+ If `circuit` is given, restricts the result to the (gate, qargs)
252
+ targets that circuit actually uses. Still exactly one entry per
253
+ unique target regardless of how many times the circuit repeats it --
254
+ promoted from Dense-Evolution-Discovery's Steane-code hardware bridge
255
+ script (scripts/steane_code_block5_qiskit_bridge.py), where an
256
+ earlier version called qiskit_aer's NoiseModel.add_quantum_error once
257
+ per gate OCCURRENCE instead of once per unique target: AerSimulator
258
+ composes the same Kraus channel with itself on repeated registration
259
+ for the same target (documented semantics, not "apply once per
260
+ instance"), and on a circuit with many repeats on the same qubits
261
+ this blew up to multi-GB memory from the resulting Kraus-term
262
+ combinatorial explosion before OOM-killing the process. This function
263
+ can't reproduce that bug by construction: it walks target[gate_name]
264
+ (already deduplicated by qargs, one InstructionProperties per target)
265
+ and, when `circuit` is given, additionally dedupes via a `seen` set
266
+ keyed on (gate, qargs) before ever appending a spec entry -- so
267
+ repeated occurrences in `circuit` collapse to the same single entry.
268
+
269
+ `measure` is excluded by default (readout error is a classical
270
+ bit-flip-on-outcome effect measured after collapse, not a
271
+ pre-measurement unitary channel on the state) -- pass a smaller/
272
+ larger `skip_gates` to change that. Gates with no calibrated error
273
+ (virtual gates like `rz`, or entries with error=None such as `delay`
274
+ or control-flow ops like `for_loop`/`if_else`) are skipped too, since
275
+ there is no error rate to convert into a channel.
276
+
277
+ Every entry uses Dense-Evolution's 'depolarizing' model with the
278
+ calibration's average gate error as `p` -- the same standard
279
+ average-gate-infidelity-to-depolarizing-parameter approximation
280
+ qiskit_aer.noise.NoiseModel.from_backend itself falls back to when no
281
+ finer-grained error data is available, not a from-scratch physical
282
+ model of this library's own invention.
283
+
284
+ `circuit` matching is order-independent on the qubit tuple (e.g. an
285
+ `ecr(0, 1)` instruction matches a calibration target stored as
286
+ `(1, 0)`) -- NoiseModel.apply_to_sv itself only ever applies
287
+ independent single-qubit channels per entry in `qubits`, never a
288
+ genuine joint multi-qubit channel, so which qubit was "control" vs
289
+ "target" in the original gate has no effect on the result here."""
290
+ _require_qiskit()
291
+ target = backend.target
292
+
293
+ wanted = None
294
+ if circuit is not None:
295
+ wanted = {
296
+ (instr.operation.name, tuple(sorted(circuit.find_bit(q).index for q in instr.qubits)))
297
+ for instr in circuit.data
298
+ }
299
+
300
+ specs = []
301
+ seen = set()
302
+ for gate_name in target.operation_names:
303
+ if gate_name in skip_gates:
304
+ continue
305
+ gate_map = target[gate_name]
306
+ if not gate_map:
307
+ continue
308
+ for qargs, props in gate_map.items():
309
+ if qargs is None or props is None or props.error is None:
310
+ continue
311
+ if wanted is not None and (gate_name, tuple(sorted(qargs))) not in wanted:
312
+ continue
313
+ key = (gate_name, qargs)
314
+ if key in seen:
315
+ continue
316
+ seen.add(key)
317
+ specs.append({
318
+ 'gate': gate_name,
319
+ 'qubits': list(qargs),
320
+ 'model': 'depolarizing',
321
+ 'p': float(props.error),
322
+ })
323
+ return specs
324
+
325
+
326
+ def run_pennylane_circuit(
327
+ circuit,
328
+ *args,
329
+ use_float32: bool = True,
330
+ sim: Optional[DenseSVSimulator] = None,
331
+ **kwargs,
332
+ ) -> Tuple[DenseSVSimulator, np.ndarray]:
333
+ """Run a PennyLane QNode/tape on DenseSVSimulator. Returns
334
+ (sim, probabilities) in Dense-Evolution's native ordering, WITHOUT any
335
+ bit-reversal — unlike run_qiskit_circuit, because PennyLane's own wire
336
+ convention (wire 0 = most significant) already matches Dense-Evolution's
337
+ MSB-first convention. Do not "symmetrize" this with the Qiskit version;
338
+ that would silently misorder circuits that are asymmetric under qubit
339
+ reversal (verified directly: no permutation needed here, one is
340
+ required for Qiskit — the two frameworks are genuinely different).
341
+
342
+ NOT DIFFERENTIABLE: from_pennylane() bakes every gate parameter into a
343
+ plain Python float inside the QASM text, so it leaves the JAX trace.
344
+ jax.grad through this function does not raise — it silently returns
345
+ 0.0 (verified), which reads as "converged" rather than "not wired up".
346
+ For a real gradient through a Dense-Evolution circuit, use the
347
+ dashboard_core._vqe_energy_fn pattern instead (jax.value_and_grad over
348
+ a jax.lax.scan template with sentinel-injected parameters)."""
349
+ circ = from_pennylane(circuit, *args, **kwargs)
350
+ if sim is None:
351
+ sim = DenseSVSimulator(n_qubits=circ.n_qubits, use_float32=use_float32)
352
+ sim.run_circuit(circ.to_tuples())
353
+ probs = np.asarray(sim.get_probabilities())
354
+ return sim, probs
355
+
356
+
357
+ _STIM_GATE_MAP = {
358
+ 'h': 'H', 'x': 'X', 'y': 'Y', 'z': 'Z',
359
+ 's': 'S', 'sdg': 'S_DAG', 'sx': 'SQRT_X', 'id': 'I',
360
+ 'cx': 'CX', 'cy': 'CY', 'cz': 'CZ',
361
+ }
362
+
363
+
364
+ def to_stim(ops, n_qubits: int) -> 'stim.Circuit':
365
+ """Convert a Dense-Evolution op-list circuit (e.g. [['h', 0], ['cx', 0, 1]])
366
+ into a stim.Circuit, for cross-validation against STIM's own stabilizer
367
+ simulator/decoder tooling. Promoted from Dense-Evolution-Discovery's
368
+ Steane-code STIM bridge script (scripts/steane_code_block4_stim_translation.py),
369
+ generalized from that script's Steane-specific gate set to every
370
+ STIM-representable gate this library has.
371
+
372
+ STIM is a STABILIZER simulator: it can only represent Clifford
373
+ operations. Every gate below maps 1:1 onto a native STIM instruction
374
+ (h/x/y/z/s/sdg/sx/id/cx/cy/cz); anything else in the op list --
375
+ continuous-angle rotations (rx/ry/rz/p/phase/u1/cp/cphase/crz) or the
376
+ non-Clifford t/tdg gates -- raises ValueError rather than being dropped
377
+ or approximated, since a silently-wrong stabilizer circuit defeats the
378
+ purpose of using STIM as an independent cross-check in the first place.
379
+
380
+ A leading no-op 'I' on qubit n_qubits - 1 is emitted first so the
381
+ returned circuit always has exactly n_qubits qubits, even if the
382
+ highest-indexed qubit is never otherwise touched by `ops`.
383
+
384
+ Qubit indexing matches `ops` directly (STIM has no reordering of its
385
+ own, unlike Qiskit's little-endian convention handled by
386
+ run_qiskit_circuit) -- but STIM's state_vector()/TableauSimulator
387
+ output is still little-endian (qubit 0 = least significant bit),
388
+ the same convention _to_qiskit_bit_order reorders into, so comparing
389
+ against DenseSVSimulator's own MSB-first probabilities/statevector
390
+ still needs that same reordering."""
391
+ _require_stim()
392
+ circuit = stim.Circuit()
393
+ if n_qubits > 0:
394
+ circuit.append('I', [n_qubits - 1])
395
+ for op in ops:
396
+ gate_name = op[0]
397
+ stim_gate = _STIM_GATE_MAP.get(gate_name)
398
+ if stim_gate is None:
399
+ raise ValueError(
400
+ f"Gate '{gate_name}' is not representable in STIM -- STIM is "
401
+ "a stabilizer simulator and only supports Clifford "
402
+ "operations (h, x, y, z, s, sdg, sx, id, cx, cy, cz here), "
403
+ "not continuous-angle rotations (rx/ry/rz/p/phase/u1/cp/"
404
+ "cphase/crz) or non-Clifford gates (t/tdg).")
405
+ circuit.append(stim_gate, list(op[1:]))
406
+ return circuit
@@ -0,0 +1,10 @@
1
+ """Backward-compatibility shim -- the real implementation moved to
2
+ dense_evolution.utils.measurement as part of the Phase 2 subpackage split
3
+ (see prog.txt). Kept so `from dense_evolution.measurement import ...`
4
+ (used by external consumers, e.g. Dense-Evolution-Discovery) keeps working
5
+ unchanged. Import from dense_evolution.utils.measurement directly in new code.
6
+ """
7
+
8
+ from dense_evolution.utils.measurement import sample_counts, statevector_fidelity
9
+
10
+ __all__ = ["sample_counts", "statevector_fidelity"]
@@ -0,0 +1,54 @@
1
+ """Mitigation subpackage: Zero-Noise Extrapolation (zne) and the
2
+ predictive-healing primitives (healing) it composes, plus density-matrix
3
+ diagnostics validated in Dense-Evolution-Discovery: the sandwiched
4
+ quantum Renyi divergence (renyi), magic entropy (magic_entropy), a
5
+ classical-shadows-based estimator for magic entropy from measurement
6
+ snapshots (magic_entropy_shadows), a multi-qubit per-state magic monotone
7
+ (stabilizer_renyi_entropy -- NOT the same quantity as magic_entropy, see
8
+ that module's own docstring for the distinction), and the classical
9
+ Kullback-Leibler divergence over probability distributions (kl_divergence)."""
10
+ from .zne import (
11
+ richardson_extrapolate, richardson_amplification_factor,
12
+ zero_noise_extrapolation, polynomial_extrapolate,
13
+ bounded_exponential_extrapolate,
14
+ project_to_physical, uhlmann_fidelity, zne_density_matrix,
15
+ jsd_predictive_zne_density_matrix,
16
+ coherence_predictive_zne_density_matrix,
17
+ classically_augmented_zne_phaseflip,
18
+ richardson_extrapolate_jit, zero_noise_extrapolation_jit,
19
+ polynomial_extrapolate_jit, uhlmann_fidelity_jit, zne_density_matrix_jit,
20
+ )
21
+ from .healing import (
22
+ calculate_advanced_sigma, calculate_phi_ab, calculate_vettore_dinamico,
23
+ calculate_vettore_statico, calculate_delta_preemp, evaluate_phi_trigger,
24
+ calculate_jax_reflection, MemoryReflectionEngine, GLOBAL_CONSTANTS,
25
+ )
26
+ from .renyi import sandwiched_renyi_divergence, sandwiched_renyi_divergence_jit
27
+ from .magic_entropy import magic_entropy, magic_entropy_jit
28
+ from .stabilizer_renyi_entropy import stabilizer_renyi_entropy, stabilizer_renyi_entropy_jit
29
+ from .magic_entropy_shadows import (
30
+ sample_classical_shadow, magic_entropy_from_shadows,
31
+ approx_shadow_std, fit_shadow_sample_complexity,
32
+ )
33
+ from .kl_divergence import kl_divergence, kl_divergence_jit
34
+
35
+ __all__ = [
36
+ "richardson_extrapolate", "richardson_amplification_factor",
37
+ "zero_noise_extrapolation", "polynomial_extrapolate",
38
+ "bounded_exponential_extrapolate",
39
+ "project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
40
+ "jsd_predictive_zne_density_matrix",
41
+ "coherence_predictive_zne_density_matrix",
42
+ "classically_augmented_zne_phaseflip",
43
+ "richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
44
+ "polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit",
45
+ "calculate_advanced_sigma", "calculate_phi_ab", "calculate_vettore_dinamico",
46
+ "calculate_vettore_statico", "calculate_delta_preemp", "evaluate_phi_trigger",
47
+ "calculate_jax_reflection", "MemoryReflectionEngine", "GLOBAL_CONSTANTS",
48
+ "sandwiched_renyi_divergence", "sandwiched_renyi_divergence_jit",
49
+ "magic_entropy", "magic_entropy_jit",
50
+ "stabilizer_renyi_entropy", "stabilizer_renyi_entropy_jit",
51
+ "sample_classical_shadow", "magic_entropy_from_shadows",
52
+ "approx_shadow_std", "fit_shadow_sample_complexity",
53
+ "kl_divergence", "kl_divergence_jit",
54
+ ]