1use 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#[derive(Debug, Clone, Serialize, Deserialize, Default)]
20pub struct ExecutionTarget {
21 pub num_qubits: usize,
23 pub qpu: Option<QPU>,
25 pub execution_protocol: ExecutionProtocol,
27 pub gradient: Option<Gradient>,
29}
30
31#[derive(Debug)]
33pub enum QuantumExecution {
34 Live(Box<dyn LiveExecution>),
36 Batch(Box<dyn BatchExecution>),
38}
39
40#[derive(Debug, Clone, Serialize, Deserialize)]
42pub enum ExecutionProtocol {
43 ManagedByTarget {
45 measure: Capability,
46 sample: Capability,
47 exp_value: Capability,
48 dump: Capability,
49 },
50 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#[derive(Debug, Clone, Serialize, Deserialize)]
67pub enum ExpValueStrategy {
68 DirectSample(usize),
70 ClassicalShadows {
72 bias: (u8, u8, u8),
74 samples: usize,
76 shots: usize,
78 },
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct QPU {
84 pub coupling_graph: Option<Vec<(usize, usize)>>,
86 pub u2_gates: U2Gates,
88 pub u4_gate: U4Gate,
90}
91
92#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
94pub enum U2Gates {
95 #[default]
96 All,
98 ZYZ,
100 RzSx,
102}
103
104#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
106pub enum U4Gate {
107 #[default]
109 CX,
110 CZ,
112}
113
114#[derive(Debug, Clone, Serialize, Deserialize, Default)]
116pub enum Capability {
117 #[default]
119 Unsupported,
120 Basic,
122 Advanced,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub enum Gradient {
129 ParameterShift,
131 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}