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

refactor: simplify json encoding using t2op #78

Merged
merged 2 commits into from
Sep 4, 2023
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
109 changes: 43 additions & 66 deletions src/json/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
//! circuits by ensuring they always define a signature, and computing the
//! explicit count of qubits and linear bits.

use crate::ops::EXTENSION_ID as QUANTUM_EXTENSION_ID;
use hugr::extension::prelude::QB_T;

use hugr::ops::custom::ExternalOp;
Expand Down Expand Up @@ -189,30 +188,8 @@ impl From<&JsonOp> for OpType {
JsonOpType::T => T2Op::T.into(),
JsonOpType::Tdg => T2Op::Tdg.into(),
JsonOpType::X => T2Op::X.into(),
JsonOpType::Rz => T2Op::RzF64.into(),
JsonOpType::noop => LeafOp::Noop { ty: QB_T }.into(),
// TODO TKET1 measure takes a bit as input, HUGR measure does not
//JsonOpType::Measure => LeafOp::Measure.into(),
// JsonOpType::Reset => LeafOp::Reset.into(),
// JsonOpType::ZZMax => LeafOp::ZZMax.into(),
// JsonOpType::Rz => LeafOp::RzF64.into(),
// JsonOpType::RzF64 => LeafOp::RzF64.into(),
// TODO TKET1 I/O needs some special handling
//JsonOpType::Input => hugr::ops::Input {
// types: json_op.signature().output,
// extensions: Default::default(),
//}
//.into(),
//JsonOpType::Output => hugr::ops::Output {
// types: json_op.signature().input,
// extensions: Default::default(),
//}
//.into(),
// JsonOpType::Z => LeafOp::Z.into(),
// JsonOpType::Y => LeafOp::Y.into(),
// JsonOpType::S => LeafOp::S.into(),
// JsonOpType::Sdg => LeafOp::Sadj.into(),
// JsonOpType::T => LeafOp::T.into(),
// JsonOpType::Tdg => LeafOp::Tadj.into(),
_ => LeafOp::CustomOp(Box::new(json_op.as_opaque_op())).into(),
}
}
Expand All @@ -222,56 +199,56 @@ impl TryFrom<&OpType> for JsonOp {
type Error = OpConvertError;

fn try_from(op: &OpType) -> Result<Self, Self::Error> {
// We only translate operations that have a 1:1 mapping between TKET and HUGR
// We only translate operations that have a 1:1 mapping between TKET and TKET2
//
// Other TKET1 operations are wrapped in an `OpaqueOp`.
//
// Non-supported Hugr operations throw an error.
let err = || OpConvertError::UnsupportedOpSerialization(op.clone());
let OpType::LeafOp(leaf) = op else {
return Err(err());
};

let json_optype: JsonOpType = match op {
OpType::LeafOp(LeafOp::Noop { .. }) => JsonOpType::noop,
OpType::LeafOp(LeafOp::CustomOp(b)) => match (*b).as_ref() {
ExternalOp::Extension(c) if c.def().extension() == &QUANTUM_EXTENSION_ID => {
match &c.def().name()[..] {
"H" => JsonOpType::H,
"CX" => JsonOpType::CX,
_ => return Err(err()),
}
}
ext => {
return try_unwrap_json_op(ext).ok_or_else(err);
} // h_gate() => JsonOpType::H,
// LeafOp::ZZMax => JsonOpType::ZZMax,
// LeafOp::Reset => JsonOpType::Reset,
// //LeafOp::Measure => JsonOpType::Measure,
// LeafOp::T => JsonOpType::T,
// LeafOp::S => JsonOpType::S,
// LeafOp::X => JsonOpType::X,
// LeafOp::Y => JsonOpType::Y,
// LeafOp::Z => JsonOpType::Z,
// LeafOp::Tadj => JsonOpType::Tdg,
// LeafOp::Sadj => JsonOpType::Sdg,
//LeafOp::RzF64 => JsonOpType::Rz, // The angle in RzF64 comes from a constant input
//LeafOp::Xor => todo!(),
//LeafOp::MakeTuple { .. } => todo!(),
//LeafOp::UnpackTuple { .. } => todo!(),
//LeafOp::Tag { .. } => todo!(),
//LeafOp::Lift { .. } => todo!(),
// CustomOp is handled above
},
//OpType::Input(_) => JsonOpType::Input,
//OpType::Output(_) => JsonOpType::Output,
//hugr::ops::OpType::FuncDefn(_) => todo!(),
//hugr::ops::OpType::FuncDecl(_) => todo!(),
//hugr::ops::OpType::Const(_) => todo!(),
//hugr::ops::OpType::Call(_) => todo!(),
//hugr::ops::OpType::CallIndirect(_) => todo!(),
//hugr::ops::OpType::LoadConstant(_) => todo!(),
//hugr::ops::OpType::DFG(_) => JsonOpType::CircBox, // TODO: Requires generating the Operation::op_box
_ => return Err(err()),
let json_optype = if let Ok(t2op) = leaf.clone().try_into() {
match t2op {
T2Op::CX => JsonOpType::CX,
T2Op::H => JsonOpType::H,
T2Op::Measure => JsonOpType::Measure,
T2Op::RzF64 => JsonOpType::RzF64,
_ => return Err(err()),
}
} else if let LeafOp::CustomOp(b) = leaf {
let ext = (*b).as_ref();
return try_unwrap_json_op(ext).ok_or_else(err);
} else {
return Err(err());
};

// let json_optype: JsonOpType = match leaf {
// LeafOp::Noop { .. } => JsonOpType::noop,
// OpType::LeafOp(l) => match l.clone().try_into() {
// Ok(t2op) => match t2op {
// T2Op::CX => JsonOpType::CX,
// T2Op::H => JsonOpType::H,
// T2Op::Measure => JsonOpType::Measure,
// T2Op::RzF64 => JsonOpType::RzF64,
// _ => {
// return Err(err());
// }
// },

// Err(_) => match l {
// LeafOp::CustomOp(b) => {
// let ext = (*b).as_ref();
// return try_unwrap_json_op(ext).ok_or_else(err);
// }

// _ => return Err(err()),
// },
// },
// _ => return Err(err()),
// };

let mut num_qubits = 0;
let mut num_bits = 0;
let mut num_params = 0;
Expand Down
41 changes: 31 additions & 10 deletions src/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize};
use std::str::FromStr;
use strum::IntoEnumIterator;
use strum_macros::{Display, EnumIter, EnumString, IntoStaticStr};
use thiserror::Error;

/// Name of tket 2 extension.
pub const EXTENSION_ID: ExtensionId = ExtensionId::new_inline("quantum.tket2");
Expand Down Expand Up @@ -61,14 +62,21 @@ pub enum Pauli {
Z,
}

#[derive(Debug, Error, PartialEq, Clone, Copy)]
#[error("Not a T2Op.")]
pub struct NotT2Op;

// this trait could be implemented in Hugr
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// this trait could be implemented in Hugr

not anymore

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

made more generic so that this remains true hopefully

trait SimpleOpEnum: Into<&'static str> + FromStr + Copy + IntoEnumIterator {
type LoadError: std::error::Error;

fn signature(&self) -> FunctionType;
fn name(&self) -> &str {
(*self).into()
}
fn try_from_op_def(op_def: &OpDef) -> Result<Self, <Self as FromStr>::Err> {
Self::from_str(op_def.name())
fn from_extension_name(extension: &str, op_name: &str) -> Result<Self, Self::LoadError>;
fn try_from_op_def(op_def: &OpDef) -> Result<Self, Self::LoadError> {
Self::from_extension_name(op_def.extension(), op_def.name())
}
fn add_to_extension<'e>(
&self,
Expand All @@ -80,13 +88,21 @@ trait SimpleOpEnum: Into<&'static str> + FromStr + Copy + IntoEnumIterator {
}
}

fn from_extension_name<T: SimpleOpEnum>(extension: &str, op_name: &str) -> Result<T, NotT2Op> {
if extension != EXTENSION_ID {
return Err(NotT2Op);
}
T::from_str(op_name).map_err(|_| NotT2Op)
}

impl Pauli {
/// Check if this pauli commutes with another.
pub fn commutes_with(&self, other: Self) -> bool {
*self == Pauli::I || other == Pauli::I || *self == other
}
}
impl SimpleOpEnum for T2Op {
type LoadError = NotT2Op;
fn signature(&self) -> FunctionType {
use T2Op::*;
let one_qb_row = type_row![QB_T];
Expand Down Expand Up @@ -117,6 +133,13 @@ impl SimpleOpEnum for T2Op {
move |_: &_| Ok(FunctionType::new(input.clone(), output.clone())),
)
}

fn from_extension_name(extension: &str, op_name: &str) -> Result<Self, Self::LoadError> {
if extension != EXTENSION_ID {
return Err(NotT2Op);
}
Self::from_str(op_name).map_err(|_| NotT2Op)
}
}

impl T2Op {
Expand Down Expand Up @@ -163,26 +186,24 @@ impl From<T2Op> for OpType {
}

impl TryFrom<OpType> for T2Op {
type Error = &'static str;
type Error = NotT2Op;

fn try_from(op: OpType) -> Result<Self, Self::Error> {
let leaf: LeafOp = op.try_into().map_err(|_| "not a leaf.")?;
let leaf: LeafOp = op.try_into().map_err(|_| NotT2Op)?;
leaf.try_into()
}
}

impl TryFrom<LeafOp> for T2Op {
type Error = &'static str;
type Error = NotT2Op;

fn try_from(op: LeafOp) -> Result<Self, Self::Error> {
match op {
LeafOp::CustomOp(b) => match *b {
ExternalOp::Extension(e) => {
Self::try_from_op_def(e.def()).map_err(|_| "not a T2Op")
}
ExternalOp::Opaque(_) => todo!(),
ExternalOp::Extension(e) => Self::try_from_op_def(e.def()),
ExternalOp::Opaque(o) => from_extension_name(o.extension(), o.name()),
},
_ => Err("not a custom."),
_ => Err(NotT2Op),
}
}
}
Expand Down
6 changes: 3 additions & 3 deletions src/portmatching/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ use thiserror::Error;
#[cfg(feature = "pyo3")]
use pyo3::prelude::*;

use crate::{circuit::Circuit, T2Op};
use crate::{circuit::Circuit, ops::NotT2Op, T2Op};

/// Matchable operations in a circuit.
///
Expand All @@ -45,13 +45,13 @@ impl From<T2Op> for MatchOp {
}

impl TryFrom<OpType> for MatchOp {
type Error = &'static str;
type Error = NotT2Op;

fn try_from(value: OpType) -> Result<Self, Self::Error> {
match value {
OpType::LeafOp(op) => Ok(Self::Op(op.try_into()?)),
OpType::LoadConstant(_) => Ok(Self::LoadConstant),
_ => Err("Unsupported op type"),
_ => Err(NotT2Op),
}
}
}
Expand Down