Skip to content

Commit

Permalink
feat!: Support any ops in portmatching (#293)
Browse files Browse the repository at this point in the history
Use the encoded operation as a hacky way to compare different
instantiations (since we need it to implement `Hash + Eq + Ord`).

We can avoid the encoding when we know the operation has no parameters.


BREAKING CHANGE: This is a breaking change to the compiled rewriter
serialisation format.
  • Loading branch information
aborgna-q authored Feb 21, 2024
1 parent 09ab3a2 commit 6b05a05
Show file tree
Hide file tree
Showing 6 changed files with 31 additions and 29 deletions.
Binary file modified test_files/nam_6_3.rwr
Binary file not shown.
Binary file modified test_files/small_eccs.rwr
Binary file not shown.
Binary file modified tket2-py/tket2/data/nam_6_3.rwr
Binary file not shown.
1 change: 1 addition & 0 deletions tket2/src/json/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ impl JsonOp {
/// [`JsonOp::new_from_op`] for a version that generates a signature if none
/// is defined.
#[allow(unused)]
#[allow(clippy::question_mark)]
pub fn new(op: circuit_json::Operation) -> Option<Self> {
let Some(sig) = &op.signature else {
return None;
Expand Down
57 changes: 29 additions & 28 deletions tket2/src/portmatching/matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,55 +12,57 @@ use hugr::hugr::views::sibling_subgraph::{
InvalidReplacement, InvalidSubgraph, InvalidSubgraphBoundary, TopoConvexChecker,
};
use hugr::hugr::views::SiblingSubgraph;
use hugr::ops::OpType;
use hugr::ops::{OpName, OpType};
use hugr::{Hugr, IncomingPort, Node, OutgoingPort, Port, PortIndex};
use itertools::Itertools;
use portgraph::algorithms::ConvexChecker;
use portmatching::{
automaton::{LineBuilder, ScopeAutomaton},
EdgeProperty, PatternID,
};
use smol_str::SmolStr;
use thiserror::Error;

#[cfg(feature = "pyo3")]
use pyo3::prelude::*;

use crate::{
circuit::Circuit,
ops::NotTk2Op,
rewrite::{CircuitRewrite, Subcircuit},
Tk2Op,
};

/// Matchable operations in a circuit.
///
/// We currently support [`Tk2Op`] and a the HUGR load constant operation.
// TODO: Support OpType::Const, but blocked by use of F64 (Eq support required)
#[derive(
Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize, serde::Deserialize,
)]
pub(crate) enum MatchOp {
/// A TKET2 operation.
Op(Tk2Op),
/// A HUGR load constant operation.
LoadConstant,
}

impl From<Tk2Op> for MatchOp {
fn from(op: Tk2Op) -> Self {
Self::Op(op)
}
pub(crate) struct MatchOp {
/// The operation identifier
op_name: SmolStr,
/// The encoded operation, if necessary for comparisons.
///
/// This as a temporary hack for comparing parametric operations, since
/// OpType doesn't implement Eq, Hash, or Ord.
encoded: Option<Vec<u8>>,
}

impl TryFrom<OpType> for MatchOp {
type Error = NotTk2Op;

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(NotTk2Op),
}
impl From<OpType> for MatchOp {
fn from(op: OpType) -> Self {
let op_name = op.name();
// Avoid encoding some operations if we know they can be uniquely
// identified by their name.
let encoded = match op {
OpType::Module(_) => None,
OpType::LeafOp(leaf)
if leaf
.as_extension_op()
.map(|ext| ext.args().is_empty())
.unwrap_or_default() =>
{
None
}
_ => rmp_serde::encode::to_vec(&op).ok(),
};
Self { op_name, encoded }
}
}

Expand Down Expand Up @@ -448,8 +450,7 @@ pub(crate) fn validate_circuit_node(
let NodeID::HugrNode(node) = node else {
return false;
};
let v_weight = MatchOp::try_from(circ.get_optype(node).clone());
v_weight.is_ok_and(|w| &w == prop)
&MatchOp::from(circ.get_optype(node).clone()) == prop
}
}

Expand Down
2 changes: 1 addition & 1 deletion tket2/src/portmatching/pattern.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ impl CircuitPattern {
let mut pattern = Pattern::new();
for cmd in circuit.commands() {
let op = cmd.optype().clone();
pattern.require(cmd.node().into(), op.try_into().unwrap());
pattern.require(cmd.node().into(), op.into());
for in_offset in 0..cmd.input_count() {
let in_offset: IncomingPort = in_offset.into();
let edge_prop = PEdge::try_from_port(cmd.node(), in_offset.into(), circuit)
Expand Down

0 comments on commit 6b05a05

Please sign in to comment.