ket/ir/
gate.rs

1// SPDX-FileCopyrightText: 2024 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use super::qubit::LogicalQubit;
6use crate::{
7    decompose::{self, Algorithm, AuxMode, Schema},
8    execution::U4Gate,
9};
10use num::Complex;
11use serde::{Deserialize, Serialize};
12use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8, PI};
13
14#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
15pub enum QuantumGate {
16    PauliX,
17    PauliY,
18    PauliZ,
19    RotationX(f64),
20    RotationY(f64),
21    RotationZ(f64),
22    Phase(f64),
23    Hadamard,
24}
25
26pub type Cf64 = Complex<f64>;
27pub type Matrix = [[Cf64; 2]; 2];
28
29impl QuantumGate {
30    pub fn s() -> Self {
31        Self::Phase(FRAC_PI_2)
32    }
33
34    pub fn sd() -> Self {
35        Self::Phase(-FRAC_PI_2)
36    }
37
38    pub fn t() -> Self {
39        Self::Phase(FRAC_PI_4)
40    }
41
42    pub fn td() -> Self {
43        Self::Phase(-FRAC_PI_4)
44    }
45
46    pub fn sqrt_t() -> Self {
47        Self::Phase(FRAC_PI_8)
48    }
49
50    pub fn sqrt_td() -> Self {
51        Self::Phase(-FRAC_PI_8)
52    }
53
54    pub(crate) fn is_identity(&self) -> bool {
55        let (angle, n) = match self {
56            QuantumGate::RotationX(angle) => (angle, 4.0),
57            QuantumGate::RotationY(angle) => (angle, 4.0),
58            QuantumGate::RotationZ(angle) => (angle, 4.0),
59            QuantumGate::Phase(angle) => (angle, 2.0),
60            _ => return false,
61        };
62
63        (angle % (n * PI)).abs() < 1e-14
64    }
65
66    pub(crate) fn is_minus_identity(&self) -> bool {
67        let angle = match self {
68            QuantumGate::RotationX(angle) => angle,
69            QuantumGate::RotationY(angle) => angle,
70            QuantumGate::RotationZ(angle) => angle,
71            _ => return false,
72        };
73
74        (angle % (2.0 * PI)).abs() < 1e-14
75    }
76
77    pub(crate) fn is_inverse(&self, other: &Self) -> bool {
78        match self {
79            QuantumGate::PauliX => matches!(other, QuantumGate::PauliX),
80            QuantumGate::PauliY => matches!(other, QuantumGate::PauliY),
81            QuantumGate::PauliZ => matches!(other, QuantumGate::PauliZ),
82            QuantumGate::RotationX(angle) => {
83                if let QuantumGate::RotationX(other) = other {
84                    QuantumGate::RotationX(angle + other).is_identity()
85                } else {
86                    false
87                }
88            }
89            QuantumGate::RotationY(angle) => {
90                if let QuantumGate::RotationY(other) = other {
91                    QuantumGate::RotationY(angle + other).is_identity()
92                } else {
93                    false
94                }
95            }
96            QuantumGate::RotationZ(angle) => {
97                if let QuantumGate::RotationZ(other) = other {
98                    QuantumGate::RotationZ(angle + other).is_identity()
99                } else {
100                    false
101                }
102            }
103            QuantumGate::Phase(angle) => {
104                if let QuantumGate::Phase(other) = other {
105                    QuantumGate::Phase(angle + other).is_identity()
106                } else {
107                    false
108                }
109            }
110            QuantumGate::Hadamard => matches!(other, QuantumGate::Hadamard),
111        }
112    }
113
114    pub(crate) fn matrix(&self) -> Matrix {
115        match self {
116            QuantumGate::PauliX => [[0.0.into(), 1.0.into()], [1.0.into(), 0.0.into()]],
117            QuantumGate::PauliY => [[0.0.into(), -Cf64::i()], [Cf64::i(), 0.0.into()]],
118            QuantumGate::PauliZ => [[1.0.into(), 0.0.into()], [0.0.into(), (-1.0).into()]],
119            QuantumGate::RotationX(angle) => [
120                [(angle / 2.0).cos().into(), -Cf64::i() * (angle / 2.0).sin()],
121                [-Cf64::i() * (angle / 2.0).sin(), (angle / 2.0).cos().into()],
122            ],
123            QuantumGate::RotationY(angle) => [
124                [(angle / 2.0).cos().into(), (-(angle / 2.0).sin()).into()],
125                [(angle / 2.0).sin().into(), (angle / 2.0).cos().into()],
126            ],
127            QuantumGate::RotationZ(angle) => [
128                [(-Cf64::i() * (angle / 2.0)).exp(), 0.0.into()],
129                [0.0.into(), (Cf64::i() * (angle / 2.0)).exp()],
130            ],
131            QuantumGate::Phase(angle) => [
132                [1.0.into(), 0.0.into()],
133                [0.0.into(), (Cf64::i() * angle).exp()],
134            ],
135            QuantumGate::Hadamard => [
136                [(1.0 / 2.0f64.sqrt()).into(), (1.0 / 2.0f64.sqrt()).into()],
137                [(1.0 / 2.0f64.sqrt()).into(), (-1.0 / 2.0f64.sqrt()).into()],
138            ],
139        }
140    }
141
142    pub(crate) fn su2_matrix(&self) -> Matrix {
143        match self {
144            QuantumGate::PauliX => QuantumGate::RotationX(PI).matrix(),
145            QuantumGate::PauliY => QuantumGate::RotationY(PI).matrix(),
146            QuantumGate::PauliZ => QuantumGate::RotationZ(PI).matrix(),
147            QuantumGate::Phase(angle) => QuantumGate::RotationZ(*angle).matrix(),
148            QuantumGate::Hadamard => [
149                [-Cf64::i() * FRAC_1_SQRT_2, -Cf64::i() * FRAC_1_SQRT_2],
150                [-Cf64::i() * FRAC_1_SQRT_2, Cf64::i() * FRAC_1_SQRT_2],
151            ],
152            _ => self.matrix(),
153        }
154    }
155
156    pub(crate) fn su2_phase(&self) -> f64 {
157        match self {
158            QuantumGate::PauliX => FRAC_PI_2,
159            QuantumGate::PauliY => FRAC_PI_2,
160            QuantumGate::PauliZ => FRAC_PI_2,
161            QuantumGate::Phase(angle) => angle / 2.0,
162            QuantumGate::Hadamard => FRAC_PI_2,
163            _ => 0.0,
164        }
165    }
166
167    pub(crate) fn inverse(&self) -> Self {
168        match self {
169            QuantumGate::PauliX => QuantumGate::PauliX,
170            QuantumGate::PauliY => QuantumGate::PauliY,
171            QuantumGate::PauliZ => QuantumGate::PauliZ,
172            QuantumGate::RotationX(angle) => QuantumGate::RotationX(-angle),
173            QuantumGate::RotationY(angle) => QuantumGate::RotationY(-angle),
174            QuantumGate::RotationZ(angle) => QuantumGate::RotationZ(-angle),
175            QuantumGate::Phase(angle) => QuantumGate::Phase(-angle),
176            QuantumGate::Hadamard => QuantumGate::Hadamard,
177        }
178    }
179
180    pub(crate) fn decomposition_list(&self, control_size: usize) -> Vec<Algorithm> {
181        match self {
182            QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
183                if control_size <= 3 {
184                    vec![Algorithm::Optimal]
185                } else if control_size == 4 {
186                    vec![
187                        Algorithm::VChain(AuxMode::Clean),
188                        Algorithm::VChain(AuxMode::Dirty),
189                        Algorithm::Optimal,
190                    ]
191                } else {
192                    vec![
193                        Algorithm::VChain(AuxMode::Clean),
194                        Algorithm::VChain(AuxMode::Dirty),
195                        Algorithm::SingleAux(AuxMode::Clean),
196                        Algorithm::SingleAux(AuxMode::Dirty),
197                        Algorithm::LinearDepth,
198                    ]
199                }
200            }
201            QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
202                if control_size > 1 {
203                    vec![Algorithm::Network, Algorithm::SU2]
204                } else {
205                    vec![Algorithm::CU2]
206                }
207            }
208            QuantumGate::Phase(_) | QuantumGate::Hadamard => {
209                if control_size > 1 {
210                    vec![
211                        Algorithm::Network,
212                        Algorithm::SU2Rewrite,
213                        Algorithm::LinearDepth,
214                    ]
215                } else {
216                    vec![Algorithm::CU2]
217                }
218            }
219        }
220    }
221
222    pub(crate) fn decompose(
223        &self,
224        target: LogicalQubit,
225        control: &[LogicalQubit],
226        schema: Schema,
227        u4_gate: U4Gate,
228    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
229        match self {
230            QuantumGate::PauliX => Self::decompose_x(target, control, schema, u4_gate),
231            QuantumGate::PauliY => Self::decompose_y(target, control, schema, u4_gate),
232            QuantumGate::PauliZ => Self::decompose_z(target, control, schema, u4_gate),
233            QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
234                self.decompose_r(target, control, schema, u4_gate)
235            }
236            QuantumGate::Phase(angle) => {
237                Self::decompose_phase(*angle, target, control, schema, u4_gate)
238            }
239            QuantumGate::Hadamard => Self::decompose_hadamard(target, control, schema, u4_gate),
240        }
241    }
242
243    fn decompose_r(
244        &self,
245        target: LogicalQubit,
246        control: &[LogicalQubit],
247        schema: Schema,
248        u4_gate: U4Gate,
249    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
250        match &schema.algorithm {
251            Algorithm::Network => {
252                decompose::u2::network(*self, control, &schema.aux_qubits.unwrap(), target, u4_gate)
253            }
254            Algorithm::SU2 => decompose::su2::decompose(*self, control, target, u4_gate),
255            Algorithm::CU2 => decompose::u2::cu2(self.matrix(), control[0], target, u4_gate),
256            _ => panic!("Invalid Decomposition for Rotation Gate"),
257        }
258    }
259
260    fn decompose_x(
261        target: LogicalQubit,
262        control: &[LogicalQubit],
263        schema: Schema,
264        u4_gate: U4Gate,
265    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
266        match &schema.algorithm {
267            Algorithm::VChain(aux_mode) => match aux_mode {
268                AuxMode::Clean => decompose::x::v_chain_clean(
269                    control,
270                    &schema.aux_qubits.unwrap(),
271                    target,
272                    u4_gate,
273                ),
274                AuxMode::Dirty => decompose::x::v_chain_dirty(
275                    control,
276                    &schema.aux_qubits.unwrap(),
277                    target,
278                    u4_gate,
279                ),
280            },
281            Algorithm::SingleAux(aux_mode) => decompose::x::mxc_1_aux(
282                control,
283                schema.aux_qubits.unwrap()[0],
284                target,
285                u4_gate,
286                *aux_mode,
287            ),
288            Algorithm::LinearDepth => {
289                decompose::u2::decompose(QuantumGate::PauliX, control, target, u4_gate)
290            }
291            Algorithm::Optimal => match control.len() {
292                1 => u4_gate.cnot(control[0], target),
293                2 => decompose::x::c2x(control[0], control[1], target, u4_gate),
294                3 => decompose::x::c3x(control[0], control[1], control[2], target, u4_gate),
295                _ => decompose::x::c4x(
296                    control[0], control[1], control[2], control[3], target, u4_gate,
297                ),
298            },
299            _ => panic!("Invalid Decomposition for Pauli Gate"),
300        }
301    }
302
303    fn decompose_y(
304        target: LogicalQubit,
305        control: &[LogicalQubit],
306        schema: Schema,
307        u4_gate: U4Gate,
308    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
309        [
310            (QuantumGate::sd(), target, None),
311            (QuantumGate::Hadamard, target, None),
312        ]
313        .into_iter()
314        .chain(Self::decompose_x(target, control, schema, u4_gate))
315        .chain([
316            (QuantumGate::Hadamard, target, None),
317            (QuantumGate::s(), target, None),
318        ])
319        .collect()
320    }
321
322    fn decompose_z(
323        target: LogicalQubit,
324        control: &[LogicalQubit],
325        schema: Schema,
326        u4_gate: U4Gate,
327    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
328        [(QuantumGate::Hadamard, target, None)]
329            .into_iter()
330            .chain(Self::decompose_x(target, control, schema, u4_gate))
331            .chain([(QuantumGate::Hadamard, target, None)])
332            .collect()
333    }
334
335    fn decompose_phase(
336        angle: f64,
337        target: LogicalQubit,
338        control: &[LogicalQubit],
339        schema: Schema,
340        u4_gate: U4Gate,
341    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
342        match &schema.algorithm {
343            Algorithm::LinearDepth => {
344                decompose::u2::decompose(Self::Phase(angle), control, target, u4_gate)
345            }
346            Algorithm::Network => decompose::u2::network(
347                Self::Phase(angle),
348                control,
349                &schema.aux_qubits.unwrap(),
350                target,
351                u4_gate,
352            ),
353            Algorithm::SU2Rewrite => {
354                let control: Vec<_> = control.iter().cloned().chain([target]).collect();
355                decompose::su2::decompose(
356                    QuantumGate::RotationZ(-2.0 * angle),
357                    &control,
358                    schema.aux_qubits.unwrap()[0],
359                    u4_gate,
360                )
361            }
362            Algorithm::CU2 => decompose::u2::cu2(
363                QuantumGate::Phase(angle).matrix(),
364                control[0],
365                target,
366                u4_gate,
367            ),
368            _ => panic!("Invalid Decomposition for Phase Gate"),
369        }
370    }
371
372    fn decompose_hadamard(
373        target: LogicalQubit,
374        control: &[LogicalQubit],
375        schema: Schema,
376        u4_gate: U4Gate,
377    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
378        match &schema.algorithm {
379            Algorithm::LinearDepth => {
380                decompose::u2::decompose(Self::Hadamard, control, target, u4_gate)
381            }
382            Algorithm::Network => decompose::u2::network(
383                Self::Hadamard,
384                control,
385                &schema.aux_qubits.unwrap(),
386                target,
387                u4_gate,
388            ),
389            Algorithm::SU2Rewrite => {
390                let phase = QuantumGate::Hadamard.su2_phase();
391
392                decompose::su2::decompose(QuantumGate::Hadamard, control, target, u4_gate)
393                    .into_iter()
394                    .chain(decompose::su2::decompose(
395                        QuantumGate::RotationZ(-2.0 * phase),
396                        control,
397                        schema.aux_qubits.unwrap()[0],
398                        u4_gate,
399                    ))
400                    .collect()
401            }
402            Algorithm::CU2 => {
403                decompose::u2::cu2(QuantumGate::Hadamard.matrix(), control[0], target, u4_gate)
404            }
405            _ => panic!("Invalid Decomposition for Hadamard Gate"),
406        }
407    }
408}
409
410pub(crate) fn matrix_dot(matrix_a: &Matrix, matrix_b: &Matrix) -> Matrix {
411    [
412        [
413            matrix_a[0][0] * matrix_b[0][0] + matrix_a[0][1] * matrix_b[1][0],
414            matrix_a[0][0] * matrix_b[0][1] + matrix_a[0][1] * matrix_b[1][1],
415        ],
416        [
417            matrix_a[1][0] * matrix_b[0][0] + matrix_a[1][1] * matrix_b[1][0],
418            matrix_a[1][0] * matrix_b[0][1] + matrix_a[1][1] * matrix_b[1][1],
419        ],
420    ]
421}