dense-evolution 8.2.0__tar.gz → 8.2.1__tar.gz
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.
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/PKG-INFO +8 -2
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/README.md +1 -1
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/__init__.py +4 -2
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/__init__.py +2 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/zne.py +95 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/scf.py +51 -4
- dense_evolution-8.2.1/dense_evolution/utils/mass_decomposition.py +246 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/PKG-INFO +8 -2
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/SOURCES.txt +2 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/requires.txt +8 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/pyproject.toml +38 -2
- dense_evolution-8.2.1/tools/ia_utils/rag.py +236 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/autodiff.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/_engine_imports.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/circuit_chunker.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/core.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/disk_overflow.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/geometry.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/guard.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/chunk/kernels.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/mps.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/backends/statevector.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/chunk.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/compiler.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/diagram.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/gates.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/parser.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/qft.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/random_circuit.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/registry.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/topology.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/trotter.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/circuits/uccsd.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/cli.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/compiler.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/config.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/drawing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/entropy.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/fermions.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/gates.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/harrison_tb.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/healing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/interop/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/interop/qiskit_pennylane.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/measurement.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/healing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/kl_divergence.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/magic_entropy.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/magic_entropy_shadows.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/renyi.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mitigation/stabilizer_renyi_entropy.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/mps.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/assembly.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/basis.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/boys.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/bridge.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/cartesian.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/coulomb.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/differentiable.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/gaussians.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/kinetic.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/libcint_bridge.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/native_hf/overlap.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/coherent_attack.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/cosmic_ray.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/density_matrix_channels.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/differentiable.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/amplitude_damping.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/bitflip.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/combined.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/depolarizing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/ideal.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus/phaseflip.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/kraus_channels.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/noise/oscillating.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/observables.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/parser.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/entropy.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/fermions.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/observables.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/qec.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/spectral.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/physics/states.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qec.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qft.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qmmm/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qmmm/ase_bridge.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qmmm/forces.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qmmm/propagation.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/qmmm/region.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/random_circuit.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/registry.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/simulator.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/solvers/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/solvers/autodiff.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/solvers/harrison_tb.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/solvers/vhd_tb.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/states.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/topology.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/trotter.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/utils/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/utils/drawing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/utils/measurement.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution/vhd_tb.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/dependency_links.txt +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/entry_points.txt +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/dense_evolution.egg-info/top_level.txt +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/license.md +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/research/local_site/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/research/local_site/app/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/research/local_site/app/server.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/setup.cfg +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/_gate_tables.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/band_structure.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/circuit_builder_component.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/circuit_diagram.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/engine.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/graphical_builder.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/hamiltonians.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/mitigation.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/noise_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/qasm_library.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/qmmm.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/state_visuals.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/system_limits.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/vector_healing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/visuals.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/vqe.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/dashboard/core/wormhole.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/ia_utils/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/ia_utils/adversarial_vector_attack.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/ia_utils/vector_healing.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/client.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/config.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/models.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/molecules.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/server.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/chemistry_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/circuit_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/mitigation_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/noise_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/system_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/tools/wormhole_tools.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/utils/__init__.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/utils/cache.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/utils/images.py +0 -0
- {dense_evolution-8.2.0 → dense_evolution-8.2.1}/tools/mcp_server/utils/truncation.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dense-evolution
|
|
3
|
-
Version: 8.2.
|
|
3
|
+
Version: 8.2.1
|
|
4
4
|
Summary: High-performance quantum simulation toolkit -- Statevector/MPS engines with JIT compilation, noise modeling, VQE, QEC, quantum chemistry, and agent-native tooling
|
|
5
5
|
Author-email: Salvatore Pennacchio <jtatopenn@libero.it>
|
|
6
6
|
License: Business Source License 1.1
|
|
@@ -64,6 +64,11 @@ Provides-Extra: qmmm
|
|
|
64
64
|
Requires-Dist: rdkit>=2023.9.1; extra == "qmmm"
|
|
65
65
|
Provides-Extra: ase
|
|
66
66
|
Requires-Dist: ase>=3.22.0; extra == "ase"
|
|
67
|
+
Provides-Extra: rag
|
|
68
|
+
Requires-Dist: scikit-learn>=1.0.0; extra == "rag"
|
|
69
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "rag"
|
|
70
|
+
Provides-Extra: armor
|
|
71
|
+
Requires-Dist: dense-armor>=1.1.18; extra == "armor"
|
|
67
72
|
Provides-Extra: full
|
|
68
73
|
Requires-Dist: cupy-cuda12x>=12.0.0; extra == "full"
|
|
69
74
|
Requires-Dist: streamlit>=1.30.0; extra == "full"
|
|
@@ -80,6 +85,7 @@ Requires-Dist: httpx>=0.27; extra == "full"
|
|
|
80
85
|
Provides-Extra: dev
|
|
81
86
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
82
87
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
88
|
+
Requires-Dist: pytest-env>=1.0.0; extra == "dev"
|
|
83
89
|
Provides-Extra: docs
|
|
84
90
|
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
|
|
85
91
|
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
@@ -343,7 +349,7 @@ If Dense-Evolution is useful in academic work, please cite it via the metadata i
|
|
|
343
349
|
Archived on [Zenodo](https://zenodo.org/):
|
|
344
350
|
|
|
345
351
|
- **Concept DOI** (always resolves to the latest version): [10.5281/zenodo.21855643](https://doi.org/10.5281/zenodo.21855643)
|
|
346
|
-
- **This release (v8.
|
|
352
|
+
- **This release (v8.2.0)**: [10.5281/zenodo.22818319](https://doi.org/10.5281/zenodo.22818319)
|
|
347
353
|
|
|
348
354
|
---
|
|
349
355
|
|
|
@@ -249,7 +249,7 @@ If Dense-Evolution is useful in academic work, please cite it via the metadata i
|
|
|
249
249
|
Archived on [Zenodo](https://zenodo.org/):
|
|
250
250
|
|
|
251
251
|
- **Concept DOI** (always resolves to the latest version): [10.5281/zenodo.21855643](https://doi.org/10.5281/zenodo.21855643)
|
|
252
|
-
- **This release (v8.
|
|
252
|
+
- **This release (v8.2.0)**: [10.5281/zenodo.22818319](https://doi.org/10.5281/zenodo.22818319)
|
|
253
253
|
|
|
254
254
|
---
|
|
255
255
|
|
|
@@ -32,6 +32,7 @@ from .mitigation.zne import (richardson_extrapolate, richardson_amplification_fa
|
|
|
32
32
|
bounded_exponential_extrapolate,
|
|
33
33
|
project_to_physical, uhlmann_fidelity, zne_density_matrix,
|
|
34
34
|
jsd_predictive_zne_density_matrix,
|
|
35
|
+
coherence_predictive_zne_density_matrix,
|
|
35
36
|
richardson_extrapolate_jit, zero_noise_extrapolation_jit,
|
|
36
37
|
polynomial_extrapolate_jit, uhlmann_fidelity_jit, zne_density_matrix_jit)
|
|
37
38
|
from .circuits.diagram import plot_circuit
|
|
@@ -57,7 +58,7 @@ from .physics.qec import (pauli_commutes, compute_syndrome, erasure_aware_decode
|
|
|
57
58
|
blind_minimum_weight_decode, decode_with_erasure_fallback,
|
|
58
59
|
counts_in_intervals_dimension, nearest_coset_decode)
|
|
59
60
|
|
|
60
|
-
__version__ = "8.1
|
|
61
|
+
__version__ = "8.2.1"
|
|
61
62
|
|
|
62
63
|
__all__ = [
|
|
63
64
|
"__version__",
|
|
@@ -87,7 +88,8 @@ __all__ = [
|
|
|
87
88
|
"zero_noise_extrapolation", "polynomial_extrapolate",
|
|
88
89
|
"bounded_exponential_extrapolate",
|
|
89
90
|
"project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
|
|
90
|
-
"jsd_predictive_zne_density_matrix", "
|
|
91
|
+
"jsd_predictive_zne_density_matrix", "coherence_predictive_zne_density_matrix",
|
|
92
|
+
"global_depolarizing_channel", "amplitude_damping_channel",
|
|
91
93
|
"cosmic_ray_burst_profile", "oscillating_p_eff",
|
|
92
94
|
"richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
|
|
93
95
|
"polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit",
|
|
@@ -13,6 +13,7 @@ from .zne import (
|
|
|
13
13
|
bounded_exponential_extrapolate,
|
|
14
14
|
project_to_physical, uhlmann_fidelity, zne_density_matrix,
|
|
15
15
|
jsd_predictive_zne_density_matrix,
|
|
16
|
+
coherence_predictive_zne_density_matrix,
|
|
16
17
|
richardson_extrapolate_jit, zero_noise_extrapolation_jit,
|
|
17
18
|
polynomial_extrapolate_jit, uhlmann_fidelity_jit, zne_density_matrix_jit,
|
|
18
19
|
)
|
|
@@ -36,6 +37,7 @@ __all__ = [
|
|
|
36
37
|
"bounded_exponential_extrapolate",
|
|
37
38
|
"project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
|
|
38
39
|
"jsd_predictive_zne_density_matrix",
|
|
40
|
+
"coherence_predictive_zne_density_matrix",
|
|
39
41
|
"richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
|
|
40
42
|
"polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit",
|
|
41
43
|
"calculate_advanced_sigma", "calculate_phi_ab", "calculate_vettore_dinamico",
|
|
@@ -836,3 +836,98 @@ def _jsd_predictive_zne_density_matrix_core(rho_at_scales: jnp.ndarray, nudge_sc
|
|
|
836
836
|
c3 = 1.0 - nudge_scale * rectified
|
|
837
837
|
extrapolated = (c1 * e_l1 + c2 * e_l2 + c3 * e_l3) / (c1 + c2 + c3)
|
|
838
838
|
return project_to_physical(extrapolated)
|
|
839
|
+
|
|
840
|
+
|
|
841
|
+
def _coherence_l1(rho: jnp.ndarray) -> jnp.ndarray:
|
|
842
|
+
"""l1-norm of coherence (Baumgratz, Cramer & Plenio, Phys. Rev. Lett.
|
|
843
|
+
113, 140401, 2014): the sum of the magnitudes of every off-diagonal
|
|
844
|
+
density-matrix entry. A standard, basis-dependent measure of how much
|
|
845
|
+
quantum coherence a state carries in the computational basis --
|
|
846
|
+
unlike `_js_divergence`, which only ever sees the diagonal
|
|
847
|
+
(populations), this is sensitive to exactly what dephasing destroys."""
|
|
848
|
+
n = rho.shape[0]
|
|
849
|
+
return jnp.sum(jnp.abs(rho) * (1.0 - jnp.eye(n, dtype=rho.dtype)))
|
|
850
|
+
|
|
851
|
+
|
|
852
|
+
def coherence_predictive_zne_density_matrix(rho_at_scales, noise_factors) -> jnp.ndarray:
|
|
853
|
+
"""Density-matrix ZNE with a coherence-informed coefficient nudge --
|
|
854
|
+
the same adaptive-nonlinearity mechanism as
|
|
855
|
+
`jsd_predictive_zne_density_matrix`, but signaled by the l1-norm of
|
|
856
|
+
coherence (`_coherence_l1`) instead of the Jensen-Shannon divergence
|
|
857
|
+
of the diagonal populations.
|
|
858
|
+
|
|
859
|
+
Motivation: `jsd_predictive_zne_density_matrix`'s signal is the
|
|
860
|
+
density-matrix diagonal only. Any purely dephasing-type noise
|
|
861
|
+
(phase-flip, or a coherent Z-axis over-rotation) is diagonal in the
|
|
862
|
+
computational basis -- it moves phase, never populations -- so that
|
|
863
|
+
signal is blind to it BY CONSTRUCTION, not merely weak: verified
|
|
864
|
+
directly in Dense-Evolution-Discovery's
|
|
865
|
+
scripts/jsd_zne_noise_generalization.py, the fidelity delta from the
|
|
866
|
+
classical-JSD nudge is exactly 0.0 at every tested phase-flip noise
|
|
867
|
+
strength and every tested coherent-rotation angle. A quantum-JSD
|
|
868
|
+
variant (von Neumann entropy of the full density matrix instead of
|
|
869
|
+
Shannon entropy of the diagonal) was tried there too and rejected: it
|
|
870
|
+
weakens the already-working amplitude-damping/combined-noise case
|
|
871
|
+
without fixing the coherent-error case, since a smooth deterministic
|
|
872
|
+
function of the noise-scale factor has `jsd_12~=jsd_23` regardless of
|
|
873
|
+
which divergence measures it -- the nonlinearity trigger this whole
|
|
874
|
+
family of methods relies on is structurally near-zero there no
|
|
875
|
+
matter the signal.
|
|
876
|
+
|
|
877
|
+
Validated scope, checked directly rather than assumed universal: real
|
|
878
|
+
effect on phase-flip/dephasing-dominated noise for `base_p<=0.10`;
|
|
879
|
+
NOT validated (and not claimed) for amplitude-damping-dominated noise
|
|
880
|
+
(use `jsd_predictive_zne_density_matrix` there instead) or for
|
|
881
|
+
coherent/deterministic errors (out of reach for this entire family of
|
|
882
|
+
methods, not just this signal -- see above).
|
|
883
|
+
|
|
884
|
+
Verified at 200 independent seeds on phase-flip noise (GHZ(4),
|
|
885
|
+
`base_p=0.05`, K=150 trajectories/scale) before promotion: the nudge
|
|
886
|
+
activates (rectified nonlinearity > 0) on 63/200 seeds (31.5%) --
|
|
887
|
+
among those, 63/63 improve over plain `zne_density_matrix`, mean
|
|
888
|
+
fidelity gain +0.014892, one-sample t-test against zero
|
|
889
|
+
p=1.07e-08, a 20000-resample permutation test finding no resample
|
|
890
|
+
matching or exceeding the observed effect (p<0.00005). Confirmed
|
|
891
|
+
across a noise-level sweep (100 seeds/level, GHZ(4)): significant by
|
|
892
|
+
both tests, 100% win rate among active points, at base_p in (0.03,
|
|
893
|
+
0.05, 0.08, 0.10); NOT significant at base_p=0.15 (p=0.288 t-test,
|
|
894
|
+
p=0.301 permutation) -- a real, honest upper boundary, not a
|
|
895
|
+
universal effect at any noise strength. Confirmed on a second circuit
|
|
896
|
+
family (hardware-efficient VQE-style ansatz, 2 layers) at
|
|
897
|
+
`base_p=0.05`: 69/150 active (46%, a higher activation rate than
|
|
898
|
+
GHZ), 67/69 positive, p=4.4e-06 -- the effect is not GHZ-specific.
|
|
899
|
+
When inactive, reduces EXACTLY to `zne_density_matrix` at 3
|
|
900
|
+
equally-spaced scales (verified: max deviation ~1e-8, floating-point
|
|
901
|
+
noise) -- zero risk in that regime, by construction, the same safety
|
|
902
|
+
property `jsd_predictive_zne_density_matrix` has.
|
|
903
|
+
|
|
904
|
+
Only defined for exactly 3 equally-spaced noise factors (1x, 2x, 3x),
|
|
905
|
+
same restriction and reason as `jsd_predictive_zne_density_matrix`:
|
|
906
|
+
the Lagrange coefficients (3, -3, 1) this nudges are specific to that
|
|
907
|
+
spacing."""
|
|
908
|
+
rho_at_scales = jnp.asarray(rho_at_scales, dtype=jnp.complex128)
|
|
909
|
+
noise_factors = jnp.asarray(noise_factors, dtype=jnp.float64)
|
|
910
|
+
if noise_factors.shape[0] != 3:
|
|
911
|
+
raise NotImplementedError(
|
|
912
|
+
"coherence_predictive_zne_density_matrix is only defined for exactly 3 noise "
|
|
913
|
+
"factors; call zne_density_matrix(...) directly for the plain N-point case."
|
|
914
|
+
)
|
|
915
|
+
return _coherence_predictive_zne_density_matrix_core(rho_at_scales)
|
|
916
|
+
|
|
917
|
+
|
|
918
|
+
def _coherence_predictive_zne_density_matrix_core(rho_at_scales: jnp.ndarray, nudge_scale: float = 0.5) -> jnp.ndarray:
|
|
919
|
+
"""`jax.jit`-traceable core of `coherence_predictive_zne_density_matrix`
|
|
920
|
+
-- `rho_at_scales` already complex128. See the public wrapper's
|
|
921
|
+
docstring for the method and its validation."""
|
|
922
|
+
c1_, c2_, c3_ = (_coherence_l1(rho_at_scales[i]) for i in range(3))
|
|
923
|
+
gap_12 = jnp.abs(c1_ - c2_)
|
|
924
|
+
gap_23 = jnp.abs(c2_ - c3_)
|
|
925
|
+
nonlinearity = (gap_23 - gap_12) / (gap_23 + gap_12 + 1e-12)
|
|
926
|
+
rectified = jnp.maximum(nonlinearity, 0.0)
|
|
927
|
+
|
|
928
|
+
e_l1, e_l2, e_l3 = rho_at_scales[0], rho_at_scales[1], rho_at_scales[2]
|
|
929
|
+
c1 = 3.0 - nudge_scale * rectified
|
|
930
|
+
c2 = -3.0 + 2.0 * nudge_scale * rectified
|
|
931
|
+
c3 = 1.0 - nudge_scale * rectified
|
|
932
|
+
extrapolated = (c1 * e_l1 + c2 * e_l2 + c3 * e_l3) / (c1 + c2 + c3)
|
|
933
|
+
return project_to_physical(extrapolated)
|
|
@@ -90,6 +90,7 @@ import dataclasses
|
|
|
90
90
|
import functools
|
|
91
91
|
|
|
92
92
|
import jax
|
|
93
|
+
import numpy as np
|
|
93
94
|
import jax.numpy as jnp
|
|
94
95
|
|
|
95
96
|
from dense_evolution.config import ensure_x64
|
|
@@ -107,6 +108,7 @@ class HFResult:
|
|
|
107
108
|
orbital_energies: jax.Array
|
|
108
109
|
orbital_coefficients: jax.Array # C, shape (n_basis, n_basis)
|
|
109
110
|
density_matrix: jax.Array
|
|
111
|
+
energy_history: jax.Array # shape (max_iterations,), NaN past n_iterations
|
|
110
112
|
|
|
111
113
|
|
|
112
114
|
def nuclear_repulsion_energy(nuclear_charges: list[float], nuclear_positions: jax.Array) -> jax.Array:
|
|
@@ -231,11 +233,11 @@ def run_scf(
|
|
|
231
233
|
return F, energy
|
|
232
234
|
|
|
233
235
|
def cond_fun(state):
|
|
234
|
-
iteration, _P, _C, _orbital_energies, _energy_prev, converged, _fock_history, _error_history = state
|
|
236
|
+
iteration, _P, _C, _orbital_energies, _energy_prev, converged, _fock_history, _error_history, _energy_history = state
|
|
235
237
|
return jnp.logical_and(jnp.logical_not(converged), iteration < max_iterations)
|
|
236
238
|
|
|
237
239
|
def body_fun(state):
|
|
238
|
-
iteration, P, C_prev, _orbital_energies, energy_prev, _converged, fock_history, error_history = state
|
|
240
|
+
iteration, P, C_prev, _orbital_energies, energy_prev, _converged, fock_history, error_history, energy_history = state
|
|
239
241
|
|
|
240
242
|
F, energy = _fock_and_energy(P)
|
|
241
243
|
error = _diis_error(F, P, S, X)
|
|
@@ -262,7 +264,9 @@ def run_scf(
|
|
|
262
264
|
P_damped = jnp.where(history_count < 2, damping * P_new + (1.0 - damping) * P, P_new)
|
|
263
265
|
P_next = jnp.where(converged, P_new, P_damped)
|
|
264
266
|
|
|
265
|
-
|
|
267
|
+
energy_history = energy_history.at[iteration].set(energy)
|
|
268
|
+
|
|
269
|
+
return (iteration + 1, P_next, C, orbital_energies, energy, converged, fock_history, error_history, energy_history)
|
|
266
270
|
|
|
267
271
|
init_state = (
|
|
268
272
|
jnp.array(0),
|
|
@@ -273,8 +277,9 @@ def run_scf(
|
|
|
273
277
|
jnp.array(False),
|
|
274
278
|
jnp.zeros((diis_dim, n_basis, n_basis), dtype=H_core.dtype),
|
|
275
279
|
jnp.zeros((diis_dim, n_basis, n_basis), dtype=H_core.dtype),
|
|
280
|
+
jnp.full((max_iterations,), jnp.nan, dtype=H_core.dtype),
|
|
276
281
|
)
|
|
277
|
-
iteration, P, C, orbital_energies, _energy_prev, converged, _fh, _eh = jax.lax.while_loop(cond_fun, body_fun, init_state)
|
|
282
|
+
iteration, P, C, orbital_energies, _energy_prev, converged, _fh, _eh, energy_history = jax.lax.while_loop(cond_fun, body_fun, init_state)
|
|
278
283
|
|
|
279
284
|
_F, electronic_energy = _fock_and_energy(P)
|
|
280
285
|
e_nuc = nuclear_repulsion_energy(nuclear_charges, nuclear_positions)
|
|
@@ -288,9 +293,51 @@ def run_scf(
|
|
|
288
293
|
orbital_energies=orbital_energies,
|
|
289
294
|
orbital_coefficients=C,
|
|
290
295
|
density_matrix=P,
|
|
296
|
+
energy_history=energy_history,
|
|
291
297
|
)
|
|
292
298
|
|
|
293
299
|
|
|
300
|
+
def _import_dense_armor_robust_filters():
|
|
301
|
+
try:
|
|
302
|
+
from dense_armor.utility.robust_filters import hampel_filter, tukey_fences
|
|
303
|
+
except ImportError as exc:
|
|
304
|
+
raise ImportError(
|
|
305
|
+
"native_hf.scf.diagnose_convergence needs Dense-Armor (pip install dense-evolution[armor])"
|
|
306
|
+
) from exc
|
|
307
|
+
return hampel_filter, tukey_fences
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def diagnose_convergence(result: HFResult, radius: int = 5, n_sigmas: float = 3.0) -> dict:
|
|
311
|
+
"""Real automatic guard on HFResult.energy_history, instead of trusting
|
|
312
|
+
the final `converged` flag in isolation: Dense-Armor's Hampel filter
|
|
313
|
+
and Tukey fences (dense_evolution.utility.robust_filters -- the sister
|
|
314
|
+
project's own anomaly detectors, Chauvenet/Tukey/Hampel/sigma-clipping,
|
|
315
|
+
validated with 0 false positives on real H2 dissociation-curve
|
|
316
|
+
chemistry) applied to the real per-iteration electronic energy trace.
|
|
317
|
+
|
|
318
|
+
Validated on a real non-converging case (a 28-heavy-atom CASMI26
|
|
319
|
+
fragment, level_shift=0.0): the broken run flags 19-24% of its 200
|
|
320
|
+
iterations as anomalous; the same fragment fixed with level_shift=0.5
|
|
321
|
+
(converges in 55 iterations) flags only ~4% -- background noise, not a
|
|
322
|
+
false-alarm storm. Needs the `armor` extra (pip install
|
|
323
|
+
dense-evolution[armor]) -- Dense-Armor is not a hard dependency of this
|
|
324
|
+
module."""
|
|
325
|
+
hampel_filter, tukey_fences = _import_dense_armor_robust_filters()
|
|
326
|
+
trace = np.asarray(result.energy_history[:result.n_iterations])
|
|
327
|
+
n = max(1, result.n_iterations)
|
|
328
|
+
|
|
329
|
+
_cleaned_h, anomalies_h = hampel_filter(trace, radius=radius, n_sigmas=n_sigmas)
|
|
330
|
+
_cleaned_t, anomalies_t = tukey_fences(trace, radius=len(trace))
|
|
331
|
+
|
|
332
|
+
return {
|
|
333
|
+
"n_iterations": result.n_iterations,
|
|
334
|
+
"n_anomalies_hampel": len(anomalies_h),
|
|
335
|
+
"n_anomalies_tukey": len(anomalies_t),
|
|
336
|
+
"anomaly_fraction_hampel": len(anomalies_h) / n,
|
|
337
|
+
"anomaly_fraction_tukey": len(anomalies_t) / n,
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
|
|
294
341
|
@functools.partial(jax.custom_vjp, nondiff_argnums=(3,))
|
|
295
342
|
def scf_electronic_energy(S: jax.Array, H_core: jax.Array, repulsion: jax.Array, n_electrons: int) -> jax.Array:
|
|
296
343
|
"""The RHF electronic energy (run_scf's own `electronic_energy`, not
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Exact bounded mass-decomposition scoring: given a target mass difference
|
|
3
|
+
and a molecular formula's atom budget, find the closest achievable
|
|
4
|
+
sub-formula mass and its RDBE (degree-of-unsaturation) validity.
|
|
5
|
+
|
|
6
|
+
Promoted from Dense-Evolution-Discovery's CASMI26 spectral-identification
|
|
7
|
+
experiments (real MS/MS mass-spectrometry data, OTRF/Enveda CASMI26
|
|
8
|
+
Kaggle dataset). Real, verified need: testing whether a peak-to-peak mass
|
|
9
|
+
difference in a spectrum corresponds to a chemically real neutral loss
|
|
10
|
+
requires knowing whether SOME integer combination of the precursor's own
|
|
11
|
+
atoms reaches that mass -- a bounded integer subset-sum problem, solved
|
|
12
|
+
here exactly via iterative Minkowski sums (not a continuous relaxation:
|
|
13
|
+
the element-count space is small enough, typically under 10^5 states,
|
|
14
|
+
to enumerate directly).
|
|
15
|
+
|
|
16
|
+
A candidate decomposition is chemically valid only if its Ring-plus-
|
|
17
|
+
Double-Bond-Equivalent (degree of unsaturation) is non-negative:
|
|
18
|
+
|
|
19
|
+
RDBE = 1 + sum_i( count_i * (valence_i - 2) ) / 2
|
|
20
|
+
|
|
21
|
+
(standard organic chemistry -- the same closed-shell constraint already
|
|
22
|
+
used in this package's own isodesmic bond-scission work). Verified on
|
|
23
|
+
real CASMI26 data: without this filter, an unrelated (wrong) molecule's
|
|
24
|
+
formula "explains" 58.5% of real peak-pair differences by coincidence
|
|
25
|
+
alone (mean, n=1952 real spectra) -- RDBE-filtering doesn't remove that
|
|
26
|
+
gap, but does remove chemically impossible matches from both sides,
|
|
27
|
+
so any downstream feature/threshold built on this function is scoring
|
|
28
|
+
real, buildable neutral losses only, not arithmetic coincidences.
|
|
29
|
+
|
|
30
|
+
build_reachable_density_fft below builds the same reachable-mass
|
|
31
|
+
landscape a second way, via the convolution theorem (an FFT instead of
|
|
32
|
+
direct enumeration) with a physically motivated ppm-level Gaussian
|
|
33
|
+
tolerance instead of a fixed absolute one -- same physics, a different
|
|
34
|
+
(and for large state spaces, cheaper) construction.
|
|
35
|
+
"""
|
|
36
|
+
import re
|
|
37
|
+
from typing import Dict, Optional, Tuple
|
|
38
|
+
|
|
39
|
+
import numpy as np
|
|
40
|
+
|
|
41
|
+
__all__ = [
|
|
42
|
+
"parse_formula", "rdbe", "build_reachable_masses", "nearest_reachable_mass",
|
|
43
|
+
"build_reachable_density_fft", "density_at_mass",
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
ATOMIC_MASS: Dict[str, float] = {
|
|
47
|
+
"C": 12.000000, "H": 1.007825, "N": 14.003074, "O": 15.994915,
|
|
48
|
+
"S": 31.972071, "P": 30.973762, "Cl": 34.968853, "F": 18.998403,
|
|
49
|
+
"Br": 78.918338, "I": 126.904473, "Na": 22.989770, "K": 38.963707,
|
|
50
|
+
"Si": 27.976927, "B": 11.009305,
|
|
51
|
+
}
|
|
52
|
+
VALENCE: Dict[str, int] = {
|
|
53
|
+
"C": 4, "H": 1, "N": 3, "O": 2, "S": 2, "P": 3, "Cl": 1, "F": 1,
|
|
54
|
+
"Br": 1, "I": 1, "Na": 1, "K": 1, "Si": 4, "B": 3,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
_FORMULA_TOKEN_RE = re.compile(r"([A-Z][a-z]?)(\d*)")
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def parse_formula(formula: str) -> Dict[str, int]:
|
|
61
|
+
"""Parse a molecular formula string (e.g. "C20H15N3O2") into
|
|
62
|
+
{element: count}. Repeated elements in the string are summed,
|
|
63
|
+
matching how real formula strings from different sources
|
|
64
|
+
occasionally repeat a symbol instead of merging it. Elements not in
|
|
65
|
+
ATOMIC_MASS/VALENCE are kept in the returned dict (parsing doesn't
|
|
66
|
+
know or care which elements downstream functions recognize) --
|
|
67
|
+
rdbe()/build_reachable_masses()/build_reachable_density_fft() each
|
|
68
|
+
skip an unrecognized element explicitly, not this function."""
|
|
69
|
+
counts: Dict[str, int] = {}
|
|
70
|
+
for elem, num in _FORMULA_TOKEN_RE.findall(formula or ""):
|
|
71
|
+
counts[elem] = counts.get(elem, 0) + (int(num) if num else 1)
|
|
72
|
+
return counts
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def rdbe(formula_counts: Dict[str, int]) -> float:
|
|
76
|
+
"""Ring-plus-Double-Bond-Equivalent (degree of unsaturation) of a
|
|
77
|
+
formula: RDBE = 1 + sum(count_i * (valence_i - 2)) / 2. A real,
|
|
78
|
+
non-negative closed-shell molecule/fragment always has RDBE >= 0;
|
|
79
|
+
negative RDBE means the atom counts cannot form any valid closed-
|
|
80
|
+
shell structure (too many univalent atoms for the given carbon/
|
|
81
|
+
nitrogen skeleton) -- unrecognized elements are ignored, not
|
|
82
|
+
counted as an error, since a formula may legitimately include an
|
|
83
|
+
element this module has no valence table entry for."""
|
|
84
|
+
acc = 0
|
|
85
|
+
for elem, count in formula_counts.items():
|
|
86
|
+
val = VALENCE.get(elem)
|
|
87
|
+
if val is not None and count > 0:
|
|
88
|
+
acc += count * (val - 2)
|
|
89
|
+
return 1.0 + acc / 2.0
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_reachable_masses(
|
|
93
|
+
formula_counts: Dict[str, int],
|
|
94
|
+
max_mass: float,
|
|
95
|
+
resolution: float = 0.0005,
|
|
96
|
+
require_rdbe_valid: bool = True,
|
|
97
|
+
max_states: int = 150_000,
|
|
98
|
+
) -> np.ndarray:
|
|
99
|
+
"""All sub-formula masses reachable by using 0..count_i atoms of
|
|
100
|
+
each element in formula_counts (0 <= f <= formula_counts,
|
|
101
|
+
component-wise), sorted ascending. Exact bounded subset-sum via
|
|
102
|
+
iterative Minkowski sums over each element in turn -- correct
|
|
103
|
+
because element counts here are small enough (real molecules,
|
|
104
|
+
typically under a few hundred atoms total) that the reachable set
|
|
105
|
+
never needs more than max_states entries after resolution-binning.
|
|
106
|
+
|
|
107
|
+
require_rdbe_valid=True (default) keeps, for each mass bin, only
|
|
108
|
+
whether ANY chemically valid (RDBE >= 0) combination reaches it --
|
|
109
|
+
the physically meaningful question for neutral-loss matching.
|
|
110
|
+
Pass False to get the pure arithmetic reachable set instead (e.g.
|
|
111
|
+
for a sanity check against a hand-verified subset-sum test case).
|
|
112
|
+
|
|
113
|
+
max_mass bounds the search (usually the precursor's own observed
|
|
114
|
+
mass) so element counts far beyond what's physically possible for
|
|
115
|
+
that precursor are never explored."""
|
|
116
|
+
import pandas as pd # local import: only this function needs it
|
|
117
|
+
|
|
118
|
+
reach = np.array([0.0])
|
|
119
|
+
rdbe2 = np.array([0]) # 2*(RDBE-1); RDBE = 1 + rdbe2/2, starts at rdbe2=0 (empty formula, RDBE=1)
|
|
120
|
+
|
|
121
|
+
for elem, count in formula_counts.items():
|
|
122
|
+
mass = ATOMIC_MASS.get(elem)
|
|
123
|
+
val = VALENCE.get(elem)
|
|
124
|
+
if mass is None or count <= 0:
|
|
125
|
+
continue
|
|
126
|
+
count = min(count, 60) # sanity cap; real formulas here are far below this
|
|
127
|
+
incr_mass = np.arange(0, count + 1) * mass
|
|
128
|
+
incr_rdbe2 = np.arange(0, count + 1) * ((val - 2) if val is not None else 0)
|
|
129
|
+
|
|
130
|
+
reach = np.add.outer(reach, incr_mass).ravel()
|
|
131
|
+
rdbe2 = np.add.outer(rdbe2, incr_rdbe2).ravel()
|
|
132
|
+
|
|
133
|
+
# No emptiness guard needed here: the "use zero atoms of this
|
|
134
|
+
# element" branch (incr_mass's k=0 term) always carries every
|
|
135
|
+
# prior surviving entry through unchanged, and RDBE contribution
|
|
136
|
+
# 0 always satisfies `>= -2` -- so `reach`/`rdbe2` can shrink but
|
|
137
|
+
# never become empty as long as they started non-empty (which
|
|
138
|
+
# the initial reach=[0.0] guarantees for any max_mass >= -1).
|
|
139
|
+
keep = reach <= max_mass + 1.0
|
|
140
|
+
reach, rdbe2 = reach[keep], rdbe2[keep]
|
|
141
|
+
|
|
142
|
+
bins = np.round(reach / resolution).astype(np.int64)
|
|
143
|
+
if require_rdbe_valid:
|
|
144
|
+
valid = rdbe2 >= -2 # RDBE >= 0 <=> 1 + rdbe2/2 >= 0
|
|
145
|
+
bins, reach, rdbe2 = bins[valid], reach[valid], rdbe2[valid]
|
|
146
|
+
df = pd.DataFrame({"bin": bins, "mass": reach, "rdbe2": rdbe2})
|
|
147
|
+
df = df.drop_duplicates(subset="bin")
|
|
148
|
+
if len(df) > max_states:
|
|
149
|
+
df = df.iloc[:: max(1, len(df) // max_states)]
|
|
150
|
+
reach, rdbe2 = df["mass"].to_numpy(), df["rdbe2"].to_numpy()
|
|
151
|
+
|
|
152
|
+
return np.sort(reach)
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def nearest_reachable_mass(target: float, reachable_sorted: np.ndarray) -> Optional[float]:
|
|
156
|
+
"""The single closest value in a (sorted, ascending) reachable-mass
|
|
157
|
+
array to `target`, or None if the array is empty/trivial (only the
|
|
158
|
+
zero-formula placeholder). O(log n) via binary search."""
|
|
159
|
+
if reachable_sorted.size <= 1:
|
|
160
|
+
return None
|
|
161
|
+
i = np.searchsorted(reachable_sorted, target)
|
|
162
|
+
candidates = [k for k in (i - 1, i) if 0 <= k < reachable_sorted.size]
|
|
163
|
+
if not candidates:
|
|
164
|
+
return None
|
|
165
|
+
return float(min((reachable_sorted[k] for k in candidates), key=lambda m: abs(m - target)))
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def build_reachable_density_fft(
|
|
169
|
+
formula_counts: Dict[str, int],
|
|
170
|
+
max_mass: float,
|
|
171
|
+
grid_resolution: float = 0.001,
|
|
172
|
+
relative_tolerance: float = 2e-5,
|
|
173
|
+
) -> Tuple[np.ndarray, np.ndarray]:
|
|
174
|
+
"""Plausibility density over achievable sub-formula masses, via the
|
|
175
|
+
convolution theorem instead of the exact Minkowski-sum enumeration
|
|
176
|
+
in build_reachable_masses.
|
|
177
|
+
|
|
178
|
+
Real precedent, not a novel trick: this is the same FFT technique
|
|
179
|
+
Rockwood & Van Orden use to compute isotope distributions
|
|
180
|
+
(Anal. Chem. 1996, "Ultrahigh-Speed Calculation of Isotope
|
|
181
|
+
Distributions") -- using 0..count_i copies of element i as a spike
|
|
182
|
+
train instead of an isotope-abundance distribution, but the same
|
|
183
|
+
convolution-theorem trick applies identically: the reachable-mass
|
|
184
|
+
landscape of "any combination of atoms up to the formula's budget"
|
|
185
|
+
is the CONVOLUTION of each element's own spike train, and
|
|
186
|
+
convolution in mass-space is a plain pointwise PRODUCT in frequency
|
|
187
|
+
space -- so combining K element types costs K FFTs and one inverse
|
|
188
|
+
FFT, not an enumeration over the (potentially huge) product of
|
|
189
|
+
per-element choices that build_reachable_masses must truncate
|
|
190
|
+
(max_states) for.
|
|
191
|
+
|
|
192
|
+
A single Gaussian broadening is applied ONCE, after combining every
|
|
193
|
+
element -- not per-element -- because real mass-measurement
|
|
194
|
+
uncertainty (ppm-level, `relative_tolerance`) is a property of the
|
|
195
|
+
FINAL observed mass difference, not of each element's contribution
|
|
196
|
+
individually. This replaces build_reachable_masses's arbitrary fixed
|
|
197
|
+
absolute tolerance with a physically motivated relative (ppm) one.
|
|
198
|
+
|
|
199
|
+
Returns (mass_grid, density). density is a plausibility landscape,
|
|
200
|
+
not a normalized probability distribution: many sub-formulas can
|
|
201
|
+
coincide near the same mass and add constructively, so its peak
|
|
202
|
+
height reflects how many/how well-supported combinations reach that
|
|
203
|
+
mass, not a probability that sums to 1.
|
|
204
|
+
|
|
205
|
+
See test_mass_decomposition.py for a direct verification that this
|
|
206
|
+
FFT-based construction agrees with brute-force convolution and with
|
|
207
|
+
build_reachable_masses's own exact enumeration on the same formula.
|
|
208
|
+
"""
|
|
209
|
+
n_points = int(max_mass / grid_resolution) + 2
|
|
210
|
+
combined_fft = None
|
|
211
|
+
|
|
212
|
+
for elem, count in formula_counts.items():
|
|
213
|
+
mass = ATOMIC_MASS.get(elem)
|
|
214
|
+
if mass is None or count <= 0:
|
|
215
|
+
continue
|
|
216
|
+
count = min(count, 60)
|
|
217
|
+
spikes = np.zeros(n_points)
|
|
218
|
+
for k in range(count + 1):
|
|
219
|
+
idx = int(round(k * mass / grid_resolution))
|
|
220
|
+
if idx < n_points:
|
|
221
|
+
spikes[idx] += 1.0
|
|
222
|
+
elem_fft = np.fft.rfft(spikes)
|
|
223
|
+
combined_fft = elem_fft if combined_fft is None else combined_fft * elem_fft
|
|
224
|
+
|
|
225
|
+
mass_grid = np.arange(n_points) * grid_resolution
|
|
226
|
+
if combined_fft is None:
|
|
227
|
+
return mass_grid, np.zeros(n_points)
|
|
228
|
+
|
|
229
|
+
density = np.fft.irfft(combined_fft, n=n_points)
|
|
230
|
+
density = np.maximum(density, 0.0) # guards float round-off, true density is never negative
|
|
231
|
+
|
|
232
|
+
sigma_mass = max(relative_tolerance * max_mass, grid_resolution)
|
|
233
|
+
sigma_points = sigma_mass / grid_resolution
|
|
234
|
+
half = max(int(6 * sigma_points), 1)
|
|
235
|
+
kx = np.arange(-half, half + 1)
|
|
236
|
+
kernel = np.exp(-0.5 * (kx / sigma_points) ** 2)
|
|
237
|
+
kernel /= kernel.sum()
|
|
238
|
+
density = np.convolve(density, kernel, mode="same")
|
|
239
|
+
|
|
240
|
+
return mass_grid, density
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def density_at_mass(mass_grid: np.ndarray, density: np.ndarray, target: float) -> float:
|
|
244
|
+
"""Linearly-interpolated plausibility density at an arbitrary target
|
|
245
|
+
mass (need not land exactly on the grid)."""
|
|
246
|
+
return float(np.interp(target, mass_grid, density, left=0.0, right=0.0))
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dense-evolution
|
|
3
|
-
Version: 8.2.
|
|
3
|
+
Version: 8.2.1
|
|
4
4
|
Summary: High-performance quantum simulation toolkit -- Statevector/MPS engines with JIT compilation, noise modeling, VQE, QEC, quantum chemistry, and agent-native tooling
|
|
5
5
|
Author-email: Salvatore Pennacchio <jtatopenn@libero.it>
|
|
6
6
|
License: Business Source License 1.1
|
|
@@ -64,6 +64,11 @@ Provides-Extra: qmmm
|
|
|
64
64
|
Requires-Dist: rdkit>=2023.9.1; extra == "qmmm"
|
|
65
65
|
Provides-Extra: ase
|
|
66
66
|
Requires-Dist: ase>=3.22.0; extra == "ase"
|
|
67
|
+
Provides-Extra: rag
|
|
68
|
+
Requires-Dist: scikit-learn>=1.0.0; extra == "rag"
|
|
69
|
+
Requires-Dist: sentence-transformers>=2.2.0; extra == "rag"
|
|
70
|
+
Provides-Extra: armor
|
|
71
|
+
Requires-Dist: dense-armor>=1.1.18; extra == "armor"
|
|
67
72
|
Provides-Extra: full
|
|
68
73
|
Requires-Dist: cupy-cuda12x>=12.0.0; extra == "full"
|
|
69
74
|
Requires-Dist: streamlit>=1.30.0; extra == "full"
|
|
@@ -80,6 +85,7 @@ Requires-Dist: httpx>=0.27; extra == "full"
|
|
|
80
85
|
Provides-Extra: dev
|
|
81
86
|
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
82
87
|
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
88
|
+
Requires-Dist: pytest-env>=1.0.0; extra == "dev"
|
|
83
89
|
Provides-Extra: docs
|
|
84
90
|
Requires-Dist: mkdocs>=1.6.0; extra == "docs"
|
|
85
91
|
Requires-Dist: mkdocs-material>=9.5.0; extra == "docs"
|
|
@@ -343,7 +349,7 @@ If Dense-Evolution is useful in academic work, please cite it via the metadata i
|
|
|
343
349
|
Archived on [Zenodo](https://zenodo.org/):
|
|
344
350
|
|
|
345
351
|
- **Concept DOI** (always resolves to the latest version): [10.5281/zenodo.21855643](https://doi.org/10.5281/zenodo.21855643)
|
|
346
|
-
- **This release (v8.
|
|
352
|
+
- **This release (v8.2.0)**: [10.5281/zenodo.22818319](https://doi.org/10.5281/zenodo.22818319)
|
|
347
353
|
|
|
348
354
|
---
|
|
349
355
|
|
|
@@ -109,6 +109,7 @@ dense_evolution/solvers/harrison_tb.py
|
|
|
109
109
|
dense_evolution/solvers/vhd_tb.py
|
|
110
110
|
dense_evolution/utils/__init__.py
|
|
111
111
|
dense_evolution/utils/drawing.py
|
|
112
|
+
dense_evolution/utils/mass_decomposition.py
|
|
112
113
|
dense_evolution/utils/measurement.py
|
|
113
114
|
research/local_site/__init__.py
|
|
114
115
|
research/local_site/app/__init__.py
|
|
@@ -133,6 +134,7 @@ tools/dashboard/core/vqe.py
|
|
|
133
134
|
tools/dashboard/core/wormhole.py
|
|
134
135
|
tools/ia_utils/__init__.py
|
|
135
136
|
tools/ia_utils/adversarial_vector_attack.py
|
|
137
|
+
tools/ia_utils/rag.py
|
|
136
138
|
tools/ia_utils/vector_healing.py
|
|
137
139
|
tools/mcp_server/__init__.py
|
|
138
140
|
tools/mcp_server/client.py
|
|
@@ -5,6 +5,9 @@ scipy>=1.9.0
|
|
|
5
5
|
jax>=0.4.0
|
|
6
6
|
jaxlib>=0.4.0
|
|
7
7
|
|
|
8
|
+
[armor]
|
|
9
|
+
dense-armor>=1.1.18
|
|
10
|
+
|
|
8
11
|
[ase]
|
|
9
12
|
ase>=3.22.0
|
|
10
13
|
|
|
@@ -24,6 +27,7 @@ plotly>=5.0.0
|
|
|
24
27
|
[dev]
|
|
25
28
|
pytest>=7.0.0
|
|
26
29
|
pytest-cov>=4.0.0
|
|
30
|
+
pytest-env>=1.0.0
|
|
27
31
|
|
|
28
32
|
[docs]
|
|
29
33
|
mkdocs>=1.6.0
|
|
@@ -78,5 +82,9 @@ qiskit>=1.0.0
|
|
|
78
82
|
[qmmm]
|
|
79
83
|
rdkit>=2023.9.1
|
|
80
84
|
|
|
85
|
+
[rag]
|
|
86
|
+
scikit-learn>=1.0.0
|
|
87
|
+
sentence-transformers>=2.2.0
|
|
88
|
+
|
|
81
89
|
[stim]
|
|
82
90
|
stim>=1.13.0
|