hugr_core/hugr/
internal.rs

1//! Internal traits, not exposed in the public `hugr` API.
2
3use std::ops::Range;
4use std::sync::OnceLock;
5
6use itertools::Itertools;
7use portgraph::{LinkMut, LinkView, MultiPortGraph, PortMut, PortOffset, PortView};
8
9use crate::core::HugrNode;
10use crate::extension::ExtensionRegistry;
11use crate::{Direction, Hugr, Node};
12
13use super::HugrView;
14use super::views::{panic_invalid_node, panic_invalid_non_entrypoint};
15use super::{NodeMetadataMap, OpType};
16use crate::ops::handle::NodeHandle;
17
18/// Trait for accessing the internals of a Hugr(View).
19///
20/// Specifically, this trait provides access to the underlying portgraph
21/// view.
22pub trait HugrInternals {
23    /// The portgraph graph structure returned by [`HugrInternals::region_portgraph`].
24    type RegionPortgraph<'p>: LinkView<LinkEndpoint: Eq, PortOffsetBase = u32> + Clone + 'p
25    where
26        Self: 'p;
27
28    /// The type of nodes in the Hugr.
29    type Node: Copy + Ord + std::fmt::Debug + std::fmt::Display + std::hash::Hash;
30
31    /// A mapping between HUGR nodes and portgraph nodes in the graph returned by
32    /// [`HugrInternals::region_portgraph`].
33    type RegionPortgraphNodes: PortgraphNodeMap<Self::Node>;
34
35    /// Returns a flat portgraph view of a region in the HUGR, and a mapping between
36    /// HUGR nodes and portgraph nodes in the graph.
37    //
38    // NOTE: Ideally here we would just return `Self::RegionPortgraph<'_>`, but
39    // when doing so we are unable to restrict the type to implement petgraph's
40    // traits over references (e.g. `&MyGraph : IntoNodeIdentifiers`, which is
41    // needed if we want to use petgraph's algorithms on the region graph).
42    // This won't be solvable until we do the big petgraph refactor -.-
43    // In the meantime, just wrap the portgraph in a `FlatRegion` as needed.
44    fn region_portgraph(
45        &self,
46        parent: Self::Node,
47    ) -> (
48        portgraph::view::FlatRegion<'_, Self::RegionPortgraph<'_>>,
49        Self::RegionPortgraphNodes,
50    );
51
52    /// Returns a metadata entry associated with a node.
53    ///
54    /// # Panics
55    ///
56    /// If the node is not in the graph.
57    fn node_metadata_map(&self, node: Self::Node) -> &NodeMetadataMap;
58}
59
60/// A map between hugr nodes and portgraph nodes in the graph returned by
61/// [`HugrInternals::region_portgraph`].
62pub trait PortgraphNodeMap<N>: Clone + Sized + std::fmt::Debug {
63    /// Returns the portgraph index of a HUGR node in the associated region
64    /// graph.
65    ///
66    /// If the node is not in the region, the result is undefined.
67    fn to_portgraph(&self, node: N) -> portgraph::NodeIndex;
68
69    /// Returns the HUGR node for a portgraph node in the associated region
70    /// graph.
71    ///
72    /// If the node is not in the region, the result is undefined.
73    #[allow(clippy::wrong_self_convention)]
74    fn from_portgraph(&self, node: portgraph::NodeIndex) -> N;
75}
76
77/// An identity map between HUGR nodes and portgraph nodes.
78#[derive(
79    Copy, Clone, Debug, Default, Eq, PartialEq, Hash, PartialOrd, Ord, derive_more::Display,
80)]
81pub struct DefaultPGNodeMap;
82
83impl PortgraphNodeMap<Node> for DefaultPGNodeMap {
84    #[inline]
85    fn to_portgraph(&self, node: Node) -> portgraph::NodeIndex {
86        node.into_portgraph()
87    }
88
89    #[inline]
90    fn from_portgraph(&self, node: portgraph::NodeIndex) -> Node {
91        node.into()
92    }
93}
94
95impl<N: HugrNode> PortgraphNodeMap<N> for std::collections::HashMap<N, Node> {
96    #[inline]
97    fn to_portgraph(&self, node: N) -> portgraph::NodeIndex {
98        self[&node].into_portgraph()
99    }
100
101    #[inline]
102    fn from_portgraph(&self, node: portgraph::NodeIndex) -> N {
103        let node = node.into();
104        self.iter()
105            .find_map(|(&k, &v)| (v == node).then_some(k))
106            .expect("Portgraph node not found in map")
107    }
108}
109
110impl HugrInternals for Hugr {
111    type RegionPortgraph<'p>
112        = &'p MultiPortGraph<u32, u32, u32>
113    where
114        Self: 'p;
115
116    type Node = Node;
117
118    type RegionPortgraphNodes = DefaultPGNodeMap;
119
120    #[inline]
121    fn region_portgraph(
122        &self,
123        parent: Self::Node,
124    ) -> (
125        portgraph::view::FlatRegion<'_, Self::RegionPortgraph<'_>>,
126        Self::RegionPortgraphNodes,
127    ) {
128        let root = parent.into_portgraph();
129        let region =
130            portgraph::view::FlatRegion::new_without_root(&self.graph, &self.hierarchy, root);
131        (region, DefaultPGNodeMap)
132    }
133
134    #[inline]
135    fn node_metadata_map(&self, node: Self::Node) -> &NodeMetadataMap {
136        static EMPTY: OnceLock<NodeMetadataMap> = OnceLock::new();
137        panic_invalid_node(self, node);
138        let map = self.metadata.get(node.into_portgraph()).as_ref();
139        map.unwrap_or(EMPTY.get_or_init(Default::default))
140    }
141}
142
143/// Trait for accessing the mutable internals of a Hugr(Mut).
144///
145/// Specifically, this trait lets you apply arbitrary modifications that may
146/// invalidate the HUGR.
147pub trait HugrMutInternals: HugrView {
148    /// Set the node at the root of the HUGR hierarchy.
149    ///
150    /// Any node not reachable from this root should be manually removed from
151    /// the HUGR.
152    ///
153    /// To set the working entrypoint of the HUGR, use
154    /// [`HugrMut::set_entrypoint`][crate::hugr::HugrMut::set_entrypoint]
155    /// instead.
156    ///
157    /// # Panics
158    ///
159    /// If the node is not in the graph.
160    fn set_module_root(&mut self, root: Self::Node);
161
162    /// Set the number of ports on a node. This may invalidate the node's `PortIndex`.
163    ///
164    /// # Panics
165    ///
166    /// If the node is not in the graph.
167    fn set_num_ports(&mut self, node: Self::Node, incoming: usize, outgoing: usize);
168
169    /// Alter the number of ports on a node and returns a range with the new
170    /// port offsets, if any. This may invalidate the node's `PortIndex`.
171    ///
172    /// The `direction` parameter specifies whether to add ports to the incoming
173    /// or outgoing list.
174    ///
175    /// Returns the range of newly created ports.
176    ///
177    /// # Panics
178    ///
179    /// If the node is not in the graph.
180    fn add_ports(&mut self, node: Self::Node, direction: Direction, amount: isize) -> Range<usize>;
181
182    /// Insert `amount` new ports for a node, starting at `index`.  The
183    /// `direction` parameter specifies whether to add ports to the incoming or
184    /// outgoing list. Links from this node are preserved, even when ports are
185    /// renumbered by the insertion.
186    ///
187    /// Returns the range of newly created ports.
188    /// # Panics
189    ///
190    /// If the node is not in the graph.
191    fn insert_ports(
192        &mut self,
193        node: Self::Node,
194        direction: Direction,
195        index: usize,
196        amount: usize,
197    ) -> Range<usize>;
198
199    /// Sets the parent of a node.
200    ///
201    /// The node becomes the parent's last child.
202    ///
203    /// # Panics
204    ///
205    /// If either the node or the parent is not in the graph.
206    fn set_parent(&mut self, node: Self::Node, parent: Self::Node);
207
208    /// Move a node in the hierarchy to be the subsequent sibling of another
209    /// node.
210    ///
211    /// The sibling node's parent becomes the new node's parent.
212    ///
213    /// The node becomes the parent's last child.
214    ///
215    /// # Panics
216    ///
217    /// If either node is not in the graph, or if it is a root.
218    fn move_after_sibling(&mut self, node: Self::Node, after: Self::Node);
219
220    /// Move a node in the hierarchy to be the prior sibling of another node.
221    ///
222    /// The sibling node's parent becomes the new node's parent.
223    ///
224    /// The node becomes the parent's last child.
225    ///
226    /// # Panics
227    ///
228    /// If either node is not in the graph, or if it is a root.
229    fn move_before_sibling(&mut self, node: Self::Node, before: Self::Node);
230
231    /// Replace the `OpType` at node and return the old `OpType`.
232    /// In general this invalidates the ports, which may need to be resized to
233    /// match the `OpType` signature.
234    ///
235    /// Returns the old `OpType`.
236    ///
237    /// If the module root is set to a non-module operation the hugr will
238    /// become invalid.
239    ///
240    /// # Panics
241    ///
242    /// If the node is not in the graph.
243    fn replace_op(&mut self, node: Self::Node, op: impl Into<OpType>) -> OpType;
244
245    /// Gets a mutable reference to the optype.
246    ///
247    /// Changing this may invalidate the ports, which may need to be resized to
248    /// match the `OpType` signature.
249    ///
250    /// Mutating the root node operation may invalidate the root tag.
251    ///
252    /// Mutating the module root into a non-module operation will invalidate the hugr.
253    ///
254    /// # Panics
255    ///
256    /// If the node is not in the graph.
257    fn optype_mut(&mut self, node: Self::Node) -> &mut OpType;
258
259    /// Returns a metadata entry associated with a node.
260    ///
261    /// # Panics
262    ///
263    /// If the node is not in the graph.
264    fn node_metadata_map_mut(&mut self, node: Self::Node) -> &mut NodeMetadataMap;
265
266    /// Returns a mutable reference to the extension registry for this HUGR.
267    ///
268    /// This set contains all extensions required to define the operations and
269    /// types in the HUGR.
270    fn extensions_mut(&mut self) -> &mut ExtensionRegistry;
271}
272
273/// Impl for non-wrapped Hugrs. Overwrites the recursive default-impls to directly use the hugr.
274impl HugrMutInternals for Hugr {
275    fn set_module_root(&mut self, root: Node) {
276        panic_invalid_node(self, root.node());
277        let root = root.into_portgraph();
278        self.hierarchy.detach(root);
279        self.module_root = root;
280    }
281
282    #[inline]
283    fn set_num_ports(&mut self, node: Node, incoming: usize, outgoing: usize) {
284        panic_invalid_node(self, node);
285        self.graph
286            .set_num_ports(node.into_portgraph(), incoming, outgoing, |_, _| {});
287    }
288
289    fn add_ports(&mut self, node: Node, direction: Direction, amount: isize) -> Range<usize> {
290        panic_invalid_node(self, node);
291        let mut incoming = self.graph.num_inputs(node.into_portgraph());
292        let mut outgoing = self.graph.num_outputs(node.into_portgraph());
293        let increment = |num: &mut usize| {
294            let new = num.saturating_add_signed(amount);
295            let range = *num..new;
296            *num = new;
297            range
298        };
299        let range = match direction {
300            Direction::Incoming => increment(&mut incoming),
301            Direction::Outgoing => increment(&mut outgoing),
302        };
303        self.graph
304            .set_num_ports(node.into_portgraph(), incoming, outgoing, |_, _| {});
305        range
306    }
307
308    fn insert_ports(
309        &mut self,
310        node: Node,
311        direction: Direction,
312        index: usize,
313        amount: usize,
314    ) -> Range<usize> {
315        panic_invalid_node(self, node);
316        let old_num_ports = self.graph.num_ports(node.into_portgraph(), direction);
317
318        self.add_ports(node, direction, amount as isize);
319
320        for swap_from_port in (index..old_num_ports).rev() {
321            let swap_to_port = swap_from_port + amount;
322            let [from_port_index, to_port_index] = [swap_from_port, swap_to_port].map(|p| {
323                self.graph
324                    .port_index(node.into_portgraph(), PortOffset::new(direction, p))
325                    .unwrap()
326            });
327            let linked_ports = self
328                .graph
329                .port_links(from_port_index)
330                .map(|(_, to_subport)| to_subport.port())
331                .collect_vec();
332            self.graph.unlink_port(from_port_index);
333            for linked_port_index in linked_ports {
334                let _ = self
335                    .graph
336                    .link_ports(to_port_index, linked_port_index)
337                    .expect("Ports exist");
338            }
339        }
340        index..index + amount
341    }
342
343    fn set_parent(&mut self, node: Node, parent: Node) {
344        panic_invalid_node(self, parent);
345        panic_invalid_node(self, node);
346        self.hierarchy.detach(node.into_portgraph());
347        self.hierarchy
348            .push_child(node.into_portgraph(), parent.into_portgraph())
349            .expect("Inserting a newly-created node into the hierarchy should never fail.");
350    }
351
352    fn move_after_sibling(&mut self, node: Node, after: Node) {
353        panic_invalid_non_entrypoint(self, node);
354        panic_invalid_non_entrypoint(self, after);
355        self.hierarchy.detach(node.into_portgraph());
356        self.hierarchy
357            .insert_after(node.into_portgraph(), after.into_portgraph())
358            .expect("Inserting a newly-created node into the hierarchy should never fail.");
359    }
360
361    fn move_before_sibling(&mut self, node: Node, before: Node) {
362        panic_invalid_non_entrypoint(self, node);
363        panic_invalid_non_entrypoint(self, before);
364        self.hierarchy.detach(node.into_portgraph());
365        self.hierarchy
366            .insert_before(node.into_portgraph(), before.into_portgraph())
367            .expect("Inserting a newly-created node into the hierarchy should never fail.");
368    }
369
370    fn replace_op(&mut self, node: Node, op: impl Into<OpType>) -> OpType {
371        panic_invalid_node(self, node);
372        std::mem::replace(self.optype_mut(node), op.into())
373    }
374
375    fn optype_mut(&mut self, node: Node) -> &mut OpType {
376        panic_invalid_node(self, node);
377        let node = node.into_portgraph();
378        self.op_types.get_mut(node)
379    }
380
381    fn node_metadata_map_mut(&mut self, node: Self::Node) -> &mut NodeMetadataMap {
382        panic_invalid_node(self, node);
383        self.metadata
384            .get_mut(node.into_portgraph())
385            .get_or_insert_with(Default::default)
386    }
387
388    fn extensions_mut(&mut self) -> &mut ExtensionRegistry {
389        &mut self.extensions
390    }
391}
392
393impl Hugr {
394    /// Consumes the HUGR and return a flat portgraph view of the region rooted
395    /// at `parent`.
396    #[inline]
397    pub fn into_region_portgraph(
398        self,
399        parent: Node,
400    ) -> portgraph::view::FlatRegion<'static, MultiPortGraph<u32, u32, u32>> {
401        let root = parent.into_portgraph();
402        let Self {
403            graph, hierarchy, ..
404        } = self;
405        portgraph::view::FlatRegion::new_without_root(graph, hierarchy, root)
406    }
407}
408
409#[cfg(test)]
410mod test {
411    use crate::{
412        Direction, HugrView as _,
413        builder::{Container, DFGBuilder, Dataflow, DataflowHugr},
414        extension::prelude::Noop,
415        hugr::internal::HugrMutInternals as _,
416        ops::handle::NodeHandle,
417        types::{Signature, Type},
418    };
419
420    #[test]
421    fn insert_ports() {
422        let (nop, mut hugr) = {
423            let mut builder = DFGBuilder::new(Signature::new_endo(Type::UNIT)).unwrap();
424            let [nop_in] = builder.input_wires_arr();
425            let nop = builder
426                .add_dataflow_op(Noop::new(Type::UNIT), [nop_in])
427                .unwrap();
428            builder.add_other_wire(nop.node(), builder.output().node());
429            let [nop_out] = nop.outputs_arr();
430            (
431                nop.node(),
432                builder.finish_hugr_with_outputs([nop_out]).unwrap(),
433            )
434        };
435        let [i, o] = hugr.get_io(hugr.entrypoint()).unwrap();
436        assert_eq!(0..2, hugr.insert_ports(nop, Direction::Incoming, 0, 2));
437        assert_eq!(1..3, hugr.insert_ports(nop, Direction::Outgoing, 1, 2));
438
439        assert_eq!(hugr.single_linked_input(i, 0), Some((nop, 2.into())));
440        assert_eq!(hugr.single_linked_output(o, 0), Some((nop, 0.into())));
441        assert_eq!(hugr.single_linked_output(o, 1), Some((nop, 3.into())));
442    }
443}