1use ndarray::{s, Array2};
48use num_complex::Complex;
49use std::f64::consts::PI;
50
51use super::error::{QuantumError, QuantumResult};
52use super::qubits::QubitRegister;
53
54pub trait QuantumGate: Send + Sync {
63 fn matrix(&self) -> Array2<Complex<f64>>;
65
66 fn n_qubits(&self) -> usize;
68
69 fn name(&self) -> &str;
71}
72
73fn mat2(
79 a: Complex<f64>,
80 b: Complex<f64>,
81 c: Complex<f64>,
82 d: Complex<f64>,
83) -> Array2<Complex<f64>> {
84 Array2::from_shape_vec((2, 2), vec![a, b, c, d]).expect("2x2 matrix construction is infallible")
85}
86
87fn c(re: f64, im: f64) -> Complex<f64> {
88 Complex::new(re, im)
89}
90
91fn cr(re: f64) -> Complex<f64> {
92 Complex::new(re, 0.0)
93}
94
95fn ci(im: f64) -> Complex<f64> {
96 Complex::new(0.0, im)
97}
98
99pub struct Identity;
105
106impl QuantumGate for Identity {
107 fn matrix(&self) -> Array2<Complex<f64>> {
108 mat2(cr(1.0), cr(0.0), cr(0.0), cr(1.0))
109 }
110 fn n_qubits(&self) -> usize {
111 1
112 }
113 fn name(&self) -> &str {
114 "I"
115 }
116}
117
118pub struct PauliX;
120
121impl QuantumGate for PauliX {
122 fn matrix(&self) -> Array2<Complex<f64>> {
123 mat2(cr(0.0), cr(1.0), cr(1.0), cr(0.0))
124 }
125 fn n_qubits(&self) -> usize {
126 1
127 }
128 fn name(&self) -> &str {
129 "X"
130 }
131}
132
133pub struct PauliY;
135
136impl QuantumGate for PauliY {
137 fn matrix(&self) -> Array2<Complex<f64>> {
138 mat2(cr(0.0), ci(-1.0), ci(1.0), cr(0.0))
139 }
140 fn n_qubits(&self) -> usize {
141 1
142 }
143 fn name(&self) -> &str {
144 "Y"
145 }
146}
147
148pub struct PauliZ;
150
151impl QuantumGate for PauliZ {
152 fn matrix(&self) -> Array2<Complex<f64>> {
153 mat2(cr(1.0), cr(0.0), cr(0.0), cr(-1.0))
154 }
155 fn n_qubits(&self) -> usize {
156 1
157 }
158 fn name(&self) -> &str {
159 "Z"
160 }
161}
162
163pub struct Hadamard;
165
166impl QuantumGate for Hadamard {
167 fn matrix(&self) -> Array2<Complex<f64>> {
168 let s = 1.0 / 2.0_f64.sqrt();
169 mat2(cr(s), cr(s), cr(s), cr(-s))
170 }
171 fn n_qubits(&self) -> usize {
172 1
173 }
174 fn name(&self) -> &str {
175 "H"
176 }
177}
178
179pub struct PhaseS;
181
182impl QuantumGate for PhaseS {
183 fn matrix(&self) -> Array2<Complex<f64>> {
184 mat2(cr(1.0), cr(0.0), cr(0.0), ci(1.0))
185 }
186 fn n_qubits(&self) -> usize {
187 1
188 }
189 fn name(&self) -> &str {
190 "S"
191 }
192}
193
194pub struct PhaseSdg;
196
197impl QuantumGate for PhaseSdg {
198 fn matrix(&self) -> Array2<Complex<f64>> {
199 mat2(cr(1.0), cr(0.0), cr(0.0), ci(-1.0))
200 }
201 fn n_qubits(&self) -> usize {
202 1
203 }
204 fn name(&self) -> &str {
205 "Sdg"
206 }
207}
208
209pub struct PhaseT;
211
212impl QuantumGate for PhaseT {
213 fn matrix(&self) -> Array2<Complex<f64>> {
214 let phase = Complex::from_polar(1.0, PI / 4.0);
215 mat2(cr(1.0), cr(0.0), cr(0.0), phase)
216 }
217 fn n_qubits(&self) -> usize {
218 1
219 }
220 fn name(&self) -> &str {
221 "T"
222 }
223}
224
225pub struct PhaseTdg;
227
228impl QuantumGate for PhaseTdg {
229 fn matrix(&self) -> Array2<Complex<f64>> {
230 let phase = Complex::from_polar(1.0, -PI / 4.0);
231 mat2(cr(1.0), cr(0.0), cr(0.0), phase)
232 }
233 fn n_qubits(&self) -> usize {
234 1
235 }
236 fn name(&self) -> &str {
237 "Tdg"
238 }
239}
240
241pub struct RotX {
245 pub theta: f64,
247}
248
249impl QuantumGate for RotX {
250 fn matrix(&self) -> Array2<Complex<f64>> {
251 let (s, co) = (self.theta / 2.0).sin_cos();
252 mat2(cr(co), ci(-s), ci(-s), cr(co))
253 }
254 fn n_qubits(&self) -> usize {
255 1
256 }
257 fn name(&self) -> &str {
258 "Rx"
259 }
260}
261
262pub struct RotY {
266 pub theta: f64,
268}
269
270impl QuantumGate for RotY {
271 fn matrix(&self) -> Array2<Complex<f64>> {
272 let (s, co) = (self.theta / 2.0).sin_cos();
273 mat2(cr(co), cr(-s), cr(s), cr(co))
274 }
275 fn n_qubits(&self) -> usize {
276 1
277 }
278 fn name(&self) -> &str {
279 "Ry"
280 }
281}
282
283pub struct RotZ {
287 pub theta: f64,
289}
290
291impl QuantumGate for RotZ {
292 fn matrix(&self) -> Array2<Complex<f64>> {
293 let neg = Complex::from_polar(1.0, -self.theta / 2.0);
294 let pos = Complex::from_polar(1.0, self.theta / 2.0);
295 mat2(neg, cr(0.0), cr(0.0), pos)
296 }
297 fn n_qubits(&self) -> usize {
298 1
299 }
300 fn name(&self) -> &str {
301 "Rz"
302 }
303}
304
305pub struct PhaseShift {
307 pub lambda: f64,
309}
310
311impl QuantumGate for PhaseShift {
312 fn matrix(&self) -> Array2<Complex<f64>> {
313 let phase = Complex::from_polar(1.0, self.lambda);
314 mat2(cr(1.0), cr(0.0), cr(0.0), phase)
315 }
316 fn n_qubits(&self) -> usize {
317 1
318 }
319 fn name(&self) -> &str {
320 "P"
321 }
322}
323
324pub struct Unitary1Q {
327 pub theta: f64,
329 pub phi: f64,
331 pub lambda: f64,
333}
334
335impl QuantumGate for Unitary1Q {
336 fn matrix(&self) -> Array2<Complex<f64>> {
337 let (s, co) = (self.theta / 2.0).sin_cos();
338 let eiphi = Complex::from_polar(1.0, self.phi);
339 let eilambda = Complex::from_polar(1.0, self.lambda);
340 let eiphilambda = Complex::from_polar(1.0, self.phi + self.lambda);
341 mat2(cr(co), -eilambda * s, eiphi * s, eiphilambda * co)
342 }
343 fn n_qubits(&self) -> usize {
344 1
345 }
346 fn name(&self) -> &str {
347 "U"
348 }
349}
350
351pub struct CNOT;
365
366impl QuantumGate for CNOT {
367 fn matrix(&self) -> Array2<Complex<f64>> {
368 let o = cr(0.0);
369 let i = cr(1.0);
370 Array2::from_shape_vec((4, 4), vec![i, o, o, o, o, i, o, o, o, o, o, i, o, o, i, o])
371 .expect("4x4 matrix construction is infallible")
372 }
373 fn n_qubits(&self) -> usize {
374 2
375 }
376 fn name(&self) -> &str {
377 "CNOT"
378 }
379}
380
381pub struct CZ;
383
384impl QuantumGate for CZ {
385 fn matrix(&self) -> Array2<Complex<f64>> {
386 let o = cr(0.0);
387 let i = cr(1.0);
388 let m = cr(-1.0);
389 Array2::from_shape_vec((4, 4), vec![i, o, o, o, o, i, o, o, o, o, i, o, o, o, o, m])
390 .expect("4x4 matrix construction is infallible")
391 }
392 fn n_qubits(&self) -> usize {
393 2
394 }
395 fn name(&self) -> &str {
396 "CZ"
397 }
398}
399
400pub struct SWAP;
402
403impl QuantumGate for SWAP {
404 fn matrix(&self) -> Array2<Complex<f64>> {
405 let o = cr(0.0);
406 let i = cr(1.0);
407 Array2::from_shape_vec((4, 4), vec![i, o, o, o, o, o, i, o, o, i, o, o, o, o, o, i])
408 .expect("4x4 matrix construction is infallible")
409 }
410 fn n_qubits(&self) -> usize {
411 2
412 }
413 fn name(&self) -> &str {
414 "SWAP"
415 }
416}
417
418pub struct ISWAP;
420
421impl QuantumGate for ISWAP {
422 fn matrix(&self) -> Array2<Complex<f64>> {
423 let o = cr(0.0);
424 let i_re = cr(1.0);
425 let i_im = ci(1.0);
426 Array2::from_shape_vec(
427 (4, 4),
428 vec![i_re, o, o, o, o, o, i_im, o, o, i_im, o, o, o, o, o, i_re],
429 )
430 .expect("4x4 matrix construction is infallible")
431 }
432 fn n_qubits(&self) -> usize {
433 2
434 }
435 fn name(&self) -> &str {
436 "iSWAP"
437 }
438}
439
440pub struct CU {
443 inner: Box<dyn QuantumGate>,
444}
445
446impl CU {
447 pub fn new(gate: impl QuantumGate + 'static) -> QuantumResult<Self> {
449 if gate.n_qubits() != 1 {
450 return Err(QuantumError::GateArityMismatch {
451 gate_qubits: gate.n_qubits(),
452 supplied: 1,
453 });
454 }
455 Ok(Self {
456 inner: Box::new(gate),
457 })
458 }
459}
460
461impl QuantumGate for CU {
462 fn matrix(&self) -> Array2<Complex<f64>> {
463 let u = self.inner.matrix();
464 let o = cr(0.0);
465 let i = cr(1.0);
466 let u00 = u[[0, 0]];
467 let u01 = u[[0, 1]];
468 let u10 = u[[1, 0]];
469 let u11 = u[[1, 1]];
470 Array2::from_shape_vec(
471 (4, 4),
472 vec![i, o, o, o, o, i, o, o, o, o, u00, u01, o, o, u10, u11],
473 )
474 .expect("4x4 matrix construction is infallible")
475 }
476 fn n_qubits(&self) -> usize {
477 2
478 }
479 fn name(&self) -> &str {
480 "CU"
481 }
482}
483
484pub struct Toffoli;
492
493impl QuantumGate for Toffoli {
494 fn matrix(&self) -> Array2<Complex<f64>> {
495 let o = cr(0.0);
496 let i = cr(1.0);
497 let mut m = Array2::<Complex<f64>>::from_elem((8, 8), o);
500 for k in 0..6usize {
501 m[[k, k]] = i;
502 }
503 m[[6, 7]] = i;
504 m[[7, 6]] = i;
505 m
506 }
507 fn n_qubits(&self) -> usize {
508 3
509 }
510 fn name(&self) -> &str {
511 "Toffoli"
512 }
513}
514
515pub struct Fredkin;
519
520impl QuantumGate for Fredkin {
521 fn matrix(&self) -> Array2<Complex<f64>> {
522 let o = cr(0.0);
523 let i = cr(1.0);
524 let mut m = Array2::<Complex<f64>>::from_elem((8, 8), o);
527 for k in 0..8usize {
528 m[[k, k]] = i;
529 }
530 m[[5, 5]] = o;
531 m[[6, 6]] = o;
532 m[[5, 6]] = i;
533 m[[6, 5]] = i;
534 m
535 }
536 fn n_qubits(&self) -> usize {
537 3
538 }
539 fn name(&self) -> &str {
540 "Fredkin"
541 }
542}
543
544pub fn apply_gate(
555 state: &mut QubitRegister,
556 gate: &dyn QuantumGate,
557 target_qubits: &[usize],
558) -> QuantumResult<()> {
559 let gate_qubits = gate.n_qubits();
560 if target_qubits.len() != gate_qubits {
561 return Err(QuantumError::GateArityMismatch {
562 gate_qubits,
563 supplied: target_qubits.len(),
564 });
565 }
566
567 for &q in target_qubits {
569 if q >= state.n_qubits() {
570 return Err(QuantumError::QubitIndexOutOfRange {
571 index: q,
572 n_qubits: state.n_qubits(),
573 });
574 }
575 }
576
577 for i in 0..target_qubits.len() {
579 for j in (i + 1)..target_qubits.len() {
580 if target_qubits[i] == target_qubits[j] {
581 return Err(QuantumError::DuplicateQubitIndex {
582 index: target_qubits[i],
583 });
584 }
585 }
586 }
587
588 let gate_mat = gate.matrix();
589 let gate_dim = 1usize << gate_qubits;
590 let total_qubits = state.n_qubits();
591 let total_dim = state.dim();
592
593 let mut new_amps = state.amplitudes.clone();
596
597 let non_target_dim = total_dim / gate_dim;
599
600 for outer in 0..non_target_dim {
601 let mut sub_amps = vec![Complex::new(0.0, 0.0); gate_dim];
603
604 let indices: Vec<usize> = (0..gate_dim)
606 .map(|g| gate_idx_to_full_idx(g, outer, target_qubits, total_qubits))
607 .collect();
608
609 for (g, &full_idx) in indices.iter().enumerate() {
610 sub_amps[g] = state.amplitudes[full_idx];
611 }
612
613 let mut result = vec![Complex::new(0.0, 0.0); gate_dim];
615 for row in 0..gate_dim {
616 for col in 0..gate_dim {
617 result[row] += gate_mat[[row, col]] * sub_amps[col];
618 }
619 }
620
621 for (g, &full_idx) in indices.iter().enumerate() {
623 new_amps[full_idx] = result[g];
624 }
625 }
626
627 state.amplitudes = new_amps;
628 Ok(())
629}
630
631fn gate_idx_to_full_idx(
636 gate_idx: usize,
637 outer: usize,
638 target_qubits: &[usize],
639 total_qubits: usize,
640) -> usize {
641 let gate_qubits = target_qubits.len();
650 let mut full = 0usize;
651
652 let mut target_set = [usize::MAX; 64];
655 for (i, &t) in target_qubits.iter().enumerate() {
656 target_set[i] = t;
657 }
658
659 let mut outer_idx = 0usize;
662
663 for bit_pos in 0..total_qubits {
664 let mut target_local = usize::MAX;
666 for i in 0..gate_qubits {
667 if target_set[i] == bit_pos {
668 target_local = i;
669 break;
670 }
671 }
672 if target_local != usize::MAX {
673 let gate_bit = (gate_idx >> (gate_qubits - 1 - target_local)) & 1;
677 full |= gate_bit << bit_pos;
678 } else {
679 let outer_bit = (outer >> outer_idx) & 1;
681 full |= outer_bit << bit_pos;
682 outer_idx += 1;
683 }
684 }
685
686 full
687}
688
689pub fn tensor_product_matrices(
697 u1: &Array2<Complex<f64>>,
698 u2: &Array2<Complex<f64>>,
699) -> Array2<Complex<f64>> {
700 let (r1, c1) = (u1.nrows(), u1.ncols());
701 let (r2, c2) = (u2.nrows(), u2.ncols());
702 let rows = r1 * r2;
703 let cols = c1 * c2;
704 let mut result = Array2::zeros((rows, cols));
705 for i in 0..r1 {
706 for j in 0..c1 {
707 for k in 0..r2 {
708 for l in 0..c2 {
709 result[[i * r2 + k, j * c2 + l]] = u1[[i, j]] * u2[[k, l]];
710 }
711 }
712 }
713 }
714 result
715}
716
717pub fn check_unitary(u: &Array2<Complex<f64>>, tol: f64) -> QuantumResult<()> {
721 let n = u.nrows();
722 if u.ncols() != n {
723 return Err(QuantumError::DimensionMismatch {
724 expected: n,
725 actual: u.ncols(),
726 });
727 }
728 let mut max_dev: f64 = 0.0;
729 for i in 0..n {
730 for j in 0..n {
731 let val: Complex<f64> = (0..n).map(|k| u[[k, i]].conj() * u[[k, j]]).sum();
733 let expected = if i == j {
734 Complex::new(1.0, 0.0)
735 } else {
736 Complex::new(0.0, 0.0)
737 };
738 let dev = (val - expected).norm();
739 if dev > max_dev {
740 max_dev = dev;
741 }
742 }
743 }
744 if max_dev > tol {
745 return Err(QuantumError::NonUnitaryGate { deviation: max_dev });
746 }
747 Ok(())
748}
749
750pub fn matrix_product(
752 a: &Array2<Complex<f64>>,
753 b: &Array2<Complex<f64>>,
754) -> QuantumResult<Array2<Complex<f64>>> {
755 let n = a.nrows();
756 if a.ncols() != n || b.nrows() != n || b.ncols() != n {
757 return Err(QuantumError::DimensionMismatch {
758 expected: n,
759 actual: b.nrows(),
760 });
761 }
762 let mut result = Array2::zeros((n, n));
763 for i in 0..n {
764 for j in 0..n {
765 let val: Complex<f64> = (0..n).map(|k| a[[i, k]] * b[[k, j]]).sum();
766 result[[i, j]] = val;
767 }
768 }
769 Ok(result)
770}
771
772#[cfg(test)]
777mod tests {
778 use super::super::qubits::QubitRegister;
779 use super::*;
780
781 const TOL: f64 = 1e-12;
782
783 fn assert_complex_close(a: Complex<f64>, b: Complex<f64>, tol: f64, msg: &str) {
784 assert!(
785 (a - b).norm() < tol,
786 "{}: expected {:?}, got {:?}",
787 msg,
788 b,
789 a
790 );
791 }
792
793 #[test]
794 fn test_pauli_x_unitary() {
795 check_unitary(&PauliX.matrix(), 1e-12).expect("PauliX should be unitary");
796 }
797
798 #[test]
799 fn test_hadamard_unitary() {
800 check_unitary(&Hadamard.matrix(), 1e-12).expect("H should be unitary");
801 }
802
803 #[test]
804 fn test_cnot_unitary() {
805 check_unitary(&CNOT.matrix(), 1e-12).expect("CNOT should be unitary");
806 }
807
808 #[test]
809 fn test_toffoli_unitary() {
810 check_unitary(&Toffoli.matrix(), 1e-12).expect("Toffoli should be unitary");
811 }
812
813 #[test]
814 fn test_x_flips_zero() {
815 let mut reg = QubitRegister::new_zero_state(1).expect("valid");
816 apply_gate(&mut reg, &PauliX, &[0]).expect("apply ok");
817 assert!((reg.probability(1).expect("ok") - 1.0).abs() < TOL);
818 }
819
820 #[test]
821 fn test_x_flips_one() {
822 let mut reg = QubitRegister::new_basis_state(1, 1).expect("valid");
823 apply_gate(&mut reg, &PauliX, &[0]).expect("apply ok");
824 assert!((reg.probability(0).expect("ok") - 1.0).abs() < TOL);
825 }
826
827 #[test]
828 fn test_hadamard_superposition() {
829 let mut reg = QubitRegister::new_zero_state(1).expect("valid");
830 apply_gate(&mut reg, &Hadamard, &[0]).expect("apply ok");
831 let p0 = reg.probability(0).expect("ok");
832 let p1 = reg.probability(1).expect("ok");
833 assert!((p0 - 0.5).abs() < TOL);
834 assert!((p1 - 0.5).abs() < TOL);
835 }
836
837 #[test]
838 fn test_cnot_creates_bell_state() {
839 let mut reg = QubitRegister::new_zero_state(2).expect("valid");
840 apply_gate(&mut reg, &Hadamard, &[0]).expect("H ok");
841 apply_gate(&mut reg, &CNOT, &[0, 1]).expect("CNOT ok");
842 let p00 = reg.probability(0).expect("ok");
844 let p11 = reg.probability(3).expect("ok");
845 let p01 = reg.probability(1).expect("ok");
846 let p10 = reg.probability(2).expect("ok");
847 assert!((p00 - 0.5).abs() < TOL, "p00={}", p00);
848 assert!((p11 - 0.5).abs() < TOL, "p11={}", p11);
849 assert!(p01.abs() < TOL, "p01={}", p01);
850 assert!(p10.abs() < TOL, "p10={}", p10);
851 }
852
853 #[test]
854 fn test_z_phase_flip() {
855 let mut reg = QubitRegister::new_zero_state(1).expect("valid");
857 apply_gate(&mut reg, &Hadamard, &[0]).expect("H ok");
858 apply_gate(&mut reg, &PauliZ, &[0]).expect("Z ok");
859 let amp1 = reg.amplitude(1).expect("ok");
861 assert!(amp1.re < 0.0);
862 }
863
864 #[test]
865 fn test_duplicate_qubit_error() {
866 let mut reg = QubitRegister::new_zero_state(2).expect("valid");
867 let err = apply_gate(&mut reg, &CNOT, &[0, 0]);
868 assert!(matches!(err, Err(QuantumError::DuplicateQubitIndex { .. })));
869 }
870
871 #[test]
872 fn test_arity_error() {
873 let mut reg = QubitRegister::new_zero_state(2).expect("valid");
874 let err = apply_gate(&mut reg, &PauliX, &[0, 1]);
875 assert!(matches!(err, Err(QuantumError::GateArityMismatch { .. })));
876 }
877
878 #[test]
879 fn test_swap_swaps_qubits() {
880 let mut reg = QubitRegister::new_basis_state(2, 2).expect("valid");
883 apply_gate(&mut reg, &SWAP, &[0, 1]).expect("SWAP ok");
884 let p1 = reg.probability(1).expect("ok");
886 assert!(
887 (p1 - 1.0).abs() < TOL,
888 "SWAP should move |10⟩ to |01⟩, got p1={}",
889 p1
890 );
891 }
892
893 #[test]
894 fn test_rot_x_pi_equals_x() {
895 let rx_pi = RotX { theta: PI };
896 let mx = rx_pi.matrix();
897 assert!((mx[[0, 1]].norm() - 1.0).abs() < 1e-10);
900 assert!((mx[[1, 0]].norm() - 1.0).abs() < 1e-10);
901 }
902
903 #[test]
904 fn test_toffoli_flips_when_both_controls_set() {
905 let mut reg = QubitRegister::new_basis_state(3, 3).expect("valid");
911 apply_gate(&mut reg, &Toffoli, &[0, 1, 2]).expect("ok");
912 let p7 = reg.probability(7).expect("ok");
913 assert!((p7 - 1.0).abs() < TOL, "p7={}", p7);
914 }
915}