hugr_core/
builder.rs

1//! Utilities for building valid HUGRs.
2//!
3//! This module includes various tools for building HUGRs.
4//!
5//! Depending on the type of HUGR you want to build, you may want to use one of
6//! the following builders:
7//!
8//! - [ModuleBuilder]: For building a module with function declarations and
9//!   definitions.
10//! - [DFGBuilder]: For building a dataflow graph.
11//! - [FunctionBuilder]: A `DFGBuilder` specialised in defining functions with a
12//!   dataflow graph.
13//! - [CFGBuilder]: For building a control flow graph.
14//! - [ConditionalBuilder]: For building a conditional node.
15//! - [TailLoopBuilder]: For building a tail-loop node.
16//!
17//! Additionally, the [CircuitBuilder] provides an alternative to the
18//! [DFGBuilder] when working with circuits, where some inputs of operations directly
19//! correspond to some outputs and operations can be directly appended using
20//! unit indices.
21//!
22//! # Example
23//!
24//! The following example shows how to build a simple HUGR module with two
25//! dataflow functions, one built using the `DFGBuilder` and the other using the
26//! `CircuitBuilder`.
27//!
28//! ```rust
29//! # use hugr::{Hugr, HugrView};
30//! # use hugr::builder::{BuildError, BuildHandle, Container, DFGBuilder, Dataflow, DataflowHugr, ModuleBuilder, DataflowSubContainer, HugrBuilder};
31//! use hugr::extension::prelude::bool_t;
32//! use hugr::std_extensions::logic::{self, LogicOp};
33//! use hugr::types::Signature;
34//!
35//! # fn doctest() -> Result<(), BuildError> {
36//! let hugr = {
37//!     let mut module_builder = ModuleBuilder::new();
38//!
39//!     // Add a `main` function with signature `bool -> bool`.
40//!     //
41//!     // This block returns a handle to the built function.
42//!     let _dfg_handle = {
43//!         let mut dfg = module_builder.define_function(
44//!             "main",
45//!             Signature::new_endo(bool_t()),
46//!         )?;
47//!
48//!         // Get the wires from the function inputs.
49//!         let [w] = dfg.input_wires_arr();
50//!
51//!         // Add an operation connected to the input wire, and get the new dangling wires.
52//!         let [w] = dfg.add_dataflow_op(LogicOp::Not, [w])?.outputs_arr();
53//!
54//!         // Finish the function, connecting some wires to the output.
55//!         dfg.finish_with_outputs([w])
56//!     }?;
57//!
58//!     // Add a similar function, using the circuit builder interface.
59//!     let _circuit_handle = {
60//!         let mut dfg = module_builder.define_function(
61//!             "circuit",
62//!             Signature::new_endo(vec![bool_t(), bool_t()]),
63//!         )?;
64//!         let mut circuit = dfg.as_circuit(dfg.input_wires());
65//!
66//!         // Add multiple operations, indicating only the wire index.
67//!         circuit.append(LogicOp::Not, [0])?.append(LogicOp::Not, [1])?;
68//!
69//!         // Finish the circuit, and return the dataflow graph after connecting its outputs.
70//!         let outputs = circuit.finish();
71//!         dfg.finish_with_outputs(outputs)
72//!     }?;
73//!
74//!     // Finish building the HUGR, consuming the builder.
75//!     //
76//!     // Requires a registry with all the extensions used in the module.
77//!     module_builder.finish_hugr()
78//! }?;
79//!
80//! // The built HUGR is always valid.
81//! hugr.validate().unwrap_or_else(|e| {
82//!     panic!("HUGR validation failed: {e}");
83//! });
84//! # Ok(())
85//! # }
86//! # doctest().unwrap();
87//! ```
88use thiserror::Error;
89
90use crate::extension::simple_op::OpLoadError;
91use crate::extension::SignatureError;
92use crate::hugr::ValidationError;
93use crate::ops::handle::{BasicBlockID, CfgID, ConditionalID, DfgID, FuncID, TailLoopID};
94use crate::ops::{NamedOp, OpType};
95use crate::types::Type;
96use crate::types::{ConstTypeError, Signature, TypeRow};
97use crate::{Node, Port, Wire};
98
99pub mod handle;
100pub use handle::BuildHandle;
101
102mod build_traits;
103pub use build_traits::{
104    Container, Dataflow, DataflowHugr, DataflowSubContainer, HugrBuilder, SubContainer,
105};
106
107mod dataflow;
108pub use dataflow::{DFGBuilder, DFGWrapper, FunctionBuilder};
109
110mod module;
111pub use module::ModuleBuilder;
112
113mod cfg;
114pub use cfg::{BlockBuilder, CFGBuilder};
115
116mod tail_loop;
117pub use tail_loop::TailLoopBuilder;
118
119mod conditional;
120pub use conditional::{CaseBuilder, ConditionalBuilder};
121
122mod circuit;
123pub use circuit::{CircuitBuildError, CircuitBuilder};
124
125/// Return a FunctionType with the same input and output types (specified).
126pub fn endo_sig(types: impl Into<TypeRow>) -> Signature {
127    Signature::new_endo(types)
128}
129
130/// Return a FunctionType with the specified input and output types.
131pub fn inout_sig(inputs: impl Into<TypeRow>, outputs: impl Into<TypeRow>) -> Signature {
132    Signature::new(inputs, outputs)
133}
134
135#[derive(Debug, Clone, PartialEq, Error)]
136#[non_exhaustive]
137/// Error while building the HUGR.
138pub enum BuildError {
139    /// The constructed HUGR is invalid.
140    #[error("The constructed HUGR is invalid: {0}.")]
141    InvalidHUGR(#[from] ValidationError<Node>),
142    /// SignatureError in trying to construct a node (differs from
143    /// [ValidationError::SignatureError] in that we could not construct a node to report about)
144    #[error(transparent)]
145    SignatureError(#[from] SignatureError),
146    /// Tried to add a malformed [Const]
147    ///
148    /// [Const]: crate::ops::constant::Const
149    #[error("Constant failed typechecking: {0}")]
150    BadConstant(#[from] ConstTypeError),
151    /// CFG can only have one entry.
152    #[error("CFG entry node already built for CFG node: {0}.")]
153    EntryBuiltError(Node),
154    /// We don't allow creating `BasicBlockBuilder<Hugr>`s when the sum-rows
155    /// are not homogeneous. Use a CFGBuilder and create a valid graph instead.
156    #[error("Cannot initialize hugr for a BasicBlockBuilder with complex sum-rows. Use a CFGBuilder instead.")]
157    BasicBlockTooComplex,
158    /// Node was expected to have a certain type but was found to not.
159    #[error("Node with index {node} does not have type {op_desc} as expected.")]
160    #[allow(missing_docs)]
161    UnexpectedType {
162        /// Index of node where error occurred.
163        node: Node,
164        /// Description of expected node.
165        op_desc: &'static str,
166    },
167    /// Error building Conditional node
168    #[error("Error building Conditional node: {0}.")]
169    ConditionalError(#[from] conditional::ConditionalBuildError),
170
171    /// Node not found in Hugr
172    #[error("{node} not found in the Hugr")]
173    NodeNotFound {
174        /// Missing node
175        node: Node,
176    },
177
178    /// Wire not found in Hugr
179    #[error("Wire not found in Hugr: {0}.")]
180    WireNotFound(Wire),
181
182    /// Error in CircuitBuilder
183    #[error("Error in CircuitBuilder: {0}.")]
184    CircuitError(#[from] circuit::CircuitBuildError),
185
186    /// Invalid wires when setting outputs
187    #[error("Found an error while setting the outputs of a {} container, {container_node}. {error}", .container_op.name())]
188    #[allow(missing_docs)]
189    OutputWiring {
190        container_op: OpType,
191        container_node: Node,
192        #[source]
193        error: BuilderWiringError,
194    },
195
196    /// Invalid input wires to a new operation
197    ///
198    /// The internal error message already contains the node index.
199    #[error("Got an input wire while adding a {} to the circuit. {error}", .op.name())]
200    #[allow(missing_docs)]
201    OperationWiring {
202        op: OpType,
203        #[source]
204        error: BuilderWiringError,
205    },
206
207    #[error("Failed to load an extension op: {0}")]
208    #[allow(missing_docs)]
209    ExtensionOp(#[from] OpLoadError),
210}
211
212#[derive(Debug, Clone, PartialEq, Error)]
213#[non_exhaustive]
214/// Error raised when wiring up a node during the build process.
215pub enum BuilderWiringError {
216    /// Tried to copy a linear type.
217    #[error("Cannot copy linear type {typ} from output {src_offset} of node {src}")]
218    #[allow(missing_docs)]
219    NoCopyLinear {
220        typ: Type,
221        src: Node,
222        src_offset: Port,
223    },
224    /// The ancestors of an inter-graph edge are not related.
225    #[error("Cannot connect an inter-graph edge between unrelated nodes. Tried connecting {src} ({src_offset}) with {dst} ({dst_offset}).")]
226    #[allow(missing_docs)]
227    NoRelationIntergraph {
228        src: Node,
229        src_offset: Port,
230        dst: Node,
231        dst_offset: Port,
232    },
233    /// Inter-Graph edges can only carry copyable data.
234    #[error("Inter-graph edges cannot carry non-copyable data {typ}. Tried connecting {src} ({src_offset}) with {dst} ({dst_offset}).")]
235    #[allow(missing_docs)]
236    NonCopyableIntergraph {
237        src: Node,
238        src_offset: Port,
239        dst: Node,
240        dst_offset: Port,
241        typ: Type,
242    },
243}
244
245#[cfg(test)]
246pub(crate) mod test {
247    use rstest::fixture;
248
249    use crate::extension::prelude::{bool_t, usize_t};
250    use crate::hugr::{views::HugrView, HugrMut};
251    use crate::ops;
252    use crate::package::Package;
253    use crate::types::{PolyFuncType, Signature};
254    use crate::Hugr;
255
256    use super::handle::BuildHandle;
257    use super::{
258        BuildError, CFGBuilder, Container, DFGBuilder, Dataflow, DataflowHugr, FuncID,
259        FunctionBuilder, ModuleBuilder,
260    };
261    use super::{DataflowSubContainer, HugrBuilder};
262
263    /// Wire up inputs of a Dataflow container to the outputs.
264    pub(crate) fn n_identity<T: DataflowSubContainer>(
265        dataflow_builder: T,
266    ) -> Result<T::ContainerHandle, BuildError> {
267        let w = dataflow_builder.input_wires();
268        dataflow_builder.finish_with_outputs(w)
269    }
270
271    pub(crate) fn build_main(
272        signature: PolyFuncType,
273        f: impl FnOnce(FunctionBuilder<&mut Hugr>) -> Result<BuildHandle<FuncID<true>>, BuildError>,
274    ) -> Result<Hugr, BuildError> {
275        let mut module_builder = ModuleBuilder::new();
276        let f_builder = module_builder.define_function("main", signature)?;
277
278        f(f_builder)?;
279
280        Ok(module_builder.finish_hugr()?)
281    }
282
283    #[fixture]
284    pub(crate) fn simple_dfg_hugr() -> Hugr {
285        let dfg_builder = DFGBuilder::new(Signature::new(vec![bool_t()], vec![bool_t()])).unwrap();
286        let [i1] = dfg_builder.input_wires_arr();
287        dfg_builder.finish_hugr_with_outputs([i1]).unwrap()
288    }
289
290    #[fixture]
291    pub(crate) fn simple_funcdef_hugr() -> Hugr {
292        let fn_builder =
293            FunctionBuilder::new("test", Signature::new(vec![bool_t()], vec![bool_t()])).unwrap();
294        let [i1] = fn_builder.input_wires_arr();
295        fn_builder.finish_hugr_with_outputs([i1]).unwrap()
296    }
297
298    #[fixture]
299    pub(crate) fn simple_module_hugr() -> Hugr {
300        let mut builder = ModuleBuilder::new();
301        let sig = Signature::new(vec![bool_t()], vec![bool_t()]);
302        builder.declare("test", sig.into()).unwrap();
303        builder.finish_hugr().unwrap()
304    }
305
306    #[fixture]
307    pub(crate) fn simple_cfg_hugr() -> Hugr {
308        let mut cfg_builder =
309            CFGBuilder::new(Signature::new(vec![usize_t()], vec![usize_t()])).unwrap();
310        super::cfg::test::build_basic_cfg(&mut cfg_builder).unwrap();
311        cfg_builder.finish_hugr().unwrap()
312    }
313
314    #[fixture]
315    pub(crate) fn simple_package() -> Package {
316        let hugr = simple_module_hugr();
317        Package::new([hugr])
318    }
319
320    #[fixture]
321    pub(crate) fn multi_module_package() -> Package {
322        let hugr0 = simple_module_hugr();
323        let hugr1 = simple_module_hugr();
324        Package::new([hugr0, hugr1])
325    }
326
327    /// A helper method which creates a DFG rooted hugr with Input and Output node
328    /// only (no wires), given a function type with extension delta.
329    // TODO consider taking two type rows and using TO_BE_INFERRED
330    pub(crate) fn closed_dfg_root_hugr(signature: Signature) -> Hugr {
331        let mut hugr = Hugr::new_with_entrypoint(ops::DFG {
332            signature: signature.clone(),
333        })
334        .unwrap();
335        hugr.add_node_with_parent(
336            hugr.entrypoint(),
337            ops::Input {
338                types: signature.input,
339            },
340        );
341        hugr.add_node_with_parent(
342            hugr.entrypoint(),
343            ops::Output {
344                types: signature.output,
345            },
346        );
347        hugr
348    }
349}