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

Instruction API: CircuitDefinition #183

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions quil-py/quil/instructions/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ class Instruction:
``binary_logic``: A binary expression defined by a ``BinaryLogic``.
``calibration_definition``: Corresponds to a `DEFCAL` instruction (not `DEFCAL MEASURE`)
defined by a ``Calibration``.
``circuit_definition``: Corresponds to a `DEFCIRCUIT` instruction and its body,
defined by a ``CircuitDefinition``.
``declaration``: Corresponds to a `DECLARE` statement defined by a ``Declaration``.
``frame_definition``: Corresponds to a `DEFFRAME` statement, defined by a ``FrameDefinition``.
``gate``: A Quil quantum gate instruction defined by a ``Gate``.
Expand Down Expand Up @@ -45,6 +47,7 @@ class Instruction:
Arithmetic,
Calibration,
BinaryLogic,
CircuitDefinition,
Declaration,
FrameDefinition,
Gate,
Expand All @@ -60,6 +63,7 @@ class Instruction:
def is_arithmetic(self) -> bool: ...
def is_binary_logic(self) -> bool: ...
def is_calibration_definition(self) -> bool: ...
def is_circuit_definition(self) -> bool: ...
def is_declaration(self) -> bool: ...
def is_frame_definition(self) -> bool: ...
def is_gate(self) -> bool: ...
Expand All @@ -80,6 +84,10 @@ class Instruction:
@staticmethod
def from_calibration_definition(calibration: Calibration) -> "Instruction": ...
@staticmethod
def from_circuit_definition(
circuit_definition: CircuitDefinition,
) -> "Instruction": ...
@staticmethod
def from_declaration(declaration: Declaration) -> "Instruction": ...
@staticmethod
def from_frame_definition(frame_definition: FrameDefinition) -> "Instruction": ...
Expand All @@ -101,6 +109,8 @@ class Instruction:
) -> "Instruction": ...
def as_arithmetic(self) -> Optional[Arithmetic]: ...
def to_arithmetic(self) -> Arithmetic: ...
def as_circuit_definition(self) -> Optional[CircuitDefinition]: ...
def to_circuit_definition(self) -> CircuitDefinition: ...
def as_declaration(self) -> Optional[Declaration]: ...
def to_declaration(self) -> Declaration: ...
def as_gate(self) -> Optional[Gate]: ...
Expand Down Expand Up @@ -295,6 +305,31 @@ class MeasureCalibrationDefinition:
instructions: List[Instruction],
) -> "MeasureCalibrationDefinition": ...

class CircuitDefinition:
def __new__(
cls,
name: str,
parameters: List[str],
qubit_variables: List[str],
instructions: List[Instruction],
) -> "CircuitDefinition": ...
@property
def name(self) -> str: ...
@name.setter
def name(self, name: str): ...
@property
def parameters(self) -> List[str]: ...
@parameters.setter
def parameters(self, parameters: List[str]): ...
@property
def qubit_variables(self) -> List[str]: ...
@qubit_variables.setter
def qubit_variables(self, qubit_variables: List[str]): ...
@property
def instructions(self) -> List[Instruction]: ...
@instructions.setter
def instructions(self, instructions: List[Instruction]): ...

class Offset:
@staticmethod
def __new__(
Expand Down
50 changes: 50 additions & 0 deletions quil-py/src/instruction/circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
use quil_rs::instruction::{CircuitDefinition, Instruction};

use rigetti_pyo3::{
impl_repr, impl_str, py_wrap_data_struct,
pyo3::{
pyclass::CompareOp, pymethods, types::PyString, IntoPy, Py, PyObject, PyResult, Python,
},
PyTryFrom, PyWrapper,
};

use super::PyInstruction;

py_wrap_data_struct! {
#[derive(Debug, PartialEq)]
#[pyo3(subclass)]
PyCircuitDefinition(CircuitDefinition) as "CircuitDefinition" {
name: String => Py<PyString>,
parameters: Vec<String> => Vec<Py<PyString>>,
qubit_variables: Vec<String> => Vec<Py<PyString>>,
instructions: Vec<Instruction> => Vec<PyInstruction>
}
}
impl_repr!(PyCircuitDefinition);
impl_str!(PyCircuitDefinition);

#[pymethods]
impl PyCircuitDefinition {
#[new]
pub fn new(
py: Python<'_>,
name: String,
parameters: Vec<String>,
qubit_variables: Vec<String>,
instructions: Vec<PyInstruction>,
) -> PyResult<Self> {
Ok(Self(CircuitDefinition::new(
name,
parameters,
qubit_variables,
Vec::<Instruction>::py_try_from(py, &instructions)?,
)))
}

pub fn __richcmp__(&self, py: Python<'_>, other: &Self, op: CompareOp) -> PyObject {
match op {
CompareOp::Eq => (self.as_inner() == other.as_inner()).into_py(py),
_ => py.NotImplemented(),
}
}
}
4 changes: 4 additions & 0 deletions quil-py/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub use self::{
PyBinaryOperands, PyBinaryOperator,
},
calibration::{PyCalibration, PyMeasureCalibrationDefinition},
circuit::PyCircuitDefinition,
declaration::{
ParseMemoryReferenceError, PyDeclaration, PyMemoryReference, PyOffset, PyScalarType,
PySharing, PyVector,
Expand All @@ -27,6 +28,7 @@ pub use self::{

mod arithmetic;
mod calibration;
mod circuit;
mod declaration;
mod frame;
mod gate;
Expand All @@ -40,6 +42,7 @@ py_wrap_union_enum! {
arithmetic: Arithmetic => PyArithmetic,
binary_logic: BinaryLogic => PyBinaryLogic,
calibration_definition: CalibrationDefinition => PyCalibration,
circuit_definition: CircuitDefinition => PyCircuitDefinition,
declaration: Declaration => PyDeclaration,
frame_definition: FrameDefinition => PyFrameDefinition,
gate: Gate => PyGate,
Expand Down Expand Up @@ -75,6 +78,7 @@ create_init_submodule! {
PyBinaryOperands,
PyBinaryOperator,
PyCalibration,
PyCircuitDefinition,
PyMeasureCalibrationDefinition,
PyDeclaration,
PyOffset,
Expand Down
130 changes: 130 additions & 0 deletions quil-rs/src/instruction/circuit.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
use std::fmt;

use super::{get_string_parameter_string, Instruction};

#[derive(Clone, Debug, PartialEq)]
pub struct CircuitDefinition {
pub name: String,
pub parameters: Vec<String>,
// These cannot be fixed qubits and thus are not typed as `Qubit`
pub qubit_variables: Vec<String>,
pub instructions: Vec<Instruction>,
}

impl CircuitDefinition {
pub fn new(
name: String,
parameters: Vec<String>,
qubit_variables: Vec<String>,
instructions: Vec<Instruction>,
) -> Self {
Self {
name,
parameters,
qubit_variables,
instructions,
}
}
}

impl fmt::Display for CircuitDefinition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "DEFCIRCUIT {}", self.name)?;
if !self.parameters.is_empty() {
let parameter_str = get_string_parameter_string(&self.parameters);
write!(f, "{parameter_str}")?;
}
for qubit_variable in &self.qubit_variables {
write!(f, " {qubit_variable}")?;
}
writeln!(f, ":")?;
for instruction in &self.instructions {
for line in instruction.to_string().split('\n') {
writeln!(f, "\t{line}")?;
}
}
Ok(())
}
}

#[cfg(test)]
mod test_circuit_definition {
use crate::expression::Expression;
use crate::instruction::{Gate, Instruction, Qubit};

use super::CircuitDefinition;

use insta::assert_snapshot;
use rstest::rstest;

#[rstest]
#[case(
"CircuitDefinition No Params",
CircuitDefinition {
name: "BELL".to_owned(),
parameters: vec![],
qubit_variables: vec!["a".to_owned(), "b".to_owned()],
instructions: vec![
Instruction::Gate(Gate {
name: "H".to_owned(),
parameters: vec![],
qubits: vec![Qubit::Variable("a".to_owned())],
modifiers: vec![],
}),
Instruction::Gate(Gate {
name: "CNOT".to_owned(),
parameters: vec![],
qubits: vec![
Qubit::Variable("a".to_owned()),
Qubit::Variable("b".to_owned())
],
modifiers: vec![],
})
]
}
)]
#[case(
"CircuitDefinition With Params",
CircuitDefinition {
name: "BELL".to_owned(),
parameters: vec!["a".to_owned()],
qubit_variables: vec!["a".to_owned(), "b".to_owned()],
instructions: vec![
Instruction::Gate(Gate {
name: "RZ".to_owned(),
parameters: vec![Expression::Variable("a".to_owned())],
qubits: vec![Qubit::Variable("a".to_owned())],
modifiers: vec![],
}),
Instruction::Gate(Gate {
name: "RX".to_owned(),
parameters: vec![Expression::Variable("a".to_owned())],
qubits: vec![Qubit::Variable("a".to_owned())],
modifiers: vec![],
}),
Instruction::Gate(Gate {
name: "RZ".to_owned(),
parameters: vec![Expression::Variable("a".to_owned())],
qubits: vec![Qubit::Variable("a".to_owned())],
modifiers: vec![],
}),
Instruction::Gate(Gate {
name: "CNOT".to_owned(),
parameters: vec![],
qubits: vec![
Qubit::Variable("a".to_owned()),
Qubit::Variable("b".to_owned())
],
modifiers: vec![],
})
]
}
)]
fn test_display(#[case] description: &str, #[case] circuit_def: CircuitDefinition) {
insta::with_settings!({
snapshot_suffix => description,
}, {
assert_snapshot!(circuit_def.to_string())
})
}
}
36 changes: 3 additions & 33 deletions quil-rs/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ use nom_locate::LocatedSpan;

mod arithmetic;
mod calibration;
mod circuit;
mod declaration;
mod frame;
mod gate;
Expand All @@ -38,6 +39,7 @@ pub use self::arithmetic::{
BinaryOperator,
};
pub use self::calibration::{Calibration, MeasureCalibrationDefinition};
pub use self::circuit::CircuitDefinition;
pub use self::declaration::{Declaration, MemoryReference, Offset, ScalarType, Sharing, Vector};
pub use self::frame::{AttributeValue, FrameAttributes, FrameDefinition, FrameIdentifier};
pub use self::gate::{
Expand Down Expand Up @@ -118,15 +120,6 @@ pub struct Include {
pub filename: String,
}

#[derive(Clone, Debug, PartialEq)]
pub struct CircuitDefinition {
pub name: String,
pub parameters: Vec<String>,
// These cannot be fixed qubits and thus are not typed as `Qubit`
pub qubit_variables: Vec<String>,
pub instructions: Vec<Instruction>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Reset {
pub qubit: Option<Qubit>,
Expand Down Expand Up @@ -461,30 +454,7 @@ impl fmt::Display for Instruction {
}
write!(f, "CAPTURE {frame} {waveform} {memory_reference}")
}
Instruction::CircuitDefinition(CircuitDefinition {
name,
parameters,
qubit_variables,
instructions,
}) => {
let mut parameter_str: String = parameters
.iter()
.map(|p| format!("%{p}"))
.collect::<Vec<String>>()
.join(", ");
if !parameter_str.is_empty() {
parameter_str = format!("({parameter_str})");
}
write!(f, "DEFCIRCUIT {name}{parameter_str}")?;
for qubit_variable in qubit_variables {
write!(f, " {qubit_variable}")?;
}
writeln!(f, ":")?;
for instruction in &**instructions {
writeln!(f, "\t{instruction}")?;
}
Ok(())
}
Instruction::CircuitDefinition(circuit) => write!(f, "{circuit}"),
Instruction::Convert(Convert { from, to }) => {
write!(f, "CONVERT {to} {from}")?;
Ok(())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: quil-rs/src/instruction/circuit.rs
expression: circuit_def.to_string()
---
DEFCIRCUIT BELL a b:
H a
CNOT a b

Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: quil-rs/src/instruction/circuit.rs
expression: circuit_def.to_string()
---
DEFCIRCUIT BELL(%a) a b:
RZ(%a) a
RX(%a) a
RZ(%a) a
CNOT a b