Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(quil-py): support extern call instructions #394

Merged
merged 10 commits into from
Sep 30, 2024
18 changes: 2 additions & 16 deletions quil-py/quil/instructions/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1187,7 +1187,7 @@ class CallArgument:
"""An argument to a ``Call`` instruction.

This may be expressed as an identifier, a memory reference, or an immediate value. Memory references and identifiers require a corresponding memory region declaration by the time of
compilation (at the time of call argument resolution and memory graph construction to be more precise).
compilation (at the time of call argument resolution and memory graph construction, to be more precise).

Additionally, an argument's resolved type must match the expected type of the corresponding ``ExternParameter``
in the ``ExternSignature``.
Expand Down Expand Up @@ -1228,7 +1228,7 @@ class CallError(ValueError):
...

class Call:
"""An instruction to an external function declared within a `PRAGMA EXTERN` instruction.
"""An instruction that calls an external function declared with a `PRAGMA EXTERN` instruction.

These calls are generally specific to a particular hardware or virtual machine
backend. For further detail, see:
Expand All @@ -1248,12 +1248,8 @@ class Call:
) -> Self: ...
@property
def name(self) -> str: ...
@name.setter
def name(self, name: str) -> None: ...
@property
def arguments(self) -> List[CallArgument]: ...
@arguments.setter
def arguments(self, arguments: List[CallArgument]) -> None: ...
def to_quil(self) -> str:
"""Attempt to convert the instruction to a valid Quil string.

Expand Down Expand Up @@ -1326,16 +1322,10 @@ class ExternParameter:
) -> Self: ...
@property
def name(self) -> str: ...
@name.setter
def name(self, name: str) -> None: ...
@property
def mutable(self) -> bool: ...
@mutable.setter
def mutable(self, mutable: bool) -> None: ...
@property
def data_type(self) -> ExternParameterType: ...
@data_type.setter
def data_type(self, data_type: ExternParameterType) -> None: ...
def to_quil(self) -> str:
"""Attempt to convert the instruction to a valid Quil string.

Expand Down Expand Up @@ -1371,12 +1361,8 @@ class ExternSignature:
) -> Self: ...
@property
def parameters(self) -> List[ExternParameter]: ...
@parameters.setter
def parameters(self, parameters: str) -> None: ...
@property
def return_type(self) -> Optional[ScalarType]: ...
@return_type.setter
def return_type(self, return_type: ScalarType) -> None: ...
def to_quil(self) -> str:
"""Attempt to convert the instruction to a valid Quil string.

Expand Down
105 changes: 69 additions & 36 deletions quil-py/src/instruction/extern_call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use quil_rs::instruction::{
};

use rigetti_pyo3::{
impl_hash, impl_repr, py_wrap_data_struct, py_wrap_error, py_wrap_union_enum,
impl_hash, impl_repr, py_wrap_error, py_wrap_union_enum,
pyo3::{pymethods, types::PyString, Py, PyResult, Python},
wrap_error, PyTryFrom, ToPythonError,
};
Expand All @@ -28,36 +28,48 @@ py_wrap_error!(
rigetti_pyo3::pyo3::exceptions::PyValueError
);

py_wrap_data_struct! {
rigetti_pyo3::py_wrap_type! {
#[derive(Debug, PartialEq, Eq)]
#[pyo3(subclass, module = "quil.instructions")]
PyCall(Call) as "Call" {
name: String => Py<PyString>,
arguments: Vec<UnresolvedCallArgument> => Vec<PyCallArgument>
PyCall(Call) as "Call"
}

#[pymethods]
impl PyCall {
#[new]
fn new(name: String, arguments: Vec<PyCallArgument>) -> PyResult<Self> {
Call::try_new(
name,
arguments.into_iter().map(PyCallArgument::into).collect(),
)
.map(Self)
.map_err(RustCallError::from)
.map_err(RustCallError::to_py_err)
}

#[getter]
fn name(&self) -> &str {
self.0.name()
}

#[getter]
fn arguments(&self) -> Vec<PyCallArgument> {
self.0
.arguments()
.iter()
.map(PyCallArgument::from)
.collect()
}
}

rigetti_pyo3::impl_as_mut_for_wrapper!(PyCall);
impl_repr!(PyCall);
impl_to_quil!(PyCall);
impl_copy_for_instruction!(PyCall);
impl_hash!(PyCall);
impl_eq!(PyCall);
impl_pickle_for_instruction!(PyCall);

#[pymethods]
impl PyCall {
#[new]
fn new(py: Python<'_>, name: String, arguments: Vec<PyCallArgument>) -> PyResult<Self> {
Ok(Self(
Call::try_new(
name,
Vec::<UnresolvedCallArgument>::py_try_from(py, &arguments)?,
)
.map_err(RustCallError::from)
.map_err(RustCallError::to_py_err)?,
))
}
}

py_wrap_union_enum! {
#[derive(Debug, PartialEq, Eq)]
PyCallArgument(UnresolvedCallArgument) as "CallArgument" {
Expand All @@ -84,15 +96,12 @@ impl_to_quil!(PyExternParameterType);
impl_hash!(PyExternParameterType);
impl_eq!(PyExternParameterType);

py_wrap_data_struct! {
rigetti_pyo3::py_wrap_type! {
#[derive(Debug, PartialEq, Eq)]
#[pyo3(subclass, module = "quil.instructions")]
PyExternParameter(ExternParameter) as "ExternParameter" {
name: String => Py<PyString>,
mutable: bool => bool,
data_type: ExternParameterType => PyExternParameterType
}
PyExternParameter(ExternParameter) as "ExternParameter"
}
rigetti_pyo3::impl_as_mut_for_wrapper!(PyExternParameter);
impl_repr!(PyExternParameter);
impl_to_quil!(PyExternParameter);
impl_copy_for_instruction!(PyExternParameter);
Expand All @@ -118,16 +127,29 @@ impl PyExternParameter {
.map_err(RustExternError::from)
.map_err(RustExternError::to_py_err)
}

#[getter]
fn name(&self) -> &str {
self.0.name()
}

#[getter]
fn mutable(&self) -> bool {
self.0.mutable()
}

#[getter]
fn data_type(&self) -> PyExternParameterType {
self.0.data_type().into()
}
}

py_wrap_data_struct! {
rigetti_pyo3::py_wrap_type! {
#[derive(Debug, PartialEq, Eq)]
#[pyo3(subclass, module = "quil.instructions")]
PyExternSignature(ExternSignature) as "ExternSignature" {
return_type: Option<ScalarType> => Option<PyScalarType>,
parameters: Vec<ExternParameter> => Vec<PyExternParameter>
}
PyExternSignature(ExternSignature) as "ExternSignature"
}
rigetti_pyo3::impl_as_mut_for_wrapper!(PyExternSignature);
impl_repr!(PyExternSignature);
impl_to_quil!(PyExternSignature);
impl_copy_for_instruction!(PyExternSignature);
Expand All @@ -143,14 +165,25 @@ impl PyExternSignature {
parameters: Vec<PyExternParameter>,
return_type: Option<PyScalarType>,
) -> PyResult<Self> {
ExternSignature::try_new(
Ok(Self(ExternSignature::new(
return_type
.map(|scalar_type| ScalarType::py_try_from(py, &scalar_type))
.transpose()?,
Vec::<ExternParameter>::py_try_from(py, &parameters)?,
)
.map(Self)
.map_err(RustExternError::from)
.map_err(RustExternError::to_py_err)
)))
}

#[getter]
fn parameters(&self) -> Vec<PyExternParameter> {
self.0
.parameters()
.iter()
.map(PyExternParameter::from)
.collect()
}

#[getter]
fn return_type(&self) -> Option<PyScalarType> {
self.0.return_type().map(Into::into)
}
}
Loading
Loading