macro_rules! field_type {
($waveform_type:ident, $field_type:ident) => {
field_type!(($waveform_type), $field_type)
};
(($($waveform_type:tt)+), ConcreteReal) => {
f64
};
(($($waveform_type:tt)+), Real) => {
$($waveform_type)+::Real
};
(($($waveform_type:tt)+), Complex) => {
$($waveform_type)+::Complex
};
}
macro_rules! extract_type_if_generic_field {
(Real; Real $($rest:tt)*) => {
T::Real
};
(Complex; Complex $($rest:tt)*) => {
T::Complex
};
($ty:ident; $other:tt $($rest:tt)*) => {
extract_type_if_generic_field!($ty; $($rest)*)
};
($ty:ident;) => {
()
};
}
macro_rules! field_referencer {
(ConcreteReal, $field_ref:expr) => {
*$field_ref
};
(Real, $field_ref:expr) => {
$field_ref
};
(Complex, $field_ref:expr) => {
$field_ref
};
}
macro_rules! field_evaluator {
(ConcreteReal, real = $real:ident, complex = $complex:ident, $field:expr) => {
$field
};
(Real, real = $real:ident, complex = $complex:ident, $field:expr) => {
$real($field)?
};
(Complex, real = $real:ident, complex = $complex:ident, $field:expr) => {
$complex($field)?
};
}
macro_rules! field_transposer {
($field:ident, ConcreteReal) => {
$field
};
($field:ident, Real) => {
$field?
};
($field:ident, Complex) => {
$field?
};
}
macro_rules! waveform_source {
(QuilT) => {
r#"This waveform is part of the [Quil-T][] spec ([§12.2, Waveforms][]).
[Quil-T]: https://quil-lang.github.io/#12Annex-T--Pulse-Level-Control
[§12.2, Waveforms]: https://quil-lang.github.io/#12-2Waveforms"#
};
(Rigetti) => {
r#"This waveform is a Rigetti extension to Quil-T."#
};
}
macro_rules! instantiated_waveform {
($($path:ident)::+<$parameter:ty>, $($field:ident)+) => {
$($path)::+<$parameter>
};
($($path:ident)::+<$parameter:ty>,) => {
$($path)::+
};
}
macro_rules! transpose_if_generic_waveform {
($value:expr, $($field:ident)+) => {
$value.transpose()
};
($value:expr,) => {
Some($value)
};
}
macro_rules! impl_builtin_waveform_traits {
($name:ident $(, $field:ident)*) => {
impl BuiltinWaveformParameters for instantiated_waveform!(
$name<Concrete>, $($field)*
) {
#[inline(always)]
fn iq_values_at_sample_rate(
self,
common: CommonBuiltinParameters<Concrete>,
sample_rate: f64,
) -> Result<IqSamples<Complex64>, SamplingError> {
self.raw_iq_values_at_sample_rate(common, sample_rate)
.map(IqSamplesFor::unwrap_total)
}
}
impl PartialBuiltinWaveformParameters for instantiated_waveform!(
$name<Partial<Concrete>>, $($field)*
) {
type Concrete = instantiated_waveform!($name<Concrete>, $($field)*);
#[inline(always)]
fn concretize(self) -> Option<Self::Concrete> {
transpose_if_generic_waveform!(self, $($field)*)
}
#[inline(always)]
fn partial_iq_values_at_sample_rate(
self,
common: CommonBuiltinParameters<Partial<Concrete>>,
sample_rate: f64,
) -> Result<IqSamplesOrPlaceholder, SamplingError> {
self.raw_iq_values_at_sample_rate(common, sample_rate)
.map(IqSamplesFor::into_iq_samples_or_placeholder)
}
}
};
}
macro_rules! impl_concretizable {
($name:ident) => {
impl ConcretizableWaveform for $name<Partial<Concrete>> {
#[inline(always)]
fn concretize(self) -> Result<$name<Concrete>, ()> {
self.transpose().ok_or(())
}
}
impl ConcretizableWaveform for $name<Concrete> {
#[inline(always)]
fn concretize(self) -> Result<Self, Infallible> {
Ok(self)
}
}
};
}
#[cfg(feature = "python")]
macro_rules! python_get_set {
($ty_name:ident, $field:ident, ConcreteReal) => {
paste::paste! {
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl $ty_name {
#[getter($field)]
fn [<py_get_$field>](&self) -> f64 {
self.0.$field
}
#[setter($field)]
fn [<py_set_$field>](&mut self, $field: f64) {
self.0.$field = $field;
}
}
}
};
($ty_name:ident, $field:ident, Real) => {
python_get_set!($ty_name, $field, PyAny("_Real"));
};
($ty_name:ident, $field:ident, Complex) => {
python_get_set!($ty_name, $field, PyAny("_Complex"));
};
($ty_name:ident, $field:ident, PyAny($type_name:literal)) => {
paste::paste! {
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl $ty_name {
#[getter($field)]
#[gen_stub(override_return_type(type_repr = $type_name))]
fn [<py_get_$field>]<'py>(&self, py: Python<'py>) -> &Bound<'py, PyAny> {
self.0.$field.0.bind(py)
}
#[setter($field)]
fn [<py_set_$field>](
&mut self,
#[gen_stub(override_type(type_repr = $type_name))]
$field: Py<PyAny>
) {
self.0.$field.0 = $field;
}
}
}
};
}
#[cfg(feature = "python")]
macro_rules! maybe_clone_ref {
($field:expr, ConcreteReal, $py:ident) => {
*$field
};
($field:expr, Real, $py:ident) => {
$field.clone_ref($py)
};
($field:expr, Complex, $py:ident) => {
$field.clone_ref($py)
};
}
#[cfg(feature = "python")]
macro_rules! python_field_eq {
($field:ident, ConcreteReal, $other:ident, $py:ident) => {
*$field == $other.$field
};
($field:ident, Real, $other:ident, $py:ident) => {
$field.py_eq($py, &$other.$field)?
};
($field:ident, Complex, $other:ident, $py:ident) => {
$field.py_eq($py, &$other.$field)?
};
}
#[cfg(feature = "python")]
macro_rules! push_field_repr {
($output:ident, $field:expr, ConcreteReal, $py:ident) => {{
use std::fmt::Write as _;
write!(&mut $output, "{}", $field).unwrap(); }};
($output:ident, $field:expr, Real, $py:ident) => {
$output.push_str($field.0.bind($py).repr()?.to_str()?);
};
($output:ident, $field:expr, Complex, $py:ident) => {
$output.push_str($field.0.bind($py).repr()?.to_str()?);
};
}
#[cfg(feature = "python")]
macro_rules! define_python_interop {
($name:ident $({ $($field:ident: $ty:ident),+ })?) => {
define_python_interop! {
@parse
$name
|
$({ $($field: $ty),+ })?
}
};
(@parse
$name:ident $({ $($pfield:ident: $pty:ident $(($pty_str:literal))?,)+ })?
|
$({})?
) => {
define_python_waveform! {
$name $({ $($pfield: $pty $(($pty_str))?),+ })?
}
add_python_waveform_convenience_constructor! {
$name $({ $($pfield: $pty $(($pty_str))?),+ })?
}
};
(@parse
$name:ident $({ $($pfield:ident: $pty:ident $(($pty_str:literal))?,)+ })?
|
{ $field1:ident: ConcreteReal $(, $field:ident: $ty:ident)* }
) => {
define_python_interop! {
@parse
$name { $($($pfield: $pty $(($pty_str))?,)+)? $field1: ConcreteReal, }
|
{ $($field: $ty),* }
}
};
(@parse
$name:ident $({ $($pfield:ident: $pty:ident $(($pty_str:literal))?,)+ })?
|
{ $field1:ident: Real $(, $field:ident: $ty:ident)* }
) => {
define_python_interop! {
@parse
$name { $($($pfield: $pty $(($pty_str))?,)+)? $field1: Real ("_Real"), }
|
{ $($field: $ty),* }
}
};
(@parse
$name:ident $({ $($pfield:ident: $pty:ident $(($pty_str:literal))?,)+ })?
|
{ $field1:ident: Complex $(, $field:ident: $ty:ident)* }
) => {
define_python_interop! {
@parse
$name { $($($pfield: $pty $(($pty_str))?,)+)? $field1: Complex ("_Complex"), }
|
{ $($field: $ty),* }
}
};
}
#[cfg(feature = "python")]
macro_rules! define_python_waveform {
($name:ident) => {
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl super::$name {
#[new]
fn __new__() -> Self {
Self
}
#[pyo3(name = "iq_values_at_sample_rate")]
fn py_iq_values_at_sample_rate<'py>(
&self,
py: Python<'py>,
#[gen_stub(override_type(
type_repr = "CommonBuiltinParameters[builtins.float, __T]",
imports = ("builtins"))
)]
common: PyCommonBuiltinParameters,
sample_rate: f64,
) -> PyResult<PyIqSamples> {
Ok(self
.iq_values_at_sample_rate(
common
.0
.as_ref()
.try_evaluate(
|PyAnyRust(r)| r.extract(py),
|PyAnyRust(c)| c.extract(py),
)?,
sample_rate,
)?
.into())
}
pub(crate) fn __eq__<'py>(
&self,
#[gen_stub(override_type(type_repr = "builtins.object", imports = ("builtins")))]
other: Bound<'py, PyAny>,
) -> PyResult<bool> {
if let Some(Self) = py_cast_and_borrow(&other)?.as_deref() {
Ok(true)
} else if let Some(super::quilpy::PyBuiltinWaveform(super::BuiltinWaveform::$name(
Self
))) = py_cast_and_borrow(&other)?.as_deref()
{
Ok(true)
} else {
Ok(false)
}
}
pub(crate) fn __repr__(&self) -> &'static str {
concat!(stringify!($name), "()")
}
}
};
($name:ident { $($field:ident: $ty:ident $(($ty_str:literal))?),+ }) => {
paste::paste! {
#[derive(Clone, Debug)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyo3::pyclass(module = "quil._quil.waveform", generic, subclass, from_py_object)]
pub struct $name(pub super::$name<Pythonic>);
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl $name {
#[pyo3(signature = (* $(, $field)+))]
#[gen_stub(override_return_type(type_repr = "$SELF"))]
#[new]
fn __new__(
$(
$(#[gen_stub(override_type(type_repr = $ty_str))])?
$field: field_type!((<Pythonic as WaveformData>), $ty)
),+
) -> Self {
Self(super::$name { $($field),+ })
}
fn __getnewargs_ex__<'py>(
&self,
py: Python<'py>
) -> PyResult<Bound<'py, PyTuple>> {
let Self(super::$name { $($field),+ }) = self;
let arguments: [(&'static str, Bound<'py, PyAny>); _] = [
$((
stringify!($field),
maybe_clone_ref!($field, $ty, py).into_bound_py_any(py)?,
)),+
];
(PyTuple::empty(py), arguments.into_py_dict(py)?).into_pyobject(py)
}
#[pyo3(name = "iq_values_at_sample_rate")]
fn py_iq_values_at_sample_rate<'py>(
&self,
py: Python<'py>,
#[gen_stub(override_type(
type_repr = "CommonBuiltinParameters[builtins.float, __T]",
imports = ("builtins"))
)]
common: PyCommonBuiltinParameters,
sample_rate: f64,
) -> PyResult<PyIqSamples> {
Ok(self
.0
.as_ref()
.try_evaluate::<crate::waveform::Concrete, _>(
|PyAnyRust(r)| r.extract(py),
|PyAnyRust(c)| c.extract(py),
)?
.iq_values_at_sample_rate(
common
.0
.as_ref()
.try_evaluate(
|PyAnyRust(r)| r.extract(py),
|PyAnyRust(c)| c.extract(py),
)?,
sample_rate,
)?
.into())
}
fn __eq__<'py>(
&self,
py: Python<'py>,
#[gen_stub(override_type(type_repr = "builtins.object", imports = ("builtins")))]
other: Bound<'py, PyAny>,
) -> PyResult<bool> {
self.0.py_eq(py, other)
}
fn __repr__<'py>(&self, py: Python<'py>) -> PyResult<String> {
self.0.py_repr(py)
}
}
$(python_get_set!($name, $field, $ty);)*
impl super::$name<Reference<'_, Pythonic>> {
pub(crate) fn py_eq<'py>(
&self,
py: Python<'py>,
other: Bound<'py, PyAny>,
) -> PyResult<bool> {
if let Some($name(other)) = py_cast_and_borrow(&other)?.as_deref() {
self.py_eq_this_type(py, other)
} else if let Some(
super::quilpy::PyBuiltinWaveform(super::BuiltinWaveform::$name(other)
)) = py_cast_and_borrow(&other)?.as_deref()
{
self.py_eq_this_type(py, other)
} else {
Ok(false)
}
}
pub(crate) fn py_eq_this_type<'py>(
&self,
py: Python<'py>,
other: &super::$name<Pythonic>,
) -> PyResult<bool> {
let Self { $($field),+ } = self;
$(
if !python_field_eq!($field, $ty, other, py) {
return Ok(false);
}
)+
Ok(true)
}
}
impl super::$name<Pythonic> {
pub(crate) fn py_eq<'py>(
&self,
py: Python<'py>,
other: Bound<'py, PyAny>,
) -> PyResult<bool> {
self.as_ref().py_eq(py, other)
}
pub(crate) fn py_repr<'py>(&self, py: Python<'py>) -> PyResult<String> {
let Self { $($field),+ } = self;
let mut output = stringify!($name).to_owned();
let mut sep = "(";
$(
output.push_str(sep);
output.push_str(concat!(stringify!($field), "="));
push_field_repr!(output, $field, $ty, py);
#[allow(unused_assignments)]
{ sep = ", "; }
)+
output.push(')');
Ok(output)
}
}
}
}
}
#[cfg(feature = "python")]
macro_rules! add_python_waveform_convenience_constructor {
($name:ident $({ $($field:ident: $ty:ident $(($ty_str:literal))?),+ })?) => {
paste::paste! {
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl crate::waveform::quilpy::PyWaveform {
#[pyo3(
name = $name:snake,
signature = (
*,
duration, scale = None, phase = None, detuning = None,
$($($field),+)?
)
)]
#[gen_stub(override_return_type(type_repr = "$SELF"))]
#[staticmethod]
#[allow(
clippy::too_many_arguments,
reason = "a many-keyword-argument function is genuinely a nice interface"
)]
fn [<py_$name:snake>]<'py>(
py: Python<'py>,
duration: f64,
#[gen_stub(override_type(
type_repr = "typing.Optional[_Real]",
imports = ("typing"))
)]
scale: Option<&Bound<'py, PyAny>>,
#[gen_stub(override_type(
type_repr = "typing.Optional[_Real]",
imports = ("typing"))
)]
phase: Option<&Bound<'py, PyAny>>,
#[gen_stub(override_type(
type_repr = "typing.Optional[_Real]",
imports = ("typing"))
)]
detuning: Option<&Bound<'py, PyAny>>,
$($(
$(#[gen_stub(override_type(type_repr = $ty_str))])?
$field: field_type!((<Pythonic as WaveformData>), $ty)
),+)?
) -> Self {
Self(crate::waveform::Waveform::Builtin {
common_parameters: super::CommonBuiltinParameters {
duration,
scale: scale.map(|scale| {
PyAnyRust(scale.as_unbound().clone_ref(py))
}),
phase: phase.map(|phase| {
Cycles(PyAnyRust(phase.as_unbound().clone_ref(py)))
}),
detuning: detuning.map(|detuning| {
PyAnyRust(detuning.as_unbound().clone_ref(py))
}),
},
waveform: super::BuiltinWaveform::$name(super::$name $({
$($field),+
})?),
})
}
}
}
}
}
#[cfg(feature = "python")]
macro_rules! reexport_python_waveform {
($submodule:ident::$name:ident as $py_name:ident) => {};
($submodule:ident::$name:ident { $($field:ident: $ty:ident),+ } as $py_name:ident) => {
pub use $submodule::$name as $py_name;
};
}
macro_rules! define_waveform {
{
$(#[$struct_meta:meta])*
pub struct $name:ident
} => {
$(#[$struct_meta])*
#[derive(Clone, PartialEq, Debug, Copy, Serialize, Deserialize)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[cfg_attr(feature = "python", pyo3::pyclass(module = "quil._quil.waveform", subclass, from_py_object))]
pub struct $name;
#[automatically_derived]
impl<T: WaveformData> parse::Extractable<T> for $name {
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<T::Real, ER>,
_complex: impl FnMut(P::Value) -> Result<T::Complex, EC>,
) -> Result<Self, GeneralWaveformParameterError<EF64, ER, EC>> {
Ok(Self)
}
}
impl_builtin_waveform_traits!($name);
};
{
$(#[$struct_meta:meta])*
pub struct $name:ident {
$(
$(#[$field_meta:meta])*
pub $field:ident: $ty:ident
),+
$(,)?
}
} => {
$(#[$struct_meta])*
#[derive_where(Clone, PartialEq, Debug)]
#[derive_where(
Copy, Serialize, Deserialize;
extract_type_if_generic_field!(Real; $($ty),+),
extract_type_if_generic_field!(Complex; $($ty),+)
)]
pub struct $name<T: WaveformData> {
$(
$(#[$field_meta])*
pub $field: field_type!(T, $ty)
),+
}
#[automatically_derived]
impl<T: WaveformData> parse::Extractable<T> for $name<T> {
#[allow(
unused_variables, unused_mut,
reason = "macro-generated code; not all waveforms have all three kinds of \
parameters"
)]
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<T::Real, ER>,
mut complex: impl FnMut(P::Value) -> Result<T::Complex, EC>,
) -> Result<Self, GeneralWaveformParameterError<EF64, ER, EC>> {
paste::paste! {
$(
let $field = parse::mandatory(
parameters,
stringify!($field),
&mut [<$ty:snake>],
GeneralWaveformParameterError::[<Bad$ty>],
)?;
)+
}
Ok(Self { $($field),+ })
}
}
impl<S: WaveformData> $name<S> {
#[doc = concat!(
"Convert an owned [`", stringify!($name), "`] into an equivalent one ",
"whose (non-concrete) parameters are all references."
)]
pub fn as_ref(&self) -> $name<Reference<'_, S>> {
let Self { $($field),+ } = self;
$name {
$($field: field_referencer!($ty, $field)),+
}
}
#[doc = concat!(
"Convert one [`", stringify!($name), "`] into another ",
"by replacing its associated data."
)]
#[allow(unused_variables, reason = "macro-generated code")]
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<$name<T>, E> {
let Self { $($field),+ } = self;
Ok($name {
$($field: field_evaluator!($ty, real = real, complex = complex, $field)),+
})
}
}
impl<T: WaveformData> $name<Partial<T>> {
#[doc = concat!(
"Returns `None` if any of the partial [`", stringify!($name),
"`]'s data is missing, and returns its underlying total form, [`",
stringify!($name), "<T>`], otherwise."
)]
pub fn transpose(self) -> Option<$name<T>> {
let Self { $($field),+ } = self;
Some($name {
$($field: field_transposer!($field, $ty)),+
})
}
}
impl<S: WaveformData> super::higher_kinded::WaveformParameters for $name<S> {
type WaveformData = S;
type WithWaveformData<T: WaveformData> = $name<T>;
#[inline(always)]
fn as_ref(&self) -> Self::WithWaveformData<Reference<'_, Self::WaveformData>> {
self.as_ref()
}
#[inline(always)]
fn try_evaluate<T: WaveformData, E>(
self,
real:
impl Fn(<Self::WaveformData as WaveformData>::Real) -> Result<T::Real, E>,
complex:
impl Fn(<Self::WaveformData as WaveformData>::Complex) -> Result<T::Complex, E>,
) -> Result<Self::WithWaveformData<T>, E> {
self.try_evaluate(real, complex)
}
}
impl<T: WaveformData>
super::higher_kinded::PartialWaveformParameters for $name<Partial<T>>
{
type TotalWaveformData = T;
#[inline(always)]
fn transpose(self) -> Option<Self::WithWaveformData<Self::TotalWaveformData>> {
self.transpose()
}
}
impl_builtin_waveform_traits!($name, $($field),+);
impl_concretizable!($name);
}
}
macro_rules! define_waveforms {
(
$(
$(#[doc = $struct_doc:literal])*
#[waveform_source($waveform_source:ident)]
$(#[$struct_meta:meta])*
pub struct $name:ident $({
$(
$(#[$field_meta:meta])*
pub $field:ident: $ty:ident
),+
$(,)?
})?
$(;)?
)+
) => {
$(
define_waveform! {
$(#[doc = $struct_doc])*
#[doc = waveform_source!($waveform_source)]
$(#[$struct_meta])*
pub struct $name $({
$(
$(#[$field_meta])*
pub $field: $ty
),+
})?
}
)+
#[cfg(feature = "python")]
mod quilpy_waveforms {
use super::*;
mod waveform_types {
use super::macros::*;
#[cfg(feature = "stubs")]
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
use pyo3::{
marker::Python,
types::{
IntoPyDict as _, PyAny, PyAnyMethods as _, PyStringMethods as _, PyTuple,
},
Bound, IntoPyObject as _, IntoPyObjectExt as _, Py, PyResult,
};
use crate::{
quilpy::py_cast_and_borrow,
waveform::{
quilpy::{PyAnyRust, Pythonic},
sampling::quilpy::PyIqSamples,
Reference, WaveformData,
},
units::Cycles,
};
use super::{
quilpy::PyCommonBuiltinParameters,
BuiltinWaveformParameters as _,
};
$(define_python_interop!($name $({ $($field: $ty),+ })?);)*
}
paste::paste! {
$(reexport_python_waveform! {
waveform_types::$name $({ $($field: $ty),+ })? as [<Py$name>]
})*
}
}
mod private {
use super::macros::*;
pub trait SealedConcrete {}
impl SealedConcrete for super::BuiltinWaveform<super::Concrete> {}
$(
#[automatically_derived]
impl SealedConcrete for instantiated_waveform!(
super::$name<super::Concrete>, $($($field)+)?
) {}
)*
pub trait SealedPartial {}
impl SealedPartial for super::BuiltinWaveform<super::Partial<super::Concrete>> {}
$(
#[automatically_derived]
impl SealedPartial for instantiated_waveform!(
super::$name<super::Partial<super::Concrete>>, $($($field)+)?
) {}
)*
}
}
}
#[cfg(feature = "python")]
pub(crate) use {
add_python_waveform_convenience_constructor, define_python_interop, define_python_waveform,
maybe_clone_ref, push_field_repr, python_field_eq, python_get_set, reexport_python_waveform,
};
pub(crate) use {
define_waveform, define_waveforms, extract_type_if_generic_field, field_evaluator,
field_referencer, field_transposer, field_type, impl_builtin_waveform_traits,
impl_concretizable, instantiated_waveform, transpose_if_generic_waveform, waveform_source,
};