use num_complex::Complex64;
use numpy::{PyArray2, ToPyArray};
use paste::paste;
use pyo3::{
prelude::*,
types::{IntoPyDict as _, PyDict, PyTuple},
};
use rigetti_pyo3::{create_init_submodule, impl_repr};
#[cfg(feature = "stubs")]
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pymethods};
use super::*;
use crate::{
instruction::gate::GateSignature,
pickleable_new,
quilpy::{
errors::{self, PickleError},
impl_to_quil,
},
validation::identifier::IdentifierValidationError,
};
create_init_submodule! {
classes: [
Arithmetic,
ArithmeticOperator,
AttributeValue,
BinaryLogic,
BinaryOperator,
CalibrationDefinition,
CalibrationIdentifier,
Call,
Capture,
CircuitDefinition,
Comparison,
ComparisonOperator,
Convert,
Declaration,
Delay,
Exchange,
ExternParameter,
ExternSignature,
Fence,
FrameDefinition,
FrameIdentifier,
Gate,
GateDefinition,
GateModifier,
Include,
Jump,
JumpUnless,
JumpWhen,
Label,
Load,
MeasureCalibrationDefinition,
MeasureCalibrationIdentifier,
Measurement,
MemoryReference,
Move,
Offset,
PauliGate,
PauliTerm,
PauliSum,
Pragma,
Pulse,
QubitPlaceholder,
RawCapture,
Reset,
ScalarType,
SetFrequency,
SetPhase,
SetScale,
Sharing,
ShiftFrequency,
ShiftPhase,
Store,
SwapPhases,
TargetPlaceholder,
UnaryLogic,
UnaryOperator,
Vector,
Waveform,
WaveformDefinition,
WaveformInvocation
],
complex_enums: [
ArithmeticOperand,
AttributeValue,
BinaryOperand,
ComparisonOperand,
ExternParameterType,
GateSpecification,
Instruction,
PragmaArgument,
Qubit,
Target,
UnresolvedCallArgument
],
errors: [
errors::InstructionError,
errors::CallError,
errors::DefGateSequenceError,
errors::ExternError,
errors::GateError,
errors::ParseInstructionError,
errors::ParseMemoryReferenceError
],
}
macro_rules! impl_parse {
($name: ident) => {
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pyo3::pymethods]
impl $name {
#[staticmethod]
#[pyo3(name = "parse")]
fn py_parse(string: &str) -> PyResult<Self> {
Ok(Self::from_str(string)?)
}
}
};
}
macro_rules! impl_instruction {
([$( $name:ident $([$($args: tt)*])? ),* ,]) => {
impl_instruction!(@list [$($name $([$($args)*])? ,)*]);
};
(@list []) => {};
(@list [$name:ident, $($tail: tt)*]) => {
impl_instruction!(@one $name [+ repr + quil]);
impl_instruction!(@list [$($tail)*]);
};
(@list [$name: ident [$($args: tt)+], $($tail: tt)*]) => {
impl_instruction!(@one $name [+ $($args)*]);
impl_instruction!(@list [$($tail)*]);
};
(@one $name: ident []) => {};
(@one $name: ident [+ repr $($tail: tt)*]) => {
impl_repr!($name);
impl_instruction!(@one $name [$($tail)*]);
};
(@one $name: ident [+ quil $($tail: tt)*]) => {
impl_to_quil!($name);
impl_instruction!(@one $name [$($tail)*]);
};
(@one $name: ident [+ parse $($tail: tt)*]) => {
impl_parse!($name);
impl_instruction!(@one $name [$($tail)*]);
};
}
impl_instruction!([
Arithmetic,
ArithmeticOperand,
ArithmeticOperator,
AttributeValue,
BinaryLogic,
BinaryOperand,
BinaryOperator,
CalibrationDefinition,
CalibrationIdentifier,
Call,
Capture,
CircuitDefinition,
Comparison,
ComparisonOperand,
ComparisonOperator,
Convert,
Declaration,
Delay,
DefGateSequence[repr],
Exchange,
ExternParameter,
ExternParameterType,
ExternSignature,
Fence,
FrameDefinition,
FrameIdentifier,
Gate,
GateDefinition,
GateModifier,
GateSpecification,
GateType,
Include,
Instruction[repr + quil + parse],
Jump,
JumpUnless,
JumpWhen,
Label,
Load,
MeasureCalibrationDefinition,
MeasureCalibrationIdentifier,
Measurement,
MemoryReference[repr + quil + parse],
Move,
Offset,
OwnedGateSignature[repr],
PauliGate[repr],
PauliTerm[repr],
PauliSum[repr],
Pragma,
PragmaArgument,
Pulse,
Qubit,
QubitPlaceholder[repr],
RawCapture,
Reset,
ScalarType,
SetFrequency,
SetPhase,
SetScale,
Sharing[repr],
ShiftFrequency,
ShiftPhase,
Store,
SwapPhases,
Target,
TargetPlaceholder[repr],
UnaryLogic,
UnaryOperator,
UnresolvedCallArgument, Vector,
Waveform[repr],
WaveformDefinition,
WaveformInvocation,
]);
macro_rules! instruction_getnewargs {
($($kind:ty),* $(,)?) => { paste! {
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Instruction {
#[gen_stub(override_return_type(
type_repr = "tuple[()] | tuple[" $($kind)" | "* "]"
))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Instruction::Halt() | Instruction::Nop() | Instruction::Wait() => {
Ok(PyTuple::empty(py))
},
$(Instruction::$kind(instr) => (instr.clone(),).into_pyobject(py),)*
}
}
}
}};
}
instruction_getnewargs!(
Arithmetic,
BinaryLogic,
Call,
Capture,
CalibrationDefinition,
CircuitDefinition,
Comparison,
Convert,
Declaration,
Delay,
Exchange,
Fence,
FrameDefinition,
Gate,
GateDefinition,
Include,
Jump,
JumpUnless,
JumpWhen,
Label,
Load,
MeasureCalibrationDefinition,
Measurement,
Move,
Pragma,
Pulse,
RawCapture,
Reset,
SetFrequency,
SetPhase,
SetScale,
ShiftFrequency,
ShiftPhase,
Store,
SwapPhases,
UnaryLogic,
WaveformDefinition,
);
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl ArithmeticOperand {
#[gen_stub(override_return_type(type_repr = "tuple[int | float | MemoryReference]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::LiteralInteger(value) => (value,).into_pyobject(py),
Self::LiteralReal(value) => (value,).into_pyobject(py),
Self::MemoryReference(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl AttributeValue {
#[gen_stub(override_return_type(type_repr = "tuple[str | Expression]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::String(value) => (value.clone(),).into_pyobject(py),
Self::Expression(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl BinaryOperand {
#[gen_stub(override_return_type(type_repr = "tuple[int | MemoryReference]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::LiteralInteger(value) => (value,).into_pyobject(py),
Self::MemoryReference(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl ComparisonOperand {
#[gen_stub(override_return_type(type_repr = "tuple[int | float | MemoryReference]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::LiteralInteger(value) => (value,).into_pyobject(py),
Self::LiteralReal(value) => (value,).into_pyobject(py),
Self::MemoryReference(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl CalibrationDefinition {
#[getter]
fn name(&self) -> &str {
&self.identifier.name
}
#[getter]
fn parameters(&self) -> Vec<Expression> {
self.identifier.parameters.clone()
}
#[getter]
fn qubits(&self) -> Vec<Qubit> {
self.identifier.qubits.clone()
}
#[getter]
fn modifiers(&self) -> Vec<GateModifier> {
self.identifier.modifiers.clone()
}
}
pickleable_new! {
impl CalibrationIdentifier {
fn __new__(
name: String,
parameters: Vec<Expression>,
qubits: Vec<Qubit>,
modifiers: Vec<GateModifier>,
) -> Result<CalibrationIdentifier, IdentifierValidationError> {
Self::new(name, modifiers, parameters, qubits)
}
}
}
#[cfg(feature = "stubs")]
impl pyo3_stub_gen::PyStubType for ExternPragmaMap {
fn type_output() -> pyo3_stub_gen::TypeInfo {
pyo3_stub_gen::TypeInfo::dict_of::<Option<String>, Pragma>()
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl ExternParameterType {
#[gen_stub(override_return_type(type_repr = "tuple[ScalarType | Vector]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Scalar(value) | Self::VariableLengthVector(value) => (*value,).into_pyobject(py),
Self::FixedLengthVector(value) => (value.clone(),).into_pyobject(py),
}
}
}
pickleable_new! {
impl Gate {
fn __new__(
name: String,
parameters: Vec<Expression>,
qubits: Vec<Qubit>,
modifiers: Vec<GateModifier>,
) -> Result<Gate, GateError> {
Self::new(&name, parameters, qubits, modifiers)
}
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Gate {
#[pyo3(name = "dagger")]
#[must_use]
fn py_dagger(&self) -> Self {
self.clone().dagger()
}
#[pyo3(name = "controlled")]
#[must_use]
fn py_controlled(&self, control_qubit: Qubit) -> Self {
self.clone().controlled(control_qubit)
}
#[pyo3(name = "forked")]
fn py_forked(&self, fork_qubit: Qubit, alt_params: Vec<Expression>) -> Result<Self, GateError> {
self.clone().forked(fork_qubit, alt_params)
}
#[pyo3(name = "to_unitary")]
fn py_to_unitary<'py>(
&self,
n_qubits: u64,
py: Python<'py>,
) -> PyResult<Bound<'py, PyArray2<Complex64>>> {
Ok(self.clone().to_unitary(n_qubits)?.to_pyarray(py))
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl GateDefinition {
#[getter(signature)]
fn py_signature(&self) -> OwnedGateSignature {
self.signature().into()
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl GateSpecification {
#[gen_stub(override_return_type(
type_repr = "tuple[list[list[Expression]] | list[int] | PauliSum | DefGateSequence]"
))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Matrix(value) => (value.clone(),).into_pyobject(py),
Self::Permutation(value) => (value.clone(),).into_pyobject(py),
Self::PauliSum(value) => (value.clone(),).into_pyobject(py),
Self::Sequence(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "stubs", gen_stub_pyclass)]
#[pyo3::pyclass(
module = "quil.instructions",
name = "GateSignature",
eq,
frozen,
hash,
get_all,
subclass
)]
pub struct OwnedGateSignature {
name: String,
gate_parameters: Vec<String>,
qubit_parameters: Vec<String>,
gate_type: GateType,
}
pickleable_new! {
impl OwnedGateSignature {
fn new(name: String, gate_parameters: Vec<String>, qubit_parameters: Vec<String>, gate_type: GateType);
}
}
impl From<GateSignature<'_>> for OwnedGateSignature {
fn from(signature: GateSignature) -> Self {
OwnedGateSignature {
name: signature.name().to_string(),
gate_parameters: signature.gate_parameters().to_vec(),
qubit_parameters: signature.qubit_parameters().to_vec(),
gate_type: signature.gate_type(),
}
}
}
impl<'a> TryFrom<&'a OwnedGateSignature> for GateSignature<'a> {
type Error = GateError;
fn try_from(signature: &'a OwnedGateSignature) -> Result<Self, Self::Error> {
GateSignature::try_new(
&signature.name,
signature.gate_parameters.as_slice(),
signature.qubit_parameters.as_slice(),
signature.gate_type,
)
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl MeasureCalibrationDefinition {
#[getter]
fn name(&self) -> Option<&str> {
self.identifier.name.as_deref()
}
#[getter]
fn qubit(&self) -> Qubit {
self.identifier.qubit.clone()
}
#[getter]
fn target(&self) -> Option<&str> {
self.identifier.target.as_deref()
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl MeasureCalibrationIdentifier {
#[pyo3(signature = (qubit, target, *, name = None))]
#[new]
fn __new__(qubit: Qubit, target: Option<String>, name: Option<String>) -> Self {
Self::new(name, qubit, target)
}
#[gen_stub(override_return_type(
type_repr = "tuple[tuple[Qubit, str | None], dict[str, str | None]]"
))]
fn __getnewargs_ex__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
let Self {
name,
qubit,
target,
} = self;
let positional: Bound<'py, PyTuple> = (qubit.clone(), target.clone()).into_pyobject(py)?;
let keyword: Bound<'py, PyDict> = [("name", name)].into_py_dict(py)?;
(positional, keyword).into_pyobject(py)
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Measurement {
#[pyo3(signature = (qubit, target, *, name = None))]
#[new]
fn __new__(qubit: Qubit, target: Option<MemoryReference>, name: Option<String>) -> Self {
Self::new(name, qubit, target)
}
#[gen_stub(override_return_type(
type_repr = "tuple[tuple[Qubit, MemoryReference | None], dict[str, str | None]]"
))]
fn __getnewargs_ex__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
let Self {
name,
qubit,
target,
} = self;
let positional: Bound<'py, PyTuple> = (qubit.clone(), target.clone()).into_pyobject(py)?;
let keyword: Bound<'py, PyDict> = [("name", name)].into_py_dict(py)?;
(positional, keyword).into_pyobject(py)
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Sharing {
#[getter]
fn name(&self) -> String {
self.name.clone()
}
#[getter]
fn offsets(&self) -> Vec<Offset> {
self.offsets.clone()
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Offset {
#[getter]
fn offset(&self) -> u64 {
self.offset
}
#[getter]
fn data_type(&self) -> ScalarType {
self.data_type
}
}
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl PauliGate {
#[staticmethod]
fn parse(input: &str) -> Result<Self, ParseInstructionError> {
<Self as std::str::FromStr>::from_str(input)
.map_err(|err| ParseInstructionError::Parse(err.to_string()))
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl PragmaArgument {
#[gen_stub(override_return_type(type_repr = "tuple[int | str]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Identifier(value) => (value.clone(),).into_pyobject(py),
Self::Integer(value) => (*value,).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Qubit {
#[gen_stub(override_return_type(type_repr = "tuple[int | str | QubitPlaceholder]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Fixed(value) => (value,).into_pyobject(py),
Self::Variable(value) => (value,).into_pyobject(py),
Self::Placeholder(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl QubitPlaceholder {
#[new]
fn new() -> Self {
Self::default()
}
#[gen_stub(override_return_type(type_repr = "typing.NoReturn", imports = ("typing")))]
fn __getnewargs__(&self) -> PyResult<()> {
Err(PickleError::new_err(
"Unable to pickle or deepcopy a QubitPlaceholder.",
))
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl Target {
#[gen_stub(override_return_type(type_repr = "tuple[str | TargetPlaceholder]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Fixed(value) => (value,).into_pyobject(py),
Self::Placeholder(value) => (value.clone(),).into_pyobject(py),
}
}
}
#[cfg_attr(not(feature = "stubs"), optipy::strip_pyo3(only_stubs))]
#[cfg_attr(feature = "stubs", gen_stub_pymethods)]
#[pymethods]
impl UnresolvedCallArgument {
#[gen_stub(override_return_type(type_repr = "tuple[str | MemoryReference | complex]"))]
fn __getnewargs__<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyTuple>> {
match self {
Self::Identifier(value) => (value.clone(),).into_pyobject(py),
Self::MemoryReference(value) => (value.clone(),).into_pyobject(py),
Self::Immediate(value) => (*value,).into_pyobject(py),
}
}
}