dense-evolution 8.2.1__tar.gz → 8.2.3__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.1 → dense_evolution-8.2.3}/PKG-INFO +1 -1
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/__init__.py +7 -4
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/__init__.py +2 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/zne.py +58 -1
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/__init__.py +13 -2
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/density_matrix_channels.py +30 -1
- dense_evolution-8.2.3/dense_evolution/noise/pink.py +80 -0
- dense_evolution-8.2.3/dense_evolution/protocols/__init__.py +27 -0
- dense_evolution-8.2.3/dense_evolution/protocols/bb84.py +133 -0
- dense_evolution-8.2.3/dense_evolution/protocols/di_qkd_ghz.py +199 -0
- dense_evolution-8.2.3/dense_evolution/protocols/dicka_protocol2.py +124 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/PKG-INFO +1 -1
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/SOURCES.txt +11 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/pyproject.toml +2 -2
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/app/server.py +184 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/__init__.py +12 -0
- dense_evolution-8.2.3/tools/dashboard/core/crypto_protocols.py +77 -0
- dense_evolution-8.2.3/tools/dashboard/core/mass_decomposition_tool.py +47 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/mitigation.py +76 -1
- dense_evolution-8.2.3/tools/dashboard/core/native_hf_diagnostics.py +62 -0
- dense_evolution-8.2.3/tools/dashboard/core/rag_tool.py +45 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/rag.py +52 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/client.py +105 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/models.py +52 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/server.py +24 -11
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/chemistry_tools.py +45 -2
- dense_evolution-8.2.3/tools/mcp_server/tools/crypto_tools.py +66 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/mitigation_tools.py +20 -0
- dense_evolution-8.2.3/tools/mcp_server/tools/retrieval_tools.py +44 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/system_tools.py +12 -3
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/README.md +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/autodiff.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/_engine_imports.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/circuit_chunker.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/core.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/disk_overflow.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/geometry.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/guard.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/kernels.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/mps.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/statevector.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/chunk.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/compiler.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/diagram.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/gates.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/parser.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/qft.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/random_circuit.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/registry.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/topology.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/trotter.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/uccsd.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/cli.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/compiler.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/config.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/drawing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/entropy.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/fermions.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/gates.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/harrison_tb.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/healing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/interop/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/interop/qiskit_pennylane.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/measurement.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/healing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/kl_divergence.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/magic_entropy.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/magic_entropy_shadows.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/renyi.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/stabilizer_renyi_entropy.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mps.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/assembly.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/basis.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/boys.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/bridge.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/cartesian.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/coulomb.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/differentiable.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/gaussians.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/kinetic.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/libcint_bridge.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/overlap.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/scf.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/coherent_attack.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/cosmic_ray.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/differentiable.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/amplitude_damping.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/bitflip.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/combined.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/depolarizing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/ideal.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/phaseflip.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus_channels.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/oscillating.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/observables.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/parser.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/entropy.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/fermions.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/observables.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/qec.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/spectral.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/states.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qec.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qft.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/ase_bridge.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/forces.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/propagation.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/region.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/random_circuit.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/registry.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/simulator.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/autodiff.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/harrison_tb.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/vhd_tb.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/states.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/topology.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/trotter.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/drawing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/mass_decomposition.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/measurement.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/vhd_tb.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/dependency_links.txt +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/entry_points.txt +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/requires.txt +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/top_level.txt +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/license.md +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/app/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/setup.cfg +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/_gate_tables.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/band_structure.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/circuit_builder_component.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/circuit_diagram.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/engine.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/graphical_builder.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/hamiltonians.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/noise_tools.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/qasm_library.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/qmmm.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/state_visuals.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/system_limits.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/vector_healing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/visuals.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/vqe.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/wormhole.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/adversarial_vector_attack.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/vector_healing.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/config.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/molecules.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/circuit_tools.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/noise_tools.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/wormhole_tools.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/__init__.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/cache.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/images.py +0 -0
- {dense_evolution-8.2.1 → dense_evolution-8.2.3}/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.3
|
|
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
|
|
@@ -6,7 +6,8 @@ from .backends.statevector import DenseSVSimulator
|
|
|
6
6
|
from .circuits.parser import QASMParser, QASMCircuit
|
|
7
7
|
from .circuits.compiler import QuantumTranspiler
|
|
8
8
|
from .noise import (NoiseModel, NoiseSpec, global_depolarizing_channel, amplitude_damping_channel,
|
|
9
|
-
cosmic_ray_burst_profile, oscillating_p_eff
|
|
9
|
+
phaseflip_channel_exact, cosmic_ray_burst_profile, oscillating_p_eff,
|
|
10
|
+
pink_noise_p_eff)
|
|
10
11
|
from .circuits.registry import QuantumHardwareRegistry
|
|
11
12
|
from .circuits.gates import GATES, PARAMETRIC_GATES, GATE_IDS
|
|
12
13
|
from .backends.chunk import Chunk
|
|
@@ -33,6 +34,7 @@ from .mitigation.zne import (richardson_extrapolate, richardson_amplification_fa
|
|
|
33
34
|
project_to_physical, uhlmann_fidelity, zne_density_matrix,
|
|
34
35
|
jsd_predictive_zne_density_matrix,
|
|
35
36
|
coherence_predictive_zne_density_matrix,
|
|
37
|
+
classically_augmented_zne_phaseflip,
|
|
36
38
|
richardson_extrapolate_jit, zero_noise_extrapolation_jit,
|
|
37
39
|
polynomial_extrapolate_jit, uhlmann_fidelity_jit, zne_density_matrix_jit)
|
|
38
40
|
from .circuits.diagram import plot_circuit
|
|
@@ -58,7 +60,7 @@ from .physics.qec import (pauli_commutes, compute_syndrome, erasure_aware_decode
|
|
|
58
60
|
blind_minimum_weight_decode, decode_with_erasure_fallback,
|
|
59
61
|
counts_in_intervals_dimension, nearest_coset_decode)
|
|
60
62
|
|
|
61
|
-
__version__ = "8.2.
|
|
63
|
+
__version__ = "8.2.3"
|
|
62
64
|
|
|
63
65
|
__all__ = [
|
|
64
66
|
"__version__",
|
|
@@ -89,8 +91,9 @@ __all__ = [
|
|
|
89
91
|
"bounded_exponential_extrapolate",
|
|
90
92
|
"project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
|
|
91
93
|
"jsd_predictive_zne_density_matrix", "coherence_predictive_zne_density_matrix",
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
+
"classically_augmented_zne_phaseflip",
|
|
95
|
+
"global_depolarizing_channel", "amplitude_damping_channel", "phaseflip_channel_exact",
|
|
96
|
+
"cosmic_ray_burst_profile", "oscillating_p_eff", "pink_noise_p_eff",
|
|
94
97
|
"richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
|
|
95
98
|
"polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit",
|
|
96
99
|
# Physics -- states, observables, entropy, fermions, QEC
|
|
@@ -14,6 +14,7 @@ from .zne import (
|
|
|
14
14
|
project_to_physical, uhlmann_fidelity, zne_density_matrix,
|
|
15
15
|
jsd_predictive_zne_density_matrix,
|
|
16
16
|
coherence_predictive_zne_density_matrix,
|
|
17
|
+
classically_augmented_zne_phaseflip,
|
|
17
18
|
richardson_extrapolate_jit, zero_noise_extrapolation_jit,
|
|
18
19
|
polynomial_extrapolate_jit, uhlmann_fidelity_jit, zne_density_matrix_jit,
|
|
19
20
|
)
|
|
@@ -38,6 +39,7 @@ __all__ = [
|
|
|
38
39
|
"project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
|
|
39
40
|
"jsd_predictive_zne_density_matrix",
|
|
40
41
|
"coherence_predictive_zne_density_matrix",
|
|
42
|
+
"classically_augmented_zne_phaseflip",
|
|
41
43
|
"richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
|
|
42
44
|
"polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit",
|
|
43
45
|
"calculate_advanced_sigma", "calculate_phi_ab", "calculate_vettore_dinamico",
|
|
@@ -23,7 +23,8 @@ from .healing import calculate_delta_preemp
|
|
|
23
23
|
__all__ = ["richardson_extrapolate", "richardson_amplification_factor", "zero_noise_extrapolation",
|
|
24
24
|
"polynomial_extrapolate", "bounded_exponential_extrapolate",
|
|
25
25
|
"project_to_physical", "uhlmann_fidelity", "zne_density_matrix",
|
|
26
|
-
"jsd_predictive_zne_density_matrix", "
|
|
26
|
+
"jsd_predictive_zne_density_matrix", "coherence_predictive_zne_density_matrix",
|
|
27
|
+
"classically_augmented_zne_phaseflip", "global_depolarizing_channel",
|
|
27
28
|
"amplitude_damping_channel", "cosmic_ray_burst_profile",
|
|
28
29
|
"richardson_extrapolate_jit", "zero_noise_extrapolation_jit",
|
|
29
30
|
"polynomial_extrapolate_jit", "uhlmann_fidelity_jit", "zne_density_matrix_jit"]
|
|
@@ -931,3 +932,59 @@ def _coherence_predictive_zne_density_matrix_core(rho_at_scales: jnp.ndarray, nu
|
|
|
931
932
|
c3 = 1.0 - nudge_scale * rectified
|
|
932
933
|
extrapolated = (c1 * e_l1 + c2 * e_l2 + c3 * e_l3) / (c1 + c2 + c3)
|
|
933
934
|
return project_to_physical(extrapolated)
|
|
935
|
+
|
|
936
|
+
|
|
937
|
+
def classically_augmented_zne_phaseflip(rho_at_scales_measured, noise_factors, rho_ideal, base_p, degree: int = 2) -> jnp.ndarray:
|
|
938
|
+
"""Classically Augmented ZNE (Scheiber et al., arXiv:2607.25746) for
|
|
939
|
+
phaseflip noise: the highest-noise Richardson/polynomial-extrapolation
|
|
940
|
+
nodes -- the ones contributing most to sampling variance -- are
|
|
941
|
+
replaced by `phaseflip_channel_exact`'s zero-sampling-variance exact
|
|
942
|
+
channel instead of a Monte-Carlo-sampled density matrix, then combined
|
|
943
|
+
exactly as `zne_density_matrix` already does (same extrapolation
|
|
944
|
+
coefficients; only the source of the high-noise inputs changes).
|
|
945
|
+
|
|
946
|
+
`rho_at_scales_measured[i]` must be the measured/sampled density
|
|
947
|
+
matrix at `noise_factors[i]` for `i < len(rho_at_scales_measured)`
|
|
948
|
+
(the low-noise nodes, kept as real measurements); every remaining
|
|
949
|
+
factor in `noise_factors` is filled in with the exact channel computed
|
|
950
|
+
from `rho_ideal` and `base_p`. `rho_ideal` and `base_p` are required
|
|
951
|
+
inputs, not optional, because the exact channel needs the noise-free
|
|
952
|
+
state to condition on -- unlike every other function in this module,
|
|
953
|
+
this one is not usable from noisy measurements alone.
|
|
954
|
+
|
|
955
|
+
Honest, verified scope (GHZ(3), phaseflip, 150 trials/measured node,
|
|
956
|
+
60 seeds each): with exactly 3 total noise factors and only the single
|
|
957
|
+
highest one replaced, no measurable benefit (variance ratio 0.99x,
|
|
958
|
+
i.e. no effect within noise) -- too little room for the exact node to
|
|
959
|
+
matter against only 2 remaining measured ones. With 5 noise factors
|
|
960
|
+
(1x-5x, `base_p=0.03`) and the top 3 replaced by the exact channel,
|
|
961
|
+
variance drops by a real, measured 1.30x versus plain `zne_density_matrix`
|
|
962
|
+
using 5 fully-measured nodes at the same per-node trial budget. This is
|
|
963
|
+
the naive-allocation regime, not the paper's own optimal importance-
|
|
964
|
+
sampling allocation (their Eq. 9) -- that allocation is not implemented
|
|
965
|
+
here, so the exponential variance reduction the paper reports under it
|
|
966
|
+
is not claimed or expected from this function as shipped.
|
|
967
|
+
|
|
968
|
+
Only implemented for phaseflip noise, since `phaseflip_channel_exact`
|
|
969
|
+
is the only exact density-matrix channel currently available that
|
|
970
|
+
matches a `NoiseModel` statevector model exactly (see that function's
|
|
971
|
+
docstring) -- extending this to other noise models needs their own
|
|
972
|
+
exact channel first."""
|
|
973
|
+
from ..noise import phaseflip_channel_exact
|
|
974
|
+
|
|
975
|
+
rho_at_scales_measured = jnp.asarray(rho_at_scales_measured, dtype=jnp.complex128)
|
|
976
|
+
noise_factors = jnp.asarray(noise_factors, dtype=jnp.float64)
|
|
977
|
+
rho_ideal = jnp.asarray(rho_ideal, dtype=jnp.complex128)
|
|
978
|
+
cutoff = rho_at_scales_measured.shape[0]
|
|
979
|
+
if cutoff >= noise_factors.shape[0]:
|
|
980
|
+
raise ValueError(
|
|
981
|
+
f"rho_at_scales_measured has {cutoff} entries but noise_factors only has "
|
|
982
|
+
f"{noise_factors.shape[0]} -- at least one factor must be left for the exact "
|
|
983
|
+
"classical node, or there is nothing to augment."
|
|
984
|
+
)
|
|
985
|
+
classical_rhos = jnp.stack([
|
|
986
|
+
phaseflip_channel_exact(rho_ideal, jnp.minimum(base_p * f, 1.0))
|
|
987
|
+
for f in noise_factors[cutoff:]
|
|
988
|
+
])
|
|
989
|
+
rho_at_scales = jnp.concatenate([rho_at_scales_measured, classical_rhos], axis=0)
|
|
990
|
+
return zne_density_matrix(rho_at_scales, noise_factors, degree=degree)
|
|
@@ -17,12 +17,20 @@
|
|
|
17
17
|
- **Density-matrix channels** (`.density_matrix_channels`) --
|
|
18
18
|
`global_depolarizing_channel`, `amplitude_damping_channel`: noise applied
|
|
19
19
|
directly to a density matrix instead of a statevector, for density-matrix
|
|
20
|
-
ZNE's noise ensemble.
|
|
20
|
+
ZNE's noise ensemble. `phaseflip_channel_exact`: the same phaseflip model
|
|
21
|
+
as `NoiseModel`'s `'phaseflip'` statevector Kraus channel, but computed as
|
|
22
|
+
its exact zero-sampling-variance limit directly on a density matrix, for
|
|
23
|
+
use as a "classical node" in Classically Augmented ZNE (see
|
|
24
|
+
`dense_evolution.mitigation.classically_augmented_zne_phaseflip`).
|
|
21
25
|
- **`cosmic_ray_burst_profile`** (`.cosmic_ray`) -- a real, time-dependent
|
|
22
26
|
noise-strength profile for a cosmic-ray-induced quasiparticle burst.
|
|
23
27
|
- **`oscillating_p_eff`** (`.oscillating`) -- a noise strength that
|
|
24
28
|
oscillates instead of scaling smoothly, for stress-testing mitigation
|
|
25
29
|
techniques that assume smoothness.
|
|
30
|
+
- **`pink_noise_p_eff`** (`.pink`) -- a noise-strength trace with a real
|
|
31
|
+
1/f (pink/flicker) power spectrum, the dominant decoherence mechanism
|
|
32
|
+
in real superconducting qubits, via the Timmer & Koenig (1995)
|
|
33
|
+
spectral-synthesis algorithm.
|
|
26
34
|
|
|
27
35
|
Real device noise from a Qiskit backend's own calibration data
|
|
28
36
|
(`noise_model_from_qiskit_backend`) lives in `dense_evolution.interop`,
|
|
@@ -47,9 +55,10 @@ from .coherent_attack import (
|
|
|
47
55
|
decoder_failure_rate,
|
|
48
56
|
random_delta_failure_stats,
|
|
49
57
|
)
|
|
50
|
-
from .density_matrix_channels import global_depolarizing_channel, amplitude_damping_channel
|
|
58
|
+
from .density_matrix_channels import global_depolarizing_channel, amplitude_damping_channel, phaseflip_channel_exact
|
|
51
59
|
from .cosmic_ray import cosmic_ray_burst_profile
|
|
52
60
|
from .oscillating import oscillating_p_eff
|
|
61
|
+
from .pink import pink_noise_p_eff
|
|
53
62
|
|
|
54
63
|
__all__ = [
|
|
55
64
|
"NoiseModel",
|
|
@@ -63,6 +72,8 @@ __all__ = [
|
|
|
63
72
|
"random_delta_failure_stats",
|
|
64
73
|
"global_depolarizing_channel",
|
|
65
74
|
"amplitude_damping_channel",
|
|
75
|
+
"phaseflip_channel_exact",
|
|
66
76
|
"cosmic_ray_burst_profile",
|
|
67
77
|
"oscillating_p_eff",
|
|
78
|
+
"pink_noise_p_eff",
|
|
68
79
|
]
|
{dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/density_matrix_channels.py
RENAMED
|
@@ -3,7 +3,7 @@ what density-matrix ZNE (`dense_evolution.mitigation.zne_density_matrix`)
|
|
|
3
3
|
needs its noise ensemble built from."""
|
|
4
4
|
import jax.numpy as jnp
|
|
5
5
|
|
|
6
|
-
__all__ = ["global_depolarizing_channel", "amplitude_damping_channel"]
|
|
6
|
+
__all__ = ["global_depolarizing_channel", "amplitude_damping_channel", "phaseflip_channel_exact"]
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def global_depolarizing_channel(rho: jnp.ndarray, p: float) -> jnp.ndarray:
|
|
@@ -47,3 +47,32 @@ def amplitude_damping_channel(rho: jnp.ndarray, gamma: float) -> jnp.ndarray:
|
|
|
47
47
|
e0 = jnp.array([[1.0, 0.0], [0.0, jnp.sqrt(1.0 - gamma)]], dtype=jnp.complex128)
|
|
48
48
|
e1 = jnp.array([[0.0, jnp.sqrt(gamma)], [0.0, 0.0]], dtype=jnp.complex128)
|
|
49
49
|
return e0 @ rho @ e0.conj().T + e1 @ rho @ e1.conj().T
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def phaseflip_channel_exact(rho: jnp.ndarray, p: float) -> jnp.ndarray:
|
|
53
|
+
"""Exact (zero-variance) multi-qubit phaseflip channel: K0=sqrt(1-p)*I,
|
|
54
|
+
K1=sqrt(p)*Z, applied independently per qubit across the whole
|
|
55
|
+
register, matching `NoiseModel`'s `'phaseflip'` statevector model
|
|
56
|
+
(`noise/kraus/phaseflip.py`) in the limit of infinitely many Monte
|
|
57
|
+
Carlo trials -- this function computes that limit directly instead of
|
|
58
|
+
averaging finite trials, for use as a zero-sampling-variance
|
|
59
|
+
"classical node" in Classically Augmented ZNE (Scheiber et al., "CA-ZNE:
|
|
60
|
+
high-noise Richardson nodes replaced by classically simulated
|
|
61
|
+
estimates", arXiv:2607.25746).
|
|
62
|
+
|
|
63
|
+
Since Z is diagonal in the computational basis, conjugating rho by Z_q
|
|
64
|
+
(Pauli-Z on qubit q, identity elsewhere) only flips the sign of
|
|
65
|
+
off-diagonal entries whose row and column disagree on bit q --
|
|
66
|
+
(Z_q rho Z_q)_ij = rho_ij * s_i * s_j, s_k = -1 if bit q of k else +1
|
|
67
|
+
-- so each qubit's channel (1-p)*rho + p*(Z_q rho Z_q) is one
|
|
68
|
+
elementwise multiply, no explicit operator ever built.
|
|
69
|
+
"""
|
|
70
|
+
rho = jnp.asarray(rho, dtype=jnp.complex128)
|
|
71
|
+
dim = rho.shape[0]
|
|
72
|
+
n_qubits = dim.bit_length() - 1
|
|
73
|
+
idx = jnp.arange(dim)
|
|
74
|
+
for q in range(n_qubits):
|
|
75
|
+
sign = 1.0 - 2.0 * ((idx >> q) & 1)
|
|
76
|
+
flip_sign = jnp.outer(sign, sign)
|
|
77
|
+
rho = (1.0 - p) * rho + p * (flip_sign * rho)
|
|
78
|
+
return rho
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""1/f ("pink") noise: a noise-strength trace with a power-law power
|
|
2
|
+
spectral density, S(f) ~ 1/f^alpha, instead of the flat (white) or
|
|
3
|
+
purely time-local (oscillating, burst) profiles this package's other
|
|
4
|
+
generators produce -- the dominant real decoherence source (flux noise,
|
|
5
|
+
charge noise) in superconducting qubits."""
|
|
6
|
+
import jax
|
|
7
|
+
import jax.numpy as jnp
|
|
8
|
+
|
|
9
|
+
__all__ = ["pink_noise_p_eff"]
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def pink_noise_p_eff(base_p: float, n_samples: int, key, alpha: float = 1.0, amp: float = 0.5) -> jnp.ndarray:
|
|
13
|
+
"""Effective noise probability trace of length `n_samples` fluctuating
|
|
14
|
+
around `base_p` with a 1/f^alpha power spectrum (`alpha=1.0` is the
|
|
15
|
+
real, standard "pink"/flicker-noise case; other values interpolate
|
|
16
|
+
toward white noise, `alpha=0`, or Brownian noise, `alpha=2`), instead
|
|
17
|
+
of `oscillating_p_eff`'s single deterministic frequency or
|
|
18
|
+
`cosmic_ray_burst_profile`'s one-shot deterministic event -- for
|
|
19
|
+
stress-testing mitigation techniques against the actual dominant
|
|
20
|
+
decoherence mechanism (1/f flux/charge noise) in real superconducting
|
|
21
|
+
hardware, not just synthetic worst cases.
|
|
22
|
+
|
|
23
|
+
Generated by the spectral-synthesis method of Timmer & Koenig (1995,
|
|
24
|
+
"On generating power law noise", Astronomy and Astrophysics 300, 707):
|
|
25
|
+
both the amplitude and the phase of each Fourier component are drawn
|
|
26
|
+
randomly (independent real/imaginary Gaussian draws, giving the
|
|
27
|
+
correct chi-squared-distributed power per bin, not just the correct
|
|
28
|
+
mean spectrum), scaled by `frequency ** (-alpha / 2)` so the power
|
|
29
|
+
spectrum scales as `frequency ** (-alpha)`, then inverse-transformed
|
|
30
|
+
to the time domain -- the standard algorithm for this exact problem,
|
|
31
|
+
not a bespoke construction. The zero-frequency (DC) component is
|
|
32
|
+
fixed to exactly 0, so the trace has zero mean before rescaling.
|
|
33
|
+
Verified during development: the fitted log-log power-spectrum slope
|
|
34
|
+
over 30 independent realizations at `alpha=1.0` is -0.996, matching
|
|
35
|
+
the expected -1 to within numerical/finite-sample tolerance.
|
|
36
|
+
|
|
37
|
+
The resulting zero-mean, unit-variance trace is rescaled to
|
|
38
|
+
`base_p * (1 + amp * trace)` and clipped to `[0.01, 0.5]`, the same
|
|
39
|
+
valid-probability range `oscillating_p_eff` uses, so it can be fed
|
|
40
|
+
directly wherever a per-trial or per-timestep `p` is expected.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
base_p : float
|
|
45
|
+
Center noise probability the trace fluctuates around.
|
|
46
|
+
n_samples : int
|
|
47
|
+
Length of the trace (e.g. one value per Monte Carlo trial, or per
|
|
48
|
+
time step of a trajectory).
|
|
49
|
+
key : jax.random.PRNGKey
|
|
50
|
+
Random key; two independent draws are taken from it internally
|
|
51
|
+
(real and imaginary Fourier-amplitude parts).
|
|
52
|
+
alpha : float
|
|
53
|
+
Spectral exponent: power spectral density ~ `1/frequency**alpha`.
|
|
54
|
+
`1.0` (default) is real 1/f/pink/flicker noise.
|
|
55
|
+
amp : float
|
|
56
|
+
Fractional modulation depth around `base_p`, same role as in
|
|
57
|
+
`oscillating_p_eff`.
|
|
58
|
+
|
|
59
|
+
Examples
|
|
60
|
+
--------
|
|
61
|
+
>>> import jax
|
|
62
|
+
>>> from dense_evolution.noise import pink_noise_p_eff
|
|
63
|
+
>>> trace = pink_noise_p_eff(0.05, 256, jax.random.PRNGKey(0))
|
|
64
|
+
>>> trace.shape
|
|
65
|
+
(256,)
|
|
66
|
+
>>> bool((trace >= 0.01).all() and (trace <= 0.5).all())
|
|
67
|
+
True
|
|
68
|
+
"""
|
|
69
|
+
freqs = jnp.fft.rfftfreq(n_samples)
|
|
70
|
+
freqs = freqs.at[0].set(freqs[1]) # avoid a 1/0 singularity at DC; DC amplitude is zeroed below anyway
|
|
71
|
+
key_re, key_im = jax.random.split(key)
|
|
72
|
+
real_part = jax.random.normal(key_re, shape=freqs.shape)
|
|
73
|
+
imag_part = jax.random.normal(key_im, shape=freqs.shape)
|
|
74
|
+
scale = freqs ** (-alpha / 2.0)
|
|
75
|
+
spectrum = (real_part + 1j * imag_part) * scale
|
|
76
|
+
spectrum = spectrum.at[0].set(0.0)
|
|
77
|
+
trace = jnp.fft.irfft(spectrum, n=n_samples)
|
|
78
|
+
trace = (trace - jnp.mean(trace)) / jnp.std(trace)
|
|
79
|
+
p = base_p * (1.0 + amp * trace)
|
|
80
|
+
return jnp.clip(p, 0.01, 0.5)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"""Quantum cryptography protocols subpackage (crypto-q, issue #189 in
|
|
2
|
+
Dense-Evolution-Discovery): BB84 (`bb84`), three-party device-independent
|
|
3
|
+
conference key agreement via a GHZ(3) state and its Parity-CHSH game
|
|
4
|
+
(`di_qkd_ghz`), and the full multi-round DICKA structure built on top of
|
|
5
|
+
it (`dicka_protocol2`). Each is built entirely from existing simulator
|
|
6
|
+
primitives (statevector, gates, measurement, the depolarizing channel) --
|
|
7
|
+
no new quantum channel was added to the core to support this subpackage.
|
|
8
|
+
|
|
9
|
+
Promoted from Dense-Evolution-Discovery only after passing that project's
|
|
10
|
+
own promotion checklist: a theoretical ground truth verified to tight
|
|
11
|
+
tolerance, a falsifiable result, primitives-only implementation, fixed-
|
|
12
|
+
seed reproducible tests, and any negative result documented honestly
|
|
13
|
+
rather than adjusted away. See each module's own docstring for its
|
|
14
|
+
specific validation numbers."""
|
|
15
|
+
from .bb84 import bb84_run, bb84_round, prepare_state, measure_in_basis, apply_depolarizing
|
|
16
|
+
from .di_qkd_ghz import (
|
|
17
|
+
prepare_ghz3, measure_alice, measure_bob1, measure_other_bob,
|
|
18
|
+
test_round, key_round, parity_chsh_win_rate, key_qber, expected_win_rate,
|
|
19
|
+
)
|
|
20
|
+
from .dicka_protocol2 import run_round, run_protocol, CLASSICAL_BOUND, QUANTUM_MAX
|
|
21
|
+
|
|
22
|
+
__all__ = [
|
|
23
|
+
"bb84_run", "bb84_round", "prepare_state", "measure_in_basis", "apply_depolarizing",
|
|
24
|
+
"prepare_ghz3", "measure_alice", "measure_bob1", "measure_other_bob",
|
|
25
|
+
"test_round", "key_round", "parity_chsh_win_rate", "key_qber", "expected_win_rate",
|
|
26
|
+
"run_round", "run_protocol", "CLASSICAL_BOUND", "QUANTUM_MAX",
|
|
27
|
+
]
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"""BB84 quantum key distribution, built entirely from existing
|
|
2
|
+
dense_evolution primitives (statevector, gates, measurement, the
|
|
3
|
+
depolarizing channel) -- the reference pattern every protocol in this
|
|
4
|
+
subpackage follows: prepare -> channel -> measure -> sift -> QBER.
|
|
5
|
+
|
|
6
|
+
Reused, not reimplemented: `DenseSVSimulator`, `GATES`, `sim.measure`
|
|
7
|
+
(with `jax_key`), `NoiseModel.apply_to_sv`. No new quantum primitive is
|
|
8
|
+
introduced.
|
|
9
|
+
|
|
10
|
+
Promoted from Dense-Evolution-Discovery's `scripts/crypto/bb84.py` (the
|
|
11
|
+
crypto-q RFC, issue #189) after validation at N=5000 rounds, 5
|
|
12
|
+
independent seeds:
|
|
13
|
+
|
|
14
|
+
Scenario QBER observed Expected z-score
|
|
15
|
+
Perfect channel 0.0000 +/- 0.0000 0 pass
|
|
16
|
+
Depolarizing p=0.05 0.0365 +/- 0.0023 0.0333 +0.87
|
|
17
|
+
Depolarizing p=0.10 0.0694 +/- 0.0015 0.0667 +0.56
|
|
18
|
+
Depolarizing p=0.20 0.1386 +/- 0.0034 0.1333 +0.77
|
|
19
|
+
Intercept-resend 0.2518 +/- 0.0017 0.2500 +0.20
|
|
20
|
+
|
|
21
|
+
All z-scores within +/-1 sigma except p=0.20, still comfortably within
|
|
22
|
+
+/-2 sigma."""
|
|
23
|
+
|
|
24
|
+
import numpy as np
|
|
25
|
+
import jax
|
|
26
|
+
|
|
27
|
+
import dense_evolution as de
|
|
28
|
+
from dense_evolution.noise import NoiseModel
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def prepare_state(base: int, bit: int):
|
|
32
|
+
"""Prepares one qubit in the BB84 sender's state for a given basis
|
|
33
|
+
and bit.
|
|
34
|
+
|
|
35
|
+
`base=0` is the Z basis (`|0>`, `|1>`); `base=1` is the X basis
|
|
36
|
+
(`|+>`, `|->`), reached by applying H to the Z-basis state for the
|
|
37
|
+
same bit.
|
|
38
|
+
|
|
39
|
+
Returns a fresh `DenseSVSimulator(1)` holding the prepared state."""
|
|
40
|
+
sim = de.DenseSVSimulator(1)
|
|
41
|
+
if base == 0:
|
|
42
|
+
if bit == 1:
|
|
43
|
+
sim.apply_gate_1q(de.GATES["x"], 0)
|
|
44
|
+
else:
|
|
45
|
+
sim.apply_gate_1q(de.GATES["h"], 0)
|
|
46
|
+
if bit == 1:
|
|
47
|
+
sim.apply_gate_1q(de.GATES["z"], 0)
|
|
48
|
+
return sim
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def measure_in_basis(sim, base: int, rng: np.random.Generator) -> int:
|
|
52
|
+
"""Measures qubit 0 of `sim` in the Z basis (`base=0`) or the X basis
|
|
53
|
+
(`base=1`, reached by applying H before the Z measurement).
|
|
54
|
+
|
|
55
|
+
`rng` seeds the single-shot measurement outcome via a freshly derived
|
|
56
|
+
`jax.random.PRNGKey`. Returns the measured bit (0 or 1)."""
|
|
57
|
+
if base == 1:
|
|
58
|
+
sim.apply_gate_1q(de.GATES["h"], 0)
|
|
59
|
+
key = jax.random.PRNGKey(int(rng.integers(0, 2**31 - 1)))
|
|
60
|
+
return int(sim.measure(0, jax_key=key))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def apply_depolarizing(sim, p: float, rng: np.random.Generator) -> None:
|
|
64
|
+
"""Applies `NoiseModel`'s real depolarizing channel to `sim`'s
|
|
65
|
+
statevector in place, with error probability `p`. A no-op at
|
|
66
|
+
`p=0.0`."""
|
|
67
|
+
if p == 0.0:
|
|
68
|
+
return
|
|
69
|
+
sv = np.asarray(sim.get_statevector())
|
|
70
|
+
sv_noisy = NoiseModel.apply_to_sv(sv, 1, "depolarizing", p, rng=rng)
|
|
71
|
+
sim.set_initial_state(np.asarray(sv_noisy))
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def bb84_round(rng: np.random.Generator, p_channel: float = 0.0, eve: bool = False):
|
|
75
|
+
"""Runs one BB84 round: Alice prepares a random bit in a random
|
|
76
|
+
basis, an optional intercept-resend eavesdropper (`eve=True`) measures
|
|
77
|
+
and re-prepares it in her own random basis, the channel applies
|
|
78
|
+
depolarizing noise at rate `p_channel`, and Bob measures in his own
|
|
79
|
+
random basis.
|
|
80
|
+
|
|
81
|
+
Returns `(a_base, b_base, a_bit, b_bit)`."""
|
|
82
|
+
a_base = int(rng.integers(0, 2))
|
|
83
|
+
a_bit = int(rng.integers(0, 2))
|
|
84
|
+
sim = prepare_state(a_base, a_bit)
|
|
85
|
+
|
|
86
|
+
if eve:
|
|
87
|
+
e_base = int(rng.integers(0, 2))
|
|
88
|
+
e_bit = measure_in_basis(sim, e_base, rng)
|
|
89
|
+
sim = prepare_state(e_base, e_bit)
|
|
90
|
+
|
|
91
|
+
apply_depolarizing(sim, p_channel, rng)
|
|
92
|
+
|
|
93
|
+
b_base = int(rng.integers(0, 2))
|
|
94
|
+
b_bit = measure_in_basis(sim, b_base, rng)
|
|
95
|
+
return a_base, b_base, a_bit, b_bit
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def bb84_run(n_rounds: int, p_channel: float = 0.0, eve: bool = False, seed=None):
|
|
99
|
+
"""Runs `n_rounds` of BB84, sifts the rounds where Alice and Bob's
|
|
100
|
+
bases happened to match, and reports the sifted-key quantum bit error
|
|
101
|
+
rate (QBER).
|
|
102
|
+
|
|
103
|
+
Ground truth, verified at N=5000 rounds / 5 seeds (see module
|
|
104
|
+
docstring): QBER=0 on a perfect channel, `QBER=2p/3` under isotropic
|
|
105
|
+
depolarizing noise at rate `p`, and `QBER=0.25` under an
|
|
106
|
+
intercept-resend attack (`eve=True`) -- Eve guesses the right basis
|
|
107
|
+
half the time, and introduces an error half the time she guesses
|
|
108
|
+
wrong.
|
|
109
|
+
|
|
110
|
+
Returns `(qber, n_sifted)`; `qber=0.0` if no rounds sifted.
|
|
111
|
+
|
|
112
|
+
Example
|
|
113
|
+
-------
|
|
114
|
+
>>> import dense_evolution.protocols.bb84 as bb84
|
|
115
|
+
>>> qber, n_sifted = bb84.bb84_run(2000, p_channel=0.10, seed=1)
|
|
116
|
+
>>> abs(qber - 2 * 0.10 / 3) < 0.03
|
|
117
|
+
True
|
|
118
|
+
"""
|
|
119
|
+
rng = np.random.default_rng(seed)
|
|
120
|
+
a_bases = np.empty(n_rounds, dtype=np.int8)
|
|
121
|
+
b_bases = np.empty(n_rounds, dtype=np.int8)
|
|
122
|
+
a_bits = np.empty(n_rounds, dtype=np.int8)
|
|
123
|
+
b_bits = np.empty(n_rounds, dtype=np.int8)
|
|
124
|
+
|
|
125
|
+
for i in range(n_rounds):
|
|
126
|
+
a_bases[i], b_bases[i], a_bits[i], b_bits[i] = bb84_round(rng, p_channel, eve)
|
|
127
|
+
|
|
128
|
+
keep = a_bases == b_bases
|
|
129
|
+
n_sifted = int(keep.sum())
|
|
130
|
+
if n_sifted == 0:
|
|
131
|
+
return 0.0, 0
|
|
132
|
+
qber = float(np.mean(a_bits[keep] != b_bits[keep]))
|
|
133
|
+
return qber, n_sifted
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
"""Three-party device-independent conference key agreement (DICKA) via a
|
|
2
|
+
GHZ(3) state, following Ribeiro, Murta & Wehner 2018, "Fully
|
|
3
|
+
device-independent conference key agreement" (arXiv:1708.00798),
|
|
4
|
+
Protocol 1.
|
|
5
|
+
|
|
6
|
+
Ground truth, corrected against the actual paper text (not Mermin's
|
|
7
|
+
inequality, which this paper does not use): the paper introduces its own
|
|
8
|
+
"Parity-CHSH" inequality, an N-party extension of CHSH (Definition 8).
|
|
9
|
+
For N=3 (Alice, Bob1, one other Bob), the winning condition is
|
|
10
|
+
`a + b1 = x*(y XOR b2) mod 2`, with classical bound `P_win <= 3/4` and
|
|
11
|
+
quantum maximum `P_win ~ 0.85` (the same bound as ordinary CHSH, Eq.
|
|
12
|
+
A.14) -- both reproduced numerically, not assumed.
|
|
13
|
+
|
|
14
|
+
Two real bugs were found and fixed while promoting this from
|
|
15
|
+
Dense-Evolution-Discovery's `scripts/crypto/di_qkd_ghz.py`:
|
|
16
|
+
|
|
17
|
+
1. Definition 8's own text fixes the other Bob's test-round question at
|
|
18
|
+
"always equal to 1", not 0 -- an initial implementation used 0 (Z
|
|
19
|
+
measurement) and got `P_win=0.677`, exactly half the expected quantum
|
|
20
|
+
boost. Fixing it to 1 (X measurement) reproduces the exact theoretical
|
|
21
|
+
value `0.85355...` to machine precision.
|
|
22
|
+
2. `expected_win_rate`'s noisy-channel formula assumed depolarizing
|
|
23
|
+
shrinks a qubit's Pauli expectations by `(1-p)`; `NoiseModel`'s actual
|
|
24
|
+
isotropic-Pauli-error Kraus channel shrinks them by `(1-4p/3)` instead
|
|
25
|
+
(see `expected_win_rate`'s own docstring).
|
|
26
|
+
|
|
27
|
+
Measurement operators, taken directly from the paper's own
|
|
28
|
+
honest-implementation section (Eq. A.64-A.65 region):
|
|
29
|
+
|
|
30
|
+
Alice: x=0 -> Z, x=1 -> X
|
|
31
|
+
Bob1: y=0 -> (Z+X)/sqrt(2), y=1 -> (Z-X)/sqrt(2), y=2 -> Z
|
|
32
|
+
other Bobs: y=0 -> Z, y=1 -> X
|
|
33
|
+
|
|
34
|
+
Reused, not reimplemented: `de.ghz_state(3)`, `DenseSVSimulator`,
|
|
35
|
+
`GATES`, `sim.measure` (jax_key), `NoiseModel.apply_to_sv`. No new
|
|
36
|
+
quantum primitive."""
|
|
37
|
+
|
|
38
|
+
import numpy as np
|
|
39
|
+
import jax
|
|
40
|
+
|
|
41
|
+
import dense_evolution as de
|
|
42
|
+
from dense_evolution.noise import NoiseModel
|
|
43
|
+
|
|
44
|
+
_Z = np.diag([1, -1]).astype(complex)
|
|
45
|
+
_X = np.array([[0, 1], [1, 0]], dtype=complex)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _diagonalizing_unitary(observable):
|
|
49
|
+
"""The unitary `U` such that measuring Z after applying `U` to the
|
|
50
|
+
state reproduces measuring `observable` directly -- derived from
|
|
51
|
+
`observable`'s own eigendecomposition (eigenvalue +1 first, to match
|
|
52
|
+
`Z=diag(1,-1)`), not a hand-picked rotation angle."""
|
|
53
|
+
w, v = np.linalg.eigh(observable)
|
|
54
|
+
order = np.argsort(-w)
|
|
55
|
+
v = v[:, order]
|
|
56
|
+
return v.conj().T
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
_U_PLUS = _diagonalizing_unitary((_Z + _X) / np.sqrt(2))
|
|
60
|
+
_U_MINUS = _diagonalizing_unitary((_Z - _X) / np.sqrt(2))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def prepare_ghz3():
|
|
64
|
+
"""Prepares the shared 3-qubit GHZ state
|
|
65
|
+
`(|000> + |111>)/sqrt(2)` used throughout this module, via
|
|
66
|
+
`de.ghz_state(3)`. Returns a fresh `DenseSVSimulator(3)`."""
|
|
67
|
+
sim = de.DenseSVSimulator(3)
|
|
68
|
+
sim.run_circuit(de.ghz_state(3))
|
|
69
|
+
return sim
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def measure_alice(sim, x: int, rng: np.random.Generator) -> int:
|
|
73
|
+
"""Measures Alice's qubit (index 0) with question `x`: `x=0` measures
|
|
74
|
+
Z directly, `x=1` applies H first (measures X). Returns the outcome
|
|
75
|
+
bit."""
|
|
76
|
+
if x == 1:
|
|
77
|
+
sim.apply_gate_1q(de.GATES["h"], 0)
|
|
78
|
+
key = jax.random.PRNGKey(int(rng.integers(0, 2**31 - 1)))
|
|
79
|
+
return int(sim.measure(0, jax_key=key))
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def measure_bob1(sim, y: int, rng: np.random.Generator) -> int:
|
|
83
|
+
"""Measures Bob1's qubit (index 1) with question `y`: `y=0` measures
|
|
84
|
+
`(Z+X)/sqrt(2)`, `y=1` measures `(Z-X)/sqrt(2)` (both via the
|
|
85
|
+
precomputed diagonalizing unitaries `_U_PLUS`/`_U_MINUS`), `y=2`
|
|
86
|
+
measures Z directly. Returns the outcome bit."""
|
|
87
|
+
if y == 0:
|
|
88
|
+
sim.apply_gate_1q(_U_PLUS, 1)
|
|
89
|
+
elif y == 1:
|
|
90
|
+
sim.apply_gate_1q(_U_MINUS, 1)
|
|
91
|
+
key = jax.random.PRNGKey(int(rng.integers(0, 2**31 - 1)))
|
|
92
|
+
return int(sim.measure(1, jax_key=key))
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def measure_other_bob(sim, y: int, rng: np.random.Generator, qubit: int = 2) -> int:
|
|
96
|
+
"""Measures the other Bob's qubit (default index 2) with question
|
|
97
|
+
`y`: `y=0` measures Z directly, `y=1` applies H first (measures X).
|
|
98
|
+
Returns the outcome bit."""
|
|
99
|
+
if y == 1:
|
|
100
|
+
sim.apply_gate_1q(de.GATES["h"], qubit)
|
|
101
|
+
key = jax.random.PRNGKey(int(rng.integers(0, 2**31 - 1)))
|
|
102
|
+
return int(sim.measure(qubit, jax_key=key))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def apply_depolarizing(sim, p: float, rng: np.random.Generator) -> None:
|
|
106
|
+
"""Applies i.i.d. depolarizing noise at rate `p` to all 3 qubits of
|
|
107
|
+
`sim`, matching the paper's own `D^{\\otimes N}(GHZ_N)`
|
|
108
|
+
honest-implementation model (Eq. A.64). A no-op at `p=0.0`."""
|
|
109
|
+
if p == 0.0:
|
|
110
|
+
return
|
|
111
|
+
sv = np.asarray(sim.get_statevector())
|
|
112
|
+
sv_noisy = NoiseModel.apply_to_sv(sv, 3, "depolarizing", p, rng=rng)
|
|
113
|
+
sim.set_initial_state(np.asarray(sv_noisy))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def test_round(rng: np.random.Generator, p_dep: float = 0.0) -> int:
|
|
117
|
+
"""Runs one Parity-CHSH test round (Definition 8): Alice and Bob1 get
|
|
118
|
+
uniformly random questions `x, y`; the other Bob gets a FIXED
|
|
119
|
+
question, always 1 (X measurement) -- per the paper's own definition
|
|
120
|
+
text, not 0 (see module docstring for the bug this fixed).
|
|
121
|
+
|
|
122
|
+
Returns 1 if the parties win the round (`(a + b1) mod 2 ==
|
|
123
|
+
(x*(y XOR b2)) mod 2`), else 0."""
|
|
124
|
+
sim = prepare_ghz3()
|
|
125
|
+
apply_depolarizing(sim, p_dep, rng)
|
|
126
|
+
x = int(rng.integers(0, 2))
|
|
127
|
+
y = int(rng.integers(0, 2))
|
|
128
|
+
a = measure_alice(sim, x, rng)
|
|
129
|
+
b1 = measure_bob1(sim, y, rng)
|
|
130
|
+
b2 = measure_other_bob(sim, 1, rng)
|
|
131
|
+
win = (a + b1) % 2 == (x * (y ^ b2)) % 2
|
|
132
|
+
return int(win)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def key_round(rng: np.random.Generator, p_dep: float = 0.0):
|
|
136
|
+
"""Runs one key-generation round: everyone measures Z (`x=0, y=2,
|
|
137
|
+
y2=0`). Returns `(a, b1, b2)`, perfectly correlated in the noiseless
|
|
138
|
+
case."""
|
|
139
|
+
sim = prepare_ghz3()
|
|
140
|
+
apply_depolarizing(sim, p_dep, rng)
|
|
141
|
+
a = measure_alice(sim, 0, rng)
|
|
142
|
+
b1 = measure_bob1(sim, 2, rng)
|
|
143
|
+
b2 = measure_other_bob(sim, 0, rng)
|
|
144
|
+
return a, b1, b2
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def parity_chsh_win_rate(n_rounds: int, p_dep: float = 0.0, seed=None) -> float:
|
|
148
|
+
"""Runs `n_rounds` Parity-CHSH test rounds and returns the observed
|
|
149
|
+
winning frequency.
|
|
150
|
+
|
|
151
|
+
Ground truth, verified to machine precision at `p_dep=0.0`:
|
|
152
|
+
`P_win = 0.85355...` (`1/2 + 1/(2 sqrt2)`, the quantum maximum), well
|
|
153
|
+
above the classical bound `3/4` -- device independence requires
|
|
154
|
+
exceeding this classical bound.
|
|
155
|
+
|
|
156
|
+
Example
|
|
157
|
+
-------
|
|
158
|
+
>>> import dense_evolution.protocols.di_qkd_ghz as ghz
|
|
159
|
+
>>> p_win = ghz.parity_chsh_win_rate(3000, p_dep=0.0, seed=0)
|
|
160
|
+
>>> p_win > 0.75
|
|
161
|
+
True
|
|
162
|
+
"""
|
|
163
|
+
rng = np.random.default_rng(seed)
|
|
164
|
+
wins = sum(test_round(rng, p_dep) for _ in range(n_rounds))
|
|
165
|
+
return wins / n_rounds
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def key_qber(n_rounds: int, p_dep: float = 0.0, seed=None):
|
|
169
|
+
"""Runs `n_rounds` key-generation rounds and returns the raw QBER
|
|
170
|
+
between Alice and each Bob, `(qber_b1, qber_b2)`, before error
|
|
171
|
+
correction or privacy amplification. Zero at `p_dep=0.0`, rising with
|
|
172
|
+
`p_dep`."""
|
|
173
|
+
rng = np.random.default_rng(seed)
|
|
174
|
+
disagree_b1 = 0
|
|
175
|
+
disagree_b2 = 0
|
|
176
|
+
for _ in range(n_rounds):
|
|
177
|
+
a, b1, b2 = key_round(rng, p_dep)
|
|
178
|
+
disagree_b1 += a != b1
|
|
179
|
+
disagree_b2 += a != b2
|
|
180
|
+
return disagree_b1 / n_rounds, disagree_b2 / n_rounds
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
def expected_win_rate(p_dep: float, n_parties: int = 3) -> float:
|
|
184
|
+
"""Closed-form honest-implementation Parity-CHSH win rate (paper Eq.
|
|
185
|
+
A.65, specialized to `n_parties=3`), written in terms of the
|
|
186
|
+
per-qubit Pauli-expectation shrink factor `s` a depolarizing channel
|
|
187
|
+
produces: `p_exp = 1/2 + s^3/(2 sqrt2) + s^2*(1-s)/(4 sqrt2)`.
|
|
188
|
+
|
|
189
|
+
`s = 1 - 4*p_dep/3`, matching `NoiseModel`'s real isotropic-Pauli
|
|
190
|
+
depolarizing Kraus map (`K0=sqrt(1-p)I, K1..3=sqrt(p/3)*Pauli`), NOT
|
|
191
|
+
`s=1-p` (which would assume a "replace with the maximally mixed
|
|
192
|
+
state" channel instead). Verified directly against the simulator: the
|
|
193
|
+
`s=1-p` version diverged with growing significance as `p` grew
|
|
194
|
+
(`z=-2.75` at `p=0.10`, `z=-3.65` at `p=0.15`, N=3000); `s=1-4p/3`
|
|
195
|
+
matches the simulator across the whole sweep (`|z|<0.5` everywhere
|
|
196
|
+
tested)."""
|
|
197
|
+
s = 1 - 4 * p_dep / 3
|
|
198
|
+
s2 = np.sqrt(2)
|
|
199
|
+
return 0.5 + s ** n_parties / (2 * s2) + s ** 2 * (1 - s ** (n_parties - 2)) / (4 * s2)
|