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 crate::{
6    decompose,
7    ir::{
8        gate::{Matrix, QuantumGate},
9        hamiltonian::Hamiltonian,
10        instructions::Instruction,
11        qubit::LogicalQubit,
12    },
13    process::{DumpData, Sample},
14};
15use serde::{Deserialize, Serialize};
16use std::f64::consts::FRAC_PI_2;
17
18/// Quantum Execution target configuration.
19#[derive(Debug, Clone, Serialize, Deserialize, Default)]
20pub struct ExecutionTarget {
21    /// Number of qubits available.
22    pub num_qubits: usize,
23    /// QPU architecture. If None, only logical circuit is generated
24    pub qpu: Option<QPU>,
25    /// How runtime lib will handle the execution.
26    pub execution_protocol: ExecutionProtocol,
27    /// If gradient will be computed and how.
28    pub gradient: Option<Gradient>,
29}
30
31/// Quantum Execution target.
32#[derive(Debug)]
33pub enum QuantumExecution {
34    /// Dynamic quantum execution.
35    Live(Box<dyn LiveExecution>),
36    /// Non-interactive quantum execution.
37    Batch(Box<dyn BatchExecution>),
38}
39
40/// How the execution will be handled.
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub enum ExecutionProtocol {
43    /// All measurements are computed by the execution target.
44    ManagedByTarget {
45        measure: Capability,
46        sample: Capability,
47        exp_value: Capability,
48        dump: Capability,
49    },
50    /// The measure and expected value are computed by the runtime lib.
51    SampleBased(ExpValueStrategy),
52}
53
54impl Default for ExecutionProtocol {
55    fn default() -> Self {
56        Self::ManagedByTarget {
57            measure: Default::default(),
58            sample: Default::default(),
59            exp_value: Default::default(),
60            dump: Default::default(),
61        }
62    }
63}
64
65/// How the expected value of an Hamiltonian can be computed.
66#[derive(Debug, Clone, Serialize, Deserialize)]
67pub enum ExpValueStrategy {
68    /// Using a sample for each observable.
69    DirectSample(usize),
70    /// Using classical shadows.
71    ClassicalShadows {
72        /// Weights for selecting the random measurement basis (X, Y,Z).
73        bias: (u8, u8, u8),
74        /// Number of measurement rounds.
75        samples: usize,
76        /// Number of shorts for each measurement round.
77        shots: usize,
78    },
79}
80
81/// QPU architecture;
82#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct QPU {
84    /// Qubit connectivity. If None, no circuit mapping will be performed.
85    pub coupling_graph: Option<Vec<(usize, usize)>>,
86    /// Single-qubit gates supported by the QPU.
87    pub u2_gates: U2Gates,
88    /// Two-qubits gates supported by the QPU.
89    pub u4_gate: U4Gate,
90}
91
92/// Single qubit gate supported by the QPU.
93#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
94pub enum U2Gates {
95    #[default]
96    /// All gates supported.
97    All,
98    /// RZ and RY supported.
99    ZYZ,
100    /// RZ and SX supported.
101    RzSx,
102}
103
104/// Two-qubit gates supported by the QPU.
105#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
106pub enum U4Gate {
107    /// CNOT gate supported.
108    #[default]
109    CX,
110    /// Controlled-Z gate supported.
111    CZ,
112}
113
114/// Execution capability for a measurement operation.
115#[derive(Debug, Clone, Serialize, Deserialize, Default)]
116pub enum Capability {
117    /// Operation not supported by the execution target.
118    #[default]
119    Unsupported,
120    /// Operation supported by the execution target, but limited.
121    Basic,
122    /// Operation supported by the execution target.
123    Advanced,
124}
125
126/// How the gradient can be computed.
127#[derive(Debug, Clone, Serialize, Deserialize)]
128pub enum Gradient {
129    /// Compute gradient using the parameter shift rule.
130    ParameterShift,
131    /// Gradient computed by the execution target.
132    NativeSupport,
133}
134
135pub trait LiveExecution {
136    fn gate(&mut self, gate: QuantumGate, target: LogicalQubit, control: &[LogicalQubit]);
137    fn measure(&mut self, qubits: &[LogicalQubit]) -> u64;
138    fn exp_value(&mut self, hamiltonian: &Hamiltonian<LogicalQubit>) -> f64;
139    fn sample(&mut self, qubits: &[LogicalQubit], shots: usize) -> Sample;
140    fn dump(&mut self, qubits: &[LogicalQubit]) -> DumpData;
141
142    fn save(&self) -> Vec<u8>;
143    fn load(&mut self, data: &[u8]);
144}
145
146pub trait BatchExecution {
147    fn submit_execution(&mut self, circuit: &[Instruction<usize>], parameters: &[f64]);
148    fn get_results(&mut self) -> ResultData;
149    fn clear(&mut self);
150}
151
152#[derive(Debug, Clone, Default, Deserialize, Serialize)]
153pub struct ResultData {
154    pub measurements: Vec<u64>,
155    pub exp_values: Vec<f64>,
156    pub samples: Vec<Sample>,
157    pub dumps: Vec<DumpData>,
158    pub gradients: Option<Vec<f64>>,
159}
160
161impl U2Gates {
162    pub fn decompose(&self, matrix: &Matrix) -> Vec<QuantumGate> {
163        match self {
164            Self::ZYZ => Self::decompose_zyz(matrix),
165            Self::RzSx => Self::decompose_rzsx(matrix),
166            Self::All => panic!("decomposition not required"),
167        }
168    }
169
170    fn decompose_zyz(matrix: &Matrix) -> Vec<QuantumGate> {
171        let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
172        if theta_1.abs() <= 1e-14 {
173            vec![QuantumGate::RotationZ((theta_2 + theta_0).into())]
174        } else {
175            vec![
176                QuantumGate::RotationZ(theta_2.into()),
177                QuantumGate::RotationY(theta_1.into()),
178                QuantumGate::RotationZ(theta_0.into()),
179            ]
180        }
181    }
182
183    fn decompose_rzsx(matrix: &Matrix) -> Vec<QuantumGate> {
184        let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
185        if theta_1.abs() <= 1e-14 {
186            vec![QuantumGate::RotationZ((theta_2 + theta_0).into())]
187        } else {
188            vec![
189                QuantumGate::RotationZ(theta_2.into()),
190                QuantumGate::RotationX(FRAC_PI_2.into()),
191                QuantumGate::RotationZ(theta_1.into()),
192                QuantumGate::RotationX((-FRAC_PI_2).into()),
193                QuantumGate::RotationZ(theta_0.into()),
194            ]
195        }
196    }
197}
198
199impl std::fmt::Debug for dyn LiveExecution {
200    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201        f.write_str("LiveExecution")
202    }
203}
204
205impl std::fmt::Debug for dyn BatchExecution {
206    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207        f.write_str("BatchExecution")
208    }
209}
210
211impl U4Gate {
212    pub(crate) fn cnot<Q: Copy>(&self, control: Q, target: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
213        match self {
214            Self::CX => vec![(QuantumGate::PauliX, target, Some(control))],
215            Self::CZ => vec![
216                (QuantumGate::Hadamard, target, None),
217                (QuantumGate::PauliZ, target, Some(control)),
218                (QuantumGate::Hadamard, target, None),
219            ],
220        }
221    }
222
223    pub(crate) fn cz<Q: Copy>(&self, control: Q, target: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
224        match self {
225            Self::CX => vec![
226                (QuantumGate::Hadamard, target, None),
227                (QuantumGate::PauliX, target, Some(control)),
228                (QuantumGate::Hadamard, target, None),
229            ],
230            Self::CZ => vec![(QuantumGate::PauliZ, target, Some(control))],
231        }
232    }
233
234    pub(crate) fn swap<Q: Copy>(&self, qubit_a: Q, qubit_b: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
235        self.cnot(qubit_a, qubit_b)
236            .into_iter()
237            .chain(self.cnot(qubit_b, qubit_a))
238            .chain(self.cnot(qubit_a, qubit_b))
239            .collect()
240    }
241}