hugr_core/hugr/
internal.rs

1//! Internal traits, not exposed in the public `hugr` API.
2
3use std::borrow::Cow;
4use std::ops::Range;
5use std::rc::Rc;
6use std::sync::Arc;
7
8use delegate::delegate;
9use itertools::Itertools;
10use portgraph::{LinkMut, LinkView, MultiPortGraph, PortMut, PortOffset, PortView};
11
12use crate::ops::handle::NodeHandle;
13use crate::ops::OpTrait;
14use crate::{Direction, Hugr, Node};
15
16use super::hugrmut::{panic_invalid_node, panic_invalid_non_root};
17use super::{HugrError, OpType, RootTagged};
18
19/// Trait for accessing the internals of a Hugr(View).
20///
21/// Specifically, this trait provides access to the underlying portgraph
22/// view.
23pub trait HugrInternals {
24    /// The underlying portgraph view type.
25    type Portgraph<'p>: LinkView + Clone + 'p
26    where
27        Self: 'p;
28
29    /// Returns a reference to the underlying portgraph.
30    fn portgraph(&self) -> Self::Portgraph<'_>;
31
32    /// Returns the Hugr at the base of a chain of views.
33    fn base_hugr(&self) -> &Hugr;
34
35    /// Return the root node of this view.
36    fn root_node(&self) -> Node;
37}
38
39impl HugrInternals for Hugr {
40    type Portgraph<'p>
41        = &'p MultiPortGraph
42    where
43        Self: 'p;
44
45    #[inline]
46    fn portgraph(&self) -> Self::Portgraph<'_> {
47        &self.graph
48    }
49
50    #[inline]
51    fn base_hugr(&self) -> &Hugr {
52        self
53    }
54
55    #[inline]
56    fn root_node(&self) -> Node {
57        self.root.into()
58    }
59}
60
61impl<T: HugrInternals> HugrInternals for &T {
62    type Portgraph<'p>
63        = T::Portgraph<'p>
64    where
65        Self: 'p;
66    delegate! {
67        to (**self) {
68            fn portgraph(&self) -> Self::Portgraph<'_>;
69            fn base_hugr(&self) -> &Hugr;
70            fn root_node(&self) -> Node;
71        }
72    }
73}
74
75impl<T: HugrInternals> HugrInternals for &mut T {
76    type Portgraph<'p>
77        = T::Portgraph<'p>
78    where
79        Self: 'p;
80    delegate! {
81        to (**self) {
82            fn portgraph(&self) -> Self::Portgraph<'_>;
83            fn base_hugr(&self) -> &Hugr;
84            fn root_node(&self) -> Node;
85        }
86    }
87}
88
89impl<T: HugrInternals> HugrInternals for Rc<T> {
90    type Portgraph<'p>
91        = T::Portgraph<'p>
92    where
93        Self: 'p;
94    delegate! {
95        to (**self) {
96            fn portgraph(&self) -> Self::Portgraph<'_>;
97            fn base_hugr(&self) -> &Hugr;
98            fn root_node(&self) -> Node;
99        }
100    }
101}
102
103impl<T: HugrInternals> HugrInternals for Arc<T> {
104    type Portgraph<'p>
105        = T::Portgraph<'p>
106    where
107        Self: 'p;
108    delegate! {
109        to (**self) {
110            fn portgraph(&self) -> Self::Portgraph<'_>;
111            fn base_hugr(&self) -> &Hugr;
112            fn root_node(&self) -> Node;
113        }
114    }
115}
116
117impl<T: HugrInternals> HugrInternals for Box<T> {
118    type Portgraph<'p>
119        = T::Portgraph<'p>
120    where
121        Self: 'p;
122    delegate! {
123        to (**self) {
124            fn portgraph(&self) -> Self::Portgraph<'_>;
125            fn base_hugr(&self) -> &Hugr;
126            fn root_node(&self) -> Node;
127        }
128    }
129}
130
131impl<T: HugrInternals + ToOwned> HugrInternals for Cow<'_, T> {
132    type Portgraph<'p>
133        = T::Portgraph<'p>
134    where
135        Self: 'p;
136    delegate! {
137        to self.as_ref() {
138            fn portgraph(&self) -> Self::Portgraph<'_>;
139            fn base_hugr(&self) -> &Hugr;
140            fn root_node(&self) -> Node;
141        }
142    }
143}
144/// Trait for accessing the mutable internals of a Hugr(Mut).
145///
146/// Specifically, this trait lets you apply arbitrary modifications that may
147/// invalidate the HUGR.
148pub trait HugrMutInternals: RootTagged {
149    /// Returns the Hugr at the base of a chain of views.
150    fn hugr_mut(&mut self) -> &mut Hugr;
151
152    /// Set the number of ports on a node. This may invalidate the node's `PortIndex`.
153    ///
154    /// # Panics
155    ///
156    /// If the node is not in the graph.
157    fn set_num_ports(&mut self, node: Node, incoming: usize, outgoing: usize) {
158        panic_invalid_node(self, node);
159        self.hugr_mut().set_num_ports(node, incoming, outgoing)
160    }
161
162    /// Alter the number of ports on a node and returns a range with the new
163    /// port offsets, if any. This may invalidate the node's `PortIndex`.
164    ///
165    /// The `direction` parameter specifies whether to add ports to the incoming
166    /// or outgoing list.
167    ///
168    /// Returns the range of newly created ports.
169    ///
170    /// # Panics
171    ///
172    /// If the node is not in the graph.
173    fn add_ports(&mut self, node: Node, direction: Direction, amount: isize) -> Range<usize> {
174        panic_invalid_node(self, node);
175        self.hugr_mut().add_ports(node, direction, amount)
176    }
177
178    /// Insert `amount` new ports for a node, starting at `index`.  The
179    /// `direction` parameter specifies whether to add ports to the incoming or
180    /// outgoing list. Links from this node are preserved, even when ports are
181    /// renumbered by the insertion.
182    ///
183    /// Returns the range of newly created ports.
184    /// # Panics
185    ///
186    /// If the node is not in the graph.
187    fn insert_ports(
188        &mut self,
189        node: Node,
190        direction: Direction,
191        index: usize,
192        amount: usize,
193    ) -> Range<usize> {
194        panic_invalid_node(self, node);
195        self.hugr_mut().insert_ports(node, direction, index, amount)
196    }
197
198    /// Sets the parent of a node.
199    ///
200    /// The node becomes the parent's last child.
201    ///
202    /// # Panics
203    ///
204    /// If either the node or the parent is not in the graph.
205    fn set_parent(&mut self, node: Node, parent: Node) {
206        panic_invalid_node(self, parent);
207        panic_invalid_non_root(self, node);
208        self.hugr_mut().set_parent(node, parent);
209    }
210
211    /// Move a node in the hierarchy to be the subsequent sibling of another
212    /// node.
213    ///
214    /// The sibling node's parent becomes the new node's parent.
215    ///
216    /// The node becomes the parent's last child.
217    ///
218    /// # Panics
219    ///
220    /// If either node is not in the graph, or if it is a root.
221    fn move_after_sibling(&mut self, node: Node, after: Node) {
222        panic_invalid_non_root(self, node);
223        panic_invalid_non_root(self, after);
224        self.hugr_mut().move_after_sibling(node, after);
225    }
226
227    /// Move a node in the hierarchy to be the prior sibling of another node.
228    ///
229    /// The sibling node's parent becomes the new node's parent.
230    ///
231    /// The node becomes the parent's last child.
232    ///
233    /// # Panics
234    ///
235    /// If either node is not in the graph, or if it is a root.
236    fn move_before_sibling(&mut self, node: Node, before: Node) {
237        panic_invalid_non_root(self, node);
238        panic_invalid_non_root(self, before);
239        self.hugr_mut().move_before_sibling(node, before)
240    }
241
242    /// Replace the OpType at node and return the old OpType.
243    /// In general this invalidates the ports, which may need to be resized to
244    /// match the OpType signature.
245    ///
246    /// Returns the old OpType.
247    ///
248    /// TODO: Add a version which ignores input extensions
249    ///
250    /// # Errors
251    ///
252    /// Returns a [`HugrError::InvalidTag`] if this would break the bound
253    /// (`Self::RootHandle`) on the root node's OpTag.
254    ///
255    /// # Panics
256    ///
257    /// If the node is not in the graph.
258    fn replace_op(&mut self, node: Node, op: impl Into<OpType>) -> Result<OpType, HugrError> {
259        panic_invalid_node(self, node);
260        let op = op.into();
261        if node == self.root() && !Self::RootHandle::TAG.is_superset(op.tag()) {
262            return Err(HugrError::InvalidTag {
263                required: Self::RootHandle::TAG,
264                actual: op.tag(),
265            });
266        }
267        self.hugr_mut().replace_op(node, op)
268    }
269}
270
271/// Impl for non-wrapped Hugrs. Overwrites the recursive default-impls to directly use the hugr.
272impl<T: RootTagged<RootHandle = Node> + AsMut<Hugr>> HugrMutInternals for T {
273    fn hugr_mut(&mut self) -> &mut Hugr {
274        self.as_mut()
275    }
276
277    #[inline]
278    fn set_num_ports(&mut self, node: Node, incoming: usize, outgoing: usize) {
279        self.hugr_mut()
280            .graph
281            .set_num_ports(node.pg_index(), incoming, outgoing, |_, _| {})
282    }
283
284    fn insert_ports(
285        &mut self,
286        node: Node,
287        direction: Direction,
288        index: usize,
289        amount: usize,
290    ) -> Range<usize> {
291        let old_num_ports = self.base_hugr().graph.num_ports(node.pg_index(), direction);
292
293        self.add_ports(node, direction, amount as isize);
294
295        for swap_from_port in (index..old_num_ports).rev() {
296            let swap_to_port = swap_from_port + amount;
297            let [from_port_index, to_port_index] = [swap_from_port, swap_to_port].map(|p| {
298                self.base_hugr()
299                    .graph
300                    .port_index(node.pg_index(), PortOffset::new(direction, p))
301                    .unwrap()
302            });
303            let linked_ports = self
304                .base_hugr()
305                .graph
306                .port_links(from_port_index)
307                .map(|(_, to_subport)| to_subport.port())
308                .collect_vec();
309            self.hugr_mut().graph.unlink_port(from_port_index);
310            for linked_port_index in linked_ports {
311                let _ = self
312                    .hugr_mut()
313                    .graph
314                    .link_ports(to_port_index, linked_port_index)
315                    .expect("Ports exist");
316            }
317        }
318        index..index + amount
319    }
320
321    fn add_ports(&mut self, node: Node, direction: Direction, amount: isize) -> Range<usize> {
322        let mut incoming = self.hugr_mut().graph.num_inputs(node.pg_index());
323        let mut outgoing = self.hugr_mut().graph.num_outputs(node.pg_index());
324        let increment = |num: &mut usize| {
325            let new = num.saturating_add_signed(amount);
326            let range = *num..new;
327            *num = new;
328            range
329        };
330        let range = match direction {
331            Direction::Incoming => increment(&mut incoming),
332            Direction::Outgoing => increment(&mut outgoing),
333        };
334        self.hugr_mut()
335            .graph
336            .set_num_ports(node.pg_index(), incoming, outgoing, |_, _| {});
337        range
338    }
339
340    fn set_parent(&mut self, node: Node, parent: Node) {
341        self.hugr_mut().hierarchy.detach(node.pg_index());
342        self.hugr_mut()
343            .hierarchy
344            .push_child(node.pg_index(), parent.pg_index())
345            .expect("Inserting a newly-created node into the hierarchy should never fail.");
346    }
347
348    fn move_after_sibling(&mut self, node: Node, after: Node) {
349        self.hugr_mut().hierarchy.detach(node.pg_index());
350        self.hugr_mut()
351            .hierarchy
352            .insert_after(node.pg_index(), after.pg_index())
353            .expect("Inserting a newly-created node into the hierarchy should never fail.");
354    }
355
356    fn move_before_sibling(&mut self, node: Node, before: Node) {
357        self.hugr_mut().hierarchy.detach(node.pg_index());
358        self.hugr_mut()
359            .hierarchy
360            .insert_before(node.pg_index(), before.pg_index())
361            .expect("Inserting a newly-created node into the hierarchy should never fail.");
362    }
363
364    fn replace_op(&mut self, node: Node, op: impl Into<OpType>) -> Result<OpType, HugrError> {
365        // We know RootHandle=Node here so no need to check
366        let cur = self.hugr_mut().op_types.get_mut(node.pg_index());
367        Ok(std::mem::replace(cur, op.into()))
368    }
369}
370
371#[cfg(test)]
372mod test {
373    use crate::{
374        builder::{Container, DFGBuilder, Dataflow, DataflowHugr},
375        extension::prelude::Noop,
376        hugr::internal::HugrMutInternals as _,
377        ops::handle::NodeHandle,
378        types::{Signature, Type},
379        Direction, HugrView as _,
380    };
381
382    #[test]
383    fn insert_ports() {
384        let (nop, mut hugr) = {
385            let mut builder =
386                DFGBuilder::new(Signature::new_endo(Type::UNIT).with_prelude()).unwrap();
387            let [nop_in] = builder.input_wires_arr();
388            let nop = builder
389                .add_dataflow_op(Noop::new(Type::UNIT), [nop_in])
390                .unwrap();
391            builder.add_other_wire(nop.node(), builder.output().node());
392            let [nop_out] = nop.outputs_arr();
393            (
394                nop.node(),
395                builder.finish_hugr_with_outputs([nop_out]).unwrap(),
396            )
397        };
398        let [i, o] = hugr.get_io(hugr.root()).unwrap();
399        assert_eq!(0..2, hugr.insert_ports(nop, Direction::Incoming, 0, 2));
400        assert_eq!(1..3, hugr.insert_ports(nop, Direction::Outgoing, 1, 2));
401
402        assert_eq!(hugr.single_linked_input(i, 0), Some((nop, 2.into())));
403        assert_eq!(hugr.single_linked_output(o, 0), Some((nop, 0.into())));
404        assert_eq!(hugr.single_linked_output(o, 1), Some((nop, 3.into())));
405    }
406}