Skip to content
Merged
Show file tree
Hide file tree
Changes from 33 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e010488
Add (local to views) mod syn_edge with SynEdgeWrapper
acl-cqc Mar 14, 2026
69eea12
Remove commented-out GetAdjacencyMatrix
acl-cqc Mar 31, 2026
b9f83a5
Move SiblingSubgraph tests into new file
acl-cqc Mar 31, 2026
12a3fd5
add petgraph convexity checker
acl-cqc Mar 31, 2026
3fcbbdc
Define scheduling_graph in HugrView
acl-cqc Mar 14, 2026
4d1d0c8
Add small test that implements expected traits
acl-cqc Mar 31, 2026
b813360
SchedulingGraph: rename node(s=>_map), add fn graph() -> impl... for …
acl-cqc Mar 31, 2026
87f0c36
Deprecate region_portgraph, switch all uses except sibling_subgraph (…
acl-cqc Mar 31, 2026
d532d3c
Fix TopoConvexChecker `where`, add test that we can use on Scheduling…
acl-cqc Mar 31, 2026
514e23e
WIP add SchedGraphChecker and try to impl HugrConvexChecker for it
acl-cqc Mar 31, 2026
d767be2
WIP2 any closer?
acl-cqc Mar 31, 2026
fbc676e
Reimplement petgraph traits conditioning on T: LinkView, except Visit…
acl-cqc Apr 8, 2026
04178e4
More tests of what compiles
acl-cqc Apr 1, 2026
3818cfe
fix sibling_subgraph??
acl-cqc Apr 8, 2026
9cf4da8
and generalize TopoConvexChecker
acl-cqc Apr 8, 2026
242193e
Deprecate stuff in sibling_subgraph, move over...some tricky region_p…
acl-cqc Apr 9, 2026
ff2e0cc
update tests to avoid deprecation
acl-cqc Apr 9, 2026
ae1bf67
Revert "More tests of what compiles"
acl-cqc Apr 9, 2026
521319f
Rename SchedulingGraph::graph -> ::petgraph
acl-cqc Apr 9, 2026
aadc6b2
better-backdoors
acl-cqc Apr 9, 2026
4137aff
syn_edge.rs module comment + remove compilation tests
acl-cqc Apr 9, 2026
7525ac4
sib_sub: ConvexChecker deprecation should name PortgraphCheckerWithNodes
acl-cqc Apr 9, 2026
e1b6fca
clippy hugr-persistent
acl-cqc Apr 9, 2026
db89b21
doc fix and doc workaround
acl-cqc Apr 9, 2026
956f087
better backdoor
acl-cqc Apr 9, 2026
9ad9d74
Remove node_map, expose node_to_pg / pg_to_node
acl-cqc Apr 27, 2026
20d2d95
refactor: inline make_boundary (only called once)
acl-cqc Apr 27, 2026
d14bf1b
test
acl-cqc Apr 27, 2026
706ea1c
views/tests.rs: add syn-edge test
acl-cqc Apr 28, 2026
f7150f1
fmt
acl-cqc Apr 28, 2026
29ed2fb
Update as_petgraph deprecation note
acl-cqc Apr 28, 2026
3d42940
also deprecate into_region_portgraph
acl-cqc Apr 29, 2026
52a9dd9
fmt
acl-cqc Apr 29, 2026
a210440
correct node_count -> capacity
acl-cqc May 5, 2026
16bbe5f
allow not expect deprecated works w/out mod hidden
acl-cqc May 5, 2026
ea9f210
Merge remote-tracking branch 'origin/main' into acl/syn_edge_prep
acl-cqc May 5, 2026
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
2 changes: 2 additions & 0 deletions hugr-core/src/hugr/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,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 scheduling_graph instead", since = "0.27.0")]
fn region_portgraph(
&self,
parent: Self::Node,
Expand Down Expand Up @@ -396,6 +397,7 @@ impl Hugr {
/// Consumes the HUGR and return a flat portgraph view of the region rooted
/// at `parent`.
#[inline]
#[deprecated(note = "Use scheduling_graph instead", since = "0.27.0")]
pub fn into_region_portgraph(
self,
parent: Node,
Expand Down
14 changes: 7 additions & 7 deletions hugr-core/src/hugr/validate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,10 +128,10 @@ 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 sg = self.hugr.scheduling_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)
let doms = dominators::simple_fast(sg.petgraph(), sg.node_to_pg(entry_node));
(doms, sg.into_node_map())
}

/// Check the constraints on a single node.
Expand Down Expand Up @@ -422,11 +422,11 @@ impl<'a, H: HugrView> ValidationContext<'a, H> {
return Ok(());
}

let (region, node_map) = self.hugr.region_portgraph(parent);
let postorder = Topo::new(&region);
let sg = self.hugr.scheduling_graph(parent);
let postorder = Topo::new(sg.petgraph());
let nodes_visited = postorder
.iter(&region)
.filter(|n| *n != node_map.to_portgraph(parent))
.iter(sg.petgraph())
.filter(|n| *n != sg.node_to_pg(parent))
.count();
let node_count = self.hugr.children(parent).count();
if nodes_visited != node_count {
Expand Down
103 changes: 91 additions & 12 deletions hugr-core/src/hugr/views.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ pub mod render;
mod rerooted;
mod root_checked;
pub mod sibling_subgraph;
mod syn_edge;

#[cfg(test)]
mod tests;

use ::petgraph::visit as pv;
use serde::de::Deserialize;
use std::borrow::Cow;
use std::collections::HashMap;
Expand All @@ -24,23 +26,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::syn_edge::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 @@ -391,10 +392,7 @@ pub trait HugrView: HugrInternals {

/// Return a wrapper over the view that can be used in petgraph algorithms.
#[inline]
#[deprecated(
since = "0.26.0",
note = "Use hugr_core::internal::HugrInternals::region_portgraph instead."
)]
#[deprecated(since = "0.26.0", note = "Use HugrView::scheduling_graph instead.")]
#[expect(deprecated)] // Remove at same time as PetgraphWrapper
fn as_petgraph(&self) -> PetgraphWrapper<'_, Self>
where
Expand All @@ -403,6 +401,22 @@ 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 scheduling_graph(&self, parent: Self::Node) -> SchedulingGraph<'_, Self> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Name is very much up for grabs, I wasn't too keen on region_petgraph (it also has to return a nodemap, and doesn't really say what it's about - edges not in the hugr).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I like scheduling_graph, it conveys well the causal order meaning.

#[expect(deprecated)] // Inline region_portgraph here when removing
let (region_view, region_nodes) = self.region_portgraph(parent);
let graph = SynEdgeWrapper {
region_view,
syn_edges: Vec::new(),
};
SchedulingGraph {
graph,
node_map: region_nodes,
region_parent: parent,
}
}

/// Return the mermaid representation of the underlying hierarchical graph.
///
/// The hierarchy is represented using subgraphs. Edges are labelled with
Expand Down Expand Up @@ -552,6 +566,71 @@ impl<S: HugrNode> ExtractionResult<S> for HashMap<S, Node> {
}
}

/// A graph of a flat region of a Hugr, including ordering constraints from nonlocal edges
pub struct SchedulingGraph<'a, V: HugrView + ?Sized + 'a> {
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So we could have fn scheduling_graph return a pair of the nodemap and some kind of graph, but I wanted to hide the type of graph below (!!), and returning a pair of (nodemap, impl pv::GraphBase+.....) didn't seem great - for one thing, it'd make it impossible for anyone to store that graph in a struct

graph: SynEdgeWrapper<portgraph::view::FlatRegion<'a, V::RegionPortgraph<'a>>>,
node_map: V::RegionPortgraphNodes,
region_parent: V::Node,
}

impl<'a, V: HugrView + 'a> SchedulingGraph<'a, V> {
/// Get the parent node of the region represented by this scheduling graph
pub fn region_parent(&self) -> V::Node {
self.region_parent
}

/// Converts a `V::Node` index in the original Hugr into
/// an index in [Self::petgraph]
///
/// # Panics
///
/// If `n` is not a child of [Self::region_parent]
pub fn node_to_pg(&self, n: V::Node) -> portgraph::NodeIndex {
self.node_map.to_portgraph(n)
}

/// Converts the index of a node in [Self::petgraph] to the corresponding
/// `V::Node` of the original Hugr.
///
/// # Panics
///
/// If `n` is not a node in `Self::petgraph`
pub fn pg_to_node(&self, n: portgraph::NodeIndex) -> V::Node {
self.node_map.from_portgraph(n)
}

/// Extracts the map between `V::Node` and the [NodeIndex] used in [Self::petgraph],
/// discarding the rest of `self`.
///
/// [NodeIndex]: portgraph::NodeIndex
pub fn into_node_map(self) -> V::RegionPortgraphNodes {
self.node_map
}

fn portgraph_no_syn_edges(
self,
) -> (
portgraph::view::FlatRegion<'a, V::RegionPortgraph<'a>>,
V::RegionPortgraphNodes,
) {
// This may need to change when the SynEdgeWrapper actually has edges in it...
// or maybe we should keep the assert to prevent this being used any time it does.
assert!(self.graph.syn_edges.is_empty());
(self.graph.region_view, self.node_map)
}

/// Access to the graph, sufficient to allow [pv::Topo]
pub fn petgraph(
&self,
) -> impl pv::NodeCount
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

this type is part of the hiding, and quite deliberate. It could be much more detailed (NodeId/EdgeId types etc.)

+ pv::IntoNodeIdentifiers
+ pv::IntoEdgeReferences
+ pv::IntoNeighborsDirected
+ pv::Visitable<NodeId = portgraph::NodeIndex> {
&self.graph
}
}

impl HugrView for Hugr {
#[inline]
fn entrypoint(&self) -> Self::Node {
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