From 548dc42d9f454cb8c29fddb88ca41f8b1b595882 Mon Sep 17 00:00:00 2001 From: hachem Date: Wed, 10 Dec 2025 23:14:16 +0100 Subject: [add]: prelimenary noise layers --- tester/src/main.rs | 8 +++ tester/src/noise.rs | 172 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+) create mode 100644 tester/src/noise.rs (limited to 'tester/src') diff --git a/tester/src/main.rs b/tester/src/main.rs index 854472c..8a9a41c 100644 --- a/tester/src/main.rs +++ b/tester/src/main.rs @@ -3,6 +3,7 @@ mod clifford; mod common; mod custom_gates; mod kernels; +mod noise; mod non_clifford; mod simd; @@ -25,6 +26,7 @@ fn print_usage() { println!(" custom Run custom gate tests only"); println!(" kernels Run kernel batching tests only"); println!(" simd Run SIMD acceleration tests only"); + println!(" noise Run noise channel tests only"); println!(" bench Run benchmark tests only"); println!(" help Show this help message"); println!(); @@ -34,6 +36,7 @@ fn print_usage() { println!(" tester non-clifford # Run only rotation/parametric gate tests"); println!(" tester kernels # Run only kernel batching tests"); println!(" tester simd # Run only SIMD tests"); + println!(" tester noise # Run only noise channel tests"); println!(" tester custom bench # Run custom gates and benchmarks"); } @@ -58,6 +61,7 @@ fn main() { let run_custom = run_all || args.iter().any(|a| a == "custom"); let run_kernels = run_all || args.iter().any(|a| a == "kernels"); let run_simd = run_all || args.iter().any(|a| a == "simd"); + let run_noise = run_all || args.iter().any(|a| a == "noise"); let run_bench = run_all || args.iter().any(|a| a == "bench"); if run_clifford { @@ -80,6 +84,10 @@ fn main() { simd::run_all(&mut results); } + if run_noise { + noise::run_all(&mut results); + } + if run_bench { benchmarks::run_all(&mut results); } diff --git a/tester/src/noise.rs b/tester/src/noise.rs new file mode 100644 index 0000000..54edfe0 --- /dev/null +++ b/tester/src/noise.rs @@ -0,0 +1,172 @@ +use crate::common::{print_section, BenchmarkResult}; +use libpsi_core::{ + complex, DensityMatrix, NoiseChannel, QuantumCircuit, Runtime, Vector, +}; +use std::time::Instant; + +pub fn run_all(results: &mut Vec) { + println!("═══════════════════════════════════════════════════════════════"); + println!(" NOISE CHANNEL TESTS"); + println!("═══════════════════════════════════════════════════════════════\n"); + + test_density_matrix_basics(results); + test_noise_channels(results); + test_noisy_circuit(results); +} + +pub fn test_density_matrix_basics(results: &mut Vec) { + print_section("Density Matrix Basics"); + + let dm = DensityMatrix::new(2); + println!("Initial |00⟩ state:"); + println!("{}", dm); + + let mut circuit = QuantumCircuit::new(2); + circuit.h(0).cnot(0, 1); + circuit.compute_with(Runtime::BasicRT); + let state = circuit.state(); + + let state_vec: Vec<_> = (0..state.size()) + .map(|i| state.get(i)) + .collect(); + + let dm_bell = DensityMatrix::from_state_vector(&state_vec); + println!("Bell state |Φ+⟩:"); + println!("{}", dm_bell); + println!("Full matrix:"); + println!("{:?}", dm_bell); + + let is_pure = dm_bell.is_pure(1e-10); + println!("Purity check: {}\n", if is_pure { "✓ Pure" } else { "✗ Mixed" }); + + results.push(BenchmarkResult { + name: "DM: Bell state".to_string(), + basic_time: std::time::Duration::from_micros(0), + mt_time: std::time::Duration::from_micros(0), + results_match: is_pure, + }); +} + +pub fn test_noise_channels(results: &mut Vec) { + print_section("Noise Channel Effects"); + + let channels: Vec<(&str, NoiseChannel)> = vec![ + ("Depolarising (p=0.1)", NoiseChannel::depolarising(0.1)), + ("Amplitude Damping (γ=0.2)", NoiseChannel::amplitude_damping(0.2)), + ("Phase Damping (γ=0.2)", NoiseChannel::phase_damping(0.2)), + ("Bit Flip (p=0.1)", NoiseChannel::bit_flip(0.1)), + ("Phase Flip (p=0.1)", NoiseChannel::phase_flip(0.1)), + ("Bit-Phase Flip (p=0.1)", NoiseChannel::bit_phase_flip(0.1)), + ]; + + let plus_state = vec![ + complex!(1.0 / 2.0_f64.sqrt(), 0.0), + complex!(1.0 / 2.0_f64.sqrt(), 0.0), + ]; + + println!("Starting with |+⟩ state: (|0⟩ + |1⟩)/√2\n"); + + for (name, channel) in channels { + let mut dm = DensityMatrix::from_state_vector(&plus_state); + let initial_purity = dm.purity(); + + let start = Instant::now(); + dm.apply_noise_channel(&channel, 0); + let elapsed = start.elapsed(); + + let final_purity = dm.purity(); + let fidelity = dm.fidelity_with_pure_state(&plus_state); + + println!("{:30}", name); + println!(" Purity: {:.4} → {:.4}", initial_purity, final_purity); + println!(" Fidelity with |+⟩: {:.4}", fidelity); + println!(" Probabilities: {:?}", dm.probabilities()); + println!(" Time: {:.2}μs\n", elapsed.as_secs_f64() * 1_000_000.0); + + let purity_decreased = final_purity <= initial_purity + 1e-10; + + results.push(BenchmarkResult { + name: format!("Noise: {}", name), + basic_time: elapsed, + mt_time: elapsed, + results_match: purity_decreased, + }); + } +} + +pub fn test_noisy_circuit(results: &mut Vec) { + print_section("Noisy Circuit Simulation"); + + let mut circuit = QuantumCircuit::new(2); + circuit.h(0).cnot(0, 1); + circuit.compute_with(Runtime::BasicRT); + let state = circuit.state(); + let state_vec: Vec<_> = (0..state.size()).map(|i| state.get(i)).collect(); + + let mut dm = DensityMatrix::from_state_vector(&state_vec); + println!("Bell state before noise:"); + println!("{}", dm); + + let depol = NoiseChannel::depolarising(0.05); + + let start = Instant::now(); + dm.apply_noise_channel(&depol, 0); + dm.apply_noise_channel(&depol, 1); + let elapsed = start.elapsed(); + + println!("Bell state after 5% depolarising on both qubits:"); + println!("{}", dm); + + let fidelity = dm.fidelity_with_pure_state(&state_vec); + println!("Fidelity with ideal Bell state: {:.4}", fidelity); + println!("Time: {:.2}μs\n", elapsed.as_secs_f64() * 1_000_000.0); + + let mut dm2 = DensityMatrix::from_state_vector(&state_vec); + let amp_damp = NoiseChannel::amplitude_damping(0.1); + + dm2.apply_noise_channel(&_damp, 0); + dm2.apply_noise_channel(&_damp, 1); + + println!("Bell state after 10% amplitude damping on both qubits:"); + println!("{}", dm2); + println!("Probabilities show decay towards |00⟩: {:?}", dm2.probabilities()); + + results.push(BenchmarkResult { + name: "Noisy Bell circuit".to_string(), + basic_time: elapsed, + mt_time: elapsed, + results_match: fidelity > 0.8 && fidelity < 1.0, + }); + + println!(); + print_section("T1/T2 Relaxation Simulation"); + + let one_state = vec![complex!(0.0, 0.0), complex!(1.0, 0.0)]; + let mut dm_t1 = DensityMatrix::from_state_vector(&one_state); + + println!("Simulating T1 decay of |1⟩ state:"); + println!(" Initial: P(0)={:.4}, P(1)={:.4}", dm_t1.probabilities()[0], dm_t1.probabilities()[1]); + + let t1_channel = NoiseChannel::amplitude_damping(0.3); + for step in 1..=5 { + dm_t1.apply_noise_channel(&t1_channel, 0); + println!( + " Step {}: P(0)={:.4}, P(1)={:.4}, Purity={:.4}", + step, + dm_t1.probabilities()[0], + dm_t1.probabilities()[1], + dm_t1.purity() + ); + } + + let decayed = dm_t1.probabilities()[0] > 0.8; + println!(" Decay complete: {}\n", if decayed { "✓" } else { "✗" }); + + results.push(BenchmarkResult { + name: "T1 decay simulation".to_string(), + basic_time: std::time::Duration::from_micros(0), + mt_time: std::time::Duration::from_micros(0), + results_match: decayed, + }); +} + -- cgit v1.3