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.
Files changed (168) hide show
  1. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/PKG-INFO +1 -1
  2. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/__init__.py +7 -4
  3. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/__init__.py +2 -0
  4. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/zne.py +58 -1
  5. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/__init__.py +13 -2
  6. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/density_matrix_channels.py +30 -1
  7. dense_evolution-8.2.3/dense_evolution/noise/pink.py +80 -0
  8. dense_evolution-8.2.3/dense_evolution/protocols/__init__.py +27 -0
  9. dense_evolution-8.2.3/dense_evolution/protocols/bb84.py +133 -0
  10. dense_evolution-8.2.3/dense_evolution/protocols/di_qkd_ghz.py +199 -0
  11. dense_evolution-8.2.3/dense_evolution/protocols/dicka_protocol2.py +124 -0
  12. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/PKG-INFO +1 -1
  13. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/SOURCES.txt +11 -0
  14. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/pyproject.toml +2 -2
  15. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/app/server.py +184 -0
  16. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/__init__.py +12 -0
  17. dense_evolution-8.2.3/tools/dashboard/core/crypto_protocols.py +77 -0
  18. dense_evolution-8.2.3/tools/dashboard/core/mass_decomposition_tool.py +47 -0
  19. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/mitigation.py +76 -1
  20. dense_evolution-8.2.3/tools/dashboard/core/native_hf_diagnostics.py +62 -0
  21. dense_evolution-8.2.3/tools/dashboard/core/rag_tool.py +45 -0
  22. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/rag.py +52 -0
  23. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/client.py +105 -0
  24. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/models.py +52 -0
  25. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/server.py +24 -11
  26. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/chemistry_tools.py +45 -2
  27. dense_evolution-8.2.3/tools/mcp_server/tools/crypto_tools.py +66 -0
  28. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/mitigation_tools.py +20 -0
  29. dense_evolution-8.2.3/tools/mcp_server/tools/retrieval_tools.py +44 -0
  30. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/system_tools.py +12 -3
  31. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/README.md +0 -0
  32. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/autodiff.py +0 -0
  33. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/__init__.py +0 -0
  34. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/__init__.py +0 -0
  35. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/_engine_imports.py +0 -0
  36. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/circuit_chunker.py +0 -0
  37. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/core.py +0 -0
  38. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/disk_overflow.py +0 -0
  39. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/geometry.py +0 -0
  40. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/guard.py +0 -0
  41. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/chunk/kernels.py +0 -0
  42. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/mps.py +0 -0
  43. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/backends/statevector.py +0 -0
  44. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/chunk.py +0 -0
  45. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/__init__.py +0 -0
  46. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/compiler.py +0 -0
  47. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/diagram.py +0 -0
  48. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/gates.py +0 -0
  49. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/parser.py +0 -0
  50. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/qft.py +0 -0
  51. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/random_circuit.py +0 -0
  52. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/registry.py +0 -0
  53. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/topology.py +0 -0
  54. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/trotter.py +0 -0
  55. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/circuits/uccsd.py +0 -0
  56. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/cli.py +0 -0
  57. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/compiler.py +0 -0
  58. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/config.py +0 -0
  59. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/drawing.py +0 -0
  60. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/entropy.py +0 -0
  61. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/fermions.py +0 -0
  62. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/gates.py +0 -0
  63. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/harrison_tb.py +0 -0
  64. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/healing.py +0 -0
  65. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/interop/__init__.py +0 -0
  66. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/interop/qiskit_pennylane.py +0 -0
  67. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/measurement.py +0 -0
  68. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/healing.py +0 -0
  69. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/kl_divergence.py +0 -0
  70. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/magic_entropy.py +0 -0
  71. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/magic_entropy_shadows.py +0 -0
  72. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/renyi.py +0 -0
  73. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mitigation/stabilizer_renyi_entropy.py +0 -0
  74. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/mps.py +0 -0
  75. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/__init__.py +0 -0
  76. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/assembly.py +0 -0
  77. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/basis.py +0 -0
  78. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/boys.py +0 -0
  79. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/bridge.py +0 -0
  80. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/cartesian.py +0 -0
  81. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/coulomb.py +0 -0
  82. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/differentiable.py +0 -0
  83. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/gaussians.py +0 -0
  84. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/kinetic.py +0 -0
  85. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/libcint_bridge.py +0 -0
  86. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/overlap.py +0 -0
  87. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/native_hf/scf.py +0 -0
  88. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/coherent_attack.py +0 -0
  89. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/cosmic_ray.py +0 -0
  90. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/differentiable.py +0 -0
  91. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/__init__.py +0 -0
  92. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/amplitude_damping.py +0 -0
  93. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/bitflip.py +0 -0
  94. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/combined.py +0 -0
  95. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/depolarizing.py +0 -0
  96. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/ideal.py +0 -0
  97. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus/phaseflip.py +0 -0
  98. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/kraus_channels.py +0 -0
  99. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/noise/oscillating.py +0 -0
  100. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/observables.py +0 -0
  101. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/parser.py +0 -0
  102. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/__init__.py +0 -0
  103. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/entropy.py +0 -0
  104. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/fermions.py +0 -0
  105. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/observables.py +0 -0
  106. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/qec.py +0 -0
  107. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/spectral.py +0 -0
  108. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/physics/states.py +0 -0
  109. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qec.py +0 -0
  110. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qft.py +0 -0
  111. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/__init__.py +0 -0
  112. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/ase_bridge.py +0 -0
  113. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/forces.py +0 -0
  114. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/propagation.py +0 -0
  115. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/qmmm/region.py +0 -0
  116. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/random_circuit.py +0 -0
  117. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/registry.py +0 -0
  118. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/simulator.py +0 -0
  119. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/__init__.py +0 -0
  120. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/autodiff.py +0 -0
  121. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/harrison_tb.py +0 -0
  122. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/solvers/vhd_tb.py +0 -0
  123. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/states.py +0 -0
  124. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/topology.py +0 -0
  125. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/trotter.py +0 -0
  126. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/__init__.py +0 -0
  127. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/drawing.py +0 -0
  128. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/mass_decomposition.py +0 -0
  129. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/utils/measurement.py +0 -0
  130. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution/vhd_tb.py +0 -0
  131. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/dependency_links.txt +0 -0
  132. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/entry_points.txt +0 -0
  133. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/requires.txt +0 -0
  134. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/dense_evolution.egg-info/top_level.txt +0 -0
  135. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/license.md +0 -0
  136. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/__init__.py +0 -0
  137. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/research/local_site/app/__init__.py +0 -0
  138. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/setup.cfg +0 -0
  139. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/_gate_tables.py +0 -0
  140. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/band_structure.py +0 -0
  141. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/circuit_builder_component.py +0 -0
  142. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/circuit_diagram.py +0 -0
  143. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/engine.py +0 -0
  144. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/graphical_builder.py +0 -0
  145. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/hamiltonians.py +0 -0
  146. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/noise_tools.py +0 -0
  147. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/qasm_library.py +0 -0
  148. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/qmmm.py +0 -0
  149. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/state_visuals.py +0 -0
  150. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/system_limits.py +0 -0
  151. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/vector_healing.py +0 -0
  152. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/visuals.py +0 -0
  153. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/vqe.py +0 -0
  154. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/dashboard/core/wormhole.py +0 -0
  155. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/__init__.py +0 -0
  156. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/adversarial_vector_attack.py +0 -0
  157. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/ia_utils/vector_healing.py +0 -0
  158. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/__init__.py +0 -0
  159. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/config.py +0 -0
  160. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/molecules.py +0 -0
  161. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/__init__.py +0 -0
  162. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/circuit_tools.py +0 -0
  163. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/noise_tools.py +0 -0
  164. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/tools/wormhole_tools.py +0 -0
  165. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/__init__.py +0 -0
  166. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/cache.py +0 -0
  167. {dense_evolution-8.2.1 → dense_evolution-8.2.3}/tools/mcp_server/utils/images.py +0 -0
  168. {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.1
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.1"
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
- "global_depolarizing_channel", "amplitude_damping_channel",
93
- "cosmic_ray_burst_profile", "oscillating_p_eff",
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", "global_depolarizing_channel",
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
  ]
@@ -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)