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

Support PAULI-SUM Gate Specifications #177

Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ members = [
[profile.release]
lto = true
codegen-units = 1

[workspace.dependencies]
strum = { version = "0.24.1", features = ["derive"] }
1 change: 1 addition & 0 deletions quil-py/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
quil-rs = { path = "../quil-rs", version="0.16.0-rc.0" }
strum.workspace = true
# pyo3 dependencies should be updated together
pyo3 = { version = "0.17" }
rigetti-pyo3 = "0.1.0-rc.6"
Expand Down
39 changes: 38 additions & 1 deletion quil-py/quil/instructions/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,38 @@ class Gate:
"""
...

@final
class PauliWord(Enum):
I = "I"
X = "X"
Y = "Y"
Z = "Z"
@staticmethod
def parse(word: str) -> "PauliWord":
"""
Parses a ``PauliWord`` from a string. Raises a ``ParseEnumError`` if the
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
string isn't a valid Pauli word.
"""
...

class PauliTerm:
@staticmethod
def __new__(
cls, words: List[PauliWord], expression: Expression, arguments: List[str]
) -> "PauliTerm": ...
@property
def words(self) -> List[PauliWord]: ...
@words.setter
def words(self, words: List[PauliWord]): ...
@property
def expression(self) -> Expression: ...
@expression.setter
def expression(self, expression: Expression): ...
@property
def arguments(self) -> List[str]: ...
@arguments.setter
def arguments(self, arguments: List[str]): ...

@final
class GateSpecification:
"""
Expand All @@ -487,21 +519,26 @@ class GateSpecification:
- from_*: Creates a new ``GateSpecification`` using an instance of the inner type for the variant.
"""

def inner(self) -> Union[List[List[Expression]], List[int]]:
def inner(self) -> Union[List[List[Expression]], List[int], List[PauliTerm]]:
"""
Returns the inner value of the variant. Raises a ``RuntimeError`` if inner data doesn't exist.
"""
...
def is_matrix(self) -> bool: ...
def is_permutation(self) -> bool: ...
def is_pauli_sum(self) -> bool: ...
def as_matrix(self) -> Optional[List[List[Expression]]]: ...
def to_matrix(self) -> List[List[Expression]]: ...
def as_permutation(self) -> Optional[List[int]]: ...
def to_permutation(self) -> List[int]: ...
def as_pauli_sum(self) -> Optional[List[PauliTerm]]: ...
def to_pauli_sum(self) -> List[PauliTerm]: ...
@staticmethod
def from_matrix(matrix: List[List[Expression]]) -> "GateSpecification": ...
@staticmethod
def from_permutation(permutation: List[int]) -> "GateSpecification": ...
@staticmethod
def from_pauli_sum(pauli_term: List[PauliTerm]) -> "GateSpecification": ...

class GateDefinition:
@classmethod
Expand Down
55 changes: 51 additions & 4 deletions quil-py/src/instruction/gate.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
use quil_rs::{
expression::Expression,
instruction::{Gate, GateDefinition, GateModifier, GateSpecification, Qubit},
instruction::{
Gate, GateDefinition, GateModifier, GateSpecification, PauliTerm, PauliWord, Qubit,
},
};
use strum;

use rigetti_pyo3::{
impl_repr, impl_str, py_wrap_data_struct, py_wrap_error, py_wrap_simple_enum,
py_wrap_union_enum,
impl_from_str, impl_parse, impl_repr, impl_str, py_wrap_data_struct, py_wrap_error,
py_wrap_simple_enum, py_wrap_union_enum,
pyo3::{
exceptions::PyValueError,
pyclass::CompareOp,
Expand All @@ -21,6 +24,8 @@ use crate::expression::PyExpression;

wrap_error!(RustGateError(quil_rs::instruction::GateError));
py_wrap_error!(quil, RustGateError, GateError, PyValueError);
wrap_error!(RustParseEnumError(strum::ParseError));
py_wrap_error!(quil, RustParseEnumError, EnumParseError, PyValueError);

py_wrap_data_struct! {
#[derive(Debug, PartialEq, Eq)]
Expand Down Expand Up @@ -114,11 +119,53 @@ impl PyGateModifier {
}
}

py_wrap_simple_enum! {
#[derive(Debug, PartialEq, Eq)]
PyPauliWord(PauliWord) as "PauliWord" {
I,
X,
Y,
Z
}
}
impl_repr!(PyPauliWord);
impl_str!(PyPauliWord);
impl_from_str!(PyPauliWord, RustParseEnumError);
impl_parse!(PyPauliWord);

py_wrap_data_struct! {
#[derive(Debug, PartialEq, Eq)]
#[pyo3(subclass)]
PyPauliTerm(PauliTerm) as "PauliTerm" {
words: Vec<PauliWord> => Vec<PyPauliWord>,
expression: Expression => PyExpression,
arguments: Vec<String> => Vec<Py<PyString>>
}
}

#[pymethods]
impl PyPauliTerm {
#[new]
pub fn new(
py: Python<'_>,
words: Vec<PyPauliWord>,
expression: PyExpression,
arguments: Vec<Py<PyString>>,
) -> PyResult<Self> {
Ok(Self(PauliTerm::new(
Vec::<PauliWord>::py_try_from(py, &words)?,
Expression::py_try_from(py, &expression)?,
Vec::<String>::py_try_from(py, &arguments)?,
)))
}
}

py_wrap_union_enum! {
#[derive(Debug, PartialEq, Eq)]
PyGateSpecification(GateSpecification) as "GateSpecification" {
matrix: Matrix => Vec<Vec<PyExpression>>,
permutation: Permutation => Vec<Py<PyInt>>
permutation: Permutation => Vec<Py<PyInt>>,
pauli_sum: PauliSum => Vec<PyPauliTerm>
}
}
impl_repr!(PyGateSpecification);
Expand Down
7 changes: 6 additions & 1 deletion quil-py/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ pub use self::{
PySharing, PyVector,
},
frame::{PyAttributeValue, PyFrameAttributes, PyFrameDefinition, PyFrameIdentifier},
gate::{GateError, PyGate, PyGateDefinition, PyGateModifier, PyGateSpecification},
gate::{
GateError, PyGate, PyGateDefinition, PyGateModifier, PyGateSpecification, PyPauliTerm,
PyPauliWord,
},
measurement::PyMeasurement,
qubit::PyQubit,
waveform::{PyWaveform, PyWaveformDefinition, PyWaveformInvocation},
Expand Down Expand Up @@ -89,6 +92,8 @@ create_init_submodule! {
PyGateDefinition,
PyGateModifier,
PyGateSpecification,
PyPauliTerm,
PyPauliWord,
PyMeasurement,
PyMemoryReference,
PyQubit,
Expand Down
2 changes: 1 addition & 1 deletion quil-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ nom_locate = "4.0.0"
num-complex = "0.4.0"
petgraph = "0.6.2"
serde = { version = "1.0.125", features = ["derive"] }
strum = { version = "0.24.1", features = ["derive"] }
strum.workspace = true
thiserror = "1.0.37"
once_cell = "1.17.1"

Expand Down
86 changes: 85 additions & 1 deletion quil-rs/src/instruction/gate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,13 +128,42 @@ impl fmt::Display for GateModifier {
}
}

#[derive(Copy, Clone, Debug, PartialEq, Eq, strum::Display, strum::EnumString)]
#[strum(serialize_all = "UPPERCASE")]
pub enum PauliWord {
I,
X,
Y,
Z,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PauliTerm {
pub words: Vec<PauliWord>,
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
pub expression: Expression,
pub arguments: Vec<String>,
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
}

impl PauliTerm {
pub fn new(words: Vec<PauliWord>, expression: Expression, arguments: Vec<String>) -> Self {
Self {
words,
expression,
arguments,
}
}
}

/// An enum representing a the specification of a [`GateDefinition`] for a given [`GateType`]
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum GateSpecification {
/// A matrix of [`Expression`]s representing a unitary operation for a [`GateType::Matrix`].
Matrix(Vec<Vec<Expression>>),
/// A vector of integers that defines the permutation used for a [`GateType::Permutation`]
Permutation(Vec<u64>),
///A Hermitian operator specified Pauli sum, a sum of combinations of Pauli operators, used for
///a [`GateType::PauliSum`]
PauliSum(Vec<PauliTerm>),
}

impl fmt::Display for GateSpecification {
Expand Down Expand Up @@ -162,6 +191,19 @@ impl fmt::Display for GateSpecification {
}
writeln!(f)?;
}
GateSpecification::PauliSum(pauli_sum) => {
for term in pauli_sum {
write!(f, "\t")?;
for word in term.words.iter() {
write!(f, "{word}")?;
}
write!(f, "{}", term.expression)?;
for argument in term.arguments.iter() {
write!(f, " {argument}")?;
}
writeln!(f)?;
}
}
}
Ok(())
}
Expand Down Expand Up @@ -196,6 +238,7 @@ impl fmt::Display for GateDefinition {
match self.specification {
GateSpecification::Matrix(_) => "MATRIX",
GateSpecification::Permutation(_) => "PERMUTATION",
GateSpecification::PauliSum(_) => "PAULI-SUM",
}
)?;
write!(f, "{}", self.specification)
Expand All @@ -204,7 +247,7 @@ impl fmt::Display for GateDefinition {

#[cfg(test)]
mod test_gate_definition {
use super::{GateDefinition, GateSpecification};
use super::{GateDefinition, GateSpecification, PauliTerm, PauliWord};
use crate::expression::{
Expression, ExpressionFunction, FunctionCallExpression, InfixExpression, InfixOperator,
PrefixExpression, PrefixOperator,
Expand Down Expand Up @@ -283,6 +326,45 @@ mod test_gate_definition {

}
)]
#[case(
"Pauli Sum GateDefinition",
GateDefinition{
name: "PauliSumGate".to_string(),
parameters: vec!["theta".to_string()],
specification: GateSpecification::PauliSum(vec![
PauliTerm {
words: vec![PauliWord::Z, PauliWord::Z],
expression: Expression::Infix(InfixExpression {
left: Box::new(Expression::Prefix(PrefixExpression {
operator: PrefixOperator::Minus,
expression: Box::new(Expression::Variable("theta".to_string()))
})),
operator: InfixOperator::Slash,
right: Box::new(Expression::Number(real!(4.0)))
}),
arguments: vec!["p".to_string(), "q".to_string()],
},
PauliTerm {
words: vec![PauliWord::Y],
expression: Expression::Infix(InfixExpression {
left: Box::new(Expression::Variable("theta".to_string())),
operator: InfixOperator::Slash,
right: Box::new(Expression::Number(real!(4.0)))
}),
arguments: vec!["p".to_string()],
},
PauliTerm {
words: vec![PauliWord::X],
expression: Expression::Infix(InfixExpression {
left: Box::new(Expression::Variable("theta".to_string())),
operator: InfixOperator::Slash,
right: Box::new(Expression::Number(real!(4.0)))
}),
arguments: vec!["q".to_string()],
},
])
}
)]
fn test_display(#[case] description: &str, #[case] gate_def: GateDefinition) {
insta::with_settings!({
snapshot_suffix => description,
Expand All @@ -297,13 +379,15 @@ mod test_gate_definition {
pub enum GateType {
Matrix,
Permutation,
PauliSum,
}

impl fmt::Display for GateType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Self::Matrix => write!(f, "MATRIX"),
Self::Permutation => write!(f, "PERMUTATION"),
Self::PauliSum => write!(f, "PAULI-SUM"),
}
}
}
5 changes: 4 additions & 1 deletion quil-rs/src/instruction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ pub use self::arithmetic::{
pub use self::calibration::{Calibration, MeasureCalibrationDefinition};
pub use self::declaration::{Declaration, MemoryReference, Offset, ScalarType, Sharing, Vector};
pub use self::frame::{AttributeValue, FrameAttributes, FrameDefinition, FrameIdentifier};
pub use self::gate::{Gate, GateDefinition, GateError, GateModifier, GateSpecification, GateType};
pub use self::gate::{
Gate, GateDefinition, GateError, GateModifier, GateSpecification, GateType, PauliTerm,
PauliWord,
};
pub use self::measurement::Measurement;
pub use self::qubit::Qubit;
pub use self::waveform::{Waveform, WaveformDefinition, WaveformInvocation};
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
source: quil-rs/src/instruction/gate.rs
expression: gate_def.to_string()
---
DEFGATE PauliSumGate(%theta) AS PAULI-SUM:
MarquessV marked this conversation as resolved.
Show resolved Hide resolved
ZZ((-%theta)/4) p q
Y(%theta/4) p
X(%theta/4) q

5 changes: 3 additions & 2 deletions quil-rs/src/parser/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ use super::{
common::{
parse_arithmetic_operand, parse_binary_logic_operand, parse_comparison_operand,
parse_frame_attribute, parse_frame_identifier, parse_gate_modifier, parse_matrix,
parse_memory_reference, parse_permutation, parse_qubit, parse_sharing, parse_vector,
parse_waveform_invocation, parse_waveform_name,
parse_memory_reference, parse_pauli_sum, parse_permutation, parse_qubit, parse_sharing,
parse_vector, parse_waveform_invocation, parse_waveform_name,
},
expression::parse_expression,
ParserInput,
Expand Down Expand Up @@ -261,6 +261,7 @@ pub(crate) fn parse_defgate<'a>(input: ParserInput<'a>) -> InternalParserResult<
let (input, specification) = match gate_type {
GateType::Matrix => map(parse_matrix, GateSpecification::Matrix)(input)?,
GateType::Permutation => map(parse_permutation, GateSpecification::Permutation)(input)?,
GateType::PauliSum => map(parse_pauli_sum, GateSpecification::PauliSum)(input)?,
};

Ok((
Expand Down
Loading