Skip to main content

ruqu_core/
gate.rs

1//! Quantum gate definitions and matrix representations
2
3use crate::types::{Complex, QubitIndex};
4use std::f64::consts::FRAC_1_SQRT_2;
5
6/// Quantum gate operations
7#[derive(Debug, Clone)]
8pub enum Gate {
9    // ----- Single-qubit gates -----
10    H(QubitIndex),
11    X(QubitIndex),
12    Y(QubitIndex),
13    Z(QubitIndex),
14    S(QubitIndex),
15    Sdg(QubitIndex),
16    T(QubitIndex),
17    Tdg(QubitIndex),
18    Rx(QubitIndex, f64),
19    Ry(QubitIndex, f64),
20    Rz(QubitIndex, f64),
21    Phase(QubitIndex, f64),
22
23    // ----- Two-qubit gates -----
24    CNOT(QubitIndex, QubitIndex),
25    CZ(QubitIndex, QubitIndex),
26    SWAP(QubitIndex, QubitIndex),
27    Rzz(QubitIndex, QubitIndex, f64),
28
29    // ----- Special operations -----
30    Measure(QubitIndex),
31    Reset(QubitIndex),
32    Barrier,
33
34    // ----- Fused / custom single-qubit unitary (produced by optimizer) -----
35    Unitary1Q(QubitIndex, [[Complex; 2]; 2]),
36}
37
38impl Gate {
39    /// Return the qubit indices this gate acts on.
40    pub fn qubits(&self) -> Vec<QubitIndex> {
41        match self {
42            Gate::H(q)
43            | Gate::X(q)
44            | Gate::Y(q)
45            | Gate::Z(q)
46            | Gate::S(q)
47            | Gate::Sdg(q)
48            | Gate::T(q)
49            | Gate::Tdg(q)
50            | Gate::Rx(q, _)
51            | Gate::Ry(q, _)
52            | Gate::Rz(q, _)
53            | Gate::Phase(q, _)
54            | Gate::Measure(q)
55            | Gate::Reset(q)
56            | Gate::Unitary1Q(q, _) => vec![*q],
57
58            Gate::CNOT(q1, q2)
59            | Gate::CZ(q1, q2)
60            | Gate::SWAP(q1, q2)
61            | Gate::Rzz(q1, q2, _) => vec![*q1, *q2],
62
63            Gate::Barrier => vec![],
64        }
65    }
66
67    /// Returns `true` for non-unitary operations (measurement, reset, barrier).
68    pub fn is_non_unitary(&self) -> bool {
69        matches!(self, Gate::Measure(_) | Gate::Reset(_) | Gate::Barrier)
70    }
71
72    /// Return the 2x2 unitary matrix for single-qubit gates; `None` otherwise.
73    pub fn matrix_1q(&self) -> Option<[[Complex; 2]; 2]> {
74        let c0 = Complex::ZERO;
75        let c1 = Complex::ONE;
76        let ci = Complex::I;
77
78        match self {
79            // H = (1/sqrt2) [[1, 1], [1, -1]]
80            Gate::H(_) => {
81                let h = Complex::new(FRAC_1_SQRT_2, 0.0);
82                Some([[h, h], [h, -h]])
83            }
84
85            // X = [[0, 1], [1, 0]]
86            Gate::X(_) => Some([[c0, c1], [c1, c0]]),
87
88            // Y = [[0, -i], [i, 0]]
89            Gate::Y(_) => Some([[c0, -ci], [ci, c0]]),
90
91            // Z = [[1, 0], [0, -1]]
92            Gate::Z(_) => Some([[c1, c0], [c0, -c1]]),
93
94            // S = [[1, 0], [0, i]]
95            Gate::S(_) => Some([[c1, c0], [c0, ci]]),
96
97            // Sdg = [[1, 0], [0, -i]]
98            Gate::Sdg(_) => Some([[c1, c0], [c0, -ci]]),
99
100            // T = [[1, 0], [0, e^(i*pi/4)]]
101            Gate::T(_) => {
102                let t = Complex::new(FRAC_1_SQRT_2, FRAC_1_SQRT_2);
103                Some([[c1, c0], [c0, t]])
104            }
105
106            // Tdg = [[1, 0], [0, e^(-i*pi/4)]]
107            Gate::Tdg(_) => {
108                let t = Complex::new(FRAC_1_SQRT_2, -FRAC_1_SQRT_2);
109                Some([[c1, c0], [c0, t]])
110            }
111
112            // Rx(theta) = [[cos(t/2), -i*sin(t/2)], [-i*sin(t/2), cos(t/2)]]
113            Gate::Rx(_, theta) => {
114                let half = *theta / 2.0;
115                let c = Complex::new(half.cos(), 0.0);
116                let s = Complex::new(0.0, -half.sin());
117                Some([[c, s], [s, c]])
118            }
119
120            // Ry(theta) = [[cos(t/2), -sin(t/2)], [sin(t/2), cos(t/2)]]
121            Gate::Ry(_, theta) => {
122                let half = *theta / 2.0;
123                let cos_h = half.cos();
124                let sin_h = half.sin();
125                Some([
126                    [Complex::new(cos_h, 0.0), Complex::new(-sin_h, 0.0)],
127                    [Complex::new(sin_h, 0.0), Complex::new(cos_h, 0.0)],
128                ])
129            }
130
131            // Rz(theta) = [[e^(-i*t/2), 0], [0, e^(i*t/2)]]
132            Gate::Rz(_, theta) => {
133                let half = *theta / 2.0;
134                Some([
135                    [Complex::from_polar(1.0, -half), c0],
136                    [c0, Complex::from_polar(1.0, half)],
137                ])
138            }
139
140            // Phase(theta) = [[1, 0], [0, e^(i*theta)]]
141            Gate::Phase(_, theta) => Some([
142                [c1, c0],
143                [c0, Complex::from_polar(1.0, *theta)],
144            ]),
145
146            // Custom fused unitary
147            Gate::Unitary1Q(_, m) => Some(*m),
148
149            // Not a single-qubit gate
150            _ => None,
151        }
152    }
153
154    /// Return the 4x4 unitary matrix for two-qubit gates; `None` otherwise.
155    ///
156    /// Row / column ordering: index = q1_bit * 2 + q2_bit
157    /// where q1 is the first qubit argument and q2 the second.
158    pub fn matrix_2q(&self) -> Option<[[Complex; 4]; 4]> {
159        let c0 = Complex::ZERO;
160        let c1 = Complex::ONE;
161
162        match self {
163            // CNOT(control, target): |c,t> -> |c, t XOR c>
164            // Rows: |00>, |01>, |10>, |11>  (control, target)
165            Gate::CNOT(_, _) => Some([
166                [c1, c0, c0, c0],
167                [c0, c1, c0, c0],
168                [c0, c0, c0, c1],
169                [c0, c0, c1, c0],
170            ]),
171
172            // CZ: diag(1, 1, 1, -1)
173            Gate::CZ(_, _) => Some([
174                [c1, c0, c0, c0],
175                [c0, c1, c0, c0],
176                [c0, c0, c1, c0],
177                [c0, c0, c0, -c1],
178            ]),
179
180            // SWAP: identity with rows 1 and 2 exchanged
181            Gate::SWAP(_, _) => Some([
182                [c1, c0, c0, c0],
183                [c0, c0, c1, c0],
184                [c0, c1, c0, c0],
185                [c0, c0, c0, c1],
186            ]),
187
188            // Rzz(theta): diag(e^{-it/2}, e^{it/2}, e^{it/2}, e^{-it/2})
189            Gate::Rzz(_, _, theta) => {
190                let half = *theta / 2.0;
191                let en = Complex::from_polar(1.0, -half);
192                let ep = Complex::from_polar(1.0, half);
193                Some([
194                    [en, c0, c0, c0],
195                    [c0, ep, c0, c0],
196                    [c0, c0, ep, c0],
197                    [c0, c0, c0, en],
198                ])
199            }
200
201            _ => None,
202        }
203    }
204}