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.
- dashboard_core/__init__.py +115 -0
- dashboard_core/_gate_tables.py +30 -0
- dashboard_core/band_structure.py +71 -0
- dashboard_core/circuit_builder_component.py +232 -0
- dashboard_core/circuit_diagram.py +216 -0
- dashboard_core/crypto_protocols.py +77 -0
- dashboard_core/engine.py +326 -0
- dashboard_core/graphical_builder.py +114 -0
- dashboard_core/hamiltonians.py +593 -0
- dashboard_core/mass_decomposition_tool.py +47 -0
- dashboard_core/mitigation.py +343 -0
- dashboard_core/native_hf_diagnostics.py +62 -0
- dashboard_core/noise_tools.py +125 -0
- dashboard_core/qasm_library.py +233 -0
- dashboard_core/qmmm.py +16 -0
- dashboard_core/rag_tool.py +45 -0
- dashboard_core/state_visuals.py +288 -0
- dashboard_core/system_limits.py +60 -0
- dashboard_core/vector_healing.py +102 -0
- dashboard_core/visuals.py +158 -0
- dashboard_core/vqe.py +533 -0
- dashboard_core/wormhole.py +580 -0
- dense_evolution/__init__.py +114 -0
- dense_evolution/autodiff.py +10 -0
- dense_evolution/backends/__init__.py +5 -0
- dense_evolution/backends/chunk/__init__.py +37 -0
- dense_evolution/backends/chunk/_engine_imports.py +57 -0
- dense_evolution/backends/chunk/circuit_chunker.py +55 -0
- dense_evolution/backends/chunk/core.py +432 -0
- dense_evolution/backends/chunk/disk_overflow.py +232 -0
- dense_evolution/backends/chunk/geometry.py +95 -0
- dense_evolution/backends/chunk/guard.py +190 -0
- dense_evolution/backends/chunk/kernels.py +531 -0
- dense_evolution/backends/mps.py +1569 -0
- dense_evolution/backends/statevector.py +616 -0
- dense_evolution/chunk.py +25 -0
- dense_evolution/circuits/__init__.py +20 -0
- dense_evolution/circuits/compiler.py +488 -0
- dense_evolution/circuits/diagram.py +94 -0
- dense_evolution/circuits/gates.py +91 -0
- dense_evolution/circuits/parser.py +632 -0
- dense_evolution/circuits/qft.py +66 -0
- dense_evolution/circuits/random_circuit.py +85 -0
- dense_evolution/circuits/registry.py +74 -0
- dense_evolution/circuits/topology.py +79 -0
- dense_evolution/circuits/trotter.py +265 -0
- dense_evolution/circuits/uccsd.py +275 -0
- dense_evolution/cli.py +199 -0
- dense_evolution/compiler.py +9 -0
- dense_evolution/config.py +49 -0
- dense_evolution/drawing.py +10 -0
- dense_evolution/entropy.py +9 -0
- dense_evolution/fermions.py +9 -0
- dense_evolution/gates.py +9 -0
- dense_evolution/harrison_tb.py +16 -0
- dense_evolution/healing.py +18 -0
- dense_evolution/interop/__init__.py +18 -0
- dense_evolution/interop/qiskit_pennylane.py +406 -0
- dense_evolution/measurement.py +10 -0
- dense_evolution/mitigation/__init__.py +54 -0
- dense_evolution/mitigation/healing.py +215 -0
- dense_evolution/mitigation/kl_divergence.py +93 -0
- dense_evolution/mitigation/magic_entropy.py +163 -0
- dense_evolution/mitigation/magic_entropy_shadows.py +262 -0
- dense_evolution/mitigation/renyi.py +168 -0
- dense_evolution/mitigation/stabilizer_renyi_entropy.py +103 -0
- dense_evolution/mitigation/zne.py +990 -0
- dense_evolution/mps.py +9 -0
- dense_evolution/native_hf/__init__.py +26 -0
- dense_evolution/native_hf/_libcint/LICENSE-libcint +10 -0
- dense_evolution/native_hf/_libcint/libdecint.dll +0 -0
- dense_evolution/native_hf/assembly.py +304 -0
- dense_evolution/native_hf/basis.py +117 -0
- dense_evolution/native_hf/boys.py +35 -0
- dense_evolution/native_hf/bridge.py +112 -0
- dense_evolution/native_hf/cartesian.py +64 -0
- dense_evolution/native_hf/coulomb.py +196 -0
- dense_evolution/native_hf/differentiable.py +53 -0
- dense_evolution/native_hf/gaussians.py +79 -0
- dense_evolution/native_hf/kinetic.py +52 -0
- dense_evolution/native_hf/libcint_bridge.py +167 -0
- dense_evolution/native_hf/overlap.py +91 -0
- dense_evolution/native_hf/scf.py +404 -0
- dense_evolution/noise/__init__.py +79 -0
- dense_evolution/noise/coherent_attack.py +264 -0
- dense_evolution/noise/cosmic_ray.py +61 -0
- dense_evolution/noise/density_matrix_channels.py +78 -0
- dense_evolution/noise/differentiable.py +66 -0
- dense_evolution/noise/kraus/__init__.py +6 -0
- dense_evolution/noise/kraus/amplitude_damping.py +47 -0
- dense_evolution/noise/kraus/bitflip.py +22 -0
- dense_evolution/noise/kraus/combined.py +16 -0
- dense_evolution/noise/kraus/depolarizing.py +47 -0
- dense_evolution/noise/kraus/ideal.py +10 -0
- dense_evolution/noise/kraus/phaseflip.py +21 -0
- dense_evolution/noise/kraus_channels.py +285 -0
- dense_evolution/noise/oscillating.py +32 -0
- dense_evolution/noise/pink.py +80 -0
- dense_evolution/observables.py +11 -0
- dense_evolution/parser.py +9 -0
- dense_evolution/physics/__init__.py +27 -0
- dense_evolution/physics/entropy.py +161 -0
- dense_evolution/physics/fermions.py +322 -0
- dense_evolution/physics/observables.py +523 -0
- dense_evolution/physics/qec.py +1113 -0
- dense_evolution/physics/spectral.py +143 -0
- dense_evolution/physics/states.py +43 -0
- dense_evolution/protocols/__init__.py +27 -0
- dense_evolution/protocols/bb84.py +133 -0
- dense_evolution/protocols/di_qkd_ghz.py +199 -0
- dense_evolution/protocols/dicka_protocol2.py +124 -0
- dense_evolution/qec.py +20 -0
- dense_evolution/qft.py +9 -0
- dense_evolution/qmmm/__init__.py +13 -0
- dense_evolution/qmmm/ase_bridge.py +97 -0
- dense_evolution/qmmm/forces.py +388 -0
- dense_evolution/qmmm/propagation.py +80 -0
- dense_evolution/qmmm/region.py +137 -0
- dense_evolution/random_circuit.py +15 -0
- dense_evolution/registry.py +9 -0
- dense_evolution/simulator.py +10 -0
- dense_evolution/solvers/__init__.py +19 -0
- dense_evolution/solvers/autodiff.py +169 -0
- dense_evolution/solvers/harrison_tb.py +189 -0
- dense_evolution/solvers/vhd_tb.py +187 -0
- dense_evolution/states.py +9 -0
- dense_evolution/topology.py +9 -0
- dense_evolution/trotter.py +9 -0
- dense_evolution/utils/__init__.py +13 -0
- dense_evolution/utils/drawing.py +101 -0
- dense_evolution/utils/mass_decomposition.py +246 -0
- dense_evolution/utils/measurement.py +94 -0
- dense_evolution/vhd_tb.py +16 -0
- dense_evolution-8.3.0.dist-info/METADATA +366 -0
- dense_evolution-8.3.0.dist-info/RECORD +165 -0
- dense_evolution-8.3.0.dist-info/WHEEL +5 -0
- dense_evolution-8.3.0.dist-info/entry_points.txt +2 -0
- dense_evolution-8.3.0.dist-info/licenses/license.md +58 -0
- dense_evolution-8.3.0.dist-info/top_level.txt +5 -0
- ia_utils/__init__.py +0 -0
- ia_utils/adversarial_vector_attack.py +196 -0
- ia_utils/rag.py +288 -0
- ia_utils/vector_healing.py +399 -0
- local_site/__init__.py +0 -0
- local_site/app/__init__.py +0 -0
- local_site/app/server.py +1009 -0
- mcp_server/__init__.py +0 -0
- mcp_server/client.py +324 -0
- mcp_server/config.py +32 -0
- mcp_server/models.py +347 -0
- mcp_server/molecules.py +71 -0
- mcp_server/server.py +119 -0
- mcp_server/tools/__init__.py +0 -0
- mcp_server/tools/chemistry_tools.py +225 -0
- mcp_server/tools/circuit_tools.py +83 -0
- mcp_server/tools/crypto_tools.py +66 -0
- mcp_server/tools/mitigation_tools.py +81 -0
- mcp_server/tools/noise_tools.py +60 -0
- mcp_server/tools/retrieval_tools.py +44 -0
- mcp_server/tools/system_tools.py +149 -0
- mcp_server/tools/wormhole_tools.py +142 -0
- mcp_server/utils/__init__.py +0 -0
- mcp_server/utils/cache.py +55 -0
- mcp_server/utils/images.py +67 -0
- 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
|
+
]
|