-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
598 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,7 @@ | |
//! # } | ||
//! ``` | ||
pub mod indexing; | ||
// pub mod matcher; | ||
// pub mod pattern; | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
//! Indexing schemes for pattern matching with portmatching. | ||
//! | ||
//! Indexing schemes assign a unique variable name for every variable (i.e every | ||
//! port and node in the hugr). This is used by the portmatcher to express | ||
//! constraints to be checked whilst matching. | ||
use std::collections::BTreeMap; | ||
|
||
use derive_more::From; | ||
use hugr::HugrView; | ||
use itertools::Itertools; | ||
use portmatching as pm; | ||
|
||
use crate::Circuit; | ||
|
||
mod path; | ||
use path::HugrPath; | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
//////////////////// Variable Naming scheme used for Hugrs ///////////////////// | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// Variables refer to either a node or a port in the hugr. | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, From)] | ||
pub enum HugrVariableID { | ||
/// A variable that binds to a node. | ||
Node(HugrNodeID), | ||
/// A variable that binds to a port. | ||
Port(HugrPortID), | ||
} | ||
|
||
impl HugrVariableID { | ||
/// Resolve the variable ID to a unique value in the hugr given `bindings`. | ||
/// | ||
/// Any non-incoming port variable and non-root node variable can be | ||
/// resolved uniquely (if it exists) to a value. Calling `resolve` on an | ||
/// incoming port variable will panic. | ||
/// | ||
/// This assumes that the required incoming port bindings are in `bindings`. | ||
fn resolve(&self, bindings: &HugrBindMap, hugr: &impl HugrView) -> Option<HugrVariableValue> { | ||
match self { | ||
HugrVariableID::Node(node) => node.resolve(bindings), | ||
HugrVariableID::Port(port) => port.resolve(bindings, hugr), | ||
} | ||
} | ||
} | ||
|
||
/// The value of a variable in the indexing scheme, either a node or a port. | ||
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
pub enum HugrVariableValue { | ||
/// The value of a HugrVariableID::Node variable. | ||
Node(hugr::Node), | ||
/// The value of a HugrVariableID::Port(HugrPortID::Incoming) variable. | ||
IncomingPort(hugr::Node, hugr::IncomingPort), | ||
/// The value of a HugrVariableID::Port(HugrPortID::Outgoing) variable. | ||
OutgoingPort(hugr::Node, hugr::OutgoingPort), | ||
} | ||
|
||
impl HugrVariableValue { | ||
fn node(&self) -> hugr::Node { | ||
match *self { | ||
HugrVariableValue::Node(node) => node, | ||
HugrVariableValue::IncomingPort(node, _) => node, | ||
HugrVariableValue::OutgoingPort(node, _) => node, | ||
} | ||
} | ||
} | ||
|
||
/// A map to store bindings for variables in a hugr. | ||
pub type HugrBindMap = BTreeMap<HugrVariableID, HugrVariableValue>; | ||
|
||
/// A port variable ID, given based on the unique IDs given to incoming ports. | ||
/// | ||
/// - An incoming port ID is given by a path from a root node to an incoming port. | ||
/// - An outgoing port ID is given by the opposite incoming port ID. This defines | ||
/// the outgoing port uniquely as there is a one-to-many outgoing port to | ||
/// incoming port relationship. | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
pub enum HugrPortID { | ||
/// A port variable that binds to an outgoing port. | ||
Outgoing { | ||
/// The path from the root node to an incoming port opposite. | ||
opposite_port: HugrPath, | ||
}, | ||
/// A port variable that binds to an incoming port. | ||
Incoming { | ||
/// The path from the root node to the incoming port. | ||
path_from_root: HugrPath, | ||
}, | ||
} | ||
|
||
impl HugrPortID { | ||
/// Resolve an outgoing port ID given `bindings` that specify the opposite port. | ||
/// | ||
/// Returns a HugrVariableValue::OutgoingPort variant. Calling this on an | ||
/// incoming port ID will result in a panic. | ||
fn resolve(&self, bindings: &HugrBindMap, hugr: &impl HugrView) -> Option<HugrVariableValue> { | ||
match self { | ||
&HugrPortID::Outgoing { opposite_port } => { | ||
let opp_var = HugrPortID::new_incoming(opposite_port).into(); | ||
let &HugrVariableValue::IncomingPort(opp_node, opp_port) = | ||
bindings.get(&opp_var)? | ||
else { | ||
panic!("expected opposite port to be incoming"); | ||
}; | ||
// Currently, silently fail if there is more than one output | ||
let (node, port) = hugr.single_linked_output(opp_node, opp_port)?; | ||
HugrVariableValue::OutgoingPort(node, port).into() | ||
} | ||
HugrPortID::Incoming { .. } => { | ||
panic!("Incoming port IDs do not resolve uniquely to a value") | ||
} | ||
} | ||
} | ||
|
||
fn new_incoming(path_from_root: HugrPath) -> Self { | ||
Self::Incoming { path_from_root } | ||
} | ||
} | ||
|
||
/// A node variable ID, given based on a unique ID given to one of its ports. | ||
/// | ||
/// The root is a special case that must be handled separately, as it might not | ||
/// have any ports. | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
pub enum HugrNodeID { | ||
/// Variable that binds to the root node. | ||
Root, | ||
/// Variable that binds to a non-root node. | ||
NonRoot { | ||
/// A port that is incident to the node. | ||
incident_port: HugrPortID, | ||
}, | ||
} | ||
|
||
impl HugrNodeID { | ||
/// Resolve a non-root node ID to a node in the hugr given `bindings`. | ||
/// | ||
/// Root IDs cannot be resolved uniquely to a value. Calling this on a | ||
/// HugrNodeID::Root will result in a panic. | ||
fn resolve(&self, bindings: &HugrBindMap) -> Option<HugrVariableValue> { | ||
match self { | ||
HugrNodeID::Root => panic!("Root node IDs do not resolve uniquely to a value"), | ||
HugrNodeID::NonRoot { incident_port } => { | ||
let node = bindings.get(&(*incident_port).into())?.node(); | ||
HugrVariableValue::Node(node).into() | ||
} | ||
} | ||
} | ||
} | ||
|
||
//////////////////////////////////////////////////////////////////////////////// | ||
////////////// Indexing scheme: resolve variable IDs to values ///////////////// | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
/// An indexing scheme for hugrs that does not handle hierarchy. | ||
#[derive(Clone, Debug, Default)] | ||
pub struct FlatHugrIndexingScheme; | ||
|
||
impl pm::IndexingScheme for FlatHugrIndexingScheme { | ||
type BindMap = HugrBindMap; | ||
|
||
fn required_bindings(&self, key: &HugrVariableID) -> Vec<HugrVariableID> { | ||
match key { | ||
HugrVariableID::Node(node) => match node { | ||
// The root node can be bound to any node. | ||
HugrNodeID::Root => vec![], | ||
// Otherwise require the incident port to be bound. | ||
&HugrNodeID::NonRoot { incident_port } => vec![incident_port.into()], | ||
}, | ||
HugrVariableID::Port(port) => match port { | ||
&HugrPortID::Outgoing { opposite_port } => { | ||
// Require the opposite port to be bound. | ||
let port_id = HugrPortID::new_incoming(opposite_port); | ||
vec![port_id.into()] | ||
} | ||
HugrPortID::Incoming { path_from_root } => { | ||
// Require the parent of the incoming port to be bound. | ||
if let Some(parent_path) = path_from_root.parent() { | ||
let port_id = HugrPortID::new_incoming(parent_path); | ||
vec![port_id.into()] | ||
} else { | ||
vec![HugrNodeID::Root.into()] | ||
} | ||
} | ||
}, | ||
} | ||
} | ||
} | ||
|
||
impl<H: HugrView> pm::IndexedData for Circuit<H> { | ||
type IndexingScheme = FlatHugrIndexingScheme; | ||
|
||
fn list_bind_options( | ||
&self, | ||
key: &HugrVariableID, | ||
known_bindings: &HugrBindMap, | ||
) -> Vec<HugrVariableValue> { | ||
match key { | ||
HugrVariableID::Node(HugrNodeID::Root) => { | ||
// Every hugr node is a valid binding for the root node. | ||
let nodes = self.commands().map(|cmd| cmd.node()); | ||
nodes.map(HugrVariableValue::Node).map_into().collect() | ||
} | ||
HugrVariableID::Port(HugrPortID::Incoming { path_from_root }) => { | ||
let ports = path_from_root.list_bind_options(known_bindings, self.hugr()); | ||
ports | ||
.into_iter() | ||
.map(|(n, p)| HugrVariableValue::IncomingPort(n, p)) | ||
.collect() | ||
} | ||
// Otherwise, resolves uniquely | ||
key @ _ => Vec::from_iter(key.resolve(known_bindings, self.hugr())), | ||
} | ||
} | ||
} |
Oops, something went wrong.