use num::complex::ComplexFloat;
use serde::{Deserialize, Serialize};
use crate::{
error::KetError,
ir::{gate::GateInstruction, hamiltonian::Hamiltonian},
matrix::Matrix,
};
pub type BitString = Vec<u64>;
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct DumpData {
pub basis_states: Vec<BitString>,
pub amplitudes_real: Vec<f64>,
pub amplitudes_imag: Vec<f64>,
}
pub type SampleData = (Vec<BitString>, Vec<usize>);
pub trait LiveExecution {
fn compute_gate(&mut self, gate: &GateInstruction) -> Result<(), KetError>;
fn compute_native_gates(&mut self, gates: &[NativeGate]) -> Result<(), KetError>;
fn measure(&mut self, qubits: &[usize]) -> Result<u64, KetError>;
fn dump(&mut self, qubits: &[usize]) -> Result<DumpData, KetError>;
fn sample(&mut self, qubits: &[usize], shots: usize) -> Result<SampleData, KetError>;
fn exp_value(&mut self, hamiltonian: Hamiltonian) -> Result<f64, KetError>;
}
impl std::fmt::Debug for dyn LiveExecution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("LiveExecution")
}
}
pub type NativeGate = (String, Vec<f64>, Vec<usize>);
pub trait BatchExecution {
fn sample(
&self,
_gates: &[GateInstruction],
_qubits_to_sample: &[usize],
_shots: usize,
) -> Result<SampleData, KetError> {
Err(KetError::GateUnsupported)
}
fn exp_value(
&self,
_gates: &[GateInstruction],
_hamiltonian_list: &[Hamiltonian],
) -> Result<Vec<f64>, KetError> {
Err(KetError::GateUnsupported)
}
fn sample_native(
&self,
_gates: &[NativeGate],
_qubits_to_sample: &[usize],
_shots: usize,
) -> Result<SampleData, KetError> {
Err(KetError::GateUnsupported)
}
fn exp_value_native(
&self,
_gates: &[NativeGate],
_hamiltonian_list: &[Hamiltonian],
) -> Result<Vec<f64>, KetError> {
Err(KetError::NativeGateUnsupported)
}
fn gradient(
&self,
_gates: &[GateInstruction],
_hamiltonian: &Hamiltonian,
) -> Result<(f64, Vec<f64>), KetError> {
Err(KetError::NativeGradientUnsuported)
}
}
impl std::fmt::Debug for dyn BatchExecution {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("BatchExecution")
}
}
#[derive(Debug, Clone, Copy, Deserialize)]
pub enum ExpValueStrategy {
Native,
QubitWiseCommutation(usize),
ClassicalShadows {
bias: (u8, u8, u8),
samples: usize,
shots: usize,
},
}
#[derive(Debug)]
pub enum GradientStrategy {
None,
ParameterShiftRule,
Native,
}
#[derive(Debug)]
pub enum QuantumExecution {
Live {
qpu: Box<dyn LiveExecution>,
decompose: bool,
native_gate_set: Option<Box<dyn NativeGateSet>>,
},
Batch {
qpu: Box<dyn BatchExecution>,
native_gate_set: Option<Box<dyn NativeGateSet>>,
gradient: GradientStrategy,
exp_value: ExpValueStrategy,
coupling_graph: Option<Vec<(usize, usize)>>,
decompose: bool,
},
}
pub trait NativeGateSet {
fn translate(&self, matrix: &Matrix, target: usize) -> Result<Vec<NativeGate>, KetError>;
fn cnot(&self, control: usize, target: usize) -> Result<Vec<NativeGate>, KetError>;
}
impl std::fmt::Debug for dyn NativeGateSet {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("NativeGateSet")
}
}
pub type RzRyCX = ();
impl NativeGateSet for RzRyCX {
fn translate(&self, matrix: &Matrix, target: usize) -> Result<Vec<NativeGate>, KetError> {
let [[a, b], [c, d]] = matrix;
let det = (*a * *d - *b * c).arg();
let theta = 2.0 * c.abs().atan2(a.abs());
let ang1 = d.arg();
let ang2 = c.arg();
let phi = ang1 + ang2 - det;
let lam = ang1 - ang2;
let mut gates = Vec::new();
const EPS: f64 = 1e-10;
if lam.abs() > EPS {
gates.push(("rz".to_owned(), vec![lam], vec![target]));
}
if theta.abs() > EPS {
gates.push(("ry".to_owned(), vec![theta], vec![target]));
}
if phi.abs() > EPS {
gates.push(("rz".to_owned(), vec![phi], vec![target]));
}
Ok(gates)
}
fn cnot(&self, c: usize, t: usize) -> Result<Vec<NativeGate>, KetError> {
Ok(vec![("cnot".to_owned(), vec![], vec![c, t])])
}
}