use std::{convert::Infallible, marker::PhantomData};
use derive_where::derive_where;
use indexmap::IndexMap;
use num_complex::Complex64;
use crate::{
expression::{EvaluationError, Expression},
instruction::WaveformInvocation,
};
use self::builtin::{BuiltinWaveform, CommonBuiltinParameters};
pub mod builtin;
pub mod higher_kinded;
pub mod sampling;
#[cfg(feature = "python")]
pub(crate) mod quilpy;
pub trait WaveformData {
type Real: Clone + PartialEq + std::fmt::Debug;
type Complex: Clone + PartialEq + std::fmt::Debug;
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Syntactic {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Concrete {}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct Partial<T>(pub T);
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
#[repr(transparent)]
pub struct Reference<'a, T: WaveformData>(pub T, pub PhantomData<(&'a T::Real, &'a T::Complex)>);
impl WaveformData for Syntactic {
type Real = Expression;
type Complex = Expression;
}
impl WaveformData for Concrete {
type Real = f64;
type Complex = Complex64;
}
impl<T: WaveformData> WaveformData for Partial<T> {
type Real = Option<T::Real>;
type Complex = Option<T::Complex>;
}
impl<'a, T: WaveformData> WaveformData for Reference<'a, T> {
type Real = &'a T::Real;
type Complex = &'a T::Complex;
}
#[derive_where(Clone, PartialEq, Debug)]
pub enum Waveform<T: WaveformData> {
Builtin {
waveform: BuiltinWaveform<T>,
common_parameters: CommonBuiltinParameters<T>,
},
Custom {
name: String,
parameters: IndexMap<String, T::Complex>,
},
}
impl<T: WaveformData> Waveform<T> {
pub fn from_parameters<P: GeneralWaveformParameters, EF64, ER, EC>(
name: String,
parameters: P,
concrete_real: impl FnMut(P::Value) -> Result<f64, EF64>,
real: impl FnMut(P::Value) -> Result<T::Real, ER>,
complex: impl FnMut(P::Value) -> Result<T::Complex, EC>,
custom: impl FnOnce(
P,
) -> Result<
IndexMap<String, T::Complex>,
GeneralWaveformParameterError<EF64, ER, EC>,
>,
) -> Result<Self, WaveformInvocationError<GeneralWaveformParameterError<EF64, ER, EC>>> {
macro_rules! parse_builtin {
($waveform:ident) => {
self::parse::builtin(
BuiltinWaveform::$waveform,
parameters,
concrete_real,
real,
complex,
)
.map_err(|error| WaveformInvocationError { name, error })
};
}
match name.as_str() {
"flat" => parse_builtin!(Flat),
"gaussian" => parse_builtin!(Gaussian),
"drag_gaussian" => parse_builtin!(DragGaussian),
"erf_square" => parse_builtin!(ErfSquare),
"hrm_gauss" => parse_builtin!(HermiteGaussian),
"boxcar_kernel" => parse_builtin!(BoxcarKernel),
_ => match custom(parameters) {
Ok(parameters) => Ok(Self::Custom { name, parameters }),
Err(error) => Err(WaveformInvocationError { name, error }),
},
}
}
}
impl Waveform<Syntactic> {
pub fn new(invocation: WaveformInvocation) -> Result<Self, WaveformInvocationError> {
let WaveformInvocation { name, parameters } = invocation;
Self::from_parameters(name, parameters, |expr| expr.to_real(), Ok, Ok, Ok)
.map_err(|error| error.map(Into::into))
}
}
impl<S: WaveformData> Waveform<S> {
pub fn as_ref(&self) -> Waveform<Reference<'_, S>> {
match self {
Self::Builtin {
waveform,
common_parameters,
} => Waveform::Builtin {
waveform: waveform.as_ref(),
common_parameters: common_parameters.as_ref(),
},
Self::Custom { name, parameters } => Waveform::Custom {
name: name.clone(),
parameters: parameters
.iter()
.map(|(name, z)| (name.clone(), z))
.collect(),
},
}
}
pub fn try_evaluate<T: WaveformData, E>(
self,
real: impl Fn(S::Real) -> Result<T::Real, E>,
complex: impl Fn(S::Complex) -> Result<T::Complex, E>,
) -> Result<Waveform<T>, E> {
Ok(match self {
Self::Builtin {
waveform,
common_parameters,
} => Waveform::Builtin {
waveform: waveform.try_evaluate(&real, &complex)?,
common_parameters: common_parameters.try_evaluate(real, complex)?,
},
Self::Custom { name, parameters } => Waveform::Custom {
name,
parameters: parameters
.into_iter()
.map(|(name, z)| Ok((name, complex(z)?)))
.collect::<Result<_, _>>()?,
},
})
}
}
pub trait GeneralWaveformParameters {
type Value;
fn remove_parameter(&mut self, name: &'static str) -> Option<Self::Value>;
fn into_keys(self) -> Vec<String>;
}
impl<V> GeneralWaveformParameters for IndexMap<String, V> {
type Value = V;
#[inline(always)]
fn remove_parameter(&mut self, name: &'static str) -> Option<Self::Value> {
self.shift_remove(name)
}
#[inline(always)]
fn into_keys(self) -> Vec<String> {
self.into_keys().collect()
}
}
impl<V> GeneralWaveformParameters for std::collections::HashMap<String, V> {
type Value = V;
#[inline(always)]
fn remove_parameter(&mut self, name: &'static str) -> Option<Self::Value> {
self.remove(name)
}
#[inline(always)]
fn into_keys(self) -> Vec<String> {
self.into_keys().collect()
}
}
impl<V> GeneralWaveformParameters for std::collections::BTreeMap<String, V> {
type Value = V;
#[inline(always)]
fn remove_parameter(&mut self, name: &'static str) -> Option<Self::Value> {
self.remove(name)
}
#[inline(always)]
fn into_keys(self) -> Vec<String> {
self.into_keys().collect()
}
}
#[derive(Clone, PartialEq, Eq, Debug, derive_more::Display, thiserror::Error)]
pub enum GeneralWaveformParameterError<EF64, ER, EC> {
#[display("missing mandatory parameter {_0}")]
Missing(&'static str),
#[display("parameter {_0} must be a real number known at compile time, but {_1}")]
BadConcreteReal(&'static str, #[source] EF64),
#[display("parameter {_0} must be a real number, but {_1}")]
BadReal(&'static str, #[source] ER),
#[display("parameter {_0} must be a complex number, but {_1}")]
BadComplex(&'static str, #[source] EC),
#[display("extra unknown parameters: {}", _0.join(", "))]
Extra(Vec<String>),
}
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
pub enum WaveformParameterError {
#[error("missing mandatory parameter {_0}")]
Missing(&'static str),
#[error("parameter {_0} must be a real number known at compile time, but {_1}")]
Nonreal(&'static str, EvaluationError),
#[error("extra unknown parameters: {}", _0.join(", "))]
Extra(Vec<String>),
}
impl From<GeneralWaveformParameterError<EvaluationError, Infallible, Infallible>>
for WaveformParameterError
{
fn from(value: GeneralWaveformParameterError<EvaluationError, Infallible, Infallible>) -> Self {
match value {
GeneralWaveformParameterError::Missing(name) => Self::Missing(name),
GeneralWaveformParameterError::BadConcreteReal(name, error) => {
Self::Nonreal(name, error)
}
GeneralWaveformParameterError::Extra(names) => Self::Extra(names),
GeneralWaveformParameterError::BadReal(_, never)
| GeneralWaveformParameterError::BadComplex(_, never) => match never {},
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
#[error("invalid invocation of waveform {name}: {error}")]
pub struct WaveformInvocationError<E = WaveformParameterError> {
pub name: String,
#[source]
pub error: E,
}
impl<E1> WaveformInvocationError<E1> {
fn map<E2>(self, f: impl FnOnce(E1) -> E2) -> WaveformInvocationError<E2> {
let Self { name, error } = self;
WaveformInvocationError {
name,
error: f(error),
}
}
}
mod parse {
use super::*;
pub(super) trait Extractable<D: WaveformData>: Sized {
fn extract_from<P: GeneralWaveformParameters, EF64, ER, EC>(
parameters: &mut P,
concrete_real: impl FnMut(P::Value) -> Result<f64, EF64>,
real: impl FnMut(P::Value) -> Result<D::Real, ER>,
complex: impl FnMut(P::Value) -> Result<D::Complex, EC>,
) -> Result<Self, GeneralWaveformParameterError<EF64, ER, EC>>;
}
impl<D: WaveformData, T1: Extractable<D>, T2: Extractable<D>> Extractable<D> for (T1, T2) {
fn extract_from<P: GeneralWaveformParameters, EF64, ER, EC>(
parameters: &mut P,
mut concrete_real: impl FnMut(P::Value) -> Result<f64, EF64>,
mut real: impl FnMut(P::Value) -> Result<D::Real, ER>,
mut complex: impl FnMut(P::Value) -> Result<D::Complex, EC>,
) -> Result<Self, GeneralWaveformParameterError<EF64, ER, EC>> {
let t1 = T1::extract_from(parameters, &mut concrete_real, &mut real, &mut complex)?;
let t2 = T2::extract_from(parameters, concrete_real, real, complex)?;
Ok((t1, t2))
}
}
pub(super) fn mandatory<P: GeneralWaveformParameters, EF64, ER, EC, T, E>(
parameters: &mut P,
name: &'static str,
parse: impl FnOnce(P::Value) -> Result<T, E>,
value_error: impl FnOnce(&'static str, E) -> GeneralWaveformParameterError<EF64, ER, EC>,
) -> Result<T, GeneralWaveformParameterError<EF64, ER, EC>> {
optional(parameters, name, parse, value_error)
.and_then(|value| value.ok_or(GeneralWaveformParameterError::Missing(name)))
}
pub(super) fn optional<P: GeneralWaveformParameters, EF64, ER, EC, T, E>(
parameters: &mut P,
name: &'static str,
parse: impl FnOnce(P::Value) -> Result<T, E>,
value_error: impl FnOnce(&'static str, E) -> GeneralWaveformParameterError<EF64, ER, EC>,
) -> Result<Option<T>, GeneralWaveformParameterError<EF64, ER, EC>> {
parameters
.remove_parameter(name)
.map(|value| parse(value).map_err(|err| value_error(name, err)))
.transpose()
}
pub(super) fn builtin<
D: WaveformData,
T: Extractable<D>,
P: GeneralWaveformParameters,
EF64,
ER,
EC,
>(
constructor: impl FnOnce(T) -> BuiltinWaveform<D>,
mut parameters: P,
concrete_real: impl FnMut(P::Value) -> Result<f64, EF64>,
real: impl FnMut(P::Value) -> Result<D::Real, ER>,
complex: impl FnMut(P::Value) -> Result<D::Complex, EC>,
) -> Result<Waveform<D>, GeneralWaveformParameterError<EF64, ER, EC>> {
let (common_parameters, builtin) =
Extractable::extract_from(&mut parameters, concrete_real, real, complex)?;
let extra_parameters = parameters.into_keys();
if extra_parameters.is_empty() {
Ok(Waveform::Builtin {
waveform: constructor(builtin),
common_parameters,
})
} else {
Err(GeneralWaveformParameterError::Extra(extra_parameters))
}
}
}