|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| use sha2::{Sha256, Digest};
|
| use serde::{Serialize, Deserialize};
|
| use crate::qec::{StabilizerTableau, apply_hadamard, apply_cnot, estimate_distance, check_commutativity};
|
|
|
|
|
|
|
|
|
|
|
| #[derive(Clone, Debug, Serialize, Deserialize)]
|
| pub struct LogicalQubit {
|
| pub alpha_re: f64,
|
| pub alpha_im: f64,
|
| pub beta_re: f64,
|
| pub beta_im: f64,
|
| pub label: String,
|
| }
|
|
|
| impl LogicalQubit {
|
| pub fn new(alpha_re: f64, alpha_im: f64, beta_re: f64, beta_im: f64) -> Self {
|
| LogicalQubit {
|
| alpha_re, alpha_im, beta_re, beta_im,
|
| label: String::new(),
|
| }
|
| }
|
|
|
|
|
| pub fn zero() -> Self { Self::new(1.0, 0.0, 0.0, 0.0) }
|
|
|
|
|
| pub fn one() -> Self { Self::new(0.0, 0.0, 1.0, 0.0) }
|
|
|
|
|
| pub fn plus() -> Self {
|
| let s = 1.0 / 2f64.sqrt();
|
| Self::new(s, 0.0, s, 0.0)
|
| }
|
|
|
|
|
| pub fn norm_sq(&self) -> f64 {
|
| self.alpha_re.powi(2) + self.alpha_im.powi(2)
|
| + self.beta_re.powi(2) + self.beta_im.powi(2)
|
| }
|
|
|
| pub fn is_normalized(&self) -> bool {
|
| (self.norm_sq() - 1.0).abs() < 1e-10
|
| }
|
| }
|
|
|
|
|
|
|
| #[derive(Clone, Debug, Serialize, Deserialize)]
|
| pub struct EncodedQubit {
|
| pub logical: LogicalQubit,
|
| pub n_physical: usize,
|
| pub code_distance: u32,
|
| pub stabilizers: Vec<Vec<u8>>,
|
| pub free_energy: f64,
|
| pub error_bound: f64,
|
| pub i4_invariant: f64,
|
| pub worm_seal: String,
|
| }
|
|
|
| impl EncodedQubit {
|
|
|
|
|
| pub fn verify_i4(&self, candidate: f64) -> bool {
|
| (self.i4_invariant - candidate).abs() < 1e-8
|
| }
|
|
|
|
|
| pub fn is_protected(&self, threshold: f64) -> bool {
|
| self.error_bound < threshold
|
| }
|
| }
|
|
|
|
|
|
|
| pub struct QubitMultiplier {
|
|
|
| pub beta: f64,
|
|
|
| pub size_nm: f64,
|
|
|
| pub xi_nm: f64,
|
|
|
| pub gap_j: f64,
|
|
|
| pub temp_k: f64,
|
| }
|
|
|
| impl QubitMultiplier {
|
| pub fn new() -> Self {
|
| QubitMultiplier {
|
| beta: 1.0,
|
| size_nm: 10_000.0,
|
| xi_nm: 50.0,
|
| gap_j: 1.38e-23,
|
| temp_k: 0.01,
|
| }
|
| }
|
|
|
|
|
|
|
|
|
|
|
| fn build_stabilizers(&self, n: usize) -> StabilizerTableau {
|
| if n < 3 {
|
| return StabilizerTableau::new(n);
|
| }
|
|
|
| let n_gen = n - 1;
|
| let mut gens = Vec::with_capacity(n_gen);
|
| for i in 0..n_gen {
|
| let mut row = vec![0u8; 2 * n];
|
| row[n + i] = 1;
|
| row[n + i + 1] = 1;
|
| gens.push(row);
|
| }
|
|
|
| let mut x_row = vec![0u8; 2 * n];
|
| for i in 0..n {
|
| x_row[i] = 1;
|
| }
|
| gens.push(x_row);
|
| StabilizerTableau::from_generators(gens)
|
| }
|
|
|
|
|
|
|
| fn error_bound(&self) -> f64 {
|
| let kb = 1.380649e-23_f64;
|
| let term1 = (-self.size_nm / (10.0 * self.xi_nm)).exp();
|
| let term2 = (-self.gap_j / (5.0 * kb * self.temp_k)).exp();
|
| term1 + term2
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| fn free_energy(&self, tableau: &StabilizerTableau) -> f64 {
|
| let n_gen = tableau.matrix.nrows();
|
| if n_gen == 0 { return 0.0; }
|
|
|
|
|
| let weights: Vec<f64> = (0..n_gen).map(|i| {
|
| let row = tableau.row(i);
|
| let n = tableau.n_qubits;
|
| (0..n).filter(|&j| row[j] != 0 || row[n + j] != 0).count() as f64
|
| }).collect();
|
|
|
|
|
| let exp_betas: Vec<f64> = weights.iter().map(|&w| (-self.beta * w).exp()).collect();
|
| let z: f64 = exp_betas.iter().sum();
|
| if z < 1e-300 { return 0.0; }
|
|
|
| let probs: Vec<f64> = exp_betas.iter().map(|&e| e / z).collect();
|
|
|
|
|
| let exp_h: f64 = probs.iter().zip(weights.iter()).map(|(p, w)| p * w).sum();
|
|
|
|
|
| let s_vn: f64 = probs.iter()
|
| .filter(|&&p| p > 1e-300)
|
| .map(|&p| -p * p.ln())
|
| .sum();
|
|
|
|
|
| exp_h - (1.0 / self.beta) * s_vn
|
| }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| fn i4_invariant(&self, d: u32, n: usize, free_energy: f64, error_bound: f64) -> f64 {
|
| let alpha = d as f64;
|
| let beta = n as f64;
|
| let tr_xy = free_energy;
|
| let n_x = error_bound;
|
| let n_y = error_bound;
|
| let tr_adj = free_energy * error_bound;
|
|
|
| let term1 = (alpha * beta - tr_xy).powi(2);
|
| let term2 = 4.0 * (alpha * n_x + beta * n_y - tr_adj);
|
| term1 - term2
|
| }
|
|
|
|
|
|
|
|
|
| fn extract_syndromes(&self, tableau: &StabilizerTableau, error: &[u8]) -> Vec<usize> {
|
| (0..tableau.matrix.nrows())
|
| .filter(|&i| {
|
| let gen = tableau.row(i);
|
| !check_commutativity(&gen, error)
|
| })
|
| .collect()
|
| }
|
|
|
|
|
| fn compute_seal(&self, logical: &LogicalQubit, n: usize, d: u32, f: f64, i4: f64) -> String {
|
| let mut h = Sha256::new();
|
| h.update(b"QUBIT_MULTIPLY:");
|
| h.update(logical.alpha_re.to_le_bytes());
|
| h.update(logical.beta_re.to_le_bytes());
|
| h.update(n.to_le_bytes());
|
| h.update(d.to_le_bytes());
|
| h.update(f.to_le_bytes());
|
| h.update(i4.to_le_bytes());
|
| format!("{:x}", h.finalize())[..16].to_string()
|
| }
|
|
|
|
|
|
|
| pub fn multiply(&self, logical: &LogicalQubit, n_physical: usize) -> Result<EncodedQubit, String> {
|
| if !logical.is_normalized() {
|
| return Err(format!("Qubit not normalized: |α|²+|β|² = {:.6}", logical.norm_sq()));
|
| }
|
| if n_physical < 3 {
|
| return Err("Need at least 3 physical qubits for error protection".into());
|
| }
|
|
|
|
|
| let tableau = self.build_stabilizers(n_physical);
|
| let d = estimate_distance(&tableau);
|
| let stabs: Vec<Vec<u8>> = (0..tableau.matrix.nrows())
|
| .map(|i| tableau.row(i))
|
| .collect();
|
|
|
|
|
| let error_bound = self.error_bound();
|
|
|
|
|
| let free_energy = self.free_energy(&tableau);
|
|
|
|
|
| let i4 = self.i4_invariant(d, n_physical, free_energy, error_bound);
|
|
|
|
|
| let seal = self.compute_seal(logical, n_physical, d, free_energy, i4);
|
|
|
| Ok(EncodedQubit {
|
| logical: logical.clone(),
|
| n_physical,
|
| code_distance: d,
|
| stabilizers: stabs,
|
| free_energy,
|
| error_bound,
|
| i4_invariant: i4,
|
| worm_seal: seal,
|
| })
|
| }
|
|
|
|
|
|
|
| pub fn decode(&self, encoded: &EncodedQubit, received: &[u8]) -> DecodeResult {
|
| let tableau = self.build_stabilizers(encoded.n_physical);
|
| let syndromes = self.extract_syndromes(&tableau, received);
|
| let correctable = syndromes.len() <= (encoded.code_distance as usize / 2);
|
|
|
|
|
| let i4_check = self.i4_invariant(
|
| encoded.code_distance,
|
| encoded.n_physical,
|
| encoded.free_energy,
|
| encoded.error_bound,
|
| );
|
| let i4_intact = encoded.verify_i4(i4_check);
|
|
|
|
|
| let mut h = Sha256::new();
|
| h.update(b"DECODE:");
|
| h.update(encoded.worm_seal.as_bytes());
|
| for &s in syndromes.iter() {
|
| h.update(s.to_le_bytes());
|
| }
|
| let seal = format!("{:x}", h.finalize())[..16].to_string();
|
|
|
| DecodeResult {
|
| syndrome_positions: syndromes,
|
| correctable,
|
| i4_intact,
|
| worm_seal: seal,
|
| }
|
| }
|
| }
|
|
|
| impl Default for QubitMultiplier {
|
| fn default() -> Self { Self::new() }
|
| }
|
|
|
|
|
|
|
| #[derive(Debug, Serialize, Deserialize)]
|
| pub struct DecodeResult {
|
| pub syndrome_positions: Vec<usize>,
|
| pub correctable: bool,
|
| pub i4_intact: bool,
|
| pub worm_seal: String,
|
| }
|
|
|
|
|
|
|
| #[derive(Debug, Serialize, Deserialize)]
|
| pub struct QubitMultiplyManifest {
|
| pub manifest_id: String,
|
| pub n_logical: usize,
|
| pub n_physical: usize,
|
| pub code_distance: u32,
|
| pub error_bound: f64,
|
| pub free_energy: f64,
|
| pub i4_invariant: f64,
|
| pub protected: bool,
|
| pub theorems_used: Vec<String>,
|
| pub worm_seal: String,
|
| }
|
|
|
| impl QubitMultiplyManifest {
|
| pub fn from_encoded(encoded: &EncodedQubit, multiplier: &QubitMultiplier) -> Self {
|
| QubitMultiplyManifest {
|
| manifest_id: format!("QM-{}-{}", encoded.n_physical, &encoded.worm_seal[..8]),
|
| n_logical: 1,
|
| n_physical: encoded.n_physical,
|
| code_distance: encoded.code_distance,
|
| error_bound: encoded.error_bound,
|
| free_energy: encoded.free_energy,
|
| i4_invariant: encoded.i4_invariant,
|
| protected: encoded.is_protected(1e-6),
|
| theorems_used: vec![
|
| "TopologicalProtection (mqs-substrate/coq/MQS/TopologicalProtection.v)".into(),
|
| "free_energy_legendre (gkn-i4-e7-lean/GKN/QuantumPartitionBridge.lean)".into(),
|
| "I4_homogeneous (gkn-i4-e7-lean/GKN/I4_CommRing.lean)".into(),
|
| "rs_correction_capacity (ahmad-docking/lean/Bio/SNA/Density.lean)".into(),
|
| ],
|
| worm_seal: encoded.worm_seal.clone(),
|
| }
|
| }
|
| }
|
|
|
| #[cfg(test)]
|
| mod tests {
|
| use super::*;
|
|
|
| #[test]
|
| fn test_zero_state_encodes() {
|
| let qm = QubitMultiplier::new();
|
| let psi = LogicalQubit::zero();
|
| let enc = qm.multiply(&psi, 5).unwrap();
|
| assert_eq!(enc.n_physical, 5);
|
| assert!(enc.code_distance >= 1);
|
| assert!(enc.error_bound < 1.0);
|
| println!("Code distance: {}", enc.code_distance);
|
| println!("Error bound: {:.2e}", enc.error_bound);
|
| println!("Free energy: {:.4}", enc.free_energy);
|
| println!("I4 invariant: {:.6}", enc.i4_invariant);
|
| }
|
|
|
| #[test]
|
| fn test_plus_state_encodes() {
|
| let qm = QubitMultiplier::new();
|
| let psi = LogicalQubit::plus();
|
| let enc = qm.multiply(&psi, 7).unwrap();
|
| assert!(enc.is_protected(0.01));
|
| }
|
|
|
| #[test]
|
| fn test_i4_scales_as_fourth_power() {
|
|
|
|
|
| let qm1 = QubitMultiplier::new();
|
| let mut qm2 = QubitMultiplier::new();
|
| qm2.size_nm *= 2.0;
|
|
|
| let psi = LogicalQubit::zero();
|
| let enc1 = qm1.multiply(&psi, 5).unwrap();
|
| let enc2 = qm2.multiply(&psi, 5).unwrap();
|
|
|
|
|
| println!("I4 (base): {:.6}", enc1.i4_invariant);
|
| println!("I4 (scaled): {:.6}", enc2.i4_invariant);
|
| assert!(enc1.i4_invariant.is_finite());
|
| assert!(enc2.i4_invariant.is_finite());
|
| }
|
|
|
| #[test]
|
| fn test_free_energy_legendre() {
|
|
|
|
|
| let qm = QubitMultiplier::new();
|
| let psi = LogicalQubit::zero();
|
| let enc5 = qm.multiply(&psi, 5).unwrap();
|
| let enc9 = qm.multiply(&psi, 9).unwrap();
|
|
|
| println!("F_β (n=5): {:.4}", enc5.free_energy);
|
| println!("F_β (n=9): {:.4}", enc9.free_energy);
|
| assert!(enc5.free_energy.is_finite());
|
| assert!(enc9.free_energy.is_finite());
|
| }
|
|
|
| #[test]
|
| fn test_worm_seal_deterministic() {
|
| let qm = QubitMultiplier::new();
|
| let psi = LogicalQubit::zero();
|
| let e1 = qm.multiply(&psi, 5).unwrap();
|
| let e2 = qm.multiply(&psi, 5).unwrap();
|
| assert_eq!(e1.worm_seal, e2.worm_seal);
|
| }
|
|
|
| #[test]
|
| fn test_unnormalized_rejected() {
|
| let qm = QubitMultiplier::new();
|
| let bad = LogicalQubit::new(2.0, 0.0, 0.0, 0.0);
|
| assert!(qm.multiply(&bad, 5).is_err());
|
| }
|
|
|
| #[test]
|
| fn test_manifest_generation() {
|
| let qm = QubitMultiplier::new();
|
| let psi = LogicalQubit::plus();
|
| let enc = qm.multiply(&psi, 5).unwrap();
|
| let m = QubitMultiplyManifest::from_encoded(&enc, &qm);
|
| assert_eq!(m.theorems_used.len(), 4);
|
| assert!(m.n_physical == 5);
|
| println!("Manifest: {:?}", m);
|
| }
|
|
|
| #[test]
|
| fn test_error_bound_fibonacci_params() {
|
|
|
|
|
|
|
| let qm = QubitMultiplier::new();
|
| assert!(qm.error_bound() < 1e-8,
|
| "Error bound should be < 1e-8 at reference params, got {:.2e}", qm.error_bound());
|
| }
|
| }
|
|
|