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 algorithm;
8pub mod algorithms;
9pub mod channel;
10pub mod circuit;
11pub mod density;
12pub mod embedding;
13pub mod error;
14pub mod error_correction;
15pub mod fourier;
16pub mod gates;
17pub mod handle;
18pub mod kernel;
19pub mod linear_systems;
20pub mod midcircuit;
21pub mod mps;
22pub mod pauli;
23pub mod ptx_kernels;
24pub mod qaoa;
25pub mod qml;
26pub mod stabilizer;
27pub mod statevec;
28pub mod tensor;
29pub mod trotter;
30pub mod vqe;
31
32pub use algorithm::{
33    BernsteinVaziraniResult, DeutschJozsaResult, FunctionKind, GroverResult, OrderFindingResult,
34    ShorResult, SimonResult, SuperdenseResult, TeleportResult, bernstein_vazirani,
35    bit_flip_correct, bit_flip_encode, classical_order, continued_fraction_convergents,
36    deutsch_jozsa, factor_from_order, gcd, grover_search, hadamard_test, marked_probability,
37    mod_exp, optimal_iterations, order_finding, phase_flip_correct, phase_flip_encode, prepare_ghz,
38    prepare_w, recover_period_from_constraints, shor_factor, simon, superdense_decode, swap_test,
39    teleport,
40};
41pub use algorithms::{
42    AmplitudeEstimationResult, CoinInit, CoinedWalk, IterativeQpeResult, LcuOperator, Mat2, Pauli,
43    PauliTerm, StatePreparation, VqlsResult, VqlsSolver, amplitude_estimation,
44    chebyshev_qsp_angles, chebyshev_t, iterative_phase_estimation, position_std_about,
45    qsp_top_left, qsp_unitary, signal_operator,
46};
47pub use channel::pauli_channel::{
48    bit_flip_channel, bit_phase_flip_channel, pauli_channel, pauli_twirl, phase_flip_channel,
49};
50pub use error::{QuantumError, QuantumResult};
51pub use error_correction::{
52    PauliError, StabKind, Stabilizer, SurfaceCode, SurfaceCodeConfig, Syndrome,
53};
54pub use fourier::{PhaseEstimationResult, phase_estimation, qft_inplace, qft_inverse_inplace};
55pub use linear_systems::{HermitianMatrix, HhlConfig, HhlResult, hhl_solve};
56pub use midcircuit::{
57    ClassicalRegister, MidCircuitOp, apply_if, measure_and_collapse, measure_deterministic,
58    run as run_midcircuit,
59};
60pub use mps::{MatrixProductState, MpsConfig};
61
62#[cfg(all(test, feature = "gpu-tests"))]
63mod gpu_tests;
64
65#[cfg(test)]
66mod e2e_tests {
67    use crate::channel::noise::{amplitude_damping_channel, depolarizing_channel};
68    use crate::circuit::circuit::{GateOp, QuantumCircuit};
69    use crate::density::density::DensityMatrix;
70    use crate::density::metrics::purity;
71    use crate::fourier::{phase_estimation, qft_inplace, qft_inverse_inplace};
72    use crate::gates::controlled::apply_cnot;
73    use crate::gates::hadamard::gate_h;
74    use crate::gates::hadamard::gate_s;
75    use crate::gates::pauli::gate_x;
76    use crate::handle::LcgRng;
77    use crate::pauli::expval::expectation_value;
78    use crate::pauli::hamiltonian::Hamiltonian;
79    use crate::pauli::pauli_string::PauliOp;
80    use crate::ptx_kernels::{
81        expval_pauli_ptx, measure_prob_ptx, partial_trace_ptx, qft_butterfly_ptx,
82        statevec_apply_1q_ptx, statevec_apply_2q_ptx, statevec_apply_cnot_ptx, trotter_step_ptx,
83    };
84    use crate::qaoa::qaoa::QaoaCircuit;
85    use crate::statevec::apply_1q::apply_1q_inplace;
86    use crate::statevec::state::StateVector;
87    use crate::vqe::ansatz::HardwareEfficientAnsatz;
88    use crate::vqe::vqe::VqeOptimizer;
89
90    // Test 1: |0⟩ state has norm 1
91    #[test]
92    fn e2e_01_zero_state_norm_is_one() {
93        let sv = StateVector::new_zero_state(3).expect("valid 3-qubit zero state");
94        let norm = sv.norm_sq();
95        assert!((norm - 1.0).abs() < 1e-6, "norm={norm}");
96    }
97
98    // Test 2: H gate on |0⟩ gives equal superposition
99    #[test]
100    fn e2e_02_hadamard_creates_superposition() {
101        let mut sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
102        apply_1q_inplace(&mut sv, 0, &gate_h()).expect("valid qubit index for H gate");
103        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
104        assert!(
105            (sv.amps[0].re - inv_sqrt2).abs() < 1e-5,
106            "amp0={:?}",
107            sv.amps[0]
108        );
109        assert!(
110            (sv.amps[1].re - inv_sqrt2).abs() < 1e-5,
111            "amp1={:?}",
112            sv.amps[1]
113        );
114    }
115
116    // Test 3: H·H = I (round-trip)
117    #[test]
118    fn e2e_03_hadamard_round_trip() {
119        let mut sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
120        apply_1q_inplace(&mut sv, 0, &gate_h()).expect("valid qubit index for first H gate");
121        apply_1q_inplace(&mut sv, 0, &gate_h()).expect("valid qubit index for second H gate");
122        assert!((sv.amps[0].re - 1.0).abs() < 1e-5, "amp0={:?}", sv.amps[0]);
123        assert!(sv.amps[1].norm() < 1e-5, "amp1={:?}", sv.amps[1]);
124    }
125
126    // Test 4: CNOT creates Bell state |00⟩+|11⟩/√2
127    #[test]
128    fn e2e_04_cnot_creates_bell_state() {
129        let mut sv = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
130        apply_1q_inplace(&mut sv, 0, &gate_h()).expect("valid qubit index for H gate");
131        apply_cnot(&mut sv, 0, 1).expect("valid CNOT qubit indices");
132        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
133        // |00⟩ at index 0, |11⟩ at index 3
134        assert!((sv.amps[0].re - inv_sqrt2).abs() < 1e-5);
135        assert!((sv.amps[3].re - inv_sqrt2).abs() < 1e-5);
136        assert!(sv.amps[1].norm() < 1e-5);
137        assert!(sv.amps[2].norm() < 1e-5);
138    }
139
140    // Test 5: Pauli-Z expectation of |0⟩ = +1
141    #[test]
142    fn e2e_05_pauli_z_expval_zero_state() {
143        let sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
144        let mut ham = Hamiltonian::new();
145        ham.add_term(1.0, vec![PauliOp::Z]);
146        let ev = expectation_value(&sv, &ham).expect("valid Hamiltonian expectation value");
147        assert!((ev - 1.0).abs() < 1e-5, "ev={ev}");
148    }
149
150    // Test 6: Pauli-Z expectation of |1⟩ = -1
151    #[test]
152    fn e2e_06_pauli_z_expval_one_state() {
153        let mut sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
154        apply_1q_inplace(&mut sv, 0, &gate_x()).expect("valid qubit index for X gate"); // flip to |1⟩
155        let mut ham = Hamiltonian::new();
156        ham.add_term(1.0, vec![PauliOp::Z]);
157        let ev = expectation_value(&sv, &ham).expect("valid Hamiltonian expectation value");
158        assert!((ev - (-1.0)).abs() < 1e-5, "ev={ev}");
159    }
160
161    // Test 7: Mixed Hamiltonian expectation value is finite
162    #[test]
163    fn e2e_07_mixed_hamiltonian_expval_finite() {
164        let mut sv = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
165        apply_1q_inplace(&mut sv, 0, &gate_h()).expect("valid qubit index for H gate");
166        apply_cnot(&mut sv, 0, 1).expect("valid CNOT qubit indices");
167        let mut ham = Hamiltonian::new();
168        ham.add_term(0.5, vec![PauliOp::Z, PauliOp::Z]);
169        ham.add_term(0.3, vec![PauliOp::X, PauliOp::I]);
170        ham.add_term(0.2, vec![PauliOp::I, PauliOp::Y]);
171        let ev = expectation_value(&sv, &ham).expect("valid Hamiltonian expectation value");
172        assert!(ev.is_finite(), "ev={ev}");
173    }
174
175    // Test 8: VQE energy decreases over iterations
176    #[test]
177    fn e2e_08_vqe_energy_decreases() {
178        let ans = HardwareEfficientAnsatz::new(2, 1);
179        let mut ham = Hamiltonian::new();
180        ham.add_term(1.0, vec![PauliOp::Z, PauliOp::Z]);
181        ham.add_term(-0.5, vec![PauliOp::X, PauliOp::I]);
182        let mut rng = LcgRng::new(42);
183        let mut opt = VqeOptimizer::new(ans, ham, &mut rng);
184        let e_initial = opt
185            .energy(&opt.params.clone())
186            .expect("valid energy computation");
187        let (e_final, _) = opt.optimize(5, 0.1).expect("valid VQE optimization run");
188        assert!(
189            e_final <= e_initial + 1e-3,
190            "e_initial={e_initial}, e_final={e_final}"
191        );
192    }
193
194    // Test 9: QAOA circuit runs without error
195    #[test]
196    fn e2e_09_qaoa_runs_without_error() {
197        let circuit = QaoaCircuit::new(4, 2, vec![0.3, 0.5], vec![0.7, 0.2])
198            .expect("valid QAOA circuit parameters");
199        let graph = vec![(0, 1), (1, 2), (2, 3), (3, 0)];
200        let sv = circuit.run(&graph).expect("valid QAOA circuit run");
201        let norm = sv.norm_sq();
202        assert!((norm - 1.0).abs() < 1e-4, "norm={norm}");
203    }
204
205    // Test 10: Density matrix from pure state has purity 1
206    #[test]
207    fn e2e_10_pure_state_purity_is_one() {
208        let sv = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
209        let dm = DensityMatrix::from_pure_state(&sv);
210        let p = purity(&dm);
211        assert!((p - 1.0).abs() < 1e-5, "purity={p}");
212    }
213
214    // Test 11: Depolarizing channel reduces purity
215    #[test]
216    fn e2e_11_depolarizing_channel_reduces_purity() {
217        let sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
218        let dm = DensityMatrix::from_pure_state(&sv);
219        let p_initial = purity(&dm);
220
221        let ch = depolarizing_channel(0.5, 2).expect("valid depolarizing channel");
222        let dm_out = ch.apply(&dm).expect("valid channel application");
223        let p_final = purity(&dm_out);
224
225        assert!(
226            p_final < p_initial,
227            "purity did not decrease: {p_initial} → {p_final}"
228        );
229    }
230
231    // Test 12: PTX kernels generate non-empty strings for all 6 SM versions
232    #[test]
233    fn e2e_12_ptx_kernels_non_empty_all_sm() {
234        for sm in [75u32, 80, 86, 89, 90, 100] {
235            let s1 = statevec_apply_1q_ptx(sm);
236            let s2 = statevec_apply_2q_ptx(sm);
237            let s3 = statevec_apply_cnot_ptx(sm);
238            let s4 = expval_pauli_ptx(sm);
239            let s5 = partial_trace_ptx(sm);
240            let s6 = trotter_step_ptx(sm);
241            let s7 = measure_prob_ptx(sm);
242            let s8 = qft_butterfly_ptx(sm);
243            assert!(!s1.is_empty(), "statevec_apply_1q sm={sm}");
244            assert!(!s2.is_empty(), "statevec_apply_2q sm={sm}");
245            assert!(!s3.is_empty(), "statevec_apply_cnot sm={sm}");
246            assert!(!s4.is_empty(), "expval_pauli sm={sm}");
247            assert!(!s5.is_empty(), "partial_trace sm={sm}");
248            assert!(!s6.is_empty(), "trotter_step sm={sm}");
249            assert!(!s7.is_empty(), "measure_prob sm={sm}");
250            assert!(!s8.is_empty(), "qft_butterfly sm={sm}");
251        }
252    }
253
254    // Supplementary: amplitude damping channel test
255    #[test]
256    fn e2e_13_amplitude_damping_channel() {
257        let sv = StateVector::new_zero_state(1).expect("valid 1-qubit zero state");
258        let dm = DensityMatrix::from_pure_state(&sv);
259        let ch = amplitude_damping_channel(0.3).expect("valid amplitude damping channel");
260        let dm_out = ch.apply(&dm).expect("valid channel application");
261        let tr = dm_out.trace();
262        assert!((tr.re - 1.0).abs() < 1e-5, "trace={}", tr.re);
263    }
264
265    // Supplementary: circuit with explicit CNOTs
266    #[test]
267    fn e2e_14_circuit_bell_state() {
268        let mut circ = QuantumCircuit::new(2);
269        circ.add_gate(GateOp::H);
270        circ.add_gate(GateOp::Cnot { ctrl: 0, tgt: 1 });
271        let sv = StateVector::new_zero_state(2).expect("valid 2-qubit zero state");
272        let mut rng = LcgRng::new(7);
273        let out = circ
274            .exec_on_state(&sv, &mut rng)
275            .expect("valid circuit execution");
276        let inv_sqrt2 = std::f32::consts::FRAC_1_SQRT_2;
277        assert!((out.amps[0].re - inv_sqrt2).abs() < 1e-5);
278        assert!((out.amps[3].re - inv_sqrt2).abs() < 1e-5);
279    }
280
281    // Test 15: QFT on |0…0⟩ yields the uniform superposition
282    #[test]
283    fn e2e_15_qft_uniform_from_zero_state() {
284        let mut sv = StateVector::new_zero_state(3).expect("valid 3-qubit zero state");
285        qft_inplace(&mut sv, &[0, 1, 2]).expect("valid QFT qubit indices");
286        let expected = 1.0 / (8.0_f32).sqrt();
287        for a in &sv.amps {
288            assert!((a.re - expected).abs() < 1e-5, "re={}", a.re);
289            assert!(a.im.abs() < 1e-5, "im={}", a.im);
290        }
291    }
292
293    // Test 16: QFT followed by inverse QFT is the identity
294    #[test]
295    fn e2e_16_qft_roundtrip_identity() {
296        let mut rng = LcgRng::new(2024);
297        let mut amps = Vec::with_capacity(8);
298        for _ in 0..8 {
299            amps.push(num_complex::Complex::new(
300                rng.next_normal(),
301                rng.next_normal(),
302            ));
303        }
304        let mut sv = StateVector { amps, n_qubits: 3 };
305        sv.normalize_inplace();
306        let original = sv.amps.clone();
307        qft_inplace(&mut sv, &[0, 1, 2]).expect("valid QFT qubit indices");
308        qft_inverse_inplace(&mut sv, &[0, 1, 2]).expect("valid inverse QFT qubit indices");
309        for (a, b) in sv.amps.iter().zip(original.iter()) {
310            assert!((a.re - b.re).abs() < 1e-5, "re {a:?} vs {b:?}");
311            assert!((a.im - b.im).abs() < 1e-5, "im {a:?} vs {b:?}");
312        }
313    }
314
315    // Test 17: QPE recovers φ = 1/4 (integer 1) for U = S on eigenstate |1⟩
316    #[test]
317    fn e2e_17_qpe_recovers_quarter_phase() {
318        let mut sv = StateVector::new_zero_state(3).expect("valid 3-qubit zero state");
319        apply_1q_inplace(&mut sv, 2, &gate_x())
320            .expect("valid qubit index for X gate on target qubit"); // target qubit → |1⟩
321        let res =
322            phase_estimation(&mut sv, &[0, 1], 2, &gate_s()).expect("valid QPE configuration");
323        assert_eq!(res.integer, 1, "integer={}", res.integer);
324        assert!((res.phase - 0.25).abs() < 1e-6, "phase={}", res.phase);
325        assert!(res.probability > 0.999, "prob={}", res.probability);
326    }
327}