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,343 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Real Zero-Noise Extrapolation (ZNE) error mitigation, wired to the same
|
|
3
|
+
engine and real noise channels (dense_evolution.NoiseModel) the rest of
|
|
4
|
+
the Composer uses. Noise is scaled by running the real channel at
|
|
5
|
+
noise_p, 2*noise_p, 3*noise_p and Richardson-extrapolating the measured
|
|
6
|
+
Pauli expectation back to zero noise via dense_evolution's own
|
|
7
|
+
zero_noise_extrapolation -- not a fabricated mitigated curve.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from dataclasses import dataclass
|
|
11
|
+
from typing import Optional
|
|
12
|
+
|
|
13
|
+
import numpy as np
|
|
14
|
+
|
|
15
|
+
import dense_evolution as de
|
|
16
|
+
|
|
17
|
+
__all__ = [
|
|
18
|
+
'MitigationResult', 'run_zne_mitigation', 'DensityMatrixZNEResult', 'run_density_matrix_zne',
|
|
19
|
+
'CoherenceZNEResult', 'run_coherence_zne_mitigation',
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
_DEFAULT_NOISE_FACTORS = (1.0, 2.0, 3.0)
|
|
23
|
+
# polynomial_extrapolate's own docstring: with exactly degree+1 points the
|
|
24
|
+
# fit is the unique interpolating polynomial, IDENTICAL to
|
|
25
|
+
# richardson_extrapolate -- the extra 2 points here (4x, 5x) are what
|
|
26
|
+
# actually changes anything, trading a bit of interpolation bias for
|
|
27
|
+
# averaging down statistical noise across more measured scales (verified
|
|
28
|
+
# in that docstring against a real 5-seed sweep, not asserted here).
|
|
29
|
+
_POLYNOMIAL_NOISE_FACTORS = (1.0, 2.0, 3.0, 4.0, 5.0)
|
|
30
|
+
_POLYNOMIAL_DEGREE = 2
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@dataclass
|
|
34
|
+
class MitigationResult:
|
|
35
|
+
n_qubits: int
|
|
36
|
+
pauli_string: str
|
|
37
|
+
ideal_expectation: float
|
|
38
|
+
noise_factors: list
|
|
39
|
+
noisy_expectations: list
|
|
40
|
+
zne_extrapolated: float
|
|
41
|
+
extrapolation_method: str = "richardson"
|
|
42
|
+
noisy_sems: list = None
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def run_zne_mitigation(
|
|
46
|
+
qasm_text: str,
|
|
47
|
+
pauli_string: str,
|
|
48
|
+
noise_model: str,
|
|
49
|
+
noise_p: float,
|
|
50
|
+
seed: Optional[int] = None,
|
|
51
|
+
noise_factors=None,
|
|
52
|
+
n_trials: int = 200,
|
|
53
|
+
extrapolation_method: str = "richardson",
|
|
54
|
+
) -> MitigationResult:
|
|
55
|
+
"""Real ZNE: <P> is measured at the real ideal state and at the real
|
|
56
|
+
channel applied at noise_p * each factor, then extrapolated to zero
|
|
57
|
+
noise -- either Richardson (dense_evolution.zero_noise_extrapolation,
|
|
58
|
+
the exact interpolating polynomial through 3 points, the default) or
|
|
59
|
+
a degree-2 least-squares polynomial fit through 5 points
|
|
60
|
+
(dense_evolution.polynomial_extrapolate) -- caller's choice, not
|
|
61
|
+
silently picked: noise_factors defaults to the 3- or 5-point set that
|
|
62
|
+
matches whichever method was requested, unless the caller overrides
|
|
63
|
+
it explicitly.
|
|
64
|
+
|
|
65
|
+
NoiseModel.apply_to_sv is a *stochastic single-shot* Kraus draw (one
|
|
66
|
+
random outcome per call, not the channel's averaged/ensemble
|
|
67
|
+
behavior) -- feeding a single draw straight into ZNE gives a
|
|
68
|
+
discontinuous, meaningless curve (verified: bitflip/depolarizing
|
|
69
|
+
jumped 1.0 -> 0.0 -> 0.0 across noise scales instead of decaying
|
|
70
|
+
smoothly). <P> under a Kraus channel is Tr(rho P) = mean over the
|
|
71
|
+
channel's trajectories, so each scale here averages n_trials
|
|
72
|
+
independent stochastic draws -- the actual expectation value ZNE is
|
|
73
|
+
defined against, not one random sample of it.
|
|
74
|
+
|
|
75
|
+
pauli_string uses dense_evolution's own qubit-0-is-position-0
|
|
76
|
+
convention (same as pauli_expectation), independent of Qiskit's
|
|
77
|
+
little-endian display convention used elsewhere on this page --
|
|
78
|
+
this function never touches a Qiskit-ordered array.
|
|
79
|
+
|
|
80
|
+
Examples
|
|
81
|
+
--------
|
|
82
|
+
>>> from dashboard_core.mitigation import run_zne_mitigation
|
|
83
|
+
>>> qasm = '''
|
|
84
|
+
... OPENQASM 2.0;
|
|
85
|
+
... include "qelib1.inc";
|
|
86
|
+
... qreg q[1];
|
|
87
|
+
... creg c[1];
|
|
88
|
+
... x q[0];
|
|
89
|
+
... measure q -> c;
|
|
90
|
+
... '''
|
|
91
|
+
>>> result = run_zne_mitigation(qasm, pauli_string='Z', noise_model='bitflip',
|
|
92
|
+
... noise_p=0.05, seed=0, n_trials=200)
|
|
93
|
+
>>> result.ideal_expectation
|
|
94
|
+
-1.0
|
|
95
|
+
>>> abs(result.zne_extrapolated - result.ideal_expectation) < abs(result.noisy_expectations[0] - result.ideal_expectation)
|
|
96
|
+
True
|
|
97
|
+
"""
|
|
98
|
+
if extrapolation_method not in ("richardson", "polynomial"):
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"unknown extrapolation_method {extrapolation_method!r}, must be 'richardson' or 'polynomial'"
|
|
101
|
+
)
|
|
102
|
+
if noise_factors is None:
|
|
103
|
+
noise_factors = (
|
|
104
|
+
_POLYNOMIAL_NOISE_FACTORS if extrapolation_method == "polynomial" else _DEFAULT_NOISE_FACTORS
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Parsed with dense_evolution's own QASMParser, never Qiskit's --
|
|
108
|
+
# QuantumCircuit.from_qasm_str itself segfaults on macOS (see
|
|
109
|
+
# dashboard_core/engine.py's module docstring for the full story);
|
|
110
|
+
# this function never needs a Qiskit circuit object at all, only the
|
|
111
|
+
# ideal statevector, so there's no reason to build one here.
|
|
112
|
+
parsed = de.QASMParser().parse(qasm_text)
|
|
113
|
+
n_qubits = parsed.n_qubits
|
|
114
|
+
if len(pauli_string) != n_qubits:
|
|
115
|
+
raise ValueError(f"pauli_string length {len(pauli_string)} != n_qubits {n_qubits}")
|
|
116
|
+
if noise_model not in de.NoiseModel.MODELS:
|
|
117
|
+
raise ValueError(f"unknown noise model {noise_model!r}, must be one of {de.NoiseModel.MODELS}")
|
|
118
|
+
|
|
119
|
+
# Same real anti-OOM guard as dashboard_core.engine.run_circuit_from_qasm
|
|
120
|
+
# -- this kernel now runs on whatever machine a Composer visitor has,
|
|
121
|
+
# not just a dev laptop, and this function was the one real gap that
|
|
122
|
+
# never checked before allocating (engine.py's own functions always did).
|
|
123
|
+
required_mb = (2 ** n_qubits) * 16 / 1e6
|
|
124
|
+
de.chunk.SafeMemoryGuard().check_allocation(required_mb, context=f"{n_qubits}-qubit statevector")
|
|
125
|
+
|
|
126
|
+
sim = de.DenseSVSimulator(n_qubits, use_float32=False)
|
|
127
|
+
sim.run_circuit(parsed.to_tuples())
|
|
128
|
+
sv_ideal = np.asarray(sim.sv)
|
|
129
|
+
ideal_expectation = float(np.real(de.pauli_expectation(sv_ideal, pauli_string)))
|
|
130
|
+
|
|
131
|
+
rng = np.random.default_rng(seed)
|
|
132
|
+
noisy_expectations = []
|
|
133
|
+
noisy_sems = []
|
|
134
|
+
for factor in noise_factors:
|
|
135
|
+
scaled_p = min(noise_p * factor, 1.0)
|
|
136
|
+
trial_values = np.empty(n_trials)
|
|
137
|
+
for i in range(n_trials):
|
|
138
|
+
sv_noisy = de.NoiseModel.apply_to_sv(
|
|
139
|
+
sv_ideal.copy(), n_qubits, noise_model, scaled_p, rng=rng,
|
|
140
|
+
)
|
|
141
|
+
trial_values[i] = np.real(de.pauli_expectation(sv_noisy, pauli_string))
|
|
142
|
+
noisy_expectations.append(float(trial_values.mean()))
|
|
143
|
+
# Standard error of the mean (sample std / sqrt(n_trials)), not
|
|
144
|
+
# the per-trial spread itself -- this is the real Monte Carlo
|
|
145
|
+
# uncertainty on the plotted mean, from the n_trials draws
|
|
146
|
+
# already computed above, not a separate/approximate estimate.
|
|
147
|
+
noisy_sems.append(float(trial_values.std(ddof=1) / np.sqrt(n_trials)))
|
|
148
|
+
|
|
149
|
+
if extrapolation_method == "polynomial":
|
|
150
|
+
zne_value = float(de.polynomial_extrapolate(noisy_expectations, list(noise_factors), degree=_POLYNOMIAL_DEGREE))
|
|
151
|
+
else:
|
|
152
|
+
zne_value = float(de.zero_noise_extrapolation(noisy_expectations, list(noise_factors)))
|
|
153
|
+
|
|
154
|
+
return MitigationResult(
|
|
155
|
+
n_qubits=n_qubits,
|
|
156
|
+
pauli_string=pauli_string,
|
|
157
|
+
ideal_expectation=ideal_expectation,
|
|
158
|
+
noise_factors=list(noise_factors),
|
|
159
|
+
noisy_expectations=noisy_expectations,
|
|
160
|
+
zne_extrapolated=zne_value,
|
|
161
|
+
extrapolation_method=extrapolation_method,
|
|
162
|
+
noisy_sems=noisy_sems,
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass
|
|
167
|
+
class DensityMatrixZNEResult:
|
|
168
|
+
n_qubits: int
|
|
169
|
+
noise_factors: list
|
|
170
|
+
fidelity_raw: float # Uhlmann fidelity: ideal vs raw noisy (base scale) rho
|
|
171
|
+
fidelity_corrected: float # Uhlmann fidelity: ideal vs ZNE-corrected rho
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def run_density_matrix_zne(
|
|
175
|
+
qasm_text: str,
|
|
176
|
+
noise_model: str,
|
|
177
|
+
noise_p: float,
|
|
178
|
+
seed: Optional[int] = None,
|
|
179
|
+
noise_factors=_DEFAULT_NOISE_FACTORS,
|
|
180
|
+
n_trials: int = 200,
|
|
181
|
+
) -> DensityMatrixZNEResult:
|
|
182
|
+
"""Density-matrix ZNE (dense_evolution.zne_density_matrix): builds a
|
|
183
|
+
real Monte-Carlo density-matrix estimate at each noise scale (the
|
|
184
|
+
mean of n_trials |psi_k><psi_k| projectors from independent
|
|
185
|
+
NoiseModel draws -- a real ensemble reconstruction, not a single
|
|
186
|
+
trajectory), extrapolates to zero noise, and projects onto the
|
|
187
|
+
nearest physical (PSD, trace-1) density matrix internally.
|
|
188
|
+
|
|
189
|
+
Graded (never fed back into the extrapolation) against the true
|
|
190
|
+
ideal density matrix via dense_evolution.uhlmann_fidelity, so the
|
|
191
|
+
reported improvement is an honest measurement of whether the
|
|
192
|
+
correction actually helped -- matches the pattern in
|
|
193
|
+
zne_density_matrix's own docstring (experiments/matrix_healing_zne.py:
|
|
194
|
+
raw ~0.865, corrected ~0.947 on a 2-qubit Bell state).
|
|
195
|
+
|
|
196
|
+
noise_factors defaults to the same 3-point set as run_zne_mitigation's
|
|
197
|
+
own Richardson default (prog.txt, dashboard_core audit point 5c: this
|
|
198
|
+
used to have no stated reason here, unlike that function's explicit
|
|
199
|
+
one) -- zne_density_matrix always fits via polynomial_extrapolate
|
|
200
|
+
(degree=2), which is mathematically IDENTICAL to exact Richardson
|
|
201
|
+
interpolation at exactly 3 points (its own "original design point")
|
|
202
|
+
but, unlike Richardson, stays well-behaved with MORE than 3 -- so a
|
|
203
|
+
caller can safely pass extra noise_factors here for more averaging,
|
|
204
|
+
without the interpolation degradation that would apply to
|
|
205
|
+
run_zne_mitigation's Richardson path at the same move (see
|
|
206
|
+
zne_density_matrix's own docstring for the measured numbers).
|
|
207
|
+
|
|
208
|
+
Examples
|
|
209
|
+
--------
|
|
210
|
+
>>> from dashboard_core.mitigation import run_density_matrix_zne
|
|
211
|
+
>>> qasm = '''
|
|
212
|
+
... OPENQASM 2.0;
|
|
213
|
+
... include "qelib1.inc";
|
|
214
|
+
... qreg q[1];
|
|
215
|
+
... creg c[1];
|
|
216
|
+
... x q[0];
|
|
217
|
+
... measure q -> c;
|
|
218
|
+
... '''
|
|
219
|
+
>>> result = run_density_matrix_zne(qasm, noise_model='bitflip', noise_p=0.05, seed=0, n_trials=200)
|
|
220
|
+
>>> result.fidelity_corrected > result.fidelity_raw
|
|
221
|
+
True
|
|
222
|
+
"""
|
|
223
|
+
# Same QASMParser-only parsing as run_zne_mitigation above -- see its
|
|
224
|
+
# comment for why a Qiskit circuit object is never built here.
|
|
225
|
+
parsed = de.QASMParser().parse(qasm_text)
|
|
226
|
+
n_qubits = parsed.n_qubits
|
|
227
|
+
if noise_model not in de.NoiseModel.MODELS:
|
|
228
|
+
raise ValueError(f"unknown noise model {noise_model!r}, must be one of {de.NoiseModel.MODELS}")
|
|
229
|
+
|
|
230
|
+
# Density matrices are dim x dim (dim = 2**n_qubits), not just dim --
|
|
231
|
+
# this holds rho_ideal, one rho per noise factor (rhos_at_scales, kept
|
|
232
|
+
# alive simultaneously so zne_density_matrix can extrapolate across all
|
|
233
|
+
# of them at once) and rho_corrected: (len(noise_factors) + 2) separate
|
|
234
|
+
# dim*dim complex128 arrays, quadratically worse than a plain
|
|
235
|
+
# statevector at the same qubit count. Same real anti-OOM guard as
|
|
236
|
+
# dashboard_core.engine.run_circuit_from_qasm, sized for what this
|
|
237
|
+
# function actually allocates -- this was the one real gap that never
|
|
238
|
+
# checked before allocating.
|
|
239
|
+
dim = 2 ** n_qubits
|
|
240
|
+
required_mb = dim * dim * 16 / 1e6 * (len(noise_factors) + 2)
|
|
241
|
+
de.chunk.SafeMemoryGuard().check_allocation(required_mb, context=f"{n_qubits}-qubit density matrix ZNE")
|
|
242
|
+
|
|
243
|
+
sim = de.DenseSVSimulator(n_qubits, use_float32=False)
|
|
244
|
+
sim.run_circuit(parsed.to_tuples())
|
|
245
|
+
sv_ideal = np.asarray(sim.sv)
|
|
246
|
+
rho_ideal = np.outer(sv_ideal, sv_ideal.conj())
|
|
247
|
+
|
|
248
|
+
rng = np.random.default_rng(seed)
|
|
249
|
+
rhos_at_scales = []
|
|
250
|
+
for factor in noise_factors:
|
|
251
|
+
scaled_p = min(noise_p * factor, 1.0)
|
|
252
|
+
rho_acc = np.zeros((dim, dim), dtype=complex)
|
|
253
|
+
for _ in range(n_trials):
|
|
254
|
+
sv_noisy = de.NoiseModel.apply_to_sv(
|
|
255
|
+
sv_ideal.copy(), n_qubits, noise_model, scaled_p, rng=rng,
|
|
256
|
+
)
|
|
257
|
+
rho_acc += np.outer(sv_noisy, sv_noisy.conj())
|
|
258
|
+
rho_acc /= n_trials
|
|
259
|
+
rhos_at_scales.append(rho_acc)
|
|
260
|
+
|
|
261
|
+
rho_corrected = np.asarray(de.zne_density_matrix(rhos_at_scales, list(noise_factors)))
|
|
262
|
+
|
|
263
|
+
fidelity_raw = float(de.uhlmann_fidelity(rho_ideal, rhos_at_scales[0]))
|
|
264
|
+
fidelity_corrected = float(de.uhlmann_fidelity(rho_ideal, rho_corrected))
|
|
265
|
+
|
|
266
|
+
return DensityMatrixZNEResult(
|
|
267
|
+
n_qubits=n_qubits,
|
|
268
|
+
noise_factors=list(noise_factors),
|
|
269
|
+
fidelity_raw=fidelity_raw,
|
|
270
|
+
fidelity_corrected=fidelity_corrected,
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
@dataclass
|
|
275
|
+
class CoherenceZNEResult:
|
|
276
|
+
n_qubits: int
|
|
277
|
+
noise_factors: list
|
|
278
|
+
fidelity_raw: float
|
|
279
|
+
fidelity_corrected: float
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def run_coherence_zne_mitigation(
|
|
283
|
+
qasm_text: str,
|
|
284
|
+
noise_model: str,
|
|
285
|
+
noise_p: float,
|
|
286
|
+
seed: Optional[int] = None,
|
|
287
|
+
noise_factors=_DEFAULT_NOISE_FACTORS,
|
|
288
|
+
n_trials: int = 200,
|
|
289
|
+
) -> CoherenceZNEResult:
|
|
290
|
+
"""Coherence-L1-predictive density-matrix ZNE
|
|
291
|
+
(dense_evolution.coherence_predictive_zne_density_matrix): identical
|
|
292
|
+
Monte-Carlo density-matrix construction to run_density_matrix_zne
|
|
293
|
+
above, but extrapolated via the coherence-L1 signal (sum of
|
|
294
|
+
off-diagonal density-matrix magnitudes, Baumgratz/Cramer/Plenio 2014)
|
|
295
|
+
instead of the classical-JSD one -- validated (dense_evolution's own
|
|
296
|
+
docstring, 200-seed phaseflip sweep) to catch phase-type noise
|
|
297
|
+
(phaseflip, dephasing) the JSD-predictive signal is structurally blind
|
|
298
|
+
to, since JSD only ever reads the density matrix's diagonal.
|
|
299
|
+
|
|
300
|
+
Same real anti-OOM guard, same Uhlmann-fidelity grading against the
|
|
301
|
+
true ideal density matrix as run_density_matrix_zne -- the two
|
|
302
|
+
functions differ in exactly one line (which dense_evolution
|
|
303
|
+
extrapolation function is called), by design, so a caller comparing
|
|
304
|
+
them is comparing the mitigation signal, not two different pipelines.
|
|
305
|
+
"""
|
|
306
|
+
parsed = de.QASMParser().parse(qasm_text)
|
|
307
|
+
n_qubits = parsed.n_qubits
|
|
308
|
+
if noise_model not in de.NoiseModel.MODELS:
|
|
309
|
+
raise ValueError(f"unknown noise model {noise_model!r}, must be one of {de.NoiseModel.MODELS}")
|
|
310
|
+
|
|
311
|
+
dim = 2 ** n_qubits
|
|
312
|
+
required_mb = dim * dim * 16 / 1e6 * (len(noise_factors) + 2)
|
|
313
|
+
de.chunk.SafeMemoryGuard().check_allocation(required_mb, context=f"{n_qubits}-qubit coherence ZNE")
|
|
314
|
+
|
|
315
|
+
sim = de.DenseSVSimulator(n_qubits, use_float32=False)
|
|
316
|
+
sim.run_circuit(parsed.to_tuples())
|
|
317
|
+
sv_ideal = np.asarray(sim.sv)
|
|
318
|
+
rho_ideal = np.outer(sv_ideal, sv_ideal.conj())
|
|
319
|
+
|
|
320
|
+
rng = np.random.default_rng(seed)
|
|
321
|
+
rhos_at_scales = []
|
|
322
|
+
for factor in noise_factors:
|
|
323
|
+
scaled_p = min(noise_p * factor, 1.0)
|
|
324
|
+
rho_acc = np.zeros((dim, dim), dtype=complex)
|
|
325
|
+
for _ in range(n_trials):
|
|
326
|
+
sv_noisy = de.NoiseModel.apply_to_sv(
|
|
327
|
+
sv_ideal.copy(), n_qubits, noise_model, scaled_p, rng=rng,
|
|
328
|
+
)
|
|
329
|
+
rho_acc += np.outer(sv_noisy, sv_noisy.conj())
|
|
330
|
+
rho_acc /= n_trials
|
|
331
|
+
rhos_at_scales.append(rho_acc)
|
|
332
|
+
|
|
333
|
+
rho_corrected = np.asarray(de.coherence_predictive_zne_density_matrix(rhos_at_scales, list(noise_factors)))
|
|
334
|
+
|
|
335
|
+
fidelity_raw = float(de.uhlmann_fidelity(rho_ideal, rhos_at_scales[0]))
|
|
336
|
+
fidelity_corrected = float(de.uhlmann_fidelity(rho_ideal, rho_corrected))
|
|
337
|
+
|
|
338
|
+
return CoherenceZNEResult(
|
|
339
|
+
n_qubits=n_qubits,
|
|
340
|
+
noise_factors=list(noise_factors),
|
|
341
|
+
fidelity_raw=fidelity_raw,
|
|
342
|
+
fidelity_corrected=fidelity_corrected,
|
|
343
|
+
)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Real Hartree-Fock SCF convergence diagnostics
|
|
3
|
+
(dense_evolution.native_hf.scf.diagnose_convergence) for an arbitrary
|
|
4
|
+
molecule, exposed as a thin Composer-kernel wrapper. Needs the `armor`
|
|
5
|
+
extra (dense-armor) -- dense_evolution.native_hf.scf itself does not
|
|
6
|
+
require it, but diagnose_convergence's Hampel/Tukey filters do.
|
|
7
|
+
"""
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
|
|
10
|
+
from basis_set_exchange.lut import element_Z_from_sym
|
|
11
|
+
|
|
12
|
+
from dense_evolution.native_hf.bridge import build_qubit_hamiltonian
|
|
13
|
+
from dense_evolution.native_hf.scf import diagnose_convergence as _diagnose_convergence
|
|
14
|
+
|
|
15
|
+
__all__ = ['NativeHfDiagnosticsResult', 'run_native_hf_diagnostics']
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass
|
|
19
|
+
class NativeHfDiagnosticsResult:
|
|
20
|
+
n_qubits: int
|
|
21
|
+
converged: bool
|
|
22
|
+
n_iterations: int
|
|
23
|
+
electronic_energy_hartree: float
|
|
24
|
+
total_energy_hartree: float
|
|
25
|
+
n_anomalies_hampel: int
|
|
26
|
+
n_anomalies_tukey: int
|
|
27
|
+
anomaly_fraction_hampel: float
|
|
28
|
+
anomaly_fraction_tukey: float
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run_native_hf_diagnostics(symbols, geometry, charge: int = 0,
|
|
32
|
+
active_electrons=None, active_orbitals=None) -> NativeHfDiagnosticsResult:
|
|
33
|
+
"""Run real Hartree-Fock SCF (dense_evolution.native_hf, not
|
|
34
|
+
PennyLane's own STO-3G table -- this is the fallback path used for
|
|
35
|
+
elements outside it, e.g. Silicon) on an arbitrary molecule, and run
|
|
36
|
+
Dense-Armor's Hampel/Tukey anomaly filters over the real per-iteration
|
|
37
|
+
energy trace instead of trusting the final `converged` flag alone.
|
|
38
|
+
Jordan-Wigner mapping only -- native_hf/bridge.py calls
|
|
39
|
+
qml.jordan_wigner directly, not a general mapper.
|
|
40
|
+
|
|
41
|
+
Raises ImportError with an actionable message if the `armor` extra
|
|
42
|
+
(pip install dense-evolution[armor]) isn't installed -- diagnostics
|
|
43
|
+
genuinely can't run without it, this doesn't silently skip them.
|
|
44
|
+
"""
|
|
45
|
+
atomic_numbers = [element_Z_from_sym(s) for s in symbols]
|
|
46
|
+
n_electrons = sum(atomic_numbers) - charge
|
|
47
|
+
H, n_qubits, hf_result = build_qubit_hamiltonian(
|
|
48
|
+
atomic_numbers, geometry, n_electrons,
|
|
49
|
+
active_electrons=active_electrons, active_orbitals=active_orbitals,
|
|
50
|
+
)
|
|
51
|
+
diagnostics = _diagnose_convergence(hf_result)
|
|
52
|
+
return NativeHfDiagnosticsResult(
|
|
53
|
+
n_qubits=n_qubits,
|
|
54
|
+
converged=bool(hf_result.converged),
|
|
55
|
+
n_iterations=diagnostics["n_iterations"],
|
|
56
|
+
electronic_energy_hartree=float(hf_result.electronic_energy),
|
|
57
|
+
total_energy_hartree=float(hf_result.total_energy),
|
|
58
|
+
n_anomalies_hampel=diagnostics["n_anomalies_hampel"],
|
|
59
|
+
n_anomalies_tukey=diagnostics["n_anomalies_tukey"],
|
|
60
|
+
anomaly_fraction_hampel=diagnostics["anomaly_fraction_hampel"],
|
|
61
|
+
anomaly_fraction_tukey=diagnostics["anomaly_fraction_tukey"],
|
|
62
|
+
)
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Composer-facing wrappers around dense_evolution.noise's standalone noise
|
|
3
|
+
profiles and density-matrix channels -- the ones that take a formula and
|
|
4
|
+
some parameters, not a circuit, so they don't fit run_circuit_from_qasm's
|
|
5
|
+
shape.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import List, Optional
|
|
10
|
+
|
|
11
|
+
import numpy as np
|
|
12
|
+
|
|
13
|
+
import dense_evolution as de
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
'CosmicRayBurstResult', 'run_cosmic_ray_burst',
|
|
17
|
+
'OscillatingNoiseResult', 'run_oscillating_noise',
|
|
18
|
+
'DensityMatrixChannelResult', 'run_density_matrix_channel',
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class CosmicRayBurstResult:
|
|
24
|
+
times_us: List[float]
|
|
25
|
+
baseline_gamma: float
|
|
26
|
+
decay_probabilities: List[float]
|
|
27
|
+
peak_ratio: float
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def run_cosmic_ray_burst(baseline_gamma: float, times_us: Optional[List[float]] = None) -> CosmicRayBurstResult:
|
|
31
|
+
"""Real cosmic-ray/gamma-ray-induced quasiparticle burst profile
|
|
32
|
+
(dense_evolution.cosmic_ray_burst_profile), reproducing a real
|
|
33
|
+
measured event from arXiv:2104.05219 on a 26-qubit chip. Default
|
|
34
|
+
`times_us` samples the impact instant, the ~10us and ~1ms rise
|
|
35
|
+
checkpoints the paper describes, and a point well into the ~25ms
|
|
36
|
+
recovery."""
|
|
37
|
+
if times_us is None:
|
|
38
|
+
times_us = [0.0, 10.0, 1000.0, 50000.0]
|
|
39
|
+
times_arr = np.asarray(times_us, dtype=float)
|
|
40
|
+
profile = np.asarray(de.cosmic_ray_burst_profile(times_arr, baseline_gamma=baseline_gamma))
|
|
41
|
+
return CosmicRayBurstResult(
|
|
42
|
+
times_us=[float(t) for t in times_arr],
|
|
43
|
+
baseline_gamma=baseline_gamma,
|
|
44
|
+
decay_probabilities=[float(p) for p in profile],
|
|
45
|
+
peak_ratio=float(np.max(profile) / baseline_gamma),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass
|
|
50
|
+
class OscillatingNoiseResult:
|
|
51
|
+
base_p: float
|
|
52
|
+
freq: float
|
|
53
|
+
amp: float
|
|
54
|
+
factors: List[float]
|
|
55
|
+
p_eff: List[float]
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def run_oscillating_noise(base_p: float, freq: float, amp: float,
|
|
59
|
+
factors: Optional[List[float]] = None) -> OscillatingNoiseResult:
|
|
60
|
+
"""Noise strength that oscillates instead of scaling smoothly with
|
|
61
|
+
`factor` (dense_evolution.oscillating_p_eff) -- for checking whether a
|
|
62
|
+
mitigation technique that assumes smooth noise-vs-scale still works
|
|
63
|
+
when that assumption breaks. Default `factors` sample one full period
|
|
64
|
+
at `freq`."""
|
|
65
|
+
if factors is None:
|
|
66
|
+
factors = [0.0, 1.0, 2.0, 3.0]
|
|
67
|
+
p_eff = [float(de.oscillating_p_eff(base_p, f, freq, amp)) for f in factors]
|
|
68
|
+
return OscillatingNoiseResult(base_p=base_p, freq=freq, amp=amp, factors=list(factors), p_eff=p_eff)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@dataclass
|
|
72
|
+
class DensityMatrixChannelResult:
|
|
73
|
+
n_qubits: int
|
|
74
|
+
channel: str
|
|
75
|
+
param: float
|
|
76
|
+
ideal_diagonal: List[float]
|
|
77
|
+
noisy_diagonal: List[float]
|
|
78
|
+
trace: float
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def run_density_matrix_channel(qasm_text: str, channel: str, param: float) -> DensityMatrixChannelResult:
|
|
82
|
+
"""Apply a density-matrix-level noise channel (as opposed to
|
|
83
|
+
NoiseModel's per-qubit statevector channels) to the ideal density
|
|
84
|
+
matrix of a QASM circuit.
|
|
85
|
+
|
|
86
|
+
channel: 'global_depolarizing' (dense_evolution.global_depolarizing_channel,
|
|
87
|
+
mixes the WHOLE register toward the fully mixed state as one unit --
|
|
88
|
+
a state-prep/measurement error reported as a single joint parameter)
|
|
89
|
+
or 'amplitude_damping' (dense_evolution.amplitude_damping_channel,
|
|
90
|
+
single-qubit only, asymmetric |1>->|0> energy relaxation)."""
|
|
91
|
+
if channel not in ('global_depolarizing', 'amplitude_damping'):
|
|
92
|
+
raise ValueError(f"channel must be 'global_depolarizing' or 'amplitude_damping', got {channel!r}")
|
|
93
|
+
|
|
94
|
+
parsed = de.QASMParser().parse(qasm_text)
|
|
95
|
+
n_qubits = parsed.n_qubits
|
|
96
|
+
# BUG FIX (prog.txt, dashboard_core audit point 3d): this function
|
|
97
|
+
# builds two full dim x dim density matrices (rho, rho_noisy) but,
|
|
98
|
+
# unlike engine.py's and mitigation.py's own equivalents, never
|
|
99
|
+
# checked SafeMemoryGuard before allocating -- the same class of OOM
|
|
100
|
+
# the rest of the dashboard already prevents. Same dim*dim*16-per-
|
|
101
|
+
# matrix estimate mitigation.py's own density-matrix guard uses.
|
|
102
|
+
dim = 2 ** n_qubits
|
|
103
|
+
required_mb = dim * dim * 16 / 1e6 * 2
|
|
104
|
+
de.chunk.SafeMemoryGuard().check_allocation(required_mb, context=f"{n_qubits}-qubit density matrix channel")
|
|
105
|
+
|
|
106
|
+
sim = de.DenseSVSimulator(n_qubits)
|
|
107
|
+
sim.run_circuit(parsed.to_tuples())
|
|
108
|
+
sv = np.asarray(sim.get_statevector())
|
|
109
|
+
rho = np.outer(sv, sv.conj())
|
|
110
|
+
|
|
111
|
+
if channel == 'global_depolarizing':
|
|
112
|
+
rho_noisy = de.global_depolarizing_channel(rho, param)
|
|
113
|
+
else:
|
|
114
|
+
if n_qubits != 1:
|
|
115
|
+
raise ValueError(f"amplitude_damping is single-qubit only, got a {n_qubits}-qubit circuit")
|
|
116
|
+
rho_noisy = de.amplitude_damping_channel(rho, param)
|
|
117
|
+
|
|
118
|
+
return DensityMatrixChannelResult(
|
|
119
|
+
n_qubits=n_qubits,
|
|
120
|
+
channel=channel,
|
|
121
|
+
param=param,
|
|
122
|
+
ideal_diagonal=[float(x) for x in np.diag(rho).real],
|
|
123
|
+
noisy_diagonal=[float(x) for x in np.diag(rho_noisy).real],
|
|
124
|
+
trace=float(np.trace(rho_noisy).real),
|
|
125
|
+
)
|