use itertools::Itertools;
use super::build_traits::{HugrBuilder, SubContainer};
use super::handle::BuildHandle;
use super::{BuildError, Container, Dataflow, DfgID, FuncID};
use std::marker::PhantomData;
use crate::hugr::internal::HugrMutInternals;
use crate::hugr::{HugrView, ValidationError};
use crate::ops::handle::NodeHandle;
use crate::ops::{self, DFG, FuncDefn, NamedOp, OpParent, OpType};
use crate::types::{PolyFuncType, Signature, Type};
use crate::{Direction, Hugr, IncomingPort, Node, OutgoingPort, Visibility, Wire, hugr::HugrMut};
#[derive(Debug, Clone, PartialEq)]
pub struct DFGBuilder<T> {
pub(crate) base: T,
pub(crate) dfg_node: Node,
pub(crate) num_in_wires: usize,
pub(crate) num_out_wires: usize,
}
#[derive(Debug, Clone, PartialEq, derive_more::Display, derive_more::Error)]
#[non_exhaustive]
pub enum DFGAddPortError {
#[display("Adding port to an optype {op} is not supported.")]
ParentOpNotSupported {
op: String,
},
}
impl<T: AsMut<Hugr> + AsRef<Hugr>> DFGBuilder<T> {
pub fn create_with_io(
mut base: T,
parent: Node,
signature: Signature,
) -> Result<Self, BuildError> {
debug_assert_eq!(base.as_ref().children(parent).count(), 0);
let num_in_wires = signature.input_count();
let num_out_wires = signature.output_count();
let input = ops::Input {
types: signature.input().clone(),
};
let output = ops::Output {
types: signature.output().clone(),
};
base.as_mut().add_node_with_parent(parent, input);
base.as_mut().add_node_with_parent(parent, output);
Ok(Self {
base,
dfg_node: parent,
num_in_wires,
num_out_wires,
})
}
pub fn create(base: T, parent: Node) -> Result<Self, BuildError> {
let sig = base
.as_ref()
.get_optype(parent)
.inner_function_type()
.expect("DFG parent must have an inner function signature.");
let num_in_wires = sig.input_count();
let num_out_wires = sig.output_count();
Ok(Self {
base,
dfg_node: parent,
num_in_wires,
num_out_wires,
})
}
pub fn add_input(&mut self, input_type: Type) -> Result<Wire, DFGAddPortError> {
let [inp_node, _] = self.io();
if !self.update_parent_signature(|mut s| {
s.input.to_mut().push(input_type.clone());
s
}) {
return Err(DFGAddPortError::ParentOpNotSupported {
op: self
.hugr()
.get_optype(self.container_node())
.name()
.to_string(),
});
}
let OpType::Input(inp) = self.hugr_mut().optype_mut(inp_node) else {
panic!("Input node {inp_node} is not an Input");
};
inp.types.to_mut().push(input_type);
let mut new_port = self.hugr_mut().add_ports(inp_node, Direction::Outgoing, 1);
let new_port = new_port.next().unwrap();
let new_value_port: OutgoingPort = (new_port - 1).into();
let new_order_port: OutgoingPort = new_port.into();
let order_edge_targets = self
.hugr()
.linked_inputs(inp_node, new_value_port)
.collect_vec();
self.hugr_mut().disconnect(inp_node, new_value_port);
for (tgt_node, tgt_port) in order_edge_targets {
self.hugr_mut()
.connect(inp_node, new_order_port, tgt_node, tgt_port);
}
self.num_in_wires += 1;
Ok(self.input_wires().next_back().unwrap())
}
pub fn add_output(&mut self, output_type: Type) -> Result<(), DFGAddPortError> {
let [_, out_node] = self.io();
if !self.update_parent_signature(|mut s| {
s.output.to_mut().push(output_type.clone());
s
}) {
return Err(DFGAddPortError::ParentOpNotSupported {
op: self
.hugr()
.get_optype(self.container_node())
.name()
.to_string(),
});
}
let OpType::Output(out) = self.hugr_mut().optype_mut(out_node) else {
panic!("Output node {out_node} is not an Output");
};
out.types.to_mut().push(output_type);
let mut new_port = self.hugr_mut().add_ports(out_node, Direction::Incoming, 1);
let new_port = new_port.next().unwrap();
let new_value_port: IncomingPort = (new_port - 1).into();
let new_order_port: IncomingPort = new_port.into();
let order_edge_sources = self
.hugr()
.linked_outputs(out_node, new_value_port)
.collect_vec();
self.hugr_mut().disconnect(out_node, new_value_port);
for (src_node, src_port) in order_edge_sources {
self.hugr_mut()
.connect(src_node, src_port, out_node, new_order_port);
}
self.num_out_wires += 1;
Ok(())
}
fn update_parent_signature(&mut self, f: impl FnOnce(Signature) -> Signature) -> bool {
let parent = self.container_node();
match self.hugr_mut().optype_mut(parent) {
ops::OpType::FuncDefn(fd) => {
let mut sig = std::mem::take(fd.signature_mut());
let body = std::mem::take(sig.body_mut());
*sig.body_mut() = f(body);
*fd.signature_mut() = sig;
}
ops::OpType::DFG(dfg) => {
let sig = std::mem::take(&mut dfg.signature);
dfg.signature = f(sig);
}
ops::OpType::DataflowBlock(dfb) => {
let inp = std::mem::take(&mut dfb.inputs);
let other_outputs = std::mem::take(&mut dfb.other_outputs);
let sig = f(Signature::new(inp, other_outputs));
dfb.inputs = sig.input;
dfb.other_outputs = sig.output;
}
_ => return false,
}
true
}
}
impl DFGBuilder<Hugr> {
pub fn new(signature: Signature) -> Result<DFGBuilder<Hugr>, BuildError> {
let dfg_op: DFG = ops::DFG {
signature: signature.clone(),
};
let base = Hugr::new_with_entrypoint(dfg_op).expect("DFG entrypoint should be valid");
let root = base.entrypoint();
DFGBuilder::create_with_io(base, root, signature)
}
}
impl HugrBuilder for DFGBuilder<Hugr> {
fn finish_hugr(self) -> Result<Hugr, ValidationError<Node>> {
self.base.validate()?;
Ok(self.base)
}
}
impl<T: AsMut<Hugr> + AsRef<Hugr>> Container for DFGBuilder<T> {
#[inline]
fn container_node(&self) -> Node {
self.dfg_node
}
#[inline]
fn hugr_mut(&mut self) -> &mut Hugr {
self.base.as_mut()
}
#[inline]
fn hugr(&self) -> &Hugr {
self.base.as_ref()
}
}
impl<T: AsMut<Hugr> + AsRef<Hugr>> SubContainer for DFGBuilder<T> {
type ContainerHandle = BuildHandle<DfgID>;
#[inline]
fn finish_sub_container(self) -> Result<Self::ContainerHandle, BuildError> {
Ok((self.dfg_node, self.num_out_wires).into())
}
}
impl<T: AsMut<Hugr> + AsRef<Hugr>> Dataflow for DFGBuilder<T> {
#[inline]
fn num_inputs(&self) -> usize {
self.num_in_wires
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct DFGWrapper<B, T>(DFGBuilder<B>, PhantomData<T>);
impl<B, T: NodeHandle> DFGWrapper<B, T> {
pub fn from_dfg_builder(db: DFGBuilder<B>) -> Self {
Self(db, PhantomData)
}
pub fn into_dfg_builder(self) -> DFGBuilder<B> {
self.0
}
}
impl<B: AsMut<Hugr> + AsRef<Hugr>, T> DFGWrapper<B, T> {
pub fn add_input(&mut self, input_type: Type) -> Result<Wire, DFGAddPortError> {
self.0.add_input(input_type)
}
pub fn add_output(&mut self, output_type: Type) -> Result<(), DFGAddPortError> {
self.0.add_output(output_type)
}
}
pub type FunctionBuilder<B> = DFGWrapper<B, BuildHandle<FuncID<true>>>;
impl FunctionBuilder<Hugr> {
pub fn new(
name: impl Into<String>,
signature: impl Into<PolyFuncType>,
) -> Result<Self, BuildError> {
Self::new_with_op(FuncDefn::new(name, signature))
}
pub fn new_vis(
name: impl Into<String>,
signature: impl Into<PolyFuncType>,
visibility: Visibility,
) -> Result<Self, BuildError> {
Self::new_with_op(FuncDefn::new_vis(name, signature, visibility))
}
fn new_with_op(op: FuncDefn) -> Result<Self, BuildError> {
let body = op.signature().body().clone();
let base = Hugr::new_with_entrypoint(op).expect("FuncDefn entrypoint should be valid");
let root = base.entrypoint();
let db = DFGBuilder::create_with_io(base, root, body)?;
Ok(Self::from_dfg_builder(db))
}
}
impl<B: AsMut<Hugr> + AsRef<Hugr>> FunctionBuilder<B> {
pub fn with_hugr(
mut hugr: B,
name: impl Into<String>,
signature: impl Into<PolyFuncType>,
) -> Result<Self, BuildError> {
let signature: PolyFuncType = signature.into();
let body = signature.body().clone();
let op = ops::FuncDefn::new(name, signature);
let module = hugr.as_ref().module_root();
let func = hugr.as_mut().add_node_with_parent(module, op);
let db = DFGBuilder::create_with_io(hugr, func, body)?;
Ok(Self::from_dfg_builder(db))
}
}
impl<B: AsMut<Hugr> + AsRef<Hugr>, T> Container for DFGWrapper<B, T> {
#[inline]
fn container_node(&self) -> Node {
self.0.container_node()
}
#[inline]
fn hugr_mut(&mut self) -> &mut Hugr {
self.0.hugr_mut()
}
#[inline]
fn hugr(&self) -> &Hugr {
self.0.hugr()
}
}
impl<B: AsMut<Hugr> + AsRef<Hugr>, T> Dataflow for DFGWrapper<B, T> {
#[inline]
fn num_inputs(&self) -> usize {
self.0.num_inputs()
}
}
impl<B: AsMut<Hugr> + AsRef<Hugr>, T: From<BuildHandle<DfgID>>> SubContainer for DFGWrapper<B, T> {
type ContainerHandle = T;
#[inline]
fn finish_sub_container(self) -> Result<Self::ContainerHandle, BuildError> {
self.0.finish_sub_container().map(Into::into)
}
}
impl<T> HugrBuilder for DFGWrapper<Hugr, T> {
fn finish_hugr(self) -> Result<Hugr, ValidationError<Node>> {
self.0.finish_hugr()
}
}
#[cfg(test)]
pub(crate) mod test {
use cool_asserts::assert_matches;
use rstest::rstest;
use std::collections::HashMap;
use crate::builder::build_traits::DataflowHugr;
use crate::builder::test::dfg_calling_defn_decl;
use crate::builder::{
BuilderWiringError, CFGBuilder, DataflowSubContainer, ModuleBuilder, TailLoopBuilder,
endo_sig, inout_sig,
};
use crate::extension::SignatureError;
use crate::extension::prelude::{Noop, bool_t, qb_t, usize_t};
use crate::hugr::linking::{NameLinkingPolicy, NodeLinkingDirective, OnMultiDefn};
use crate::hugr::validate::InterGraphEdgeError;
use crate::metadata::Metadata;
use crate::ops::{FuncDecl, FuncDefn, OpParent, OpTag, OpTrait, Value, handle::NodeHandle};
use crate::std_extensions::logic::test::and_op;
use crate::types::type_param::TypeParam;
use crate::types::{EdgeKind, FuncValueType, RowVariable, Signature, Type, TypeBound, TypeRV};
use crate::utils::test_quantum_extension::h_gate;
use crate::{Wire, builder::test::n_identity, type_row};
use super::super::test::simple_dfg_hugr;
use super::*;
#[test]
fn nested_identity() -> Result<(), BuildError> {
let build_result = {
let mut outer_builder = DFGBuilder::new(endo_sig(vec![usize_t(), qb_t()]))?;
let [int, qb] = outer_builder.input_wires_arr();
let q_out = outer_builder.add_dataflow_op(h_gate(), vec![qb])?;
let inner_builder = outer_builder.dfg_builder_endo([(usize_t(), int)])?;
let inner_id = n_identity(inner_builder)?;
outer_builder.finish_hugr_with_outputs(inner_id.outputs().chain(q_out.outputs()))
};
assert_eq!(build_result.err(), None);
Ok(())
}
fn copy_scaffold<F>(f: F, msg: &'static str) -> Result<(), BuildError>
where
F: FnOnce(&mut DFGBuilder<Hugr>) -> Result<(), BuildError>,
{
let build_result = {
let mut builder = DFGBuilder::new(inout_sig([bool_t()], vec![bool_t(); 2]))?;
f(&mut builder)?;
builder.finish_hugr()
};
assert_matches!(build_result, Ok(_), "Failed on example: {}", msg);
Ok(())
}
#[test]
fn copy_insertion() -> Result<(), BuildError> {
copy_scaffold(
|f_build| {
let [b1] = f_build.input_wires_arr();
f_build.set_outputs([b1, b1])
},
"Copy input and output",
)?;
copy_scaffold(
|f_build| {
let [b1] = f_build.input_wires_arr();
let xor = f_build.add_dataflow_op(and_op(), [b1, b1])?;
f_build.set_outputs([xor.out_wire(0), b1])
},
"Copy input and use with binary function",
)?;
copy_scaffold(
|f_build| {
let [b1] = f_build.input_wires_arr();
let xor1 = f_build.add_dataflow_op(and_op(), [b1, b1])?;
let xor2 = f_build.add_dataflow_op(and_op(), [b1, xor1.out_wire(0)])?;
f_build.set_outputs([xor2.out_wire(0), b1])
},
"Copy multiple times",
)?;
Ok(())
}
#[test]
fn copy_insertion_qubit() {
let builder = || {
let mut module_builder = ModuleBuilder::new();
let f_build = module_builder
.define_function("main", Signature::new(vec![qb_t()], vec![qb_t(), qb_t()]))?;
let [q1] = f_build.input_wires_arr();
f_build.finish_with_outputs([q1, q1])?;
Ok(module_builder.finish_hugr()?)
};
assert_matches!(
builder(),
Err(BuildError::OutputWiring {
error: BuilderWiringError::NoCopyLinear { typ, .. },
..
})
if *typ == qb_t()
);
}
#[test]
fn simple_inter_graph_edge() {
let builder = || -> Result<Hugr, BuildError> {
let mut f_build =
FunctionBuilder::new("main", Signature::new(vec![bool_t()], vec![bool_t()]))?;
let [i1] = f_build.input_wires_arr();
let noop = f_build.add_dataflow_op(Noop(bool_t()), [i1])?;
let i1 = noop.out_wire(0);
let mut nested =
f_build.dfg_builder(Signature::new(type_row![], vec![bool_t()]), [])?;
let id = nested.add_dataflow_op(Noop(bool_t()), [i1])?;
let nested = nested.finish_with_outputs([id.out_wire(0)])?;
f_build.finish_hugr_with_outputs([nested.out_wire(0)])
};
assert_matches!(builder(), Ok(_));
}
#[rstest]
#[case::function(|sig| FunctionBuilder::new("main", sig).unwrap().into_dfg_builder())]
#[case::dfg(|sig| DFGBuilder::new(sig).unwrap())]
#[case::df_block(|sig: Signature| {
let outs = sig.output.clone();
let mut h = CFGBuilder::new(sig).unwrap();
let entry_node = h.entry_builder([], outs).unwrap().finish_sub_container().unwrap().node();
let hugr = std::mem::take(h.hugr_mut());
DFGBuilder::create(hugr, entry_node).unwrap()
})]
fn add_inputs_outputs(#[case] builder: impl FnOnce(Signature) -> DFGBuilder<Hugr>) {
let builder = || -> Result<(Hugr, Node, Signature), BuildError> {
let mut dfg = builder(Signature::new(vec![bool_t()], vec![bool_t()]));
let dfg_node = dfg.container_node();
let initial_sig = dfg
.hugr()
.get_optype(dfg_node)
.inner_function_type()
.unwrap()
.into_owned();
let [i0] = dfg.input_wires_arr();
let noop0 = dfg.add_dataflow_op(Noop(bool_t()), [i0])?;
dfg.set_order(&dfg.io()[0], &noop0.node());
dfg.set_order(&noop0.node(), &dfg.io()[1]);
dfg.add_output(qb_t()).unwrap();
let i1 = dfg.add_input(qb_t()).unwrap();
let noop1 = dfg.add_dataflow_op(Noop(qb_t()), [i1])?;
dfg.set_outputs([noop0.out_wire(0), noop1.out_wire(0)])?;
let hugr = std::mem::take(dfg.hugr_mut());
Ok((hugr, dfg_node, initial_sig))
};
let (hugr, dfg_node, initial_sig) = builder().unwrap_or_else(|e| panic!("{e}"));
let container_sig = hugr.get_optype(dfg_node).inner_function_type().unwrap();
let mut expected_sig = initial_sig;
expected_sig.input.to_mut().push(qb_t());
expected_sig.output.to_mut().push(qb_t());
assert_eq!(
container_sig.io(),
(&expected_sig.input, &expected_sig.output),
"Got signature: {container_sig}, expected: {expected_sig}",
);
}
#[rstest]
#[case::tail_loop(|sig: Signature| TailLoopBuilder::new(sig.input, vec![], sig.output).unwrap().into_dfg_builder())]
fn add_inputs_outputs_unsupported(#[case] builder: impl FnOnce(Signature) -> DFGBuilder<Hugr>) {
let mut dfg = builder(Signature::new(vec![bool_t()], vec![bool_t()]));
assert!(dfg.add_output(qb_t()).is_err());
assert!(dfg.add_input(qb_t()).is_err());
}
#[test]
fn error_on_linear_inter_graph_edge() -> Result<(), BuildError> {
let mut f_build = FunctionBuilder::new("main", Signature::new(vec![qb_t()], vec![qb_t()]))?;
let [i1] = f_build.input_wires_arr();
let noop = f_build.add_dataflow_op(Noop(qb_t()), [i1])?;
let i1 = noop.out_wire(0);
let mut nested = f_build.dfg_builder(Signature::new(type_row![], vec![qb_t()]), [])?;
let id_res = nested.add_dataflow_op(Noop(qb_t()), [i1]);
assert_matches!(
id_res.map(|bh| bh.handle().node()), Err(BuildError::OperationWiring {
error: BuilderWiringError::NonCopyableIntergraph { .. },
..
})
);
Ok(())
}
#[rstest]
fn dfg_hugr(simple_dfg_hugr: Hugr) {
assert_eq!(simple_dfg_hugr.num_nodes(), 7);
assert_eq!(simple_dfg_hugr.entry_descendants().count(), 3);
assert_matches!(simple_dfg_hugr.entrypoint_optype().tag(), OpTag::Dfg);
}
#[test]
fn add_hugr() -> Result<(), BuildError> {
struct XIntMetadata;
impl Metadata for XIntMetadata {
type Type<'hugr> = u32;
const KEY: &'static str = "x";
}
struct XStringMetadata;
impl Metadata for XStringMetadata {
type Type<'hugr> = &'hugr str;
const KEY: &'static str = "x";
}
let mut dfg_builder = DFGBuilder::new(Signature::new_endo([bool_t()]))?;
let [i1] = dfg_builder.input_wires_arr();
dfg_builder.set_metadata::<XIntMetadata>(42);
let dfg_hugr = dfg_builder.finish_hugr_with_outputs([i1])?;
let mut module_builder = ModuleBuilder::new();
let (dfg_node, f_node) = {
let mut f_build =
module_builder.define_function("main", Signature::new_endo([bool_t()]))?;
let [i1] = f_build.input_wires_arr();
let dfg = f_build.add_hugr_with_wires(dfg_hugr, [i1])?;
let f = f_build.finish_with_outputs([dfg.out_wire(0)])?;
module_builder.set_child_metadata::<XStringMetadata>(f.node(), "hi");
(dfg.node(), f.node())
};
let hugr = module_builder.finish_hugr()?;
assert_eq!(hugr.entry_descendants().count(), 7);
assert_eq!(hugr.get_metadata::<XIntMetadata>(hugr.entrypoint()), None);
assert_eq!(hugr.get_metadata::<XIntMetadata>(dfg_node), Some(42));
assert_eq!(hugr.get_metadata::<XStringMetadata>(f_node), Some("hi"));
Ok(())
}
#[rstest]
fn add_link_hugr_by_node(
#[values(false, true)] replace: bool,
#[values(true, false)] view: bool,
) {
let mut fb = FunctionBuilder::new("main", Signature::new_endo([bool_t()])).unwrap();
let my_decl = fb
.module_root_builder()
.declare("func1", Signature::new_endo([bool_t()]).into())
.unwrap();
let (insert, ins_defn, ins_decl) = dfg_calling_defn_decl();
let ins_defn_name = insert
.get_optype(ins_defn.node())
.as_func_defn()
.unwrap()
.func_name()
.clone();
let ins_decl_name = insert
.get_optype(ins_decl.node())
.as_func_decl()
.unwrap()
.func_name()
.clone();
let decl_mode = if replace {
NodeLinkingDirective::UseExisting(my_decl.node())
} else {
NodeLinkingDirective::add()
};
let link_spec = HashMap::from([
(ins_defn.node(), NodeLinkingDirective::add()),
(ins_decl.node(), decl_mode),
]);
let inserted = if view {
fb.add_link_view_by_node_with_wires(&insert, [], link_spec)
.unwrap()
} else {
fb.add_link_hugr_by_node_with_wires(insert, [], link_spec)
.unwrap()
};
let h = fb.finish_hugr_with_outputs(inserted.outputs()).unwrap();
let defn_names = h
.nodes()
.filter_map(|n| h.get_optype(n).as_func_defn().map(FuncDefn::func_name))
.collect_vec();
assert_eq!(defn_names, [&"main".to_string(), &ins_defn_name]);
let decl_names = h
.nodes()
.filter_map(|n| h.get_optype(n).as_func_decl().map(FuncDecl::func_name))
.cloned()
.collect_vec();
let mut expected_decl_names = vec!["func1".to_string()];
if !replace {
expected_decl_names.push(ins_decl_name)
}
assert_eq!(decl_names, expected_decl_names);
}
#[test]
fn add_link_hugr() {
let to_insert = {
let mut dfb = DFGBuilder::new(endo_sig(vec![usize_t(); 2])).unwrap();
let mut mb = dfb.module_root_builder();
let fb = mb
.define_function_vis("foo", endo_sig([usize_t()]), Visibility::Public)
.unwrap();
let ins = fb.input_wires();
let func = fb.finish_with_outputs(ins).unwrap();
let [in1, in2] = dfb.input_wires_arr();
let [out1] = dfb.call(func.handle(), &[], [in1]).unwrap().outputs_arr();
let [out2] = dfb.call(func.handle(), &[], [in2]).unwrap().outputs_arr();
dfb.finish_hugr_with_outputs([out1, out2]).unwrap()
};
let mut dfb = DFGBuilder::new(inout_sig([usize_t()], vec![usize_t(); 2])).unwrap();
let [in1] = dfb.input_wires_arr();
let pol = NameLinkingPolicy::default().on_multiple_defn(OnMultiDefn::UseTarget);
let [out1, out2] = dfb
.add_link_view_with_wires(&to_insert, &pol, [in1, in1])
.unwrap()
.outputs_arr();
assert!({
let h = dfb.hugr();
h.children(h.module_root())
.skip(1)
.exactly_one()
.is_ok_and(|n| h.static_targets(n).unwrap().count() == 2)
});
let [out3, out4] = dfb
.add_link_hugr_with_wires(to_insert, &pol, [out1, out2])
.unwrap()
.outputs_arr();
let h = dfb.finish_hugr_with_outputs([out3, out4]).unwrap();
assert!(
h.children(h.module_root())
.skip(1)
.exactly_one()
.is_ok_and(|n| h.static_targets(n).unwrap().count() == 4)
);
}
#[test]
fn barrier_node() -> Result<(), BuildError> {
let mut parent = DFGBuilder::new(endo_sig([bool_t()]))?;
let [w] = parent.input_wires_arr();
let mut dfg_b = parent.dfg_builder(endo_sig([bool_t()]), [w])?;
let [w] = dfg_b.input_wires_arr();
let barr0 = dfg_b.add_barrier([w])?;
let [w] = barr0.outputs_arr();
let barr1 = dfg_b.add_barrier([w])?;
let [w] = barr1.outputs_arr();
let dfg = dfg_b.finish_with_outputs([w])?;
let [w] = dfg.outputs_arr();
let mut dfg2_b = parent.dfg_builder(endo_sig(vec![bool_t(), bool_t()]), [w, w])?;
let [w1, w2] = dfg2_b.input_wires_arr();
let barr2 = dfg2_b.add_barrier([w1, w2])?;
let wires: Vec<Wire> = barr2.outputs().collect();
let dfg2 = dfg2_b.finish_with_outputs(wires)?;
let [w, _] = dfg2.outputs_arr();
parent.finish_hugr_with_outputs([w])?;
Ok(())
}
#[test]
fn non_cfg_ancestor() -> Result<(), BuildError> {
let unit_sig = Signature::new_endo([Type::UNIT]);
let mut b = DFGBuilder::new(unit_sig.clone())?;
let b_child = b.dfg_builder(unit_sig.clone(), b.input_wires())?;
let [b_child_in_wire] = b_child.input_wires_arr();
b_child.finish_with_outputs([b_child_in_wire])?;
let b_child_2 = b.dfg_builder(Signature::new([], [Type::UNIT]), [])?;
let b_child_2_handle = b_child_2.finish_with_outputs([b_child_in_wire])?;
let res = b.finish_hugr_with_outputs([b_child_2_handle.out_wire(0)]);
assert_matches!(
res,
Err(BuildError::InvalidHUGR(
ValidationError::InterGraphEdgeError(InterGraphEdgeError::NonCFGAncestor { .. })
))
);
Ok(())
}
#[test]
fn no_relation_edge() -> Result<(), BuildError> {
let unit_sig = Signature::new(type_row![Type::UNIT], type_row![Type::UNIT]);
let mut b = DFGBuilder::new(unit_sig.clone())?;
let mut b_child = b.dfg_builder(unit_sig.clone(), [b.input().out_wire(0)])?;
let b_child_child = b_child.dfg_builder(unit_sig.clone(), [b_child.input().out_wire(0)])?;
let b_child_child_in_wire = b_child_child.input().out_wire(0);
b_child_child.finish_with_outputs([])?;
b_child.finish_with_outputs([])?;
let mut b_child_2 = b.dfg_builder(unit_sig.clone(), [])?;
let b_child_2_child =
b_child_2.dfg_builder(unit_sig.clone(), [b_child_2.input().out_wire(0)])?;
let res = b_child_2_child.finish_with_outputs([b_child_child_in_wire]);
assert_matches!(
res.map(|h| h.handle().node()), Err(BuildError::OutputWiring {
error: BuilderWiringError::NoRelationIntergraph { .. },
..
})
);
Ok(())
}
#[test]
fn no_outer_row_variables() -> Result<(), BuildError> {
let e = crate::hugr::validate::test::extension_with_eval_parallel();
let tv = TypeRV::new_row_var_use(0, TypeBound::Copyable);
FunctionBuilder::new(
"bad_eval",
PolyFuncType::new(
[TypeParam::new_list_type(TypeBound::Copyable)],
Signature::new(
[Type::new_function(FuncValueType::new(
[usize_t()],
[tv.clone()],
))],
[],
),
),
)?;
let ev = e.instantiate_extension_op(
"eval",
[vec![usize_t().into()].into(), vec![tv.into()].into()],
);
assert_eq!(
ev,
Err(SignatureError::RowVarWhereTypeExpected {
var: RowVariable(0, TypeBound::Copyable)
})
);
Ok(())
}
#[test]
fn order_edges() {
let (mut hugr, load_constant, call) = {
let mut builder = ModuleBuilder::new();
let func = builder
.declare("func", Signature::new_endo([bool_t()]).into())
.unwrap();
let (load_constant, call) = {
let mut builder = builder
.define_function("main", Signature::new(Type::EMPTY_TYPEROW, [bool_t()]))
.unwrap();
let load_constant = builder.add_load_value(Value::true_val());
let [r] = builder
.call(&func, &[], [load_constant])
.unwrap()
.outputs_arr();
builder.finish_with_outputs([r]).unwrap();
(load_constant.node(), r.node())
};
(builder.finish_hugr().unwrap(), load_constant, call)
};
let lc_optype = hugr.get_optype(load_constant);
let call_optype = hugr.get_optype(call);
assert_eq!(EdgeKind::StateOrder, lc_optype.other_input().unwrap());
assert_eq!(EdgeKind::StateOrder, lc_optype.other_output().unwrap());
assert_eq!(EdgeKind::StateOrder, call_optype.other_input().unwrap());
assert_eq!(EdgeKind::StateOrder, call_optype.other_output().unwrap());
hugr.connect(
load_constant,
lc_optype.other_output_port().unwrap(),
call,
call_optype.other_input_port().unwrap(),
);
hugr.validate().unwrap();
}
}