Skip to content
Draft
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
20 changes: 6 additions & 14 deletions hugr-core/src/builder/build_traits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::hugr::hugrmut::InsertionResult;
use crate::hugr::linking::{HugrLinking, NameLinkingPolicy, NodeLinkingDirective};
use crate::hugr::views::HugrView;
use crate::metadata::Metadata;
use crate::ops::{self, OpTag, OpTrait, OpType, Tag, TailLoop};
use crate::ops::{self, OpType, Tag, TailLoop};
use crate::utils::collect_array;
use crate::{Extension, IncomingPort, Node, OutgoingPort};

Expand Down Expand Up @@ -851,29 +851,21 @@ fn wire_up<T: Dataflow + ?Sized>(
}

let src_parent = src_parent.expect("Node has no parent");
let Some(src_sibling) = iter::successors(dst_parent, |&p| base.get_parent(p))
if !iter::successors(dst_parent, |&p| base.get_parent(p))
.tuple_windows()
.find_map(|(ancestor, ancestor_parent)| {
(ancestor_parent == src_parent ||
.any(|(_ancestor, ancestor_parent)| {
ancestor_parent == src_parent ||
// Dom edge - in CFGs
Some(ancestor_parent) == src_parent_parent)
.then_some(ancestor)
Some(ancestor_parent) == src_parent_parent
})
else {
{
return Err(BuilderWiringError::NoRelationIntergraph {
src,
src_offset: src_port.into(),
dst,
dst_offset: dst_port.into(),
});
};

if !OpTag::ControlFlowChild.is_superset(base.get_optype(src).tag())
&& !OpTag::ControlFlowChild.is_superset(base.get_optype(src_sibling).tag())
{
// Add a state order constraint unless one of the nodes is a CFG BasicBlock
base.add_other_edge(src, src_sibling);
}
} else if !typ.copyable() & base.linked_ports(src, src_port).next().is_some() {
// Don't copy linear edges.
return Err(BuilderWiringError::NoCopyLinear {
Expand Down
1 change: 1 addition & 0 deletions hugr-core/src/hugr/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ pub trait HugrInternals {
// needed if we want to use petgraph's algorithms on the region graph).
// This won't be solvable until we do the big petgraph refactor -.-
// In the meantime, just wrap the portgraph in a `FlatRegion` as needed.
#[deprecated(note = "Use order_graph instead", since = "0.27.0")]
fn region_portgraph(
&self,
parent: Self::Node,
Expand Down
21 changes: 6 additions & 15 deletions hugr-core/src/hugr/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ impl<'a, H: HugrView> ValidationContext<'a, H> {
&self,
parent: H::Node,
) -> (Dominators<portgraph::NodeIndex>, H::RegionPortgraphNodes) {
let (region, node_map) = self.hugr.region_portgraph(parent);
let (region, node_map) = self.hugr.order_graph(parent);
let entry_node = self.hugr.children(parent).next().unwrap();
let doms = dominators::simple_fast(&region, node_map.to_portgraph(entry_node));
(doms, node_map)
Expand Down Expand Up @@ -422,7 +422,7 @@ impl<'a, H: HugrView> ValidationContext<'a, H> {
return Ok(());
}

let (region, node_map) = self.hugr.region_portgraph(parent);
let (region, node_map) = self.hugr.order_graph(parent);
let postorder = Topo::new(&region);
let nodes_visited = postorder
.iter(&region)
Expand Down Expand Up @@ -486,19 +486,6 @@ impl<'a, H: HugrView> ValidationContext<'a, H> {
{
if ancestor_parent == from_parent {
// External edge.
if !is_static {
// Must have an order edge.
self.hugr
.node_connections(from, ancestor)
.find(|&[p, _]| from_optype.port_kind(p) == Some(EdgeKind::StateOrder))
.ok_or(InterGraphEdgeError::MissingOrderEdge {
from,
from_offset,
to,
to_offset,
to_ancestor: ancestor,
})?;
}
return Ok(());
} else if Some(ancestor_parent) == from_parent_parent && !is_static {
// Dominator edge
Expand Down Expand Up @@ -793,6 +780,10 @@ pub enum InterGraphEdgeError<N: HugrNode> {
to_offset: Port,
ancestor_parent_op: Box<OpType>,
},
#[deprecated(
note = "These edges are not required, error will be removed in future",
since = "0.27.0"
)]
/// The sibling ancestors of the external inter-graph edge endpoints must be have an order edge between them.
#[error(
"Missing state order between the external inter-graph source {from} and the ancestor of the target {to_ancestor}. In an external inter-graph edge from {from} ({from_offset}) to {to} ({to_offset})."
Expand Down
17 changes: 9 additions & 8 deletions hugr-core/src/hugr/validate/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ fn df_children_restrictions() {

#[test]
fn test_ext_edge() {
use petgraph::visit::IntoNeighbors as _;
let mut h = closed_dfg_root_hugr(Signature::new(vec![bool_t(), bool_t()], vec![bool_t()]));
let [input, output] = h.get_io(h.entrypoint()).unwrap();

Expand All @@ -220,15 +221,15 @@ fn test_ext_edge() {
assert_matches!(h.validate(), Err(ValidationError::UnconnectedPort { .. }));

h.connect(input, 1, sub_op, 1);
assert_matches!(
h.validate(),
Err(ValidationError::InterGraphEdgeError(
InterGraphEdgeError::MissingOrderEdge { .. }
))
);
//Order edge. This will need metadata indicating its purpose.
h.add_other_edge(input, sub_dfg);
h.validate().unwrap();
// ALAN this part really belongs in views::test or somewhere
let (region, node_map) = h.order_graph(h.entrypoint());
let input = node_map.to_portgraph(input);
assert!(
region
.neighbors(input)
.contains(&node_map.to_portgraph(sub_dfg))
);
}

#[test]
Expand Down
80 changes: 71 additions & 9 deletions hugr-core/src/hugr/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

mod impls;
mod nodes_iter;
pub mod petgraph;
mod petgraph;
pub mod petgraph2;
pub mod render;
mod rerooted;
mod root_checked;
Expand All @@ -24,23 +25,22 @@ pub use rerooted::Rerooted;
pub use root_checked::{InvalidSignature, RootChecked, check_tag};
pub use sibling_subgraph::SiblingSubgraph;

use itertools::Itertools;
use itertools::{Either, Itertools};
use portgraph::render::{DotFormat, MermaidFormat};
use portgraph::{LinkView, PortView};

use super::internal::{HugrInternals, HugrMutInternals};
use super::validate::ValidationContext;
use super::{Hugr, HugrMut, Node, ValidationError};
use crate::core::HugrNode;
use crate::extension::ExtensionRegistry;
use crate::hugr::internal::PortgraphNodeMap;
use crate::hugr::views::petgraph2::SynEdgeWrapper;
use crate::metadata::{Metadata, RawMetadataValue};
use crate::ops::handle::NodeHandle;
use crate::ops::{OpParent, OpTag, OpTrait, OpType};

use crate::ops::{OpParent, OpTag, OpTrait, OpType, handle::NodeHandle};
use crate::types::{EdgeKind, PolyFuncType, Signature, Type};
use crate::{Direction, IncomingPort, OutgoingPort, Port};

use itertools::Either;
use super::internal::{HugrInternals, HugrMutInternals};
use super::validate::ValidationContext;
use super::{Hugr, HugrMut, Node, ValidationError};

/// A trait for inspecting HUGRs.
/// For end users we intend this to be superseded by region-specific APIs.
Expand Down Expand Up @@ -403,6 +403,68 @@ pub trait HugrView: HugrInternals {
PetgraphWrapper { hugr: self }
}

/// A view of a flat region, including ordering constraints from nonlocal edges,
/// suitable for use with petgraph algorithms.
fn order_graph(
&self,
parent: Self::Node,
) -> (
SynEdgeWrapper<portgraph::view::FlatRegion<'_, Self::RegionPortgraph<'_>>>,
Self::RegionPortgraphNodes,
) {
#[expect(deprecated)] // inline region_portgraph here
let (region_view, region_nodes) = self.region_portgraph(parent);
let mut syn_edges = Vec::new();
if OpTag::DataflowParent.is_superset(self.get_optype(parent).tag()) {
let mut cache: HashMap<Self::Node, Self::Node> = HashMap::new();
fn find_sib_anc<N: HugrNode>(
n: N,
hugr: &(impl HugrView<Node = N> + ?Sized),
cache: &mut HashMap<N, N>,
parent: N,
) -> Option<N> {
// If we don't hit parent, it's a Dom edge, so ignore.
let p = hugr.get_parent(n)?;
if p == parent {
return Some(n);
}
match cache.get(&p) {
Some(&cached) => Some(cached),
None => {
// can't be borrowing cache during recursive call
let anc = find_sib_anc(p, hugr, cache, parent);
if let Some(anc) = anc {
cache.insert(p, anc);
}
anc
}
}
}
for child in self.children(parent) {
for (p, _) in self.out_value_types(child) {
for (tgt, _) in self.linked_inputs(child, p) {
if let Some(tgt_anc) = find_sib_anc(tgt, self, &mut cache, parent)
&& tgt_anc != tgt
{
syn_edges.push((
region_nodes.to_portgraph(child),
region_nodes.to_portgraph(tgt_anc),
));
}
}
}
}
}

(
SynEdgeWrapper {
region_view,
syn_edges,
},
region_nodes,
)
}

/// Return the mermaid representation of the underlying hierarchical graph.
///
/// The hierarchy is represented using subgraphs. Edges are labelled with
Expand Down
1 change: 1 addition & 0 deletions hugr-core/src/hugr/views/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ macro_rules! hugr_internal_methods {
($arg:ident, $e:expr) => {
delegate::delegate! {
to ({let $arg=self; $e}) {
#[expect(deprecated)] // Remove delegate along with region_portgraph
fn region_portgraph(&self, parent: Self::Node) -> (portgraph::view::FlatRegion<'_, Self::RegionPortgraph<'_>>, Self::RegionPortgraphNodes);
fn node_metadata_map(&self, node: Self::Node) -> &crate::hugr::NodeMetadataMap;
}
Expand Down
Loading
Loading