hugr_core/builder/
dataflow.rs

1use itertools::Itertools;
2
3use super::build_traits::{HugrBuilder, SubContainer};
4use super::handle::BuildHandle;
5use super::{BuildError, Container, Dataflow, DfgID, FuncID};
6
7use std::marker::PhantomData;
8
9use crate::hugr::internal::HugrMutInternals;
10use crate::hugr::{HugrView, ValidationError};
11use crate::ops;
12use crate::ops::{DataflowParent, Input, Output};
13use crate::{Direction, IncomingPort, OutgoingPort, Wire};
14
15use crate::types::{PolyFuncType, Signature, Type};
16
17use crate::Node;
18use crate::{hugr::HugrMut, Hugr};
19
20/// Builder for a [`ops::DFG`] node.
21#[derive(Debug, Clone, PartialEq)]
22pub struct DFGBuilder<T> {
23    pub(crate) base: T,
24    pub(crate) dfg_node: Node,
25    pub(crate) num_in_wires: usize,
26    pub(crate) num_out_wires: usize,
27}
28
29impl<T: AsMut<Hugr> + AsRef<Hugr>> DFGBuilder<T> {
30    pub(super) fn create_with_io(
31        mut base: T,
32        parent: Node,
33        signature: Signature,
34    ) -> Result<Self, BuildError> {
35        let num_in_wires = signature.input().len();
36        let num_out_wires = signature.output().len();
37        /* For a given dataflow graph with extension requirements IR -> IR + dR,
38         - The output node's extension requirements are IR + dR -> IR + dR
39           (but we expect no output wires)
40
41         - The input node's extension requirements are IR -> IR, though we
42           expect no input wires. We must avoid the case where the difference
43           in extensions is an open variable, as it would be if the requirements
44           were 0 -> IR.
45           N.B. This means that for input nodes, we can't infer the extensions
46           from the input wires as we normally expect, but have to infer the
47           output wires and make use of the equality between the two.
48        */
49        let input = ops::Input {
50            types: signature.input().clone(),
51        };
52        let output = ops::Output {
53            types: signature.output().clone(),
54        };
55        base.as_mut().add_node_with_parent(parent, input);
56        base.as_mut().add_node_with_parent(parent, output);
57
58        Ok(Self {
59            base,
60            dfg_node: parent,
61            num_in_wires,
62            num_out_wires,
63        })
64    }
65}
66
67impl DFGBuilder<Hugr> {
68    /// Begin building a new DFG-rooted HUGR given its inputs, outputs,
69    /// and extension delta.
70    ///
71    /// # Errors
72    ///
73    /// Error in adding DFG child nodes.
74    pub fn new(signature: Signature) -> Result<DFGBuilder<Hugr>, BuildError> {
75        let dfg_op = ops::DFG {
76            signature: signature.clone(),
77        };
78        let base = Hugr::new(dfg_op);
79        let root = base.root();
80        DFGBuilder::create_with_io(base, root, signature)
81    }
82}
83
84impl HugrBuilder for DFGBuilder<Hugr> {
85    fn finish_hugr(mut self) -> Result<Hugr, ValidationError> {
86        if cfg!(feature = "extension_inference") {
87            self.base.infer_extensions(false)?;
88        }
89        self.base.validate()?;
90        Ok(self.base)
91    }
92}
93
94impl<T: AsMut<Hugr> + AsRef<Hugr>> Container for DFGBuilder<T> {
95    #[inline]
96    fn container_node(&self) -> Node {
97        self.dfg_node
98    }
99
100    #[inline]
101    fn hugr_mut(&mut self) -> &mut Hugr {
102        self.base.as_mut()
103    }
104
105    #[inline]
106    fn hugr(&self) -> &Hugr {
107        self.base.as_ref()
108    }
109}
110
111impl<T: AsMut<Hugr> + AsRef<Hugr>> SubContainer for DFGBuilder<T> {
112    type ContainerHandle = BuildHandle<DfgID>;
113    #[inline]
114    fn finish_sub_container(self) -> Result<Self::ContainerHandle, BuildError> {
115        Ok((self.dfg_node, self.num_out_wires).into())
116    }
117}
118
119impl<T: AsMut<Hugr> + AsRef<Hugr>> Dataflow for DFGBuilder<T> {
120    #[inline]
121    fn num_inputs(&self) -> usize {
122        self.num_in_wires
123    }
124}
125
126/// Wrapper around [`DFGBuilder`] used to build other dataflow regions.
127// Stores option of DFGBuilder so it can be taken out without moving.
128#[derive(Debug, Clone, PartialEq)]
129pub struct DFGWrapper<B, T>(DFGBuilder<B>, PhantomData<T>);
130
131impl<B, T> DFGWrapper<B, T> {
132    pub(super) fn from_dfg_builder(db: DFGBuilder<B>) -> Self {
133        Self(db, PhantomData)
134    }
135}
136
137/// Builder for a [`ops::FuncDefn`] node
138pub type FunctionBuilder<B> = DFGWrapper<B, BuildHandle<FuncID<true>>>;
139
140impl FunctionBuilder<Hugr> {
141    /// Initialize a builder for a FuncDefn rooted HUGR
142    /// # Errors
143    ///
144    /// Error in adding DFG child nodes.
145    pub fn new(
146        name: impl Into<String>,
147        signature: impl Into<PolyFuncType>,
148    ) -> Result<Self, BuildError> {
149        let signature = signature.into();
150        let body = signature.body().clone();
151        let op = ops::FuncDefn {
152            signature,
153            name: name.into(),
154        };
155
156        let base = Hugr::new(op);
157        let root = base.root();
158
159        let db = DFGBuilder::create_with_io(base, root, body)?;
160        Ok(Self::from_dfg_builder(db))
161    }
162
163    /// Add a new input to the function being constructed.
164    ///
165    /// Returns the new wire from the input node.
166    pub fn add_input(&mut self, input_type: Type) -> Wire {
167        let [inp_node, _] = self.io();
168
169        // Update the parent's root type
170        let new_optype = self.update_fn_signature(|mut s| {
171            s.input.to_mut().push(input_type);
172            s
173        });
174
175        // Update the inner input node
176        let types = new_optype.signature.body().input.clone();
177        self.hugr_mut()
178            .replace_op(inp_node, Input { types })
179            .unwrap();
180        let mut new_port = self.hugr_mut().add_ports(inp_node, Direction::Outgoing, 1);
181        let new_port = new_port.next().unwrap();
182
183        // The last port in an input/output node is an order edge port, so we must shift any connections to it.
184        let new_value_port: OutgoingPort = (new_port - 1).into();
185        let new_order_port: OutgoingPort = new_port.into();
186        let order_edge_targets = self
187            .hugr()
188            .linked_inputs(inp_node, new_value_port)
189            .collect_vec();
190        self.hugr_mut().disconnect(inp_node, new_value_port);
191        for (tgt_node, tgt_port) in order_edge_targets {
192            self.hugr_mut()
193                .connect(inp_node, new_order_port, tgt_node, tgt_port);
194        }
195
196        // Update the builder metadata
197        self.0.num_in_wires += 1;
198
199        self.input_wires().last().unwrap()
200    }
201
202    /// Add a new output to the function being constructed.
203    pub fn add_output(&mut self, output_type: Type) {
204        let [_, out_node] = self.io();
205
206        // Update the parent's root type
207        let new_optype = self.update_fn_signature(|mut s| {
208            s.output.to_mut().push(output_type);
209            s
210        });
211
212        // Update the inner input node
213        let types = new_optype.signature.body().output.clone();
214        self.hugr_mut()
215            .replace_op(out_node, Output { types })
216            .unwrap();
217        let mut new_port = self.hugr_mut().add_ports(out_node, Direction::Incoming, 1);
218        let new_port = new_port.next().unwrap();
219
220        // The last port in an input/output node is an order edge port, so we must shift any connections to it.
221        let new_value_port: IncomingPort = (new_port - 1).into();
222        let new_order_port: IncomingPort = new_port.into();
223        let order_edge_sources = self
224            .hugr()
225            .linked_outputs(out_node, new_value_port)
226            .collect_vec();
227        self.hugr_mut().disconnect(out_node, new_value_port);
228        for (src_node, src_port) in order_edge_sources {
229            self.hugr_mut()
230                .connect(src_node, src_port, out_node, new_order_port);
231        }
232
233        // Update the builder metadata
234        self.0.num_out_wires += 1;
235    }
236
237    /// Update the function builder's parent signature.
238    ///
239    /// Internal function used in [add_input] and [add_output].
240    ///
241    /// Does not update the input and output nodes.
242    ///
243    /// Returns a reference to the new optype.
244    fn update_fn_signature(&mut self, f: impl FnOnce(Signature) -> Signature) -> &ops::FuncDefn {
245        let parent = self.container_node();
246        let old_optype = self
247            .hugr()
248            .get_optype(parent)
249            .as_func_defn()
250            .expect("FunctionBuilder node must be a FuncDefn");
251        let signature = old_optype.inner_signature().into_owned();
252        let name = old_optype.name.clone();
253        self.hugr_mut()
254            .replace_op(
255                parent,
256                ops::FuncDefn {
257                    signature: f(signature).into(),
258                    name,
259                },
260            )
261            .expect("Could not replace FunctionBuilder operation");
262
263        self.hugr().get_optype(parent).as_func_defn().unwrap()
264    }
265}
266
267impl<B: AsMut<Hugr> + AsRef<Hugr>, T> Container for DFGWrapper<B, T> {
268    #[inline]
269    fn container_node(&self) -> Node {
270        self.0.container_node()
271    }
272
273    #[inline]
274    fn hugr_mut(&mut self) -> &mut Hugr {
275        self.0.hugr_mut()
276    }
277
278    #[inline]
279    fn hugr(&self) -> &Hugr {
280        self.0.hugr()
281    }
282}
283
284impl<B: AsMut<Hugr> + AsRef<Hugr>, T> Dataflow for DFGWrapper<B, T> {
285    #[inline]
286    fn num_inputs(&self) -> usize {
287        self.0.num_inputs()
288    }
289}
290
291impl<B: AsMut<Hugr> + AsRef<Hugr>, T: From<BuildHandle<DfgID>>> SubContainer for DFGWrapper<B, T> {
292    type ContainerHandle = T;
293
294    #[inline]
295    fn finish_sub_container(self) -> Result<Self::ContainerHandle, BuildError> {
296        self.0.finish_sub_container().map(Into::into)
297    }
298}
299
300impl<T> HugrBuilder for DFGWrapper<Hugr, T> {
301    fn finish_hugr(self) -> Result<Hugr, ValidationError> {
302        self.0.finish_hugr()
303    }
304}
305
306#[cfg(test)]
307pub(crate) mod test {
308    use cool_asserts::assert_matches;
309    use ops::OpParent;
310    use rstest::rstest;
311    use serde_json::json;
312
313    use crate::builder::build_traits::DataflowHugr;
314    use crate::builder::{
315        endo_sig, inout_sig, BuilderWiringError, DataflowSubContainer, ModuleBuilder,
316    };
317    use crate::extension::prelude::{bool_t, qb_t, usize_t};
318    use crate::extension::prelude::{Lift, Noop};
319    use crate::extension::{ExtensionId, SignatureError};
320    use crate::hugr::validate::InterGraphEdgeError;
321    use crate::ops::{handle::NodeHandle, OpTag};
322    use crate::ops::{OpTrait, Value};
323
324    use crate::std_extensions::logic::test::and_op;
325    use crate::types::type_param::TypeParam;
326    use crate::types::{EdgeKind, FuncValueType, RowVariable, Signature, Type, TypeBound, TypeRV};
327    use crate::utils::test_quantum_extension::h_gate;
328    use crate::{builder::test::n_identity, type_row, Wire};
329
330    use super::super::test::simple_dfg_hugr;
331    use super::*;
332    #[test]
333    fn nested_identity() -> Result<(), BuildError> {
334        let build_result = {
335            let mut outer_builder = DFGBuilder::new(endo_sig(vec![usize_t(), qb_t()]))?;
336
337            let [int, qb] = outer_builder.input_wires_arr();
338
339            let q_out = outer_builder.add_dataflow_op(h_gate(), vec![qb])?;
340
341            let inner_builder = outer_builder.dfg_builder_endo([(usize_t(), int)])?;
342            let inner_id = n_identity(inner_builder)?;
343
344            outer_builder.finish_hugr_with_outputs(inner_id.outputs().chain(q_out.outputs()))
345        };
346
347        assert_eq!(build_result.err(), None);
348
349        Ok(())
350    }
351
352    // Scaffolding for copy insertion tests
353    fn copy_scaffold<F>(f: F, msg: &'static str) -> Result<(), BuildError>
354    where
355        F: FnOnce(&mut DFGBuilder<Hugr>) -> Result<(), BuildError>,
356    {
357        let build_result = {
358            let mut builder = DFGBuilder::new(inout_sig(bool_t(), vec![bool_t(), bool_t()]))?;
359
360            f(&mut builder)?;
361
362            builder.finish_hugr()
363        };
364        assert_matches!(build_result, Ok(_), "Failed on example: {}", msg);
365
366        Ok(())
367    }
368    #[test]
369    fn copy_insertion() -> Result<(), BuildError> {
370        copy_scaffold(
371            |f_build| {
372                let [b1] = f_build.input_wires_arr();
373                f_build.set_outputs([b1, b1])
374            },
375            "Copy input and output",
376        )?;
377
378        copy_scaffold(
379            |f_build| {
380                let [b1] = f_build.input_wires_arr();
381                let xor = f_build.add_dataflow_op(and_op(), [b1, b1])?;
382                f_build.set_outputs([xor.out_wire(0), b1])
383            },
384            "Copy input and use with binary function",
385        )?;
386
387        copy_scaffold(
388            |f_build| {
389                let [b1] = f_build.input_wires_arr();
390                let xor1 = f_build.add_dataflow_op(and_op(), [b1, b1])?;
391                let xor2 = f_build.add_dataflow_op(and_op(), [b1, xor1.out_wire(0)])?;
392                f_build.set_outputs([xor2.out_wire(0), b1])
393            },
394            "Copy multiple times",
395        )?;
396
397        Ok(())
398    }
399
400    #[test]
401    fn copy_insertion_qubit() {
402        let builder = || {
403            let mut module_builder = ModuleBuilder::new();
404
405            let f_build = module_builder
406                .define_function("main", Signature::new(vec![qb_t()], vec![qb_t(), qb_t()]))?;
407
408            let [q1] = f_build.input_wires_arr();
409            f_build.finish_with_outputs([q1, q1])?;
410
411            Ok(module_builder.finish_hugr()?)
412        };
413
414        assert_matches!(
415            builder(),
416            Err(BuildError::OutputWiring {
417                error: BuilderWiringError::NoCopyLinear { typ, .. },
418                ..
419            })
420            if typ == qb_t()
421        );
422    }
423
424    #[test]
425    fn simple_inter_graph_edge() {
426        let builder = || -> Result<Hugr, BuildError> {
427            let mut f_build = FunctionBuilder::new(
428                "main",
429                Signature::new(vec![bool_t()], vec![bool_t()]).with_prelude(),
430            )?;
431
432            let [i1] = f_build.input_wires_arr();
433            let noop = f_build.add_dataflow_op(Noop(bool_t()), [i1])?;
434            let i1 = noop.out_wire(0);
435
436            let mut nested = f_build.dfg_builder(
437                Signature::new(type_row![], vec![bool_t()]).with_prelude(),
438                [],
439            )?;
440
441            let id = nested.add_dataflow_op(Noop(bool_t()), [i1])?;
442
443            let nested = nested.finish_with_outputs([id.out_wire(0)])?;
444
445            f_build.finish_hugr_with_outputs([nested.out_wire(0)])
446        };
447
448        assert_matches!(builder(), Ok(_));
449    }
450
451    #[test]
452    fn add_inputs_outputs() {
453        let builder = || -> Result<(Hugr, Node), BuildError> {
454            let mut f_build = FunctionBuilder::new(
455                "main",
456                Signature::new(vec![bool_t()], vec![bool_t()]).with_prelude(),
457            )?;
458            let f_node = f_build.container_node();
459
460            let [i0] = f_build.input_wires_arr();
461            let noop0 = f_build.add_dataflow_op(Noop(bool_t()), [i0])?;
462
463            // Some some order edges
464            f_build.set_order(&f_build.io()[0], &noop0.node());
465            f_build.set_order(&noop0.node(), &f_build.io()[1]);
466
467            // Add a new input and output, and connect them with a noop in between
468            f_build.add_output(qb_t());
469            let i1 = f_build.add_input(qb_t());
470            let noop1 = f_build.add_dataflow_op(Noop(qb_t()), [i1])?;
471
472            let hugr = f_build.finish_hugr_with_outputs([noop0.out_wire(0), noop1.out_wire(0)])?;
473            Ok((hugr, f_node))
474        };
475
476        let (hugr, f_node) = builder().unwrap_or_else(|e| panic!("{e}"));
477
478        let func_sig = hugr.get_optype(f_node).inner_function_type().unwrap();
479        assert_eq!(
480            func_sig.io(),
481            (
482                &vec![bool_t(), qb_t()].into(),
483                &vec![bool_t(), qb_t()].into()
484            )
485        );
486    }
487
488    #[test]
489    fn error_on_linear_inter_graph_edge() -> Result<(), BuildError> {
490        let mut f_build = FunctionBuilder::new("main", Signature::new(vec![qb_t()], vec![qb_t()]))?;
491
492        let [i1] = f_build.input_wires_arr();
493        let noop = f_build.add_dataflow_op(Noop(qb_t()), [i1])?;
494        let i1 = noop.out_wire(0);
495
496        let mut nested = f_build.dfg_builder(Signature::new(type_row![], vec![qb_t()]), [])?;
497
498        let id_res = nested.add_dataflow_op(Noop(qb_t()), [i1]);
499
500        // The error would anyway be caught in validation when we finish the Hugr,
501        // but the builder catches it earlier
502        assert_matches!(
503            id_res.map(|bh| bh.handle().node()), // Transform into something that impl's Debug
504            Err(BuildError::OperationWiring {
505                error: BuilderWiringError::NonCopyableIntergraph { .. },
506                ..
507            })
508        );
509
510        Ok(())
511    }
512
513    #[rstest]
514    fn dfg_hugr(simple_dfg_hugr: Hugr) {
515        assert_eq!(simple_dfg_hugr.node_count(), 3);
516        assert_matches!(simple_dfg_hugr.root_type().tag(), OpTag::Dfg);
517    }
518
519    #[test]
520    fn insert_hugr() -> Result<(), BuildError> {
521        // Create a simple DFG
522        let mut dfg_builder = DFGBuilder::new(Signature::new(vec![bool_t()], vec![bool_t()]))?;
523        let [i1] = dfg_builder.input_wires_arr();
524        dfg_builder.set_metadata("x", 42);
525        let dfg_hugr = dfg_builder.finish_hugr_with_outputs([i1])?;
526
527        // Create a module, and insert the DFG into it
528        let mut module_builder = ModuleBuilder::new();
529
530        let (dfg_node, f_node) = {
531            let mut f_build = module_builder
532                .define_function("main", Signature::new(vec![bool_t()], vec![bool_t()]))?;
533
534            let [i1] = f_build.input_wires_arr();
535            let dfg = f_build.add_hugr_with_wires(dfg_hugr, [i1])?;
536            let f = f_build.finish_with_outputs([dfg.out_wire(0)])?;
537            module_builder.set_child_metadata(f.node(), "x", "hi");
538            (dfg.node(), f.node())
539        };
540
541        let hugr = module_builder.finish_hugr()?;
542        assert_eq!(hugr.node_count(), 7);
543
544        assert_eq!(hugr.get_metadata(hugr.root(), "x"), None);
545        assert_eq!(hugr.get_metadata(dfg_node, "x").cloned(), Some(json!(42)));
546        assert_eq!(hugr.get_metadata(f_node, "x").cloned(), Some(json!("hi")));
547
548        Ok(())
549    }
550
551    #[test]
552    fn lift_node() -> Result<(), BuildError> {
553        let xa: ExtensionId = "A".try_into().unwrap();
554        let xb: ExtensionId = "B".try_into().unwrap();
555        let xc: ExtensionId = "C".try_into().unwrap();
556
557        let mut parent = DFGBuilder::new(endo_sig(bool_t()))?;
558
559        let [w] = parent.input_wires_arr();
560
561        // A box which adds extensions A and B, via child Lift nodes
562        let mut add_ab = parent.dfg_builder(endo_sig(bool_t()), [w])?;
563        let [w] = add_ab.input_wires_arr();
564
565        let lift_a = add_ab.add_dataflow_op(Lift::new(vec![bool_t()].into(), xa.clone()), [w])?;
566        let [w] = lift_a.outputs_arr();
567
568        let lift_b = add_ab.add_dataflow_op(Lift::new(vec![bool_t()].into(), xb), [w])?;
569        let [w] = lift_b.outputs_arr();
570
571        let add_ab = add_ab.finish_with_outputs([w])?;
572        let [w] = add_ab.outputs_arr();
573
574        // Add another node (a sibling to add_ab) which adds extension C
575        // via a child lift node
576        let mut add_c = parent.dfg_builder(endo_sig(bool_t()), [w])?;
577        let [w] = add_c.input_wires_arr();
578        let lift_c = add_c.add_dataflow_op(Lift::new(vec![bool_t()].into(), xc), [w])?;
579        let wires: Vec<Wire> = lift_c.outputs().collect();
580
581        let add_c = add_c.finish_with_outputs(wires)?;
582        let [w] = add_c.outputs_arr();
583        parent.finish_hugr_with_outputs([w])?;
584
585        Ok(())
586    }
587
588    #[test]
589    fn non_cfg_ancestor() -> Result<(), BuildError> {
590        let unit_sig = Signature::new(type_row![Type::UNIT], type_row![Type::UNIT]);
591        let mut b = DFGBuilder::new(unit_sig.clone())?;
592        let b_child = b.dfg_builder(unit_sig.clone(), [b.input().out_wire(0)])?;
593        let b_child_in_wire = b_child.input().out_wire(0);
594        b_child.finish_with_outputs([])?;
595        let b_child_2 = b.dfg_builder(unit_sig.clone(), [])?;
596
597        // DFG block has edge coming a sibling block, which is only valid for
598        // CFGs
599        let b_child_2_handle = b_child_2.finish_with_outputs([b_child_in_wire])?;
600
601        let res = b.finish_hugr_with_outputs([b_child_2_handle.out_wire(0)]);
602
603        assert_matches!(
604            res,
605            Err(BuildError::InvalidHUGR(
606                ValidationError::InterGraphEdgeError(InterGraphEdgeError::NonCFGAncestor { .. })
607            ))
608        );
609        Ok(())
610    }
611
612    #[test]
613    fn no_relation_edge() -> Result<(), BuildError> {
614        let unit_sig = Signature::new(type_row![Type::UNIT], type_row![Type::UNIT]);
615        let mut b = DFGBuilder::new(unit_sig.clone())?;
616        let mut b_child = b.dfg_builder(unit_sig.clone(), [b.input().out_wire(0)])?;
617        let b_child_child = b_child.dfg_builder(unit_sig.clone(), [b_child.input().out_wire(0)])?;
618        let b_child_child_in_wire = b_child_child.input().out_wire(0);
619
620        b_child_child.finish_with_outputs([])?;
621        b_child.finish_with_outputs([])?;
622
623        let mut b_child_2 = b.dfg_builder(unit_sig.clone(), [])?;
624        let b_child_2_child =
625            b_child_2.dfg_builder(unit_sig.clone(), [b_child_2.input().out_wire(0)])?;
626
627        let res = b_child_2_child.finish_with_outputs([b_child_child_in_wire]);
628
629        assert_matches!(
630            res.map(|h| h.handle().node()), // map to something that implements Debug
631            Err(BuildError::OutputWiring {
632                error: BuilderWiringError::NoRelationIntergraph { .. },
633                ..
634            })
635        );
636        Ok(())
637    }
638
639    #[test]
640    fn no_outer_row_variables() -> Result<(), BuildError> {
641        let e = crate::hugr::validate::test::extension_with_eval_parallel();
642        let tv = TypeRV::new_row_var_use(0, TypeBound::Copyable);
643        // Can *declare* a function that takes a function-value of unknown #args
644        FunctionBuilder::new(
645            "bad_eval",
646            PolyFuncType::new(
647                [TypeParam::new_list(TypeBound::Copyable)],
648                Signature::new(
649                    Type::new_function(FuncValueType::new(usize_t(), tv.clone())),
650                    vec![],
651                ),
652            ),
653        )?;
654
655        // But cannot eval it...
656        let ev = e.instantiate_extension_op(
657            "eval",
658            [vec![usize_t().into()].into(), vec![tv.into()].into()],
659        );
660        assert_eq!(
661            ev,
662            Err(SignatureError::RowVarWhereTypeExpected {
663                var: RowVariable(0, TypeBound::Copyable)
664            })
665        );
666        Ok(())
667    }
668
669    #[test]
670    fn order_edges() {
671        let (mut hugr, load_constant, call) = {
672            let mut builder = ModuleBuilder::new();
673            let func = builder
674                .declare("func", Signature::new_endo(bool_t()).into())
675                .unwrap();
676            let (load_constant, call) = {
677                let mut builder = builder
678                    .define_function("main", Signature::new(Type::EMPTY_TYPEROW, bool_t()))
679                    .unwrap();
680                let load_constant = builder.add_load_value(Value::true_val());
681                let [r] = builder
682                    .call(&func, &[], [load_constant])
683                    .unwrap()
684                    .outputs_arr();
685                builder.finish_with_outputs([r]).unwrap();
686                (load_constant.node(), r.node())
687            };
688            (builder.finish_hugr().unwrap(), load_constant, call)
689        };
690
691        let lc_optype = hugr.get_optype(load_constant);
692        let call_optype = hugr.get_optype(call);
693        assert_eq!(EdgeKind::StateOrder, lc_optype.other_input().unwrap());
694        assert_eq!(EdgeKind::StateOrder, lc_optype.other_output().unwrap());
695        assert_eq!(EdgeKind::StateOrder, call_optype.other_input().unwrap());
696        assert_eq!(EdgeKind::StateOrder, call_optype.other_output().unwrap());
697
698        hugr.connect(
699            load_constant,
700            lc_optype.other_output_port().unwrap(),
701            call,
702            call_optype.other_input_port().unwrap(),
703        );
704
705        hugr.validate().unwrap();
706    }
707}