Skip to main content

proof_engine/quantum/
computing.rs

1use std::f64::consts::PI;
2use super::schrodinger::Complex;
3use super::entanglement::QubitState;
4
5/// Alias for single qubit.
6pub type Qubit = QubitState;
7
8/// Quantum register: n qubits with 2^n amplitudes.
9#[derive(Clone, Debug)]
10pub struct QuantumRegister {
11    pub n_qubits: usize,
12    pub state: Vec<Complex>,
13}
14
15impl QuantumRegister {
16    pub fn new(n_qubits: usize) -> Self {
17        let size = 1 << n_qubits;
18        let mut state = vec![Complex::zero(); size];
19        state[0] = Complex::one(); // |00...0>
20        Self { n_qubits, state }
21    }
22
23    pub fn from_state(n_qubits: usize, state: Vec<Complex>) -> Self {
24        Self { n_qubits, state }
25    }
26
27    pub fn size(&self) -> usize {
28        self.state.len()
29    }
30
31    pub fn norm_sq(&self) -> f64 {
32        self.state.iter().map(|c| c.norm_sq()).sum()
33    }
34
35    pub fn normalize(&mut self) {
36        let n = self.norm_sq().sqrt();
37        if n > 1e-30 {
38            for c in &mut self.state {
39                *c = *c / n;
40            }
41        }
42    }
43}
44
45/// Quantum gate: a unitary matrix.
46#[derive(Clone, Debug)]
47pub struct QuantumGate {
48    pub matrix: Vec<Vec<Complex>>,
49    pub n_qubits: usize,
50    pub name: String,
51}
52
53impl QuantumGate {
54    pub fn new(matrix: Vec<Vec<Complex>>, n_qubits: usize, name: &str) -> Self {
55        Self { matrix, n_qubits, name: name.to_string() }
56    }
57
58    pub fn dim(&self) -> usize {
59        self.matrix.len()
60    }
61}
62
63// --- Standard gates ---
64
65pub fn hadamard() -> QuantumGate {
66    let s = 1.0 / 2.0_f64.sqrt();
67    QuantumGate::new(
68        vec![
69            vec![Complex::new(s, 0.0), Complex::new(s, 0.0)],
70            vec![Complex::new(s, 0.0), Complex::new(-s, 0.0)],
71        ],
72        1,
73        "H",
74    )
75}
76
77pub fn pauli_x() -> QuantumGate {
78    QuantumGate::new(
79        vec![
80            vec![Complex::zero(), Complex::one()],
81            vec![Complex::one(), Complex::zero()],
82        ],
83        1,
84        "X",
85    )
86}
87
88pub fn pauli_y() -> QuantumGate {
89    QuantumGate::new(
90        vec![
91            vec![Complex::zero(), Complex::new(0.0, -1.0)],
92            vec![Complex::new(0.0, 1.0), Complex::zero()],
93        ],
94        1,
95        "Y",
96    )
97}
98
99pub fn pauli_z() -> QuantumGate {
100    QuantumGate::new(
101        vec![
102            vec![Complex::one(), Complex::zero()],
103            vec![Complex::zero(), Complex::new(-1.0, 0.0)],
104        ],
105        1,
106        "Z",
107    )
108}
109
110pub fn phase(theta: f64) -> QuantumGate {
111    QuantumGate::new(
112        vec![
113            vec![Complex::one(), Complex::zero()],
114            vec![Complex::zero(), Complex::from_polar(1.0, theta)],
115        ],
116        1,
117        "P",
118    )
119}
120
121pub fn t_gate() -> QuantumGate {
122    phase(PI / 4.0)
123}
124
125pub fn cnot() -> QuantumGate {
126    QuantumGate::new(
127        vec![
128            vec![Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()],
129            vec![Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()],
130            vec![Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()],
131            vec![Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()],
132        ],
133        2,
134        "CNOT",
135    )
136}
137
138pub fn swap() -> QuantumGate {
139    QuantumGate::new(
140        vec![
141            vec![Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()],
142            vec![Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()],
143            vec![Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()],
144            vec![Complex::zero(), Complex::zero(), Complex::zero(), Complex::one()],
145        ],
146        2,
147        "SWAP",
148    )
149}
150
151pub fn toffoli() -> QuantumGate {
152    let mut m = vec![vec![Complex::zero(); 8]; 8];
153    for i in 0..6 {
154        m[i][i] = Complex::one();
155    }
156    // Swap |110> and |111>
157    m[6][7] = Complex::one();
158    m[7][6] = Complex::one();
159    QuantumGate::new(m, 3, "Toffoli")
160}
161
162/// Apply a gate to specific target qubits in a register.
163pub fn apply_gate(register: &QuantumRegister, gate: &QuantumGate, target_qubits: &[usize]) -> QuantumRegister {
164    let n = register.n_qubits;
165    let size = register.size();
166    let g_dim = gate.dim();
167
168    let mut new_state = vec![Complex::zero(); size];
169
170    for i in 0..size {
171        if register.state[i].norm_sq() < 1e-30 {
172            continue;
173        }
174
175        // Extract target qubit values from state index i
176        let target_val = extract_bits(i, target_qubits, n);
177
178        // For each possible output of the gate
179        for out_val in 0..g_dim {
180            let coeff = gate.matrix[out_val][target_val];
181            if coeff.norm_sq() < 1e-30 {
182                continue;
183            }
184            // Compute the output state index
185            let j = replace_bits(i, target_qubits, out_val, n);
186            new_state[j] += register.state[i] * coeff;
187        }
188    }
189
190    QuantumRegister::from_state(n, new_state)
191}
192
193/// Extract bits at given positions from a state index.
194fn extract_bits(state_idx: usize, positions: &[usize], n_qubits: usize) -> usize {
195    let mut val = 0;
196    for (k, &pos) in positions.iter().enumerate() {
197        let bit = (state_idx >> (n_qubits - 1 - pos)) & 1;
198        val |= bit << (positions.len() - 1 - k);
199    }
200    val
201}
202
203/// Replace bits at given positions in a state index.
204fn replace_bits(state_idx: usize, positions: &[usize], new_val: usize, n_qubits: usize) -> usize {
205    let mut result = state_idx;
206    for (k, &pos) in positions.iter().enumerate() {
207        let bit = (new_val >> (positions.len() - 1 - k)) & 1;
208        let mask = 1 << (n_qubits - 1 - pos);
209        if bit == 1 {
210            result |= mask;
211        } else {
212            result &= !mask;
213        }
214    }
215    result
216}
217
218/// Measure all qubits in a register.
219pub fn measure_register(register: &QuantumRegister, rng_val: f64) -> (Vec<u8>, QuantumRegister) {
220    let n = register.n_qubits;
221    let size = register.size();
222    let mut cumulative = 0.0;
223    let mut outcome = 0;
224
225    for i in 0..size {
226        cumulative += register.state[i].norm_sq();
227        if rng_val < cumulative {
228            outcome = i;
229            break;
230        }
231        if i == size - 1 {
232            outcome = i;
233        }
234    }
235
236    let bits: Vec<u8> = (0..n)
237        .map(|bit| ((outcome >> (n - 1 - bit)) & 1) as u8)
238        .collect();
239
240    let mut new_state = vec![Complex::zero(); size];
241    new_state[outcome] = Complex::one();
242    let new_reg = QuantumRegister::from_state(n, new_state);
243
244    (bits, new_reg)
245}
246
247/// Measure a single qubit in a register.
248pub fn measure_qubit_in_register(
249    register: &QuantumRegister,
250    qubit_index: usize,
251    rng_val: f64,
252) -> (u8, QuantumRegister) {
253    let n = register.n_qubits;
254    let size = register.size();
255    let mask = 1 << (n - 1 - qubit_index);
256
257    // Probability of measuring 0
258    let p0: f64 = (0..size)
259        .filter(|&i| (i & mask) == 0)
260        .map(|i| register.state[i].norm_sq())
261        .sum();
262
263    let outcome = if rng_val < p0 { 0u8 } else { 1u8 };
264
265    // Collapse
266    let mut new_state = vec![Complex::zero(); size];
267    let norm = if outcome == 0 { p0.sqrt() } else { (1.0 - p0).sqrt() };
268
269    for i in 0..size {
270        let bit = if (i & mask) == 0 { 0 } else { 1 };
271        if bit == outcome {
272            new_state[i] = register.state[i] / norm.max(1e-30);
273        }
274    }
275
276    (outcome, QuantumRegister::from_state(n, new_state))
277}
278
279/// Quantum circuit: sequence of gate applications.
280#[derive(Clone, Debug)]
281pub struct QuantumCircuit {
282    pub gates: Vec<(QuantumGate, Vec<usize>)>,
283}
284
285impl QuantumCircuit {
286    pub fn new() -> Self {
287        Self { gates: Vec::new() }
288    }
289
290    pub fn add_gate(&mut self, gate: QuantumGate, targets: Vec<usize>) {
291        self.gates.push((gate, targets));
292    }
293}
294
295/// Execute a circuit on a register.
296pub fn execute(circuit: &QuantumCircuit, register: &QuantumRegister) -> QuantumRegister {
297    let mut reg = register.clone();
298    for (gate, targets) in &circuit.gates {
299        reg = apply_gate(&reg, gate, targets);
300    }
301    reg
302}
303
304/// Deutsch-Jozsa algorithm: determine if an oracle function is constant or balanced.
305/// oracle_fn: takes an n-bit input and returns 0 or 1.
306/// Returns true if balanced, false if constant.
307pub fn deutsch_jozsa(n_qubits: usize, oracle_fn: &dyn Fn(usize) -> u8) -> bool {
308    let n = n_qubits;
309    let total = n + 1; // n input qubits + 1 output qubit
310    let size = 1 << total;
311
312    // Initial state: |0...0>|1>
313    let mut reg = QuantumRegister::new(total);
314    // Set last qubit to |1>
315    reg.state[0] = Complex::zero();
316    reg.state[1] = Complex::one(); // |0...01>
317
318    // Apply H to all qubits
319    for i in 0..total {
320        reg = apply_gate(&reg, &hadamard(), &[i]);
321    }
322
323    // Apply oracle: |x>|y> -> |x>|y XOR f(x)>
324    let mut new_state = vec![Complex::zero(); size];
325    for i in 0..size {
326        let input = i >> 1; // first n qubits
327        let output_bit = i & 1; // last qubit
328        let f_x = oracle_fn(input % (1 << n)) as usize;
329        let new_output = output_bit ^ f_x;
330        let j = (input << 1) | new_output;
331        new_state[j] += reg.state[i];
332    }
333    reg.state = new_state;
334
335    // Apply H to input qubits
336    for i in 0..n {
337        reg = apply_gate(&reg, &hadamard(), &[i]);
338    }
339
340    // Measure input qubits: if all 0, function is constant
341    let input_zero_prob: f64 = (0..size)
342        .filter(|&i| (i >> 1) == 0)
343        .map(|i| reg.state[i].norm_sq())
344        .sum();
345
346    // If probability of |0...0> is ~1, function is constant
347    input_zero_prob < 0.5
348}
349
350/// Grover's search algorithm.
351/// oracle: marks the target state (returns true for target).
352/// Returns the found state index.
353pub fn grover_search(oracle: &dyn Fn(usize) -> bool, n_qubits: usize, iterations: usize) -> usize {
354    let size = 1 << n_qubits;
355    let mut reg = QuantumRegister::new(n_qubits);
356
357    // Apply H to all qubits to create uniform superposition
358    for i in 0..n_qubits {
359        reg = apply_gate(&reg, &hadamard(), &[i]);
360    }
361
362    for _ in 0..iterations {
363        // Oracle: flip sign of target states
364        for i in 0..size {
365            if oracle(i) {
366                reg.state[i] = -reg.state[i];
367            }
368        }
369
370        // Diffusion operator: 2|s><s| - I
371        // Apply H to all
372        for q in 0..n_qubits {
373            reg = apply_gate(&reg, &hadamard(), &[q]);
374        }
375        // Flip all except |0>
376        for i in 1..size {
377            reg.state[i] = -reg.state[i];
378        }
379        // Apply H to all
380        for q in 0..n_qubits {
381            reg = apply_gate(&reg, &hadamard(), &[q]);
382        }
383    }
384
385    // Find most probable state
386    let mut max_prob = 0.0;
387    let mut max_idx = 0;
388    for i in 0..size {
389        let prob = reg.state[i].norm_sq();
390        if prob > max_prob {
391            max_prob = prob;
392            max_idx = i;
393        }
394    }
395    max_idx
396}
397
398/// Quantum Fourier Transform on a register.
399pub fn qft(register: &QuantumRegister) -> QuantumRegister {
400    let n = register.n_qubits;
401    let mut reg = register.clone();
402
403    for i in 0..n {
404        // Apply Hadamard to qubit i
405        reg = apply_gate(&reg, &hadamard(), &[i]);
406
407        // Apply controlled phase rotations
408        for j in (i + 1)..n {
409            let k = j - i;
410            let theta = PI / (1 << k) as f64;
411            // Controlled phase: if qubit j is 1, apply phase to qubit i
412            let cp = controlled_phase(theta);
413            reg = apply_gate(&reg, &cp, &[j, i]);
414        }
415    }
416
417    // Reverse qubit order
418    for i in 0..n / 2 {
419        reg = apply_gate(&reg, &swap(), &[i, n - 1 - i]);
420    }
421
422    reg
423}
424
425fn controlled_phase(theta: f64) -> QuantumGate {
426    QuantumGate::new(
427        vec![
428            vec![Complex::one(), Complex::zero(), Complex::zero(), Complex::zero()],
429            vec![Complex::zero(), Complex::one(), Complex::zero(), Complex::zero()],
430            vec![Complex::zero(), Complex::zero(), Complex::one(), Complex::zero()],
431            vec![Complex::zero(), Complex::zero(), Complex::zero(), Complex::from_polar(1.0, theta)],
432        ],
433        2,
434        "CP",
435    )
436}
437
438/// Render circuit diagram as ASCII.
439pub struct CircuitRenderer;
440
441impl CircuitRenderer {
442    pub fn render(circuit: &QuantumCircuit, n_qubits: usize) -> Vec<String> {
443        let mut lines: Vec<String> = (0..n_qubits)
444            .map(|i| format!("q{}: ", i))
445            .collect();
446
447        for (gate, targets) in &circuit.gates {
448            let max_target = targets.iter().copied().max().unwrap_or(0);
449            let min_target = targets.iter().copied().min().unwrap_or(0);
450
451            // Add gate symbol
452            for q in 0..n_qubits {
453                if targets.contains(&q) {
454                    if targets.len() == 1 {
455                        lines[q].push_str(&format!("[{}]", gate.name));
456                    } else if q == targets[0] {
457                        lines[q].push_str(&format!("[{}]", gate.name));
458                    } else {
459                        lines[q].push_str(" * ");
460                    }
461                } else if q > min_target && q < max_target {
462                    lines[q].push_str(" | ");
463                } else {
464                    lines[q].push_str("---");
465                }
466            }
467
468            // Separator
469            for q in 0..n_qubits {
470                lines[q].push('-');
471            }
472        }
473
474        lines
475    }
476}
477
478#[cfg(test)]
479mod tests {
480    use super::*;
481
482    #[test]
483    fn test_hadamard_squared_is_identity() {
484        let mut reg = QuantumRegister::new(1);
485        reg = apply_gate(&reg, &hadamard(), &[0]);
486        reg = apply_gate(&reg, &hadamard(), &[0]);
487        // Should be back to |0>
488        assert!((reg.state[0].re - 1.0).abs() < 1e-10);
489        assert!(reg.state[1].norm() < 1e-10);
490    }
491
492    #[test]
493    fn test_hadamard_creates_superposition() {
494        let mut reg = QuantumRegister::new(1);
495        reg = apply_gate(&reg, &hadamard(), &[0]);
496        let s = 1.0 / 2.0_f64.sqrt();
497        assert!((reg.state[0].re - s).abs() < 1e-10);
498        assert!((reg.state[1].re - s).abs() < 1e-10);
499    }
500
501    #[test]
502    fn test_pauli_x_flips() {
503        let mut reg = QuantumRegister::new(1);
504        reg = apply_gate(&reg, &pauli_x(), &[0]);
505        assert!(reg.state[0].norm() < 1e-10);
506        assert!((reg.state[1].re - 1.0).abs() < 1e-10);
507    }
508
509    #[test]
510    fn test_cnot_entangles() {
511        // |00> -> H on qubit 0 -> (|0>+|1>)/sqrt(2) |0> -> CNOT -> (|00>+|11>)/sqrt(2)
512        let mut reg = QuantumRegister::new(2);
513        reg = apply_gate(&reg, &hadamard(), &[0]);
514        reg = apply_gate(&reg, &cnot(), &[0, 1]);
515
516        let s = 1.0 / 2.0_f64.sqrt();
517        assert!((reg.state[0].re - s).abs() < 1e-10, "|00>: {}", reg.state[0].re); // |00>
518        assert!(reg.state[1].norm() < 1e-10);  // |01>
519        assert!(reg.state[2].norm() < 1e-10);  // |10>
520        assert!((reg.state[3].re - s).abs() < 1e-10, "|11>: {}", reg.state[3].re); // |11>
521    }
522
523    #[test]
524    fn test_measure_register() {
525        let mut reg = QuantumRegister::new(2);
526        // Set to |01>
527        reg.state[0] = Complex::zero();
528        reg.state[1] = Complex::one();
529
530        let (bits, _) = measure_register(&reg, 0.5);
531        assert_eq!(bits, vec![0, 1]);
532    }
533
534    #[test]
535    fn test_measure_qubit() {
536        let mut reg = QuantumRegister::new(2);
537        reg = apply_gate(&reg, &hadamard(), &[0]);
538        // State is (|00> + |10>)/sqrt(2)
539
540        let (outcome, new_reg) = measure_qubit_in_register(&reg, 0, 0.3);
541        let norm = new_reg.norm_sq();
542        assert!((norm - 1.0).abs() < 1e-6, "Post-measurement norm: {}", norm);
543    }
544
545    #[test]
546    fn test_swap_gate() {
547        let mut reg = QuantumRegister::new(2);
548        // Set to |01>
549        reg.state[0] = Complex::zero();
550        reg.state[1] = Complex::one();
551
552        reg = apply_gate(&reg, &swap(), &[0, 1]);
553        // Should be |10>
554        assert!(reg.state[1].norm() < 1e-10);
555        assert!((reg.state[2].re - 1.0).abs() < 1e-10);
556    }
557
558    #[test]
559    fn test_circuit_execution() {
560        let mut circuit = QuantumCircuit::new();
561        circuit.add_gate(hadamard(), vec![0]);
562        circuit.add_gate(cnot(), vec![0, 1]);
563
564        let reg = QuantumRegister::new(2);
565        let result = execute(&circuit, &reg);
566
567        let s = 1.0 / 2.0_f64.sqrt();
568        assert!((result.state[0].re - s).abs() < 1e-10);
569        assert!((result.state[3].re - s).abs() < 1e-10);
570    }
571
572    #[test]
573    fn test_deutsch_jozsa_constant() {
574        // Constant function: f(x) = 0
575        let is_balanced = deutsch_jozsa(2, &|_x| 0);
576        assert!(!is_balanced, "Constant function should not be balanced");
577    }
578
579    #[test]
580    fn test_deutsch_jozsa_balanced() {
581        // Balanced function: f(x) = x & 1 (LSB)
582        let is_balanced = deutsch_jozsa(2, &|x| (x & 1) as u8);
583        assert!(is_balanced, "Balanced function should be detected");
584    }
585
586    #[test]
587    fn test_grover_search() {
588        // Search for state |3> = |11> in 2-qubit space
589        let target = 3;
590        let result = grover_search(&|x| x == target, 2, 1);
591        assert_eq!(result, target, "Grover should find target {}, got {}", target, result);
592    }
593
594    #[test]
595    fn test_grover_larger() {
596        // 3-qubit search for |5> = |101>
597        let target = 5;
598        let n = 3;
599        let iterations = ((PI / 4.0) * (8.0_f64).sqrt()).floor() as usize;
600        let result = grover_search(&|x| x == target, n, iterations);
601        assert_eq!(result, target, "Grover should find {}, got {}", target, result);
602    }
603
604    #[test]
605    fn test_qft_basic() {
606        // QFT of |0> should give uniform superposition
607        let reg = QuantumRegister::new(2);
608        let result = qft(&reg);
609        let expected = 0.5; // 1/sqrt(4) squared
610        for i in 0..4 {
611            assert!(
612                (result.state[i].norm_sq() - expected).abs() < 0.1,
613                "QFT |0>[{}] prob: {}",
614                i,
615                result.state[i].norm_sq()
616            );
617        }
618    }
619
620    #[test]
621    fn test_qft_preserves_norm() {
622        let mut reg = QuantumRegister::new(3);
623        reg = apply_gate(&reg, &hadamard(), &[0]);
624        reg = apply_gate(&reg, &pauli_x(), &[1]);
625        let norm_before = reg.norm_sq();
626        let result = qft(&reg);
627        let norm_after = result.norm_sq();
628        assert!((norm_after - norm_before).abs() < 0.1, "QFT norm: {} -> {}", norm_before, norm_after);
629    }
630
631    #[test]
632    fn test_toffoli() {
633        // Toffoli only flips target when both controls are 1
634        let mut reg = QuantumRegister::new(3);
635        // Set to |110>
636        reg.state[0] = Complex::zero();
637        reg.state[6] = Complex::one(); // |110> = index 6
638        reg = apply_gate(&reg, &toffoli(), &[0, 1, 2]);
639        // Should be |111> = index 7
640        assert!((reg.state[7].re - 1.0).abs() < 1e-10);
641    }
642
643    #[test]
644    fn test_circuit_renderer() {
645        let mut circuit = QuantumCircuit::new();
646        circuit.add_gate(hadamard(), vec![0]);
647        circuit.add_gate(cnot(), vec![0, 1]);
648        let lines = CircuitRenderer::render(&circuit, 2);
649        assert_eq!(lines.len(), 2);
650        assert!(lines[0].contains("H"));
651    }
652
653    #[test]
654    fn test_phase_gate() {
655        let mut reg = QuantumRegister::new(1);
656        // Set to |1>
657        reg.state[0] = Complex::zero();
658        reg.state[1] = Complex::one();
659        reg = apply_gate(&reg, &phase(PI), &[0]);
660        // Should get -|1>
661        assert!((reg.state[1].re - (-1.0)).abs() < 1e-10);
662    }
663}