pub mod matcher;
pub mod pattern;
use hugr::types::EdgeKind;
use hugr::{HugrView, OutgoingPort};
use itertools::Itertools;
pub use matcher::{PatternMatch, PatternMatcher};
pub use pattern::CircuitPattern;
use derive_more::{Display, Error};
use hugr::{
ops::{OpTag, OpTrait},
Node, Port,
};
use matcher::MatchOp;
use crate::{circuit::Circuit, utils::type_is_linear};
type PNode = MatchOp;
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
enum PEdge {
InternalEdge {
src: Port,
dst: Port,
is_reversible: bool,
},
InputEdge { src: Port },
}
#[derive(Debug, Clone, Error, Display)]
#[non_exhaustive]
enum InvalidEdgeProperty {
#[display("{port} in {node} is linked to multiple edges")]
AmbiguousEdge { port: Port, node: Node },
#[display("{port} in {node} is not linked to any edge")]
NoLinkedEdge { port: Port, node: Node },
#[display("{port} in {node} does not have a type")]
UntypedPort { port: Port, node: Node },
}
impl PEdge {
fn try_from_port(node: Node, port: Port, circ: &Circuit) -> Result<Self, InvalidEdgeProperty> {
let hugr = circ.hugr();
let src = port;
let (dst_node, dst) = hugr
.linked_ports(node, src)
.exactly_one()
.map_err(|mut e| {
if e.next().is_some() {
InvalidEdgeProperty::AmbiguousEdge { port: src, node }
} else {
InvalidEdgeProperty::NoLinkedEdge { port: src, node }
}
})?;
if hugr.get_optype(dst_node).tag() == OpTag::Input {
return Ok(Self::InputEdge { src });
}
let port_type = match hugr.get_optype(node).port_kind(src) {
Some(EdgeKind::Value(typ)) => typ,
Some(EdgeKind::Const(typ)) => typ,
_ => return Err(InvalidEdgeProperty::UntypedPort { node, port: src }),
};
let is_reversible = type_is_linear(&port_type);
Ok(Self::InternalEdge {
src,
dst,
is_reversible,
})
}
}
impl portmatching::EdgeProperty for PEdge {
type OffsetID = Port;
fn reverse(&self) -> Option<Self> {
match *self {
Self::InternalEdge {
src,
dst,
is_reversible,
} => is_reversible.then_some(Self::InternalEdge {
src: dst,
dst: src,
is_reversible,
}),
Self::InputEdge { .. } => None,
}
}
fn offset_id(&self) -> Self::OffsetID {
match *self {
Self::InternalEdge { src, .. } => src,
Self::InputEdge { src, .. } => src,
}
}
}
#[derive(
Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize, serde::Deserialize,
)]
pub(super) enum NodeID {
HugrNode(Node),
CopyNode(Node, Port),
}
impl NodeID {
pub fn new_copy(node: Node, port: impl Into<OutgoingPort>) -> Self {
let port: OutgoingPort = port.into();
Self::CopyNode(node, port.into())
}
}
impl From<Node> for NodeID {
fn from(node: Node) -> Self {
Self::HugrNode(node)
}
}
#[cfg(test)]
mod tests {
use crate::{Circuit, Tk2Op};
use hugr::{
builder::{DFGBuilder, Dataflow, DataflowHugr},
extension::prelude::qb_t,
types::Signature,
};
use rstest::{fixture, rstest};
use super::{CircuitPattern, PatternMatcher};
#[fixture]
fn lhs() -> Circuit {
let mut h = DFGBuilder::new(Signature::new(vec![], vec![qb_t()])).unwrap();
let res = h.add_dataflow_op(Tk2Op::QAlloc, []).unwrap();
let q = res.out_wire(0);
h.finish_hugr_with_outputs([q]).unwrap().into()
}
#[fixture]
pub fn circ() -> Circuit {
let mut h = DFGBuilder::new(Signature::new(vec![qb_t()], vec![qb_t()])).unwrap();
let mut inps = h.input_wires();
let q_in = inps.next().unwrap();
let res = h.add_dataflow_op(Tk2Op::QAlloc, []).unwrap();
let q_out = res.out_wire(0);
let res = h.add_dataflow_op(Tk2Op::CZ, [q_in, q_out]).unwrap();
let q_in = res.out_wire(0);
let q_out = res.out_wire(1);
h.add_dataflow_op(Tk2Op::QFree, [q_in]).unwrap();
h.finish_hugr_with_outputs([q_out]).unwrap().into()
}
#[rstest]
fn simple_match(circ: Circuit, lhs: Circuit) {
let p = CircuitPattern::try_from_circuit(&lhs).unwrap();
let m = PatternMatcher::from_patterns(vec![p]);
let matches = m.find_matches(&circ);
assert_eq!(matches.len(), 1);
}
}