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::{
8        self,
9        network::network,
10        u2::su2_rewrite_hadamard,
11        x::{single_aux, v_chain::v_chain, CXMode},
12        Algorithm, AuxMode, DepthMode, Schema,
13    },
14    execution::U4Gate,
15};
16use num::Complex;
17use serde::{Deserialize, Serialize};
18use std::f64::consts::{FRAC_1_SQRT_2, FRAC_PI_2, FRAC_PI_4, FRAC_PI_8, PI};
19
20#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
21pub enum Param {
22    Value(f64),
23    Ref {
24        index: usize,
25        value: f64,
26        multiplier: f64,
27    },
28}
29
30impl From<f64> for Param {
31    fn from(value: f64) -> Self {
32        Self::Value(value)
33    }
34}
35
36impl Param {
37    pub fn new_ref(index: usize, multiplier: f64) -> Self {
38        Self::Ref {
39            index,
40            value: 0.0,
41            multiplier,
42        }
43    }
44
45    pub fn new_value(value: f64) -> Self {
46        value.into()
47    }
48
49    pub(crate) fn update_ref(&mut self, new_value: f64) {
50        if let Param::Ref { value, .. } = self {
51            *value = new_value;
52        } else {
53            panic!()
54        }
55    }
56
57    pub(crate) fn index(&self) -> usize {
58        if let Param::Ref { index, .. } = self {
59            *index
60        } else {
61            panic!()
62        }
63    }
64
65    pub(crate) fn value(&self) -> f64 {
66        match self {
67            Param::Value(value) => *value,
68            Param::Ref {
69                value, multiplier, ..
70            } => value * multiplier,
71        }
72    }
73
74    fn inverse(&self) -> Self {
75        match self {
76            Param::Value(value) => Param::Value(-value),
77            Param::Ref {
78                index,
79                value,
80                multiplier,
81            } => Param::Ref {
82                index: *index,
83                value: *value,
84                multiplier: -multiplier,
85            },
86        }
87    }
88}
89
90#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
91pub enum QuantumGate {
92    PauliX,
93    PauliY,
94    PauliZ,
95    RotationX(Param),
96    RotationY(Param),
97    RotationZ(Param),
98    Phase(Param),
99    Hadamard,
100}
101
102pub type Cf64 = Complex<f64>;
103pub type Matrix = [[Cf64; 2]; 2];
104
105impl QuantumGate {
106    pub fn s() -> Self {
107        Self::Phase(FRAC_PI_2.into())
108    }
109
110    pub fn sd() -> Self {
111        Self::Phase((-FRAC_PI_2).into())
112    }
113
114    pub fn t() -> Self {
115        Self::Phase(FRAC_PI_4.into())
116    }
117
118    pub fn td() -> Self {
119        Self::Phase((-FRAC_PI_4).into())
120    }
121
122    pub fn sqrt_t() -> Self {
123        Self::Phase(FRAC_PI_8.into())
124    }
125
126    pub fn sqrt_td() -> Self {
127        Self::Phase((-FRAC_PI_8).into())
128    }
129
130    pub fn is_permutation(&self) -> bool {
131        match self {
132            QuantumGate::RotationX(param) | QuantumGate::RotationY(param) => {
133                if let Param::Value(value) = param {
134                    (value.abs() - PI).abs() <= 1e-10 || (value.abs() - 2.0 * PI).abs() <= 1e-10
135                } else {
136                    false
137                }
138            }
139            QuantumGate::Hadamard => false,
140            _ => true,
141        }
142    }
143
144    pub fn is_diagonal(&self) -> bool {
145        matches!(
146            self,
147            QuantumGate::PauliZ | QuantumGate::RotationZ(_) | QuantumGate::Phase(_)
148        )
149    }
150
151    pub(crate) fn is_identity(&self) -> bool {
152        let (angle, n) = match self {
153            QuantumGate::RotationX(angle) => (angle, 4.0),
154            QuantumGate::RotationY(angle) => (angle, 4.0),
155            QuantumGate::RotationZ(angle) => (angle, 4.0),
156            QuantumGate::Phase(angle) => (angle, 2.0),
157            _ => return false,
158        };
159        if let Param::Value(angle) = angle {
160            (angle % (n * PI)).abs() < 1e-14
161        } else {
162            false
163        }
164    }
165
166    pub(crate) fn is_inverse(&self, other: &Self) -> bool {
167        match self {
168            QuantumGate::PauliX => matches!(other, QuantumGate::PauliX),
169            QuantumGate::PauliY => matches!(other, QuantumGate::PauliY),
170            QuantumGate::PauliZ => matches!(other, QuantumGate::PauliZ),
171            QuantumGate::RotationX(angle) => {
172                if let QuantumGate::RotationX(Param::Value(other)) = other {
173                    if let Param::Value(angle) = angle {
174                        QuantumGate::RotationX((angle + other).into()).is_identity()
175                    } else {
176                        false
177                    }
178                } else {
179                    false
180                }
181            }
182            QuantumGate::RotationY(angle) => {
183                if let QuantumGate::RotationY(Param::Value(other)) = other {
184                    if let Param::Value(angle) = angle {
185                        QuantumGate::RotationY((angle + other).into()).is_identity()
186                    } else {
187                        false
188                    }
189                } else {
190                    false
191                }
192            }
193            QuantumGate::RotationZ(angle) => {
194                if let QuantumGate::RotationZ(Param::Value(other)) = other {
195                    if let Param::Value(angle) = angle {
196                        QuantumGate::RotationZ((angle + other).into()).is_identity()
197                    } else {
198                        false
199                    }
200                } else {
201                    false
202                }
203            }
204            QuantumGate::Phase(angle) => {
205                if let QuantumGate::Phase(Param::Value(other)) = other {
206                    if let Param::Value(angle) = angle {
207                        QuantumGate::Phase((angle + other).into()).is_identity()
208                    } else {
209                        false
210                    }
211                } else {
212                    false
213                }
214            }
215            QuantumGate::Hadamard => matches!(other, QuantumGate::Hadamard),
216        }
217    }
218
219    pub(crate) fn matrix(&self) -> Matrix {
220        match self {
221            QuantumGate::PauliX => [[0.0.into(), 1.0.into()], [1.0.into(), 0.0.into()]],
222            QuantumGate::PauliY => [[0.0.into(), -Cf64::i()], [Cf64::i(), 0.0.into()]],
223            QuantumGate::PauliZ => [[1.0.into(), 0.0.into()], [0.0.into(), (-1.0).into()]],
224            QuantumGate::RotationX(angle) => {
225                let angle = angle.value();
226                [
227                    [(angle / 2.0).cos().into(), -Cf64::i() * (angle / 2.0).sin()],
228                    [-Cf64::i() * (angle / 2.0).sin(), (angle / 2.0).cos().into()],
229                ]
230            }
231            QuantumGate::RotationY(angle) => {
232                let angle = angle.value();
233                [
234                    [(angle / 2.0).cos().into(), (-(angle / 2.0).sin()).into()],
235                    [(angle / 2.0).sin().into(), (angle / 2.0).cos().into()],
236                ]
237            }
238            QuantumGate::RotationZ(angle) => {
239                let angle = angle.value();
240                [
241                    [(-Cf64::i() * (angle / 2.0)).exp(), 0.0.into()],
242                    [0.0.into(), (Cf64::i() * (angle / 2.0)).exp()],
243                ]
244            }
245            QuantumGate::Phase(angle) => [
246                [1.0.into(), 0.0.into()],
247                [0.0.into(), (Cf64::i() * angle.value()).exp()],
248            ],
249            QuantumGate::Hadamard => [
250                [(1.0 / 2.0f64.sqrt()).into(), (1.0 / 2.0f64.sqrt()).into()],
251                [(1.0 / 2.0f64.sqrt()).into(), (-1.0 / 2.0f64.sqrt()).into()],
252            ],
253        }
254    }
255
256    pub(crate) fn su2_matrix(&self) -> Matrix {
257        match self {
258            QuantumGate::PauliX => QuantumGate::RotationX(PI.into()).matrix(),
259            QuantumGate::PauliY => QuantumGate::RotationY(PI.into()).matrix(),
260            QuantumGate::PauliZ => QuantumGate::RotationZ(PI.into()).matrix(),
261            QuantumGate::Phase(angle) => QuantumGate::RotationZ(*angle).matrix(),
262            QuantumGate::Hadamard => [
263                [-Cf64::i() * FRAC_1_SQRT_2, -Cf64::i() * FRAC_1_SQRT_2],
264                [-Cf64::i() * FRAC_1_SQRT_2, Cf64::i() * FRAC_1_SQRT_2],
265            ],
266            _ => self.matrix(),
267        }
268    }
269
270    pub(crate) fn inverse(&self) -> Self {
271        match self {
272            QuantumGate::PauliX => QuantumGate::PauliX,
273            QuantumGate::PauliY => QuantumGate::PauliY,
274            QuantumGate::PauliZ => QuantumGate::PauliZ,
275            QuantumGate::RotationX(angle) => QuantumGate::RotationX(angle.inverse()),
276            QuantumGate::RotationY(angle) => QuantumGate::RotationY(angle.inverse()),
277            QuantumGate::RotationZ(angle) => QuantumGate::RotationZ(angle.inverse()),
278            QuantumGate::Phase(angle) => QuantumGate::Phase(angle.inverse()),
279            QuantumGate::Hadamard => QuantumGate::Hadamard,
280        }
281    }
282
283    pub(crate) fn decomposition_list(&self, control_size: usize) -> Vec<Algorithm> {
284        if std::env::var("KET_FORCE_DECOMPOSE_ALGORITHM").unwrap_or("0".to_string()) == "1" {
285            let algorithm = match std::env::var("KET_DECOMPOSE_ALGORITHM")
286                .unwrap_or_default()
287                .as_str()
288            {
289                "VChainC2XClean" => Algorithm::VChain(CXMode::C2X, AuxMode::Clean),
290                "VChainC3XClean" => Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
291                "VChainC2XDirty" => Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
292                "VChainC3XDirty" => Algorithm::VChain(CXMode::C3X, AuxMode::Dirty),
293                "NetworkU2C2X" => Algorithm::NetworkU2(CXMode::C2X),
294                "NetworkU2C3X" => Algorithm::NetworkU2(CXMode::C3X),
295                "NetworkPauliC2X" => Algorithm::NetworkPauli(CXMode::C2X),
296                "NetworkPauliC3X" => Algorithm::NetworkPauli(CXMode::C3X),
297                "SingleAuxLinearClean" => Algorithm::SingleAux(DepthMode::Linear, AuxMode::Clean),
298                "SingleAuxLinearDirty" => Algorithm::SingleAux(DepthMode::Linear, AuxMode::Dirty),
299                "SingleAuxLogClean" => Algorithm::SingleAux(DepthMode::Log, AuxMode::Clean),
300                "SingleAuxLogDirty" => Algorithm::SingleAux(DepthMode::Log, AuxMode::Dirty),
301                "SingleAuxU2" => Algorithm::SingleAuxU2,
302                "LinearDepth" => Algorithm::LinearDepth,
303                "SU2Linear" => Algorithm::SU2(DepthMode::Linear),
304                "SU2Log" => Algorithm::SU2(DepthMode::Log),
305                "SU2Rewrite" => Algorithm::SU2Rewrite,
306                "AdjustableDepth" => Algorithm::AdjustableDepth,
307                other => panic!("undefined decomposition algorithm: {other}. Are you sure that you want to use this configuration?"),
308            };
309            return match self {
310                QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
311                    assert!(
312                        control_size <= 3
313                            || matches!(
314                                algorithm,
315                                Algorithm::NetworkPauli(_)
316                                    | Algorithm::VChain(_, _)
317                                    | Algorithm::AdjustableDepth
318                                    | Algorithm::SingleAux(_, _)
319                                    | Algorithm::LinearDepth
320                            )
321                    );
322                    if control_size <= 3 {
323                        vec![Algorithm::NoAuxCX]
324                    } else if control_size == 4 {
325                        vec![algorithm, Algorithm::NoAuxCX]
326                    } else {
327                        vec![algorithm, Algorithm::LinearDepth]
328                    }
329                }
330                QuantumGate::RotationX(_)
331                | QuantumGate::RotationY(_)
332                | QuantumGate::RotationZ(_) => {
333                    assert!(
334                        control_size <= 1
335                            || matches!(
336                                algorithm,
337                                Algorithm::NetworkU2(_)
338                                    | Algorithm::SU2(_)
339                                    | Algorithm::LinearDepth
340                            )
341                    );
342                    if control_size > 1 {
343                        vec![algorithm, Algorithm::LinearDepth]
344                    } else {
345                        vec![Algorithm::CU2]
346                    }
347                }
348                QuantumGate::Phase(_) | QuantumGate::Hadamard => {
349                    assert!(
350                        control_size <= 1
351                            || matches!(
352                                algorithm,
353                                Algorithm::NetworkU2(_)
354                                    | Algorithm::SingleAuxU2
355                                    | Algorithm::SU2Rewrite
356                                    | Algorithm::LinearDepth
357                            )
358                    );
359                    if control_size > 1 {
360                        vec![algorithm, Algorithm::LinearDepth]
361                    } else {
362                        vec![Algorithm::CU2]
363                    }
364                }
365            };
366        }
367
368        match self {
369            QuantumGate::PauliX | QuantumGate::PauliY | QuantumGate::PauliZ => {
370                if control_size <= 3 {
371                    vec![Algorithm::NoAuxCX]
372                } else if control_size == 4 {
373                    vec![
374                        Algorithm::NetworkPauli(CXMode::C2X),
375                        Algorithm::NetworkPauli(CXMode::C3X),
376                        Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
377                        Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
378                        Algorithm::NoAuxCX,
379                    ]
380                } else {
381                    vec![
382                        Algorithm::NetworkPauli(CXMode::C2X),
383                        Algorithm::NetworkPauli(CXMode::C3X),
384                        Algorithm::VChain(CXMode::C3X, AuxMode::Clean),
385                        Algorithm::VChain(CXMode::C2X, AuxMode::Dirty),
386                        Algorithm::SingleAux(DepthMode::Linear, AuxMode::Dirty),
387                        Algorithm::LinearDepth,
388                    ]
389                }
390            }
391            QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
392                if control_size > 1 {
393                    vec![
394                        Algorithm::NetworkU2(CXMode::C3X),
395                        Algorithm::SU2(DepthMode::Linear),
396                    ]
397                } else {
398                    vec![Algorithm::CU2]
399                }
400            }
401            QuantumGate::Phase(_) | QuantumGate::Hadamard => {
402                if control_size > 1 {
403                    vec![
404                        Algorithm::NetworkU2(CXMode::C3X),
405                        Algorithm::SU2Rewrite,
406                        Algorithm::LinearDepth,
407                    ]
408                } else {
409                    vec![Algorithm::CU2]
410                }
411            }
412        }
413    }
414
415    pub(crate) fn decompose(
416        &self,
417        target: LogicalQubit,
418        control: &[LogicalQubit],
419        schema: Schema,
420        u4_gate: U4Gate,
421    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
422        match self {
423            QuantumGate::PauliX => Self::decompose_x(target, control, schema, u4_gate),
424            QuantumGate::PauliY => Self::decompose_y(target, control, schema, u4_gate),
425            QuantumGate::PauliZ => Self::decompose_z(target, control, schema, u4_gate),
426            QuantumGate::RotationX(_) | QuantumGate::RotationY(_) | QuantumGate::RotationZ(_) => {
427                self.decompose_r(target, control, schema, u4_gate)
428            }
429            QuantumGate::Phase(angle) => {
430                Self::decompose_phase(angle.value(), target, control, schema, u4_gate)
431            }
432            QuantumGate::Hadamard => Self::decompose_hadamard(target, control, schema, u4_gate),
433        }
434    }
435
436    fn decompose_r(
437        &self,
438        target: LogicalQubit,
439        control: &[LogicalQubit],
440        schema: Schema,
441        u4_gate: U4Gate,
442    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
443        match &schema.algorithm {
444            Algorithm::LinearDepth => decompose::u2::linear_depth(*self, control, target, u4_gate),
445
446            Algorithm::NetworkU2(cx_mode) => decompose::network::network(
447                *self,
448                control,
449                &schema.aux_qubits.unwrap().1,
450                target,
451                u4_gate,
452                *cx_mode,
453                schema.approximated,
454            ),
455            Algorithm::SU2(depth_mode) => decompose::su2::decompose(
456                *self,
457                control,
458                target,
459                u4_gate,
460                *depth_mode,
461                schema.approximated,
462            ),
463            Algorithm::CU2 => decompose::u2::cu2(self.matrix(), control[0], target, u4_gate),
464            _ => panic!("Invalid Decomposition for Rotation Gate"),
465        }
466    }
467
468    fn decompose_x(
469        target: LogicalQubit,
470        control: &[LogicalQubit],
471        schema: Schema,
472        u4_gate: U4Gate,
473    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
474        match &schema.algorithm {
475            Algorithm::VChain(cx_mode, aux_mode) => v_chain(
476                control,
477                &schema.aux_qubits.unwrap().1,
478                target,
479                u4_gate,
480                *aux_mode,
481                *cx_mode,
482                schema.approximated,
483            ),
484            Algorithm::SingleAux(depth_mode, aux_mode) => single_aux::decompose(
485                control,
486                schema.aux_qubits.unwrap().1[0],
487                target,
488                u4_gate,
489                *aux_mode,
490                *depth_mode,
491                schema.approximated,
492            ),
493            Algorithm::LinearDepth => {
494                decompose::u2::linear_depth(QuantumGate::PauliX, control, target, u4_gate)
495            }
496            Algorithm::NoAuxCX => {
497                decompose::x::c2to4x::c1to4x(control, target, u4_gate, schema.approximated)
498            }
499            Algorithm::AdjustableDepth => decompose::x::adjustable_depth(
500                control,
501                &schema.aux_qubits.unwrap().1,
502                target,
503                u4_gate,
504                schema.approximated,
505            ),
506            Algorithm::NetworkPauli(cx_mode) => network(
507                QuantumGate::PauliX,
508                control,
509                &schema.aux_qubits.unwrap().1,
510                target,
511                u4_gate,
512                *cx_mode,
513                schema.approximated,
514            ),
515            _ => panic!("Invalid Decomposition {schema:?} for Pauli Gate"),
516        }
517    }
518
519    fn decompose_y(
520        target: LogicalQubit,
521        control: &[LogicalQubit],
522        schema: Schema,
523        u4_gate: U4Gate,
524    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
525        [(QuantumGate::sd(), target, None)]
526            .into_iter()
527            .chain(Self::decompose_x(target, control, schema, u4_gate))
528            .chain([(QuantumGate::s(), target, None)])
529            .collect()
530    }
531
532    fn decompose_z(
533        target: LogicalQubit,
534        control: &[LogicalQubit],
535        schema: Schema,
536        u4_gate: U4Gate,
537    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
538        [(QuantumGate::Hadamard, target, None)]
539            .into_iter()
540            .chain(Self::decompose_x(target, control, schema, u4_gate))
541            .chain([(QuantumGate::Hadamard, target, None)])
542            .collect()
543    }
544
545    fn decompose_phase(
546        angle: f64,
547        target: LogicalQubit,
548        control: &[LogicalQubit],
549        schema: Schema,
550        u4_gate: U4Gate,
551    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
552        match &schema.algorithm {
553            Algorithm::LinearDepth => {
554                decompose::u2::linear_depth(Self::Phase(angle.into()), control, target, u4_gate)
555            }
556            Algorithm::NetworkU2(cx_mode) => network(
557                Self::Phase(angle.into()),
558                control,
559                &schema.aux_qubits.unwrap().1,
560                target,
561                u4_gate,
562                *cx_mode,
563                schema.approximated,
564            ),
565            Algorithm::SU2Rewrite => {
566                let control: Vec<_> = control.iter().cloned().chain([target]).collect();
567                decompose::su2::decompose(
568                    QuantumGate::RotationZ((-2.0 * angle).into()),
569                    &control,
570                    schema.aux_qubits.unwrap().1[0],
571                    u4_gate,
572                    DepthMode::Linear,
573                    schema.approximated,
574                )
575            }
576            Algorithm::CU2 => decompose::u2::cu2(
577                QuantumGate::Phase(angle.into()).matrix(),
578                control[0],
579                target,
580                u4_gate,
581            ),
582            Algorithm::SingleAuxU2 => decompose::u2::single_aux(
583                Self::Phase(angle.into()),
584                control,
585                schema.aux_qubits.unwrap().1[0],
586                target,
587                u4_gate,
588                schema.approximated,
589            ),
590            _ => panic!("Invalid Decomposition for Phase Gate"),
591        }
592    }
593
594    fn decompose_hadamard(
595        target: LogicalQubit,
596        control: &[LogicalQubit],
597        schema: Schema,
598        u4_gate: U4Gate,
599    ) -> Vec<(QuantumGate, LogicalQubit, Option<LogicalQubit>)> {
600        match &schema.algorithm {
601            Algorithm::LinearDepth => {
602                decompose::u2::linear_depth(Self::Hadamard, control, target, u4_gate)
603            }
604            Algorithm::NetworkU2(cx_mode) => network(
605                Self::Hadamard,
606                control,
607                &schema.aux_qubits.unwrap().1,
608                target,
609                u4_gate,
610                *cx_mode,
611                schema.approximated,
612            ),
613            Algorithm::SU2Rewrite => {
614                su2_rewrite_hadamard(control, schema.aux_qubits.unwrap().1[0], target, u4_gate)
615            }
616            Algorithm::CU2 => {
617                decompose::u2::cu2(QuantumGate::Hadamard.matrix(), control[0], target, u4_gate)
618            }
619            Algorithm::SingleAuxU2 => decompose::u2::single_aux(
620                QuantumGate::Hadamard,
621                control,
622                schema.aux_qubits.unwrap().1[0],
623                target,
624                u4_gate,
625                schema.approximated,
626            ),
627            _ => panic!("Invalid Decomposition for Hadamard Gate"),
628        }
629    }
630}
631
632pub(crate) fn matrix_dot(matrix_a: &Matrix, matrix_b: &Matrix) -> Matrix {
633    [
634        [
635            matrix_a[0][0] * matrix_b[0][0] + matrix_a[0][1] * matrix_b[1][0],
636            matrix_a[0][0] * matrix_b[0][1] + matrix_a[0][1] * matrix_b[1][1],
637        ],
638        [
639            matrix_a[1][0] * matrix_b[0][0] + matrix_a[1][1] * matrix_b[1][0],
640            matrix_a[1][0] * matrix_b[0][1] + matrix_a[1][1] * matrix_b[1][1],
641        ],
642    ]
643}