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,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
+ )