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,225 @@
|
|
|
1
|
+
"""Tools: molecular Hamiltonians, VQE, QM/MM forces, and molecular
|
|
2
|
+
dynamics."""
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from ..client import _request, catch_errors
|
|
7
|
+
from ..config import COMPUTE
|
|
8
|
+
from ..models import (
|
|
9
|
+
CustomMoleculeInput, EnergyScanInput, MassDecompositionInput, MdTrajectoryInput, MixMoleculesInput,
|
|
10
|
+
MoleculeEnergyInput, NativeHfDiagnosticsInput, QmmmForcesInput, RunVqeInput,
|
|
11
|
+
)
|
|
12
|
+
from ..molecules import _resolve_molecule_name
|
|
13
|
+
from ..server import mcp
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@mcp.tool(name="dense_evolution_molecule_energy", annotations={"title": "Get catalog molecule ground-state energy", **COMPUTE})
|
|
17
|
+
@catch_errors
|
|
18
|
+
async def dense_evolution_molecule_energy(params: MoleculeEnergyInput) -> str:
|
|
19
|
+
"""Compute the exact ground-state energy of a catalog molecule via real
|
|
20
|
+
Hartree-Fock + Jordan-Wigner/Bravyi-Kitaev Hamiltonian construction and
|
|
21
|
+
exact dense diagonalization.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
params (MoleculeEnergyInput): name (short id or full catalog name), mapping.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
str: JSON with n_qubits, symbols, geometry, charge, ground_state_energy_hartree.
|
|
28
|
+
"Error: ..." with a 404-style message if `name` is not in the catalog
|
|
29
|
+
(call dense_evolution_list_molecules to see valid names/ids).
|
|
30
|
+
"""
|
|
31
|
+
resolved = await _resolve_molecule_name(params.name)
|
|
32
|
+
payload = {**params.model_dump(), "name": resolved}
|
|
33
|
+
return json.dumps(await _request("POST", "/api/hamiltonian/molecule", timeout=60.0, json=payload), indent=2)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@mcp.tool(name="dense_evolution_mix_molecules", annotations={"title": "Mix two catalog Hamiltonians", **COMPUTE})
|
|
37
|
+
@catch_errors
|
|
38
|
+
async def dense_evolution_mix_molecules(params: MixMoleculesInput) -> str:
|
|
39
|
+
"""Compute H_mix = weight_a*H_a + weight_b*H_b for two catalog molecules
|
|
40
|
+
that share the same qubit count (same electron space), and diagonalize
|
|
41
|
+
all three (H_a, H_b, H_mix) for their ground-state energies. Mixing
|
|
42
|
+
molecules with different qubit counts is physically meaningless and is
|
|
43
|
+
rejected with a clear error.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
params (MixMoleculesInput): name_a, name_b (short id or full catalog name), weight_a, weight_b, mapping.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
str: JSON with n_qubits, energy_a, energy_b, energy_mixed (all in Hartree).
|
|
50
|
+
"""
|
|
51
|
+
name_a = await _resolve_molecule_name(params.name_a)
|
|
52
|
+
name_b = await _resolve_molecule_name(params.name_b)
|
|
53
|
+
payload = {**params.model_dump(), "name_a": name_a, "name_b": name_b}
|
|
54
|
+
return json.dumps(await _request("POST", "/api/hamiltonian/mix", timeout=60.0, json=payload), indent=2)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@mcp.tool(name="dense_evolution_custom_molecule_energy", annotations={"title": "Get custom molecule ground-state energy", **COMPUTE})
|
|
58
|
+
@catch_errors
|
|
59
|
+
async def dense_evolution_custom_molecule_energy(params: CustomMoleculeInput) -> str:
|
|
60
|
+
"""Compute the ground-state energy of an arbitrary molecule (not in the
|
|
61
|
+
catalog) from its atomic symbols and geometry, via the same Hartree-Fock
|
|
62
|
+
pipeline as the catalog. Small molecules only -- exact dense
|
|
63
|
+
diagonalization caps out at 12 qubits, rejected before PennyLane runs if
|
|
64
|
+
the electron/orbital count would exceed that.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
params (CustomMoleculeInput): symbols, geometry, charge, mapping.
|
|
68
|
+
len(symbols) must equal len(geometry).
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
str: JSON with n_qubits, ground_state_energy_hartree, or "Error: ..."
|
|
72
|
+
if the molecule needs more than 12 qubits.
|
|
73
|
+
"""
|
|
74
|
+
return json.dumps(await _request("POST", "/api/hamiltonian/custom", timeout=60.0, json=params.model_dump()), indent=2)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@mcp.tool(name="dense_evolution_native_hf_diagnostics", annotations={"title": "Diagnose Hartree-Fock SCF convergence", **COMPUTE})
|
|
78
|
+
@catch_errors
|
|
79
|
+
async def dense_evolution_native_hf_diagnostics(params: NativeHfDiagnosticsInput) -> str:
|
|
80
|
+
"""Run real Hartree-Fock SCF (dense_evolution.native_hf, the fallback
|
|
81
|
+
path for elements outside PennyLane's bundled STO-3G table, e.g.
|
|
82
|
+
Silicon) and run Dense-Armor's Hampel/Tukey anomaly filters over the
|
|
83
|
+
real per-iteration energy trace, instead of trusting the final
|
|
84
|
+
`converged` flag alone. Needs the `armor` extra (pip install
|
|
85
|
+
dense-evolution[armor]) -- returns an actionable "Error: ..." if it
|
|
86
|
+
isn't installed.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
params (NativeHfDiagnosticsInput): symbols, geometry, charge,
|
|
90
|
+
active_electrons, active_orbitals. len(symbols) must equal len(geometry).
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
str: JSON with n_qubits, converged, n_iterations,
|
|
94
|
+
electronic_energy_hartree, total_energy_hartree,
|
|
95
|
+
n_anomalies_hampel, n_anomalies_tukey, anomaly_fraction_hampel,
|
|
96
|
+
anomaly_fraction_tukey.
|
|
97
|
+
"""
|
|
98
|
+
return json.dumps(await _request("POST", "/api/native_hf/diagnose", timeout=60.0, json=params.model_dump()), indent=2)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@mcp.tool(name="dense_evolution_mass_decomposition", annotations={"title": "Check a mass-spectrometry neutral loss", **COMPUTE})
|
|
102
|
+
@catch_errors
|
|
103
|
+
async def dense_evolution_mass_decomposition(params: MassDecompositionInput) -> str:
|
|
104
|
+
"""Check whether a mass-spectrometry peak-pair difference is a
|
|
105
|
+
chemically valid, reachable sub-formula mass of a precursor formula
|
|
106
|
+
(dense_evolution.utils.mass_decomposition, promoted from CASMI26
|
|
107
|
+
spectral-identification work). `nearest_reachable_mass` is the exact,
|
|
108
|
+
RDBE-filtered answer; `density_at_target` is a plausibility density
|
|
109
|
+
from the same landscape via FFT, not a probability.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
params (MassDecompositionInput): formula, target_mass, max_mass.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
str: JSON with formula_counts, rdbe, nearest_reachable_mass, density_at_target.
|
|
116
|
+
"""
|
|
117
|
+
return json.dumps(await _request("POST", "/api/mass_decomposition", timeout=30.0, json=params.model_dump()), indent=2)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@mcp.tool(name="dense_evolution_energy_scan", annotations={"title": "Scan ground-state energy over several geometries", **COMPUTE})
|
|
121
|
+
@catch_errors
|
|
122
|
+
async def dense_evolution_energy_scan(params: EnergyScanInput) -> str:
|
|
123
|
+
"""Compute the ground-state energy at each of several geometries in one
|
|
124
|
+
call -- e.g. a bond-length dissociation curve or a bond-angle sweep --
|
|
125
|
+
instead of calling dense_evolution_custom_molecule_energy once per
|
|
126
|
+
point. Points are evaluated concurrently against the kernel. A point
|
|
127
|
+
that fails (e.g. too many qubits for exact diagonalization) is reported
|
|
128
|
+
with its own error and does not abort the rest of the scan.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
params (EnergyScanInput): symbols, geometries (list of points, max 50),
|
|
132
|
+
charge, mapping, labels (optional, one per point).
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
str: JSON with:
|
|
136
|
+
{
|
|
137
|
+
"n_points": int,
|
|
138
|
+
"results": [{"label": ..., "n_qubits": int, "ground_state_energy_hartree": float} | {"label": ..., "error": str}, ...],
|
|
139
|
+
"minimum": {"label": ..., "ground_state_energy_hartree": float} | null # over successful points only
|
|
140
|
+
}
|
|
141
|
+
"""
|
|
142
|
+
if params.labels is not None and len(params.labels) != len(params.geometries):
|
|
143
|
+
raise ValueError(f"{len(params.labels)} labels but {len(params.geometries)} geometries -- must match.")
|
|
144
|
+
labels = params.labels if params.labels is not None else list(range(len(params.geometries)))
|
|
145
|
+
|
|
146
|
+
async def _one_point(label, geometry):
|
|
147
|
+
if len(params.symbols) != len(geometry):
|
|
148
|
+
return {"label": label, "error": f"{len(params.symbols)} symbols but {len(geometry)} geometry rows"}
|
|
149
|
+
try:
|
|
150
|
+
data = await _request(
|
|
151
|
+
"POST", "/api/hamiltonian/custom", timeout=60.0,
|
|
152
|
+
json={"symbols": params.symbols, "geometry": geometry, "charge": params.charge, "mapping": params.mapping},
|
|
153
|
+
)
|
|
154
|
+
return {"label": label, "n_qubits": data["n_qubits"], "ground_state_energy_hartree": data["ground_state_energy_hartree"]}
|
|
155
|
+
except Exception as e:
|
|
156
|
+
return {"label": label, "error": str(e)}
|
|
157
|
+
|
|
158
|
+
results = await asyncio.gather(*(_one_point(l, g) for l, g in zip(labels, params.geometries)))
|
|
159
|
+
successful = [r for r in results if "ground_state_energy_hartree" in r]
|
|
160
|
+
minimum = min(successful, key=lambda r: r["ground_state_energy_hartree"]) if successful else None
|
|
161
|
+
return json.dumps({"n_points": len(results), "results": results, "minimum": minimum}, indent=2)
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
@mcp.tool(name="dense_evolution_run_vqe", annotations={"title": "Run VQE ground-state optimization", **COMPUTE})
|
|
165
|
+
@catch_errors
|
|
166
|
+
async def dense_evolution_run_vqe(params: RunVqeInput) -> str:
|
|
167
|
+
"""Run real VQE (Adam gradient descent with adjoint differentiation)
|
|
168
|
+
against a molecule's Jordan-Wigner Hamiltonian, from a fresh random
|
|
169
|
+
start every call -- not a cached/precomputed result. Can take a while
|
|
170
|
+
for 'uccsd' ansatz or high maxiter; consider dense_evolution_health's
|
|
171
|
+
RAM figures and start with a small maxiter/n_layers to gauge cost first.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
params (RunVqeInput): either `name` (short id or full catalog name)
|
|
175
|
+
or `symbols`+`geometry` (custom) must be given, plus
|
|
176
|
+
ansatz_type, n_layers, maxiter, step_size, beta1, beta2, seed.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
str: JSON with the optimized energy, convergence history, and
|
|
180
|
+
optimized parameters. "Error: ..." if neither name nor
|
|
181
|
+
symbols+geometry is provided, or the molecule is unknown.
|
|
182
|
+
"""
|
|
183
|
+
payload = params.model_dump()
|
|
184
|
+
if params.name:
|
|
185
|
+
payload["name"] = await _resolve_molecule_name(params.name)
|
|
186
|
+
return json.dumps(await _request("POST", "/api/vqe", timeout=600.0, json=payload), indent=2)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
@mcp.tool(name="dense_evolution_qmmm_forces", annotations={"title": "Compute Hellmann-Feynman nuclear forces", **COMPUTE})
|
|
190
|
+
@catch_errors
|
|
191
|
+
async def dense_evolution_qmmm_forces(params: QmmmForcesInput) -> str:
|
|
192
|
+
"""Compute real Hellmann-Feynman nuclear forces (F = -d<psi|H(R)|psi>/dR
|
|
193
|
+
via PennyLane autodiff, not finite differences) on a catalog molecule's
|
|
194
|
+
real Hartree-Fock ground state.
|
|
195
|
+
|
|
196
|
+
Args:
|
|
197
|
+
params (QmmmForcesInput): name (short id or full catalog name), mapping.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
str: JSON with per-atom force vectors and related energetics.
|
|
201
|
+
"""
|
|
202
|
+
resolved = await _resolve_molecule_name(params.name)
|
|
203
|
+
payload = {**params.model_dump(), "name": resolved}
|
|
204
|
+
return json.dumps(await _request("POST", "/api/qmmm_forces", timeout=120.0, json=payload), indent=2)
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
@mcp.tool(name="dense_evolution_md_trajectory", annotations={"title": "Run a molecular dynamics trajectory", **COMPUTE})
|
|
208
|
+
@catch_errors
|
|
209
|
+
async def dense_evolution_md_trajectory(params: MdTrajectoryInput) -> str:
|
|
210
|
+
"""Run a real Velocity-Verlet MD trajectory driven by Hellmann-Feynman
|
|
211
|
+
forces at every step, for a catalog molecule.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
params (MdTrajectoryInput): name (short id or full catalog name),
|
|
215
|
+
n_steps, dt_fs, mapping, recompute_electronic_state (true = true
|
|
216
|
+
ab-initio MD, capped at 30 steps; false = fixed electronic
|
|
217
|
+
state, capped at 200 steps).
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
str: JSON with the trajectory (positions/energies per step).
|
|
221
|
+
"Error: ..." if n_steps is out of range for the chosen mode.
|
|
222
|
+
"""
|
|
223
|
+
resolved = await _resolve_molecule_name(params.name)
|
|
224
|
+
payload = {**params.model_dump(), "name": resolved}
|
|
225
|
+
return json.dumps(await _request("POST", "/api/md_trajectory", timeout=600.0, json=payload), indent=2)
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""Tools: circuit building and execution."""
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from ..client import _request, catch_errors
|
|
5
|
+
from ..config import COMPUTE, READ_ONLY_IDEMPOTENT
|
|
6
|
+
from ..models import BuildCircuitInput, RunCircuitInput
|
|
7
|
+
from ..server import mcp
|
|
8
|
+
from ..utils.images import _save_png
|
|
9
|
+
from ..utils.truncation import _truncate_probabilities, _truncate_statevector
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@mcp.tool(name="dense_evolution_build_circuit", annotations={"title": "Build OpenQASM from gate operations", **READ_ONLY_IDEMPOTENT})
|
|
13
|
+
@catch_errors
|
|
14
|
+
async def dense_evolution_build_circuit(params: BuildCircuitInput) -> str:
|
|
15
|
+
"""Convert a list of gate operations (as used by the graphical circuit
|
|
16
|
+
builder) into real OpenQASM text, ready to pass to `dense_evolution_run_circuit`.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
params (BuildCircuitInput): n_qubits, ops (see dense_evolution_list_gates for valid gate names).
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: JSON {"qasm": "..."} on success, or "Error: ..." if the op list is invalid.
|
|
23
|
+
"""
|
|
24
|
+
data = await _request("POST", "/api/build_from_ops", timeout=30.0, json=params.model_dump())
|
|
25
|
+
return json.dumps(data, indent=2)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@mcp.tool(name="dense_evolution_run_circuit", annotations={"title": "Run an OpenQASM circuit", **COMPUTE})
|
|
29
|
+
@catch_errors
|
|
30
|
+
async def dense_evolution_run_circuit(params: RunCircuitInput) -> str:
|
|
31
|
+
"""Run real OpenQASM on dense_evolution's DenseSVSimulator (or the MPS
|
|
32
|
+
backend for large circuits) and return measurement counts, probabilities,
|
|
33
|
+
and statevector amplitudes. Above the dense backend's safe qubit ceiling
|
|
34
|
+
(see dense_evolution_system_limits), automatically switches to an MPS
|
|
35
|
+
top-k-states approximation instead of failing.
|
|
36
|
+
|
|
37
|
+
Large statevectors/probability arrays are truncated to their most
|
|
38
|
+
significant entries (see 'shown' vs 'total_*' fields) to keep the
|
|
39
|
+
response usable in an agent's context -- the full histogram is always
|
|
40
|
+
returned in 'counts' since it's naturally bounded by `shots`.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
params (RunCircuitInput): qasm, shots, seed, noise_model, noise_p, backend,
|
|
44
|
+
top_k, include_visualizations (see field descriptions).
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
str: JSON with n_qubits, backend, counts, truncated probabilities/statevector,
|
|
48
|
+
fidelity_vs_ideal, and (if include_visualizations) paths to saved PNG files.
|
|
49
|
+
For circuits above the dense limit on the 'mps' backend, returns a
|
|
50
|
+
differently-shaped {"large_scale": true, "top_k_states": [...], ...} response.
|
|
51
|
+
"""
|
|
52
|
+
payload = params.model_dump(exclude={"include_visualizations", "top_k"})
|
|
53
|
+
data = await _request("POST", "/api/run", timeout=60.0, json=payload)
|
|
54
|
+
|
|
55
|
+
image_metadata = {
|
|
56
|
+
"tool": "dense_evolution_run_circuit", "qasm": params.qasm, "shots": params.shots,
|
|
57
|
+
"seed": params.seed, "noise_model": params.noise_model, "noise_p": params.noise_p,
|
|
58
|
+
"backend": params.backend,
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if data.get("large_scale"):
|
|
62
|
+
result = {k: v for k, v in data.items() if k != "circuit_png"}
|
|
63
|
+
if params.include_visualizations:
|
|
64
|
+
result["circuit_png_path"] = _save_png(data.get("circuit_png"), "circuit_large_scale", metadata=image_metadata)
|
|
65
|
+
return json.dumps(result, indent=2)
|
|
66
|
+
|
|
67
|
+
result = {
|
|
68
|
+
"n_qubits": data["n_qubits"],
|
|
69
|
+
"backend": data["backend"],
|
|
70
|
+
"counts": data["counts"],
|
|
71
|
+
"probabilities": _truncate_probabilities(data["probabilities"], top_k=params.top_k),
|
|
72
|
+
"statevector": _truncate_statevector(data["statevector"], top_k=params.top_k),
|
|
73
|
+
"fidelity_vs_ideal": data.get("fidelity_vs_ideal"),
|
|
74
|
+
"mps_max_bond_used": data.get("mps_max_bond_used"),
|
|
75
|
+
"mps_memory_mb": data.get("mps_memory_mb"),
|
|
76
|
+
"mps_avg_jsd": data.get("mps_avg_jsd"),
|
|
77
|
+
}
|
|
78
|
+
if params.include_visualizations:
|
|
79
|
+
result["circuit_png_path"] = _save_png(data.get("circuit_png"), "circuit", metadata=image_metadata)
|
|
80
|
+
result["histogram_png_path"] = _save_png(data.get("histogram_png"), "histogram", metadata=image_metadata)
|
|
81
|
+
result["qsphere_png_path"] = _save_png(data.get("qsphere_png"), "qsphere", metadata=image_metadata)
|
|
82
|
+
result["bloch_png_path"] = _save_png(data.get("bloch_png"), "bloch", metadata=image_metadata)
|
|
83
|
+
return json.dumps(result, indent=2)
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""Tools: quantum cryptography protocols (crypto-q, promoted from
|
|
2
|
+
Dense-Evolution-Discovery issue #189). Registered against the shared
|
|
3
|
+
`mcp` instance created in server.py -- see that module's docstring for
|
|
4
|
+
why importing `mcp` back from there (rather than the other way around)
|
|
5
|
+
is safe despite looking circular."""
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from ..client import _request, catch_errors
|
|
9
|
+
from ..config import COMPUTE
|
|
10
|
+
from ..models import Bb84Input, DiQkdGhzInput, DickaInput
|
|
11
|
+
from ..server import mcp
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@mcp.tool(name="dense_evolution_crypto_bb84", annotations={"title": "Run BB84 quantum key distribution", **COMPUTE})
|
|
15
|
+
@catch_errors
|
|
16
|
+
async def dense_evolution_crypto_bb84(params: Bb84Input) -> str:
|
|
17
|
+
"""Run real BB84 quantum key distribution: prepare -> channel ->
|
|
18
|
+
measure -> sift -> QBER. Validated at N=5000 rounds, 5 independent
|
|
19
|
+
seeds: QBER=0 on a perfect channel, QBER=2*p_channel/3 under isotropic
|
|
20
|
+
depolarizing noise, QBER=0.25 under an intercept-resend attack
|
|
21
|
+
(eve=True) -- all within +-1sigma of theory.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
params (Bb84Input): n_rounds, p_channel, eve, seed.
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
str: JSON with n_rounds, p_channel, eve, qber, sifted_key_length.
|
|
28
|
+
"""
|
|
29
|
+
return json.dumps(await _request("POST", "/api/crypto/bb84", timeout=60.0, json=params.model_dump()), indent=2)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@mcp.tool(name="dense_evolution_crypto_di_qkd_ghz", annotations={"title": "Run device-independent QKD via GHZ(3)", **COMPUTE})
|
|
33
|
+
@catch_errors
|
|
34
|
+
async def dense_evolution_crypto_di_qkd_ghz(params: DiQkdGhzInput) -> str:
|
|
35
|
+
"""Run real three-party device-independent conference key agreement
|
|
36
|
+
via a GHZ(3) state (Ribeiro, Murta & Wehner 2018, arXiv:1708.00798),
|
|
37
|
+
using the paper's own Parity-CHSH inequality. win_rate reaches the
|
|
38
|
+
quantum maximum ~0.85355 to machine precision on an ideal channel,
|
|
39
|
+
comfortably clearing the classical bound of 0.75.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
params (DiQkdGhzInput): n_rounds, p_dep, seed.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
str: JSON with n_rounds, p_dep, win_rate, expected_win_rate, qber_b1, qber_b2.
|
|
46
|
+
"""
|
|
47
|
+
return json.dumps(await _request("POST", "/api/crypto/di_qkd_ghz", timeout=60.0, json=params.model_dump()), indent=2)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@mcp.tool(name="dense_evolution_crypto_dicka", annotations={"title": "Run the multi-round DICKA protocol", **COMPUTE})
|
|
51
|
+
@catch_errors
|
|
52
|
+
async def dense_evolution_crypto_dicka(params: DickaInput) -> str:
|
|
53
|
+
"""Run the full multi-round DICKA structure (Appendix Protocol 2 of
|
|
54
|
+
Ribeiro, Murta & Wehner 2018) around the GHZ(3) primitives:
|
|
55
|
+
round selection, parameter estimation, and the abort decision.
|
|
56
|
+
Deliberately does not report a secure key length -- Theorem 4's exact
|
|
57
|
+
value depends on a numerical optimization the source paper never
|
|
58
|
+
reduces to closed form.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
params (DickaInput): n_rounds, gamma, beta, p_dep, seed.
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
str: JSON with n_rounds, n_test, n_key, p_hat, beta, aborted, qber_b1, qber_b2.
|
|
65
|
+
"""
|
|
66
|
+
return json.dumps(await _request("POST", "/api/crypto/dicka", timeout=60.0, json=params.model_dump()), indent=2)
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"""Tools: error mitigation (ZNE, density-matrix ZNE, vector healing)."""
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from ..client import _request, catch_errors
|
|
5
|
+
from ..config import COMPUTE
|
|
6
|
+
from ..models import MitigateDensityMatrixInput, MitigateZneInput, VectorHealingInput
|
|
7
|
+
from ..server import mcp
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@mcp.tool(name="dense_evolution_mitigate_zne", annotations={"title": "Zero-Noise Extrapolation on an expectation value", **COMPUTE})
|
|
11
|
+
@catch_errors
|
|
12
|
+
async def dense_evolution_mitigate_zne(params: MitigateZneInput) -> str:
|
|
13
|
+
"""Run real Zero-Noise Extrapolation: measure a Pauli expectation value
|
|
14
|
+
at several noise scales under a real Kraus noise channel, then
|
|
15
|
+
extrapolate back to zero noise.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
params (MitigateZneInput): qasm, pauli_string, noise_model, noise_p,
|
|
19
|
+
seed, extrapolation_method.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: JSON with n_qubits, ideal_expectation, noise_factors,
|
|
23
|
+
noisy_expectations, zne_extrapolated, extrapolation_method.
|
|
24
|
+
"""
|
|
25
|
+
return json.dumps(await _request("POST", "/api/mitigate", timeout=120.0, json=params.model_dump()), indent=2)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@mcp.tool(name="dense_evolution_mitigate_density_matrix", annotations={"title": "Density-matrix Zero-Noise Extrapolation", **COMPUTE})
|
|
29
|
+
@catch_errors
|
|
30
|
+
async def dense_evolution_mitigate_density_matrix(params: MitigateDensityMatrixInput) -> str:
|
|
31
|
+
"""Run real density-matrix ZNE: Monte-Carlo density-matrix estimate at
|
|
32
|
+
1x/2x/3x noise_p, extrapolated and projected onto the nearest physical
|
|
33
|
+
state, graded by Uhlmann fidelity against the true ideal state.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
params (MitigateDensityMatrixInput): qasm, noise_model, noise_p, seed.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: JSON with n_qubits, noise_factors, fidelity_raw, fidelity_corrected.
|
|
40
|
+
"""
|
|
41
|
+
return json.dumps(await _request("POST", "/api/mitigate_matrix", timeout=120.0, json=params.model_dump()), indent=2)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@mcp.tool(name="dense_evolution_mitigate_coherence", annotations={"title": "Coherence-predictive density-matrix ZNE", **COMPUTE})
|
|
45
|
+
@catch_errors
|
|
46
|
+
async def dense_evolution_mitigate_coherence(params: MitigateDensityMatrixInput) -> str:
|
|
47
|
+
"""Run real coherence-L1-predictive density-matrix ZNE: the same
|
|
48
|
+
Monte-Carlo density-matrix construction as
|
|
49
|
+
dense_evolution_mitigate_density_matrix, but extrapolated via a signal
|
|
50
|
+
that covers phase-type noise (phaseflip, dephasing) the classical-JSD
|
|
51
|
+
signal behind that other tool is structurally blind to -- it only ever
|
|
52
|
+
reads the density matrix's diagonal. Use this one for phase-type noise
|
|
53
|
+
models, the JSD one for amplitude/bitflip-type noise.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
params (MitigateDensityMatrixInput): qasm, noise_model, noise_p, seed.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
str: JSON with n_qubits, noise_factors, fidelity_raw, fidelity_corrected.
|
|
60
|
+
"""
|
|
61
|
+
return json.dumps(await _request("POST", "/api/mitigate_coherence", timeout=120.0, json=params.model_dump()), indent=2)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@mcp.tool(name="dense_evolution_vector_healing", annotations={"title": "Heal a noisy vector sequence", **COMPUTE})
|
|
65
|
+
@catch_errors
|
|
66
|
+
async def dense_evolution_vector_healing(params: VectorHealingInput) -> str:
|
|
67
|
+
"""Run a real predictive-healing pass over a noisy (n_steps, dim)
|
|
68
|
+
vector sequence -- e.g. VQE convergence telemetry or an MD
|
|
69
|
+
trajectory. Per step, a Phi-Trigger (dense_evolution.healing) decides
|
|
70
|
+
whether the change from a local baseline looks like genuine dynamics
|
|
71
|
+
(kept as-is) or static noise (replaced by the local median). NaN/Inf
|
|
72
|
+
entries are sanitized first regardless of that decision.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
params (VectorHealingInput): vectors, radius_baseline.
|
|
76
|
+
|
|
77
|
+
Returns:
|
|
78
|
+
str: JSON with healed_vectors, fallback_triggered,
|
|
79
|
+
adaptive_radius_used, reconstruction_error.
|
|
80
|
+
"""
|
|
81
|
+
return json.dumps(await _request("POST", "/api/vector_healing", timeout=30.0, json=params.model_dump()), indent=2)
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""Tools: standalone noise profiles and density-matrix channels."""
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from ..client import _request, catch_errors
|
|
5
|
+
from ..config import COMPUTE
|
|
6
|
+
from ..models import CosmicRayBurstInput, OscillatingNoiseInput, DensityMatrixChannelInput
|
|
7
|
+
from ..server import mcp
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@mcp.tool(name="dense_evolution_cosmic_ray_burst", annotations={"title": "Cosmic-ray burst noise profile", **COMPUTE})
|
|
11
|
+
@catch_errors
|
|
12
|
+
async def dense_evolution_cosmic_ray_burst(params: CosmicRayBurstInput) -> str:
|
|
13
|
+
"""Real time-dependent decay-probability profile for a cosmic-ray/
|
|
14
|
+
gamma-ray-induced quasiparticle burst, reproducing a real measured
|
|
15
|
+
event from arXiv:2104.05219 on a 26-qubit chip: a two-stage rise then
|
|
16
|
+
a single-exponential recovery.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
params (CosmicRayBurstInput): baseline_gamma, times_us.
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
str: JSON with times_us, baseline_gamma, decay_probabilities, peak_ratio.
|
|
23
|
+
"""
|
|
24
|
+
return json.dumps(await _request("POST", "/api/cosmic_ray_burst", timeout=30.0, json=params.model_dump()), indent=2)
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@mcp.tool(name="dense_evolution_oscillating_noise", annotations={"title": "Oscillating noise-scale profile", **COMPUTE})
|
|
28
|
+
@catch_errors
|
|
29
|
+
async def dense_evolution_oscillating_noise(params: OscillatingNoiseInput) -> str:
|
|
30
|
+
"""A noise strength that oscillates instead of scaling smoothly with
|
|
31
|
+
a ZNE-style scale factor -- for checking whether a mitigation
|
|
32
|
+
technique that assumes smooth noise-vs-scale still works when that
|
|
33
|
+
assumption breaks down.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
params (OscillatingNoiseInput): base_p, freq, amp, factors.
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
str: JSON with base_p, freq, amp, factors, p_eff.
|
|
40
|
+
"""
|
|
41
|
+
return json.dumps(await _request("POST", "/api/oscillating_noise", timeout=30.0, json=params.model_dump()), indent=2)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@mcp.tool(name="dense_evolution_density_matrix_channel", annotations={"title": "Apply a density-matrix noise channel", **COMPUTE})
|
|
45
|
+
@catch_errors
|
|
46
|
+
async def dense_evolution_density_matrix_channel(params: DensityMatrixChannelInput) -> str:
|
|
47
|
+
"""Apply a density-matrix-level noise channel to the ideal density
|
|
48
|
+
matrix of a QASM circuit -- distinct from per-qubit statevector
|
|
49
|
+
noise (see dense_evolution_run_circuit's noise_model/noise_p):
|
|
50
|
+
'global_depolarizing' mixes the whole register toward the fully
|
|
51
|
+
mixed state as one unit (a SPAM-style error), 'amplitude_damping' is
|
|
52
|
+
single-qubit asymmetric energy relaxation.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
params (DensityMatrixChannelInput): qasm, channel, param.
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
str: JSON with n_qubits, channel, param, ideal_diagonal, noisy_diagonal, trace.
|
|
59
|
+
"""
|
|
60
|
+
return json.dumps(await _request("POST", "/api/density_matrix_channel", timeout=30.0, json=params.model_dump()), indent=2)
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Tools: document retrieval (ia_utils.rag, promoted from the quantumrag
|
|
2
|
+
literature-grounding tool). Registered against the shared `mcp` instance
|
|
3
|
+
created in server.py -- see that module's docstring for why importing
|
|
4
|
+
`mcp` back from there (rather than the other way around) is safe despite
|
|
5
|
+
looking circular."""
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from ..client import _request, catch_errors
|
|
9
|
+
from ..config import COMPUTE
|
|
10
|
+
from ..models import RagSearchInput
|
|
11
|
+
from ..server import mcp
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@mcp.tool(name="dense_evolution_rag_search", annotations={"title": "Search documents with hybrid retrieval", **COMPUTE})
|
|
15
|
+
@catch_errors
|
|
16
|
+
async def dense_evolution_rag_search(params: RagSearchInput) -> str:
|
|
17
|
+
"""Build an ephemeral index over the given documents and search it for
|
|
18
|
+
`query` in one call -- ground an agent's own answer in a document
|
|
19
|
+
collection, not a quantum measurement result. Nothing persists
|
|
20
|
+
server-side: each call builds and searches its own index from
|
|
21
|
+
scratch, so pass the same `documents` again for a second query rather
|
|
22
|
+
than expecting a prior index to still exist.
|
|
23
|
+
|
|
24
|
+
Two modes: semantic (default) pools TF-IDF and dense-bi-encoder
|
|
25
|
+
candidates then re-scores with a cross-encoder if rerank=True --
|
|
26
|
+
catches paraphrases plain TF-IDF misses. exact=True instead does a
|
|
27
|
+
substring/regex search over raw chunk text (no embedding, no
|
|
28
|
+
reranker) -- use this when you already know roughly what wording
|
|
29
|
+
you're looking for and semantic ranking buries it under
|
|
30
|
+
topically-similar-but-wrong chunks.
|
|
31
|
+
|
|
32
|
+
Needs the `rag` extra (pip install dense-evolution[rag]) -- returns an
|
|
33
|
+
actionable "Error: ..." if it isn't installed.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
params (RagSearchInput): documents ([[text, source], ...]), query,
|
|
37
|
+
top, rerank, exact, regex, max_hits, context.
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
str: JSON with `results` -- semantic mode: a list of {rank,
|
|
41
|
+
source, text, score, score_type, cosine}; exact mode: a list of
|
|
42
|
+
{source, chunk_index, match, snippet, start, end}.
|
|
43
|
+
"""
|
|
44
|
+
return json.dumps(await _request("POST", "/api/rag_search", timeout=60.0, json=params.model_dump()), indent=2)
|