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,102 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Real predictive-healing pass over a noisy vector sequence (VQE/MD
|
|
3
|
+
telemetry, quantum state trajectories, or any other (n_steps, dim) array)
|
|
4
|
+
-- a thin wrapper around `ia_utils.vector_healing.enhanced_dense_healing_hybrid`,
|
|
5
|
+
which itself is built on `dense_evolution.healing`'s Phi-Trigger primitives
|
|
6
|
+
(calculate_phi_ab, calculate_vettore_dinamico, evaluate_phi_trigger).
|
|
7
|
+
|
|
8
|
+
This existed in the pre-rebuild dashboard_core (Streamlit dashboard's
|
|
9
|
+
"AI healing shield" middleware, routing VQE/MD telemetry through it
|
|
10
|
+
before any panel was built from it) but was left behind when
|
|
11
|
+
dashboard_core was rebuilt around the Composer kernel -- see this
|
|
12
|
+
package's __init__.py docstring ("will be reintegrated selectively once
|
|
13
|
+
this base is solid"). Reintegrated here as its own module, mirroring
|
|
14
|
+
mitigation.py's shape (a dataclass result + one thin run_* function),
|
|
15
|
+
rather than resurrecting the old monolithic dashboard_core.py.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from dataclasses import dataclass
|
|
19
|
+
from typing import Optional
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
from ia_utils.vector_healing import enhanced_dense_healing_hybrid
|
|
25
|
+
except ImportError as _import_error:
|
|
26
|
+
# ia_utils ships in the same distribution as dashboard_core (see
|
|
27
|
+
# pyproject.toml's packages list) so this shouldn't fail on a normal
|
|
28
|
+
# install -- but ia_utils.vector_healing's own dependency chain
|
|
29
|
+
# (dense_evolution.healing, which needs JAX) can still fail
|
|
30
|
+
# transitively, or a stripped/vendored copy could omit ia_utils
|
|
31
|
+
# entirely. Deferred to call time instead of failing this whole
|
|
32
|
+
# module's import, which would otherwise take down every unrelated
|
|
33
|
+
# dashboard_core feature at import time for a healing-specific gap.
|
|
34
|
+
enhanced_dense_healing_hybrid = None
|
|
35
|
+
_IMPORT_ERROR = _import_error
|
|
36
|
+
else:
|
|
37
|
+
_IMPORT_ERROR = None
|
|
38
|
+
|
|
39
|
+
__all__ = ['VectorHealingResult', 'run_vector_healing']
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@dataclass
|
|
43
|
+
class VectorHealingResult:
|
|
44
|
+
healed_vectors: list # (n_steps, dim), same shape as input
|
|
45
|
+
fallback_triggered: bool # True only if genuine NaN/Inf corruption was present AND corrected
|
|
46
|
+
adaptive_radius_used: int
|
|
47
|
+
reconstruction_error: float # mean per-step norm of (healed - sanitized) vectors
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def run_vector_healing(vectors: np.ndarray, radius_baseline: Optional[int] = None) -> VectorHealingResult:
|
|
51
|
+
"""Heal a noisy (n_steps, dim) vector sequence: per step, a Phi-Trigger
|
|
52
|
+
(dense_evolution.healing) decides whether the change from a local
|
|
53
|
+
baseline looks like genuine dynamics (kept as-is) or static noise
|
|
54
|
+
(replaced by the local median) -- see
|
|
55
|
+
ia_utils.vector_healing.enhanced_dense_healing_hybrid's own docstring
|
|
56
|
+
for the full algorithm. NaN/Inf entries are sanitized first
|
|
57
|
+
(column-mean imputation) regardless of the trigger's decision.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
vectors: array-like, shape (n_steps, dim), n_steps >= 0.
|
|
61
|
+
radius_baseline: fixed radius for the local baseline window; if
|
|
62
|
+
None (default), computed adaptively as
|
|
63
|
+
min(20, max(3, n_steps // 3)).
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
VectorHealingResult
|
|
67
|
+
|
|
68
|
+
Examples
|
|
69
|
+
--------
|
|
70
|
+
>>> import numpy as np
|
|
71
|
+
>>> from dashboard_core.vector_healing import run_vector_healing
|
|
72
|
+
>>> rng = np.random.default_rng(0)
|
|
73
|
+
>>> vectors = rng.normal(0, 1, size=(30, 3))
|
|
74
|
+
>>> vectors[10, 1] += 8.0 # a noise spike -- the Phi-Trigger heals this
|
|
75
|
+
>>> vectors[15, 0] = np.nan # genuine NaN corruption -- this is what fallback_triggered reports
|
|
76
|
+
>>> result = run_vector_healing(vectors)
|
|
77
|
+
>>> result.fallback_triggered # True only because of the NaN, not the spike
|
|
78
|
+
True
|
|
79
|
+
>>> abs(result.healed_vectors[10][1]) < 1.0 # spike replaced by the local median regardless
|
|
80
|
+
True
|
|
81
|
+
"""
|
|
82
|
+
if enhanced_dense_healing_hybrid is None:
|
|
83
|
+
raise ImportError(
|
|
84
|
+
"run_vector_healing requires ia_utils.vector_healing, which failed "
|
|
85
|
+
f"to import ({_IMPORT_ERROR}). It ships with dense-evolution's own "
|
|
86
|
+
"packages, so this usually means a stripped/vendored install is "
|
|
87
|
+
"missing it, or one of its own dependencies (dense_evolution.healing "
|
|
88
|
+
"needs JAX) isn't available."
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
arr = np.asarray(vectors, dtype=np.float64)
|
|
92
|
+
if arr.ndim != 2:
|
|
93
|
+
raise ValueError(f"vectors must be 2D (n_steps, dim), got shape {arr.shape}")
|
|
94
|
+
|
|
95
|
+
healed, metadata = enhanced_dense_healing_hybrid(arr, radius_baseline=radius_baseline)
|
|
96
|
+
|
|
97
|
+
return VectorHealingResult(
|
|
98
|
+
healed_vectors=healed.tolist(),
|
|
99
|
+
fallback_triggered=bool(metadata['fallback_triggered']),
|
|
100
|
+
adaptive_radius_used=int(metadata['adaptive_radius_used']),
|
|
101
|
+
reconstruction_error=float(metadata['reconstruction_error']),
|
|
102
|
+
)
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Every visualization here is native (plain matplotlib/numpy) -- no Qiskit
|
|
3
|
+
anywhere in this module. The circuit diagram was already native
|
|
4
|
+
(dashboard_core.circuit_diagram); histogram/Q-sphere/Bloch spheres are
|
|
5
|
+
now native too (dashboard_core.state_visuals), replacing
|
|
6
|
+
qiskit.visualization.{plot_histogram, plot_state_qsphere,
|
|
7
|
+
plot_bloch_multivector} -- the last Qiskit call sites this dashboard had.
|
|
8
|
+
Qiskit's macOS instability was never proven scoped to just
|
|
9
|
+
QuantumCircuit (see circuit_diagram.py's own docstring), so making these
|
|
10
|
+
three merely "optional" instead of actually removing Qiskit would have
|
|
11
|
+
left the same open question for a different set of functions; there's no
|
|
12
|
+
reason left to keep it as a dependency here at all.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import matplotlib.pyplot as plt
|
|
16
|
+
|
|
17
|
+
from .circuit_diagram import draw_native_circuit_diagram
|
|
18
|
+
from .state_visuals import native_histogram_figure, native_qsphere_figure, native_bloch_multivector_figure
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
'draw_circuit_figure', 'histogram_figure', 'qsphere_figure', 'bloch_multivector_figure',
|
|
22
|
+
'energy_landscape_figure', 'zne_bar_figure', 'qec_syndrome_map_figure',
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
# dense_evolution.registry's dark_background styling used to be a bare
|
|
26
|
+
# import-time side effect -- fixed (prog.txt, core audit point 2, PR
|
|
27
|
+
# #256) into an explicit, opt-in apply_dark_theme(), so it no longer
|
|
28
|
+
# fires just from importing dense_evolution. Nothing in this dashboard
|
|
29
|
+
# calls it today, but plt.style.use() is a genuinely global, process-wide
|
|
30
|
+
# mutation, so the wrapper below isn't guarding a stale concern (prog.txt,
|
|
31
|
+
# dashboard_core audit point 5a) -- it's still real protection: rendering
|
|
32
|
+
# inside style.context('default') pins every figure here to matplotlib's
|
|
33
|
+
# light default regardless of whatever ELSE in the same process (a future
|
|
34
|
+
# caller of apply_dark_theme(), another library, a user's own
|
|
35
|
+
# matplotlibrc) may have changed globally.
|
|
36
|
+
_LIGHT_STYLE = 'default'
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def draw_circuit_figure(ops, n_qubits: int):
|
|
40
|
+
"""Native matplotlib circuit diagram (dashboard_core.circuit_diagram).
|
|
41
|
+
|
|
42
|
+
Examples
|
|
43
|
+
--------
|
|
44
|
+
>>> from dashboard_core.visuals import draw_circuit_figure
|
|
45
|
+
>>> fig = draw_circuit_figure([('h', 0), ('cx', 0, 1)], n_qubits=2)
|
|
46
|
+
>>> type(fig).__name__
|
|
47
|
+
'Figure'
|
|
48
|
+
"""
|
|
49
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
50
|
+
return draw_native_circuit_diagram(ops, n_qubits)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def histogram_figure(counts: dict, statevector=None):
|
|
54
|
+
"""Native shot-count histogram (dashboard_core.state_visuals).
|
|
55
|
+
statevector (optional): color bars by amplitude phase instead of a
|
|
56
|
+
flat color -- see native_histogram_figure's own docstring."""
|
|
57
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
58
|
+
return native_histogram_figure(counts, statevector=statevector)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def qsphere_figure(statevector):
|
|
62
|
+
"""Native Q-sphere (dashboard_core.state_visuals) -- the multi-qubit
|
|
63
|
+
generalization of the Bloch sphere used by IBM Quantum Composer."""
|
|
64
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
65
|
+
return native_qsphere_figure(statevector)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def bloch_multivector_figure(statevector):
|
|
69
|
+
"""Native per-qubit Bloch spheres (dashboard_core.state_visuals)."""
|
|
70
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
71
|
+
return native_bloch_multivector_figure(statevector)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def energy_landscape_figure(
|
|
75
|
+
values_i, values_j, energies, param_i, param_j,
|
|
76
|
+
converged_i, converged_j, converged_energy,
|
|
77
|
+
):
|
|
78
|
+
"""Contour plot of a real VQE energy landscape scan (dashboard_core.
|
|
79
|
+
vqe.scan_hardware_efficient_energy_landscape) over two parameters,
|
|
80
|
+
with the converged optimum marked at its actual (converged_i,
|
|
81
|
+
converged_j) coordinates -- not assumed to sit at the grid's center,
|
|
82
|
+
since the caller decides the scan range independently."""
|
|
83
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
84
|
+
fig, ax = plt.subplots(figsize=(5, 4))
|
|
85
|
+
contour = ax.contourf(values_i, values_j, energies.T, levels=30, cmap='viridis')
|
|
86
|
+
fig.colorbar(contour, ax=ax, label='Energia (Hartree)')
|
|
87
|
+
ax.scatter(
|
|
88
|
+
[converged_i], [converged_j],
|
|
89
|
+
color='red', marker='o', s=60, label=f'Minimo VQE ({converged_energy:.6f} Hartree)',
|
|
90
|
+
)
|
|
91
|
+
ax.set_xlabel(f'θ[{param_i}]')
|
|
92
|
+
ax.set_ylabel(f'θ[{param_j}]')
|
|
93
|
+
ax.legend(loc='upper right', fontsize=8)
|
|
94
|
+
fig.tight_layout()
|
|
95
|
+
return fig
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def zne_bar_figure(noise_factors, noisy_expectations, noisy_sems, zne_extrapolated, ideal_expectation):
|
|
99
|
+
"""Real ZNE measurement chart with a +-1 SEM (standard error of the
|
|
100
|
+
mean, not the raw per-trial spread) band around each measured point
|
|
101
|
+
-- the Monte Carlo uncertainty on run_zne_mitigation's own
|
|
102
|
+
n_trials-averaged expectation values, not a decorative estimate."""
|
|
103
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
104
|
+
fig, ax = plt.subplots(figsize=(5, 4))
|
|
105
|
+
ax.errorbar(
|
|
106
|
+
noise_factors, noisy_expectations, yerr=noisy_sems,
|
|
107
|
+
fmt='o-', color='#1f77b4', capsize=4, label='Misurato (media Monte Carlo ±1 SEM)',
|
|
108
|
+
)
|
|
109
|
+
ax.axhline(ideal_expectation, color='green', linestyle='--', label=f'Ideale ({ideal_expectation:.4f})')
|
|
110
|
+
ax.scatter([0], [zne_extrapolated], color='red', marker='*', s=150, zorder=5,
|
|
111
|
+
label=f'Estrapolato a 0 ({zne_extrapolated:.4f})')
|
|
112
|
+
ax.set_xlabel('Fattore di scala del rumore')
|
|
113
|
+
ax.set_ylabel('<P> misurato')
|
|
114
|
+
ax.legend(loc='best', fontsize=8)
|
|
115
|
+
fig.tight_layout()
|
|
116
|
+
return fig
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def qec_syndrome_map_figure(stabilizers, syndrome, pauli_error=None):
|
|
120
|
+
"""Real syndrome-measurement map: physical qubits laid out in a line,
|
|
121
|
+
each stabilizer drawn as a bracket connecting the qubits it acts on
|
|
122
|
+
(its own non-identity Pauli positions), colored red if its syndrome
|
|
123
|
+
bit is 1 (anticommutes / detected, from dense_evolution's own
|
|
124
|
+
compute_syndrome output) or gray if 0 (commutes / undetected) --
|
|
125
|
+
not a schematic, the real per-stabilizer outcome. Qubits touched by
|
|
126
|
+
pauli_error (if given) are marked filled and labeled with their
|
|
127
|
+
Pauli letter."""
|
|
128
|
+
with plt.style.context(_LIGHT_STYLE):
|
|
129
|
+
n_qubits = len(stabilizers[0]) if stabilizers else (len(pauli_error) if pauli_error else 1)
|
|
130
|
+
error_positions = {i for i, p in enumerate(pauli_error) if p != 'I'} if pauli_error else set()
|
|
131
|
+
|
|
132
|
+
fig_height = 2.2 + 0.4 * max(1, len(stabilizers))
|
|
133
|
+
fig, ax = plt.subplots(figsize=(max(4, n_qubits * 1.2), fig_height))
|
|
134
|
+
|
|
135
|
+
for i in range(n_qubits):
|
|
136
|
+
if i in error_positions:
|
|
137
|
+
ax.scatter([i], [0], s=350, color='#d62728', edgecolors='black', zorder=3)
|
|
138
|
+
ax.annotate(pauli_error[i], (i, 0), ha='center', va='center', color='white',
|
|
139
|
+
fontsize=9, fontweight='bold', zorder=4)
|
|
140
|
+
else:
|
|
141
|
+
ax.scatter([i], [0], s=350, color='white', edgecolors='black', zorder=3)
|
|
142
|
+
ax.annotate(f'q{i}', (i, -0.35), ha='center', fontsize=8)
|
|
143
|
+
|
|
144
|
+
for s_idx, (stab, bit) in enumerate(zip(stabilizers, syndrome)):
|
|
145
|
+
positions = [i for i, p in enumerate(stab) if p != 'I']
|
|
146
|
+
if not positions:
|
|
147
|
+
continue
|
|
148
|
+
height = 0.5 + 0.4 * s_idx
|
|
149
|
+
color = '#d62728' if bit else '#999999'
|
|
150
|
+
lo, hi = min(positions), max(positions)
|
|
151
|
+
ax.plot([lo, lo, hi, hi], [0.15, height, height, 0.15], color=color, linewidth=2)
|
|
152
|
+
ax.annotate(stab, ((lo + hi) / 2, height + 0.05), ha='center', fontsize=8, color=color)
|
|
153
|
+
|
|
154
|
+
ax.set_xlim(-0.7, n_qubits - 0.3)
|
|
155
|
+
ax.set_ylim(-0.6, fig_height - 1.9)
|
|
156
|
+
ax.axis('off')
|
|
157
|
+
fig.tight_layout()
|
|
158
|
+
return fig
|