hugr_core/hugr/
hugrmut.rs

1//! Low-level interface for modifying a HUGR.
2
3use std::collections::{BTreeMap, HashMap, VecDeque};
4use std::sync::Arc;
5
6use portgraph::{LinkMut, PortMut, PortView, SecondaryMap};
7
8use crate::core::HugrNode;
9use crate::extension::ExtensionRegistry;
10use crate::hugr::views::SiblingSubgraph;
11use crate::hugr::{HugrView, Node, OpType};
12use crate::hugr::{NodeMetadata, Patch};
13use crate::ops::OpTrait;
14use crate::types::Substitution;
15use crate::{Extension, Hugr, IncomingPort, OutgoingPort, Port, PortIndex};
16
17use super::internal::HugrMutInternals;
18use super::views::{
19    Rerooted, panic_invalid_node, panic_invalid_non_entrypoint, panic_invalid_port,
20};
21
22/// Functions for low-level building of a HUGR.
23pub trait HugrMut: HugrMutInternals {
24    /// Set entrypoint to the HUGR.
25    ///
26    /// This node represents the execution entrypoint of the HUGR. When running
27    /// local graph analysis or optimizations, the region defined under this
28    /// node will be used as the starting point.
29    ///
30    /// For the hugr to remain valid, the entrypoint must be a region-container
31    /// node, i.e. a node that can have children in the hierarchy.
32    ///
33    /// To get a borrowed view of the HUGR with a different entrypoint, use
34    /// [`HugrView::with_entrypoint`] or [`HugrMut::with_entrypoint_mut`] instead.
35    ///
36    /// # Panics
37    ///
38    /// If the node is not in the graph.
39    fn set_entrypoint(&mut self, root: Self::Node);
40
41    /// Returns a mutable view of the HUGR with a different entrypoint.
42    ///
43    /// Changes to the returned HUGR affect the original one, and overwriting
44    /// the entrypoint sets it both in the wrapper and the wrapped HUGR.
45    ///
46    /// For a non-mut view, use [`HugrView::with_entrypoint`] instead.
47    ///
48    /// # Panics
49    ///
50    /// Panics if the entrypoint node is not valid in the HUGR.
51    fn with_entrypoint_mut(&mut self, entrypoint: Self::Node) -> Rerooted<&mut Self>
52    where
53        Self: Sized,
54    {
55        Rerooted::new(self, entrypoint)
56    }
57
58    /// Returns a metadata entry associated with a node.
59    ///
60    /// # Panics
61    ///
62    /// If the node is not in the graph.
63    fn get_metadata_mut(&mut self, node: Self::Node, key: impl AsRef<str>) -> &mut NodeMetadata;
64
65    /// Sets a metadata value associated with a node.
66    ///
67    /// # Panics
68    ///
69    /// If the node is not in the graph.
70    fn set_metadata(
71        &mut self,
72        node: Self::Node,
73        key: impl AsRef<str>,
74        metadata: impl Into<NodeMetadata>,
75    );
76
77    /// Remove a metadata entry associated with a node.
78    ///
79    /// # Panics
80    ///
81    /// If the node is not in the graph.
82    fn remove_metadata(&mut self, node: Self::Node, key: impl AsRef<str>);
83
84    /// Add a node to the graph with a parent in the hierarchy.
85    ///
86    /// The node becomes the parent's last child.
87    ///
88    /// # Panics
89    ///
90    /// If the parent is not in the graph.
91    fn add_node_with_parent(&mut self, parent: Self::Node, op: impl Into<OpType>) -> Self::Node;
92
93    /// Add a node to the graph as the previous sibling of another node.
94    ///
95    /// The sibling node's parent becomes the new node's parent.
96    ///
97    /// # Panics
98    ///
99    /// If the sibling is not in the graph, or if the sibling is the root node.
100    fn add_node_before(&mut self, sibling: Self::Node, nodetype: impl Into<OpType>) -> Self::Node;
101
102    /// Add a node to the graph as the next sibling of another node.
103    ///
104    /// The sibling node's parent becomes the new node's parent.
105    ///
106    /// # Panics
107    ///
108    /// If the sibling is not in the graph, or if the sibling is the root node.
109    fn add_node_after(&mut self, sibling: Self::Node, op: impl Into<OpType>) -> Self::Node;
110
111    /// Remove a node from the graph and return the node weight.
112    /// Note that if the node has children, they are not removed; this leaves
113    /// the Hugr in an invalid state. See [`Self::remove_subtree`].
114    ///
115    /// # Panics
116    ///
117    /// If the node is not in the graph, or if the node is the root node.
118    fn remove_node(&mut self, node: Self::Node) -> OpType;
119
120    /// Remove a node from the graph, along with all its descendants in the hierarchy.
121    ///
122    /// # Panics
123    ///
124    /// If the node is not in the graph, or is the root (this would leave an empty Hugr).
125    fn remove_subtree(&mut self, node: Self::Node);
126
127    /// Copies the strict descendants of `root` to under the `new_parent`, optionally applying a
128    /// [Substitution] to the [`OpType`]s of the copied nodes.
129    ///
130    /// That is, the immediate children of root, are copied to make children of `new_parent`.
131    ///
132    /// Note this may invalidate the Hugr in two ways:
133    /// * Adding children of `root` may make the children-list of `new_parent` invalid e.g.
134    ///   leading to multiple [Input](OpType::Input), [Output](OpType::Output) or
135    ///   [`ExitBlock`](OpType::ExitBlock) nodes or Input/Output in the wrong positions
136    /// * Nonlocal edges incoming to the subtree of `root` will be copied to target the subtree under `new_parent`
137    ///   which may be invalid if `new_parent` is not a child of `root`s parent (for `Ext` edges - or
138    ///   correspondingly for `Dom` edges)
139    fn copy_descendants(
140        &mut self,
141        root: Self::Node,
142        new_parent: Self::Node,
143        subst: Option<Substitution>,
144    ) -> BTreeMap<Self::Node, Self::Node>;
145
146    /// Connect two nodes at the given ports.
147    ///
148    /// # Panics
149    ///
150    /// If either node is not in the graph or if the ports are invalid.
151    fn connect(
152        &mut self,
153        src: Self::Node,
154        src_port: impl Into<OutgoingPort>,
155        dst: Self::Node,
156        dst_port: impl Into<IncomingPort>,
157    );
158
159    /// Disconnects all edges from the given port.
160    ///
161    /// The port is left in place.
162    ///
163    /// # Panics
164    ///
165    /// If the node is not in the graph, or if the port is invalid.
166    fn disconnect(&mut self, node: Self::Node, port: impl Into<Port>);
167
168    /// Adds a non-dataflow edge between two nodes. The kind is given by the
169    /// operation's [`OpTrait::other_input`] or [`OpTrait::other_output`].
170    ///
171    /// Returns the offsets of the new input and output ports.
172    ///
173    /// [`OpTrait::other_input`]: crate::ops::OpTrait::other_input
174    /// [`OpTrait::other_output`]: crate::ops::OpTrait::other_output
175    ///
176    /// # Panics
177    ///
178    /// If the node is not in the graph, or if the port is invalid.
179    fn add_other_edge(&mut self, src: Self::Node, dst: Self::Node) -> (OutgoingPort, IncomingPort);
180
181    /// Insert another hugr into this one, under a given parent node. Edges into the
182    /// inserted subtree (i.e. nonlocal or static) will be disconnected in `self`.
183    /// (See [Self::insert_forest] or trait [HugrLinking] for methods that can
184    /// preserve such edges by also inserting their sources.)
185    ///
186    /// # Panics
187    ///
188    /// If the root node is not in the graph.
189    ///
190    /// [HugrLinking]: super::linking::HugrLinking
191    fn insert_hugr(&mut self, root: Self::Node, other: Hugr) -> InsertionResult<Node, Self::Node> {
192        let region = other.entrypoint();
193        Self::insert_region(self, root, other, region)
194    }
195
196    /// Insert a subtree of another hugr into this one, under a given parent node.
197    /// Edges into the inserted subtree (i.e. nonlocal or static) will be disconnected
198    /// in `self`. (See [Self::insert_forest] or trait [HugrLinking] for methods that
199    /// can preserve such edges by also inserting their sources.)
200    ///
201    /// # Panics
202    ///
203    /// - If the root node is not in the graph.
204    /// - If the `region` node is not in `other`.
205    ///
206    /// [HugrLinking]: super::linking::HugrLinking
207    fn insert_region(
208        &mut self,
209        root: Self::Node,
210        other: Hugr,
211        region: Node,
212    ) -> InsertionResult<Node, Self::Node> {
213        let node_map = self
214            .insert_forest(other, [(region, root)])
215            .expect("No errors possible for single subtree")
216            .node_map;
217        InsertionResult {
218            inserted_entrypoint: node_map[&region],
219            node_map,
220        }
221    }
222
223    /// Copy the entrypoint subtree of another hugr into this one, under a given parent node.
224    /// Edges into the inserted subtree (i.e. nonlocal or static) will be disconnected
225    /// in `self`. (See [Self::insert_view_forest] or trait [HugrLinking] for methods that
226    /// can preserve such edges by also copying their sources.)
227    ///
228    /// # Panics
229    ///
230    /// If the root node is not in the graph.
231    ///
232    /// [HugrLinking]: super::linking::HugrLinking
233    fn insert_from_view<H: HugrView>(
234        &mut self,
235        root: Self::Node,
236        other: &H,
237    ) -> InsertionResult<H::Node, Self::Node> {
238        let ep = other.entrypoint();
239        let node_map = self
240            .insert_view_forest(other, other.descendants(ep), [(ep, root)])
241            .expect("No errors possible for single subtree")
242            .node_map;
243        InsertionResult {
244            inserted_entrypoint: node_map[&ep],
245            node_map,
246        }
247    }
248
249    /// Copy a subgraph from another hugr into this one, under a given parent node.
250    ///
251    /// Sibling order is not preserved.
252    ///
253    /// The return value is a map from indices in `other` to the indices of the
254    /// corresponding new nodes in `self`.
255    ///
256    /// # Panics
257    ///
258    /// If the root node is not in the graph.
259    //
260    // TODO: Try to preserve the order when possible? We cannot always ensure
261    // it, since the subgraph may have arbitrary nodes without including their
262    // parent.
263    fn insert_subgraph<H: HugrView>(
264        &mut self,
265        root: Self::Node,
266        other: &H,
267        subgraph: &SiblingSubgraph<H::Node>,
268    ) -> HashMap<H::Node, Self::Node> {
269        self.insert_view_forest(
270            other,
271            subgraph.nodes().iter().cloned(),
272            subgraph.nodes().iter().map(|n| (*n, root)),
273        )
274        .expect("SiblingSubgraph nodes are a set")
275        .node_map
276    }
277
278    /// Insert a forest of nodes from another Hugr into this one.
279    ///
280    /// `root_parents` contains pairs of
281    ///    * the root of a region in `other` to insert,
282    ///    * the node in `self` that shall be parent for that region.
283    ///
284    /// Later entries for the same region override earlier ones.
285    /// If `root_parents` is empty, nothing is inserted.
286    ///
287    /// # Errors
288    ///
289    /// [InsertForestError::SubtreeAlreadyCopied] if the regions in `root_parents` are not disjoint
290    ///
291    /// # Panics
292    ///
293    /// If any of the keys in `root_parents` are not nodes in `other`,
294    /// or any of the values not in `self`.
295    fn insert_forest(
296        &mut self,
297        other: Hugr,
298        root_parents: impl IntoIterator<Item = (Node, Self::Node)>,
299    ) -> InsertForestResult<Node, Self::Node>;
300
301    /// Copy a forest of nodes from a view into this one.
302    ///
303    ///  `nodes` enumerates all nodes in `other` to copy.
304    ///
305    /// `root_parents` contains pairs of a node in `nodes` and the parent in `self` under which
306    /// it should be to placed. Later entries (for the same node) override earlier ones.
307    /// Note that unlike [Self::insert_forest] this allows inserting most of a subtree in one
308    /// location but with subparts of that subtree placed elsewhere.
309    ///
310    /// Nodes in `nodes` which are not mentioned in `root_parents` and whose parent in `other`
311    /// is not in `nodes`, will have no parent in `self`.
312    ///
313    /// # Errors
314    ///
315    /// [InsertForestError::DuplicateNode] if any node appears in `nodes` more than once.
316    ///
317    /// # Panics
318    ///
319    /// If any of the keys in `root_parents` are not in `nodes`, or any of the values not nodes in `self`.
320    fn insert_view_forest<H: HugrView>(
321        &mut self,
322        other: &H,
323        nodes: impl Iterator<Item = H::Node> + Clone,
324        root_parents: impl IntoIterator<Item = (H::Node, Self::Node)>,
325    ) -> InsertForestResult<H::Node, Self::Node>;
326
327    /// Applies a patch to the graph.
328    fn apply_patch<R, E>(&mut self, rw: impl Patch<Self, Outcome = R, Error = E>) -> Result<R, E>
329    where
330        Self: Sized,
331    {
332        rw.apply(self)
333    }
334
335    /// Registers a new extension in the set used by the hugr, keeping the one
336    /// most recent one if the extension already exists.
337    ///
338    /// These can be queried using [`HugrView::extensions`].
339    ///
340    /// See [`ExtensionRegistry::register_updated`] for more information.
341    fn use_extension(&mut self, extension: impl Into<Arc<Extension>>);
342
343    /// Extend the set of extensions used by the hugr with the extensions in the
344    /// registry.
345    ///
346    /// For each extension, keeps the most recent version if the id already
347    /// exists.
348    ///
349    /// These can be queried using [`HugrView::extensions`].
350    ///
351    /// See [`ExtensionRegistry::register_updated`] for more information.
352    fn use_extensions<Reg>(&mut self, registry: impl IntoIterator<Item = Reg>)
353    where
354        ExtensionRegistry: Extend<Reg>;
355}
356
357/// Result of inserting a forest from a hugr of `SN` nodes, into a hugr with
358/// `TN` nodes.
359///
360/// On success, a map giving the new indices; or an error in the request.
361/// Used by [HugrMut::insert_forest] and [HugrMut::insert_view_forest].
362pub type InsertForestResult<SN, TN> = Result<InsertedForest<SN, TN>, InsertForestError<SN>>;
363
364/// An error from [HugrMut::insert_forest] or [HugrMut::insert_view_forest].
365///
366/// `SN` is the type of nodes in the source Hugr
367#[derive(Clone, Debug, derive_more::Display, derive_more::Error, PartialEq)]
368#[non_exhaustive]
369pub enum InsertForestError<SN: HugrNode = Node> {
370    /// A source node was specified twice in a call to [HugrMut::insert_view_forest]
371    #[display("Node {_0} would be copied twice")]
372    DuplicateNode(SN),
373    /// A subtree would be copied twice (i.e. it is contained in another) in a call to
374    /// [HugrMut::insert_forest]
375    #[display(
376        "Subtree rooted at {subtree} is already being copied as part of that rooted at {parent}"
377    )]
378    SubtreeAlreadyCopied {
379        /// Root of the inner subtree
380        subtree: SN,
381        /// Root of the outer subtree that also contains the inner
382        parent: SN,
383    },
384}
385
386/// Records the result of inserting a Hugr or view via [`HugrMut::insert_hugr`],
387/// [`HugrMut::insert_from_view`], or [`HugrMut::insert_region`].
388///
389/// Contains a map from the nodes in the source HUGR to the nodes in the target
390/// HUGR, using their respective `Node` types.
391pub struct InsertionResult<SourceN = Node, TargetN = Node> {
392    /// The node, after insertion, that was the root of the inserted Hugr.
393    ///
394    /// That is, the value in [`InsertionResult::node_map`] under the key that
395    /// was the the `region` passed to [`HugrMut::insert_region`] or the
396    /// [`HugrView::entrypoint`] in the other cases.
397    pub inserted_entrypoint: TargetN,
398    /// Map from nodes in the Hugr/view that was inserted, to their new
399    /// positions in the Hugr into which said was inserted.
400    pub node_map: HashMap<SourceN, TargetN>,
401}
402
403/// Records the result of inserting a Hugr or view via [`HugrMut::insert_forest`]
404/// or [`HugrMut::insert_view_forest`].
405///
406/// Contains a map from the nodes in the source HUGR that were copied, to the
407/// corresponding nodes in the target HUGR, using the respective `Node` types.
408#[derive(Clone, Debug, Default)]
409pub struct InsertedForest<SourceN = Node, TargetN = Node> {
410    /// Map from the nodes from the source Hugr/view that were inserted,
411    /// to the corresponding nodes in the Hugr into which said was inserted.
412    pub node_map: HashMap<SourceN, TargetN>,
413}
414
415/// Translate a portgraph node index map into a map from nodes in the source
416/// HUGR to nodes in the target HUGR.
417///
418/// This is as a helper in `insert_hugr` and `insert_subgraph`, where the source
419/// HUGR may be an arbitrary `HugrView` with generic node types.
420fn translate_indices<N: HugrNode>(
421    mut source_node: impl FnMut(portgraph::NodeIndex) -> N,
422    mut target_node: impl FnMut(portgraph::NodeIndex) -> Node,
423    node_map: HashMap<portgraph::NodeIndex, portgraph::NodeIndex>,
424) -> impl Iterator<Item = (N, Node)> {
425    node_map
426        .into_iter()
427        .map(move |(k, v)| (source_node(k), target_node(v)))
428}
429
430/// Impl for non-wrapped Hugrs. Overwrites the recursive default-impls to directly use the hugr.
431impl HugrMut for Hugr {
432    #[inline]
433    fn set_entrypoint(&mut self, root: Node) {
434        panic_invalid_node(self, root);
435        self.entrypoint = root.into_portgraph();
436    }
437
438    fn get_metadata_mut(&mut self, node: Self::Node, key: impl AsRef<str>) -> &mut NodeMetadata {
439        panic_invalid_node(self, node);
440        self.node_metadata_map_mut(node)
441            .entry(key.as_ref())
442            .or_insert(serde_json::Value::Null)
443    }
444
445    fn set_metadata(
446        &mut self,
447        node: Self::Node,
448        key: impl AsRef<str>,
449        metadata: impl Into<NodeMetadata>,
450    ) {
451        let entry = self.get_metadata_mut(node, key);
452        *entry = metadata.into();
453    }
454
455    fn remove_metadata(&mut self, node: Self::Node, key: impl AsRef<str>) {
456        panic_invalid_node(self, node);
457        let node_meta = self.node_metadata_map_mut(node);
458        node_meta.remove(key.as_ref());
459    }
460
461    fn add_node_with_parent(&mut self, parent: Node, node: impl Into<OpType>) -> Node {
462        let node = self.as_mut().add_node(node.into());
463        self.hierarchy
464            .push_child(node.into_portgraph(), parent.into_portgraph())
465            .expect("Inserting a newly-created node into the hierarchy should never fail.");
466        node
467    }
468
469    fn add_node_before(&mut self, sibling: Node, nodetype: impl Into<OpType>) -> Node {
470        let node = self.as_mut().add_node(nodetype.into());
471        self.hierarchy
472            .insert_before(node.into_portgraph(), sibling.into_portgraph())
473            .expect("Inserting a newly-created node into the hierarchy should never fail.");
474        node
475    }
476
477    fn add_node_after(&mut self, sibling: Node, op: impl Into<OpType>) -> Node {
478        let node = self.as_mut().add_node(op.into());
479        self.hierarchy
480            .insert_after(node.into_portgraph(), sibling.into_portgraph())
481            .expect("Inserting a newly-created node into the hierarchy should never fail.");
482        node
483    }
484
485    fn remove_node(&mut self, node: Node) -> OpType {
486        panic_invalid_non_entrypoint(self, node);
487        self.hierarchy.remove(node.into_portgraph());
488        self.graph.remove_node(node.into_portgraph());
489        self.op_types.take(node.into_portgraph())
490    }
491
492    fn remove_subtree(&mut self, node: Node) {
493        panic_invalid_non_entrypoint(self, node);
494        let mut queue = VecDeque::new();
495        queue.push_back(node);
496        while let Some(n) = queue.pop_front() {
497            queue.extend(self.children(n));
498            self.remove_node(n);
499        }
500    }
501
502    fn connect(
503        &mut self,
504        src: Node,
505        src_port: impl Into<OutgoingPort>,
506        dst: Node,
507        dst_port: impl Into<IncomingPort>,
508    ) {
509        let src_port = src_port.into();
510        let dst_port = dst_port.into();
511        panic_invalid_port(self, src, src_port);
512        panic_invalid_port(self, dst, dst_port);
513        self.graph
514            .link_nodes(
515                src.into_portgraph(),
516                src_port.index(),
517                dst.into_portgraph(),
518                dst_port.index(),
519            )
520            .expect("The ports should exist at this point.");
521    }
522
523    fn disconnect(&mut self, node: Node, port: impl Into<Port>) {
524        let port = port.into();
525        let offset = port.pg_offset();
526        panic_invalid_port(self, node, port);
527        let port = self
528            .graph
529            .port_index(node.into_portgraph(), offset)
530            .expect("The port should exist at this point.");
531        self.graph.unlink_port(port);
532    }
533
534    fn add_other_edge(&mut self, src: Node, dst: Node) -> (OutgoingPort, IncomingPort) {
535        let src_port = self
536            .get_optype(src)
537            .other_output_port()
538            .expect("Source operation has no non-dataflow outgoing edges");
539        let dst_port = self
540            .get_optype(dst)
541            .other_input_port()
542            .expect("Destination operation has no non-dataflow incoming edges");
543        self.connect(src, src_port, dst, dst_port);
544        (src_port, dst_port)
545    }
546
547    fn insert_forest(
548        &mut self,
549        mut other: Hugr,
550        root_parents: impl IntoIterator<Item = (Node, Self::Node)>,
551    ) -> InsertForestResult<Node, Self::Node> {
552        let roots: HashMap<_, _> = root_parents.into_iter().collect();
553        for &subtree in roots.keys() {
554            let mut n = subtree;
555            while let Some(parent) = other.get_parent(n) {
556                if roots.contains_key(&parent) {
557                    return Err(InsertForestError::SubtreeAlreadyCopied { subtree, parent });
558                }
559                n = parent;
560            }
561        }
562        let inserted = insert_forest_internal(
563            self,
564            &other,
565            roots.keys().flat_map(|n| other.descendants(*n)),
566            roots.iter().map(|(r, p)| (*r, *p)),
567        )
568        .expect("Trees disjoint so no repeated nodes");
569        // Merge the extension sets.
570        self.extensions.extend(other.extensions());
571        // Update the optypes and metadata, taking them from the other graph.
572        //
573        // No need to compute each node's extensions here, as we merge `other.extensions` directly.
574        for (&node, &new_node) in &inserted.node_map {
575            let node_pg = node.into_portgraph();
576            let new_node_pg = new_node.into_portgraph();
577            let optype = other.op_types.take(node_pg);
578            self.op_types.set(new_node_pg, optype);
579            let meta = other.metadata.take(node_pg);
580            self.metadata.set(new_node_pg, meta);
581        }
582        Ok(inserted)
583    }
584
585    fn insert_view_forest<H: HugrView>(
586        &mut self,
587        other: &H,
588        nodes: impl Iterator<Item = H::Node> + Clone,
589        root_parents: impl IntoIterator<Item = (H::Node, Self::Node)>,
590    ) -> InsertForestResult<H::Node, Self::Node> {
591        let inserted = insert_forest_internal(self, other, nodes, root_parents.into_iter())?;
592        // Merge the extension sets.
593        self.extensions.extend(other.extensions());
594        // Update the optypes and metadata, copying them from the other graph.
595        //
596        // No need to compute each node's extensions here, as we merge `other.extensions` directly.
597        for (&node, &new_node) in &inserted.node_map {
598            let nodetype = other.get_optype(node);
599            self.op_types
600                .set(new_node.into_portgraph(), nodetype.clone());
601            let meta = other.node_metadata_map(node);
602            if !meta.is_empty() {
603                self.metadata
604                    .set(new_node.into_portgraph(), Some(meta.clone()));
605            }
606        }
607        Ok(inserted)
608    }
609
610    fn copy_descendants(
611        &mut self,
612        root: Self::Node,
613        new_parent: Self::Node,
614        subst: Option<Substitution>,
615    ) -> BTreeMap<Self::Node, Self::Node> {
616        let mut descendants = self.hierarchy.descendants(root.into_portgraph());
617        let root2 = descendants.next();
618        debug_assert_eq!(root2, Some(root.into_portgraph()));
619        let nodes = Vec::from_iter(descendants);
620        let node_map = portgraph::view::Subgraph::with_nodes(&mut self.graph, nodes)
621            .copy_in_parent()
622            .expect("Is a MultiPortGraph");
623        let node_map =
624            translate_indices(Into::into, Into::into, node_map).collect::<BTreeMap<_, _>>();
625
626        for node in self.children(root).collect::<Vec<_>>() {
627            self.set_parent(*node_map.get(&node).unwrap(), new_parent);
628        }
629
630        // Copy the optypes, metadata, and hierarchy
631        for (&node, &new_node) in &node_map {
632            for ch in self.children(node).collect::<Vec<_>>() {
633                self.set_parent(*node_map.get(&ch).unwrap(), new_node);
634            }
635            let new_optype = match (&subst, self.get_optype(node)) {
636                (None, op) => op.clone(),
637                (Some(subst), op) => op.substitute(subst),
638            };
639            self.op_types.set(new_node.into_portgraph(), new_optype);
640            let meta = self.metadata.get(node.into_portgraph()).clone();
641            self.metadata.set(new_node.into_portgraph(), meta);
642        }
643        node_map
644    }
645
646    #[inline]
647    fn use_extension(&mut self, extension: impl Into<Arc<Extension>>) {
648        self.extensions_mut().register_updated(extension);
649    }
650
651    #[inline]
652    fn use_extensions<Reg>(&mut self, registry: impl IntoIterator<Item = Reg>)
653    where
654        ExtensionRegistry: Extend<Reg>,
655    {
656        self.extensions_mut().extend(registry);
657    }
658}
659
660/// Internal implementation of `insert_hugr`, `insert_view`, and
661/// `insert_subgraph`.
662///
663/// Inserts all the nodes in `other_nodes` into `hugr`, under the given `root` node.
664///
665/// Returns a mapping from the nodes in the inserted graph to their new indices
666/// in `hugr`.
667///
668/// This function does not update the optypes of the inserted nodes, the
669/// metadata, nor the hugr extensions, so the caller must do that.
670///
671/// # Parameters
672/// - `hugr`: The hugr to insert into.
673/// - `other`: The other graph to insert from.
674/// - `other_nodes`: The nodes in the other graph to insert.
675/// - `root_parents`: a list of pairs of (node in `other`, parent to assign in `hugr`)
676fn insert_forest_internal<H: HugrView>(
677    hugr: &mut Hugr,
678    other: &H,
679    other_nodes: impl Iterator<Item = H::Node> + Clone,
680    root_parents: impl Iterator<Item = (H::Node, Node)>,
681) -> InsertForestResult<H::Node, Node> {
682    let new_node_count_hint = other_nodes.size_hint().1.unwrap_or_default();
683
684    // Insert the nodes from the other graph into this one.
685    let mut node_map = HashMap::with_capacity(new_node_count_hint);
686    hugr.reserve(new_node_count_hint, 0);
687
688    for old in other_nodes.clone() {
689        // We use a dummy optype here. The callers take care of updating the
690        // correct optype, avoiding cloning if possible.
691        let op = OpType::default();
692        let new = hugr.add_node(op);
693        if node_map.insert(old, new).is_some() {
694            return Err(InsertForestError::DuplicateNode(old));
695        }
696
697        hugr.set_num_ports(new, other.num_inputs(old), other.num_outputs(old));
698
699        // Reconnect the edges to the new node.
700        for tgt in other.node_inputs(old) {
701            for (neigh, src) in other.linked_outputs(old, tgt) {
702                let Some(&neigh) = node_map.get(&neigh) else {
703                    continue;
704                };
705                hugr.connect(neigh, src, new, tgt);
706            }
707        }
708        for src in other.node_outputs(old) {
709            for (neigh, tgt) in other.linked_inputs(old, src) {
710                if neigh == old {
711                    continue;
712                }
713                let Some(&neigh) = node_map.get(&neigh) else {
714                    continue;
715                };
716                hugr.connect(new, src, neigh, tgt);
717            }
718        }
719    }
720    for (r, p) in root_parents {
721        hugr.set_parent(node_map[&r], p);
722    }
723    for old in other_nodes {
724        let new = node_map[&old];
725        if hugr.get_parent(new).is_none() {
726            let old_parent = other.get_parent(old).unwrap();
727            let new_parent = node_map[&old_parent];
728            hugr.set_parent(new, new_parent);
729        }
730    }
731    Ok(InsertedForest { node_map })
732}
733
734#[cfg(test)]
735pub(super) mod test {
736    use cool_asserts::assert_matches;
737    use itertools::Itertools;
738    use rstest::rstest;
739
740    use crate::builder::test::{dfg_calling_defn_decl, simple_dfg_hugr};
741
742    use crate::extension::PRELUDE;
743    use crate::extension::prelude::{Noop, usize_t};
744    use crate::hugr::ValidationError;
745    use crate::ops::handle::{FuncID, NodeHandle};
746    use crate::ops::{self, FuncDefn, Input, Output, dataflow::IOTrait};
747    use crate::types::Signature;
748
749    use super::*;
750
751    #[test]
752    fn simple_function() -> Result<(), Box<dyn std::error::Error>> {
753        let mut hugr = Hugr::default();
754        hugr.use_extension(PRELUDE.to_owned());
755
756        // Create the root module definition
757        let module: Node = hugr.entrypoint();
758
759        // Start a main function with two nat inputs.
760        let f: Node = hugr.add_node_with_parent(
761            module,
762            ops::FuncDefn::new(
763                "main",
764                Signature::new(usize_t(), vec![usize_t(), usize_t()]),
765            ),
766        );
767
768        {
769            let f_in = hugr.add_node_with_parent(f, ops::Input::new(vec![usize_t()]));
770            let f_out = hugr.add_node_with_parent(f, ops::Output::new(vec![usize_t(), usize_t()]));
771            let noop = hugr.add_node_with_parent(f, Noop(usize_t()));
772
773            hugr.connect(f_in, 0, noop, 0);
774            hugr.connect(noop, 0, f_out, 0);
775            hugr.connect(noop, 0, f_out, 1);
776        }
777
778        hugr.validate()?;
779
780        Ok(())
781    }
782
783    #[test]
784    fn metadata() {
785        let mut hugr = Hugr::default();
786
787        // Create the root module definition
788        let root: Node = hugr.entrypoint();
789
790        assert_eq!(hugr.get_metadata(root, "meta"), None);
791
792        *hugr.get_metadata_mut(root, "meta") = "test".into();
793        assert_eq!(hugr.get_metadata(root, "meta"), Some(&"test".into()));
794
795        hugr.set_metadata(root, "meta", "new");
796        assert_eq!(hugr.get_metadata(root, "meta"), Some(&"new".into()));
797
798        hugr.remove_metadata(root, "meta");
799        assert_eq!(hugr.get_metadata(root, "meta"), None);
800    }
801
802    #[test]
803    fn remove_subtree() {
804        let mut hugr = Hugr::default();
805        hugr.use_extension(PRELUDE.to_owned());
806        let root = hugr.entrypoint();
807        let [foo, bar] = ["foo", "bar"].map(|name| {
808            let fd = hugr
809                .add_node_with_parent(root, FuncDefn::new(name, Signature::new_endo(usize_t())));
810            let inp = hugr.add_node_with_parent(fd, Input::new(usize_t()));
811            let out = hugr.add_node_with_parent(fd, Output::new(usize_t()));
812            hugr.connect(inp, 0, out, 0);
813            fd
814        });
815        hugr.validate().unwrap();
816        assert_eq!(hugr.num_nodes(), 7);
817
818        hugr.remove_subtree(foo);
819        hugr.validate().unwrap();
820        assert_eq!(hugr.num_nodes(), 4);
821
822        hugr.remove_subtree(bar);
823        hugr.validate().unwrap();
824        assert_eq!(hugr.num_nodes(), 1);
825    }
826
827    pub(in crate::hugr) fn check_calls_defn_decl(h: &Hugr, call1_defn: bool, call2_decl: bool) {
828        if call1_defn && call2_decl {
829            h.validate().unwrap();
830        } else {
831            assert!(matches!(
832                h.validate(),
833                Err(ValidationError::UnconnectedPort { .. })
834            ));
835        }
836        assert_eq!(
837            h.children(h.module_root()).count(),
838            1 + (call1_defn as usize) + (call2_decl as usize)
839        );
840        let [call1, call2] = h
841            .nodes()
842            .filter(|n| h.get_optype(*n).is_call())
843            .collect_array()
844            .unwrap();
845
846        let tgt1 = h.nodes().find(|n| {
847            h.get_optype(*n)
848                .as_func_defn()
849                .is_some_and(|fd| fd.func_name() == "helper_id")
850        });
851        assert_eq!(tgt1.is_some(), call1_defn);
852        assert_eq!(h.static_source(call1), tgt1);
853
854        let tgt2 = h.nodes().find(|n| {
855            h.get_optype(*n)
856                .as_func_decl()
857                .is_some_and(|fd| fd.func_name() == "helper2")
858        });
859        assert_eq!(tgt2.is_some(), call2_decl);
860        assert_eq!(h.static_source(call2), tgt2);
861    }
862
863    #[rstest]
864    fn test_insert_forest(
865        dfg_calling_defn_decl: (Hugr, FuncID<true>, FuncID<false>),
866        #[values(false, true)] copy_defn: bool,
867        #[values(false, true)] copy_decl: bool,
868    ) {
869        let (insert, defn, decl) = dfg_calling_defn_decl;
870        let mut h = simple_dfg_hugr();
871        let roots = std::iter::once((insert.entrypoint(), h.entrypoint()))
872            .chain(copy_defn.then_some((defn.node(), h.module_root())))
873            .chain(copy_decl.then_some((decl.node(), h.module_root())));
874        h.insert_forest(insert, roots).unwrap();
875        check_calls_defn_decl(&h, copy_defn, copy_decl);
876    }
877
878    #[rstest]
879    fn test_insert_view_forest(dfg_calling_defn_decl: (Hugr, FuncID<true>, FuncID<false>)) {
880        let (insert, defn, decl) = dfg_calling_defn_decl;
881        let mut h = simple_dfg_hugr();
882
883        let mut roots = HashMap::from([
884            (insert.entrypoint(), h.entrypoint()),
885            (defn.node(), h.module_root()),
886            (decl.node(), h.module_root()),
887        ]);
888
889        // Straightforward case: three complete subtrees
890        h.insert_view_forest(
891            &insert,
892            insert
893                .entry_descendants()
894                .chain(insert.descendants(defn.node()))
895                .chain(std::iter::once(decl.node())),
896            roots.clone(),
897        )
898        .unwrap();
899        h.validate().unwrap();
900
901        // Copy the FuncDefn node but not its children
902        let mut h = simple_dfg_hugr();
903        let node_map = h
904            .insert_view_forest(
905                &insert,
906                insert.entry_descendants().chain([defn.node(), decl.node()]),
907                roots.clone(),
908            )
909            .unwrap()
910            .node_map;
911        assert_matches!(h.validate(),
912            Err(ValidationError::ContainerWithoutChildren { node, optype: _ }) => assert_eq!(node, node_map[&defn.node()]));
913
914        // Copy the FuncDefn *containing* the entrypoint but transplant the entrypoint
915        let func_containing_entry = insert.get_parent(insert.entrypoint()).unwrap();
916        assert!(matches!(
917            insert.get_optype(func_containing_entry),
918            OpType::FuncDefn(_)
919        ));
920        roots.insert(func_containing_entry, h.module_root());
921        let mut h = simple_dfg_hugr();
922        let node_map = h
923            .insert_view_forest(&insert, insert.nodes().skip(1), roots)
924            .unwrap()
925            .node_map;
926        assert!(matches!(
927            h.validate(),
928            Err(ValidationError::InterGraphEdgeError(_))
929        ));
930        for c in h.nodes().filter(|n| h.get_optype(*n).is_call()) {
931            assert!(h.static_source(c).is_some());
932        }
933        // The DFG (entrypoint) has been moved:
934        let inserted_ep = node_map[&insert.entrypoint()];
935        assert_eq!(h.get_parent(inserted_ep), Some(h.entrypoint()));
936        let new_defn = node_map[&func_containing_entry];
937        assert_eq!(h.children(new_defn).count(), 2);
938
939        let [inp, outp] = h.get_io(new_defn).unwrap();
940        assert!(matches!(h.get_optype(inp), OpType::Input(_)));
941        assert!(matches!(h.get_optype(outp), OpType::Output(_)));
942        // It seems the edge from Input is disconnected, but the edge to Output preserved
943        assert_eq!(h.all_neighbours(inp).next(), None);
944        assert_eq!(h.input_neighbours(outp).next(), Some(inserted_ep));
945    }
946
947    #[rstest]
948    fn bad_insert_forest(dfg_calling_defn_decl: (Hugr, FuncID<true>, FuncID<false>)) {
949        let backup = simple_dfg_hugr();
950        let mut h = backup.clone();
951
952        let (insert, _, _) = dfg_calling_defn_decl;
953        let ep = insert.entrypoint();
954        let epp = insert.get_parent(ep).unwrap();
955        let roots = [(epp, h.module_root()), (ep, h.entrypoint())];
956        let r = h.insert_view_forest(
957            &insert,
958            insert.descendants(epp).chain(insert.descendants(ep)),
959            roots,
960        );
961        assert_eq!(r.err(), Some(InsertForestError::DuplicateNode(ep)));
962        assert!(h.validate().is_err());
963
964        let mut h = backup.clone();
965        let r = h.insert_forest(insert, roots);
966        assert_eq!(
967            r.err(),
968            Some(InsertForestError::SubtreeAlreadyCopied {
969                subtree: ep,
970                parent: epp
971            })
972        );
973        // Here the error is detected in building `nodes` from `roots` so before any mutation
974        assert_eq!(h, backup);
975    }
976}