1
1
//! The Hugr data structure, and its basic component handles.
2
2
3
- mod hugrmut;
3
+ pub mod hugrmut;
4
4
5
5
pub mod rewrite;
6
6
pub mod serialize;
@@ -10,14 +10,14 @@ pub mod views;
10
10
use std:: collections:: { HashMap , VecDeque } ;
11
11
use std:: iter;
12
12
13
- pub ( crate ) use self :: hugrmut:: HugrInternalsMut ;
13
+ pub ( crate ) use self :: hugrmut:: HugrMut ;
14
14
pub use self :: validate:: ValidationError ;
15
15
16
16
use derive_more:: From ;
17
17
pub use rewrite:: { Rewrite , SimpleReplacement , SimpleReplacementError } ;
18
18
19
19
use portgraph:: multiportgraph:: MultiPortGraph ;
20
- use portgraph:: { Hierarchy , PortMut , UnmanagedDenseMap } ;
20
+ use portgraph:: { Hierarchy , NodeIndex , PortMut , UnmanagedDenseMap } ;
21
21
use thiserror:: Error ;
22
22
23
23
#[ cfg( feature = "pyo3" ) ]
@@ -251,12 +251,31 @@ impl Hugr {
251
251
}
252
252
}
253
253
254
- /// Produce a canonical ordering of the nodes.
254
+ /// Add a node to the graph, with the default conversion from OpType to NodeType
255
+ pub ( crate ) fn add_op ( & mut self , op : impl Into < OpType > ) -> Node {
256
+ // TODO: Default to `NodeType::open_extensions` once we can infer extensions
257
+ self . add_node ( NodeType :: pure ( op) )
258
+ }
259
+
260
+ /// Add a node to the graph.
261
+ pub ( crate ) fn add_node ( & mut self , nodetype : NodeType ) -> Node {
262
+ let node = self
263
+ . graph
264
+ . add_node ( nodetype. input_count ( ) , nodetype. output_count ( ) ) ;
265
+ self . op_types [ node] = nodetype;
266
+ node. into ( )
267
+ }
268
+
269
+ /// Produce a canonical ordering of the descendant nodes of a root,
270
+ /// following the graph hierarchy.
271
+ ///
272
+ /// This starts with the root, and then proceeds in BFS order through the
273
+ /// contained regions.
255
274
///
256
275
/// Used by [`HugrMut::canonicalize_nodes`] and the serialization code.
257
- fn canonical_order ( & self ) -> impl Iterator < Item = Node > + ' _ {
276
+ fn canonical_order ( & self , root : Node ) -> impl Iterator < Item = Node > + ' _ {
258
277
// Generate a BFS-ordered list of nodes based on the hierarchy
259
- let mut queue = VecDeque :: from ( [ self . root . into ( ) ] ) ;
278
+ let mut queue = VecDeque :: from ( [ root] ) ;
260
279
iter:: from_fn ( move || {
261
280
let node = queue. pop_front ( ) ?;
262
281
for child in self . children ( node) {
@@ -265,6 +284,46 @@ impl Hugr {
265
284
Some ( node)
266
285
} )
267
286
}
287
+
288
+ /// Compact the nodes indices of the hugr to be contiguous, and order them as a breadth-first
289
+ /// traversal of the hierarchy.
290
+ ///
291
+ /// The rekey function is called for each moved node with the old and new indices.
292
+ ///
293
+ /// After this operation, a serialization and deserialization of the Hugr is guaranteed to
294
+ /// preserve the indices.
295
+ pub fn canonicalize_nodes ( & mut self , mut rekey : impl FnMut ( Node , Node ) ) {
296
+ // Generate the ordered list of nodes
297
+ let mut ordered = Vec :: with_capacity ( self . node_count ( ) ) ;
298
+ let root = self . root ( ) ;
299
+ ordered. extend ( self . as_mut ( ) . canonical_order ( root) ) ;
300
+
301
+ // Permute the nodes in the graph to match the order.
302
+ //
303
+ // Invariant: All the elements before `position` are in the correct place.
304
+ for position in 0 ..ordered. len ( ) {
305
+ // Find the element's location. If it originally came from a previous position
306
+ // then it has been swapped somewhere else, so we follow the permutation chain.
307
+ let mut source: Node = ordered[ position] ;
308
+ while position > source. index . index ( ) {
309
+ source = ordered[ source. index . index ( ) ] ;
310
+ }
311
+
312
+ let target: Node = NodeIndex :: new ( position) . into ( ) ;
313
+ if target != source {
314
+ self . graph . swap_nodes ( target. index , source. index ) ;
315
+ self . op_types . swap ( target. index , source. index ) ;
316
+ self . hierarchy . swap_nodes ( target. index , source. index ) ;
317
+ rekey ( source, target) ;
318
+ }
319
+ }
320
+ self . root = NodeIndex :: new ( 0 ) ;
321
+
322
+ // Finish by compacting the copy nodes.
323
+ // The operation nodes will be left in place.
324
+ // This step is not strictly necessary.
325
+ self . graph . compact_nodes ( |_, _| { } ) ;
326
+ }
268
327
}
269
328
270
329
impl Port {
@@ -367,6 +426,9 @@ pub enum HugrError {
367
426
/// An error occurred while manipulating the hierarchy.
368
427
#[ error( "An error occurred while manipulating the hierarchy." ) ]
369
428
HierarchyError ( #[ from] portgraph:: hierarchy:: AttachError ) ,
429
+ /// The node doesn't exist.
430
+ #[ error( "Invalid node {0:?}." ) ]
431
+ InvalidNode ( Node ) ,
370
432
}
371
433
372
434
#[ cfg( feature = "pyo3" ) ]
0 commit comments