Skip to main content

oxilean_std/quantum_computing/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use super::functions::*;
6use std::f64::consts::PI;
7
8/// A gate operation in a `QuantumCircuit`.
9#[derive(Debug, Clone)]
10pub enum GateOp {
11    /// Apply a single-qubit gate to qubit `q`.
12    Single { gate: Gate2x2, qubit: usize },
13    /// Apply a CNOT gate.
14    Cnot { control: usize, target: usize },
15}
16#[allow(dead_code)]
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum ChannelType {
19    Depolarizing,
20    AmplitudeDamping,
21    PhaseDamping,
22    BitFlip,
23    PhaseFlip,
24    Erasure,
25    Unitary,
26    Identity,
27}
28/// Clifford group properties.
29#[allow(dead_code)]
30#[derive(Debug, Clone)]
31pub struct CliffordGroup {
32    pub num_qubits: usize,
33}
34impl CliffordGroup {
35    #[allow(dead_code)]
36    pub fn new(n: usize) -> Self {
37        Self { num_qubits: n }
38    }
39    #[allow(dead_code)]
40    pub fn normalizes_pauli_group(&self) -> bool {
41        true
42    }
43    #[allow(dead_code)]
44    pub fn is_efficiently_simulable(&self) -> bool {
45        true
46    }
47    #[allow(dead_code)]
48    pub fn gottesman_knill_theorem(&self) -> String {
49        format!(
50            "Clifford circuits on {} qubits can be simulated in poly time (Gottesman-Knill)",
51            self.num_qubits
52        )
53    }
54    #[allow(dead_code)]
55    pub fn universal_with_t_gate(&self) -> bool {
56        true
57    }
58}
59/// Quantum approximate optimization algorithm (QAOA).
60#[allow(dead_code)]
61#[derive(Debug, Clone)]
62pub struct QaoaConfig {
63    pub problem_name: String,
64    pub p_layers: usize,
65}
66impl QaoaConfig {
67    #[allow(dead_code)]
68    pub fn new(problem: &str, p: usize) -> Self {
69        Self {
70            problem_name: problem.to_string(),
71            p_layers: p,
72        }
73    }
74    #[allow(dead_code)]
75    pub fn approximation_ratio_lower_bound(&self) -> f64 {
76        let neg_layers = -(self.p_layers as i64) as f64;
77        0.5 + 0.1924 * (1.0 - (neg_layers * 0.5).exp())
78    }
79    #[allow(dead_code)]
80    pub fn num_circuit_parameters(&self) -> usize {
81        2 * self.p_layers
82    }
83}
84/// Quantum error-correcting code.
85#[allow(dead_code)]
86#[derive(Debug, Clone)]
87pub struct QuantumErrorCode {
88    pub name: String,
89    pub n: usize,
90    pub k: usize,
91    pub d: usize,
92}
93impl QuantumErrorCode {
94    #[allow(dead_code)]
95    pub fn steane_7() -> Self {
96        Self {
97            name: "Steane [[7,1,3]]".to_string(),
98            n: 7,
99            k: 1,
100            d: 3,
101        }
102    }
103    #[allow(dead_code)]
104    pub fn shor_9() -> Self {
105        Self {
106            name: "Shor [[9,1,3]]".to_string(),
107            n: 9,
108            k: 1,
109            d: 3,
110        }
111    }
112    #[allow(dead_code)]
113    pub fn surface_code(d: usize) -> Self {
114        let n = 2 * d * d - 2 * d + 1;
115        Self {
116            name: format!("Surface [[{n},1,{d}]]"),
117            n,
118            k: 1,
119            d,
120        }
121    }
122    #[allow(dead_code)]
123    pub fn encoding_rate(&self) -> f64 {
124        self.k as f64 / self.n as f64
125    }
126    #[allow(dead_code)]
127    pub fn corrects_errors_up_to(&self) -> usize {
128        (self.d - 1) / 2
129    }
130    #[allow(dead_code)]
131    pub fn qec_bound_satisfied(&self) -> bool {
132        true
133    }
134}
135/// A quantum gate represented by its name and matrix dimension.
136#[allow(dead_code)]
137#[derive(Debug, Clone)]
138pub struct QuantumGate {
139    pub name: String,
140    pub num_qubits: usize,
141    pub is_unitary: bool,
142    pub is_clifford: bool,
143}
144impl QuantumGate {
145    #[allow(dead_code)]
146    pub fn hadamard() -> Self {
147        Self {
148            name: "H".to_string(),
149            num_qubits: 1,
150            is_unitary: true,
151            is_clifford: true,
152        }
153    }
154    #[allow(dead_code)]
155    pub fn pauli_x() -> Self {
156        Self {
157            name: "X".to_string(),
158            num_qubits: 1,
159            is_unitary: true,
160            is_clifford: true,
161        }
162    }
163    #[allow(dead_code)]
164    pub fn pauli_y() -> Self {
165        Self {
166            name: "Y".to_string(),
167            num_qubits: 1,
168            is_unitary: true,
169            is_clifford: true,
170        }
171    }
172    #[allow(dead_code)]
173    pub fn pauli_z() -> Self {
174        Self {
175            name: "Z".to_string(),
176            num_qubits: 1,
177            is_unitary: true,
178            is_clifford: true,
179        }
180    }
181    #[allow(dead_code)]
182    pub fn phase(theta_desc: &str) -> Self {
183        Self {
184            name: format!("P({theta_desc})"),
185            num_qubits: 1,
186            is_unitary: true,
187            is_clifford: false,
188        }
189    }
190    #[allow(dead_code)]
191    pub fn t_gate() -> Self {
192        Self {
193            name: "T".to_string(),
194            num_qubits: 1,
195            is_unitary: true,
196            is_clifford: false,
197        }
198    }
199    #[allow(dead_code)]
200    pub fn cnot() -> Self {
201        Self {
202            name: "CNOT".to_string(),
203            num_qubits: 2,
204            is_unitary: true,
205            is_clifford: true,
206        }
207    }
208    #[allow(dead_code)]
209    pub fn toffoli() -> Self {
210        Self {
211            name: "Toffoli".to_string(),
212            num_qubits: 3,
213            is_unitary: true,
214            is_clifford: false,
215        }
216    }
217    #[allow(dead_code)]
218    pub fn swap() -> Self {
219        Self {
220            name: "SWAP".to_string(),
221            num_qubits: 2,
222            is_unitary: true,
223            is_clifford: true,
224        }
225    }
226    #[allow(dead_code)]
227    pub fn matrix_size(&self) -> usize {
228        1usize << self.num_qubits
229    }
230}
231/// A complex number a + bi (simple f64 wrapper).
232#[derive(Debug, Clone, Copy)]
233pub struct Complex {
234    pub re: f64,
235    pub im: f64,
236}
237impl Complex {
238    pub fn new(re: f64, im: f64) -> Self {
239        Complex { re, im }
240    }
241    pub fn zero() -> Self {
242        Complex { re: 0.0, im: 0.0 }
243    }
244    pub fn one() -> Self {
245        Complex { re: 1.0, im: 0.0 }
246    }
247    pub fn i() -> Self {
248        Complex { re: 0.0, im: 1.0 }
249    }
250    /// Modulus |z|.
251    pub fn abs(&self) -> f64 {
252        (self.re * self.re + self.im * self.im).sqrt()
253    }
254    /// |z|².
255    pub fn abs_sq(&self) -> f64 {
256        self.re * self.re + self.im * self.im
257    }
258    /// Complex conjugate.
259    pub fn conj(&self) -> Self {
260        Complex {
261            re: self.re,
262            im: -self.im,
263        }
264    }
265    pub fn add(&self, other: &Self) -> Self {
266        Complex {
267            re: self.re + other.re,
268            im: self.im + other.im,
269        }
270    }
271    pub fn mul(&self, other: &Self) -> Self {
272        Complex {
273            re: self.re * other.re - self.im * other.im,
274            im: self.re * other.im + self.im * other.re,
275        }
276    }
277    pub fn scale(&self, s: f64) -> Self {
278        Complex {
279            re: self.re * s,
280            im: self.im * s,
281        }
282    }
283    /// e^{iθ} = cos θ + i sin θ.
284    pub fn exp(theta: f64) -> Self {
285        Complex {
286            re: theta.cos(),
287            im: theta.sin(),
288        }
289    }
290}
291/// An n-qubit quantum register holding 2^n complex amplitudes.
292///
293/// This is a higher-level wrapper around `QuantumStatevector` that tracks
294/// qubit count explicitly and provides convenience initializers.
295#[derive(Debug)]
296pub struct QuantumRegister {
297    sv: QuantumStatevector,
298}
299impl QuantumRegister {
300    /// Create a new register in the |0…0⟩ state.
301    pub fn new(n_qubits: usize) -> Self {
302        QuantumRegister {
303            sv: QuantumStatevector::new(n_qubits),
304        }
305    }
306    /// Number of qubits.
307    pub fn n_qubits(&self) -> usize {
308        self.sv.n_qubits
309    }
310    /// Number of basis states (2^n).
311    pub fn size(&self) -> usize {
312        self.sv.amplitudes.len()
313    }
314    /// Read amplitude at index `i`.
315    pub fn amplitude(&self, i: usize) -> Complex {
316        self.sv.amplitude(i)
317    }
318    /// Probability of basis state `i`.
319    pub fn prob(&self, i: usize) -> f64 {
320        self.sv.prob(i)
321    }
322    /// Apply a single-qubit gate to qubit `q`.
323    pub fn apply_gate(&mut self, gate: &Gate2x2, qubit: usize) {
324        self.sv.apply_single_qubit(gate, qubit);
325    }
326    /// Apply CNOT with given control and target qubits.
327    pub fn apply_cnot(&mut self, control: usize, target: usize) {
328        self.sv.apply_cnot(control, target);
329    }
330    /// Prepare the uniform superposition state H^⊗n |0⟩.
331    pub fn prepare_uniform_superposition(&mut self) {
332        let h = Gate2x2::hadamard();
333        for q in 0..self.sv.n_qubits {
334            self.sv.apply_single_qubit(&h, q);
335        }
336    }
337    /// Measure qubit `q` (deterministic via seed).
338    pub fn measure_qubit(&self, qubit: usize, seed: u64) -> u8 {
339        self.sv.measure_qubit(qubit, seed)
340    }
341    /// Check normalisation.
342    pub fn is_normalized(&self) -> bool {
343        self.sv.is_normalized()
344    }
345    /// Amplitudes as a slice.
346    pub fn amplitudes(&self) -> &[Complex] {
347        &self.sv.amplitudes
348    }
349}
350/// A quantum register of n qubits.
351#[allow(dead_code)]
352#[derive(Debug, Clone)]
353pub struct QuantumRegisterData {
354    pub num_qubits: usize,
355    pub label: String,
356}
357impl QuantumRegisterData {
358    #[allow(dead_code)]
359    pub fn new(n: usize, label: &str) -> Self {
360        Self {
361            num_qubits: n,
362            label: label.to_string(),
363        }
364    }
365    #[allow(dead_code)]
366    pub fn hilbert_space_dim(&self) -> usize {
367        1usize << self.num_qubits
368    }
369    #[allow(dead_code)]
370    pub fn computational_basis_size(&self) -> usize {
371        self.hilbert_space_dim()
372    }
373}
374/// Quantum Fourier Transform simulator for up to ~20 qubits.
375pub struct QFTSimulator {
376    pub n_qubits: usize,
377}
378impl QFTSimulator {
379    /// Create a QFT simulator for `n_qubits` qubits.
380    pub fn new(n_qubits: usize) -> Self {
381        QFTSimulator { n_qubits }
382    }
383    /// Apply the QFT in-place to a `QuantumRegister`.
384    ///
385    /// Implements the standard circuit decomposition:
386    ///   for j = 0..n: H on j, then CPhase(2Ï€/2^k) for k=2..n-j
387    pub fn apply(&self, reg: &mut QuantumRegister) {
388        let n = self.n_qubits;
389        for j in 0..n {
390            reg.apply_gate(&Gate2x2::hadamard(), j);
391            for k in 2..=(n - j) {
392                let theta = 2.0 * PI / (1u64 << k) as f64;
393                let cphase = Gate2x2::phase(theta);
394                let control = j + k - 1;
395                let target = j;
396                let size = reg.size();
397                for i in 0..size {
398                    if (i >> control) & 1 == 1 && (i >> target) & 1 == 1 {
399                        reg.sv.amplitudes[i] = reg.sv.amplitudes[i].mul(&cphase.matrix[1][1]);
400                    }
401                }
402            }
403        }
404        Self::bit_reverse(reg);
405    }
406    /// Apply the inverse QFT in-place.
407    pub fn apply_inverse(&self, reg: &mut QuantumRegister) {
408        let n = self.n_qubits;
409        Self::bit_reverse(reg);
410        for j in (0..n).rev() {
411            for k in (2..=(n - j)).rev() {
412                let theta = -2.0 * PI / (1u64 << k) as f64;
413                let cphase = Gate2x2::phase(theta);
414                let control = j + k - 1;
415                let target = j;
416                let size = reg.size();
417                for i in 0..size {
418                    if (i >> control) & 1 == 1 && (i >> target) & 1 == 1 {
419                        reg.sv.amplitudes[i] = reg.sv.amplitudes[i].mul(&cphase.matrix[1][1]);
420                    }
421                }
422            }
423            reg.apply_gate(&Gate2x2::hadamard(), j);
424        }
425    }
426    /// Compute the QFT of a given amplitude vector and return the transformed
427    /// amplitudes.  Convenience wrapper that does not require a `QuantumRegister`.
428    pub fn transform(&self, input: &[Complex]) -> Vec<Complex> {
429        let n = input.len();
430        let inv_sqrt_n = 1.0 / (n as f64).sqrt();
431        (0..n)
432            .map(|k| {
433                (0..n)
434                    .fold(Complex::zero(), |acc, j| {
435                        let angle = 2.0 * PI * (j * k) as f64 / n as f64;
436                        acc.add(&input[j].mul(&Complex::exp(angle)))
437                    })
438                    .scale(inv_sqrt_n)
439            })
440            .collect()
441    }
442    /// Bit-reversal permutation of register amplitudes (needed by standard QFT).
443    fn bit_reverse(reg: &mut QuantumRegister) {
444        let n = reg.n_qubits();
445        let size = reg.size();
446        for i in 0..size {
447            let j = Self::reverse_bits(i, n);
448            if j > i {
449                reg.sv.amplitudes.swap(i, j);
450            }
451        }
452    }
453    fn reverse_bits(mut x: usize, n: usize) -> usize {
454        let mut result = 0;
455        for _ in 0..n {
456            result = (result << 1) | (x & 1);
457            x >>= 1;
458        }
459        result
460    }
461}
462/// A single qubit state α|0⟩ + β|1⟩ with |α|² + |β|² = 1.
463#[derive(Debug, Clone)]
464pub struct Qubit {
465    pub alpha: Complex,
466    pub beta: Complex,
467}
468impl Qubit {
469    /// |0⟩ state.
470    pub fn zero() -> Self {
471        Qubit {
472            alpha: Complex::one(),
473            beta: Complex::zero(),
474        }
475    }
476    /// |1⟩ state.
477    pub fn one() -> Self {
478        Qubit {
479            alpha: Complex::zero(),
480            beta: Complex::one(),
481        }
482    }
483    /// |+⟩ = (|0⟩ + |1⟩) / √2.
484    pub fn plus() -> Self {
485        let v = 1.0 / 2.0f64.sqrt();
486        Qubit {
487            alpha: Complex::new(v, 0.0),
488            beta: Complex::new(v, 0.0),
489        }
490    }
491    /// |−⟩ = (|0⟩ − |1⟩) / √2.
492    pub fn minus() -> Self {
493        let v = 1.0 / 2.0f64.sqrt();
494        Qubit {
495            alpha: Complex::new(v, 0.0),
496            beta: Complex::new(-v, 0.0),
497        }
498    }
499    /// Probability of measuring 0.
500    pub fn prob_zero(&self) -> f64 {
501        self.alpha.abs_sq()
502    }
503    /// Probability of measuring 1.
504    pub fn prob_one(&self) -> f64 {
505        self.beta.abs_sq()
506    }
507    /// Check that |α|² + |β|² ≈ 1.
508    pub fn is_normalized(&self) -> bool {
509        (self.prob_zero() + self.prob_one() - 1.0).abs() < 1e-10
510    }
511    /// Perform a projective measurement.
512    ///
513    /// Uses a simple deterministic hash of `seed` to decide the outcome.
514    /// Returns `(outcome, collapsed_state)`.
515    pub fn measure(&self, seed: u64) -> (u8, Qubit) {
516        let t = (seed
517            .wrapping_mul(6364136223846793005)
518            .wrapping_add(1442695040888963407)
519            >> 33) as f64
520            / u32::MAX as f64;
521        let p0 = self.prob_zero();
522        if t < p0 {
523            (0, Qubit::zero())
524        } else {
525            (1, Qubit::one())
526        }
527    }
528}
529/// Statevector simulator for n-qubit systems.
530#[derive(Debug)]
531pub struct QuantumStatevector {
532    pub n_qubits: usize,
533    /// 2^n complex amplitudes, indexed by basis state |b_{n-1}...b_0⟩.
534    pub amplitudes: Vec<Complex>,
535}
536impl QuantumStatevector {
537    /// Initialise the |0...0⟩ state.
538    pub fn new(n_qubits: usize) -> Self {
539        let size = 1usize << n_qubits;
540        let mut amplitudes = vec![Complex::zero(); size];
541        amplitudes[0] = Complex::one();
542        QuantumStatevector {
543            n_qubits,
544            amplitudes,
545        }
546    }
547    /// Amplitude of a basis state.
548    pub fn amplitude(&self, basis_state: usize) -> Complex {
549        self.amplitudes[basis_state]
550    }
551    /// Probability of a basis state.
552    pub fn prob(&self, basis_state: usize) -> f64 {
553        self.amplitudes[basis_state].abs_sq()
554    }
555    /// Apply a single-qubit gate to qubit `qubit_idx` (0 = least significant).
556    pub fn apply_single_qubit(&mut self, gate: &Gate2x2, qubit_idx: usize) {
557        let size = self.amplitudes.len();
558        let step = 1usize << qubit_idx;
559        let mut block_start = 0;
560        while block_start < size {
561            for j in block_start..(block_start + step) {
562                let j1 = j + step;
563                let a0 = self.amplitudes[j];
564                let a1 = self.amplitudes[j1];
565                self.amplitudes[j] = gate.matrix[0][0].mul(&a0).add(&gate.matrix[0][1].mul(&a1));
566                self.amplitudes[j1] = gate.matrix[1][0].mul(&a0).add(&gate.matrix[1][1].mul(&a1));
567            }
568            block_start += step * 2;
569        }
570    }
571    /// Apply a CNOT gate with the given control and target qubit indices.
572    pub fn apply_cnot(&mut self, control: usize, target: usize) {
573        let size = self.amplitudes.len();
574        for i in 0..size {
575            if (i >> control) & 1 == 1 {
576                let j = i ^ (1 << target);
577                if j > i {
578                    self.amplitudes.swap(i, j);
579                }
580            }
581        }
582    }
583    /// Measure a single qubit (destructive, deterministic via `seed`). Returns 0 or 1.
584    pub fn measure_qubit(&self, qubit_idx: usize, seed: u64) -> u8 {
585        let prob1: f64 = self
586            .amplitudes
587            .iter()
588            .enumerate()
589            .filter(|(i, _)| (i >> qubit_idx) & 1 == 1)
590            .map(|(_, a)| a.abs_sq())
591            .sum();
592        let t = (seed
593            .wrapping_mul(6364136223846793005)
594            .wrapping_add(1442695040888963407)
595            >> 33) as f64
596            / u32::MAX as f64;
597        if t < prob1 {
598            1
599        } else {
600            0
601        }
602    }
603    /// Check that Σ |aᵢ|² ≈ 1.
604    pub fn is_normalized(&self) -> bool {
605        let total: f64 = self.amplitudes.iter().map(|a| a.abs_sq()).sum();
606        (total - 1.0).abs() < 1e-9
607    }
608    /// Number of amplitudes (= 2^n_qubits).
609    pub fn n_amplitudes(&self) -> usize {
610        self.amplitudes.len()
611    }
612}
613/// Single-qubit Pauli operator.
614#[derive(Debug, Clone, Copy, PartialEq, Eq)]
615pub enum Pauli {
616    I,
617    X,
618    Y,
619    Z,
620}
621impl Pauli {
622    /// Matrix representation as a `Gate2x2`.
623    pub fn to_gate(self) -> Gate2x2 {
624        match self {
625            Pauli::I => Gate2x2 {
626                name: "I".to_string(),
627                matrix: [
628                    [Complex::one(), Complex::zero()],
629                    [Complex::zero(), Complex::one()],
630                ],
631            },
632            Pauli::X => Gate2x2::pauli_x(),
633            Pauli::Y => Gate2x2::pauli_y(),
634            Pauli::Z => Gate2x2::pauli_z(),
635        }
636    }
637    /// Multiply two single-qubit Paulis (ignoring global phase).
638    pub fn mul(self, other: Pauli) -> Pauli {
639        match (self, other) {
640            (Pauli::I, p) | (p, Pauli::I) => p,
641            (Pauli::X, Pauli::X) => Pauli::I,
642            (Pauli::Y, Pauli::Y) => Pauli::I,
643            (Pauli::Z, Pauli::Z) => Pauli::I,
644            (Pauli::X, Pauli::Y) => Pauli::Z,
645            (Pauli::Y, Pauli::X) => Pauli::Z,
646            (Pauli::Y, Pauli::Z) => Pauli::X,
647            (Pauli::Z, Pauli::Y) => Pauli::X,
648            (Pauli::Z, Pauli::X) => Pauli::Y,
649            (Pauli::X, Pauli::Z) => Pauli::Y,
650        }
651    }
652    /// Return true iff this Pauli commutes with `other`.
653    pub fn commutes_with(self, other: Pauli) -> bool {
654        matches!(
655            (self, other),
656            (Pauli::I, _)
657                | (_, Pauli::I)
658                | (Pauli::X, Pauli::X)
659                | (Pauli::Y, Pauli::Y)
660                | (Pauli::Z, Pauli::Z)
661        )
662    }
663}
664/// Quantum complexity class hierarchy.
665#[allow(dead_code)]
666#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
667pub enum QuantumComplexityClass {
668    BPP,
669    BQP,
670    QMA,
671    QMAM,
672    PSharpP,
673    PSPACE,
674    EXP,
675}
676impl QuantumComplexityClass {
677    #[allow(dead_code)]
678    pub fn name(&self) -> &'static str {
679        match self {
680            Self::BPP => "BPP",
681            Self::BQP => "BQP",
682            Self::QMA => "QMA",
683            Self::QMAM => "QMAM",
684            Self::PSharpP => "P#P",
685            Self::PSPACE => "PSPACE",
686            Self::EXP => "EXP",
687        }
688    }
689    #[allow(dead_code)]
690    pub fn contains_bpp(&self) -> bool {
691        !matches!(self, Self::BPP)
692    }
693    #[allow(dead_code)]
694    pub fn is_believed_strictly_larger_than_bqp(&self) -> bool {
695        matches!(
696            self,
697            Self::QMA | Self::QMAM | Self::PSharpP | Self::PSPACE | Self::EXP
698        )
699    }
700}
701/// Quantum walk on a graph.
702#[allow(dead_code)]
703#[derive(Debug, Clone)]
704pub struct QuantumWalk {
705    pub graph_name: String,
706    pub walk_type: QuantumWalkType,
707    pub num_steps: usize,
708}
709impl QuantumWalk {
710    #[allow(dead_code)]
711    pub fn new(graph: &str, wt: QuantumWalkType, steps: usize) -> Self {
712        Self {
713            graph_name: graph.to_string(),
714            walk_type: wt,
715            num_steps: steps,
716        }
717    }
718    #[allow(dead_code)]
719    pub fn speedup_over_classical(&self) -> f64 {
720        match self.walk_type {
721            QuantumWalkType::ContinuousTime => 2.0,
722            QuantumWalkType::DiscreteTimeCoin => 2.0,
723            QuantumWalkType::Scattering => 1.5,
724        }
725    }
726    #[allow(dead_code)]
727    pub fn element_distinctness_uses_walk(&self) -> bool {
728        self.graph_name.contains("Johnson")
729    }
730}
731/// Quantum teleportation protocol.
732#[allow(dead_code)]
733#[derive(Debug, Clone)]
734pub struct TeleportationProtocol {
735    pub input_qubits: usize,
736    pub classical_bits_needed: usize,
737}
738impl TeleportationProtocol {
739    #[allow(dead_code)]
740    pub fn new(n: usize) -> Self {
741        Self {
742            input_qubits: n,
743            classical_bits_needed: 2 * n,
744        }
745    }
746    #[allow(dead_code)]
747    pub fn requires_entanglement(&self) -> bool {
748        true
749    }
750    #[allow(dead_code)]
751    pub fn no_faster_than_light(&self) -> bool {
752        self.classical_bits_needed > 0
753    }
754    #[allow(dead_code)]
755    pub fn fidelity_with_perfect_channel(&self) -> f64 {
756        1.0
757    }
758}
759/// Pauli group on n qubits.
760#[allow(dead_code)]
761#[derive(Debug, Clone)]
762pub struct PauliGroup {
763    pub num_qubits: usize,
764}
765impl PauliGroup {
766    #[allow(dead_code)]
767    pub fn new(n: usize) -> Self {
768        Self { num_qubits: n }
769    }
770    #[allow(dead_code)]
771    pub fn size(&self) -> usize {
772        4usize.pow(self.num_qubits as u32 + 1)
773    }
774    #[allow(dead_code)]
775    pub fn is_abelian(&self) -> bool {
776        self.num_qubits == 0
777    }
778    #[allow(dead_code)]
779    pub fn stabilizer_group_description(&self) -> String {
780        format!(
781            "Abelian subgroup of P_{} stabilizing a code space",
782            self.num_qubits
783        )
784    }
785}
786/// A Grover oracle that marks a single target basis state.
787///
788/// The oracle implements the phase-flip O|x⟩ = -|x⟩ if x == target, else |x⟩.
789#[derive(Debug, Clone)]
790pub struct GroverOracle {
791    /// The target basis state (integer index).
792    pub target: usize,
793}
794impl GroverOracle {
795    /// Create an oracle for the given target basis state.
796    pub fn new(target: usize) -> Self {
797        GroverOracle { target }
798    }
799    /// Apply the oracle phase-flip to the register amplitudes.
800    pub fn apply(&self, reg: &mut QuantumRegister) {
801        if self.target < reg.size() {
802            reg.sv.amplitudes[self.target] = reg.sv.amplitudes[self.target].scale(-1.0);
803        }
804    }
805    /// Apply the Grover diffusion operator (inversion about the mean) to the
806    /// register.
807    pub fn diffusion(reg: &mut QuantumRegister) {
808        let n = reg.size();
809        let mean = reg
810            .sv
811            .amplitudes
812            .iter()
813            .fold(Complex::zero(), |acc, a| acc.add(a))
814            .scale(1.0 / n as f64);
815        for amp in reg.sv.amplitudes.iter_mut() {
816            let two_mean = mean.scale(2.0);
817            *amp = Complex {
818                re: two_mean.re - amp.re,
819                im: two_mean.im - amp.im,
820            };
821        }
822    }
823    /// Run Grover's algorithm for `iterations` steps and return the register.
824    pub fn run_grover(&self, n_qubits: usize, iterations: u32) -> QuantumRegister {
825        let mut reg = QuantumRegister::new(n_qubits);
826        reg.prepare_uniform_superposition();
827        for _ in 0..iterations {
828            self.apply(&mut reg);
829            Self::diffusion(&mut reg);
830        }
831        reg
832    }
833}
834/// Quantum Shannon theory capacities.
835#[allow(dead_code)]
836#[derive(Debug, Clone)]
837pub struct QuantumCapacities {
838    pub channel_name: String,
839    pub classical_capacity: f64,
840    pub quantum_capacity: f64,
841    pub entanglement_assisted_capacity: f64,
842    pub private_capacity: f64,
843}
844impl QuantumCapacities {
845    #[allow(dead_code)]
846    pub fn for_qubit_channel(name: &str, cc: f64, qc: f64, ea: f64, pc: f64) -> Self {
847        Self {
848            channel_name: name.to_string(),
849            classical_capacity: cc,
850            quantum_capacity: qc,
851            entanglement_assisted_capacity: ea,
852            private_capacity: pc,
853        }
854    }
855    #[allow(dead_code)]
856    pub fn quantum_is_at_most_private(&self) -> bool {
857        self.quantum_capacity <= self.private_capacity + 1e-10
858    }
859    #[allow(dead_code)]
860    pub fn superdense_coding_factor(&self) -> f64 {
861        if self.classical_capacity > 0.0 {
862            self.entanglement_assisted_capacity / self.classical_capacity
863        } else {
864            0.0
865        }
866    }
867}
868/// An n-qubit Pauli string (tensor product of single-qubit Paulis), optionally
869/// with a sign.
870#[derive(Debug, Clone)]
871pub struct PauliString {
872    /// Factor: +1 or -1.
873    pub sign: i8,
874    /// One Pauli per qubit.
875    pub paulis: Vec<Pauli>,
876}
877impl PauliString {
878    /// Create a new Pauli string with the given sign and per-qubit Paulis.
879    pub fn new(sign: i8, paulis: Vec<Pauli>) -> Self {
880        PauliString { sign, paulis }
881    }
882    /// The all-identity Pauli string on `n` qubits.
883    pub fn identity(n: usize) -> Self {
884        PauliString {
885            sign: 1,
886            paulis: vec![Pauli::I; n],
887        }
888    }
889    /// Number of qubits.
890    pub fn n_qubits(&self) -> usize {
891        self.paulis.len()
892    }
893    /// Component-wise Pauli product (ignoring phase from YY products).
894    pub fn mul(&self, other: &PauliString) -> PauliString {
895        assert_eq!(self.paulis.len(), other.paulis.len());
896        let paulis: Vec<Pauli> = self
897            .paulis
898            .iter()
899            .zip(other.paulis.iter())
900            .map(|(&a, &b)| a.mul(b))
901            .collect();
902        PauliString {
903            sign: self.sign * other.sign,
904            paulis,
905        }
906    }
907    /// Check that this Pauli string commutes with `other`.
908    ///
909    /// Two Pauli strings commute iff the number of qubit positions where they
910    /// anti-commute is even.
911    pub fn commutes_with(&self, other: &PauliString) -> bool {
912        let anti: usize = self
913            .paulis
914            .iter()
915            .zip(other.paulis.iter())
916            .filter(|(&a, &b)| !a.commutes_with(b))
917            .count();
918        anti % 2 == 0
919    }
920}
921/// Variational Quantum Eigensolver (VQE) configuration.
922#[allow(dead_code)]
923#[derive(Debug, Clone)]
924pub struct VqeConfig {
925    pub hamiltonian_name: String,
926    pub ansatz_name: String,
927    pub num_layers: usize,
928    pub num_qubits: usize,
929}
930impl VqeConfig {
931    #[allow(dead_code)]
932    pub fn new(ham: &str, ansatz: &str, layers: usize, qubits: usize) -> Self {
933        Self {
934            hamiltonian_name: ham.to_string(),
935            ansatz_name: ansatz.to_string(),
936            num_layers: layers,
937            num_qubits: qubits,
938        }
939    }
940    #[allow(dead_code)]
941    pub fn num_parameters(&self) -> usize {
942        self.num_layers * self.num_qubits * 2
943    }
944    #[allow(dead_code)]
945    pub fn is_hybrid_classical_quantum(&self) -> bool {
946        true
947    }
948    #[allow(dead_code)]
949    pub fn variational_principle(&self) -> String {
950        format!(
951            "E(theta) = <psi(theta)|H|psi(theta)> >= E_ground for {}",
952            self.hamiltonian_name
953        )
954    }
955}
956/// A single-qubit (2×2 unitary) quantum gate.
957#[derive(Debug, Clone)]
958pub struct Gate2x2 {
959    pub matrix: [[Complex; 2]; 2],
960    pub name: String,
961}
962impl Gate2x2 {
963    /// Pauli-X (NOT) gate: [[0,1],[1,0]].
964    pub fn pauli_x() -> Self {
965        Gate2x2 {
966            name: "X".to_string(),
967            matrix: [
968                [Complex::zero(), Complex::one()],
969                [Complex::one(), Complex::zero()],
970            ],
971        }
972    }
973    /// Pauli-Y gate: [[0,−i],[i,0]].
974    pub fn pauli_y() -> Self {
975        Gate2x2 {
976            name: "Y".to_string(),
977            matrix: [
978                [Complex::zero(), Complex::new(0.0, -1.0)],
979                [Complex::new(0.0, 1.0), Complex::zero()],
980            ],
981        }
982    }
983    /// Pauli-Z gate: [[1,0],[0,−1]].
984    pub fn pauli_z() -> Self {
985        Gate2x2 {
986            name: "Z".to_string(),
987            matrix: [
988                [Complex::one(), Complex::zero()],
989                [Complex::zero(), Complex::new(-1.0, 0.0)],
990            ],
991        }
992    }
993    /// Hadamard gate: H = 1/√2 [[1,1],[1,−1]].
994    pub fn hadamard() -> Self {
995        let v = Complex::new(1.0 / 2.0f64.sqrt(), 0.0);
996        let neg_v = Complex::new(-1.0 / 2.0f64.sqrt(), 0.0);
997        Gate2x2 {
998            name: "H".to_string(),
999            matrix: [[v, v], [v, neg_v]],
1000        }
1001    }
1002    /// Phase gate: [[1,0],[0,e^{iθ}]].
1003    pub fn phase(theta: f64) -> Self {
1004        Gate2x2 {
1005            name: format!("P({theta:.4})"),
1006            matrix: [
1007                [Complex::one(), Complex::zero()],
1008                [Complex::zero(), Complex::exp(theta)],
1009            ],
1010        }
1011    }
1012    /// T gate: phase(Ï€/4).
1013    pub fn t_gate() -> Self {
1014        let mut g = Self::phase(PI / 4.0);
1015        g.name = "T".to_string();
1016        g
1017    }
1018    /// S gate: phase(Ï€/2).
1019    pub fn s_gate() -> Self {
1020        let mut g = Self::phase(PI / 2.0);
1021        g.name = "S".to_string();
1022        g
1023    }
1024    /// Apply this gate to a qubit: |ψ'⟩ = U|ψ⟩.
1025    pub fn apply(&self, qubit: &Qubit) -> Qubit {
1026        let a = self.matrix[0][0]
1027            .mul(&qubit.alpha)
1028            .add(&self.matrix[0][1].mul(&qubit.beta));
1029        let b = self.matrix[1][0]
1030            .mul(&qubit.alpha)
1031            .add(&self.matrix[1][1].mul(&qubit.beta));
1032        Qubit { alpha: a, beta: b }
1033    }
1034    /// Compose gates: returns self · other (apply `other` first, then `self`).
1035    pub fn compose(&self, other: &Gate2x2) -> Gate2x2 {
1036        let mut m = [[Complex::zero(); 2]; 2];
1037        for i in 0..2 {
1038            for j in 0..2 {
1039                for k in 0..2 {
1040                    m[i][j] = m[i][j].add(&self.matrix[i][k].mul(&other.matrix[k][j]));
1041                }
1042            }
1043        }
1044        Gate2x2 {
1045            name: format!("{}·{}", self.name, other.name),
1046            matrix: m,
1047        }
1048    }
1049    /// Check that U†U ≈ I (unitarity).
1050    pub fn is_unitary(&self) -> bool {
1051        let tol = 1e-9;
1052        for i in 0..2 {
1053            for j in 0..2 {
1054                let mut sum = Complex::zero();
1055                for k in 0..2 {
1056                    sum = sum.add(&self.matrix[k][i].conj().mul(&self.matrix[k][j]));
1057                }
1058                let expected_re = if i == j { 1.0 } else { 0.0 };
1059                if (sum.re - expected_re).abs() > tol || sum.im.abs() > tol {
1060                    return false;
1061                }
1062            }
1063        }
1064        true
1065    }
1066}
1067/// A quantum circuit as a sequence of gate applications.
1068#[allow(dead_code)]
1069#[derive(Debug, Clone)]
1070pub struct QuantumCircuitData {
1071    pub num_qubits: usize,
1072    pub gates: Vec<(QuantumGate, Vec<usize>)>,
1073    pub depth: usize,
1074}
1075impl QuantumCircuitData {
1076    #[allow(dead_code)]
1077    pub fn new(n: usize) -> Self {
1078        Self {
1079            num_qubits: n,
1080            gates: Vec::new(),
1081            depth: 0,
1082        }
1083    }
1084    #[allow(dead_code)]
1085    pub fn apply(&mut self, gate: QuantumGate, qubits: Vec<usize>) {
1086        self.depth += 1;
1087        self.gates.push((gate, qubits));
1088    }
1089    #[allow(dead_code)]
1090    pub fn gate_count(&self) -> usize {
1091        self.gates.len()
1092    }
1093    #[allow(dead_code)]
1094    pub fn t_count(&self) -> usize {
1095        self.gates.iter().filter(|(g, _)| g.name == "T").count()
1096    }
1097    #[allow(dead_code)]
1098    pub fn is_clifford(&self) -> bool {
1099        self.gates.iter().all(|(g, _)| g.is_clifford)
1100    }
1101}
1102/// Quantum channel (completely positive trace-preserving map).
1103#[allow(dead_code)]
1104#[derive(Debug, Clone)]
1105pub struct QuantumChannel {
1106    pub name: String,
1107    pub input_dim: usize,
1108    pub output_dim: usize,
1109    pub channel_type: ChannelType,
1110}
1111impl QuantumChannel {
1112    #[allow(dead_code)]
1113    pub fn depolarizing(dim: usize, _p: f64) -> Self {
1114        Self {
1115            name: format!("Depolarizing(dim={dim})"),
1116            input_dim: dim,
1117            output_dim: dim,
1118            channel_type: ChannelType::Depolarizing,
1119        }
1120    }
1121    #[allow(dead_code)]
1122    pub fn amplitude_damping(gamma: f64) -> Self {
1123        let name = format!("AmplitudeDamping(gamma={gamma:.3})");
1124        Self {
1125            name,
1126            input_dim: 2,
1127            output_dim: 2,
1128            channel_type: ChannelType::AmplitudeDamping,
1129        }
1130    }
1131    #[allow(dead_code)]
1132    pub fn is_unital(&self) -> bool {
1133        matches!(
1134            self.channel_type,
1135            ChannelType::Depolarizing
1136                | ChannelType::PhaseDamping
1137                | ChannelType::BitFlip
1138                | ChannelType::PhaseFlip
1139                | ChannelType::Unitary
1140                | ChannelType::Identity
1141        )
1142    }
1143    #[allow(dead_code)]
1144    pub fn is_degradable(&self) -> bool {
1145        matches!(
1146            self.channel_type,
1147            ChannelType::AmplitudeDamping | ChannelType::Unitary
1148        )
1149    }
1150    #[allow(dead_code)]
1151    pub fn quantum_capacity_achievable(&self) -> bool {
1152        !matches!(self.channel_type, ChannelType::Erasure)
1153    }
1154}
1155/// A stabilizer state specified by its stabilizer group generators.
1156///
1157/// The stabilizer state |ψ⟩ is the unique state satisfying g|ψ⟩ = |ψ⟩ for
1158/// every generator g in the stabilizer group.
1159#[derive(Debug, Clone)]
1160pub struct StabilizerState {
1161    pub n_qubits: usize,
1162    /// Generators of the stabilizer group (each is an n-qubit Pauli string).
1163    pub generators: Vec<PauliString>,
1164}
1165impl StabilizerState {
1166    /// Create a stabilizer state from a list of generators.
1167    ///
1168    /// Assumes the generators are already a valid independent commuting set.
1169    pub fn from_generators(n_qubits: usize, generators: Vec<PauliString>) -> Self {
1170        StabilizerState {
1171            n_qubits,
1172            generators,
1173        }
1174    }
1175    /// The |0…0⟩ state, stabilized by Z_0, Z_1, …, Z_{n-1}.
1176    pub fn computational_zero(n_qubits: usize) -> Self {
1177        let generators: Vec<PauliString> = (0..n_qubits)
1178            .map(|i| {
1179                let mut paulis = vec![Pauli::I; n_qubits];
1180                paulis[i] = Pauli::Z;
1181                PauliString::new(1, paulis)
1182            })
1183            .collect();
1184        StabilizerState {
1185            n_qubits,
1186            generators,
1187        }
1188    }
1189    /// The |+…+⟩ state, stabilized by X_0, X_1, …, X_{n-1}.
1190    pub fn plus_state(n_qubits: usize) -> Self {
1191        let generators: Vec<PauliString> = (0..n_qubits)
1192            .map(|i| {
1193                let mut paulis = vec![Pauli::I; n_qubits];
1194                paulis[i] = Pauli::X;
1195                PauliString::new(1, paulis)
1196            })
1197            .collect();
1198        StabilizerState {
1199            n_qubits,
1200            generators,
1201        }
1202    }
1203    /// Check that all generators commute pairwise (consistency requirement).
1204    pub fn is_consistent(&self) -> bool {
1205        let n = self.generators.len();
1206        for i in 0..n {
1207            for j in (i + 1)..n {
1208                if !self.generators[i].commutes_with(&self.generators[j]) {
1209                    return false;
1210                }
1211            }
1212        }
1213        true
1214    }
1215    /// Measure a Pauli observable P on this stabilizer state.
1216    ///
1217    /// Returns `Some(+1)` if P is in the stabilizer group, `Some(-1)` if -P
1218    /// is in the stabilizer group, or `None` if the outcome is probabilistic
1219    /// (P anti-commutes with some generator).
1220    pub fn measure_pauli(&self, p: &PauliString) -> Option<i8> {
1221        let all_commute = self.generators.iter().all(|g| p.commutes_with(g));
1222        if !all_commute {
1223            return None;
1224        }
1225        let _product = p.mul(&PauliString::identity(self.n_qubits));
1226        Some(p.sign)
1227    }
1228}
1229/// A quantum circuit: an ordered list of gate operations.
1230#[derive(Debug, Clone, Default)]
1231pub struct QuantumCircuit {
1232    pub n_qubits: usize,
1233    pub ops: Vec<GateOp>,
1234}
1235impl QuantumCircuit {
1236    /// Create an empty circuit on `n_qubits` qubits.
1237    pub fn new(n_qubits: usize) -> Self {
1238        QuantumCircuit {
1239            n_qubits,
1240            ops: Vec::new(),
1241        }
1242    }
1243    /// Append a single-qubit gate on qubit `q`.
1244    pub fn add_gate(&mut self, gate: Gate2x2, qubit: usize) {
1245        self.ops.push(GateOp::Single { gate, qubit });
1246    }
1247    /// Append a CNOT gate.
1248    pub fn add_cnot(&mut self, control: usize, target: usize) {
1249        self.ops.push(GateOp::Cnot { control, target });
1250    }
1251    /// Number of gate operations.
1252    pub fn depth(&self) -> usize {
1253        self.ops.len()
1254    }
1255    /// Execute the circuit on a fresh |0…0⟩ state and return the resulting
1256    /// `QuantumRegister`.
1257    pub fn run(&self) -> QuantumRegister {
1258        let mut reg = QuantumRegister::new(self.n_qubits);
1259        for op in &self.ops {
1260            match op {
1261                GateOp::Single { gate, qubit } => reg.apply_gate(gate, *qubit),
1262                GateOp::Cnot { control, target } => reg.apply_cnot(*control, *target),
1263            }
1264        }
1265        reg
1266    }
1267    /// Execute the circuit on a fresh state and measure all qubits.
1268    ///
1269    /// Returns a `Vec<u8>` of 0/1 measurement outcomes (qubit 0 first).
1270    pub fn run_and_measure(&self, seed: u64) -> Vec<u8> {
1271        let reg = self.run();
1272        (0..self.n_qubits)
1273            .map(|q| reg.measure_qubit(q, seed.wrapping_add(q as u64 * 1234567891)))
1274            .collect()
1275    }
1276}
1277#[allow(dead_code)]
1278#[derive(Debug, Clone, PartialEq, Eq)]
1279pub enum QuantumWalkType {
1280    ContinuousTime,
1281    DiscreteTimeCoin,
1282    Scattering,
1283}