From 065f675b1e9362f193b4fa2dc1ae75411471bae0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Agust=C3=ADn=20Borgna?= <121866228+aborgna-q@users.noreply.github.com> Date: Fri, 1 Mar 2024 16:26:05 +0000 Subject: [PATCH] docs: Add builder module docs + example (#853) drive-by: Make the `CircuitBuilder` constructor a bit more generic. --- src/builder.rs | 88 +++++++++++++++++++++++++++++- src/builder/build_traits.rs | 2 +- src/builder/circuit.rs | 9 ++- src/hugr/rewrite/simple_replace.rs | 2 +- 4 files changed, 95 insertions(+), 6 deletions(-) diff --git a/src/builder.rs b/src/builder.rs index c96465c35..a893fb44a 100644 --- a/src/builder.rs +++ b/src/builder.rs @@ -1,4 +1,90 @@ -//! Tools for building valid HUGRs. +//! Utilities for building valid HUGRs. +//! +//! This module includes various tools for building HUGRs. +//! +//! Depending on the type of HUGR you want to build, you may want to use one of +//! the following builders: +//! +//! - [ModuleBuilder]: For building a module with function declarations and +//! definitions. +//! - [DFGBuilder]: For building a dataflow graph. +//! - [FunctionBuilder]: A `DFGBuilder` specialised in defining functions with a +//! dataflow graph. +//! - [CFGBuilder]: For building a control flow graph. +//! - [ConditionalBuilder]: For building a conditional node. +//! - [TailLoopBuilder]: For building a tail-loop node. +//! +//! Additionally, the [CircuitBuilder] provides an alternative to the +//! [DFGBuilder] when working with circuits, where some inputs of operations directly +//! correspond to some outputs and operations can be directly appended using +//! unit indices. +//! +//! # Example +//! +//! The following example shows how to build a simple HUGR module with two +//! dataflow functions, one built using the `DFGBuilder` and the other using the +//! `CircuitBuilder`. +//! +//! ```rust +//! # use hugr::Hugr; +//! # use hugr::builder::{BuildError, BuildHandle, Container, DFGBuilder, Dataflow, DataflowHugr, ModuleBuilder, DataflowSubContainer, HugrBuilder}; +//! use hugr::extension::prelude::BOOL_T; +//! use hugr::std_extensions::logic::{NotOp, LOGIC_REG}; +//! use hugr::types::FunctionType; +//! +//! # fn doctest() -> Result<(), BuildError> { +//! let hugr = { +//! let mut module_builder = ModuleBuilder::new(); +//! +//! // Add a `main` function with signature `bool -> bool`. +//! // +//! // This block returns a handle to the built function. +//! let _dfg_handle = { +//! let mut dfg = module_builder.define_function( +//! "main", +//! FunctionType::new(vec![BOOL_T], vec![BOOL_T]).into(), +//! )?; +//! +//! // Get the wires from the function inputs. +//! let [w] = dfg.input_wires_arr(); +//! +//! // Add an operation connected to the input wire, and get the new dangling wires. +//! let [w] = dfg.add_dataflow_op(NotOp, [w])?.outputs_arr(); +//! +//! // Finish the function, connecting some wires to the output. +//! dfg.finish_with_outputs([w]) +//! }?; +//! +//! // Add a similar function, using the circuit builder interface. +//! let _circuit_handle = { +//! let mut dfg = module_builder.define_function( +//! "circuit", +//! FunctionType::new(vec![BOOL_T, BOOL_T], vec![BOOL_T, BOOL_T]).into(), +//! )?; +//! let mut circuit = dfg.as_circuit(dfg.input_wires()); +//! +//! // Add multiple operations, indicating only the wire index. +//! circuit.append(NotOp, [0])?.append(NotOp, [1])?; +//! +//! // Finish the circuit, and return the dataflow graph after connecting its outputs. +//! let outputs = circuit.finish(); +//! dfg.finish_with_outputs(outputs) +//! }?; +//! +//! // Finish building the HUGR, consuming the builder. +//! // +//! // Requires a registry with all the extensions used in the module. +//! module_builder.finish_hugr(&LOGIC_REG) +//! }?; +//! +//! // The built HUGR is always valid. +//! hugr.validate(&LOGIC_REG).unwrap_or_else(|e| { +//! panic!("HUGR validation failed: {e}"); +//! }); +//! # Ok(()) +//! # } +//! # doctest().unwrap(); +//! ``` //! use thiserror::Error; diff --git a/src/builder/build_traits.rs b/src/builder/build_traits.rs index c85a02eb0..41af807c0 100644 --- a/src/builder/build_traits.rs +++ b/src/builder/build_traits.rs @@ -613,7 +613,7 @@ pub trait Dataflow: Container { /// For the vector of `wires`, produce a `CircuitBuilder` where ops can be /// added using indices in to the vector. - fn as_circuit(&mut self, wires: Vec) -> CircuitBuilder { + fn as_circuit(&mut self, wires: impl IntoIterator) -> CircuitBuilder { CircuitBuilder::new(wires, self) } } diff --git a/src/builder/circuit.rs b/src/builder/circuit.rs index d730566dd..70da68910 100644 --- a/src/builder/circuit.rs +++ b/src/builder/circuit.rs @@ -27,8 +27,11 @@ pub enum CircuitBuildError { impl<'a, T: Dataflow + ?Sized> CircuitBuilder<'a, T> { /// Construct a new [`CircuitBuilder`] from a vector of incoming wires and the /// builder for the graph - pub fn new(wires: Vec, builder: &'a mut T) -> Self { - Self { wires, builder } + pub fn new(wires: impl IntoIterator, builder: &'a mut T) -> Self { + Self { + wires: wires.into_iter().collect(), + builder, + } } /// Number of wires tracked, upper bound of valid wire indices @@ -188,7 +191,7 @@ mod test { |mut f_build| { let [q0, q1, angle]: [Wire; 3] = f_build.input_wires_arr(); - let mut linear = f_build.as_circuit(vec![q0, q1]); + let mut linear = f_build.as_circuit([q0, q1]); let measure_out = linear .append(cx_gate(), [0, 1])? diff --git a/src/hugr/rewrite/simple_replace.rs b/src/hugr/rewrite/simple_replace.rs index 2f14dee3e..694faa835 100644 --- a/src/hugr/rewrite/simple_replace.rs +++ b/src/hugr/rewrite/simple_replace.rs @@ -484,7 +484,7 @@ pub(in crate::hugr::rewrite) mod test { fn test_replace_cx_cross() { let q_row: Vec = vec![QB, QB]; let mut builder = DFGBuilder::new(FunctionType::new(q_row.clone(), q_row)).unwrap(); - let mut circ = builder.as_circuit(builder.input_wires().collect()); + let mut circ = builder.as_circuit(builder.input_wires()); circ.append(cx_gate(), [0, 1]).unwrap(); circ.append(cx_gate(), [1, 0]).unwrap(); let wires = circ.finish();