ket/
execution.rs

1// SPDX-FileCopyrightText: 2024 Evandro Chagas Ribeiro da Rosa <evandro@quantuloop.com>
2//
3// SPDX-License-Identifier: Apache-2.0
4
5use std::f64::consts::FRAC_PI_2;
6
7use serde::{Deserialize, Serialize};
8
9use std::fmt::Debug;
10
11use crate::{
12    decompose,
13    graph::GraphMatrix,
14    ir::{
15        gate::{Matrix, QuantumGate},
16        hamiltonian::Hamiltonian,
17    },
18};
19
20pub use crate::{
21    ir::{
22        instructions::Instruction,
23        qubit::{LogicalQubit, PhysicalQubit, Qubit},
24    },
25    process::{DumpData, Sample},
26};
27
28#[derive(Debug, Default)]
29pub struct Configuration {
30    pub measure: FeatureStatus,
31    pub sample: FeatureStatus,
32    pub exp_value: FeatureStatus,
33    pub dump: FeatureStatus,
34    pub execution: Option<QuantumExecution>,
35    pub num_qubits: usize,
36    pub qpu: Option<QPU>,
37}
38
39#[derive(Debug)]
40pub enum QuantumExecution {
41    Live(Box<dyn LiveExecution>),
42    Batch(Box<dyn BatchExecution>),
43}
44
45pub trait LiveExecution {
46    fn gate(&mut self, gate: QuantumGate, target: LogicalQubit, control: &[LogicalQubit]);
47    fn measure(&mut self, qubits: &[LogicalQubit]) -> u64;
48    fn exp_value(&mut self, hamiltonian: &Hamiltonian<LogicalQubit>) -> f64;
49    fn sample(&mut self, qubits: &[LogicalQubit], shots: usize) -> Sample;
50    fn dump(&mut self, qubits: &[LogicalQubit]) -> DumpData;
51    fn free_aux(&mut self, aux_group: usize, num_qubits: usize);
52}
53
54pub trait BatchExecution {
55    fn submit_execution(
56        &mut self,
57        logical_circuit: &[Instruction<LogicalQubit>],
58        physical_circuit: Option<&[Instruction<PhysicalQubit>]>,
59    );
60    fn get_results(&mut self) -> ResultData;
61}
62
63#[derive(Default, Debug, Clone, Copy)]
64pub enum FeatureStatus {
65    Disable,
66    #[default]
67    Allowed,
68    ValidAfter,
69}
70
71#[derive(Debug, Clone, Default, Deserialize, Serialize)]
72pub struct ResultData {
73    pub measurements: Vec<u64>,
74    pub exp_values: Vec<f64>,
75    pub samples: Vec<Sample>,
76    pub dumps: Vec<DumpData>,
77}
78
79#[derive(Debug, Default)]
80pub struct QPU {
81    pub(crate) coupling_graph: Option<GraphMatrix<PhysicalQubit>>,
82    pub u2_gates: U2Gates,
83    pub u4_gate: U4Gate,
84}
85
86#[derive(Debug, Default, Clone, Copy)]
87pub enum U2Gates {
88    #[default]
89    All,
90    ZYZ,
91    RzSx,
92}
93
94impl U2Gates {
95    pub fn decompose(&self, matrix: &Matrix) -> Vec<QuantumGate> {
96        match self {
97            Self::ZYZ => Self::decompose_zyz(matrix),
98            Self::RzSx => Self::decompose_rzsx(matrix),
99            Self::All => panic!("decomposition not required"),
100        }
101    }
102
103    fn decompose_zyz(matrix: &Matrix) -> Vec<QuantumGate> {
104        let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
105        if theta_1.abs() <= 1e-14 {
106            vec![QuantumGate::RotationZ(theta_2 + theta_0)]
107        } else {
108            vec![
109                QuantumGate::RotationZ(theta_2),
110                QuantumGate::RotationY(theta_1),
111                QuantumGate::RotationZ(theta_0),
112            ]
113        }
114    }
115
116    fn decompose_rzsx(matrix: &Matrix) -> Vec<QuantumGate> {
117        let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
118        if theta_1.abs() <= 1e-14 {
119            vec![QuantumGate::RotationZ(theta_2 + theta_0)]
120        } else {
121            vec![
122                QuantumGate::RotationZ(theta_2),
123                QuantumGate::RotationX(FRAC_PI_2),
124                QuantumGate::RotationZ(theta_1),
125                QuantumGate::RotationX(-FRAC_PI_2),
126                QuantumGate::RotationZ(theta_0),
127            ]
128        }
129    }
130}
131
132#[derive(Debug, Default, Clone, Copy)]
133pub enum U4Gate {
134    #[default]
135    CX,
136    CZ,
137}
138
139impl std::fmt::Debug for dyn LiveExecution {
140    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
141        f.write_str("LiveExecution")
142    }
143}
144
145impl std::fmt::Debug for dyn BatchExecution {
146    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
147        f.write_str("BatchExecution")
148    }
149}
150
151impl QPU {
152    pub fn new(
153        coupling_graph: Option<Vec<(usize, usize)>>,
154        num_qubits: usize,
155        u2_gates: U2Gates,
156        u4_gate: U4Gate,
157    ) -> Self {
158        let coupling_graph = coupling_graph.map(|edges| {
159            let mut coupling_graph: GraphMatrix<PhysicalQubit> = GraphMatrix::new(num_qubits);
160            for (i, j) in edges {
161                coupling_graph.set_edge(i.into(), j.into(), 1);
162            }
163            coupling_graph
164        });
165
166        Self {
167            coupling_graph,
168            u2_gates,
169            u4_gate,
170        }
171    }
172}
173
174impl U4Gate {
175    pub(crate) fn cnot<Q: Copy>(&self, control: Q, target: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
176        match self {
177            Self::CX => vec![(QuantumGate::PauliX, target, Some(control))],
178            Self::CZ => vec![
179                (QuantumGate::Hadamard, target, None),
180                (QuantumGate::PauliZ, target, Some(control)),
181                (QuantumGate::Hadamard, target, None),
182            ],
183        }
184    }
185
186    pub(crate) fn swap<Q: Copy>(&self, qubit_a: Q, qubit_b: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
187        self.cnot(qubit_a, qubit_b)
188            .into_iter()
189            .chain(self.cnot(qubit_b, qubit_a))
190            .chain(self.cnot(qubit_a, qubit_b))
191            .collect()
192    }
193}
194
195impl FeatureStatus {
196    pub fn from(value: i32) -> Self {
197        match value {
198            0 => Self::Disable,
199            1 => Self::Allowed,
200            2 => Self::ValidAfter,
201            _ => panic!("Invalid value for FeatureStatus"),
202        }
203    }
204}