use crate::{
decompose,
ir::{
gate::{Matrix, QuantumGate},
hamiltonian::Hamiltonian,
instructions::Instruction,
qubit::LogicalQubit,
},
process::{DumpData, Sample},
};
use serde::{Deserialize, Serialize};
use std::f64::consts::FRAC_PI_2;
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct ExecutionTarget {
pub num_qubits: usize,
pub qpu: Option<QPU>,
pub execution_protocol: ExecutionProtocol,
pub gradient: Option<Gradient>,
}
#[derive(Debug)]
pub enum QuantumExecution {
Live(Box<dyn LiveExecution>),
Batch(Box<dyn BatchExecution>),
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExecutionProtocol {
ManagedByTarget {
measure: Capability,
sample: Capability,
exp_value: Capability,
dump: Capability,
},
SampleBased(ExpValueStrategy),
}
impl Default for ExecutionProtocol {
fn default() -> Self {
Self::ManagedByTarget {
measure: Default::default(),
sample: Default::default(),
exp_value: Default::default(),
dump: Default::default(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ExpValueStrategy {
DirectSample(usize),
ClassicalShadows {
bias: (u8, u8, u8),
samples: usize,
shots: usize,
},
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QPU {
pub coupling_graph: Option<Vec<(usize, usize)>>,
pub u2_gates: U2Gates,
pub u4_gate: U4Gate,
}
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
pub enum U2Gates {
#[default]
All,
ZYZ,
RzSx,
}
#[derive(Debug, Default, Clone, Copy, Serialize, Deserialize)]
pub enum U4Gate {
#[default]
CX,
CZ,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub enum Capability {
#[default]
Unsupported,
Basic,
Advanced,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Gradient {
ParameterShift,
NativeSupport,
}
pub trait LiveExecution {
fn gate(&mut self, gate: QuantumGate, target: LogicalQubit, control: &[LogicalQubit]);
fn measure(&mut self, qubits: &[LogicalQubit]) -> u64;
fn exp_value(&mut self, hamiltonian: &Hamiltonian<LogicalQubit>) -> f64;
fn sample(&mut self, qubits: &[LogicalQubit], shots: usize) -> Sample;
fn dump(&mut self, qubits: &[LogicalQubit]) -> DumpData;
fn save(&self) -> Vec<u8>;
fn load(&mut self, data: &[u8]);
}
pub trait BatchExecution {
fn submit_execution(&mut self, circuit: &[Instruction<usize>], parameters: &[f64]);
fn get_results(&mut self) -> ResultData;
fn clear(&mut self);
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct ResultData {
pub measurements: Vec<u64>,
pub exp_values: Vec<f64>,
pub samples: Vec<Sample>,
pub dumps: Vec<DumpData>,
pub gradients: Option<Vec<f64>>,
}
impl U2Gates {
pub fn decompose(&self, matrix: &Matrix) -> Vec<QuantumGate> {
match self {
Self::ZYZ => Self::decompose_zyz(matrix),
Self::RzSx => Self::decompose_rzsx(matrix),
Self::All => panic!("decomposition not required"),
}
}
fn decompose_zyz(matrix: &Matrix) -> Vec<QuantumGate> {
let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
if theta_1.abs() <= 1e-14 {
vec![QuantumGate::RotationZ((theta_2 + theta_0).into())]
} else {
vec![
QuantumGate::RotationZ(theta_2.into()),
QuantumGate::RotationY(theta_1.into()),
QuantumGate::RotationZ(theta_0.into()),
]
}
}
fn decompose_rzsx(matrix: &Matrix) -> Vec<QuantumGate> {
let (_, theta_0, theta_1, theta_2) = decompose::util::zyz(*matrix);
if theta_1.abs() <= 1e-14 {
vec![QuantumGate::RotationZ((theta_2 + theta_0).into())]
} else {
vec![
QuantumGate::RotationZ(theta_2.into()),
QuantumGate::RotationX(FRAC_PI_2.into()),
QuantumGate::RotationZ(theta_1.into()),
QuantumGate::RotationX((-FRAC_PI_2).into()),
QuantumGate::RotationZ(theta_0.into()),
]
}
}
}
impl std::fmt::Debug for dyn LiveExecution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("LiveExecution")
}
}
impl std::fmt::Debug for dyn BatchExecution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("BatchExecution")
}
}
impl U4Gate {
pub(crate) fn cnot<Q: Copy>(&self, control: Q, target: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
match self {
Self::CX => vec![(QuantumGate::PauliX, target, Some(control))],
Self::CZ => vec![
(QuantumGate::Hadamard, target, None),
(QuantumGate::PauliZ, target, Some(control)),
(QuantumGate::Hadamard, target, None),
],
}
}
pub(crate) fn cz<Q: Copy>(&self, control: Q, target: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
match self {
Self::CX => vec![
(QuantumGate::Hadamard, target, None),
(QuantumGate::PauliX, target, Some(control)),
(QuantumGate::Hadamard, target, None),
],
Self::CZ => vec![(QuantumGate::PauliZ, target, Some(control))],
}
}
pub(crate) fn swap<Q: Copy>(&self, qubit_a: Q, qubit_b: Q) -> Vec<(QuantumGate, Q, Option<Q>)> {
self.cnot(qubit_a, qubit_b)
.into_iter()
.chain(self.cnot(qubit_b, qubit_a))
.chain(self.cnot(qubit_a, qubit_b))
.collect()
}
}