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