Dashboard Core — Mitigation (Composer's ZNE panel)¶
Real Zero-Noise Extrapolation (ZNE) error mitigation, wired to the same
engine and real noise channels (dense_evolution.registry.NoiseModel)
the rest of Composer uses — a thin dashboard-facing layer over
dense_evolution.mitigation, not a separate
reimplementation.
from dashboard_core.mitigation import run_zne_mitigation, run_density_matrix_zne
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[1];
creg c[1];
x q[0];
measure q -> c;
"""
result = run_zne_mitigation(qasm, pauli_string="Z", noise_model="bitflip", noise_p=0.05, seed=0)
print(result.ideal_expectation) # -1.0
print(result.noisy_expectations) # [-0.880, -0.790, -0.810] -- decays with noise scale
print(result.zne_extrapolated) # -1.080 -- closer to -1.0 than any single noisy measurement
dm_result = run_density_matrix_zne(qasm, noise_model="bitflip", noise_p=0.05, seed=0)
print(dm_result.fidelity_raw, dm_result.fidelity_corrected) # 0.940 -> 1.000
mitigation ¶
Real Zero-Noise Extrapolation (ZNE) error mitigation, wired to the same engine and real noise channels (dense_evolution.NoiseModel) the rest of the Composer uses. Noise is scaled by running the real channel at noise_p, 2noise_p, 3noise_p and Richardson-extrapolating the measured Pauli expectation back to zero noise via dense_evolution's own zero_noise_extrapolation -- not a fabricated mitigated curve.
run_zne_mitigation ¶
run_zne_mitigation(
qasm_text: str,
pauli_string: str,
noise_model: str,
noise_p: float,
seed: Optional[int] = None,
noise_factors=None,
n_trials: int = 200,
extrapolation_method: str = "richardson",
) -> MitigationResult
Real ZNE:
is measured at the real ideal state and at the real channel applied at noise_p * each factor, then extrapolated to zero noise -- either Richardson (dense_evolution.zero_noise_extrapolation, the exact interpolating polynomial through 3 points, the default) or a degree-2 least-squares polynomial fit through 5 points (dense_evolution.polynomial_extrapolate) -- caller's choice, not silently picked: noise_factors defaults to the 3- or 5-point set that matches whichever method was requested, unless the caller overrides it explicitly.
NoiseModel.apply_to_sv is a stochastic single-shot Kraus draw (one random outcome per call, not the channel's averaged/ensemble behavior) -- feeding a single draw straight into ZNE gives a discontinuous, meaningless curve (verified: bitflip/depolarizing jumped 1.0 -> 0.0 -> 0.0 across noise scales instead of decaying smoothly).
under a Kraus channel is Tr(rho P) = mean over the channel's trajectories, so each scale here averages n_trials independent stochastic draws -- the actual expectation value ZNE is defined against, not one random sample of it.
pauli_string uses dense_evolution's own qubit-0-is-position-0 convention (same as pauli_expectation), independent of Qiskit's little-endian display convention used elsewhere on this page -- this function never touches a Qiskit-ordered array.
Examples:
>>> from dashboard_core.mitigation import run_zne_mitigation
>>> qasm = '''
... OPENQASM 2.0;
... include "qelib1.inc";
... qreg q[1];
... creg c[1];
... x q[0];
... measure q -> c;
... '''
>>> result = run_zne_mitigation(qasm, pauli_string='Z', noise_model='bitflip',
... noise_p=0.05, seed=0, n_trials=200)
>>> result.ideal_expectation
-1.0
>>> abs(result.zne_extrapolated - result.ideal_expectation) < abs(result.noisy_expectations[0] - result.ideal_expectation)
True
Source code in tools/dashboard/core/mitigation.py
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
run_density_matrix_zne ¶
run_density_matrix_zne(
qasm_text: str,
noise_model: str,
noise_p: float,
seed: Optional[int] = None,
noise_factors=_DEFAULT_NOISE_FACTORS,
n_trials: int = 200,
) -> DensityMatrixZNEResult
Density-matrix ZNE (dense_evolution.zne_density_matrix): builds a real Monte-Carlo density-matrix estimate at each noise scale (the mean of n_trials |psi_k><psi_k| projectors from independent NoiseModel draws -- a real ensemble reconstruction, not a single trajectory), extrapolates to zero noise, and projects onto the nearest physical (PSD, trace-1) density matrix internally.
Graded (never fed back into the extrapolation) against the true ideal density matrix via dense_evolution.uhlmann_fidelity, so the reported improvement is an honest measurement of whether the correction actually helped -- matches the pattern in zne_density_matrix's own docstring (experiments/matrix_healing_zne.py: raw ~0.865, corrected ~0.947 on a 2-qubit Bell state).
noise_factors defaults to the same 3-point set as run_zne_mitigation's own Richardson default (prog.txt, dashboard_core audit point 5c: this used to have no stated reason here, unlike that function's explicit one) -- zne_density_matrix always fits via polynomial_extrapolate (degree=2), which is mathematically IDENTICAL to exact Richardson interpolation at exactly 3 points (its own "original design point") but, unlike Richardson, stays well-behaved with MORE than 3 -- so a caller can safely pass extra noise_factors here for more averaging, without the interpolation degradation that would apply to run_zne_mitigation's Richardson path at the same move (see zne_density_matrix's own docstring for the measured numbers).
Examples:
>>> from dashboard_core.mitigation import run_density_matrix_zne
>>> qasm = '''
... OPENQASM 2.0;
... include "qelib1.inc";
... qreg q[1];
... creg c[1];
... x q[0];
... measure q -> c;
... '''
>>> result = run_density_matrix_zne(qasm, noise_model='bitflip', noise_p=0.05, seed=0, n_trials=200)
>>> result.fidelity_corrected > result.fidelity_raw
True
Source code in tools/dashboard/core/mitigation.py
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | |
run_coherence_zne_mitigation ¶
run_coherence_zne_mitigation(
qasm_text: str,
noise_model: str,
noise_p: float,
seed: Optional[int] = None,
noise_factors=_DEFAULT_NOISE_FACTORS,
n_trials: int = 200,
) -> CoherenceZNEResult
Coherence-L1-predictive density-matrix ZNE (dense_evolution.coherence_predictive_zne_density_matrix): identical Monte-Carlo density-matrix construction to run_density_matrix_zne above, but extrapolated via the coherence-L1 signal (sum of off-diagonal density-matrix magnitudes, Baumgratz/Cramer/Plenio 2014) instead of the classical-JSD one -- validated (dense_evolution's own docstring, 200-seed phaseflip sweep) to catch phase-type noise (phaseflip, dephasing) the JSD-predictive signal is structurally blind to, since JSD only ever reads the density matrix's diagonal.
Same real anti-OOM guard, same Uhlmann-fidelity grading against the true ideal density matrix as run_density_matrix_zne -- the two functions differ in exactly one line (which dense_evolution extrapolation function is called), by design, so a caller comparing them is comparing the mitigation signal, not two different pipelines.
Source code in tools/dashboard/core/mitigation.py
Not to be confused with dense_evolution.mitigation
(same name, different module) — that one is the actual ZNE
implementation (richardson_extrapolate, zne_density_matrix,
jsd_predictive_zne_density_matrix, ...); this one is the dashboard's
request/response wrapper around it.