Skip to main content

oxicuda_quantum/
lib.rs

1//! Quantum simulation primitives for OxiCUDA.
2//!
3//! Provides state-vector simulation, Pauli expectation values, VQE/QAOA,
4//! density matrices, Trotter-Suzuki time evolution, quantum kernels, and
5//! PTX kernel code generation for GPU execution.
6
7pub mod channel;
8pub mod circuit;
9pub mod density;
10pub mod embedding;
11pub mod error;
12pub mod gates;
13pub mod handle;
14pub mod kernel;
15pub mod pauli;
16pub mod ptx_kernels;
17pub mod qaoa;
18pub mod statevec;
19pub mod trotter;
20pub mod vqe;
21
22pub use error::{QuantumError, QuantumResult};
23
24#[cfg(test)]
25mod e2e_tests {
26    use crate::channel::noise::{amplitude_damping_channel, depolarizing_channel};
27    use crate::circuit::circuit::{GateOp, QuantumCircuit};
28    use crate::density::density::DensityMatrix;
29    use crate::density::metrics::purity;
30    use crate::gates::controlled::apply_cnot;
31    use crate::gates::hadamard::gate_h;
32    use crate::gates::pauli::gate_x;
33    use crate::handle::LcgRng;
34    use crate::pauli::expval::expectation_value;
35    use crate::pauli::hamiltonian::Hamiltonian;
36    use crate::pauli::pauli_string::PauliOp;
37    use crate::ptx_kernels::{
38        expval_pauli_ptx, measure_prob_ptx, partial_trace_ptx, statevec_apply_1q_ptx,
39        statevec_apply_2q_ptx, statevec_apply_cnot_ptx, trotter_step_ptx,
40    };
41    use crate::qaoa::qaoa::QaoaCircuit;
42    use crate::statevec::apply_1q::apply_1q_inplace;
43    use crate::statevec::state::StateVector;
44    use crate::vqe::ansatz::HardwareEfficientAnsatz;
45    use crate::vqe::vqe::VqeOptimizer;
46
47    // Test 1: |0⟩ state has norm 1
48    #[test]
49    fn e2e_01_zero_state_norm_is_one() {
50        let sv = StateVector::new_zero_state(3).unwrap();
51        let norm = sv.norm_sq();
52        assert!((norm - 1.0).abs() < 1e-6, "norm={norm}");
53    }
54
55    // Test 2: H gate on |0⟩ gives equal superposition
56    #[test]
57    fn e2e_02_hadamard_creates_superposition() {
58        let mut sv = StateVector::new_zero_state(1).unwrap();
59        apply_1q_inplace(&mut sv, 0, &gate_h()).unwrap();
60        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
61        assert!(
62            (sv.amps[0].re - inv_sqrt2).abs() < 1e-5,
63            "amp0={:?}",
64            sv.amps[0]
65        );
66        assert!(
67            (sv.amps[1].re - inv_sqrt2).abs() < 1e-5,
68            "amp1={:?}",
69            sv.amps[1]
70        );
71    }
72
73    // Test 3: H·H = I (round-trip)
74    #[test]
75    fn e2e_03_hadamard_round_trip() {
76        let mut sv = StateVector::new_zero_state(1).unwrap();
77        apply_1q_inplace(&mut sv, 0, &gate_h()).unwrap();
78        apply_1q_inplace(&mut sv, 0, &gate_h()).unwrap();
79        assert!((sv.amps[0].re - 1.0).abs() < 1e-5, "amp0={:?}", sv.amps[0]);
80        assert!(sv.amps[1].norm() < 1e-5, "amp1={:?}", sv.amps[1]);
81    }
82
83    // Test 4: CNOT creates Bell state |00⟩+|11⟩/√2
84    #[test]
85    fn e2e_04_cnot_creates_bell_state() {
86        let mut sv = StateVector::new_zero_state(2).unwrap();
87        apply_1q_inplace(&mut sv, 0, &gate_h()).unwrap();
88        apply_cnot(&mut sv, 0, 1).unwrap();
89        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
90        // |00⟩ at index 0, |11⟩ at index 3
91        assert!((sv.amps[0].re - inv_sqrt2).abs() < 1e-5);
92        assert!((sv.amps[3].re - inv_sqrt2).abs() < 1e-5);
93        assert!(sv.amps[1].norm() < 1e-5);
94        assert!(sv.amps[2].norm() < 1e-5);
95    }
96
97    // Test 5: Pauli-Z expectation of |0⟩ = +1
98    #[test]
99    fn e2e_05_pauli_z_expval_zero_state() {
100        let sv = StateVector::new_zero_state(1).unwrap();
101        let mut ham = Hamiltonian::new();
102        ham.add_term(1.0, vec![PauliOp::Z]);
103        let ev = expectation_value(&sv, &ham).unwrap();
104        assert!((ev - 1.0).abs() < 1e-5, "ev={ev}");
105    }
106
107    // Test 6: Pauli-Z expectation of |1⟩ = -1
108    #[test]
109    fn e2e_06_pauli_z_expval_one_state() {
110        let mut sv = StateVector::new_zero_state(1).unwrap();
111        apply_1q_inplace(&mut sv, 0, &gate_x()).unwrap(); // flip to |1⟩
112        let mut ham = Hamiltonian::new();
113        ham.add_term(1.0, vec![PauliOp::Z]);
114        let ev = expectation_value(&sv, &ham).unwrap();
115        assert!((ev - (-1.0)).abs() < 1e-5, "ev={ev}");
116    }
117
118    // Test 7: Mixed Hamiltonian expectation value is finite
119    #[test]
120    fn e2e_07_mixed_hamiltonian_expval_finite() {
121        let mut sv = StateVector::new_zero_state(2).unwrap();
122        apply_1q_inplace(&mut sv, 0, &gate_h()).unwrap();
123        apply_cnot(&mut sv, 0, 1).unwrap();
124        let mut ham = Hamiltonian::new();
125        ham.add_term(0.5, vec![PauliOp::Z, PauliOp::Z]);
126        ham.add_term(0.3, vec![PauliOp::X, PauliOp::I]);
127        ham.add_term(0.2, vec![PauliOp::I, PauliOp::Y]);
128        let ev = expectation_value(&sv, &ham).unwrap();
129        assert!(ev.is_finite(), "ev={ev}");
130    }
131
132    // Test 8: VQE energy decreases over iterations
133    #[test]
134    fn e2e_08_vqe_energy_decreases() {
135        let ans = HardwareEfficientAnsatz::new(2, 1);
136        let mut ham = Hamiltonian::new();
137        ham.add_term(1.0, vec![PauliOp::Z, PauliOp::Z]);
138        ham.add_term(-0.5, vec![PauliOp::X, PauliOp::I]);
139        let mut rng = LcgRng::new(42);
140        let mut opt = VqeOptimizer::new(ans, ham, &mut rng);
141        let e_initial = opt.energy(&opt.params.clone()).unwrap();
142        let (e_final, _) = opt.optimize(5, 0.1).unwrap();
143        assert!(
144            e_final <= e_initial + 1e-3,
145            "e_initial={e_initial}, e_final={e_final}"
146        );
147    }
148
149    // Test 9: QAOA circuit runs without error
150    #[test]
151    fn e2e_09_qaoa_runs_without_error() {
152        let circuit = QaoaCircuit::new(4, 2, vec![0.3, 0.5], vec![0.7, 0.2]).unwrap();
153        let graph = vec![(0, 1), (1, 2), (2, 3), (3, 0)];
154        let sv = circuit.run(&graph).unwrap();
155        let norm = sv.norm_sq();
156        assert!((norm - 1.0).abs() < 1e-4, "norm={norm}");
157    }
158
159    // Test 10: Density matrix from pure state has purity 1
160    #[test]
161    fn e2e_10_pure_state_purity_is_one() {
162        let sv = StateVector::new_zero_state(2).unwrap();
163        let dm = DensityMatrix::from_pure_state(&sv);
164        let p = purity(&dm);
165        assert!((p - 1.0).abs() < 1e-5, "purity={p}");
166    }
167
168    // Test 11: Depolarizing channel reduces purity
169    #[test]
170    fn e2e_11_depolarizing_channel_reduces_purity() {
171        let sv = StateVector::new_zero_state(1).unwrap();
172        let dm = DensityMatrix::from_pure_state(&sv);
173        let p_initial = purity(&dm);
174
175        let ch = depolarizing_channel(0.5, 2).unwrap();
176        let dm_out = ch.apply(&dm).unwrap();
177        let p_final = purity(&dm_out);
178
179        assert!(
180            p_final < p_initial,
181            "purity did not decrease: {p_initial} → {p_final}"
182        );
183    }
184
185    // Test 12: PTX kernels generate non-empty strings for all 6 SM versions
186    #[test]
187    fn e2e_12_ptx_kernels_non_empty_all_sm() {
188        for sm in [75u32, 80, 86, 89, 90, 100] {
189            let s1 = statevec_apply_1q_ptx(sm);
190            let s2 = statevec_apply_2q_ptx(sm);
191            let s3 = statevec_apply_cnot_ptx(sm);
192            let s4 = expval_pauli_ptx(sm);
193            let s5 = partial_trace_ptx(sm);
194            let s6 = trotter_step_ptx(sm);
195            let s7 = measure_prob_ptx(sm);
196            assert!(!s1.is_empty(), "statevec_apply_1q sm={sm}");
197            assert!(!s2.is_empty(), "statevec_apply_2q sm={sm}");
198            assert!(!s3.is_empty(), "statevec_apply_cnot sm={sm}");
199            assert!(!s4.is_empty(), "expval_pauli sm={sm}");
200            assert!(!s5.is_empty(), "partial_trace sm={sm}");
201            assert!(!s6.is_empty(), "trotter_step sm={sm}");
202            assert!(!s7.is_empty(), "measure_prob sm={sm}");
203        }
204    }
205
206    // Supplementary: amplitude damping channel test
207    #[test]
208    fn e2e_13_amplitude_damping_channel() {
209        let sv = StateVector::new_zero_state(1).unwrap();
210        let dm = DensityMatrix::from_pure_state(&sv);
211        let ch = amplitude_damping_channel(0.3).unwrap();
212        let dm_out = ch.apply(&dm).unwrap();
213        let tr = dm_out.trace();
214        assert!((tr.re - 1.0).abs() < 1e-5, "trace={}", tr.re);
215    }
216
217    // Supplementary: circuit with explicit CNOTs
218    #[test]
219    fn e2e_14_circuit_bell_state() {
220        let mut circ = QuantumCircuit::new(2);
221        circ.add_gate(GateOp::H);
222        circ.add_gate(GateOp::Cnot { ctrl: 0, tgt: 1 });
223        let sv = StateVector::new_zero_state(2).unwrap();
224        let mut rng = LcgRng::new(7);
225        let out = circ.exec_on_state(&sv, &mut rng).unwrap();
226        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
227        assert!((out.amps[0].re - inv_sqrt2).abs() < 1e-5);
228        assert!((out.amps[3].re - inv_sqrt2).abs() < 1e-5);
229    }
230}