From 8d117e899efc53c836ef081afd1c3ec31b98f606 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 12:47:34 +0100 Subject: [PATCH 01/39] WIP start removing NodeType --- hugr-core/src/builder/build_traits.rs | 5 ++--- hugr-core/src/hugr.rs | 15 ++++++++------- hugr-core/src/hugr/hugrmut.rs | 8 ++++---- hugr-core/src/hugr/serialize.rs | 6 ++---- hugr-core/src/hugr/views.rs | 4 ++-- 5 files changed, 18 insertions(+), 20 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index 9703d65c3..c3c48160f 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -348,9 +348,8 @@ pub trait Dataflow: Container { /// Adds a [`OpType::LoadConstant`] node. fn load_const(&mut self, cid: &ConstID) -> Wire { let const_node = cid.node(); - let nodetype = self.hugr().get_nodetype(const_node); - let op: ops::Const = nodetype - .op() + let optype = self.hugr().get_optype(const_node); + let op: ops::Const = optype .clone() .try_into() .expect("ConstID does not refer to Const op."); diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index f54e56655..68e7bf4e7 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -49,7 +49,7 @@ pub struct Hugr { root: portgraph::NodeIndex, /// Operation types for each node. - op_types: UnmanagedDenseMap, + op_types: UnmanagedDenseMap, /// Node metadata metadata: UnmanagedDenseMap>, @@ -402,11 +402,12 @@ mod test { hugr.connect(lift, 0, output, 0); hugr.infer_extensions()?; - assert_eq!( - hugr.get_nodetype(lift).input_extensions().unwrap(), - &ExtensionSet::new() - ); - assert_eq!(hugr.get_nodetype(output).input_extensions().unwrap(), &r); - Ok(()) +// ALAN do something here? Check it doesn't work if we don't express the correct delta for the function? +// assert_eq!( +// hugr.get_nodetype(lift).input_extensions().unwrap(), +// &ExtensionSet::new() +// ); +// assert_eq!(hugr.get_nodetype(output).input_extensions().unwrap(), &r); +// Ok(()) } } diff --git a/hugr-core/src/hugr/hugrmut.rs b/hugr-core/src/hugr/hugrmut.rs index c06d87fd5..d49565cfe 100644 --- a/hugr-core/src/hugr/hugrmut.rs +++ b/hugr-core/src/hugr/hugrmut.rs @@ -341,8 +341,8 @@ impl + AsMut> HugrMut for T { let (new_root, node_map) = insert_hugr_internal(self.as_mut(), root, other); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let nodetype = other.get_nodetype(node.into()); - self.as_mut().op_types.set(new_node, nodetype.clone()); + let optype = other.get_optype(node.into()); + self.as_mut().op_types.set(new_node, optype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); } @@ -372,8 +372,8 @@ impl + AsMut> HugrMut for T { let node_map = insert_subgraph_internal(self.as_mut(), root, other, &portgraph); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let nodetype = other.get_nodetype(node.into()); - self.as_mut().op_types.set(new_node, nodetype.clone()); + let optype = other.get_optype(node.into()); + self.as_mut().op_types.set(new_node, optype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); } diff --git a/hugr-core/src/hugr/serialize.rs b/hugr-core/src/hugr/serialize.rs index a93eb2070..7b42a347d 100644 --- a/hugr-core/src/hugr/serialize.rs +++ b/hugr-core/src/hugr/serialize.rs @@ -48,7 +48,6 @@ impl Versioned { #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] struct NodeSer { parent: Node, - input_extensions: Option, #[serde(flatten)] op: OpType, } @@ -143,12 +142,11 @@ impl TryFrom<&Hugr> for SerHugrV1 { let mut metadata = vec![None; hugr.node_count()]; for n in hugr.nodes() { let parent = node_rekey[&hugr.get_parent(n).unwrap_or(n)]; - let opt = hugr.get_nodetype(n); + let opt = hugr.get_optype(n); let new_node = node_rekey[&n].index(); nodes[new_node] = Some(NodeSer { parent, - input_extensions: opt.input_extensions.clone(), - op: opt.op.clone(), + op: opt.clone(), }); metadata[new_node].clone_from(hugr.metadata.get(n.pg_index())); } diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index 8ad14190f..d7042e18f 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -79,8 +79,8 @@ pub trait HugrView: HugrInternals { /// Return the type of the HUGR root node. #[inline] - fn root_type(&self) -> &NodeType { - let node_type = self.get_nodetype(self.root()); + fn root_type(&self) -> &OpType { + let op_type = self.get_optype(self.root()); // Sadly no way to do this at present // debug_assert!(Self::RootHandle::can_hold(node_type.tag())); node_type From 974236bedd14d07acc9e90dcb907c4b1e36809e9 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 12:47:36 +0100 Subject: [PATCH 02/39] Revert "WIP start removing NodeType" This reverts commit 8d117e899efc53c836ef081afd1c3ec31b98f606. --- hugr-core/src/builder/build_traits.rs | 5 +++-- hugr-core/src/hugr.rs | 15 +++++++-------- hugr-core/src/hugr/hugrmut.rs | 8 ++++---- hugr-core/src/hugr/serialize.rs | 6 ++++-- hugr-core/src/hugr/views.rs | 4 ++-- 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index c3c48160f..9703d65c3 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -348,8 +348,9 @@ pub trait Dataflow: Container { /// Adds a [`OpType::LoadConstant`] node. fn load_const(&mut self, cid: &ConstID) -> Wire { let const_node = cid.node(); - let optype = self.hugr().get_optype(const_node); - let op: ops::Const = optype + let nodetype = self.hugr().get_nodetype(const_node); + let op: ops::Const = nodetype + .op() .clone() .try_into() .expect("ConstID does not refer to Const op."); diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 68e7bf4e7..f54e56655 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -49,7 +49,7 @@ pub struct Hugr { root: portgraph::NodeIndex, /// Operation types for each node. - op_types: UnmanagedDenseMap, + op_types: UnmanagedDenseMap, /// Node metadata metadata: UnmanagedDenseMap>, @@ -402,12 +402,11 @@ mod test { hugr.connect(lift, 0, output, 0); hugr.infer_extensions()?; -// ALAN do something here? Check it doesn't work if we don't express the correct delta for the function? -// assert_eq!( -// hugr.get_nodetype(lift).input_extensions().unwrap(), -// &ExtensionSet::new() -// ); -// assert_eq!(hugr.get_nodetype(output).input_extensions().unwrap(), &r); -// Ok(()) + assert_eq!( + hugr.get_nodetype(lift).input_extensions().unwrap(), + &ExtensionSet::new() + ); + assert_eq!(hugr.get_nodetype(output).input_extensions().unwrap(), &r); + Ok(()) } } diff --git a/hugr-core/src/hugr/hugrmut.rs b/hugr-core/src/hugr/hugrmut.rs index d49565cfe..c06d87fd5 100644 --- a/hugr-core/src/hugr/hugrmut.rs +++ b/hugr-core/src/hugr/hugrmut.rs @@ -341,8 +341,8 @@ impl + AsMut> HugrMut for T { let (new_root, node_map) = insert_hugr_internal(self.as_mut(), root, other); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let optype = other.get_optype(node.into()); - self.as_mut().op_types.set(new_node, optype.clone()); + let nodetype = other.get_nodetype(node.into()); + self.as_mut().op_types.set(new_node, nodetype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); } @@ -372,8 +372,8 @@ impl + AsMut> HugrMut for T { let node_map = insert_subgraph_internal(self.as_mut(), root, other, &portgraph); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let optype = other.get_optype(node.into()); - self.as_mut().op_types.set(new_node, optype.clone()); + let nodetype = other.get_nodetype(node.into()); + self.as_mut().op_types.set(new_node, nodetype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); } diff --git a/hugr-core/src/hugr/serialize.rs b/hugr-core/src/hugr/serialize.rs index 7b42a347d..a93eb2070 100644 --- a/hugr-core/src/hugr/serialize.rs +++ b/hugr-core/src/hugr/serialize.rs @@ -48,6 +48,7 @@ impl Versioned { #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] struct NodeSer { parent: Node, + input_extensions: Option, #[serde(flatten)] op: OpType, } @@ -142,11 +143,12 @@ impl TryFrom<&Hugr> for SerHugrV1 { let mut metadata = vec![None; hugr.node_count()]; for n in hugr.nodes() { let parent = node_rekey[&hugr.get_parent(n).unwrap_or(n)]; - let opt = hugr.get_optype(n); + let opt = hugr.get_nodetype(n); let new_node = node_rekey[&n].index(); nodes[new_node] = Some(NodeSer { parent, - op: opt.clone(), + input_extensions: opt.input_extensions.clone(), + op: opt.op.clone(), }); metadata[new_node].clone_from(hugr.metadata.get(n.pg_index())); } diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index d7042e18f..8ad14190f 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -79,8 +79,8 @@ pub trait HugrView: HugrInternals { /// Return the type of the HUGR root node. #[inline] - fn root_type(&self) -> &OpType { - let op_type = self.get_optype(self.root()); + fn root_type(&self) -> &NodeType { + let node_type = self.get_nodetype(self.root()); // Sadly no way to do this at present // debug_assert!(Self::RootHandle::can_hold(node_type.tag())); node_type From 16c72e593f90ccd55ce2d063e1bce1ef704d4765 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 13:19:44 +0100 Subject: [PATCH 03/39] Drop validate_with_extension_closure...may want to revert eventually --- hugr-core/src/extension/infer.rs | 5 -- hugr-core/src/extension/infer/test.rs | 78 +-------------------------- hugr-core/src/hugr.rs | 4 +- hugr-core/src/hugr/validate.rs | 26 ++------- 4 files changed, 7 insertions(+), 106 deletions(-) diff --git a/hugr-core/src/extension/infer.rs b/hugr-core/src/extension/infer.rs index bcb8608cc..bf00231e7 100644 --- a/hugr-core/src/extension/infer.rs +++ b/hugr-core/src/extension/infer.rs @@ -32,11 +32,6 @@ use thiserror::Error; pub type ExtensionSolution = HashMap; /// Infer extensions for a hugr. This is the main API exposed by this module. -/// -/// Return all the solutions found for locations on the graph, these can be -/// passed to [`validate_with_extension_closure`] -/// -/// [`validate_with_extension_closure`]: crate::Hugr::validate_with_extension_closure pub fn infer_extensions(hugr: &impl HugrView) -> Result { let mut ctx = UnificationContext::new(hugr); ctx.main_loop()?; diff --git a/hugr-core/src/extension/infer/test.rs b/hugr-core/src/extension/infer/test.rs index 5954ae7ce..15376947e 100644 --- a/hugr-core/src/extension/infer/test.rs +++ b/hugr-core/src/extension/infer/test.rs @@ -15,8 +15,7 @@ use crate::ops::{CustomOp, Lift, OpType}; #[cfg(feature = "extension_inference")] use crate::{ builder::test::closed_dfg_root_hugr, - extension::prelude::PRELUDE_ID, - hugr::{internal::HugrMutInternals, validate::ValidationError}, + hugr::validate::ValidationError, ops::{dataflow::DataflowParent, handle::NodeHandle}, }; @@ -794,81 +793,6 @@ fn test_cfg_loops() -> Result<(), Box> { Ok(()) } -#[test] -#[cfg(feature = "extension_inference")] -fn test_validate_with_closure() -> Result<(), Box> { - fn dfg_hugr_with_exts(e: Option) -> (Hugr, Node, Node) { - let mut h = closed_dfg_root_hugr(FunctionType::new_endo(type_row![QB_T])); - h.replace_op(h.root(), NodeType::new(h.get_optype(h.root()).clone(), e)) - .unwrap(); - let [input, output] = h.get_io(h.root()).unwrap(); - (h, input, output) - } - fn identity_hugr_with_exts(e: Option) -> Hugr { - let (mut h, input, output) = dfg_hugr_with_exts(e); - h.connect(input, 0, output, 0); - h - } - - const EXT_ID: ExtensionId = ExtensionId::new_unchecked("foo"); - - let inner_open = identity_hugr_with_exts(None); - - let inner_prelude = identity_hugr_with_exts(Some(ExtensionSet::singleton(&PRELUDE_ID))); - - let inner_other = identity_hugr_with_exts(Some(ExtensionSet::singleton(&EXT_ID))); - - // All three can be inferred and validated, without writing solutions in: - for inner in [&inner_open, &inner_prelude, &inner_other] { - assert_matches!( - inner.validate(&PRELUDE_REGISTRY), - Err(ValidationError::ExtensionError(_)) - ); - - let soln = infer_extensions(inner)?; - inner.validate_with_extension_closure(soln, &PRELUDE_REGISTRY)?; - } - - // Helper builds a Hugr with extensions {PRELUDE_ID}, around argument - let build_outer_prelude = |inner: Hugr| -> Hugr { - let (mut h, input, output) = dfg_hugr_with_exts(Some(ExtensionSet::singleton(&PRELUDE_ID))); - let inner_node = h.insert_hugr(h.root(), inner).new_root; - h.connect(input, 0, inner_node, 0); - h.connect(inner_node, 0, output, 0); - h - }; - - // Building a Hugr around the inner DFG works if the inner DFG is open, - // or has the correct (prelude) extensions: - for inner in [&inner_open, &inner_prelude] { - let mut h = build_outer_prelude(inner.clone()); - h.update_validate(&PRELUDE_REGISTRY)?; - } - - // ...but fails if the inner DFG already has the 'wrong' extensions: - assert_matches!( - build_outer_prelude(inner_other.clone()).update_validate(&PRELUDE_REGISTRY), - Err(ValidationError::CantInfer(_)) - ); - - // If we do inference on the inner Hugr first, this (still) works if the - // inner DFG already had the correct input-extensions: - let mut inner_prelude_inferred = inner_prelude; - inner_prelude_inferred.update_validate(&PRELUDE_REGISTRY)?; - build_outer_prelude(inner_prelude_inferred).update_validate(&PRELUDE_REGISTRY)?; - - // But fails for previously-open inner DFG as inference - // infers an incorrect (empty) solution: - let mut inner_inferred = inner_open; - inner_inferred.update_validate(&PRELUDE_REGISTRY)?; - assert_matches!( - build_outer_prelude(inner_inferred).update_validate(&PRELUDE_REGISTRY), - Err(ValidationError::CantInfer(_)) - ); - - Ok(()) -} - #[test] /// A control flow graph consisting of an entry node and a single block /// which adds a resource and links to both itself and the exit node. diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index f54e56655..be84d48c3 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -9,8 +9,6 @@ pub mod serialize; pub mod validate; pub mod views; -#[cfg(feature = "extension_inference")] -use std::collections::HashMap; use std::collections::VecDeque; use std::iter; @@ -208,7 +206,7 @@ impl Hugr { #[cfg(feature = "extension_inference")] { self.infer_extensions()?; - self.validate_extensions(HashMap::new())?; + self.validate_extensions()?; } Ok(()) } diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index 0af7e7e4a..7f9b2199f 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -11,9 +11,7 @@ use thiserror::Error; use crate::extension::validate::ExtensionValidator; use crate::extension::SignatureError; -use crate::extension::{ - validate::ExtensionError, ExtensionRegistry, ExtensionSolution, InferExtensionError, -}; +use crate::extension::{validate::ExtensionError, ExtensionRegistry, InferExtensionError}; use crate::ops::custom::CustomOpError; use crate::ops::custom::{resolve_opaque_op, CustomOp}; @@ -45,10 +43,9 @@ impl Hugr { /// TODO: Add a version of validation which allows for open extension /// variables (see github issue #457) pub fn validate(&self, extension_registry: &ExtensionRegistry) -> Result<(), ValidationError> { - #[cfg(feature = "extension_inference")] - self.validate_with_extension_closure(HashMap::new(), extension_registry)?; - #[cfg(not(feature = "extension_inference"))] self.validate_no_extensions(extension_registry)?; + #[cfg(feature = "extension_inference")] + self.validate_extensions()?; Ok(()) } @@ -65,8 +62,8 @@ impl Hugr { /// Validate extensions on the input and output edges of nodes. Check that /// the target ends of edges require the extensions from the sources, and /// check extension deltas from parent nodes are reflected in their children. - pub fn validate_extensions(&self, closure: ExtensionSolution) -> Result<(), ValidationError> { - let validator = ExtensionValidator::new(self, closure); + pub fn validate_extensions(&self) -> Result<(), ValidationError> { + let validator = ExtensionValidator::new(self, HashMap::new()); for src_node in self.nodes() { let node_type = self.get_nodetype(src_node); @@ -91,19 +88,6 @@ impl Hugr { } Ok(()) } - - /// Check the validity of a hugr, taking an argument of a closure for the - /// free extension variables - pub fn validate_with_extension_closure( - &self, - closure: ExtensionSolution, - extension_registry: &ExtensionRegistry, - ) -> Result<(), ValidationError> { - let mut validator = ValidationContext::new(self, extension_registry); - validator.validate()?; - self.validate_extensions(closure)?; - Ok(()) - } } impl<'a, 'b> ValidationContext<'a, 'b> { From 759af8668cc93752f70fc638b9387f0bd5e3d249 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 13:09:41 +0100 Subject: [PATCH 04/39] Rm extension/validate.rs (ExtensionValidator), just check child.is_subset(parent) --- hugr-core/src/extension.rs | 1 - hugr-core/src/extension/infer.rs | 33 +++-- hugr-core/src/extension/validate.rs | 209 ---------------------------- hugr-core/src/hugr/validate.rs | 65 +++++---- hugr-core/src/hugr/validate/test.rs | 164 ++-------------------- 5 files changed, 65 insertions(+), 407 deletions(-) delete mode 100644 hugr-core/src/extension/validate.rs diff --git a/hugr-core/src/extension.rs b/hugr-core/src/extension.rs index 1ef9c50cd..b6d6bab14 100644 --- a/hugr-core/src/extension.rs +++ b/hugr-core/src/extension.rs @@ -35,7 +35,6 @@ pub use type_def::{TypeDef, TypeDefBound}; mod const_fold; pub mod prelude; pub mod simple_op; -pub mod validate; pub use const_fold::{ConstFold, ConstFoldResult, Folder}; pub use prelude::{PRELUDE, PRELUDE_REGISTRY}; diff --git a/hugr-core/src/extension/infer.rs b/hugr-core/src/extension/infer.rs index bf00231e7..3310daa6c 100644 --- a/hugr-core/src/extension/infer.rs +++ b/hugr-core/src/extension/infer.rs @@ -18,8 +18,6 @@ use crate::{ Direction, Node, }; -use super::validate::ExtensionError; - use petgraph::graph as pg; use petgraph::{Directed, EdgeType, Undirected}; @@ -88,11 +86,24 @@ pub enum InferExtensionError { /// The location on the hugr that's associated to the unsolved meta location: (Node, Direction), }, - /// An extension mismatch between two nodes which are connected by an edge. - /// This should mirror (or reuse) `ValidationError`'s SrcExceedsTgtExtensions - /// and TgtExceedsSrcExtensions - #[error("Edge mismatch: {0}")] - EdgeMismatch(#[from] ExtensionError), + /// Too many extension requirements coming from src + #[error("Extensions at source node {from:?} ({from_extensions}) exceed those at target {to:?} ({to_extensions})")] + #[allow(missing_docs)] + SrcExceedsTgtExtensions { + from: Node, + from_extensions: ExtensionSet, + to: Node, + to_extensions: ExtensionSet, + }, + /// Missing lift node + #[error("Extensions at target node {to:?} ({to_extensions}) exceed those at source {from:?} ({from_extensions})")] + #[allow(missing_docs)] + TgtExceedsSrcExtensions { + from: Node, + from_extensions: ExtensionSet, + to: Node, + to_extensions: ExtensionSet, + }, } /// A graph of metavariables connected by constraints. @@ -384,21 +395,21 @@ impl UnificationContext { [(node2, rs2.clone()), (node1, rs1.clone())] }; - return InferExtensionError::EdgeMismatch(if src_rs.is_subset(&tgt_rs) { - ExtensionError::TgtExceedsSrcExtensions { + return if src_rs.is_subset(&tgt_rs) { + InferExtensionError::TgtExceedsSrcExtensions { from: *src, from_extensions: src_rs, to: *tgt, to_extensions: tgt_rs, } } else { - ExtensionError::SrcExceedsTgtExtensions { + InferExtensionError::SrcExceedsTgtExtensions { from: *src, from_extensions: src_rs, to: *tgt, to_extensions: tgt_rs, } - }); + }; } } if let (Some(loc1), Some(loc2)) = (loc1, loc2) { diff --git a/hugr-core/src/extension/validate.rs b/hugr-core/src/extension/validate.rs deleted file mode 100644 index 246e4c3a3..000000000 --- a/hugr-core/src/extension/validate.rs +++ /dev/null @@ -1,209 +0,0 @@ -//! Validation routines for instantiations of a extension ops and types in a -//! Hugr. - -use std::collections::HashMap; - -use thiserror::Error; - -use super::{ExtensionSet, ExtensionSolution}; -use crate::hugr::NodeType; -use crate::{Direction, Hugr, HugrView, Node, Port}; - -/// Context for validating the extension requirements defined in a Hugr. -#[derive(Debug, Clone, Default)] -pub struct ExtensionValidator { - /// Extension requirements associated with each edge - extensions: HashMap<(Node, Direction), ExtensionSet>, -} - -impl ExtensionValidator { - /// Initialise a new extension validator, pre-computing the extension - /// requirements for each node in the Hugr. - /// - /// The `closure` argument is a set of extensions which doesn't actually - /// live on the graph, but is used to close the graph for validation - pub fn new(hugr: &Hugr, closure: ExtensionSolution) -> Self { - let mut extensions: HashMap<(Node, Direction), ExtensionSet> = HashMap::new(); - for (node, incoming_sol) in closure.into_iter() { - let extension_reqs = hugr - .get_nodetype(node) - .op_signature() - .map(|s| s.extension_reqs) - .unwrap_or_default(); - - let outgoing_sol = extension_reqs.union(incoming_sol.clone()); - - extensions.insert((node, Direction::Incoming), incoming_sol); - extensions.insert((node, Direction::Outgoing), outgoing_sol); - } - - let mut validator = ExtensionValidator { extensions }; - - for node in hugr.nodes() { - validator.gather_extensions(&node, hugr.get_nodetype(node)); - } - - validator - } - - /// Use the signature supplied by a dataflow node to work out the - /// extension requirements for all of its input and output edges, then put - /// those requirements in the extension validation context. - fn gather_extensions(&mut self, node: &Node, node_type: &NodeType) { - if let Some((input_exts, output_exts)) = node_type.io_extensions() { - let prev_i = self - .extensions - .insert((*node, Direction::Incoming), input_exts.clone()); - assert!(prev_i.is_none()); - let prev_o = self - .extensions - .insert((*node, Direction::Outgoing), output_exts); - assert!(prev_o.is_none()); - } - } - - /// Get the input or output extension requirements for a particular node in the Hugr. - /// - /// # Errors - /// - /// If the node extensions are missing. - fn query_extensions( - &self, - node: Node, - dir: Direction, - ) -> Result<&ExtensionSet, ExtensionError> { - self.extensions - .get(&(node, dir)) - .ok_or(ExtensionError::MissingInputExtensions(node)) - } - - /// Check that two `PortIndex` have compatible extension requirements, - /// according to the information accumulated by `gather_extensions`. - /// - /// This extension checking assumes that free extension variables - /// (e.g. implicit lifting of `A -> B` to `[R]A -> [R]B`) - /// and adding of lift nodes - /// (i.e. those which transform an edge from `A` to `[R]A`) - /// has already been done. - pub fn check_extensions_compatible( - &self, - src: &(Node, Port), - tgt: &(Node, Port), - ) -> Result<(), ExtensionError> { - let rs_src = self.query_extensions(src.0, Direction::Outgoing)?; - let rs_tgt = self.query_extensions(tgt.0, Direction::Incoming)?; - - if rs_src == rs_tgt { - Ok(()) - } else if rs_src.is_subset(rs_tgt) { - // The extra extension requirements reside in the target node. - // If so, we can fix this mismatch with a lift node - Err(ExtensionError::TgtExceedsSrcExtensionsAtPort { - from: src.0, - from_offset: src.1, - from_extensions: rs_src.clone(), - to: tgt.0, - to_offset: tgt.1, - to_extensions: rs_tgt.clone(), - }) - } else { - Err(ExtensionError::SrcExceedsTgtExtensionsAtPort { - from: src.0, - from_offset: src.1, - from_extensions: rs_src.clone(), - to: tgt.0, - to_offset: tgt.1, - to_extensions: rs_tgt.clone(), - }) - } - } - - /// Check that a pair of input and output nodes declare the same extensions - /// as in the signature of their parents. - #[allow(unused_variables)] - pub fn validate_io_extensions( - &self, - parent: Node, - input: Node, - output: Node, - ) -> Result<(), ExtensionError> { - #[cfg(feature = "extension_inference")] - { - let parent_input_extensions = self.query_extensions(parent, Direction::Incoming)?; - let parent_output_extensions = self.query_extensions(parent, Direction::Outgoing)?; - for dir in Direction::BOTH { - let input_extensions = self.query_extensions(input, dir)?; - let output_extensions = self.query_extensions(output, dir)?; - if parent_input_extensions != input_extensions { - return Err(ExtensionError::ParentIOExtensionMismatch { - parent, - parent_extensions: parent_input_extensions.clone(), - child: input, - child_extensions: input_extensions.clone(), - }); - }; - if parent_output_extensions != output_extensions { - return Err(ExtensionError::ParentIOExtensionMismatch { - parent, - parent_extensions: parent_output_extensions.clone(), - child: output, - child_extensions: output_extensions.clone(), - }); - }; - } - } - Ok(()) - } -} - -/// Errors that can occur while validating a Hugr. -#[derive(Debug, Clone, PartialEq, Error)] -#[allow(missing_docs)] -#[non_exhaustive] -pub enum ExtensionError { - /// Missing lift node - #[error("Extensions at target node {to:?} ({to_extensions}) exceed those at source {from:?} ({from_extensions})")] - TgtExceedsSrcExtensions { - from: Node, - from_extensions: ExtensionSet, - to: Node, - to_extensions: ExtensionSet, - }, - /// A version of the above which includes port info - #[error("Extensions at target node {to:?} ({to_offset:?}) ({to_extensions}) exceed those at source {from:?} ({from_offset:?}) ({from_extensions})")] - TgtExceedsSrcExtensionsAtPort { - from: Node, - from_offset: Port, - from_extensions: ExtensionSet, - to: Node, - to_offset: Port, - to_extensions: ExtensionSet, - }, - /// Too many extension requirements coming from src - #[error("Extensions at source node {from:?} ({from_extensions}) exceed those at target {to:?} ({to_extensions})")] - SrcExceedsTgtExtensions { - from: Node, - from_extensions: ExtensionSet, - to: Node, - to_extensions: ExtensionSet, - }, - /// A version of the above which includes port info - #[error("Extensions at source node {from:?} ({from_offset:?}) ({from_extensions}) exceed those at target {to:?} ({to_offset:?}) ({to_extensions})")] - SrcExceedsTgtExtensionsAtPort { - from: Node, - from_offset: Port, - from_extensions: ExtensionSet, - to: Node, - to_offset: Port, - to_extensions: ExtensionSet, - }, - #[error("Missing input extensions for node {0:?}")] - MissingInputExtensions(Node), - #[error("Extensions of I/O node ({child:?}) {child_extensions:?} don't match those expected by parent node ({parent:?}): {parent_extensions:?}")] - ParentIOExtensionMismatch { - parent: Node, - parent_extensions: ExtensionSet, - child: Node, - child_extensions: ExtensionSet, - }, -} diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index 7f9b2199f..e82287669 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -9,14 +9,11 @@ use petgraph::visit::{Topo, Walker}; use portgraph::{LinkView, PortView}; use thiserror::Error; -use crate::extension::validate::ExtensionValidator; -use crate::extension::SignatureError; -use crate::extension::{validate::ExtensionError, ExtensionRegistry, InferExtensionError}; +use crate::extension::{ExtensionRegistry, ExtensionSet, InferExtensionError, SignatureError}; -use crate::ops::custom::CustomOpError; -use crate::ops::custom::{resolve_opaque_op, CustomOp}; +use crate::ops::custom::{resolve_opaque_op, CustomOp, CustomOpError}; use crate::ops::validate::{ChildrenEdgeData, ChildrenValidationError, EdgeValidationError}; -use crate::ops::{FuncDefn, OpTag, OpTrait, OpType, ValidateOp}; +use crate::ops::{FuncDefn, OpParent, OpTag, OpTrait, OpType, ValidateOp}; use crate::types::type_param::TypeParam; use crate::types::{EdgeKind, FunctionType}; use crate::{Direction, Hugr, Node, Port}; @@ -59,30 +56,33 @@ impl Hugr { validator.validate() } - /// Validate extensions on the input and output edges of nodes. Check that - /// the target ends of edges require the extensions from the sources, and - /// check extension deltas from parent nodes are reflected in their children. + /// Validate extensions, i.e. that extension deltas from parent nodes are reflected in their children. pub fn validate_extensions(&self) -> Result<(), ValidationError> { - let validator = ExtensionValidator::new(self, HashMap::new()); - for src_node in self.nodes() { - let node_type = self.get_nodetype(src_node); - - // FuncDefns have no resources since they're static nodes, but the - // functions they define can have any extension delta. - if node_type.tag() != OpTag::FuncDefn { - // If this is a container with I/O nodes, check that the extension they - // define match the extensions of the container. - if let Some([input, output]) = self.get_io(src_node) { - validator.validate_io_extensions(src_node, input, output)?; + for parent in self.nodes() { + let parent_op = self.get_optype(parent); + let parent_extensions = match parent_op.inner_function_type() { + Some(FunctionType { extension_reqs, .. }) => extension_reqs, + None => { + if matches!(parent_op.tag(), OpTag::Cfg | OpTag::Conditional) { + parent_op.extension_delta() + } else { + assert!( + parent_op.tag() == OpTag::ModuleRoot + || self.children(parent).next().is_none() + ); + continue; + } } - } - - for src_port in self.node_outputs(src_node) { - for (tgt_node, tgt_port) in self.linked_inputs(src_node, src_port) { - validator.check_extensions_compatible( - &(src_node, src_port.into()), - &(tgt_node, tgt_port.into()), - )?; + }; + for child in self.children(parent) { + let child_extensions = self.get_optype(child).extension_delta(); + if !parent_extensions.is_superset(&child_extensions) { + return Err(ValidationError::ExtensionError { + parent, + parent_extensions, + child, + child_extensions, + }); } } } @@ -741,9 +741,14 @@ pub enum ValidationError { /// There are invalid inter-graph edges. #[error(transparent)] InterGraphEdgeError(#[from] InterGraphEdgeError), + #[error("Extensions of child node ({child}) {child_extensions} are not a subset of the parent node ({parent}): {parent_extensions}")] /// There are errors in the extension declarations. - #[error(transparent)] - ExtensionError(#[from] ExtensionError), + ExtensionError { + parent: Node, + parent_extensions: ExtensionSet, + child: Node, + child_extensions: ExtensionSet, + }, #[error(transparent)] CantInfer(#[from] InferExtensionError), /// Error in a node signature diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 8e8e1f0c5..30692e5ea 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -872,7 +872,6 @@ mod extension_tests { use crate::macros::const_extension_ids; const_extension_ids! { - const XA: ExtensionId = "A"; const XB: ExtensionId = "BOOL_EXT"; } @@ -998,6 +997,7 @@ mod extension_tests { } #[test] + // ALAN TODO parametrize this by some different OpTypes e.g. DFG, Function fn parent_io_mismatch() { // The DFG node declares that it has an empty extension delta, // but it's child graph adds extension "XB", causing a mismatch. @@ -1033,162 +1033,14 @@ mod extension_tests { hugr.connect(lift, 0, output, 0); let result = hugr.validate(&PRELUDE_REGISTRY); - assert_matches!( + assert_eq!( result, - Err(ValidationError::ExtensionError( - ExtensionError::ParentIOExtensionMismatch { .. } - )) - ); - } - - #[test] - /// A wire with no extension requirements is wired into a node which has - /// [A,BOOL_T] extensions required on its inputs and outputs. This could be fixed - /// by adding a lift node, but for validation this is an error. - fn missing_lift_node() -> Result<(), BuildError> { - let mut module_builder = ModuleBuilder::new(); - let mut main = module_builder.define_function( - "main", - FunctionType::new(type_row![NAT], type_row![NAT]).into(), - )?; - let [main_input] = main.input_wires_arr(); - - let f_builder = main.dfg_builder( - FunctionType::new(type_row![NAT], type_row![NAT]), - // Inner DFG has extension requirements that the wire wont satisfy - Some(ExtensionSet::from_iter([XA, XB])), - [main_input], - )?; - let f_inputs = f_builder.input_wires(); - let f_handle = f_builder.finish_with_outputs(f_inputs)?; - let [f_output] = f_handle.outputs_arr(); - main.finish_with_outputs([f_output])?; - let handle = module_builder.hugr().validate(&PRELUDE_REGISTRY); - - assert_matches!( - handle, - Err(ValidationError::ExtensionError( - ExtensionError::TgtExceedsSrcExtensionsAtPort { .. } - )) - ); - Ok(()) - } - - #[test] - /// A wire with extension requirement `[A]` is wired into a an output with no - /// extension req. In the validation extension typechecking, we don't do any - /// unification, so don't allow open extension variables on the function - /// signature, so this fails. - fn too_many_extension() -> Result<(), BuildError> { - let mut module_builder = ModuleBuilder::new(); - - let main_sig = FunctionType::new(type_row![NAT], type_row![NAT]).into(); - - let mut main = module_builder.define_function("main", main_sig)?; - let [main_input] = main.input_wires_arr(); - - let inner_sig = FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(XA); - - let f_builder = main.dfg_builder(inner_sig, Some(ExtensionSet::new()), [main_input])?; - let f_inputs = f_builder.input_wires(); - let f_handle = f_builder.finish_with_outputs(f_inputs)?; - let [f_output] = f_handle.outputs_arr(); - main.finish_with_outputs([f_output])?; - let handle = module_builder.hugr().validate(&PRELUDE_REGISTRY); - assert_matches!( - handle, - Err(ValidationError::ExtensionError( - ExtensionError::SrcExceedsTgtExtensionsAtPort { .. } - )) - ); - Ok(()) - } - - #[test] - /// A wire with extension requirements `[A]` and another with requirements - /// `[BOOL_T]` are both wired into a node which requires its inputs to have - /// requirements `[A,BOOL_T]`. A slightly more complex test of the error from - /// `missing_lift_node`. - fn extensions_mismatch() -> Result<(), BuildError> { - let mut module_builder = ModuleBuilder::new(); - - let all_rs = ExtensionSet::from_iter([XA, XB]); - - let main_sig = FunctionType::new(type_row![NAT], type_row![NAT]) - .with_extension_delta(all_rs.clone()) - .into(); - - let mut main = module_builder.define_function("main", main_sig)?; - - let [inp_wire] = main.input_wires_arr(); - - let [left_wire] = main - .dfg_builder( - FunctionType::new(type_row![], type_row![NAT]), - Some(XA.into()), - [], - )? - .finish_with_outputs([inp_wire])? - .outputs_arr(); - - let [right_wire] = main - .dfg_builder( - FunctionType::new(type_row![], type_row![NAT]), - Some(XB.into()), - [], - )? - .finish_with_outputs([inp_wire])? - .outputs_arr(); - - let builder = main.dfg_builder( - FunctionType::new(type_row![NAT, NAT], type_row![NAT]), - Some(all_rs), - [left_wire, right_wire], - )?; - let [left, _] = builder.input_wires_arr(); - let [output] = builder.finish_with_outputs([left])?.outputs_arr(); - - main.finish_with_outputs([output])?; - let handle = module_builder.hugr().validate(&PRELUDE_REGISTRY); - assert_matches!( - handle, - Err(ValidationError::ExtensionError( - ExtensionError::TgtExceedsSrcExtensionsAtPort { .. } - )) - ); - Ok(()) - } - - #[test] - fn parent_signature_mismatch() { - let main_signature = - FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(XA); - - let mut hugr = Hugr::new(NodeType::new_pure(ops::DFG { - signature: main_signature, - })); - let input = hugr.add_node_with_parent( - hugr.root(), - NodeType::new_pure(ops::Input { - types: type_row![NAT], - }), - ); - let output = hugr.add_node_with_parent( - hugr.root(), - NodeType::new( - ops::Output { - types: type_row![NAT], - }, - Some(XA.into()), - ), - ); - hugr.connect(input, 0, output, 0); - - assert_matches!( - hugr.validate(&PRELUDE_REGISTRY), - Err(ValidationError::ExtensionError( - ExtensionError::TgtExceedsSrcExtensionsAtPort { .. } - )) + Err(ValidationError::ExtensionError { + parent: hugr.root(), + parent_extensions: ExtensionSet::new(), + child: lift, + child_extensions: XB.into() + }) ); } } From 07c09cd74f3a26897bf66cd35c69a07a16657430 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 14:58:17 +0100 Subject: [PATCH 05/39] Fix DataflowBlock::inner_signature (fixes 2*cfg tests in infer.rs) --- hugr-core/src/ops/controlflow.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index c09333ee3..2aba3667a 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -159,6 +159,7 @@ impl DataflowParent for DataflowBlock { let mut node_outputs = vec![sum_type]; node_outputs.extend_from_slice(&self.other_outputs); FunctionType::new(self.inputs.clone(), TypeRow::from(node_outputs)) + .with_extension_delta(self.extension_delta.clone()) } } From 08bb5a59caeba3c6d30a970043a952e8291b6dd4 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:15:42 +0100 Subject: [PATCH 06/39] Fix tail_loop tests by adding extension_delta to TailLoop and impl DataflowParent --- hugr-core/src/builder/build_traits.rs | 2 ++ hugr-core/src/builder/tail_loop.rs | 17 ++++++++++++----- hugr-core/src/ops.rs | 1 - hugr-core/src/ops/controlflow.rs | 11 ++++++++++- hugr-core/src/ops/validate.rs | 25 ------------------------- 5 files changed, 24 insertions(+), 32 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index 9703d65c3..9279a6ba5 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -429,6 +429,7 @@ pub trait Dataflow: Container { just_inputs: impl IntoIterator, inputs_outputs: impl IntoIterator, just_out_types: TypeRow, + extension_delta: ExtensionSet, ) -> Result, BuildError> { let (input_types, mut input_wires): (Vec, Vec) = just_inputs.into_iter().unzip(); @@ -440,6 +441,7 @@ pub trait Dataflow: Container { just_inputs: input_types.into(), just_outputs: just_out_types, rest: rest_types.into(), + extension_delta, }; // TODO: Make input extensions a parameter let (loop_node, _) = add_node_with_wires(self, tail_loop.clone(), input_wires)?; diff --git a/hugr-core/src/builder/tail_loop.rs b/hugr-core/src/builder/tail_loop.rs index 901324fe9..3f41574b5 100644 --- a/hugr-core/src/builder/tail_loop.rs +++ b/hugr-core/src/builder/tail_loop.rs @@ -1,3 +1,4 @@ +use crate::extension::ExtensionSet; use crate::ops; use crate::hugr::{views::HugrView, NodeType}; @@ -74,11 +75,13 @@ impl TailLoopBuilder { just_inputs: impl Into, inputs_outputs: impl Into, just_outputs: impl Into, + extension_delta: ExtensionSet, ) -> Result { let tail_loop = ops::TailLoop { just_inputs: just_inputs.into(), just_outputs: just_outputs.into(), rest: inputs_outputs.into(), + extension_delta, }; // TODO: Allow input extensions to be specified let base = Hugr::new(NodeType::new_open(tail_loop.clone())); @@ -97,7 +100,6 @@ mod test { DataflowSubContainer, HugrBuilder, ModuleBuilder, }, extension::prelude::{ConstUsize, PRELUDE_ID, USIZE_T}, - extension::ExtensionSet, hugr::ValidationError, ops::Value, type_row, @@ -107,7 +109,8 @@ mod test { #[test] fn basic_loop() -> Result<(), BuildError> { let build_result: Result = { - let mut loop_b = TailLoopBuilder::new(vec![], vec![BIT], vec![USIZE_T])?; + let mut loop_b = + TailLoopBuilder::new(vec![], vec![BIT], vec![USIZE_T], PRELUDE_ID.into())?; let [i1] = loop_b.input_wires_arr(); let const_wire = loop_b.add_load_value(ConstUsize::new(1)); @@ -141,8 +144,12 @@ mod test { )? .outputs_arr(); let loop_id = { - let mut loop_b = - fbuild.tail_loop_builder(vec![(BIT, b1)], vec![], type_row![NAT])?; + let mut loop_b = fbuild.tail_loop_builder( + vec![(BIT, b1)], + vec![], + type_row![NAT], + PRELUDE_ID.into(), + )?; let signature = loop_b.loop_signature()?.clone(); let const_val = Value::true_val(); let const_wire = loop_b.add_load_const(Value::true_val()); @@ -161,7 +168,7 @@ mod test { ([type_row![], type_row![]], const_wire), vec![(BIT, b1)], output_row, - ExtensionSet::new(), + PRELUDE_ID.into(), )?; let mut branch_0 = conditional_b.case_builder(0)?; diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 2e48d1f9d..76f3e54a4 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -432,7 +432,6 @@ impl OpParent for MakeTuple {} impl OpParent for UnpackTuple {} impl OpParent for Tag {} impl OpParent for Lift {} -impl OpParent for TailLoop {} impl OpParent for CFG {} impl OpParent for Conditional {} impl OpParent for FuncDecl {} diff --git a/hugr-core/src/ops/controlflow.rs b/hugr-core/src/ops/controlflow.rs index 2aba3667a..b1a6b0e37 100644 --- a/hugr-core/src/ops/controlflow.rs +++ b/hugr-core/src/ops/controlflow.rs @@ -18,6 +18,8 @@ pub struct TailLoop { pub just_outputs: TypeRow, /// Types that are appended to both input and output pub rest: TypeRow, + /// Extension requirements to execute the body + pub extension_delta: ExtensionSet, } impl_op_name!(TailLoop); @@ -32,7 +34,7 @@ impl DataflowOpTrait for TailLoop { fn signature(&self) -> FunctionType { let [inputs, outputs] = [&self.just_inputs, &self.just_outputs].map(|row| row.extend(self.rest.iter())); - FunctionType::new(inputs, outputs) + FunctionType::new(inputs, outputs).with_extension_delta(self.extension_delta.clone()) } } @@ -51,6 +53,13 @@ impl TailLoop { } } +impl DataflowParent for TailLoop { + fn inner_signature(&self) -> FunctionType { + FunctionType::new(self.body_input_row(), self.body_output_row()) + .with_extension_delta(self.extension_delta.clone()) + } +} + /// Conditional operation, defined by child `Case` nodes for each branch. #[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)] #[cfg_attr(test, derive(proptest_derive::Arbitrary))] diff --git a/hugr-core/src/ops/validate.rs b/hugr-core/src/ops/validate.rs index c87034d60..aaee46049 100644 --- a/hugr-core/src/ops/validate.rs +++ b/hugr-core/src/ops/validate.rs @@ -106,31 +106,6 @@ impl ValidateOp for super::Conditional { } } -impl ValidateOp for super::TailLoop { - fn validity_flags(&self) -> OpValidityFlags { - OpValidityFlags { - allowed_children: OpTag::DataflowChild, - allowed_first_child: OpTag::Input, - allowed_second_child: OpTag::Output, - requires_children: true, - requires_dag: true, - ..Default::default() - } - } - - fn validate_op_children<'a>( - &self, - children: impl DoubleEndedIterator, - ) -> Result<(), ChildrenValidationError> { - validate_io_nodes( - &self.body_input_row(), - &self.body_output_row(), - "tail-controlled loop graph", - children, - ) - } -} - impl ValidateOp for super::CFG { fn validity_flags(&self) -> OpValidityFlags { OpValidityFlags { From d4e92d8e0891b7c71778e8a64b206e6fc1c19f8b Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:31:33 +0100 Subject: [PATCH 07/39] Fix/regenerate schema for TailLoop --- hugr-py/src/hugr/serialization/ops.py | 1 + specification/schema/hugr_schema_strict_v1.json | 7 +++++++ specification/schema/hugr_schema_v1.json | 7 +++++++ specification/schema/testing_hugr_schema_strict_v1.json | 7 +++++++ specification/schema/testing_hugr_schema_v1.json | 7 +++++++ 5 files changed, 29 insertions(+) diff --git a/hugr-py/src/hugr/serialization/ops.py b/hugr-py/src/hugr/serialization/ops.py index af127bfbd..e8cf88db3 100644 --- a/hugr-py/src/hugr/serialization/ops.py +++ b/hugr-py/src/hugr/serialization/ops.py @@ -348,6 +348,7 @@ class TailLoop(DataflowOp): just_outputs: TypeRow = Field(default_factory=list) # Types that are only output # Types that are appended to both input and output: rest: TypeRow = Field(default_factory=list) + extension_delta: ExtensionSet = Field(default_factory=list) def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: assert in_types == out_types diff --git a/specification/schema/hugr_schema_strict_v1.json b/specification/schema/hugr_schema_strict_v1.json index d0c5aa92e..08f948996 100644 --- a/specification/schema/hugr_schema_strict_v1.json +++ b/specification/schema/hugr_schema_strict_v1.json @@ -1870,6 +1870,13 @@ }, "title": "Rest", "type": "array" + }, + "extension_delta": { + "items": { + "type": "string" + }, + "title": "Extension Delta", + "type": "array" } }, "required": [ diff --git a/specification/schema/hugr_schema_v1.json b/specification/schema/hugr_schema_v1.json index d5dab6428..8399c4feb 100644 --- a/specification/schema/hugr_schema_v1.json +++ b/specification/schema/hugr_schema_v1.json @@ -1870,6 +1870,13 @@ }, "title": "Rest", "type": "array" + }, + "extension_delta": { + "items": { + "type": "string" + }, + "title": "Extension Delta", + "type": "array" } }, "required": [ diff --git a/specification/schema/testing_hugr_schema_strict_v1.json b/specification/schema/testing_hugr_schema_strict_v1.json index 9c272a944..567c481dd 100644 --- a/specification/schema/testing_hugr_schema_strict_v1.json +++ b/specification/schema/testing_hugr_schema_strict_v1.json @@ -1947,6 +1947,13 @@ }, "title": "Rest", "type": "array" + }, + "extension_delta": { + "items": { + "type": "string" + }, + "title": "Extension Delta", + "type": "array" } }, "required": [ diff --git a/specification/schema/testing_hugr_schema_v1.json b/specification/schema/testing_hugr_schema_v1.json index 01c3d6bb8..47ed10526 100644 --- a/specification/schema/testing_hugr_schema_v1.json +++ b/specification/schema/testing_hugr_schema_v1.json @@ -1947,6 +1947,13 @@ }, "title": "Rest", "type": "array" + }, + "extension_delta": { + "items": { + "type": "string" + }, + "title": "Extension Delta", + "type": "array" } }, "required": [ From 968cc159edd5a3b44b0c7aad8537f4308a74ca25 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 14:58:47 +0100 Subject: [PATCH 08/39] Fix constant.rs test_sum --- hugr-core/src/ops/constant.rs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hugr-core/src/ops/constant.rs b/hugr-core/src/ops/constant.rs index 5f059f4f0..ea55908fa 100644 --- a/hugr-core/src/ops/constant.rs +++ b/hugr-core/src/ops/constant.rs @@ -433,6 +433,7 @@ pub type ValueNameRef = str; mod test { use super::Value; use crate::builder::test::simple_dfg_hugr; + use crate::extension::prelude::PRELUDE_ID; use crate::std_extensions::arithmetic::int_types::ConstInt; use crate::{ builder::{BuildError, DFGBuilder, Dataflow, DataflowHugr}, @@ -492,10 +493,13 @@ mod test { let pred_rows = vec![type_row![USIZE_T, FLOAT64_TYPE], Type::EMPTY_TYPEROW]; let pred_ty = SumType::new(pred_rows.clone()); - let mut b = DFGBuilder::new(FunctionType::new( - type_row![], - TypeRow::from(vec![pred_ty.clone().into()]), - ))?; + let mut b = DFGBuilder::new( + FunctionType::new(type_row![], TypeRow::from(vec![pred_ty.clone().into()])) + .with_extension_delta(ExtensionSet::from_iter([ + float_types::EXTENSION_ID, + PRELUDE_ID, + ])), + )?; let c = b.add_constant(Value::sum( 0, [ From e74452716174a38e5679191c51941d04a8f8d662 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:22:46 +0100 Subject: [PATCH 09/39] Fix prelude.rs tests --- hugr-core/src/extension/prelude.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/hugr-core/src/extension/prelude.rs b/hugr-core/src/extension/prelude.rs index 89719a6c0..a3d8176aa 100644 --- a/hugr-core/src/extension/prelude.rs +++ b/hugr-core/src/extension/prelude.rs @@ -396,7 +396,9 @@ mod test { assert!(error_val.equal_consts(&ConstError::new(2, "my message"))); assert!(!error_val.equal_consts(&ConstError::new(3, "my message"))); - let mut b = DFGBuilder::new(FunctionType::new_endo(type_row![])).unwrap(); + let mut b = + DFGBuilder::new(FunctionType::new_endo(type_row![]).with_extension_delta(PRELUDE_ID)) + .unwrap(); let err = b.add_load_value(error_val); @@ -430,7 +432,10 @@ mod test { ) .unwrap(); - let mut b = DFGBuilder::new(FunctionType::new_endo(type_row![QB_T, QB_T])).unwrap(); + let mut b = DFGBuilder::new( + FunctionType::new_endo(type_row![QB_T, QB_T]).with_extension_delta(PRELUDE_ID), + ) + .unwrap(); let [q0, q1] = b.input_wires_arr(); let [q0, q1] = b .add_dataflow_op(cx_gate(), [q0, q1]) @@ -468,7 +473,9 @@ mod test { #[test] /// Test print operation fn test_print() { - let mut b: DFGBuilder = DFGBuilder::new(FunctionType::new(vec![], vec![])).unwrap(); + let mut b: DFGBuilder = + DFGBuilder::new(FunctionType::new_endo(vec![]).with_extension_delta(PRELUDE_ID)) + .unwrap(); let greeting: ConstString = ConstString::new("Hello, world!".into()); let greeting_out: Wire = b.add_load_value(greeting); let print_op = PRELUDE From 7aa06b16da7db9cf2b00f6430ea24bf6d95e2eb9 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:26:12 +0100 Subject: [PATCH 10/39] Fix constant.rs test --- hugr-core/src/hugr/serialize/test.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index f50556498..fdd58e398 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -11,7 +11,7 @@ use crate::ops::custom::{ExtensionOp, OpaqueOp}; use crate::ops::{self, dataflow::IOTrait, Input, Module, Noop, Output, Value, DFG}; use crate::std_extensions::arithmetic::float_types::FLOAT64_TYPE; use crate::std_extensions::arithmetic::int_ops::INT_OPS_REGISTRY; -use crate::std_extensions::arithmetic::int_types::{int_custom_type, ConstInt, INT_TYPES}; +use crate::std_extensions::arithmetic::int_types::{self, int_custom_type, ConstInt, INT_TYPES}; use crate::std_extensions::logic::NotOp; use crate::types::{ type_param::TypeParam, FunctionType, PolyFuncType, SumType, Type, TypeArg, TypeBound, @@ -351,8 +351,11 @@ fn hierarchy_order() -> Result<(), Box> { #[test] fn constants_roundtrip() -> Result<(), Box> { - let mut builder = - DFGBuilder::new(FunctionType::new(vec![], vec![INT_TYPES[4].clone()])).unwrap(); + let mut builder = DFGBuilder::new( + FunctionType::new(vec![], vec![INT_TYPES[4].clone()]) + .with_extension_delta(int_types::EXTENSION_ID), + ) + .unwrap(); let w = builder.add_load_value(ConstInt::new_s(4, -2).unwrap()); let hugr = builder.finish_hugr_with_outputs([w], &INT_OPS_REGISTRY)?; From fcd10c17cbce416b9e347c5b0edf3064b69088a9 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:37:24 +0100 Subject: [PATCH 11/39] Fix merge_bbs tests --- hugr-passes/src/merge_bbs.rs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/hugr-passes/src/merge_bbs.rs b/hugr-passes/src/merge_bbs.rs index 7d1cf9d52..399e86055 100644 --- a/hugr-passes/src/merge_bbs.rs +++ b/hugr-passes/src/merge_bbs.rs @@ -227,14 +227,14 @@ mod test { FunctionType::new(loop_variants.clone(), exit_types.clone()) .with_extension_delta(ExtensionSet::singleton(&PRELUDE_ID)), )?; - let mut no_b1 = h.simple_entry_builder(loop_variants.clone(), 1, ExtensionSet::new())?; + let mut no_b1 = h.simple_entry_builder(loop_variants.clone(), 1, PRELUDE_ID.into())?; let n = no_b1.add_dataflow_op(Noop::new(QB_T), no_b1.input_wires())?; let br = lifted_unary_unit_sum(&mut no_b1); let no_b1 = no_b1.finish_with_outputs(br, n.outputs())?; let mut test_block = h.block_builder( loop_variants.clone(), vec![loop_variants.clone(), exit_types], - ExtensionSet::singleton(&PRELUDE_ID), + PRELUDE_ID.into(), type_row![], )?; let [test_input] = test_block.input_wires_arr(); @@ -246,7 +246,10 @@ mod test { let loop_backedge_target = if self_loop { no_b1 } else { - let mut no_b2 = h.simple_block_builder(FunctionType::new_endo(loop_variants), 1)?; + let mut no_b2 = h.simple_block_builder( + FunctionType::new_endo(loop_variants).with_extension_delta(PRELUDE_ID), + 1, + )?; let n = no_b2.add_dataflow_op(Noop::new(QB_T), no_b2.input_wires())?; let br = lifted_unary_unit_sum(&mut no_b2); let nid = no_b2.finish_with_outputs(br, n.outputs())?; @@ -339,7 +342,7 @@ mod test { let mut bb2 = h.block_builder( type_row![USIZE_T, QB_T], vec![type_row![]], - ExtensionSet::new(), + PRELUDE_ID.into(), type_row![QB_T, USIZE_T], )?; let [u, q] = bb2.input_wires_arr(); @@ -349,7 +352,7 @@ mod test { let mut bb3 = h.block_builder( type_row![QB_T, USIZE_T], vec![type_row![]], - ExtensionSet::new(), + PRELUDE_ID.into(), res_t.clone().into(), )?; let [q, u] = bb3.input_wires_arr(); From 664112f2959869006edf9cb84cbe6ac21c46f4e2 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 15:49:02 +0100 Subject: [PATCH 12/39] Calculate delta in const_fold --- hugr-passes/src/const_fold.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hugr-passes/src/const_fold.rs b/hugr-passes/src/const_fold.rs index bead78c50..8235ecb3b 100644 --- a/hugr-passes/src/const_fold.rs +++ b/hugr-passes/src/const_fold.rs @@ -2,6 +2,7 @@ use std::collections::{BTreeSet, HashMap}; +use hugr_core::extension::ExtensionSet; use itertools::Itertools; use thiserror::Error; @@ -82,7 +83,10 @@ pub fn fold_leaf_op(op: &OpType, consts: &[(IncomingPort, Value)]) -> ConstFoldR /// against `reg`. fn const_graph(consts: Vec, reg: &ExtensionRegistry) -> Hugr { let const_types = consts.iter().map(Value::get_type).collect_vec(); - let mut b = DFGBuilder::new(FunctionType::new(type_row![], const_types)).unwrap(); + let exts = ExtensionSet::union_over(consts.iter().map(Value::extension_reqs)); + let mut b = + DFGBuilder::new(FunctionType::new(type_row![], const_types).with_extension_delta(exts)) + .unwrap(); let outputs = consts .into_iter() From fffacf902d8b4d7964c1acc1bdeb253259983630 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 16:23:32 +0100 Subject: [PATCH 13/39] const_fold/tests.rs via helper functions --- hugr-passes/src/const_fold/test.rs | 168 +++++++++++------------------ 1 file changed, 60 insertions(+), 108 deletions(-) diff --git a/hugr-passes/src/const_fold/test.rs b/hugr-passes/src/const_fold/test.rs index 3f5eb2ba4..ef3b82a66 100644 --- a/hugr-passes/src/const_fold/test.rs +++ b/hugr-passes/src/const_fold/test.rs @@ -3,9 +3,9 @@ use hugr_core::builder::{DFGBuilder, Dataflow, DataflowHugr}; use hugr_core::extension::prelude::{sum_with_error, ConstError, ConstString, BOOL_T, STRING_TYPE}; use hugr_core::extension::{ExtensionRegistry, PRELUDE}; use hugr_core::ops::Value; -use hugr_core::std_extensions::arithmetic; use hugr_core::std_extensions::arithmetic::int_ops::IntOpDef; use hugr_core::std_extensions::arithmetic::int_types::{ConstInt, INT_TYPES}; +use hugr_core::std_extensions::arithmetic::{self, float_types, int_types}; use hugr_core::std_extensions::logic::{self, NaryLogic, NotOp}; use hugr_core::type_row; use hugr_core::types::{FunctionType, Type, TypeRow}; @@ -72,6 +72,11 @@ fn test_add(#[case] a: f64, #[case] b: f64, #[case] c: f64) { assert_eq!(outs.as_slice(), &[(0.into(), c)]); } + +fn float_fn(inputs: impl Into, outputs: impl Into) -> FunctionType { + FunctionType::new(inputs, outputs).with_extension_delta(float_types::EXTENSION_ID) +} + #[test] fn test_big() { /* @@ -80,11 +85,7 @@ fn test_big() { int(x.0 - x.1) == 2 */ let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(float_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let tup = build.add_load_const(Value::tuple([f2c(5.6), f2c(3.2)])); @@ -273,7 +274,7 @@ fn test_folding_pass_issue_996() { // x6 := flt(x0, x5) // false // x7 := or(x4, x6) // true // output x7 - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(float_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(3.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(4.0))); let x2 = build.add_dataflow_op(FloatOps::fne, [x0, x1]).unwrap(); @@ -313,7 +314,7 @@ fn test_const_fold_to_nonfinite() { .unwrap(); // HUGR computing 1.0 / 1.0 - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![FLOAT64_TYPE])).unwrap(); + let mut build = DFGBuilder::new(float_fn(type_row![], vec![FLOAT64_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x2 = build.add_dataflow_op(FloatOps::fdiv, [x0, x1]).unwrap(); @@ -325,7 +326,7 @@ fn test_const_fold_to_nonfinite() { assert_eq!(h0.node_count(), 5); // HUGR computing 1.0 / 0.0 - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![FLOAT64_TYPE])).unwrap(); + let mut build = DFGBuilder::new(float_fn(type_row![], vec![FLOAT64_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(0.0))); let x2 = build.add_dataflow_op(FloatOps::fdiv, [x0, x1]).unwrap(); @@ -334,6 +335,10 @@ fn test_const_fold_to_nonfinite() { assert_eq!(h1.node_count(), 8); } +fn int_fn(inputs: impl Into, outputs: impl Into) -> FunctionType { + FunctionType::new(inputs, outputs).with_extension_delta(int_types::EXTENSION_ID) +} + #[test] fn test_fold_iwiden_u() { // pseudocode: @@ -341,8 +346,7 @@ fn test_fold_iwiden_u() { // x0 := int_u<4>(13); // x1 := iwiden_u<4, 5>(x0); // output x1 == int_u<5>(13); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(4, 13).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::iwiden_u.with_two_log_widths(4, 5), [x0]) @@ -365,8 +369,7 @@ fn test_fold_iwiden_s() { // x0 := int_u<4>(-3); // x1 := iwiden_u<4, 5>(x0); // output x1 == int_s<5>(-3); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(4, -3).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::iwiden_s.with_two_log_widths(4, 5), [x0]) @@ -411,11 +414,7 @@ fn test_fold_inarrow, E: std::fmt::Debug>( // succeeds => whether to expect a int variant or an error // variant. let sum_type = sum_with_error(INT_TYPES[to_log_width as usize].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(mk_const(from_log_width, val).unwrap().into()); let x1 = build .add_dataflow_op( @@ -452,7 +451,7 @@ fn test_fold_itobool() { // x0 := int_u<0>(1); // x1 := itobool(x0); // output x1 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(0, 1).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itobool.without_log_width(), [x0]) @@ -498,7 +497,7 @@ fn test_fold_ieq() { // x0, x1 := int_s<3>(-1), int_u<3>(255) // x2 := ieq(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(3, -1).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 255).unwrap())); let x2 = build @@ -521,7 +520,7 @@ fn test_fold_ine() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ine(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -544,7 +543,7 @@ fn test_fold_ilt_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -567,7 +566,7 @@ fn test_fold_ilt_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -590,7 +589,7 @@ fn test_fold_igt_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -613,7 +612,7 @@ fn test_fold_igt_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -636,7 +635,7 @@ fn test_fold_ile_u() { // x0, x1 := int_u<5>(3), int_u<5>(3) // x2 := ile_u(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x2 = build @@ -659,7 +658,7 @@ fn test_fold_ile_s() { // x0, x1 := int_s<5>(-4), int_s<5>(-4) // x2 := ile_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -682,7 +681,7 @@ fn test_fold_ige_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -705,7 +704,7 @@ fn test_fold_ige_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -728,8 +727,7 @@ fn test_fold_imax_u() { // x0, x1 := int_u<5>(7), int_u<5>(11); // x2 := imax_u(x0, x1); // output x2 == int_u<5>(11); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 7).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 11).unwrap())); let x2 = build @@ -752,8 +750,7 @@ fn test_fold_imax_s() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := imax_u(x0, x1); // output x2 == int_s<5>(1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -776,8 +773,7 @@ fn test_fold_imin_u() { // x0, x1 := int_u<5>(7), int_u<5>(11); // x2 := imin_u(x0, x1); // output x2 == int_u<5>(7); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 7).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 11).unwrap())); let x2 = build @@ -800,8 +796,7 @@ fn test_fold_imin_s() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := imin_u(x0, x1); // output x2 == int_s<5>(-2); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -824,8 +819,7 @@ fn test_fold_iadd() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := iadd(x0, x1); // output x2 == int_s<5>(-1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -848,8 +842,7 @@ fn test_fold_isub() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := isub(x0, x1); // output x2 == int_s<5>(-3); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -872,8 +865,7 @@ fn test_fold_ineg() { // x0 := int_s<5>(-2); // x1 := ineg(x0); // output x1 == int_s<5>(2); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::ineg.with_log_width(5), [x0]) @@ -895,8 +887,7 @@ fn test_fold_imul() { // x0, x1 := int_s<5>(-2), int_s<5>(7); // x2 := imul(x0, x1); // output x2 == int_s<5>(-14); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 7).unwrap())); let x2 = build @@ -921,11 +912,7 @@ fn test_fold_idivmod_checked_u() { // output x2 == error let intpair: TypeRow = vec![INT_TYPES[5].clone(), INT_TYPES[3].clone()].into(); let sum_type = sum_with_error(Type::new_tuple(intpair)); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -962,8 +949,7 @@ fn test_fold_idivmod_u() { // x4 := iwiden_u<3,5>(x3); // 2 // x5 := iadd<5>(x2, x4); // 8 // output x5 == int_u<5>(8); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let [x2, x3] = build @@ -996,11 +982,7 @@ fn test_fold_idivmod_checked_s() { // output x2 == error let intpair: TypeRow = vec![INT_TYPES[5].clone(), INT_TYPES[3].clone()].into(); let sum_type = sum_with_error(Type::new_tuple(intpair)); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1038,8 +1020,7 @@ fn test_fold_idivmod_checked_s() { #[case(i64::MIN, 1u64 << 63, -1)] // c = a/b + a%b fn test_fold_idivmod_s(#[case] a: i64, #[case] b: u64, #[case] c: i64) { - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[6].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[6].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(6, a).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(6, b).unwrap())); let [x2, x3] = build @@ -1067,11 +1048,7 @@ fn test_fold_idiv_checked_u() { // x2 := idiv_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1103,8 +1080,7 @@ fn test_fold_idiv_u() { // x0, x1 := int_u<5>(20), int_u<3>(3); // x2 := idiv_u(x0, x1); // output x2 == int_u<5>(6); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1128,11 +1104,7 @@ fn test_fold_imod_checked_u() { // x2 := imod_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[3].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1164,8 +1136,7 @@ fn test_fold_imod_u() { // x0, x1 := int_u<5>(20), int_u<3>(3); // x2 := imod_u(x0, x1); // output x2 == int_u<3>(2); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1189,11 +1160,7 @@ fn test_fold_idiv_checked_s() { // x2 := idiv_checked_s(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1225,8 +1192,7 @@ fn test_fold_idiv_s() { // x0, x1 := int_s<5>(-20), int_u<3>(3); // x2 := idiv_s(x0, x1); // output x2 == int_s<5>(-7); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1250,11 +1216,7 @@ fn test_fold_imod_checked_s() { // x2 := imod_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[3].to_owned()); - let mut build = DFGBuilder::new(FunctionType::new( - type_row![], - vec![sum_type.clone().into()], - )) - .unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1286,8 +1248,7 @@ fn test_fold_imod_s() { // x0, x1 := int_s<5>(-20), int_u<3>(3); // x2 := imod_s(x0, x1); // output x2 == int_u<3>(1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1310,8 +1271,7 @@ fn test_fold_iabs() { // x0 := int_s<5>(-2); // x1 := iabs(x0); // output x1 == int_s<5>(2); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::iabs.with_log_width(5), [x0]) @@ -1333,8 +1293,7 @@ fn test_fold_iand() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := iand(x0, x1); // output x2 == int_u<5>(4); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1357,8 +1316,7 @@ fn test_fold_ior() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := ior(x0, x1); // output x2 == int_u<5>(30); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1381,8 +1339,7 @@ fn test_fold_ixor() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := ixor(x0, x1); // output x2 == int_u<5>(26); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1405,8 +1362,7 @@ fn test_fold_inot() { // x0 := int_u<5>(14); // x1 := inot(x0); // output x1 == int_u<5>(17); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::inot.with_log_width(5), [x0]) @@ -1428,8 +1384,7 @@ fn test_fold_ishl() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := ishl(x0, x1); // output x2 == int_u<5>(112); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1452,8 +1407,7 @@ fn test_fold_ishr() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := ishr(x0, x1); // output x2 == int_u<5>(1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1476,8 +1430,7 @@ fn test_fold_irotl() { // x0, x1 := int_u<5>(14), int_u<3>(61); // x2 := irotl(x0, x1); // output x2 == int_u<5>(2^30 + 2^31 + 1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 61).unwrap())); let x2 = build @@ -1500,8 +1453,7 @@ fn test_fold_irotr() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := irotr(x0, x1); // output x2 == int_u<5>(2^30 + 2^31 + 1); - let mut build = - DFGBuilder::new(FunctionType::new(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1524,7 +1476,7 @@ fn test_fold_itostring_u() { // x0 := int_u<5>(17); // x1 := itostring_u(x0); // output x2 := "17"; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![STRING_TYPE])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![STRING_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 17).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itostring_u.with_log_width(5), [x0]) @@ -1546,7 +1498,7 @@ fn test_fold_itostring_s() { // x0 := int_s<5>(-17); // x1 := itostring_s(x0); // output x2 := "-17"; - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![STRING_TYPE])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![STRING_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -17).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itostring_s.with_log_width(5), [x0]) @@ -1575,7 +1527,7 @@ fn test_fold_int_ops() { // x6 := ilt_s(x0, x5) // false // x7 := or(x4, x6) // true // output x7 - let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build From 3367490d1d6bfd4b92cdf15741693aad215c1471 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Thu, 30 May 2024 16:25:26 +0100 Subject: [PATCH 14/39] const_fold/test.rs: drop first param to helper --- hugr-passes/src/const_fold/test.rs | 108 ++++++++++++++--------------- 1 file changed, 54 insertions(+), 54 deletions(-) diff --git a/hugr-passes/src/const_fold/test.rs b/hugr-passes/src/const_fold/test.rs index ef3b82a66..b5c0913bd 100644 --- a/hugr-passes/src/const_fold/test.rs +++ b/hugr-passes/src/const_fold/test.rs @@ -73,8 +73,8 @@ fn test_add(#[case] a: f64, #[case] b: f64, #[case] c: f64) { assert_eq!(outs.as_slice(), &[(0.into(), c)]); } -fn float_fn(inputs: impl Into, outputs: impl Into) -> FunctionType { - FunctionType::new(inputs, outputs).with_extension_delta(float_types::EXTENSION_ID) +fn float_fn(outputs: impl Into) -> FunctionType { + FunctionType::new(type_row![], outputs).with_extension_delta(float_types::EXTENSION_ID) } #[test] @@ -85,7 +85,7 @@ fn test_big() { int(x.0 - x.1) == 2 */ let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(float_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(float_fn(vec![sum_type.clone().into()])).unwrap(); let tup = build.add_load_const(Value::tuple([f2c(5.6), f2c(3.2)])); @@ -274,7 +274,7 @@ fn test_folding_pass_issue_996() { // x6 := flt(x0, x5) // false // x7 := or(x4, x6) // true // output x7 - let mut build = DFGBuilder::new(float_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(float_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(3.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(4.0))); let x2 = build.add_dataflow_op(FloatOps::fne, [x0, x1]).unwrap(); @@ -314,7 +314,7 @@ fn test_const_fold_to_nonfinite() { .unwrap(); // HUGR computing 1.0 / 1.0 - let mut build = DFGBuilder::new(float_fn(type_row![], vec![FLOAT64_TYPE])).unwrap(); + let mut build = DFGBuilder::new(float_fn(vec![FLOAT64_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x2 = build.add_dataflow_op(FloatOps::fdiv, [x0, x1]).unwrap(); @@ -326,7 +326,7 @@ fn test_const_fold_to_nonfinite() { assert_eq!(h0.node_count(), 5); // HUGR computing 1.0 / 0.0 - let mut build = DFGBuilder::new(float_fn(type_row![], vec![FLOAT64_TYPE])).unwrap(); + let mut build = DFGBuilder::new(float_fn(vec![FLOAT64_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstF64::new(1.0))); let x1 = build.add_load_const(Value::extension(ConstF64::new(0.0))); let x2 = build.add_dataflow_op(FloatOps::fdiv, [x0, x1]).unwrap(); @@ -335,8 +335,8 @@ fn test_const_fold_to_nonfinite() { assert_eq!(h1.node_count(), 8); } -fn int_fn(inputs: impl Into, outputs: impl Into) -> FunctionType { - FunctionType::new(inputs, outputs).with_extension_delta(int_types::EXTENSION_ID) +fn int_fn(outputs: impl Into) -> FunctionType { + FunctionType::new(type_row![], outputs).with_extension_delta(int_types::EXTENSION_ID) } #[test] @@ -346,7 +346,7 @@ fn test_fold_iwiden_u() { // x0 := int_u<4>(13); // x1 := iwiden_u<4, 5>(x0); // output x1 == int_u<5>(13); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(4, 13).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::iwiden_u.with_two_log_widths(4, 5), [x0]) @@ -369,7 +369,7 @@ fn test_fold_iwiden_s() { // x0 := int_u<4>(-3); // x1 := iwiden_u<4, 5>(x0); // output x1 == int_s<5>(-3); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(4, -3).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::iwiden_s.with_two_log_widths(4, 5), [x0]) @@ -414,7 +414,7 @@ fn test_fold_inarrow, E: std::fmt::Debug>( // succeeds => whether to expect a int variant or an error // variant. let sum_type = sum_with_error(INT_TYPES[to_log_width as usize].to_owned()); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(mk_const(from_log_width, val).unwrap().into()); let x1 = build .add_dataflow_op( @@ -451,7 +451,7 @@ fn test_fold_itobool() { // x0 := int_u<0>(1); // x1 := itobool(x0); // output x1 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(0, 1).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itobool.without_log_width(), [x0]) @@ -497,7 +497,7 @@ fn test_fold_ieq() { // x0, x1 := int_s<3>(-1), int_u<3>(255) // x2 := ieq(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(3, -1).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 255).unwrap())); let x2 = build @@ -520,7 +520,7 @@ fn test_fold_ine() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ine(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -543,7 +543,7 @@ fn test_fold_ilt_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -566,7 +566,7 @@ fn test_fold_ilt_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -589,7 +589,7 @@ fn test_fold_igt_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -612,7 +612,7 @@ fn test_fold_igt_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -635,7 +635,7 @@ fn test_fold_ile_u() { // x0, x1 := int_u<5>(3), int_u<5>(3) // x2 := ile_u(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x2 = build @@ -658,7 +658,7 @@ fn test_fold_ile_s() { // x0, x1 := int_s<5>(-4), int_s<5>(-4) // x2 := ile_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -681,7 +681,7 @@ fn test_fold_ige_u() { // x0, x1 := int_u<5>(3), int_u<5>(4) // x2 := ilt_u(x0, x1) // output x2 == false; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build @@ -704,7 +704,7 @@ fn test_fold_ige_s() { // x0, x1 := int_s<5>(3), int_s<5>(-4) // x2 := ilt_s(x0, x1) // output x2 == true; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, -4).unwrap())); let x2 = build @@ -727,7 +727,7 @@ fn test_fold_imax_u() { // x0, x1 := int_u<5>(7), int_u<5>(11); // x2 := imax_u(x0, x1); // output x2 == int_u<5>(11); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 7).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 11).unwrap())); let x2 = build @@ -750,7 +750,7 @@ fn test_fold_imax_s() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := imax_u(x0, x1); // output x2 == int_s<5>(1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -773,7 +773,7 @@ fn test_fold_imin_u() { // x0, x1 := int_u<5>(7), int_u<5>(11); // x2 := imin_u(x0, x1); // output x2 == int_u<5>(7); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 7).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 11).unwrap())); let x2 = build @@ -796,7 +796,7 @@ fn test_fold_imin_s() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := imin_u(x0, x1); // output x2 == int_s<5>(-2); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -819,7 +819,7 @@ fn test_fold_iadd() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := iadd(x0, x1); // output x2 == int_s<5>(-1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -842,7 +842,7 @@ fn test_fold_isub() { // x0, x1 := int_s<5>(-2), int_s<5>(1); // x2 := isub(x0, x1); // output x2 == int_s<5>(-3); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 1).unwrap())); let x2 = build @@ -865,7 +865,7 @@ fn test_fold_ineg() { // x0 := int_s<5>(-2); // x1 := ineg(x0); // output x1 == int_s<5>(2); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::ineg.with_log_width(5), [x0]) @@ -887,7 +887,7 @@ fn test_fold_imul() { // x0, x1 := int_s<5>(-2), int_s<5>(7); // x2 := imul(x0, x1); // output x2 == int_s<5>(-14); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_s(5, 7).unwrap())); let x2 = build @@ -912,7 +912,7 @@ fn test_fold_idivmod_checked_u() { // output x2 == error let intpair: TypeRow = vec![INT_TYPES[5].clone(), INT_TYPES[3].clone()].into(); let sum_type = sum_with_error(Type::new_tuple(intpair)); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -949,7 +949,7 @@ fn test_fold_idivmod_u() { // x4 := iwiden_u<3,5>(x3); // 2 // x5 := iadd<5>(x2, x4); // 8 // output x5 == int_u<5>(8); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let [x2, x3] = build @@ -982,7 +982,7 @@ fn test_fold_idivmod_checked_s() { // output x2 == error let intpair: TypeRow = vec![INT_TYPES[5].clone(), INT_TYPES[3].clone()].into(); let sum_type = sum_with_error(Type::new_tuple(intpair)); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1020,7 +1020,7 @@ fn test_fold_idivmod_checked_s() { #[case(i64::MIN, 1u64 << 63, -1)] // c = a/b + a%b fn test_fold_idivmod_s(#[case] a: i64, #[case] b: u64, #[case] c: i64) { - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[6].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[6].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(6, a).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(6, b).unwrap())); let [x2, x3] = build @@ -1048,7 +1048,7 @@ fn test_fold_idiv_checked_u() { // x2 := idiv_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1080,7 +1080,7 @@ fn test_fold_idiv_u() { // x0, x1 := int_u<5>(20), int_u<3>(3); // x2 := idiv_u(x0, x1); // output x2 == int_u<5>(6); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1104,7 +1104,7 @@ fn test_fold_imod_checked_u() { // x2 := imod_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[3].to_owned()); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1136,7 +1136,7 @@ fn test_fold_imod_u() { // x0, x1 := int_u<5>(20), int_u<3>(3); // x2 := imod_u(x0, x1); // output x2 == int_u<3>(2); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[3].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1160,7 +1160,7 @@ fn test_fold_idiv_checked_s() { // x2 := idiv_checked_s(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[5].to_owned()); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1192,7 +1192,7 @@ fn test_fold_idiv_s() { // x0, x1 := int_s<5>(-20), int_u<3>(3); // x2 := idiv_s(x0, x1); // output x2 == int_s<5>(-7); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1216,7 +1216,7 @@ fn test_fold_imod_checked_s() { // x2 := imod_checked_u(x0, x1) // output x2 == error let sum_type = sum_with_error(INT_TYPES[3].to_owned()); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![sum_type.clone().into()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![sum_type.clone().into()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 0).unwrap())); let x2 = build @@ -1248,7 +1248,7 @@ fn test_fold_imod_s() { // x0, x1 := int_s<5>(-20), int_u<3>(3); // x2 := imod_s(x0, x1); // output x2 == int_u<3>(1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[3].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[3].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -20).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1271,7 +1271,7 @@ fn test_fold_iabs() { // x0 := int_s<5>(-2); // x1 := iabs(x0); // output x1 == int_s<5>(2); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -2).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::iabs.with_log_width(5), [x0]) @@ -1293,7 +1293,7 @@ fn test_fold_iand() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := iand(x0, x1); // output x2 == int_u<5>(4); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1316,7 +1316,7 @@ fn test_fold_ior() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := ior(x0, x1); // output x2 == int_u<5>(30); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1339,7 +1339,7 @@ fn test_fold_ixor() { // x0, x1 := int_u<5>(14), int_u<5>(20); // x2 := ixor(x0, x1); // output x2 == int_u<5>(26); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 20).unwrap())); let x2 = build @@ -1362,7 +1362,7 @@ fn test_fold_inot() { // x0 := int_u<5>(14); // x1 := inot(x0); // output x1 == int_u<5>(17); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 14).unwrap())); let x2 = build .add_dataflow_op(IntOpDef::inot.with_log_width(5), [x0]) @@ -1384,7 +1384,7 @@ fn test_fold_ishl() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := ishl(x0, x1); // output x2 == int_u<5>(112); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1407,7 +1407,7 @@ fn test_fold_ishr() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := ishr(x0, x1); // output x2 == int_u<5>(1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1430,7 +1430,7 @@ fn test_fold_irotl() { // x0, x1 := int_u<5>(14), int_u<3>(61); // x2 := irotl(x0, x1); // output x2 == int_u<5>(2^30 + 2^31 + 1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 61).unwrap())); let x2 = build @@ -1453,7 +1453,7 @@ fn test_fold_irotr() { // x0, x1 := int_u<5>(14), int_u<3>(3); // x2 := irotr(x0, x1); // output x2 == int_u<5>(2^30 + 2^31 + 1); - let mut build = DFGBuilder::new(int_fn(type_row![], vec![INT_TYPES[5].clone()])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![INT_TYPES[5].clone()])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, 14).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(3, 3).unwrap())); let x2 = build @@ -1476,7 +1476,7 @@ fn test_fold_itostring_u() { // x0 := int_u<5>(17); // x1 := itostring_u(x0); // output x2 := "17"; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![STRING_TYPE])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![STRING_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 17).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itostring_u.with_log_width(5), [x0]) @@ -1498,7 +1498,7 @@ fn test_fold_itostring_s() { // x0 := int_s<5>(-17); // x1 := itostring_s(x0); // output x2 := "-17"; - let mut build = DFGBuilder::new(int_fn(type_row![], vec![STRING_TYPE])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![STRING_TYPE])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_s(5, -17).unwrap())); let x1 = build .add_dataflow_op(IntOpDef::itostring_s.with_log_width(5), [x0]) @@ -1527,7 +1527,7 @@ fn test_fold_int_ops() { // x6 := ilt_s(x0, x5) // false // x7 := or(x4, x6) // true // output x7 - let mut build = DFGBuilder::new(int_fn(type_row![], vec![BOOL_T])).unwrap(); + let mut build = DFGBuilder::new(int_fn(vec![BOOL_T])).unwrap(); let x0 = build.add_load_const(Value::extension(ConstInt::new_u(5, 3).unwrap())); let x1 = build.add_load_const(Value::extension(ConstInt::new_u(5, 4).unwrap())); let x2 = build From 8a9628f038f297885657699365a34646eca2e5ce Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 01:02:33 +0100 Subject: [PATCH 15/39] Generalize parent_io_mismatch to cover Case, DFG and FuncDefn --- hugr-core/src/hugr/validate/test.rs | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 30692e5ea..fc87ba0cb 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -872,6 +872,7 @@ mod extension_tests { use crate::macros::const_extension_ids; const_extension_ids! { + const XA: ExtensionId = "A"; const XB: ExtensionId = "BOOL_EXT"; } @@ -996,14 +997,21 @@ mod extension_tests { ); } - #[test] - // ALAN TODO parametrize this by some different OpTypes e.g. DFG, Function - fn parent_io_mismatch() { - // The DFG node declares that it has an empty extension delta, - // but it's child graph adds extension "XB", causing a mismatch. - let mut hugr = Hugr::new(NodeType::new_pure(ops::DFG { - signature: FunctionType::new(type_row![USIZE_T], type_row![USIZE_T]), - })); + #[rstest] + #[case::d1(|signature| ops::DFG {signature}.into())] + #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] + #[case::c1(|signature| ops::Case {signature}.into())] + // ALAN TODO TailLoop/BasicBlock a bit harder, need a tag op inside + fn parent_io_mismatch( + #[case] parent_f: impl Fn(FunctionType) -> OpType, + #[values(ExtensionSet::new(), XA.into())] parent_extensions: ExtensionSet, + ) { + // Child graph adds extension "XB", but the parent (in all cases) + // declares a different delta, causing a mismatch. + let parent = parent_f( + FunctionType::new_endo(USIZE_T).with_extension_delta(parent_extensions.clone()), + ); + let mut hugr = Hugr::new(NodeType::new_pure(parent)); let input = hugr.add_node_with_parent( hugr.root(), @@ -1037,7 +1045,7 @@ mod extension_tests { result, Err(ValidationError::ExtensionError { parent: hugr.root(), - parent_extensions: ExtensionSet::new(), + parent_extensions, child: lift, child_extensions: XB.into() }) From a6e6459ca9ba1ee4dbfa74ff5c3b2151e0a532fd Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 01:04:29 +0100 Subject: [PATCH 16/39] Drop funcdefn_signature_mismatch(,2) tests First is subsumed by parent_io_mismatch Second we don't care (delta broader than necessary) --- hugr-core/src/extension/infer/test.rs | 53 --------------------------- 1 file changed, 53 deletions(-) diff --git a/hugr-core/src/extension/infer/test.rs b/hugr-core/src/extension/infer/test.rs index 15376947e..8dc0159b3 100644 --- a/hugr-core/src/extension/infer/test.rs +++ b/hugr-core/src/extension/infer/test.rs @@ -952,56 +952,3 @@ fn simple_funcdefn() -> Result<(), Box> { builder.finish_prelude_hugr()?; Ok(()) } - -#[cfg(feature = "extension_inference")] -#[test] -fn funcdefn_signature_mismatch() -> Result<(), Box> { - let mut builder = ModuleBuilder::new(); - let mut func_builder = builder.define_function( - "F", - FunctionType::new(vec![NAT], vec![NAT]) - .with_extension_delta(A) - .into(), - )?; - - let [w] = func_builder.input_wires_arr(); - let lift = func_builder.add_dataflow_op( - Lift { - type_row: type_row![NAT], - new_extension: B, - }, - [w], - )?; - let [w] = lift.outputs_arr(); - func_builder.finish_with_outputs([w])?; - let result = builder.finish_prelude_hugr(); - assert_matches!( - result, - Err(ValidationError::CantInfer( - InferExtensionError::MismatchedConcreteWithLocations { .. } - )) - ); - Ok(()) -} - -#[cfg(feature = "extension_inference")] -#[test] -// Test that the difference between a FuncDefn's input and output nodes is being -// constrained to be the same as the extension delta in the FuncDefn signature. -// The FuncDefn here is declared to add resource "A", but its body just wires -// the input to the output. -fn funcdefn_signature_mismatch2() -> Result<(), Box> { - let mut builder = ModuleBuilder::new(); - let func_builder = builder.define_function( - "F", - FunctionType::new(vec![NAT], vec![NAT]) - .with_extension_delta(A) - .into(), - )?; - - let [w] = func_builder.input_wires_arr(); - func_builder.finish_with_outputs([w])?; - let result = builder.finish_prelude_hugr(); - assert_matches!(result, Err(ValidationError::CantInfer(..))); - Ok(()) -} From abe27fb85061f52432e96887eee08c72c6e0224d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 01:08:18 +0100 Subject: [PATCH 17/39] Don't call infer_extensions; drop broken tests --- hugr-core/src/extension/infer/test.rs | 163 -------------------------- hugr-core/src/hugr.rs | 46 -------- 2 files changed, 209 deletions(-) diff --git a/hugr-core/src/extension/infer/test.rs b/hugr-core/src/extension/infer/test.rs index 8dc0159b3..581e44a0a 100644 --- a/hugr-core/src/extension/infer/test.rs +++ b/hugr-core/src/extension/infer/test.rs @@ -12,12 +12,6 @@ use crate::macros::const_extension_ids; use crate::ops::custom::OpaqueOp; use crate::ops::{self, dataflow::IOTrait}; use crate::ops::{CustomOp, Lift, OpType}; -#[cfg(feature = "extension_inference")] -use crate::{ - builder::test::closed_dfg_root_hugr, - hugr::validate::ValidationError, - ops::{dataflow::DataflowParent, handle::NodeHandle}, -}; use crate::type_row; use crate::types::{FunctionType, Type, TypeRow}; @@ -149,40 +143,6 @@ fn plus() -> Result<(), InferExtensionError> { Ok(()) } -#[cfg(feature = "extension_inference")] -#[test] -// This generates a solution that causes validation to fail -// because of a missing lift node -fn missing_lift_node() { - let mut hugr = Hugr::new(NodeType::new_pure(ops::DFG { - signature: FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(A), - })); - - let input = hugr.add_node_with_parent( - hugr.root(), - NodeType::new_pure(ops::Input { - types: type_row![NAT], - }), - ); - - let output = hugr.add_node_with_parent( - hugr.root(), - NodeType::new_pure(ops::Output { - types: type_row![NAT], - }), - ); - - hugr.connect(input, 0, output, 0); - - // Fail to catch the actual error because it's a difference between I/O - // nodes and their parents and `report_mismatch` isn't yet smart enough - // to handle that. - assert_matches!( - hugr.update_validate(&PRELUDE_REGISTRY), - Err(ValidationError::CantInfer(_)) - ); -} - #[test] // Tests that we can succeed even when all variables don't have concrete // extension sets, and we have an open variable at the start of the graph. @@ -209,55 +169,6 @@ fn open_variables() -> Result<(), InferExtensionError> { Ok(()) } -#[cfg(feature = "extension_inference")] -#[test] -// Infer the extensions on a child node with no inputs -fn dangling_src() -> Result<(), Box> { - let rs = ExtensionSet::singleton(&"R".try_into().unwrap()); - - let mut hugr = closed_dfg_root_hugr( - FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(rs.clone()), - ); - - let [input, output] = hugr.get_io(hugr.root()).unwrap(); - let add_r_sig = - FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(rs.clone()); - - let add_r = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: add_r_sig, - }, - ); - - // Dangling thingy - let src_sig = FunctionType::new(type_row![], type_row![NAT]); - - let src = hugr.add_node_with_parent(hugr.root(), ops::DFG { signature: src_sig }); - - let mult_sig = FunctionType::new(type_row![NAT, NAT], type_row![NAT]); - // Mult has open extension requirements, which we should solve to be "R" - let mult = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: mult_sig, - }, - ); - - hugr.connect(input, 0, add_r, 0); - hugr.connect(add_r, 0, mult, 0); - hugr.connect(src, 0, mult, 1); - hugr.connect(mult, 0, output, 0); - - hugr.infer_extensions()?; - assert_eq!(hugr.get_nodetype(src.node()).io_extensions().unwrap().1, rs); - assert_eq!( - hugr.get_nodetype(mult.node()).io_extensions().unwrap(), - (rs.clone(), rs) - ); - Ok(()) -} - #[test] fn resolve_test() -> Result<(), InferExtensionError> { let mut ctx = UnificationContext::new(&Hugr::default()); @@ -300,80 +211,6 @@ fn create_with_io( Ok([node, input, output]) } -#[cfg(feature = "extension_inference")] -#[test] -fn test_conditional_inference() -> Result<(), Box> { - fn build_case( - hugr: &mut Hugr, - conditional_node: Node, - op: ops::Case, - first_ext: ExtensionId, - second_ext: ExtensionId, - ) -> Result> { - let [case, case_in, case_out] = - create_with_io(hugr, conditional_node, op.clone(), op.inner_signature())?; - - let lift1 = hugr.add_node_with_parent( - case, - Lift { - type_row: type_row![NAT], - new_extension: first_ext, - }, - ); - - let lift2 = hugr.add_node_with_parent( - case, - Lift { - type_row: type_row![NAT], - new_extension: second_ext, - }, - ); - - hugr.connect(case_in, 0, lift1, 0); - hugr.connect(lift1, 0, lift2, 0); - hugr.connect(lift2, 0, case_out, 0); - - Ok(case) - } - - let sum_rows = vec![type_row![]; 2]; - let rs = ExtensionSet::from_iter([A, B]); - - let inputs = type_row![NAT]; - let outputs = type_row![NAT]; - - let op = ops::Conditional { - sum_rows, - other_inputs: inputs.clone(), - outputs: outputs.clone(), - extension_delta: rs.clone(), - }; - - let mut hugr = Hugr::new(NodeType::new_pure(op)); - let conditional_node = hugr.root(); - - let case_op = ops::Case { - signature: FunctionType::new(inputs, outputs).with_extension_delta(rs), - }; - let case0_node = build_case(&mut hugr, conditional_node, case_op.clone(), A, B)?; - - let case1_node = build_case(&mut hugr, conditional_node, case_op, B, A)?; - - hugr.infer_extensions()?; - - for node in [case0_node, case1_node, conditional_node] { - assert_eq!( - hugr.get_nodetype(node).io_extensions().unwrap().0, - ExtensionSet::new() - ); - assert_eq!( - hugr.get_nodetype(node).io_extensions().unwrap().0, - ExtensionSet::new() - ); - } - Ok(()) -} - #[test] fn extension_adding_sequence() -> Result<(), Box> { let df_sig = FunctionType::new(type_row![NAT], type_row![NAT]); diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index be84d48c3..ffda91d2f 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -24,8 +24,6 @@ use thiserror::Error; pub use self::views::{HugrView, RootTagged}; use crate::core::NodeIndex; -#[cfg(feature = "extension_inference")] -use crate::extension::infer_extensions; use crate::extension::{ExtensionRegistry, ExtensionSet, ExtensionSolution, InferExtensionError}; use crate::ops::custom::resolve_extension_ops; use crate::ops::{OpTag, OpTrait, OpType, DEFAULT_OPTYPE}; @@ -205,7 +203,6 @@ impl Hugr { self.validate_no_extensions(extension_registry)?; #[cfg(feature = "extension_inference")] { - self.infer_extensions()?; self.validate_extensions()?; } Ok(()) @@ -214,11 +211,6 @@ impl Hugr { /// Infer extension requirements and add new information to `op_types` field /// (if the "extension_inference" feature is on; otherwise, do nothing) pub fn infer_extensions(&mut self) -> Result<(), InferExtensionError> { - #[cfg(feature = "extension_inference")] - { - let solution = infer_extensions(self)?; - self.instantiate_extensions(&solution); - } Ok(()) } @@ -351,8 +343,6 @@ pub enum HugrError { #[cfg(test)] mod test { use super::{Hugr, HugrView}; - #[cfg(feature = "extension_inference")] - use std::error::Error; #[test] fn impls_send_and_sync() { @@ -371,40 +361,4 @@ mod test { let hugr = simple_dfg_hugr(); assert_matches!(hugr.get_io(hugr.root()), Some(_)); } - - #[cfg(feature = "extension_inference")] - #[test] - fn extension_instantiation() -> Result<(), Box> { - use crate::builder::test::closed_dfg_root_hugr; - use crate::extension::ExtensionSet; - use crate::hugr::HugrMut; - use crate::ops::Lift; - use crate::type_row; - use crate::types::{FunctionType, Type}; - - const BIT: Type = crate::extension::prelude::USIZE_T; - let r = ExtensionSet::singleton(&"R".try_into().unwrap()); - - let mut hugr = closed_dfg_root_hugr( - FunctionType::new(type_row![BIT], type_row![BIT]).with_extension_delta(r.clone()), - ); - let [input, output] = hugr.get_io(hugr.root()).unwrap(); - let lift = hugr.add_node_with_parent( - hugr.root(), - Lift { - type_row: type_row![BIT], - new_extension: "R".try_into().unwrap(), - }, - ); - hugr.connect(input, 0, lift, 0); - hugr.connect(lift, 0, output, 0); - hugr.infer_extensions()?; - - assert_eq!( - hugr.get_nodetype(lift).input_extensions().unwrap(), - &ExtensionSet::new() - ); - assert_eq!(hugr.get_nodetype(output).input_extensions().unwrap(), &r); - Ok(()) - } } From 87b80d3fa81a4b068b8536396548886a7cfc080d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 01:16:36 +0100 Subject: [PATCH 18/39] Rm infer.rs, infer/test.rs, infer_extensions, instantiate, etc. (keep featflag) --- hugr-core/src/extension.rs | 6 - hugr-core/src/extension/infer.rs | 739 ------------------------ hugr-core/src/extension/infer/test.rs | 791 -------------------------- hugr-core/src/hugr.rs | 24 +- hugr-core/src/hugr/validate.rs | 4 +- hugr-core/src/hugr/validate/test.rs | 2 - 6 files changed, 2 insertions(+), 1564 deletions(-) delete mode 100644 hugr-core/src/extension/infer.rs delete mode 100644 hugr-core/src/extension/infer/test.rs diff --git a/hugr-core/src/extension.rs b/hugr-core/src/extension.rs index b6d6bab14..deecf1cbb 100644 --- a/hugr-core/src/extension.rs +++ b/hugr-core/src/extension.rs @@ -19,12 +19,6 @@ use crate::types::type_param::{TypeArg, TypeArgError, TypeParam}; use crate::types::{check_typevar_decl, CustomType, Substitution, TypeBound, TypeName}; use crate::types::{FunctionType, TypeNameRef}; -#[allow(dead_code)] -mod infer; -#[cfg(feature = "extension_inference")] -pub use infer::infer_extensions; -pub use infer::{ExtensionSolution, InferExtensionError}; - mod op_def; pub use op_def::{ CustomSignatureFunc, CustomValidator, LowerFunc, OpDef, SignatureFromArgs, SignatureFunc, diff --git a/hugr-core/src/extension/infer.rs b/hugr-core/src/extension/infer.rs deleted file mode 100644 index 3310daa6c..000000000 --- a/hugr-core/src/extension/infer.rs +++ /dev/null @@ -1,739 +0,0 @@ -//! Inference for extension requirements on nodes of a hugr. -//! -//! Checks if the extensions requirements have a solution in terms of some -//! number of starting variables, and comes up with concrete solutions when -//! possible. -//! -//! Open extension variables can come from toplevel nodes: notionally "inputs" -//! to the graph where being wired up to a larger hugr would provide the -//! information needed to solve variables. When extension requirements of nodes -//! depend on these open variables, then the validation check for extensions -//! will succeed regardless of what the variable is instantiated to. - -use super::ExtensionSet; -use crate::{ - hugr::views::HugrView, - ops::{OpTag, OpTrait}, - types::EdgeKind, - Direction, Node, -}; - -use petgraph::graph as pg; -use petgraph::{Directed, EdgeType, Undirected}; - -use std::collections::{HashMap, HashSet, VecDeque}; - -use thiserror::Error; - -/// A mapping from nodes on the hugr to extension requirement sets which have -/// been inferred for their inputs. -pub type ExtensionSolution = HashMap; - -/// Infer extensions for a hugr. This is the main API exposed by this module. -pub fn infer_extensions(hugr: &impl HugrView) -> Result { - let mut ctx = UnificationContext::new(hugr); - ctx.main_loop()?; - ctx.instantiate_variables(); - let all_results = ctx.main_loop()?; - let new_results = all_results - .into_iter() - .filter(|(n, _sol)| hugr.get_nodetype(*n).input_extensions().is_none()) - .collect(); - Ok(new_results) -} - -/// Metavariables don't need much -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] -struct Meta(u32); - -#[derive(Clone, Debug, Eq, PartialEq, Hash)] -/// Things we know about metavariables -enum Constraint { - /// A variable has the same value as another variable - Equal(Meta), - /// Variable extends the value of another by a set of extensions - Plus(ExtensionSet, Meta), -} - -#[derive(Debug, Clone, PartialEq, Error)] -#[non_exhaustive] -/// Errors which arise during unification -pub enum InferExtensionError { - #[error("Mismatched extension sets {expected} and {actual}")] - /// We've solved a metavariable, then encountered a constraint - /// that says it should be something other than our solution - MismatchedConcrete { - /// The solution we were trying to insert for this meta - expected: ExtensionSet, - /// The incompatible solution that we found was already there - actual: ExtensionSet, - }, - #[error("Solved extensions {expected} at {expected_loc:?} and {actual} at {actual_loc:?} should be equal.")] - /// A version of the above with info about which nodes failed to unify - MismatchedConcreteWithLocations { - /// Where the solution we want to insert came from - expected_loc: (Node, Direction), - /// The solution we were trying to insert for this meta - expected: ExtensionSet, - /// Which node we're trying to add a solution for - actual_loc: (Node, Direction), - /// The incompatible solution that we found was already there - actual: ExtensionSet, - }, - /// A variable went unsolved that wasn't related to a parameter - #[error("Unsolved variable at location {:?}", location)] - Unsolved { - /// The location on the hugr that's associated to the unsolved meta - location: (Node, Direction), - }, - /// Too many extension requirements coming from src - #[error("Extensions at source node {from:?} ({from_extensions}) exceed those at target {to:?} ({to_extensions})")] - #[allow(missing_docs)] - SrcExceedsTgtExtensions { - from: Node, - from_extensions: ExtensionSet, - to: Node, - to_extensions: ExtensionSet, - }, - /// Missing lift node - #[error("Extensions at target node {to:?} ({to_extensions}) exceed those at source {from:?} ({from_extensions})")] - #[allow(missing_docs)] - TgtExceedsSrcExtensions { - from: Node, - from_extensions: ExtensionSet, - to: Node, - to_extensions: ExtensionSet, - }, -} - -/// A graph of metavariables connected by constraints. -/// The edges represent `Equal` constraints in the undirected graph and `Plus` -/// constraints in the directed case. -struct GraphContainer { - graph: pg::Graph, - node_map: HashMap, -} - -impl GraphContainer { - /// Add a metavariable to the graph as a node and return the `NodeIndex`. - /// If it's already there, just return the existing `NodeIndex` - fn add_or_retrieve(&mut self, m: Meta) -> pg::NodeIndex { - self.node_map.get(&m).cloned().unwrap_or_else(|| { - let ix = self.graph.add_node(m); - self.node_map.insert(m, ix); - ix - }) - } - - /// Create an edge between two nodes on the graph - fn add_edge(&mut self, src: Meta, tgt: Meta) { - let src_ix = self.add_or_retrieve(src); - let tgt_ix = self.add_or_retrieve(tgt); - self.graph.add_edge(src_ix, tgt_ix, ()); - } - - /// Return the strongly connected components of the graph in terms of - /// metavariables. In the undirected case, return the connected components - fn sccs(&self) -> Vec> { - petgraph::algo::tarjan_scc(&self.graph) - .into_iter() - .map(|cc| { - cc.into_iter() - .map(|n| *self.graph.node_weight(n).unwrap()) - .collect() - }) - .collect() - } -} - -impl GraphContainer { - fn new() -> Self { - GraphContainer { - graph: pg::Graph::new_undirected(), - node_map: HashMap::new(), - } - } -} - -impl GraphContainer { - fn new() -> Self { - GraphContainer { - graph: pg::Graph::new(), - node_map: HashMap::new(), - } - } -} - -type EqGraph = GraphContainer; - -/// Our current knowledge about the extensions of the graph -struct UnificationContext { - /// A list of constraints for each metavariable - constraints: HashMap>, - /// A map which says which nodes correspond to which metavariables - extensions: HashMap<(Node, Direction), Meta>, - /// Solutions to metavariables - solved: HashMap, - /// A graph which says which metavariables should be equal - eq_graph: EqGraph, - /// A mapping from metavariables which have been merged, to the meta they've - // been merged to - shunted: HashMap, - /// Variables we're allowed to include in solutionss - variables: HashSet, - /// A name for the next metavariable we create - fresh_name: u32, -} - -/// Invariant: Constraint::Plus always points to a fresh metavariable -impl UnificationContext { - /// Create a new unification context, and populate it with constraints from - /// traversing the hugr which is passed in. - fn new(hugr: &impl HugrView) -> Self { - let mut ctx = Self { - constraints: HashMap::new(), - extensions: HashMap::new(), - solved: HashMap::new(), - eq_graph: EqGraph::new(), - shunted: HashMap::new(), - variables: HashSet::new(), - fresh_name: 0, - }; - ctx.gen_constraints(hugr); - ctx - } - - /// Create a fresh metavariable, and increment `fresh_name` for next time - fn fresh_meta(&mut self) -> Meta { - let fresh = Meta(self.fresh_name); - self.fresh_name += 1; - self.constraints.insert(fresh, HashSet::new()); - fresh - } - - /// Declare a constraint on the metavariable - fn add_constraint(&mut self, m: Meta, c: Constraint) { - self.constraints.entry(m).or_default().insert(c); - } - - /// Declare that a meta has been solved - fn add_solution(&mut self, m: Meta, rs: ExtensionSet) { - let existing_sol = self.solved.insert(m, rs); - debug_assert!(existing_sol.is_none()); - } - - /// If a metavariable has been merged, return the new meta, otherwise return - /// the same meta. - /// - /// This could loop if there were a cycle in the `shunted` list, but there - /// shouldn't be, because we only ever shunt to *new* metas. - fn resolve(&self, m: Meta) -> Meta { - self.shunted.get(&m).cloned().map_or(m, |m| self.resolve(m)) - } - - /// Get the relevant constraints for a metavariable. If it's been merged, - /// get the constraints for the merged metavariable - fn get_constraints(&self, m: &Meta) -> Option<&HashSet> { - self.constraints.get(&self.resolve(*m)) - } - - /// Get the relevant solution for a metavariable. If it's been merged, get - /// the solution for the merged metavariable - fn get_solution(&self, m: &Meta) -> Option<&ExtensionSet> { - self.solved.get(&self.resolve(*m)) - } - - /// Return the metavariable corresponding to the given location on the - /// graph, either by making a new meta, or looking it up - fn make_or_get_meta(&mut self, node: Node, dir: Direction) -> Meta { - if let Some(m) = self.extensions.get(&(node, dir)) { - *m - } else { - let m = self.fresh_meta(); - self.extensions.insert((node, dir), m); - m - } - } - - /// Iterate over the nodes in a hugr and generate unification constraints - fn gen_constraints(&mut self, hugr: &T) - where - T: HugrView, - { - if hugr.root_type().input_extensions().is_none() { - let m_input = self.make_or_get_meta(hugr.root(), Direction::Incoming); - self.variables.insert(m_input); - } - - for node in hugr.nodes() { - let m_input = self.make_or_get_meta(node, Direction::Incoming); - let m_output = self.make_or_get_meta(node, Direction::Outgoing); - - let node_type = hugr.get_nodetype(node); - - // Add constraints for the inputs and outputs of dataflow nodes according - // to the signature of the parent node - if let Some([input, output]) = hugr.get_io(node) { - for dir in Direction::BOTH { - let m_input_node = self.make_or_get_meta(input, dir); - self.add_constraint(m_input_node, Constraint::Equal(m_input)); - let m_output_node = self.make_or_get_meta(output, dir); - // If the parent node is a FuncDefn, it will have no - // op_signature, so the Incoming and Outgoing ports will - // have equal extension requirements. - // The function that it contains, however, may have an - // extension delta, so its output shouldn't be equal to the - // FuncDefn's output. - // - // TODO: Add a constraint that the extensions of the output - // node of a FuncDefn should be those of the input node plus - // the extension delta specified in the function signature. - if node_type.tag() != OpTag::FuncDefn { - self.add_constraint(m_output_node, Constraint::Equal(m_output)); - } - } - } - - if hugr.get_optype(node).tag() == OpTag::Conditional { - for case in hugr.children(node) { - let m_case_in = self.make_or_get_meta(case, Direction::Incoming); - let m_case_out = self.make_or_get_meta(case, Direction::Outgoing); - self.add_constraint(m_case_in, Constraint::Equal(m_input)); - self.add_constraint(m_case_out, Constraint::Equal(m_output)); - } - } - - if node_type.tag() == OpTag::Cfg { - let mut children = hugr.children(node); - let entry = children.next().unwrap(); - let exit = children.next().unwrap(); - let m_entry = self.make_or_get_meta(entry, Direction::Incoming); - let m_exit = self.make_or_get_meta(exit, Direction::Outgoing); - self.add_constraint(m_input, Constraint::Equal(m_entry)); - self.add_constraint(m_output, Constraint::Equal(m_exit)); - } - - match node_type.io_extensions() { - // Input extensions are open - None => { - let delta = node_type.op().extension_delta(); - let c = if delta.is_empty() { - Constraint::Equal(m_input) - } else { - Constraint::Plus(delta, m_input) - }; - self.add_constraint(m_output, c); - } - // We have a solution for everything! - Some((input_exts, output_exts)) => { - self.add_solution(m_input, input_exts.clone()); - self.add_solution(m_output, output_exts); - } - } - } - // Separate loop so that we can assume that a metavariable has been - // added for every (Node, Direction) in the graph already. - for tgt_node in hugr.nodes() { - let sig = hugr.get_nodetype(tgt_node).op(); - // Incoming ports with an edge that should mean equal extension reqs - for port in hugr.node_inputs(tgt_node).filter(|src_port| { - let kind = sig.port_kind(*src_port); - kind.as_ref().is_some_and(EdgeKind::is_static) - || matches!(kind, Some(EdgeKind::Value(_)) | Some(EdgeKind::ControlFlow)) - }) { - let m_tgt = *self - .extensions - .get(&(tgt_node, Direction::Incoming)) - .unwrap(); - for (src_node, _) in hugr.linked_ports(tgt_node, port) { - let m_src = self - .extensions - .get(&(src_node, Direction::Outgoing)) - .unwrap(); - self.add_constraint(*m_src, Constraint::Equal(m_tgt)); - } - } - } - } - - /// When trying to unify two metas, check if they both correspond to - /// different ends of the same wire. If so, return an `ExtensionError`. - /// Otherwise check whether they both correspond to *some* location on the - /// graph and include that info the otherwise generic `MismatchedConcrete`. - fn report_mismatch( - &self, - m1: Meta, - m2: Meta, - rs1: ExtensionSet, - rs2: ExtensionSet, - ) -> InferExtensionError { - let loc1 = self - .extensions - .iter() - .find(|(_, m)| **m == m1 || self.resolve(**m) == m1) - .map(|a| a.0); - let loc2 = self - .extensions - .iter() - .find(|(_, m)| **m == m2 || self.resolve(**m) == m2) - .map(|a| a.0); - if let (Some((node1, dir1)), Some((node2, dir2))) = (loc1, loc2) { - // N.B. We're looking for the case where an equality constraint - // arose because the two locations are connected by an edge - - // If the directions are the same, they shouldn't be connected - // to each other. If the nodes are the same, there's no edge! - // - // TODO: It's still possible that the equality constraint - // arose because one node is a dataflow parent and the other - // is one of it's I/O nodes. In that case, the directions could be - // the same, and we should try to detect it - if dir1 != dir2 && node1 != node2 { - let [(src, src_rs), (tgt, tgt_rs)] = if *dir2 == Direction::Incoming { - [(node1, rs1.clone()), (node2, rs2.clone())] - } else { - [(node2, rs2.clone()), (node1, rs1.clone())] - }; - - return if src_rs.is_subset(&tgt_rs) { - InferExtensionError::TgtExceedsSrcExtensions { - from: *src, - from_extensions: src_rs, - to: *tgt, - to_extensions: tgt_rs, - } - } else { - InferExtensionError::SrcExceedsTgtExtensions { - from: *src, - from_extensions: src_rs, - to: *tgt, - to_extensions: tgt_rs, - } - }; - } - } - if let (Some(loc1), Some(loc2)) = (loc1, loc2) { - InferExtensionError::MismatchedConcreteWithLocations { - expected_loc: *loc1, - expected: rs1, - actual_loc: *loc2, - actual: rs2, - } - } else { - InferExtensionError::MismatchedConcrete { - expected: rs1, - actual: rs2, - } - } - } - - /// Take a group of equal metas and merge them into a new, single meta. - /// - /// Returns the set of new metas created and the set of metas that were - /// merged. - fn merge_equal_metas(&mut self) -> Result<(HashSet, HashSet), InferExtensionError> { - let mut merged: HashSet = HashSet::new(); - let mut new_metas: HashSet = HashSet::new(); - for cc in self.eq_graph.sccs().into_iter() { - // Within a connected component everything is equal - let combined_meta = self.fresh_meta(); - for m in cc.iter() { - // The same meta shouldn't be shunted twice directly. Only - // transitively, as we still process the meta it was shunted to - if self.shunted.contains_key(m) { - continue; - } - - if let Some(cs) = self.constraints.remove(m) { - for c in cs - .iter() - .filter(|c| !matches!(c, Constraint::Equal(_))) - .cloned() - .collect::>() - .into_iter() - { - self.add_constraint(combined_meta, c.clone()); - } - merged.insert(*m); - // Record a new meta the first time that we use it; don't - // bother recording a new meta if we don't add any - // constraints. It should be safe to call this multiple times - new_metas.insert(combined_meta); - } - // Here, solved.get is equivalent to get_solution, because if - // `m` had already been shunted, we wouldn't skipped it - if let Some(solution) = self.solved.get(m) { - match self.solved.get(&combined_meta) { - Some(existing_solution) => { - if solution != existing_solution { - return Err(self.report_mismatch( - *m, - combined_meta, - solution.clone(), - existing_solution.clone(), - )); - } - } - None => { - self.solved.insert(combined_meta, solution.clone()); - } - } - } - if self.variables.contains(m) { - self.variables.insert(combined_meta); - self.variables.remove(m); - } - self.shunted.insert(*m, combined_meta); - } - } - Ok((new_metas, merged)) - } - - /// Inspect the constraints of a given metavariable and try to find a - /// solution based on those. - /// Returns whether a solution was found - fn solve_meta(&mut self, meta: Meta) -> Result { - let mut solved = false; - for c in self.get_constraints(&meta).unwrap().clone().iter() { - match c { - // Just register the equality in the EqGraph, we'll process it later - Constraint::Equal(other_meta) => { - self.eq_graph.add_edge(meta, *other_meta); - } - // N.B. If `meta` is already solved, we can't use that - // information to solve `other_meta`. This is because the Plus - // constraint only signifies a preorder. - // I.e. if meta = other_meta + 'R', it's still possible that the - // solution is meta = other_meta because we could be adding 'R' - // to a set which already contained it. - Constraint::Plus(r, other_meta) => { - if let Some(rs) = self.get_solution(other_meta) { - let rrs = rs.clone().union(r.clone()); - match self.get_solution(&meta) { - // Let's check that this is right? - Some(rs) => { - if rs != &rrs { - return Err(self.report_mismatch( - meta, - *other_meta, - rs.clone(), - rrs, - )); - } - } - None => { - self.add_solution(meta, rrs); - solved = true; - } - }; - }; - } - } - } - Ok(solved) - } - - /// Tries to return concrete extensions for each node in the graph. This only - /// works when there are no variables in the graph! - /// - /// What we really want is to give the concrete extensions where they're - /// available. When there are variables, we should leave the graph as it is, - /// but make sure that no matter what they're instantiated to, the graph - /// still makes sense (should pass the extension validation check) - fn results(&self) -> Result { - // Check that all of the metavariables associated with nodes of the - // graph are solved - let depended_upon = { - let mut h: HashMap> = HashMap::new(); - for (m, m2) in self.constraints.iter().flat_map(|(m, cs)| { - cs.iter().flat_map(|c| match c { - Constraint::Plus(_, m2) => Some((*m, self.resolve(*m2))), - _ => None, - }) - }) { - h.entry(m2).or_default().push(m); - } - h - }; - // Calculate everything dependent upon a variable. - // Note it would be better to find metas ALL of whose dependencies were (transitively) - // on variables, but this is more complex, and hard to define if there are cycles - // of PLUS constraints, so leaving that as a TODO until we've handled such cycles. - let mut depends_on_var = HashSet::new(); - let mut queue = VecDeque::from_iter(self.variables.iter()); - while let Some(m) = queue.pop_front() { - if depends_on_var.insert(m) { - if let Some(d) = depended_upon.get(m) { - queue.extend(d.iter()) - } - } - } - - let mut results: ExtensionSolution = HashMap::new(); - for (loc, meta) in self.extensions.iter() { - if let Some(rs) = self.get_solution(meta) { - if loc.1 == Direction::Incoming { - results.insert(loc.0, rs.clone()); - } - } else { - // Unsolved nodes must be unsolved because they depend on graph variables. - if !depends_on_var.contains(&self.resolve(*meta)) { - return Err(InferExtensionError::Unsolved { location: *loc }); - } - } - } - Ok(results) - } - - /// Iterates over a set of metas (the argument) and tries to solve - /// them. - /// Returns the metas that we solved - fn solve_constraints( - &mut self, - vars: &HashSet, - ) -> Result, InferExtensionError> { - let mut solved = HashSet::new(); - for m in vars.iter() { - if self.solve_meta(*m)? { - solved.insert(*m); - } - } - Ok(solved) - } - - /// Once the unification context is set up, attempt to infer ExtensionSets - /// for all of the metavariables in the `UnificationContext`. - /// - /// Return a mapping from locations in the graph to concrete `ExtensionSets` - /// where it was possible to infer them. If it wasn't possible to infer a - /// *concrete* `ExtensionSet`, e.g. if the ExtensionSet relies on an open - /// variable in the toplevel graph, don't include that location in the map - fn main_loop(&mut self) -> Result { - let mut remaining = HashSet::::from_iter(self.constraints.keys().cloned()); - - // Keep going as long as we're making progress (= merging and solving nodes) - loop { - // Try to solve metas with the information we have now. This may - // register new equalities on the EqGraph - let to_delete = self.solve_constraints(&remaining)?; - // Merge metas based on the equalities we just registered - let (new, merged) = self.merge_equal_metas()?; - // All of the metas for which we've made progress - let delta: HashSet = HashSet::from_iter(to_delete.union(&merged).cloned()); - - // Clean up dangling constraints on solved metavariables - to_delete.iter().for_each(|m| { - self.constraints.remove(m); - }); - // Remove solved and merged metas from remaining "to solve" list - delta.iter().for_each(|m| { - remaining.remove(m); - }); - - // If we made no progress, we're done! - if delta.is_empty() && new.is_empty() { - break; - } - remaining.extend(new) - } - self.results() - } - - /// Gather all the transitive dependencies (induced by constraints) of the - /// variables in the context. - fn search_variable_deps(&self) -> HashSet { - let mut seen = HashSet::new(); - let mut new_variables: HashSet = self.variables.clone(); - while !new_variables.is_empty() { - new_variables = new_variables - .into_iter() - .filter(|m| seen.insert(*m)) - .flat_map(|m| self.get_constraints(&m)) - .flatten() - .map(|c| match c { - Constraint::Plus(_, other) => self.resolve(*other), - Constraint::Equal(other) => self.resolve(*other), - }) - .collect(); - } - seen - } - - /// Instantiate all variables in the graph with the empty extension set, or - /// the smallest solution possible given their constraints. - /// This is done to solve metas which depend on variables, which allows - /// us to come up with a fully concrete solution to pass into validation. - /// - /// Nodes which loop into themselves must be considered as a "minimum" set - /// of requirements. If we have - /// 1 = 2 + X, ... - /// 2 = 1 + x, ... - /// then 1 and 2 both definitely contain X, even if we don't know what else. - /// So instead of instantiating to the empty set, we'll instantiate to `{X}` - fn instantiate_variables(&mut self) { - // A directed graph to keep track of `Plus` constraint relationships - let mut relations = GraphContainer::::new(); - let mut solutions: HashMap = HashMap::new(); - - let variable_scope = self.search_variable_deps(); - for m in variable_scope.into_iter() { - // If `m` has been merged, [`self.variables`] entry - // will have already been updated to the merged - // value by [`self.merge_equal_metas`] so we don't - // need to worry about resolving it. - if !self.solved.contains_key(&m) { - // Handle the case where the constraints for `m` contain a self - // reference, i.e. "m = Plus(E, m)", in which case the variable - // should be instantiated to E rather than the empty set. - let plus_constraints = - self.get_constraints(&m) - .unwrap() - .iter() - .cloned() - .flat_map(|c| match c { - Constraint::Plus(r, other_m) => Some((r, self.resolve(other_m))), - _ => None, - }); - - let (rs, other_ms): (Vec<_>, Vec<_>) = plus_constraints.unzip(); - let solution = ExtensionSet::union_over(rs); - let unresolved_metas = other_ms - .into_iter() - .filter(|other_m| m != *other_m) - .collect::>(); - - // If `m` doesn't depend on any other metas then we have all the - // information we need to come up with a solution for it. - relations.add_or_retrieve(m); - unresolved_metas - .iter() - .for_each(|other_m| relations.add_edge(m, *other_m)); - solutions.insert(m, solution); - } - } - - // Process the strongly-connected components. petgraph/sccs() returns these - // depended-upon before dependant, as we need. - for cc in relations.sccs() { - // Strongly connected components are looping constraint dependencies. - // This means that each metavariable in the CC has the same solution. - let combined_solution = cc - .iter() - .flat_map(|m| self.get_constraints(m).unwrap()) - .filter_map(|c| match c { - Constraint::Plus(_, other_m) => solutions.get(&self.resolve(*other_m)).cloned(), - Constraint::Equal(_) => None, - }) - .fold(ExtensionSet::new(), ExtensionSet::union); - - for m in cc.iter() { - self.add_solution(*m, combined_solution.clone()); - solutions.insert(*m, combined_solution.clone()); - } - } - self.variables = HashSet::new(); - } -} - -#[cfg(test)] -mod test; diff --git a/hugr-core/src/extension/infer/test.rs b/hugr-core/src/extension/infer/test.rs deleted file mode 100644 index 581e44a0a..000000000 --- a/hugr-core/src/extension/infer/test.rs +++ /dev/null @@ -1,791 +0,0 @@ -use std::error::Error; - -use super::*; -use crate::builder::{ - Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, HugrBuilder, ModuleBuilder, -}; -use crate::extension::prelude::PRELUDE_REGISTRY; -use crate::extension::prelude::QB_T; -use crate::extension::ExtensionId; -use crate::hugr::{Hugr, HugrMut, NodeType}; -use crate::macros::const_extension_ids; -use crate::ops::custom::OpaqueOp; -use crate::ops::{self, dataflow::IOTrait}; -use crate::ops::{CustomOp, Lift, OpType}; - -use crate::type_row; -use crate::types::{FunctionType, Type, TypeRow}; - -use cool_asserts::assert_matches; -use itertools::Itertools; -use portgraph::NodeIndex; - -const NAT: Type = crate::extension::prelude::USIZE_T; - -const_extension_ids! { - const A: ExtensionId = "A"; - const B: ExtensionId = "B"; - const C: ExtensionId = "C"; - const UNKNOWN_EXTENSION: ExtensionId = "Unknown"; -} - -#[test] -// Build up a graph with some holes in its extension requirements, and infer -// them. -fn from_graph() -> Result<(), Box> { - let rs = ExtensionSet::from_iter([A, B, C]); - let main_sig = FunctionType::new(type_row![NAT, NAT], type_row![NAT]).with_extension_delta(rs); - - let op = ops::DFG { - signature: main_sig, - }; - - let root_node = NodeType::new_open(op); - let mut hugr = Hugr::new(root_node); - - let input = ops::Input::new(type_row![NAT, NAT]); - let output = ops::Output::new(type_row![NAT]); - - let input = hugr.add_node_with_parent(hugr.root(), input); - let output = hugr.add_node_with_parent(hugr.root(), output); - - assert_matches!(hugr.get_io(hugr.root()), Some(_)); - - let add_a_sig = FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(A); - - let add_b_sig = FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(B); - - let add_ab_sig = FunctionType::new(type_row![NAT], type_row![NAT]) - .with_extension_delta(ExtensionSet::from_iter([A, B])); - - let mult_c_sig = FunctionType::new(type_row![NAT, NAT], type_row![NAT]).with_extension_delta(C); - - let add_a = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: add_a_sig, - }, - ); - let add_b = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: add_b_sig, - }, - ); - let add_ab = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: add_ab_sig, - }, - ); - let mult_c = hugr.add_node_with_parent( - hugr.root(), - ops::DFG { - signature: mult_c_sig, - }, - ); - - hugr.connect(input, 0, add_a, 0); - hugr.connect(add_a, 0, add_b, 0); - hugr.connect(add_b, 0, mult_c, 0); - - hugr.connect(input, 1, add_ab, 0); - hugr.connect(add_ab, 0, mult_c, 1); - - hugr.connect(mult_c, 0, output, 0); - - let solution = infer_extensions(&hugr)?; - let empty = ExtensionSet::new(); - let ab = ExtensionSet::from_iter([A, B]); - assert_eq!(*solution.get(&(hugr.root())).unwrap(), empty); - assert_eq!(*solution.get(&(mult_c)).unwrap(), ab); - assert_eq!(*solution.get(&(add_ab)).unwrap(), empty); - assert_eq!(*solution.get(&add_b).unwrap(), ExtensionSet::singleton(&A)); - Ok(()) -} - -#[test] -// Basic test that the `Plus` constraint works -fn plus() -> Result<(), InferExtensionError> { - let hugr = Hugr::default(); - let mut ctx = UnificationContext::new(&hugr); - - let metas: Vec = (2..8) - .map(|i| { - let meta = ctx.fresh_meta(); - ctx.extensions - .insert((NodeIndex::new(i).into(), Direction::Incoming), meta); - meta - }) - .collect(); - - ctx.solved.insert(metas[2], A.into()); - ctx.add_constraint(metas[1], Constraint::Equal(metas[2])); - ctx.add_constraint(metas[0], Constraint::Plus(B.into(), metas[2])); - ctx.add_constraint(metas[4], Constraint::Plus(C.into(), metas[0])); - ctx.add_constraint(metas[3], Constraint::Equal(metas[4])); - ctx.add_constraint(metas[5], Constraint::Equal(metas[0])); - ctx.main_loop()?; - - let a = ExtensionSet::singleton(&A); - let mut ab = a.clone(); - ab.insert(&B); - let mut abc = ab.clone(); - abc.insert(&C); - - assert_eq!(ctx.get_solution(&metas[0]).unwrap(), &ab); - assert_eq!(ctx.get_solution(&metas[1]).unwrap(), &a); - assert_eq!(ctx.get_solution(&metas[2]).unwrap(), &a); - assert_eq!(ctx.get_solution(&metas[3]).unwrap(), &abc); - assert_eq!(ctx.get_solution(&metas[4]).unwrap(), &abc); - assert_eq!(ctx.get_solution(&metas[5]).unwrap(), &ab); - - Ok(()) -} - -#[test] -// Tests that we can succeed even when all variables don't have concrete -// extension sets, and we have an open variable at the start of the graph. -fn open_variables() -> Result<(), InferExtensionError> { - let mut ctx = UnificationContext::new(&Hugr::default()); - let a = ctx.fresh_meta(); - let b = ctx.fresh_meta(); - let ab = ctx.fresh_meta(); - // Some nonsense so that the constraints register as "live" - ctx.extensions - .insert((NodeIndex::new(2).into(), Direction::Outgoing), a); - ctx.extensions - .insert((NodeIndex::new(3).into(), Direction::Outgoing), b); - ctx.extensions - .insert((NodeIndex::new(4).into(), Direction::Incoming), ab); - ctx.variables.insert(a); - ctx.variables.insert(b); - ctx.add_constraint(ab, Constraint::Plus(A.into(), b)); - ctx.add_constraint(ab, Constraint::Plus(B.into(), a)); - let solution = ctx.main_loop()?; - // We'll only find concrete solutions for the Incoming extension reqs of - // the main node created by `Hugr::default` - assert_eq!(solution.len(), 1); - Ok(()) -} - -#[test] -fn resolve_test() -> Result<(), InferExtensionError> { - let mut ctx = UnificationContext::new(&Hugr::default()); - let m0 = ctx.fresh_meta(); - let m1 = ctx.fresh_meta(); - let m2 = ctx.fresh_meta(); - ctx.add_constraint(m0, Constraint::Equal(m1)); - ctx.main_loop()?; - let mid0 = ctx.resolve(m0); - assert_eq!(ctx.resolve(m0), ctx.resolve(m1)); - ctx.add_constraint(mid0, Constraint::Equal(m2)); - ctx.main_loop()?; - assert_eq!(ctx.resolve(m0), ctx.resolve(m2)); - assert_eq!(ctx.resolve(m1), ctx.resolve(m2)); - assert!(ctx.resolve(m0) != mid0); - Ok(()) -} - -fn create_with_io( - hugr: &mut Hugr, - parent: Node, - op: impl Into, - op_sig: FunctionType, -) -> Result<[Node; 3], Box> { - let op: OpType = op.into(); - - let node = hugr.add_node_with_parent(parent, op); - let input = hugr.add_node_with_parent( - node, - ops::Input { - types: op_sig.input, - }, - ); - let output = hugr.add_node_with_parent( - node, - ops::Output { - types: op_sig.output, - }, - ); - Ok([node, input, output]) -} - -#[test] -fn extension_adding_sequence() -> Result<(), Box> { - let df_sig = FunctionType::new(type_row![NAT], type_row![NAT]); - - let mut hugr = Hugr::new(NodeType::new_open(ops::DFG { - signature: df_sig - .clone() - .with_extension_delta(ExtensionSet::from_iter([A, B])), - })); - - let root = hugr.root(); - let input = hugr.add_node_with_parent( - root, - ops::Input { - types: type_row![NAT], - }, - ); - let output = hugr.add_node_with_parent( - root, - ops::Output { - types: type_row![NAT], - }, - ); - - // Make identical dataflow nodes which add extension requirement "A" or "B" - let df_nodes: Vec = vec![A, A, B, B, A, B] - .into_iter() - .map(|ext| { - let dfg_sig = df_sig.clone().with_extension_delta(ext.clone()); - let [node, input, output] = create_with_io( - &mut hugr, - root, - ops::DFG { - signature: dfg_sig.clone(), - }, - dfg_sig, - ) - .unwrap(); - - let lift = hugr.add_node_with_parent( - node, - Lift { - type_row: type_row![NAT], - new_extension: ext, - }, - ); - - hugr.connect(input, 0, lift, 0); - hugr.connect(lift, 0, output, 0); - - node - }) - .collect(); - - // Connect nodes in order (0 -> 1 -> 2 ...) - let nodes = [vec![input], df_nodes, vec![output]].concat(); - for (src, tgt) in nodes.into_iter().tuple_windows() { - hugr.connect(src, 0, tgt, 0); - } - hugr.update_validate(&PRELUDE_REGISTRY)?; - Ok(()) -} - -fn make_opaque(extension: impl Into, signature: FunctionType) -> CustomOp { - ops::custom::OpaqueOp::new(extension.into(), "", "".into(), vec![], signature).into() -} - -fn make_block( - hugr: &mut Hugr, - bb_parent: Node, - inputs: TypeRow, - sum_rows: impl IntoIterator, - extension_delta: ExtensionSet, -) -> Result> { - let sum_rows: Vec<_> = sum_rows.into_iter().collect(); - let sum_type = Type::new_sum(sum_rows.clone()); - let dfb_sig = FunctionType::new(inputs.clone(), vec![sum_type]) - .with_extension_delta(extension_delta.clone()); - let dfb = ops::DataflowBlock { - inputs, - other_outputs: type_row![], - sum_rows, - extension_delta, - }; - let op = make_opaque(UNKNOWN_EXTENSION, dfb_sig.clone()); - - let [bb, bb_in, bb_out] = create_with_io(hugr, bb_parent, dfb, dfb_sig)?; - - let dfg = hugr.add_node_with_parent(bb, op); - - hugr.connect(bb_in, 0, dfg, 0); - hugr.connect(dfg, 0, bb_out, 0); - - Ok(bb) -} - -fn oneway(ty: Type) -> Vec { - vec![Type::new_sum([vec![ty].into()])] -} - -fn twoway(ty: Type) -> Vec { - vec![Type::new_sum([vec![ty.clone()].into(), vec![ty].into()])] -} - -fn create_entry_exit( - hugr: &mut Hugr, - root: Node, - inputs: TypeRow, - entry_variants: Vec, - entry_extensions: ExtensionSet, - exit_types: impl Into, -) -> Result<([Node; 3], Node), Box> { - let entry_sum = Type::new_sum(entry_variants.clone()); - let dfb = ops::DataflowBlock { - inputs: inputs.clone(), - other_outputs: type_row![], - sum_rows: entry_variants, - extension_delta: entry_extensions, - }; - - let exit = hugr.add_node_with_parent( - root, - ops::ExitBlock { - cfg_outputs: exit_types.into(), - }, - ); - - let entry = hugr.add_node_before(exit, dfb); - let entry_in = hugr.add_node_with_parent(entry, ops::Input { types: inputs }); - let entry_out = hugr.add_node_with_parent( - entry, - ops::Output { - types: vec![entry_sum].into(), - }, - ); - - Ok(([entry, entry_in, entry_out], exit)) -} - -/// A CFG rooted hugr adding resources at each basic block. -/// Looks like this: -/// -/// +-------------+ -/// | Entry | -/// | (Adds [A]) | -/// +-/---------\-+ -/// / \ -/// +-------/-----+ +-\-------------+ -/// | BB0 | | BB1 | -/// | (Adds [BC]) | | (Adds [B]) | -/// +----\--------+ +---/------\----+ -/// \ / \ -/// \ / \ -/// \ +----/-------+ +-\---------+ -/// \ | BB10 | | BB11 | -/// \ | (Adds [C]) | | (Adds [C])| -/// \ +----+-------+ +/----------+ -/// \ | / -/// +-----\-------+---------/-+ -/// | Exit | -/// +-------------------------+ -#[test] -fn infer_cfg_test() -> Result<(), Box> { - let abc = ExtensionSet::from_iter([A, B, C]); - let bc = ExtensionSet::from_iter([B, C]); - - let mut hugr = Hugr::new(NodeType::new_open(ops::CFG { - signature: FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(abc), - })); - - let root = hugr.root(); - - let ([entry, entry_in, entry_out], exit) = create_entry_exit( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT], type_row![NAT]], - A.into(), - type_row![NAT], - )?; - - let mkpred = hugr.add_node_with_parent( - entry, - make_opaque( - A, - FunctionType::new(vec![NAT], twoway(NAT)).with_extension_delta(A), - ), - ); - - // Internal wiring for DFGs - hugr.connect(entry_in, 0, mkpred, 0); - hugr.connect(mkpred, 0, entry_out, 0); - - let bb0 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - bc.clone(), - )?; - - let bb1 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT], type_row![NAT]], - B.into(), - )?; - - let bb10 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - C.into(), - )?; - - let bb11 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - C.into(), - )?; - - // CFG Wiring - hugr.connect(entry, 0, bb0, 0); - hugr.connect(entry, 0, bb1, 0); - hugr.connect(bb1, 0, bb10, 0); - hugr.connect(bb1, 0, bb11, 0); - - hugr.connect(bb0, 0, exit, 0); - hugr.connect(bb10, 0, exit, 0); - hugr.connect(bb11, 0, exit, 0); - - hugr.infer_extensions()?; - - Ok(()) -} - -/// A test case for a CFG with a node (BB2) which has multiple predecessors, -/// Like so: -/// -/// +-----------------+ -/// | Entry | -/// +------/--\-------+ -/// / \ -/// / \ -/// / \ -/// +---------/--+ +----\-------+ -/// | BB0 | | BB1 | -/// +--------\---+ +----/-------+ -/// \ / -/// \ / -/// \ / -/// +------\---/--------+ -/// | BB2 | -/// +---------+---------+ -/// | -/// +---------+----------+ -/// | Exit | -/// +--------------------+ -#[test] -fn multi_entry() -> Result<(), Box> { - let mut hugr = Hugr::new(NodeType::new_open(ops::CFG { - signature: FunctionType::new(type_row![NAT], type_row![NAT]), // maybe add extensions? - })); - let cfg = hugr.root(); - let ([entry, entry_in, entry_out], exit) = create_entry_exit( - &mut hugr, - cfg, - type_row![NAT], - vec![type_row![NAT], type_row![NAT]], - ExtensionSet::new(), - type_row![NAT], - )?; - - let entry_mid = hugr.add_node_with_parent( - entry, - make_opaque(UNKNOWN_EXTENSION, FunctionType::new(vec![NAT], twoway(NAT))), - ); - - hugr.connect(entry_in, 0, entry_mid, 0); - hugr.connect(entry_mid, 0, entry_out, 0); - - let bb0 = make_block( - &mut hugr, - cfg, - type_row![NAT], - vec![type_row![NAT]], - ExtensionSet::new(), - )?; - - let bb1 = make_block( - &mut hugr, - cfg, - type_row![NAT], - vec![type_row![NAT]], - ExtensionSet::new(), - )?; - - let bb2 = make_block( - &mut hugr, - cfg, - type_row![NAT], - vec![type_row![NAT]], - ExtensionSet::new(), - )?; - - hugr.connect(entry, 0, bb0, 0); - hugr.connect(entry, 0, bb1, 0); - hugr.connect(bb0, 0, bb2, 0); - hugr.connect(bb1, 0, bb2, 0); - hugr.connect(bb2, 0, exit, 0); - - hugr.update_validate(&PRELUDE_REGISTRY)?; - - Ok(()) -} - -/// Create a CFG of the form below, with the extension deltas for `Entry`, -/// `BB1`, and `BB2` specified by arguments to the function. -/// -/// +-----------+ -/// +--->| Entry | -/// | +-----+-----+ -/// | | -/// | V -/// | +------------+ -/// | | BB1 +---+ -/// | +-----+------+ | -/// | | | -/// | V | -/// | +------------+ | -/// +----+ BB2 | | -/// +------------+ | -/// | -/// +------------+ | -/// | Exit |<--+ -/// +------------+ -fn make_looping_cfg( - entry_ext: ExtensionSet, - bb1_ext: ExtensionSet, - bb2_ext: ExtensionSet, -) -> Result> { - let hugr_delta = entry_ext - .clone() - .union(bb1_ext.clone()) - .union(bb2_ext.clone()); - - let mut hugr = Hugr::new(NodeType::new_open(ops::CFG { - signature: FunctionType::new(type_row![NAT], type_row![NAT]) - .with_extension_delta(hugr_delta), - })); - - let root = hugr.root(); - - let ([entry, entry_in, entry_out], exit) = create_entry_exit( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - entry_ext.clone(), - type_row![NAT], - )?; - - let entry_dfg = hugr.add_node_with_parent( - entry, - make_opaque( - UNKNOWN_EXTENSION, - FunctionType::new(vec![NAT], oneway(NAT)).with_extension_delta(entry_ext), - ), - ); - - hugr.connect(entry_in, 0, entry_dfg, 0); - hugr.connect(entry_dfg, 0, entry_out, 0); - - let bb1 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT], type_row![NAT]], - bb1_ext.clone(), - )?; - - let bb2 = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - bb2_ext.clone(), - )?; - - hugr.connect(entry, 0, bb1, 0); - hugr.connect(bb1, 0, bb2, 0); - hugr.connect(bb1, 0, exit, 0); - hugr.connect(bb2, 0, entry, 0); - - Ok(hugr) -} - -#[test] -fn test_cfg_loops() -> Result<(), Box> { - let just_a = ExtensionSet::singleton(&A); - let mut variants = Vec::new(); - for entry in [ExtensionSet::new(), just_a.clone()] { - for bb1 in [ExtensionSet::new(), just_a.clone()] { - for bb2 in [ExtensionSet::new(), just_a.clone()] { - variants.push((entry.clone(), bb1.clone(), bb2.clone())); - } - } - } - for (bb0, bb1, bb2) in variants.into_iter() { - let mut hugr = make_looping_cfg(bb0, bb1, bb2)?; - hugr.update_validate(&PRELUDE_REGISTRY)?; - } - Ok(()) -} - -#[test] -/// A control flow graph consisting of an entry node and a single block -/// which adds a resource and links to both itself and the exit node. -fn simple_cfg_loop() -> Result<(), Box> { - let just_a = ExtensionSet::singleton(&A); - - let mut hugr = Hugr::new(NodeType::new( - ops::CFG { - signature: FunctionType::new(type_row![NAT], type_row![NAT]).with_extension_delta(A), - }, - Some(A.into()), - )); - - let root = hugr.root(); - - let ([entry, entry_in, entry_out], exit) = create_entry_exit( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT]], - ExtensionSet::new(), - type_row![NAT], - )?; - - let entry_mid = hugr.add_node_with_parent( - entry, - make_opaque(UNKNOWN_EXTENSION, FunctionType::new(vec![NAT], oneway(NAT))), - ); - - hugr.connect(entry_in, 0, entry_mid, 0); - hugr.connect(entry_mid, 0, entry_out, 0); - - let bb = make_block( - &mut hugr, - root, - type_row![NAT], - vec![type_row![NAT], type_row![NAT]], - just_a.clone(), - )?; - - hugr.connect(entry, 0, bb, 0); - hugr.connect(bb, 0, bb, 0); - hugr.connect(bb, 0, exit, 0); - - hugr.update_validate(&PRELUDE_REGISTRY)?; - - Ok(()) -} - -/// This was stack-overflowing approx 50% of the time, -/// see https://github.com/CQCL/hugr/issues/633 -#[test] -fn plus_on_self() -> Result<(), Box> { - let ext = ExtensionId::new("unknown1").unwrap(); - let ft = FunctionType::new_endo(type_row![QB_T, QB_T]).with_extension_delta(ext.clone()); - let mut dfg = DFGBuilder::new(ft.clone())?; - - // While https://github.com/CQCL/hugr/issues/388 is unsolved, - // most operations have empty extension_reqs (not including their own extension). - // Define some that do. - let binop = CustomOp::new_opaque(OpaqueOp::new( - ext.clone(), - "2qb_op", - String::new(), - vec![], - ft, - )); - let unary_sig = FunctionType::new_endo(type_row![QB_T]).with_extension_delta(ext.clone()); - let unop = CustomOp::new_opaque(OpaqueOp::new( - ext, - "1qb_op", - String::new(), - vec![], - unary_sig, - )); - // Constrain q1,q2 as PLUS(ext1, inputs): - let [q1, q2] = dfg - .add_dataflow_op(binop.clone(), dfg.input_wires())? - .outputs_arr(); - // Constrain q1 as PLUS(ext2, q2): - let [q1] = dfg.add_dataflow_op(unop, [q1])?.outputs_arr(); - // Constrain q1 as EQUALS(q2) by using both together - dfg.finish_hugr_with_outputs([q1, q2], &PRELUDE_REGISTRY)?; - // The combined q1+q2 variable now has two PLUS constraints - on itself and the inputs. - Ok(()) -} - -/// [plus_on_self] had about a 50% rate of failing with stack overflow. -/// So if we run 10 times, that would succeed about 1 run in 2^10, i.e. <0.1% -#[test] -fn plus_on_self_10_times() { - [0; 10].iter().for_each(|_| plus_on_self().unwrap()) -} - -#[test] -// Test that logic for dealing with self-referential constraints doesn't -// fall over when a self-referencing group of metas also references a meta -// outside the group -fn sccs() { - let hugr = Hugr::default(); - let mut ctx = UnificationContext::new(&hugr); - // Make a strongly-connected component (loop) - let m1 = ctx.fresh_meta(); - let m2 = ctx.fresh_meta(); - let m3 = ctx.fresh_meta(); - ctx.add_constraint(m1, Constraint::Plus(ExtensionSet::singleton(&A), m3)); - ctx.add_constraint(m2, Constraint::Plus(ExtensionSet::singleton(&B), m1)); - ctx.add_constraint(m3, Constraint::Plus(ExtensionSet::singleton(&A), m2)); - // And a second scc - let m4 = ctx.fresh_meta(); - let m5 = ctx.fresh_meta(); - ctx.add_constraint(m4, Constraint::Plus(ExtensionSet::singleton(&C), m5)); - ctx.add_constraint(m5, Constraint::Plus(ExtensionSet::singleton(&C), m4)); - // Make second component depend upon first - ctx.add_constraint( - m4, - Constraint::Plus(ExtensionSet::singleton(&UNKNOWN_EXTENSION), m3), - ); - ctx.variables.insert(m1); - ctx.variables.insert(m4); - ctx.instantiate_variables(); - assert_eq!( - ctx.get_solution(&m1), - Some(&ExtensionSet::from_iter([A, B])) - ); - assert_eq!( - ctx.get_solution(&m4), - Some(&ExtensionSet::from_iter([A, B, C, UNKNOWN_EXTENSION])) - ); -} - -#[test] -/// Note: This test is relying on the builder's `define_function` doing the -/// right thing: it takes input resources via a [`Signature`], which it passes -/// to `create_with_io`, creating concrete resource sets. -/// Inference can still fail for a valid FuncDefn hugr created without using -/// the builder API. -fn simple_funcdefn() -> Result<(), Box> { - let mut builder = ModuleBuilder::new(); - let mut func_builder = builder.define_function( - "F", - FunctionType::new(vec![NAT], vec![NAT]) - .with_extension_delta(A) - .into(), - )?; - - let [w] = func_builder.input_wires_arr(); - let lift = func_builder.add_dataflow_op( - Lift { - type_row: type_row![NAT], - new_extension: A, - }, - [w], - )?; - let [w] = lift.outputs_arr(); - func_builder.finish_with_outputs([w])?; - builder.finish_prelude_hugr()?; - Ok(()) -} diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index ffda91d2f..1f9e3a7c1 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -24,7 +24,7 @@ use thiserror::Error; pub use self::views::{HugrView, RootTagged}; use crate::core::NodeIndex; -use crate::extension::{ExtensionRegistry, ExtensionSet, ExtensionSolution, InferExtensionError}; +use crate::extension::{ExtensionRegistry, ExtensionSet}; use crate::ops::custom::resolve_extension_ops; use crate::ops::{OpTag, OpTrait, OpType, DEFAULT_OPTYPE}; use crate::types::FunctionType; @@ -207,28 +207,6 @@ impl Hugr { } Ok(()) } - - /// Infer extension requirements and add new information to `op_types` field - /// (if the "extension_inference" feature is on; otherwise, do nothing) - pub fn infer_extensions(&mut self) -> Result<(), InferExtensionError> { - Ok(()) - } - - #[allow(dead_code)] - /// Add extension requirement information to the hugr in place. - fn instantiate_extensions(&mut self, solution: &ExtensionSolution) { - // We only care about inferred _input_ extensions, because `NodeType` - // uses those to infer the output extensions - for (node, input_extensions) in solution.iter() { - let nodetype = self.op_types.try_get_mut(node.pg_index()).unwrap(); - match &nodetype.input_extensions { - None => nodetype.input_extensions = Some(input_extensions.clone()), - Some(existing_ext_reqs) => { - debug_assert_eq!(existing_ext_reqs, input_extensions) - } - } - } - } } /// Internal API for HUGRs, not intended for use by users. diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index e82287669..eef53937b 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -9,7 +9,7 @@ use petgraph::visit::{Topo, Walker}; use portgraph::{LinkView, PortView}; use thiserror::Error; -use crate::extension::{ExtensionRegistry, ExtensionSet, InferExtensionError, SignatureError}; +use crate::extension::{ExtensionRegistry, ExtensionSet, SignatureError}; use crate::ops::custom::{resolve_opaque_op, CustomOp, CustomOpError}; use crate::ops::validate::{ChildrenEdgeData, ChildrenValidationError, EdgeValidationError}; @@ -749,8 +749,6 @@ pub enum ValidationError { child: Node, child_extensions: ExtensionSet, }, - #[error(transparent)] - CantInfer(#[from] InferExtensionError), /// Error in a node signature #[error("Error in signature of node {node:?}: {cause}")] SignatureError { node: Node, cause: SignatureError }, diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index fc87ba0cb..8d2b22ebd 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -152,7 +152,6 @@ fn children_restrictions() { b.update_validate(&EMPTY_REG), Err(ValidationError::NonContainerWithChildren { node, .. }) => assert_eq!(node, copy) ); - b.infer_extensions().unwrap(); b.set_parent(new_def, root); // After moving the previous definition to a valid place, @@ -915,7 +914,6 @@ mod extension_tests { .unwrap(); // Write Extension annotations into the Hugr while it's still well-formed // enough for us to compute them - b.infer_extensions().unwrap(); b.validate(&EMPTY_REG).unwrap(); b.replace_op( copy, From 5715ea14cb21f4e679020d9bab4ff54edcff82d0 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 01:20:05 +0100 Subject: [PATCH 19/39] Bonus (BREAKING): remove io_extensions() --- hugr-core/src/hugr.rs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 1f9e3a7c1..202650c82 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -119,16 +119,6 @@ impl NodeType { self.input_extensions.as_ref() } - /// The input and output extensions for this node, if set. - /// - /// `None`` if the [Self::input_extensions] is `None`. - /// Otherwise, will return Some, with the output extensions computed from the node's delta - pub fn io_extensions(&self) -> Option<(ExtensionSet, ExtensionSet)> { - self.input_extensions - .clone() - .map(|e| (e.clone(), self.op.extension_delta().union(e))) - } - /// Gets the underlying [OpType] i.e. without any [input_extensions] /// /// [input_extensions]: NodeType::input_extensions From d605c5b09357128d84614437c38bc13f6f6e6b77 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 09:20:38 +0100 Subject: [PATCH 20/39] Reinstate ExtensionError but as a struct --- hugr-core/src/hugr/validate.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index eef53937b..ff575948b 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -77,12 +77,13 @@ impl Hugr { for child in self.children(parent) { let child_extensions = self.get_optype(child).extension_delta(); if !parent_extensions.is_superset(&child_extensions) { - return Err(ValidationError::ExtensionError { + return Err(ExtensionError { parent, parent_extensions, child, child_extensions, - }); + } + .into()); } } } @@ -741,14 +742,9 @@ pub enum ValidationError { /// There are invalid inter-graph edges. #[error(transparent)] InterGraphEdgeError(#[from] InterGraphEdgeError), - #[error("Extensions of child node ({child}) {child_extensions} are not a subset of the parent node ({parent}): {parent_extensions}")] - /// There are errors in the extension declarations. - ExtensionError { - parent: Node, - parent_extensions: ExtensionSet, - child: Node, - child_extensions: ExtensionSet, - }, + /// There are errors in the extension deltas. + #[error(transparent)] + ExtensionError(#[from] ExtensionError), /// Error in a node signature #[error("Error in signature of node {node:?}: {cause}")] SignatureError { node: Node, cause: SignatureError }, @@ -821,5 +817,15 @@ pub enum InterGraphEdgeError { }, } +#[derive(Debug, Clone, PartialEq, Error)] +#[error("Extensions of child node ({child}) {child_extensions} are not a subset of the parent node ({parent}): {parent_extensions}")] +/// An error in the extension deltas. +pub struct ExtensionError { + parent: Node, + parent_extensions: ExtensionSet, + child: Node, + child_extensions: ExtensionSet, +} + #[cfg(test)] pub(crate) mod test; From e691dea1f92f6cd73fd73c287fbfe8efb1ed772f Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 09:26:15 +0100 Subject: [PATCH 21/39] And keep infer_extensions around as a No-op --- hugr-core/src/hugr.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 202650c82..875bd8ddc 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -13,6 +13,7 @@ use std::collections::VecDeque; use std::iter; pub(crate) use self::hugrmut::HugrMut; +use self::validate::ExtensionError; pub use self::validate::ValidationError; pub use ident::{IdentList, InvalidIdentifier}; @@ -193,10 +194,17 @@ impl Hugr { self.validate_no_extensions(extension_registry)?; #[cfg(feature = "extension_inference")] { + self.infer_extensions()?; self.validate_extensions()?; } Ok(()) } + + /// Leaving this here as in the future we plan for it to infer deltas + /// of container nodes e.g. [DFG]. For the moment it does nothing. + pub fn infer_extensions(&mut self) -> Result<(), ExtensionError> { + Ok(()) + } } /// Internal API for HUGRs, not intended for use by users. From 4b61ff07a537f84394e87d83da6465fdb05148cd Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 09:50:27 +0100 Subject: [PATCH 22/39] Test for CFG, BB --- hugr-core/src/hugr/validate/test.rs | 87 +++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 8d2b22ebd..c07c848a4 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -867,6 +867,7 @@ fn test_polymorphic_load() -> Result<(), Box> { #[cfg(feature = "extension_inference")] mod extension_tests { use super::*; + use crate::builder::{BlockBuilder, CFGBuilder}; use crate::extension::ExtensionSet; use crate::macros::const_extension_ids; @@ -999,8 +1000,8 @@ mod extension_tests { #[case::d1(|signature| ops::DFG {signature}.into())] #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] #[case::c1(|signature| ops::Case {signature}.into())] - // ALAN TODO TailLoop/BasicBlock a bit harder, need a tag op inside - fn parent_io_mismatch( + // ALAN TODO TailLoop/Conditional versions too + fn parent_extension_mismatch( #[case] parent_f: impl Fn(FunctionType) -> OpType, #[values(ExtensionSet::new(), XA.into())] parent_extensions: ExtensionSet, ) { @@ -1041,12 +1042,90 @@ mod extension_tests { let result = hugr.validate(&PRELUDE_REGISTRY); assert_eq!( result, - Err(ValidationError::ExtensionError { + Err(ValidationError::ExtensionError(ExtensionError { parent: hugr.root(), parent_extensions, child: lift, child_extensions: XB.into() - }) + })) ); } + + #[rstest] + #[case(XA.into(), false)] + #[case(ExtensionSet::new(), false)] + #[case(ExtensionSet::from_iter([XA, XB]), true)] + fn cfg_extension_mismatch( + #[case] parent_extensions: ExtensionSet, + #[case] success: bool, + ) -> Result<(), BuildError> { + let mut cfg = CFGBuilder::new( + FunctionType::new_endo(USIZE_T).with_extension_delta(parent_extensions.clone()), + )?; + let mut bb = cfg.simple_entry_builder(USIZE_T.into(), 1, XB.into())?; + let pred = bb.add_load_value(Value::unary_unit_sum()); + let inputs = bb.input_wires(); + let blk = bb.finish_with_outputs(pred, inputs)?; + let exit = cfg.exit_block(); + cfg.branch(&blk, 0, &exit)?; + let root = cfg.hugr().root(); + let res = cfg.finish_prelude_hugr(); + if success { + assert!(res.is_ok()) + } else { + assert_eq!( + res, + Err(ValidationError::ExtensionError(ExtensionError { + parent: root, + parent_extensions, + child: blk.node(), + child_extensions: XB.into() + })) + ); + } + Ok(()) + } + + #[rstest] + #[case(XA.into(), false)] + #[case(ExtensionSet::new(), false)] + #[case(ExtensionSet::from_iter([XA, XB]), true)] + fn bb_extension_mismatch( + #[case] parent_extensions: ExtensionSet, + #[case] success: bool, + ) -> Result<(), BuildError> { + let mut bb = BlockBuilder::new( + USIZE_T, + None, + vec![USIZE_T.into()], + type_row![], + parent_extensions.clone(), + )?; + let lift = bb.add_dataflow_op( + ops::Lift { + type_row: USIZE_T.into(), + new_extension: XB, + }, + bb.input_wires(), + )?; + let pred = bb.make_tuple(lift.outputs())?; + let root = bb.hugr().root(); + let res = bb.finish_prelude_hugr_with_outputs([pred]); + if success { + assert!(res.is_ok()) + } else { + assert_eq!( + res, + Err(BuildError::InvalidHUGR(ValidationError::ExtensionError( + ExtensionError { + parent: root, + parent_extensions, + child: lift.node(), + child_extensions: XB.into() + } + ))) + ); + } + Ok(()) + } } From 19e64a97f480141001b888a5f0619894c34965e6 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 31 May 2024 11:17:00 +0100 Subject: [PATCH 23/39] Be more explicit in ModuleRoot check --- hugr-core/src/hugr/validate.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index ff575948b..85270d242 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -62,17 +62,15 @@ impl Hugr { let parent_op = self.get_optype(parent); let parent_extensions = match parent_op.inner_function_type() { Some(FunctionType { extension_reqs, .. }) => extension_reqs, - None => { - if matches!(parent_op.tag(), OpTag::Cfg | OpTag::Conditional) { - parent_op.extension_delta() - } else { - assert!( - parent_op.tag() == OpTag::ModuleRoot - || self.children(parent).next().is_none() - ); + None => match parent_op.tag() { + OpTag::Cfg | OpTag::Conditional => parent_op.extension_delta(), + // ModuleRoot holds but does not execute its children, so allow any extensions + OpTag::ModuleRoot => continue, + _ => { + assert!(self.children(parent).next().is_none()); continue; } - } + }, }; for child in self.children(parent) { let child_extensions = self.get_optype(child).extension_delta(); From 63a8a87475563915e62081a69a4e3024dfd3ccb2 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 15:00:40 +0100 Subject: [PATCH 24/39] Tweak message --- hugr-core/src/hugr/validate.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index 85270d242..1d69e0d12 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -816,7 +816,7 @@ pub enum InterGraphEdgeError { } #[derive(Debug, Clone, PartialEq, Error)] -#[error("Extensions of child node ({child}) {child_extensions} are not a subset of the parent node ({parent}): {parent_extensions}")] +#[error("Parent node {parent} has extensions {parent_extensions} that are too restrictive for child node {child}, they must include child extensions {child_extensions}")] /// An error in the extension deltas. pub struct ExtensionError { parent: Node, From 39fc4feba350f4851a6c263af0cf2be8ba45511a Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 15:18:21 +0100 Subject: [PATCH 25/39] Add assert message --- hugr-core/src/hugr/validate.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index 1d69e0d12..1d98abab4 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -67,7 +67,9 @@ impl Hugr { // ModuleRoot holds but does not execute its children, so allow any extensions OpTag::ModuleRoot => continue, _ => { - assert!(self.children(parent).next().is_none()); + assert!(self.children(parent).next().is_none(), + "Unknown parent node type {:?} - not a DataflowParent, Module, Cfg or Conditional", + parent_op); continue; } }, From 00f152a8fe51a894b25a6bdc56782068e0247716 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 15:50:25 +0100 Subject: [PATCH 26/39] Conditional test --- hugr-core/src/hugr/validate/test.rs | 78 ++++++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index c07c848a4..5be6a45df 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -1000,7 +1000,7 @@ mod extension_tests { #[case::d1(|signature| ops::DFG {signature}.into())] #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] #[case::c1(|signature| ops::Case {signature}.into())] - // ALAN TODO TailLoop/Conditional versions too + // ALAN TODO TailLoop version too fn parent_extension_mismatch( #[case] parent_f: impl Fn(FunctionType) -> OpType, #[values(ExtensionSet::new(), XA.into())] parent_extensions: ExtensionSet, @@ -1086,6 +1086,82 @@ mod extension_tests { Ok(()) } + #[rstest] + #[case(XA.into(), false)] + #[case(ExtensionSet::new(), false)] + #[case(ExtensionSet::from_iter([XA, XB]), true)] + fn conditional_extension_mismatch( + #[case] parent_extensions: ExtensionSet, + #[case] success: bool, + ) { + // Child graph adds extension "XB", but the parent + // declares a different delta, in same cases causing a mismatch. + let parent = ops::Conditional { + sum_rows: vec![type_row![], type_row![]], + other_inputs: type_row![USIZE_T], + outputs: type_row![USIZE_T], + extension_delta: parent_extensions.clone(), + }; + let mut hugr = Hugr::new(NodeType::new_pure(parent)); + + // First case with no delta should be ok in all cases. Second one may not be. + let [_, child] = [None, Some(XB)].map(|case_ext| { + let case_exts = ExtensionSet::from_iter(case_ext.clone()); + let case = hugr.add_node_with_parent( + hugr.root(), + ops::Case { + signature: FunctionType::new_endo(USIZE_T) + .with_extension_delta(case_exts.clone()), + }, + ); + + let input = hugr.add_node_with_parent( + case, + NodeType::new_pure(ops::Input { + types: type_row![USIZE_T], + }), + ); + let output = hugr.add_node_with_parent( + case, + NodeType::new( + ops::Output { + types: type_row![USIZE_T], + }, + Some(case_exts), + ), + ); + let res = match case_ext { + None => input, + Some(new_ext) => { + let lift = hugr.add_node_with_parent( + case, + NodeType::new_pure(ops::Lift { + type_row: type_row![USIZE_T], + new_extension: new_ext, + }), + ); + hugr.connect(input, 0, lift, 0); + lift + } + }; + hugr.connect(res, 0, output, 0); + case + }); + // case is the last-assigned child, i.e. the one that requires 'XB' + let result = hugr.validate(&PRELUDE_REGISTRY); + let expected = if success { + Ok(()) + } else { + Err(ValidationError::ExtensionError(ExtensionError { + parent: hugr.root(), + parent_extensions, + child, + child_extensions: XB.into(), + })) + }; + assert_eq!(result, expected); + } + #[rstest] #[case(XA.into(), false)] #[case(ExtensionSet::new(), false)] From ae55091b788a58f0075b4a522b439654700e121c Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 16:01:57 +0100 Subject: [PATCH 27/39] Add TailLoop test --- hugr-core/src/hugr/validate/test.rs | 45 +++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 5be6a45df..36568ae46 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -867,7 +867,7 @@ fn test_polymorphic_load() -> Result<(), Box> { #[cfg(feature = "extension_inference")] mod extension_tests { use super::*; - use crate::builder::{BlockBuilder, CFGBuilder}; + use crate::builder::{BlockBuilder, CFGBuilder, TailLoopBuilder}; use crate::extension::ExtensionSet; use crate::macros::const_extension_ids; @@ -1000,7 +1000,6 @@ mod extension_tests { #[case::d1(|signature| ops::DFG {signature}.into())] #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] #[case::c1(|signature| ops::Case {signature}.into())] - // ALAN TODO TailLoop version too fn parent_extension_mismatch( #[case] parent_f: impl Fn(FunctionType) -> OpType, #[values(ExtensionSet::new(), XA.into())] parent_extensions: ExtensionSet, @@ -1204,4 +1203,46 @@ mod extension_tests { } Ok(()) } + + #[rstest] + #[case(XA.into(), false)] + #[case(ExtensionSet::new(), false)] + #[case(ExtensionSet::from_iter([XA, XB]), true)] + fn tailloop_extension_mismatch( + #[case] parent_extensions: ExtensionSet, + #[case] success: bool, + ) -> Result<(), BuildError> { + let mut tl = TailLoopBuilder::new( + type_row![USIZE_T], + &[], + type_row![USIZE_T], + parent_extensions.clone(), + )?; + let lift = tl.add_dataflow_op( + ops::Lift { + type_row: USIZE_T.into(), + new_extension: XB, + }, + tl.input_wires(), + )?; + let pred = tl.make_break(tl.loop_signature()?.clone(), lift.outputs())?; + let root = tl.hugr().root(); + let res = tl.finish_prelude_hugr_with_outputs([pred]); + if success { + assert!(res.is_ok()) + } else { + assert_eq!( + res, + Err(BuildError::InvalidHUGR(ValidationError::ExtensionError( + ExtensionError { + parent: root, + parent_extensions, + child: lift.node(), + child_extensions: XB.into() + } + ))) + ); + } + Ok(()) + } } From bcd23df63afd30d901e986a3a91be814cfd48347 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 16:18:50 +0100 Subject: [PATCH 28/39] Combine BasicBlock/TailLoop tests --- hugr-core/src/hugr/validate/test.rs | 85 +++++++++-------------------- 1 file changed, 26 insertions(+), 59 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 36568ae46..8a7a3b0ef 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -866,10 +866,14 @@ fn test_polymorphic_load() -> Result<(), Box> { #[cfg(feature = "extension_inference")] mod extension_tests { + use self::ops::handle::{BasicBlockID, TailLoopID}; + use super::*; - use crate::builder::{BlockBuilder, CFGBuilder, TailLoopBuilder}; + use crate::builder::handle::Outputs; + use crate::builder::{BlockBuilder, BuildHandle, CFGBuilder, DFGWrapper, TailLoopBuilder}; use crate::extension::ExtensionSet; use crate::macros::const_extension_ids; + use crate::Wire; const_extension_ids! { const XA: ExtensionId = "A"; @@ -1162,30 +1166,26 @@ mod extension_tests { } #[rstest] - #[case(XA.into(), false)] - #[case(ExtensionSet::new(), false)] - #[case(ExtensionSet::from_iter([XA, XB]), true)] - fn bb_extension_mismatch( - #[case] parent_extensions: ExtensionSet, - #[case] success: bool, + #[case(make_bb, |bb: &mut DFGWrapper<_,_>, outs| bb.make_tuple(outs))] + #[case(make_tailloop, |tl: &mut DFGWrapper<_,_>, outs| tl.make_break(tl.loop_signature().unwrap().clone(), outs))] + fn bb_extension_mismatch( + #[case] dfg_fn: impl Fn(Type, ExtensionSet) -> DFGWrapper, + #[case] make_pred: impl Fn(&mut DFGWrapper, Outputs) -> Result, + #[values((XA.into(), false), (ExtensionSet::new(), false), (ExtensionSet::from_iter([XA,XB]), true))] + parent_exts_success: (ExtensionSet, bool), ) -> Result<(), BuildError> { - let mut bb = BlockBuilder::new( - USIZE_T, - None, - vec![USIZE_T.into()], - type_row![], - parent_extensions.clone(), - )?; - let lift = bb.add_dataflow_op( + let (parent_extensions, success) = parent_exts_success; + let mut dfg = dfg_fn(USIZE_T, parent_extensions.clone()); + let lift = dfg.add_dataflow_op( ops::Lift { type_row: USIZE_T.into(), new_extension: XB, }, - bb.input_wires(), + dfg.input_wires(), )?; - let pred = bb.make_tuple(lift.outputs())?; - let root = bb.hugr().root(); - let res = bb.finish_prelude_hugr_with_outputs([pred]); + let pred = make_pred(&mut dfg, lift.outputs())?; + let root = dfg.hugr().root(); + let res = dfg.finish_prelude_hugr_with_outputs([pred]); if success { assert!(res.is_ok()) } else { @@ -1204,45 +1204,12 @@ mod extension_tests { Ok(()) } - #[rstest] - #[case(XA.into(), false)] - #[case(ExtensionSet::new(), false)] - #[case(ExtensionSet::from_iter([XA, XB]), true)] - fn tailloop_extension_mismatch( - #[case] parent_extensions: ExtensionSet, - #[case] success: bool, - ) -> Result<(), BuildError> { - let mut tl = TailLoopBuilder::new( - type_row![USIZE_T], - &[], - type_row![USIZE_T], - parent_extensions.clone(), - )?; - let lift = tl.add_dataflow_op( - ops::Lift { - type_row: USIZE_T.into(), - new_extension: XB, - }, - tl.input_wires(), - )?; - let pred = tl.make_break(tl.loop_signature()?.clone(), lift.outputs())?; - let root = tl.hugr().root(); - let res = tl.finish_prelude_hugr_with_outputs([pred]); - if success { - assert!(res.is_ok()) - } else { - assert_eq!( - res, - Err(BuildError::InvalidHUGR(ValidationError::ExtensionError( - ExtensionError { - parent: root, - parent_extensions, - child: lift.node(), - child_extensions: XB.into() - } - ))) - ); - } - Ok(()) + fn make_bb(t: Type, es: ExtensionSet) -> DFGWrapper { + BlockBuilder::new(t.clone(), None, vec![t.into()], type_row![], es).unwrap() + } + + fn make_tailloop(t: Type, es: ExtensionSet) -> DFGWrapper> { + let row = TypeRow::from(t); + TailLoopBuilder::new(row.clone(), &[], row, es).unwrap() } } From 0f37a8f82e3ea71c2e7072b41e97efaf7aac320d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 17:19:51 +0100 Subject: [PATCH 29/39] Move cfg_children_restrictions out of extension_tests --- hugr-core/src/hugr/validate/test.rs | 237 ++++++++++++++-------------- 1 file changed, 118 insertions(+), 119 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 8a7a3b0ef..6c2c87e45 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -7,7 +7,7 @@ use crate::builder::{ BuildError, Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, SubContainer, }; -use crate::extension::prelude::{BOOL_T, PRELUDE, PRELUDE_ID, USIZE_T}; +use crate::extension::prelude::{BOOL_T, PRELUDE, PRELUDE_ID, QB_T, USIZE_T}; use crate::extension::{Extension, ExtensionSet, TypeDefBound, EMPTY_REG, PRELUDE_REGISTRY}; use crate::hugr::internal::HugrMutInternals; use crate::hugr::HugrMut; @@ -864,6 +864,123 @@ fn test_polymorphic_load() -> Result<(), Box> { Ok(()) } +/// Adds an input{BOOL_T}, tag_constant(0, BOOL_T^sum_size), tag(BOOL_T^sum_size), and +/// output{Sum{unit^sum_size}, BOOL_T} operation to a dataflow container. +/// Intended to be used to populate a BasicBlock node in a CFG. +/// +/// Returns the node indices of each of the operations. +fn add_block_children(b: &mut Hugr, parent: Node, sum_size: usize) -> (Node, Node, Node, Node) { + let const_op: ops::Const = ops::Value::unit_sum(0, sum_size as u8) + .expect("`sum_size` must be greater than 0") + .into(); + let tag_type = Type::new_unit_sum(sum_size as u8); + + let input = b.add_node_with_parent(parent, ops::Input::new(type_row![BOOL_T])); + let output = b.add_node_with_parent(parent, ops::Output::new(vec![tag_type.clone(), BOOL_T])); + let tag_def = b.add_node_with_parent(b.root(), const_op); + let tag = b.add_node_with_parent(parent, ops::LoadConstant { datatype: tag_type }); + + b.connect(tag_def, 0, tag, 0); + b.add_other_edge(input, tag); + b.connect(tag, 0, output, 0); + b.connect(input, 0, output, 1); + + (input, tag_def, tag, output) +} + +#[test] +/// Validation errors in a dataflow subgraph. +fn cfg_children_restrictions() { + let (mut b, def) = make_simple_hugr(1); + let (_input, _output, copy) = b + .hierarchy + .children(def.pg_index()) + .map_into() + .collect_tuple() + .unwrap(); + // Write Extension annotations into the Hugr while it's still well-formed + // enough for us to compute them + b.validate(&EMPTY_REG).unwrap(); + b.replace_op( + copy, + NodeType::new_pure(ops::CFG { + signature: FunctionType::new(type_row![BOOL_T], type_row![BOOL_T]), + }), + ) + .unwrap(); + assert_matches!( + b.validate(&EMPTY_REG), + Err(ValidationError::ContainerWithoutChildren { .. }) + ); + let cfg = copy; + + // Construct a valid CFG, with one BasicBlock node and one exit node + let block = b.add_node_with_parent( + cfg, + ops::DataflowBlock { + inputs: type_row![BOOL_T], + sum_rows: vec![type_row![]], + other_outputs: type_row![BOOL_T], + extension_delta: ExtensionSet::new(), + }, + ); + add_block_children(&mut b, block, 1); + let exit = b.add_node_with_parent( + cfg, + ops::ExitBlock { + cfg_outputs: type_row![BOOL_T], + }, + ); + b.add_other_edge(block, exit); + assert_eq!(b.update_validate(&EMPTY_REG), Ok(())); + + // Test malformed errors + + // Add an internal exit node + let exit2 = b.add_node_after( + exit, + ops::ExitBlock { + cfg_outputs: type_row![BOOL_T], + }, + ); + assert_matches!( + b.validate(&EMPTY_REG), + Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::InternalExitChildren { child, .. }, .. }) + => {assert_eq!(parent, cfg); assert_eq!(child, exit2.pg_index())} + ); + b.remove_node(exit2); + + // Change the types in the BasicBlock node to work on qubits instead of bits + b.replace_op( + block, + NodeType::new_pure(ops::DataflowBlock { + inputs: type_row![QB_T], + sum_rows: vec![type_row![]], + other_outputs: type_row![QB_T], + extension_delta: ExtensionSet::new(), + }), + ) + .unwrap(); + let mut block_children = b.hierarchy.children(block.pg_index()); + let block_input = block_children.next().unwrap().into(); + let block_output = block_children.next_back().unwrap().into(); + b.replace_op( + block_input, + NodeType::new_pure(ops::Input::new(type_row![QB_T])), + ) + .unwrap(); + b.replace_op( + block_output, + NodeType::new_pure(ops::Output::new(type_row![Type::new_unit_sum(1), QB_T])), + ) + .unwrap(); + assert_matches!( + b.validate(&EMPTY_REG), + Err(ValidationError::InvalidEdges { parent, source: EdgeValidationError::CFGEdgeSignatureMismatch { .. }, .. }) + => assert_eq!(parent, cfg) + ); +} + #[cfg(feature = "extension_inference")] mod extension_tests { use self::ops::handle::{BasicBlockID, TailLoopID}; @@ -882,124 +999,6 @@ mod extension_tests { const Q: Type = crate::extension::prelude::QB_T; - /// Adds an input{BOOL_T}, tag_constant(0, BOOL_T^sum_size), tag(BOOL_T^sum_size), and - /// output{Sum{unit^sum_size}, BOOL_T} operation to a dataflow container. - /// Intended to be used to populate a BasicBlock node in a CFG. - /// - /// Returns the node indices of each of the operations. - fn add_block_children(b: &mut Hugr, parent: Node, sum_size: usize) -> (Node, Node, Node, Node) { - let const_op: ops::Const = ops::Value::unit_sum(0, sum_size as u8) - .expect("`sum_size` must be greater than 0") - .into(); - let tag_type = Type::new_unit_sum(sum_size as u8); - - let input = b.add_node_with_parent(parent, ops::Input::new(type_row![BOOL_T])); - let output = - b.add_node_with_parent(parent, ops::Output::new(vec![tag_type.clone(), BOOL_T])); - let tag_def = b.add_node_with_parent(b.root(), const_op); - let tag = b.add_node_with_parent(parent, ops::LoadConstant { datatype: tag_type }); - - b.connect(tag_def, 0, tag, 0); - b.add_other_edge(input, tag); - b.connect(tag, 0, output, 0); - b.connect(input, 0, output, 1); - - (input, tag_def, tag, output) - } - - #[test] - /// Validation errors in a dataflow subgraph. - fn cfg_children_restrictions() { - let (mut b, def) = make_simple_hugr(1); - let (_input, _output, copy) = b - .hierarchy - .children(def.pg_index()) - .map_into() - .collect_tuple() - .unwrap(); - // Write Extension annotations into the Hugr while it's still well-formed - // enough for us to compute them - b.validate(&EMPTY_REG).unwrap(); - b.replace_op( - copy, - NodeType::new_pure(ops::CFG { - signature: FunctionType::new(type_row![BOOL_T], type_row![BOOL_T]), - }), - ) - .unwrap(); - assert_matches!( - b.validate(&EMPTY_REG), - Err(ValidationError::ContainerWithoutChildren { .. }) - ); - let cfg = copy; - - // Construct a valid CFG, with one BasicBlock node and one exit node - let block = b.add_node_with_parent( - cfg, - ops::DataflowBlock { - inputs: type_row![BOOL_T], - sum_rows: vec![type_row![]], - other_outputs: type_row![BOOL_T], - extension_delta: ExtensionSet::new(), - }, - ); - add_block_children(&mut b, block, 1); - let exit = b.add_node_with_parent( - cfg, - ops::ExitBlock { - cfg_outputs: type_row![BOOL_T], - }, - ); - b.add_other_edge(block, exit); - assert_eq!(b.update_validate(&EMPTY_REG), Ok(())); - - // Test malformed errors - - // Add an internal exit node - let exit2 = b.add_node_after( - exit, - ops::ExitBlock { - cfg_outputs: type_row![BOOL_T], - }, - ); - assert_matches!( - b.validate(&EMPTY_REG), - Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::InternalExitChildren { child, .. }, .. }) - => {assert_eq!(parent, cfg); assert_eq!(child, exit2.pg_index())} - ); - b.remove_node(exit2); - - // Change the types in the BasicBlock node to work on qubits instead of bits - b.replace_op( - block, - NodeType::new_pure(ops::DataflowBlock { - inputs: type_row![Q], - sum_rows: vec![type_row![]], - other_outputs: type_row![Q], - extension_delta: ExtensionSet::new(), - }), - ) - .unwrap(); - let mut block_children = b.hierarchy.children(block.pg_index()); - let block_input = block_children.next().unwrap().into(); - let block_output = block_children.next_back().unwrap().into(); - b.replace_op( - block_input, - NodeType::new_pure(ops::Input::new(type_row![Q])), - ) - .unwrap(); - b.replace_op( - block_output, - NodeType::new_pure(ops::Output::new(type_row![Type::new_unit_sum(1), Q])), - ) - .unwrap(); - assert_matches!( - b.validate(&EMPTY_REG), - Err(ValidationError::InvalidEdges { parent, source: EdgeValidationError::CFGEdgeSignatureMismatch { .. }, .. }) - => assert_eq!(parent, cfg) - ); - } - #[rstest] #[case::d1(|signature| ops::DFG {signature}.into())] #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] From 53d2b88dbd586270fcdf90754f9e348f076c417b Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 17:22:23 +0100 Subject: [PATCH 30/39] ...and inline singly-used add_block_children --- hugr-core/src/hugr/validate/test.rs | 39 +++++++++++------------------ 1 file changed, 14 insertions(+), 25 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 6c2c87e45..cd4089fb9 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -864,30 +864,6 @@ fn test_polymorphic_load() -> Result<(), Box> { Ok(()) } -/// Adds an input{BOOL_T}, tag_constant(0, BOOL_T^sum_size), tag(BOOL_T^sum_size), and -/// output{Sum{unit^sum_size}, BOOL_T} operation to a dataflow container. -/// Intended to be used to populate a BasicBlock node in a CFG. -/// -/// Returns the node indices of each of the operations. -fn add_block_children(b: &mut Hugr, parent: Node, sum_size: usize) -> (Node, Node, Node, Node) { - let const_op: ops::Const = ops::Value::unit_sum(0, sum_size as u8) - .expect("`sum_size` must be greater than 0") - .into(); - let tag_type = Type::new_unit_sum(sum_size as u8); - - let input = b.add_node_with_parent(parent, ops::Input::new(type_row![BOOL_T])); - let output = b.add_node_with_parent(parent, ops::Output::new(vec![tag_type.clone(), BOOL_T])); - let tag_def = b.add_node_with_parent(b.root(), const_op); - let tag = b.add_node_with_parent(parent, ops::LoadConstant { datatype: tag_type }); - - b.connect(tag_def, 0, tag, 0); - b.add_other_edge(input, tag); - b.connect(tag, 0, output, 0); - b.connect(input, 0, output, 1); - - (input, tag_def, tag, output) -} - #[test] /// Validation errors in a dataflow subgraph. fn cfg_children_restrictions() { @@ -924,7 +900,20 @@ fn cfg_children_restrictions() { extension_delta: ExtensionSet::new(), }, ); - add_block_children(&mut b, block, 1); + let const_op: ops::Const = ops::Value::unit_sum(0, 1).unwrap().into(); + let tag_type = Type::new_unit_sum(1); + { + let input = b.add_node_with_parent(block, ops::Input::new(type_row![BOOL_T])); + let output = + b.add_node_with_parent(block, ops::Output::new(vec![tag_type.clone(), BOOL_T])); + let tag_def = b.add_node_with_parent(b.root(), const_op); + let tag = b.add_node_with_parent(block, ops::LoadConstant { datatype: tag_type }); + + b.connect(tag_def, 0, tag, 0); + b.add_other_edge(input, tag); + b.connect(tag, 0, output, 0); + b.connect(input, 0, output, 1); + } let exit = b.add_node_with_parent( cfg, ops::ExitBlock { From 7e1cb4a29c1212a6d6bf27730ae041333119ea6c Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 17:24:57 +0100 Subject: [PATCH 31/39] Add new test of too-many-connections in cfg --- hugr-core/src/hugr/validate/test.rs | 38 ++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index cd4089fb9..33e89753b 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -865,7 +865,7 @@ fn test_polymorphic_load() -> Result<(), Box> { } #[test] -/// Validation errors in a dataflow subgraph. +/// Validation errors in a controlflow subgraph. fn cfg_children_restrictions() { let (mut b, def) = make_simple_hugr(1); let (_input, _output, copy) = b @@ -970,6 +970,42 @@ fn cfg_children_restrictions() { ); } +#[test] +// /->->\ +// | | +// Entry -> Middle -> Exit +fn cfg_connections() -> Result<(), Box> { + use crate::builder::CFGBuilder; + + let mut hugr = CFGBuilder::new(FunctionType::new_endo(USIZE_T))?; + let unary_pred = hugr.add_constant(Value::unary_unit_sum()); + let mut entry = hugr.simple_entry_builder(type_row![USIZE_T], 1, ExtensionSet::new())?; + let p = entry.load_const(&unary_pred); + let ins = entry.input_wires(); + let entry = entry.finish_with_outputs(p, ins)?; + + let mut middle = hugr.simple_block_builder(FunctionType::new_endo(USIZE_T), 1)?; + let p = middle.load_const(&unary_pred); + let ins = middle.input_wires(); + let middle = middle.finish_with_outputs(p, ins)?; + + let exit = hugr.exit_block(); + hugr.branch(&entry, 0, &middle)?; + hugr.branch(&middle, 0, &exit)?; + let mut h = hugr.finish_hugr(&PRELUDE_REGISTRY)?; + + h.connect(middle.node(), 0, middle.node(), 0); + assert_eq!( + h.validate(&PRELUDE_REGISTRY), + Err(ValidationError::TooManyConnections { + node: middle.node(), + port: Port::new(Direction::Outgoing, 0), + port_kind: EdgeKind::ControlFlow + }) + ); + Ok(()) +} + #[cfg(feature = "extension_inference")] mod extension_tests { use self::ops::handle::{BasicBlockID, TailLoopID}; From f256960149af98050a864ef1daae632fb55e96ef Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 17:26:11 +0100 Subject: [PATCH 32/39] fix broken doclink --- hugr-core/src/hugr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index d0b28a4d9..0ab25f7ba 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -201,7 +201,7 @@ impl Hugr { } /// Leaving this here as in the future we plan for it to infer deltas - /// of container nodes e.g. [DFG]. For the moment it does nothing. + /// of container nodes e.g. [OpType::DFG]. For the moment it does nothing. pub fn infer_extensions(&mut self) -> Result<(), ExtensionError> { Ok(()) } From 1bf1c14d013860d01e0456e638a14d540bd7e4f0 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Fri, 7 Jun 2024 17:33:54 +0100 Subject: [PATCH 33/39] clippy --- hugr-core/src/hugr/validate/test.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 33e89753b..3d68aac99 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -1022,8 +1022,6 @@ mod extension_tests { const XB: ExtensionId = "BOOL_EXT"; } - const Q: Type = crate::extension::prelude::QB_T; - #[rstest] #[case::d1(|signature| ops::DFG {signature}.into())] #[case::f1(|ft: FunctionType| ops::FuncDefn {name: "foo".to_string(), signature: ft.into()}.into())] From 546ced4ac3f561c1b82d78e85467532e94cd5de1 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 10 Jun 2024 14:19:54 +0100 Subject: [PATCH 34/39] Try to fix 1.75 test --- hugr-core/src/hugr/validate/test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 3d68aac99..47c197416 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -1232,6 +1232,6 @@ mod extension_tests { fn make_tailloop(t: Type, es: ExtensionSet) -> DFGWrapper> { let row = TypeRow::from(t); - TailLoopBuilder::new(row.clone(), &[], row, es).unwrap() + TailLoopBuilder::new(row.clone(), type_row![], row, es).unwrap() } } From 960f8d33453113315bd653c6094fd93b84d75961 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 10 Jun 2024 13:52:47 +0100 Subject: [PATCH 35/39] Remove NodeType --- hugr-core/src/builder.rs | 11 +- hugr-core/src/builder/build_traits.rs | 73 ++++-------- hugr-core/src/builder/cfg.rs | 18 +-- hugr-core/src/builder/conditional.rs | 14 +-- hugr-core/src/builder/dataflow.rs | 84 ++++++------- hugr-core/src/builder/module.rs | 15 +-- hugr-core/src/builder/tail_loop.rs | 7 +- hugr-core/src/hugr.rs | 119 ++----------------- hugr-core/src/hugr/hugrmut.rs | 21 ++-- hugr-core/src/hugr/internal.rs | 10 +- hugr-core/src/hugr/rewrite/inline_dfg.rs | 8 +- hugr-core/src/hugr/rewrite/outline_cfg.rs | 4 +- hugr-core/src/hugr/rewrite/replace.rs | 15 ++- hugr-core/src/hugr/rewrite/simple_replace.rs | 1 - hugr-core/src/hugr/serialize.rs | 21 +--- hugr-core/src/hugr/serialize/test.rs | 15 +-- hugr-core/src/hugr/validate.rs | 9 +- hugr-core/src/hugr/validate/test.rs | 88 ++++++-------- hugr-core/src/hugr/views.rs | 19 +-- hugr-core/src/hugr/views/descendants.rs | 7 +- hugr-core/src/hugr/views/root_checked.rs | 20 ++-- hugr-core/src/hugr/views/sibling.rs | 14 +-- hugr-core/src/ops/constant.rs | 2 +- hugr-core/src/ops/custom.rs | 7 +- hugr-passes/src/const_fold/test.rs | 3 +- hugr-py/src/hugr/serialization/ops.py | 1 - hugr-py/src/hugr/serialization/tys.py | 4 +- hugr/src/hugr.rs | 4 +- 28 files changed, 195 insertions(+), 419 deletions(-) diff --git a/hugr-core/src/builder.rs b/hugr-core/src/builder.rs index 787decc07..19d501566 100644 --- a/hugr-core/src/builder.rs +++ b/hugr-core/src/builder.rs @@ -220,7 +220,7 @@ pub enum BuilderWiringError { pub(crate) mod test { use rstest::fixture; - use crate::hugr::{views::HugrView, HugrMut, NodeType}; + use crate::hugr::{views::HugrView, HugrMut}; use crate::ops; use crate::std_extensions::arithmetic::float_ops::FLOAT_OPS_REGISTRY; use crate::types::{FunctionType, PolyFuncType, Type}; @@ -278,9 +278,12 @@ pub(crate) mod test { /// inference. Using DFGBuilder will default to a root node with an open /// extension variable pub(crate) fn closed_dfg_root_hugr(signature: FunctionType) -> Hugr { - let mut hugr = Hugr::new(NodeType::new_pure(ops::DFG { - signature: signature.clone(), - })); + let mut hugr = Hugr::new( + ops::DFG { + signature: signature.clone(), + } + .into(), + ); hugr.add_node_with_parent( hugr.root(), ops::Input { diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index 9279a6ba5..243d890f9 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -14,7 +14,6 @@ use super::{ use super::{BuilderWiringError, FunctionBuilder}; use crate::{ - hugr::NodeType, ops::handle::{ConstID, DataflowOpID, FuncID, NodeHandle}, types::EdgeKind, }; @@ -49,8 +48,8 @@ pub trait Container { let parent = self.container_node(); self.hugr_mut().add_node_with_parent(parent, op) } - /// Add a [`NodeType`] as the final child of the container. - fn add_child_node(&mut self, node: NodeType) -> Node { + /// Add an [`OpType`] as the final child of the container. + fn add_child_node(&mut self, node: impl Into) -> Node { let parent = self.container_node(); self.hugr_mut().add_node_with_parent(parent, node) } @@ -71,8 +70,7 @@ pub trait Container { /// This function will return an error if there is an error in adding the /// [`OpType::Const`] node. fn add_constant(&mut self, constant: impl Into) -> ConstID { - self.add_child_node(NodeType::new_pure(constant.into())) - .into() + self.add_child_node(constant.into()).into() } /// Add a [`ops::FuncDefn`] node and returns a builder to define the function @@ -88,13 +86,12 @@ pub trait Container { signature: PolyFuncType, ) -> Result, BuildError> { let body = signature.body().clone(); - let f_node = self.add_child_node(NodeType::new_pure(ops::FuncDefn { + let f_node = self.add_child_node(ops::FuncDefn { name: name.into(), signature, - })); + }); - let db = - DFGBuilder::create_with_io(self.hugr_mut(), f_node, body, Some(ExtensionSet::new()))?; + let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body)?; Ok(FunctionBuilder::from_dfg_builder(db)) } @@ -182,7 +179,7 @@ pub trait Dataflow: Container { fn input_wires(&self) -> Outputs { self.input().outputs() } - /// Add a dataflow op to the sibling graph, wiring up the `input_wires` to the + /// Add a dataflow [`OpType`] to the sibling graph, wiring up the `input_wires` to the /// incoming ports of the resulting node. /// /// # Errors @@ -190,21 +187,7 @@ pub trait Dataflow: Container { /// Returns a [`BuildError::OperationWiring`] error if the `input_wires` cannot be connected. fn add_dataflow_op( &mut self, - op: impl Into, - input_wires: impl IntoIterator, - ) -> Result, BuildError> { - self.add_dataflow_node(NodeType::new_auto(op), input_wires) - } - - /// Add a dataflow [`NodeType`] to the sibling graph, wiring up the `input_wires` to the - /// incoming ports of the resulting node. - /// - /// # Errors - /// - /// Returns a [`BuildError::OperationWiring`] error if the `input_wires` cannot be connected. - fn add_dataflow_node( - &mut self, - nodetype: NodeType, + nodetype: impl Into, input_wires: impl IntoIterator, ) -> Result, BuildError> { let outs = add_node_with_wires(self, nodetype, input_wires)?; @@ -297,16 +280,14 @@ pub trait Dataflow: Container { fn dfg_builder( &mut self, signature: FunctionType, - input_extensions: Option, input_wires: impl IntoIterator, ) -> Result, BuildError> { let op = ops::DFG { signature: signature.clone(), }; - let nodetype = NodeType::new(op, input_extensions.clone()); - let (dfg_n, _) = add_node_with_wires(self, nodetype, input_wires)?; + let (dfg_n, _) = add_node_with_wires(self, op, input_wires)?; - DFGBuilder::create_with_io(self.hugr_mut(), dfg_n, signature, input_extensions) + DFGBuilder::create_with_io(self.hugr_mut(), dfg_n, signature) } /// Return a builder for a [`crate::ops::CFG`] node, @@ -322,7 +303,6 @@ pub trait Dataflow: Container { fn cfg_builder( &mut self, inputs: impl IntoIterator, - input_extensions: impl Into>, output_types: TypeRow, extension_delta: ExtensionSet, ) -> Result, BuildError> { @@ -332,13 +312,10 @@ pub trait Dataflow: Container { let (cfg_node, _) = add_node_with_wires( self, - NodeType::new( - ops::CFG { - signature: FunctionType::new(inputs.clone(), output_types.clone()) - .with_extension_delta(extension_delta), - }, - input_extensions.into(), - ), + ops::CFG { + signature: FunctionType::new(inputs.clone(), output_types.clone()) + .with_extension_delta(extension_delta), + }, input_wires, )?; CFGBuilder::create(self.hugr_mut(), cfg_node, inputs, output_types) @@ -348,9 +325,8 @@ pub trait Dataflow: Container { /// Adds a [`OpType::LoadConstant`] node. fn load_const(&mut self, cid: &ConstID) -> Wire { let const_node = cid.node(); - let nodetype = self.hugr().get_nodetype(const_node); + let nodetype = self.hugr().get_optype(const_node); let op: ops::Const = nodetype - .op() .clone() .try_into() .expect("ConstID does not refer to Const op."); @@ -394,7 +370,7 @@ pub trait Dataflow: Container { exts: &ExtensionRegistry, ) -> Result { let func_node = fid.node(); - let func_op = self.hugr().get_nodetype(func_node).op(); + let func_op = self.hugr().get_optype(func_node); let func_sig = match func_op { OpType::FuncDefn(ops::FuncDefn { signature, .. }) | OpType::FuncDecl(ops::FuncDecl { signature, .. }) => signature.clone(), @@ -643,26 +619,23 @@ pub trait Dataflow: Container { /// invalid edge. fn add_node_with_wires( data_builder: &mut T, - nodetype: impl Into, + nodetype: impl Into, inputs: impl IntoIterator, ) -> Result<(Node, usize), BuildError> { - let nodetype: NodeType = nodetype.into(); + let op = nodetype.into(); // Check there are no row variables, as that would prevent us // from indexing into the node's ports in order to wire up - nodetype - .op_signature() + op.dataflow_signature() .as_ref() .and_then(FunctionType::find_rowvar) .map_or(Ok(()), |(idx, _)| { Err(SignatureError::RowVarWhereTypeExpected { idx }) })?; - let num_outputs = nodetype.op().value_output_count(); - let op_node = data_builder.add_child_node(nodetype.clone()); + let num_outputs = op.value_output_count(); + let op_node = data_builder.add_child_node(op.clone()); - wire_up_inputs(inputs, op_node, data_builder).map_err(|error| BuildError::OperationWiring { - op: nodetype.into_op(), - error, - })?; + wire_up_inputs(inputs, op_node, data_builder) + .map_err(|error| BuildError::OperationWiring { op, error })?; Ok((op_node, num_outputs)) } diff --git a/hugr-core/src/builder/cfg.rs b/hugr-core/src/builder/cfg.rs index 1ac852b45..377ebf3cb 100644 --- a/hugr-core/src/builder/cfg.rs +++ b/hugr-core/src/builder/cfg.rs @@ -13,10 +13,7 @@ use crate::{ use crate::{hugr::views::HugrView, types::TypeRow}; use crate::Node; -use crate::{ - hugr::{HugrMut, NodeType}, - type_row, Hugr, -}; +use crate::{hugr::HugrMut, type_row, Hugr}; /// Builder for a [`crate::ops::CFG`] child control /// flow graph. @@ -158,7 +155,7 @@ impl CFGBuilder { signature: signature.clone(), }; - let base = Hugr::new(NodeType::new_open(cfg_op)); + let base = Hugr::new(cfg_op.into()); let cfg_node = base.root(); CFGBuilder::create(base, cfg_node, signature.input, signature.output) } @@ -336,12 +333,7 @@ impl + AsRef> BlockBuilder { fn create(base: B, block_n: Node) -> Result { let block_op = base.get_optype(block_n).as_dataflow_block().unwrap(); let signature = block_op.inner_signature(); - let inp_ex = base - .as_ref() - .get_nodetype(block_n) - .input_extensions() - .cloned(); - let db = DFGBuilder::create_with_io(base, block_n, signature, inp_ex)?; + let db = DFGBuilder::create_with_io(base, block_n, signature)?; Ok(BlockBuilder::from_dfg_builder(db)) } @@ -363,7 +355,6 @@ impl BlockBuilder { /// Initialize a [`DataflowBlock`] rooted HUGR builder pub fn new( inputs: impl Into, - input_extensions: impl Into>, sum_rows: impl IntoIterator, other_outputs: impl Into, extension_delta: ExtensionSet, @@ -378,7 +369,7 @@ impl BlockBuilder { extension_delta, }; - let base = Hugr::new(NodeType::new(op, input_extensions)); + let base = Hugr::new(op.into()); let root = base.root(); Self::create(base, root) } @@ -418,7 +409,6 @@ pub(crate) mod test { let cfg_id = { let mut cfg_builder = func_builder.cfg_builder( vec![(NAT, int)], - None, type_row![NAT], ExtensionSet::new(), )?; diff --git a/hugr-core/src/builder/conditional.rs b/hugr-core/src/builder/conditional.rs index e39c88451..9ecc5652d 100644 --- a/hugr-core/src/builder/conditional.rs +++ b/hugr-core/src/builder/conditional.rs @@ -16,11 +16,7 @@ use super::{ }; use crate::Node; -use crate::{ - extension::ExtensionSet, - hugr::{HugrMut, NodeType}, - Hugr, -}; +use crate::{extension::ExtensionSet, hugr::HugrMut, Hugr}; use std::collections::HashSet; @@ -139,7 +135,6 @@ impl + AsRef> ConditionalBuilder { self.hugr_mut(), case_node, FunctionType::new(inputs, outputs).with_extension_delta(extension_delta), - None, )?; Ok(CaseBuilder::from_dfg_builder(dfg_builder)) @@ -177,8 +172,7 @@ impl ConditionalBuilder { outputs, extension_delta, }; - // TODO: Allow input extensions to be specified - let base = Hugr::new(NodeType::new_open(op)); + let base = Hugr::new(op.into()); let conditional_node = base.root(); Ok(ConditionalBuilder { @@ -196,9 +190,9 @@ impl CaseBuilder { let op = ops::Case { signature: signature.clone(), }; - let base = Hugr::new(NodeType::new_open(op)); + let base = Hugr::new(op.into()); let root = base.root(); - let dfg_builder = DFGBuilder::create_with_io(base, root, signature, None)?; + let dfg_builder = DFGBuilder::create_with_io(base, root, signature)?; Ok(CaseBuilder::from_dfg_builder(dfg_builder)) } diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 67865e13a..8dd743175 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -4,12 +4,12 @@ use super::{BuildError, Container, Dataflow, DfgID, FuncID}; use std::marker::PhantomData; -use crate::hugr::{HugrView, NodeType, ValidationError}; +use crate::hugr::{HugrView, ValidationError}; use crate::ops; use crate::types::{FunctionType, PolyFuncType}; -use crate::extension::{ExtensionRegistry, ExtensionSet}; +use crate::extension::ExtensionRegistry; use crate::Node; use crate::{hugr::HugrMut, Hugr}; @@ -27,7 +27,6 @@ impl + AsRef> DFGBuilder { mut base: T, parent: Node, signature: FunctionType, - input_extensions: Option, ) -> Result { let num_in_wires = signature.input().len(); let num_out_wires = signature.output().len(); @@ -49,15 +48,8 @@ impl + AsRef> DFGBuilder { let output = ops::Output { types: signature.output().clone(), }; - base.as_mut() - .add_node_with_parent(parent, NodeType::new(input, input_extensions.clone())); - base.as_mut().add_node_with_parent( - parent, - NodeType::new( - output, - input_extensions.map(|inp| inp.union(signature.extension_reqs)), - ), - ); + base.as_mut().add_node_with_parent(parent, input); + base.as_mut().add_node_with_parent(parent, output); Ok(Self { base, @@ -79,9 +71,9 @@ impl DFGBuilder { let dfg_op = ops::DFG { signature: signature.clone(), }; - let base = Hugr::new(NodeType::new_open(dfg_op)); + let base = Hugr::new(dfg_op.into()); let root = base.root(); - DFGBuilder::create_with_io(base, root, signature, None) + DFGBuilder::create_with_io(base, root, signature) } } @@ -153,10 +145,10 @@ impl FunctionBuilder { name: name.into(), }; - let base = Hugr::new(NodeType::new_pure(op)); + let base = Hugr::new(op.into()); let root = base.root(); - let db = DFGBuilder::create_with_io(base, root, body, Some(ExtensionSet::new()))?; + let db = DFGBuilder::create_with_io(base, root, body)?; Ok(Self::from_dfg_builder(db)) } } @@ -209,8 +201,11 @@ pub(crate) mod test { use crate::builder::build_traits::DataflowHugr; use crate::builder::{BuilderWiringError, DataflowSubContainer, ModuleBuilder}; use crate::extension::prelude::{BOOL_T, USIZE_T}; - use crate::extension::{ExtensionId, SignatureError, EMPTY_REG, PRELUDE_REGISTRY}; + use crate::extension::{ + ExtensionId, ExtensionSet, SignatureError, EMPTY_REG, PRELUDE_REGISTRY, + }; use crate::hugr::validate::InterGraphEdgeError; + use crate::ops::OpTrait; use crate::ops::{handle::NodeHandle, Lift, Noop, OpTag}; use crate::std_extensions::logic::test::and_op; @@ -239,11 +234,8 @@ pub(crate) mod test { let q_out = func_builder.add_dataflow_op(h_gate(), vec![qb])?; - let inner_builder = func_builder.dfg_builder( - FunctionType::new(type_row![NAT], type_row![NAT]), - None, - [int], - )?; + let inner_builder = func_builder + .dfg_builder(FunctionType::new(type_row![NAT], type_row![NAT]), [int])?; let inner_id = n_identity(inner_builder)?; func_builder.finish_with_outputs(inner_id.outputs().chain(q_out.outputs()))? @@ -348,7 +340,7 @@ pub(crate) mod test { let i1 = noop.out_wire(0); let mut nested = - f_build.dfg_builder(FunctionType::new(type_row![], type_row![BIT]), None, [])?; + f_build.dfg_builder(FunctionType::new(type_row![], type_row![BIT]), [])?; let id = nested.add_dataflow_op(Noop { ty: BIT }, [i1])?; @@ -371,8 +363,7 @@ pub(crate) mod test { let noop = f_build.add_dataflow_op(Noop { ty: QB }, [i1])?; let i1 = noop.out_wire(0); - let mut nested = - f_build.dfg_builder(FunctionType::new(type_row![], type_row![QB]), None, [])?; + let mut nested = f_build.dfg_builder(FunctionType::new(type_row![], type_row![QB]), [])?; let id_res = nested.add_dataflow_op(Noop { ty: QB }, [i1]); @@ -450,7 +441,7 @@ pub(crate) mod test { .with_extension_delta(ab_extensions.clone()); // A box which adds extensions A and B, via child Lift nodes - let mut add_ab = parent.dfg_builder(add_ab_sig, Some(ExtensionSet::new()), [w])?; + let mut add_ab = parent.dfg_builder(add_ab_sig, [w])?; let [w] = add_ab.input_wires_arr(); let lift_a = add_ab.add_dataflow_op( @@ -462,14 +453,11 @@ pub(crate) mod test { )?; let [w] = lift_a.outputs_arr(); - let lift_b = add_ab.add_dataflow_node( - NodeType::new( - Lift { - type_row: type_row![BIT], - new_extension: xb, - }, - ExtensionSet::from_iter([xa]), - ), + let lift_b = add_ab.add_dataflow_op( + Lift { + type_row: type_row![BIT], + new_extension: xb, + }, [w], )?; let [w] = lift_b.outputs_arr(); @@ -479,16 +467,13 @@ pub(crate) mod test { // Add another node (a sibling to add_ab) which adds extension C // via a child lift node - let mut add_c = parent.dfg_builder(add_c_sig, Some(ab_extensions.clone()), [w])?; + let mut add_c = parent.dfg_builder(add_c_sig, [w])?; let [w] = add_c.input_wires_arr(); - let lift_c = add_c.add_dataflow_node( - NodeType::new( - Lift { - type_row: type_row![BIT], - new_extension: xc, - }, - ab_extensions, - ), + let lift_c = add_c.add_dataflow_op( + Lift { + type_row: type_row![BIT], + new_extension: xc, + }, [w], )?; let wires: Vec = lift_c.outputs().collect(); @@ -504,10 +489,10 @@ pub(crate) mod test { fn non_cfg_ancestor() -> Result<(), BuildError> { let unit_sig = FunctionType::new(type_row![Type::UNIT], type_row![Type::UNIT]); let mut b = DFGBuilder::new(unit_sig.clone())?; - let b_child = b.dfg_builder(unit_sig.clone(), None, [b.input().out_wire(0)])?; + let b_child = b.dfg_builder(unit_sig.clone(), [b.input().out_wire(0)])?; let b_child_in_wire = b_child.input().out_wire(0); b_child.finish_with_outputs([])?; - let b_child_2 = b.dfg_builder(unit_sig.clone(), None, [])?; + let b_child_2 = b.dfg_builder(unit_sig.clone(), [])?; // DFG block has edge coming a sibling block, which is only valid for // CFGs @@ -528,17 +513,16 @@ pub(crate) mod test { fn no_relation_edge() -> Result<(), BuildError> { let unit_sig = FunctionType::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(), None, [b.input().out_wire(0)])?; - let b_child_child = - b_child.dfg_builder(unit_sig.clone(), None, [b_child.input().out_wire(0)])?; + 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(), None, [])?; + let mut b_child_2 = b.dfg_builder(unit_sig.clone(), [])?; let b_child_2_child = - b_child_2.dfg_builder(unit_sig.clone(), None, [b_child_2.input().out_wire(0)])?; + 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]); diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 9866ab326..84ca942d3 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -13,11 +13,9 @@ use crate::types::{PolyFuncType, Type, TypeBound}; use crate::ops::handle::{AliasID, FuncID, NodeHandle}; -use crate::Node; +use crate::{Hugr, Node}; use smol_str::SmolStr; -use crate::{hugr::NodeType, Hugr}; - /// Builder for a HUGR module. #[derive(Debug, Clone, PartialEq)] pub struct ModuleBuilder(pub(super) T); @@ -86,13 +84,10 @@ impl + AsRef> ModuleBuilder { .clone(); let body = signature.body().clone(); self.hugr_mut() - .replace_op( - f_node, - NodeType::new_pure(ops::FuncDefn { name, signature }), - ) + .replace_op(f_node, ops::FuncDefn { name, signature }) .expect("Replacing a FuncDecl node with a FuncDefn should always be valid"); - let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body, None)?; + let db = DFGBuilder::create_with_io(self.hugr_mut(), f_node, body)?; Ok(FunctionBuilder::from_dfg_builder(db)) } @@ -108,10 +103,10 @@ impl + AsRef> ModuleBuilder { signature: PolyFuncType, ) -> Result, BuildError> { // TODO add param names to metadata - let declare_n = self.add_child_node(NodeType::new_pure(ops::FuncDecl { + let declare_n = self.add_child_node(ops::FuncDecl { signature, name: name.into(), - })); + }); Ok(declare_n.into()) } diff --git a/hugr-core/src/builder/tail_loop.rs b/hugr-core/src/builder/tail_loop.rs index 2bee9bcfa..24804dc15 100644 --- a/hugr-core/src/builder/tail_loop.rs +++ b/hugr-core/src/builder/tail_loop.rs @@ -1,7 +1,7 @@ use crate::extension::ExtensionSet; use crate::ops; -use crate::hugr::{views::HugrView, NodeType}; +use crate::hugr::views::HugrView; use crate::types::{FunctionType, TypeRow}; use crate::{Hugr, Node}; @@ -22,7 +22,7 @@ impl + AsRef> TailLoopBuilder { tail_loop: &ops::TailLoop, ) -> Result { let signature = FunctionType::new(tail_loop.body_input_row(), tail_loop.body_output_row()); - let dfg_build = DFGBuilder::create_with_io(base, loop_node, signature, None)?; + let dfg_build = DFGBuilder::create_with_io(base, loop_node, signature)?; Ok(TailLoopBuilder::from_dfg_builder(dfg_build)) } @@ -83,8 +83,7 @@ impl TailLoopBuilder { rest: inputs_outputs.into(), extension_delta, }; - // TODO: Allow input extensions to be specified - let base = Hugr::new(NodeType::new_open(tail_loop.clone())); + let base = Hugr::new(tail_loop.clone().into()); let root = base.root(); Self::create_with_io(base, root, &tail_loop) } diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index 0ab25f7ba..a191f38c3 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -25,14 +25,12 @@ use thiserror::Error; pub use self::views::{HugrView, RootTagged}; use crate::core::NodeIndex; -use crate::extension::{ExtensionRegistry, ExtensionSet}; +use crate::extension::ExtensionRegistry; use crate::ops::custom::resolve_extension_ops; -use crate::ops::{OpTag, OpTrait, OpType, DEFAULT_OPTYPE}; -use crate::types::FunctionType; +use crate::ops::OpTag; +pub use crate::ops::{OpType, DEFAULT_OPTYPE}; use crate::{Direction, Node}; -use delegate::delegate; - /// The Hugr data structure. #[derive(Clone, Debug, PartialEq)] pub struct Hugr { @@ -46,115 +44,15 @@ pub struct Hugr { root: portgraph::NodeIndex, /// Operation types for each node. - op_types: UnmanagedDenseMap, + op_types: UnmanagedDenseMap, /// Node metadata metadata: UnmanagedDenseMap>, } -#[derive(Clone, Debug, Default, PartialEq, serde::Serialize, serde::Deserialize)] -/// The type of a node on a graph. In addition to the [`OpType`], it also -/// describes the extensions inferred to be used by the node. -pub struct NodeType { - /// The underlying OpType - op: OpType, - /// The extensions that the signature has been specialised to - input_extensions: Option, -} - -/// The default NodeType, with open extensions -pub const DEFAULT_NODETYPE: NodeType = NodeType { - op: DEFAULT_OPTYPE, - input_extensions: None, // Default for any Option -}; - -impl NodeType { - /// Create a new optype with some ExtensionSet - pub fn new(op: impl Into, input_extensions: impl Into>) -> Self { - NodeType { - op: op.into(), - input_extensions: input_extensions.into(), - } - } - - /// Instantiate an OpType with no input extensions - pub fn new_pure(op: impl Into) -> Self { - NodeType { - op: op.into(), - input_extensions: Some(ExtensionSet::new()), - } - } - - /// Instantiate an OpType with an unknown set of input extensions - /// (to be inferred later) - pub fn new_open(op: impl Into) -> Self { - NodeType { - op: op.into(), - input_extensions: None, - } - } - - /// Instantiate an [OpType] with the default set of input extensions - /// for that OpType. - pub fn new_auto(op: impl Into) -> Self { - let op = op.into(); - if OpTag::ModuleOp.is_superset(op.tag()) { - Self::new_pure(op) - } else { - Self::new_open(op) - } - } - - /// Get the function type from the embedded op - pub fn op_signature(&self) -> Option { - self.op.dataflow_signature() - } - - /// The input extensions defined for this node. - /// - /// The output extensions will correspond to the input extensions plus any - /// extension delta defined by the operation type. - /// - /// If the input extensions are not known, this will return None. - pub fn input_extensions(&self) -> Option<&ExtensionSet> { - self.input_extensions.as_ref() - } - - /// Gets the underlying [OpType] i.e. without any [input_extensions] - /// - /// [input_extensions]: NodeType::input_extensions - pub fn op(&self) -> &OpType { - &self.op - } - - /// Returns the underlying [OpType] i.e. without any [input_extensions] - /// - /// [input_extensions]: NodeType::input_extensions - pub fn into_op(self) -> OpType { - self.op - } - - delegate! { - to self.op { - /// Tag identifying the operation. - pub fn tag(&self) -> OpTag; - /// Returns the number of inputs ports for the operation. - pub fn input_count(&self) -> usize; - /// Returns the number of outputs ports for the operation. - pub fn output_count(&self) -> usize; - } - } -} - -impl> From for NodeType { - fn from(value: T) -> Self { - NodeType::new_auto(value.into()) - } -} - impl Default for Hugr { fn default() -> Self { - Self::new(NodeType::new_pure(crate::ops::Module)) + Self::new(crate::ops::Module.into()) } } @@ -181,7 +79,7 @@ pub type NodeMetadataMap = serde_json::Map; /// Public API for HUGRs. impl Hugr { /// Create a new Hugr, with a single root node. - pub fn new(root_node: NodeType) -> Self { + pub fn new(root_node: OpType) -> Self { Self::with_capacity(root_node, 0, 0) } @@ -210,8 +108,7 @@ impl Hugr { /// Internal API for HUGRs, not intended for use by users. impl Hugr { /// Create a new Hugr, with a single root node and preallocated capacity. - // TODO: Make this take a NodeType - pub(crate) fn with_capacity(root_node: NodeType, nodes: usize, ports: usize) -> Self { + pub(crate) fn with_capacity(root_node: OpType, nodes: usize, ports: usize) -> Self { let mut graph = MultiPortGraph::with_capacity(nodes, ports); let hierarchy = Hierarchy::new(); let mut op_types = UnmanagedDenseMap::with_capacity(nodes); @@ -236,7 +133,7 @@ impl Hugr { } /// Add a node to the graph. - pub(crate) fn add_node(&mut self, nodetype: NodeType) -> Node { + pub(crate) fn add_node(&mut self, nodetype: OpType) -> Node { let node = self .graph .add_node(nodetype.input_count(), nodetype.output_count()); diff --git a/hugr-core/src/hugr/hugrmut.rs b/hugr-core/src/hugr/hugrmut.rs index c06d87fd5..08d852249 100644 --- a/hugr-core/src/hugr/hugrmut.rs +++ b/hugr-core/src/hugr/hugrmut.rs @@ -7,7 +7,7 @@ use portgraph::view::{NodeFilter, NodeFiltered}; use portgraph::{LinkMut, NodeIndex, PortMut, PortView, SecondaryMap}; use crate::hugr::views::SiblingSubgraph; -use crate::hugr::{HugrView, Node, NodeType, RootTagged}; +use crate::hugr::{HugrView, Node, OpType, RootTagged}; use crate::hugr::{NodeMetadata, Rewrite}; use crate::{Hugr, IncomingPort, OutgoingPort, Port, PortIndex}; @@ -74,7 +74,7 @@ pub trait HugrMut: HugrMutInternals { /// /// If the parent is not in the graph. #[inline] - fn add_node_with_parent(&mut self, parent: Node, op: impl Into) -> Node { + fn add_node_with_parent(&mut self, parent: Node, op: impl Into) -> Node { panic_invalid_node(self, parent); self.hugr_mut().add_node_with_parent(parent, op) } @@ -87,7 +87,7 @@ pub trait HugrMut: HugrMutInternals { /// /// If the sibling is not in the graph, or if the sibling is the root node. #[inline] - fn add_node_before(&mut self, sibling: Node, nodetype: impl Into) -> Node { + fn add_node_before(&mut self, sibling: Node, nodetype: impl Into) -> Node { panic_invalid_non_root(self, sibling); self.hugr_mut().add_node_before(sibling, nodetype) } @@ -100,7 +100,7 @@ pub trait HugrMut: HugrMutInternals { /// /// If the sibling is not in the graph, or if the sibling is the root node. #[inline] - fn add_node_after(&mut self, sibling: Node, op: impl Into) -> Node { + fn add_node_after(&mut self, sibling: Node, op: impl Into) -> Node { panic_invalid_non_root(self, sibling); self.hugr_mut().add_node_after(sibling, op) } @@ -237,7 +237,7 @@ fn translate_indices(node_map: HashMap) -> HashMap + AsMut> HugrMut for T { - fn add_node_with_parent(&mut self, parent: Node, node: impl Into) -> Node { + fn add_node_with_parent(&mut self, parent: Node, node: impl Into) -> Node { let node = self.as_mut().add_node(node.into()); self.as_mut() .hierarchy @@ -246,7 +246,7 @@ impl + AsMut> HugrMut for T { node } - fn add_node_before(&mut self, sibling: Node, nodetype: impl Into) -> Node { + fn add_node_before(&mut self, sibling: Node, nodetype: impl Into) -> Node { let node = self.as_mut().add_node(nodetype.into()); self.as_mut() .hierarchy @@ -255,7 +255,7 @@ impl + AsMut> HugrMut for T { node } - fn add_node_after(&mut self, sibling: Node, op: impl Into) -> Node { + fn add_node_after(&mut self, sibling: Node, op: impl Into) -> Node { let node = self.as_mut().add_node(op.into()); self.as_mut() .hierarchy @@ -341,7 +341,7 @@ impl + AsMut> HugrMut for T { let (new_root, node_map) = insert_hugr_internal(self.as_mut(), root, other); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let nodetype = other.get_nodetype(node.into()); + let nodetype = other.get_optype(node.into()); self.as_mut().op_types.set(new_node, nodetype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); @@ -372,7 +372,7 @@ impl + AsMut> HugrMut for T { let node_map = insert_subgraph_internal(self.as_mut(), root, other, &portgraph); // Update the optypes and metadata, copying them from the other graph. for (&node, &new_node) in node_map.iter() { - let nodetype = other.get_nodetype(node.into()); + let nodetype = other.get_optype(node.into()); self.as_mut().op_types.set(new_node, nodetype.clone()); let meta = other.base_hugr().metadata.get(node); self.as_mut().metadata.set(new_node, meta.clone()); @@ -534,8 +534,7 @@ mod test { ); { - let f_in = - hugr.add_node_with_parent(f, NodeType::new_pure(ops::Input::new(type_row![NAT]))); + let f_in = hugr.add_node_with_parent(f, ops::Input::new(type_row![NAT])); let f_out = hugr.add_node_with_parent(f, ops::Output::new(type_row![NAT, NAT])); let noop = hugr.add_node_with_parent(f, Noop { ty: NAT }); diff --git a/hugr-core/src/hugr/internal.rs b/hugr-core/src/hugr/internal.rs index 3cf2b9c63..b8ac050c3 100644 --- a/hugr-core/src/hugr/internal.rs +++ b/hugr-core/src/hugr/internal.rs @@ -5,10 +5,11 @@ use std::ops::Range; use portgraph::{LinkView, MultiPortGraph, PortMut, PortView}; use crate::ops::handle::NodeHandle; +use crate::ops::OpTrait; use crate::{Direction, Hugr, Node}; use super::hugrmut::{panic_invalid_node, panic_invalid_non_root}; -use super::{HugrError, NodeType, RootTagged}; +use super::{HugrError, OpType, RootTagged}; /// Trait for accessing the internals of a Hugr(View). /// @@ -138,8 +139,9 @@ pub trait HugrMutInternals: RootTagged { /// # Panics /// /// If the node is not in the graph. - fn replace_op(&mut self, node: Node, op: NodeType) -> Result { + fn replace_op(&mut self, node: Node, op: impl Into) -> Result { panic_invalid_node(self, node); + let op = op.into(); if node == self.root() && !Self::RootHandle::TAG.is_superset(op.tag()) { return Err(HugrError::InvalidTag { required: Self::RootHandle::TAG, @@ -206,9 +208,9 @@ impl + AsMut> HugrMutInternals for T { .expect("Inserting a newly-created node into the hierarchy should never fail."); } - fn replace_op(&mut self, node: Node, op: NodeType) -> Result { + fn replace_op(&mut self, node: Node, op: impl Into) -> Result { // We know RootHandle=Node here so no need to check let cur = self.hugr_mut().op_types.get_mut(node.pg_index()); - Ok(std::mem::replace(cur, op)) + Ok(std::mem::replace(cur, op.into())) } } diff --git a/hugr-core/src/hugr/rewrite/inline_dfg.rs b/hugr-core/src/hugr/rewrite/inline_dfg.rs index adba1170f..b98e57bf5 100644 --- a/hugr-core/src/hugr/rewrite/inline_dfg.rs +++ b/hugr-core/src/hugr/rewrite/inline_dfg.rs @@ -201,7 +201,6 @@ mod test { let inner = { let mut inner = outer.dfg_builder( FunctionType::new_endo(vec![int_ty.clone()]).with_extension_delta(delta), - None, [a], )?; let [a] = inner.input_wires_arr(); @@ -261,11 +260,7 @@ mod test { .add_dataflow_op(test_quantum_extension::h_gate(), [p])? .outputs_arr(); let swap = { - let swap = h.dfg_builder( - FunctionType::new_endo(type_row![QB_T, QB_T]), - None, - [p_h, q], - )?; + let swap = h.dfg_builder(FunctionType::new_endo(type_row![QB_T, QB_T]), [p_h, q])?; let [a, b] = swap.input_wires_arr(); swap.finish_with_outputs([b, a])? }; @@ -363,7 +358,6 @@ mod test { let h_b = outer.add_dataflow_op(test_quantum_extension::h_gate(), [b])?; let mut inner = outer.dfg_builder( FunctionType::new_endo(type_row![QB_T]).with_extension_delta(float_types::EXTENSION_ID), - None, h_b.outputs(), )?; let [i] = inner.input_wires_arr(); diff --git a/hugr-core/src/hugr/rewrite/outline_cfg.rs b/hugr-core/src/hugr/rewrite/outline_cfg.rs index 335a7a44d..5880708e0 100644 --- a/hugr-core/src/hugr/rewrite/outline_cfg.rs +++ b/hugr-core/src/hugr/rewrite/outline_cfg.rs @@ -125,10 +125,8 @@ impl Rewrite for OutlineCfg { // 2. new_block contains input node, sub-cfg, exit node all connected let (new_block, cfg_node) = { - let input_extensions = h.get_nodetype(entry).input_extensions().cloned(); let mut new_block_bldr = BlockBuilder::new( inputs.clone(), - input_extensions.clone(), vec![type_row![]], outputs.clone(), extension_delta.clone(), @@ -136,7 +134,7 @@ impl Rewrite for OutlineCfg { .unwrap(); let wires_in = inputs.iter().cloned().zip(new_block_bldr.input_wires()); let cfg = new_block_bldr - .cfg_builder(wires_in, input_extensions, outputs, extension_delta) + .cfg_builder(wires_in, outputs, extension_delta) .unwrap(); let cfg = cfg.finish_sub_container().unwrap(); let unit_sum = new_block_bldr.add_constant(ops::Value::unary_unit_sum()); diff --git a/hugr-core/src/hugr/rewrite/replace.rs b/hugr-core/src/hugr/rewrite/replace.rs index 9cc028053..5a938b685 100644 --- a/hugr-core/src/hugr/rewrite/replace.rs +++ b/hugr-core/src/hugr/rewrite/replace.rs @@ -455,7 +455,7 @@ mod test { }; use crate::hugr::internal::HugrMutInternals; use crate::hugr::rewrite::replace::WhichHugr; - use crate::hugr::{HugrMut, NodeType, Rewrite}; + use crate::hugr::{HugrMut, Rewrite}; use crate::ops::custom::{CustomOp, OpaqueOp}; use crate::ops::dataflow::DataflowOpTrait; use crate::ops::handle::{BasicBlockID, ConstID, NodeHandle}; @@ -523,9 +523,12 @@ mod test { // Replacement: one BB with two DFGs inside. // Use Hugr rather than Builder because DFGs must be empty (not even Input/Output). - let mut replacement = Hugr::new(NodeType::new_open(ops::CFG { - signature: FunctionType::new_endo(just_list.clone()), - })); + let mut replacement = Hugr::new( + ops::CFG { + signature: FunctionType::new_endo(just_list.clone()), + } + .into(), + ); let r_bb = replacement.add_node_with_parent( replacement.root(), DataflowBlock { @@ -676,14 +679,14 @@ mod test { let case1 = case1.finish_with_outputs(foo.outputs())?.node(); let mut case2 = cond.case_builder(1)?; let bar = case2.add_dataflow_op(mk_op("bar"), case2.input_wires())?; - let mut baz_dfg = case2.dfg_builder(utou.clone(), None, bar.outputs())?; + let mut baz_dfg = case2.dfg_builder(utou.clone(), bar.outputs())?; let baz = baz_dfg.add_dataflow_op(mk_op("baz"), baz_dfg.input_wires())?; let baz_dfg = baz_dfg.finish_with_outputs(baz.outputs())?; let case2 = case2.finish_with_outputs(baz_dfg.outputs())?.node(); let cond = cond.finish_sub_container()?; let h = h.finish_hugr_with_outputs(cond.outputs(), &PRELUDE_REGISTRY)?; - let mut r_hugr = Hugr::new(NodeType::new_open(h.get_optype(cond.node()).clone())); + let mut r_hugr = Hugr::new(h.get_optype(cond.node()).clone()); let r1 = r_hugr.add_node_with_parent( r_hugr.root(), Case { diff --git a/hugr-core/src/hugr/rewrite/simple_replace.rs b/hugr-core/src/hugr/rewrite/simple_replace.rs index 387b97b76..a70c275aa 100644 --- a/hugr-core/src/hugr/rewrite/simple_replace.rs +++ b/hugr-core/src/hugr/rewrite/simple_replace.rs @@ -244,7 +244,6 @@ pub(in crate::hugr::rewrite) mod test { let mut inner_builder = func_builder.dfg_builder( FunctionType::new(type_row![QB, QB], type_row![QB, QB]), - None, [qb0, qb1], )?; let inner_graph = { diff --git a/hugr-core/src/hugr/serialize.rs b/hugr-core/src/hugr/serialize.rs index a93eb2070..f5554094b 100644 --- a/hugr-core/src/hugr/serialize.rs +++ b/hugr-core/src/hugr/serialize.rs @@ -5,8 +5,7 @@ use std::collections::HashMap; use thiserror::Error; use crate::core::NodeIndex; -use crate::extension::ExtensionSet; -use crate::hugr::{Hugr, NodeType}; +use crate::hugr::Hugr; use crate::ops::OpType; use crate::{Node, PortIndex}; use portgraph::hierarchy::AttachError; @@ -48,7 +47,6 @@ impl Versioned { #[derive(Clone, Serialize, Deserialize, PartialEq, Debug)] struct NodeSer { parent: Node, - input_extensions: Option, #[serde(flatten)] op: OpType, } @@ -143,12 +141,11 @@ impl TryFrom<&Hugr> for SerHugrV1 { let mut metadata = vec![None; hugr.node_count()]; for n in hugr.nodes() { let parent = node_rekey[&hugr.get_parent(n).unwrap_or(n)]; - let opt = hugr.get_nodetype(n); + let opt = hugr.get_optype(n); let new_node = node_rekey[&n].index(); nodes[new_node] = Some(NodeSer { parent, - input_extensions: opt.input_extensions.clone(), - op: opt.op.clone(), + op: opt.clone(), }); metadata[new_node].clone_from(hugr.metadata.get(n.pg_index())); } @@ -205,7 +202,6 @@ impl TryFrom for Hugr { let mut nodes = nodes.into_iter(); let NodeSer { parent: root_parent, - input_extensions, op: root_type, } = nodes.next().unwrap(); if root_parent.index() != 0 { @@ -213,17 +209,10 @@ impl TryFrom for Hugr { } // if there are any unconnected ports or copy nodes the capacity will be // an underestimate - let mut hugr = Hugr::with_capacity( - NodeType::new(root_type, input_extensions), - nodes.len(), - edges.len() * 2, - ); + let mut hugr = Hugr::with_capacity(root_type, nodes.len(), edges.len() * 2); for node_ser in nodes { - hugr.add_node_with_parent( - node_ser.parent, - NodeType::new(node_ser.op, node_ser.input_extensions), - ); + hugr.add_node_with_parent(node_ser.parent, node_ser.op); } if let Some(metadata) = metadata { diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index fdd58e398..712c871b4 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -5,7 +5,7 @@ use crate::builder::{ }; use crate::extension::prelude::{BOOL_T, PRELUDE_ID, QB_T, USIZE_T}; use crate::extension::simple_op::MakeRegisteredOp; -use crate::extension::{test::SimpleOpDef, EMPTY_REG, PRELUDE_REGISTRY}; +use crate::extension::{test::SimpleOpDef, ExtensionSet, EMPTY_REG, PRELUDE_REGISTRY}; use crate::hugr::internal::HugrMutInternals; use crate::ops::custom::{ExtensionOp, OpaqueOp}; use crate::ops::{self, dataflow::IOTrait, Input, Module, Noop, Output, Value, DFG}; @@ -226,11 +226,11 @@ fn simpleser() { let mut h = Hierarchy::new(); let mut op_types = UnmanagedDenseMap::new(); - op_types[root] = NodeType::new_open(gen_optype(&g, root)); + op_types[root] = gen_optype(&g, root); for n in [a, b, c] { h.push_child(n, root).unwrap(); - op_types[n] = NodeType::new_pure(gen_optype(&g, n)); + op_types[n] = gen_optype(&g, n); } let hugr = Hugr { @@ -469,7 +469,6 @@ fn roundtrip_polyfunctype(#[case] poly_func_type: PolyFuncType) { fn roundtrip_optype(#[case] optype: impl Into + std::fmt::Debug) { check_testing_roundtrip(NodeSer { parent: portgraph::NodeIndex::new(0).into(), - input_extensions: None, op: optype.into(), }); } @@ -477,7 +476,6 @@ fn roundtrip_optype(#[case] optype: impl Into + std::fmt::Debug) { mod proptest { use super::check_testing_roundtrip; use super::{NodeSer, SimpleOpDef}; - use crate::extension::ExtensionSet; use crate::ops::{OpType, Value}; use crate::types::{PolyFuncType, Type}; use proptest::prelude::*; @@ -488,14 +486,9 @@ mod proptest { fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { ( (0..i32::MAX as usize).prop_map(|x| portgraph::NodeIndex::new(x).into()), - any::>(), any::(), ) - .prop_map(|(parent, input_extensions, op)| NodeSer { - parent, - input_extensions, - op, - }) + .prop_map(|(parent, op)| NodeSer { parent, op }) .boxed() } } diff --git a/hugr-core/src/hugr/validate.rs b/hugr-core/src/hugr/validate.rs index ce6532ab0..be2e06003 100644 --- a/hugr-core/src/hugr/validate.rs +++ b/hugr-core/src/hugr/validate.rs @@ -19,7 +19,6 @@ use crate::types::{EdgeKind, FunctionType}; use crate::{Direction, Hugr, Node, Port}; use super::views::{HierarchyView, HugrView, SiblingGraph}; -use super::NodeType; /// Structure keeping track of pre-computed information used in the validation /// process. @@ -152,8 +151,7 @@ impl<'a, 'b> ValidationContext<'a, 'b> { /// - Matching the number of ports with the signature /// - Dataflow ports are correct. See `validate_df_port` fn validate_node(&self, node: Node) -> Result<(), ValidationError> { - let node_type = self.hugr.get_nodetype(node); - let op_type = &node_type.op; + let op_type = self.hugr.get_optype(node); // The Hugr can have only one root node. if node == self.hugr.root() { @@ -210,7 +208,7 @@ impl<'a, 'b> ValidationContext<'a, 'b> { })?; // Thirdly that the node has correct children - self.validate_children(node, node_type)?; + self.validate_children(node, op_type)?; Ok(()) } @@ -315,8 +313,7 @@ impl<'a, 'b> ValidationContext<'a, 'b> { /// Check operation-specific constraints. /// /// These are flags defined for each operation type as an [`OpValidityFlags`] object. - fn validate_children(&self, node: Node, node_type: &NodeType) -> Result<(), ValidationError> { - let op_type = &node_type.op; + fn validate_children(&self, node: Node, op_type: &OpType) -> Result<(), ValidationError> { let flags = op_type.validity_flags(); if self.hugr.hierarchy.child_count(node.pg_index()) > 0 { diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index 47c197416..a7c318b8c 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -77,7 +77,7 @@ fn invalid_root() { Err(ValidationError::NoParent { node }) => assert_eq!(node, other) ); b.set_parent(other, root); - b.replace_op(other, NodeType::new_pure(declare_op)).unwrap(); + b.replace_op(other, declare_op).unwrap(); b.add_ports(other, Direction::Outgoing, 1); assert_eq!(b.validate(&EMPTY_REG), Ok(())); @@ -96,7 +96,7 @@ fn invalid_root() { fn leaf_root() { let leaf_op: OpType = Noop { ty: USIZE_T }.into(); - let b = Hugr::new(NodeType::new_pure(leaf_op)); + let b = Hugr::new(leaf_op); assert_eq!(b.validate(&EMPTY_REG), Ok(())); } @@ -107,7 +107,7 @@ fn dfg_root() { } .into(); - let mut b = Hugr::new(NodeType::new_pure(dfg_op)); + let mut b = Hugr::new(dfg_op); let root = b.root(); add_df_children(&mut b, root, 1); assert_eq!(b.update_validate(&EMPTY_REG), Ok(())); @@ -175,36 +175,26 @@ fn df_children_restrictions() { .unwrap(); // Replace the output operation of the df subgraph with a copy - b.replace_op(output, NodeType::new_pure(Noop { ty: NAT })) - .unwrap(); + b.replace_op(output, Noop { ty: NAT }).unwrap(); assert_matches!( b.validate(&EMPTY_REG), Err(ValidationError::InvalidInitialChild { parent, .. }) => assert_eq!(parent, def) ); // Revert it back to an output, but with the wrong number of ports - b.replace_op( - output, - NodeType::new_pure(ops::Output::new(type_row![BOOL_T])), - ) - .unwrap(); + b.replace_op(output, ops::Output::new(type_row![BOOL_T])) + .unwrap(); assert_matches!( b.validate(&EMPTY_REG), Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::IOSignatureMismatch { child, .. }, .. }) => {assert_eq!(parent, def); assert_eq!(child, output.pg_index())} ); - b.replace_op( - output, - NodeType::new_pure(ops::Output::new(type_row![BOOL_T, BOOL_T])), - ) - .unwrap(); + b.replace_op(output, ops::Output::new(type_row![BOOL_T, BOOL_T])) + .unwrap(); // After fixing the output back, replace the copy with an output op - b.replace_op( - copy, - NodeType::new_pure(ops::Output::new(type_row![BOOL_T, BOOL_T])), - ) - .unwrap(); + b.replace_op(copy, ops::Output::new(type_row![BOOL_T, BOOL_T])) + .unwrap(); assert_matches!( b.validate(&EMPTY_REG), Err(ValidationError::InvalidChildren { parent, source: ChildrenValidationError::InternalIOChildren { child, .. }, .. }) @@ -265,7 +255,6 @@ fn no_ext_edge_into_func() -> Result<(), Box> { let mut dfg = h.dfg_builder( FunctionType::new(vec![], Type::new_function(b2b.clone())), - None, [], )?; let mut func = dfg.define_function("AndWithOuter", b2b.clone().into())?; @@ -879,9 +868,9 @@ fn cfg_children_restrictions() { b.validate(&EMPTY_REG).unwrap(); b.replace_op( copy, - NodeType::new_pure(ops::CFG { + ops::CFG { signature: FunctionType::new(type_row![BOOL_T], type_row![BOOL_T]), - }), + }, ) .unwrap(); assert_matches!( @@ -942,25 +931,22 @@ fn cfg_children_restrictions() { // Change the types in the BasicBlock node to work on qubits instead of bits b.replace_op( block, - NodeType::new_pure(ops::DataflowBlock { + ops::DataflowBlock { inputs: type_row![QB_T], sum_rows: vec![type_row![]], other_outputs: type_row![QB_T], extension_delta: ExtensionSet::new(), - }), + }, ) .unwrap(); let mut block_children = b.hierarchy.children(block.pg_index()); let block_input = block_children.next().unwrap().into(); let block_output = block_children.next_back().unwrap().into(); - b.replace_op( - block_input, - NodeType::new_pure(ops::Input::new(type_row![QB_T])), - ) - .unwrap(); + b.replace_op(block_input, ops::Input::new(type_row![QB_T])) + .unwrap(); b.replace_op( block_output, - NodeType::new_pure(ops::Output::new(type_row![Type::new_unit_sum(1), QB_T])), + ops::Output::new(type_row![Type::new_unit_sum(1), QB_T]), ) .unwrap(); assert_matches!( @@ -1035,30 +1021,27 @@ mod extension_tests { let parent = parent_f( FunctionType::new_endo(USIZE_T).with_extension_delta(parent_extensions.clone()), ); - let mut hugr = Hugr::new(NodeType::new_pure(parent)); + let mut hugr = Hugr::new(parent); let input = hugr.add_node_with_parent( hugr.root(), - NodeType::new_pure(ops::Input { + ops::Input { types: type_row![USIZE_T], - }), + }, ); let output = hugr.add_node_with_parent( hugr.root(), - NodeType::new( - ops::Output { - types: type_row![USIZE_T], - }, - Some(XB.into()), - ), + ops::Output { + types: type_row![USIZE_T], + }, ); let lift = hugr.add_node_with_parent( hugr.root(), - NodeType::new_pure(ops::Lift { + ops::Lift { type_row: type_row![USIZE_T], new_extension: XB, - }), + }, ); hugr.connect(input, 0, lift, 0); @@ -1127,7 +1110,7 @@ mod extension_tests { outputs: type_row![USIZE_T], extension_delta: parent_extensions.clone(), }; - let mut hugr = Hugr::new(NodeType::new_pure(parent)); + let mut hugr = Hugr::new(parent.into()); // First case with no delta should be ok in all cases. Second one may not be. let [_, child] = [None, Some(XB)].map(|case_ext| { @@ -1142,28 +1125,25 @@ mod extension_tests { let input = hugr.add_node_with_parent( case, - NodeType::new_pure(ops::Input { + ops::Input { types: type_row![USIZE_T], - }), + }, ); let output = hugr.add_node_with_parent( case, - NodeType::new( - ops::Output { - types: type_row![USIZE_T], - }, - Some(case_exts), - ), + ops::Output { + types: type_row![USIZE_T], + }, ); let res = match case_ext { None => input, Some(new_ext) => { let lift = hugr.add_node_with_parent( case, - NodeType::new_pure(ops::Lift { + ops::Lift { type_row: type_row![USIZE_T], new_extension: new_ext, - }), + }, ); hugr.connect(input, 0, lift, 0); lift @@ -1227,7 +1207,7 @@ mod extension_tests { } fn make_bb(t: Type, es: ExtensionSet) -> DFGWrapper { - BlockBuilder::new(t.clone(), None, vec![t.into()], type_row![], es).unwrap() + BlockBuilder::new(t.clone(), vec![t.into()], type_row![], es).unwrap() } fn make_tailloop(t: Type, es: ExtensionSet) -> DFGWrapper> { diff --git a/hugr-core/src/hugr/views.rs b/hugr-core/src/hugr/views.rs index 607f88768..6371e7772 100644 --- a/hugr-core/src/hugr/views.rs +++ b/hugr-core/src/hugr/views.rs @@ -26,8 +26,7 @@ use portgraph::{multiportgraph, LinkView, PortView}; use super::internal::HugrInternals; use super::{ - Hugr, HugrError, HugrMut, NodeMetadata, NodeMetadataMap, NodeType, ValidationError, - DEFAULT_NODETYPE, + Hugr, HugrError, HugrMut, NodeMetadata, NodeMetadataMap, ValidationError, DEFAULT_OPTYPE, }; use crate::extension::ExtensionRegistry; use crate::ops::handle::NodeHandle; @@ -80,8 +79,8 @@ pub trait HugrView: HugrInternals { /// Return the type of the HUGR root node. #[inline] - fn root_type(&self) -> &NodeType { - let node_type = self.get_nodetype(self.root()); + fn root_type(&self) -> &OpType { + let node_type = self.get_optype(self.root()); // Sadly no way to do this at present // debug_assert!(Self::RootHandle::can_hold(node_type.tag())); node_type @@ -119,15 +118,9 @@ pub trait HugrView: HugrInternals { /// Returns the operation type of a node. #[inline] fn get_optype(&self, node: Node) -> &OpType { - &self.get_nodetype(node).op - } - - /// Returns the type of a node. - #[inline] - fn get_nodetype(&self, node: Node) -> &NodeType { match self.contains_node(node) { true => self.base_hugr().op_types.get(node.pg_index()), - false => &DEFAULT_NODETYPE, + false => &DEFAULT_OPTYPE, } } @@ -323,8 +316,8 @@ pub trait HugrView: HugrInternals { /// If the node isn't a dataflow parent, then return None #[inline] fn get_io(&self, node: Node) -> Option<[Node; 2]> { - let op = self.get_nodetype(node); - // Nodes outside the view have no children (and a non-DataflowParent NodeType::default()) + let op = self.get_optype(node); + // Nodes outside the view have no children (and a non-DataflowParent OpType::default()) if OpTag::DataflowParent.is_superset(op.tag()) { self.children(node).take(2).collect_vec().try_into().ok() } else { diff --git a/hugr-core/src/hugr/views/descendants.rs b/hugr-core/src/hugr/views/descendants.rs index bee6dbd26..264890a31 100644 --- a/hugr-core/src/hugr/views/descendants.rs +++ b/hugr-core/src/hugr/views/descendants.rs @@ -231,11 +231,8 @@ pub(super) mod test { let q_out = func_builder.add_dataflow_op(h_gate(), vec![qb])?; let inner_id = { - let inner_builder = func_builder.dfg_builder( - FunctionType::new(type_row![NAT], type_row![NAT]), - None, - [int], - )?; + let inner_builder = func_builder + .dfg_builder(FunctionType::new(type_row![NAT], type_row![NAT]), [int])?; let w = inner_builder.input_wires(); inner_builder.finish_with_outputs(w) }?; diff --git a/hugr-core/src/hugr/views/root_checked.rs b/hugr-core/src/hugr/views/root_checked.rs index d8d52a7ae..668fdb83d 100644 --- a/hugr-core/src/hugr/views/root_checked.rs +++ b/hugr-core/src/hugr/views/root_checked.rs @@ -72,16 +72,17 @@ mod test { use super::RootChecked; use crate::extension::ExtensionSet; use crate::hugr::internal::HugrMutInternals; - use crate::hugr::{HugrError, HugrMut, NodeType}; + use crate::hugr::{HugrError, HugrMut}; use crate::ops::handle::{BasicBlockID, CfgID, DataflowParentID, DfgID}; - use crate::ops::{DataflowBlock, MakeTuple, OpTag}; + use crate::ops::{DataflowBlock, MakeTuple, OpTag, OpType}; use crate::{ops, type_row, types::FunctionType, Hugr, HugrView}; #[test] fn root_checked() { - let root_type = NodeType::new_pure(ops::DFG { + let root_type: OpType = ops::DFG { signature: FunctionType::new(vec![], vec![]), - }); + } + .into(); let mut h = Hugr::new(root_type.clone()); let cfg_v = RootChecked::<&Hugr, CfgID>::try_new(&h); assert_eq!( @@ -94,12 +95,13 @@ mod test { let mut dfg_v = RootChecked::<&mut Hugr, DfgID>::try_new(&mut h).unwrap(); // That is a HugrMutInternal, so we can try: let root = dfg_v.root(); - let bb = NodeType::new_pure(DataflowBlock { + let bb: OpType = DataflowBlock { inputs: type_row![], other_outputs: type_row![], sum_rows: vec![type_row![]], extension_delta: ExtensionSet::new(), - }); + } + .into(); let r = dfg_v.replace_op(root, bb.clone()); assert_eq!( r, @@ -109,7 +111,7 @@ mod test { }) ); // That didn't do anything: - assert_eq!(dfg_v.get_nodetype(root), &root_type); + assert_eq!(dfg_v.get_optype(root), &root_type); // Make a RootChecked that allows any DataflowParent // We won't be able to do this by widening the bound: @@ -124,12 +126,12 @@ mod test { let mut dfp_v = RootChecked::<&mut Hugr, DataflowParentID>::try_new(&mut h).unwrap(); let r = dfp_v.replace_op(root, bb.clone()); assert_eq!(r, Ok(root_type)); - assert_eq!(dfp_v.get_nodetype(root), &bb); + assert_eq!(dfp_v.get_optype(root), &bb); // Just check we can create a nested instance (narrowing the bound) let mut bb_v = RootChecked::<_, BasicBlockID>::try_new(dfp_v).unwrap(); // And it's a HugrMut: - let nodetype = NodeType::new_pure(MakeTuple { tys: type_row![] }); + let nodetype = MakeTuple { tys: type_row![] }; bb_v.add_node_with_parent(bb_v.root(), nodetype); } } diff --git a/hugr-core/src/hugr/views/sibling.rs b/hugr-core/src/hugr/views/sibling.rs index 25bc477a4..662764e6c 100644 --- a/hugr-core/src/hugr/views/sibling.rs +++ b/hugr-core/src/hugr/views/sibling.rs @@ -381,9 +381,9 @@ mod test { use crate::builder::test::simple_dfg_hugr; use crate::builder::{Container, Dataflow, DataflowSubContainer, HugrBuilder, ModuleBuilder}; use crate::extension::PRELUDE_REGISTRY; - use crate::hugr::NodeType; use crate::ops::handle::{CfgID, DataflowParentID, DfgID, FuncID}; use crate::ops::{dataflow::IOTrait, Input, OpTag, Output}; + use crate::ops::{OpTrait, OpType}; use crate::type_row; use crate::types::{FunctionType, Type}; @@ -411,7 +411,7 @@ mod test { let mut module_builder = ModuleBuilder::new(); let fty = FunctionType::new(type_row![NAT], type_row![NAT]); let mut fbuild = module_builder.define_function("main", fty.clone().into())?; - let dfg = fbuild.dfg_builder(fty, None, fbuild.input_wires())?; + let dfg = fbuild.dfg_builder(fty, fbuild.input_wires())?; let ins = dfg.input_wires(); let sub_dfg = dfg.finish_with_outputs(ins)?; let fun = fbuild.finish_with_outputs(sub_dfg.outputs())?; @@ -455,7 +455,7 @@ mod test { ); let mut sib_mut = SiblingMut::::try_new(&mut simple_dfg_hugr, root).unwrap(); - let bad_nodetype = NodeType::new_open(crate::ops::CFG { signature }); + let bad_nodetype: OpType = crate::ops::CFG { signature }.into(); assert_eq!( sib_mut.replace_op(sib_mut.root(), bad_nodetype.clone()), Err(HugrError::InvalidTag { @@ -472,13 +472,13 @@ mod test { #[rstest] fn sibling_mut_covariance(mut simple_dfg_hugr: Hugr) { let root = simple_dfg_hugr.root(); - let case_nodetype = NodeType::new_open(crate::ops::Case { - signature: simple_dfg_hugr.root_type().op_signature().unwrap(), - }); + let case_nodetype = crate::ops::Case { + signature: simple_dfg_hugr.root_type().dataflow_signature().unwrap(), + }; let mut sib_mut = SiblingMut::::try_new(&mut simple_dfg_hugr, root).unwrap(); // As expected, we cannot replace the root with a Case assert_eq!( - sib_mut.replace_op(root, case_nodetype.clone()), + sib_mut.replace_op(root, case_nodetype), Err(HugrError::InvalidTag { required: OpTag::Dfg, actual: OpTag::Case diff --git a/hugr-core/src/ops/constant.rs b/hugr-core/src/ops/constant.rs index ea55908fa..7975c40fe 100644 --- a/hugr-core/src/ops/constant.rs +++ b/hugr-core/src/ops/constant.rs @@ -274,7 +274,7 @@ fn mono_fn_type(h: &Hugr) -> Result { } } Err(ConstTypeError::NotMonomorphicFunction { - hugr_root_type: h.root_type().op().clone(), + hugr_root_type: h.root_type().clone(), }) } diff --git a/hugr-core/src/ops/custom.rs b/hugr-core/src/ops/custom.rs index d97a8202c..8f4e3f19c 100644 --- a/hugr-core/src/ops/custom.rs +++ b/hugr-core/src/ops/custom.rs @@ -12,7 +12,7 @@ use { use crate::extension::{ConstFoldResult, ExtensionId, ExtensionRegistry, OpDef, SignatureError}; use crate::hugr::internal::HugrMutInternals; -use crate::hugr::{HugrView, NodeType}; +use crate::hugr::HugrView; use crate::types::EdgeKind; use crate::types::{type_param::TypeArg, FunctionType}; use crate::{ops, Hugr, IncomingPort, Node}; @@ -362,10 +362,9 @@ pub fn resolve_extension_ops( } // Only now can we perform the replacements as the 'for' loop was borrowing 'h' preventing use from using it mutably for (n, op) in replacements { - let node_type = NodeType::new(op, h.get_nodetype(n).input_extensions().cloned()); debug_assert_eq!(h.get_optype(n).tag(), OpTag::Leaf); - debug_assert_eq!(node_type.tag(), OpTag::Leaf); - h.replace_op(n, node_type).unwrap(); + debug_assert_eq!(op.tag(), OpTag::Leaf); + h.replace_op(n, op).unwrap(); } Ok(()) } diff --git a/hugr-passes/src/const_fold/test.rs b/hugr-passes/src/const_fold/test.rs index b5c0913bd..b116853ab 100644 --- a/hugr-passes/src/const_fold/test.rs +++ b/hugr-passes/src/const_fold/test.rs @@ -231,7 +231,6 @@ fn orphan_output() { // // We arange things so that the `or` folds away first, leaving the not // with no outputs. - use hugr_core::hugr::NodeType; use hugr_core::ops::handle::NodeHandle; let mut build = DFGBuilder::new(FunctionType::new(type_row![], vec![BOOL_T])).unwrap(); @@ -252,7 +251,7 @@ fn orphan_output() { // we delete the original Not and create a new One. This means it will be // traversed by `constant_fold_pass` after the Or. - let new_not = h.add_node_with_parent(parent, NodeType::new_auto(NotOp)); + let new_not = h.add_node_with_parent(parent, NotOp); h.connect(true_wire.node(), true_wire.source(), new_not, 0); h.disconnect(or_node, IncomingPort::from(1)); h.connect(new_not, 0, or_node, 1); diff --git a/hugr-py/src/hugr/serialization/ops.py b/hugr-py/src/hugr/serialization/ops.py index e96dfa733..60242b77d 100644 --- a/hugr-py/src/hugr/serialization/ops.py +++ b/hugr-py/src/hugr/serialization/ops.py @@ -29,7 +29,6 @@ class BaseOp(ABC, ConfiguredBaseModel): # Parent node index of node the op belongs to, used only at serialization time parent: NodeID - input_extensions: ExtensionSet | None = Field(default=None) def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: """Hook to insert type information from the input and output ports into the diff --git a/hugr-py/src/hugr/serialization/tys.py b/hugr-py/src/hugr/serialization/tys.py index f19e27340..e5bf047d5 100644 --- a/hugr-py/src/hugr/serialization/tys.py +++ b/hugr-py/src/hugr/serialization/tys.py @@ -366,9 +366,7 @@ class Signature(ConfiguredBaseModel): signature: "PolyFuncType" # The underlying signature - # The extensions which are associated with all the inputs and carried through - input_extensions: ExtensionSet - + # ALAN is it worth keeping this now/still? (Also, surprised this is "Poly"...??) # Now that all classes are defined, we need to update the ForwardRefs in all type # annotations. We use some inspect magic to find all classes defined in this file diff --git a/hugr/src/hugr.rs b/hugr/src/hugr.rs index c2b6a70b7..cdf4fe42b 100644 --- a/hugr/src/hugr.rs +++ b/hugr/src/hugr.rs @@ -3,6 +3,6 @@ // Exports everything except the `internal` module. pub use hugr_core::hugr::{ hugrmut, rewrite, serialize, validate, views, Hugr, HugrError, HugrView, IdentList, - InvalidIdentifier, NodeMetadata, NodeMetadataMap, NodeType, Rewrite, RootTagged, - SimpleReplacement, SimpleReplacementError, ValidationError, DEFAULT_NODETYPE, + InvalidIdentifier, NodeMetadata, NodeMetadataMap, OpType, Rewrite, RootTagged, + SimpleReplacement, SimpleReplacementError, ValidationError, DEFAULT_OPTYPE, }; From 088f451b702978ae02dd5b03759d1bf2163fc765 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 10 Jun 2024 14:02:43 +0100 Subject: [PATCH 36/39] Hugr::new takes impl Into --- hugr-core/src/builder.rs | 9 +++------ hugr-core/src/builder/cfg.rs | 4 ++-- hugr-core/src/builder/conditional.rs | 4 ++-- hugr-core/src/builder/dataflow.rs | 4 ++-- hugr-core/src/builder/tail_loop.rs | 2 +- hugr-core/src/hugr.rs | 6 +++--- hugr-core/src/hugr/rewrite/replace.rs | 9 +++------ hugr-core/src/hugr/validate/test.rs | 19 ++++++++----------- 8 files changed, 24 insertions(+), 33 deletions(-) diff --git a/hugr-core/src/builder.rs b/hugr-core/src/builder.rs index 19d501566..56ded9129 100644 --- a/hugr-core/src/builder.rs +++ b/hugr-core/src/builder.rs @@ -278,12 +278,9 @@ pub(crate) mod test { /// inference. Using DFGBuilder will default to a root node with an open /// extension variable pub(crate) fn closed_dfg_root_hugr(signature: FunctionType) -> Hugr { - let mut hugr = Hugr::new( - ops::DFG { - signature: signature.clone(), - } - .into(), - ); + let mut hugr = Hugr::new(ops::DFG { + signature: signature.clone(), + }); hugr.add_node_with_parent( hugr.root(), ops::Input { diff --git a/hugr-core/src/builder/cfg.rs b/hugr-core/src/builder/cfg.rs index 377ebf3cb..c7ae975ae 100644 --- a/hugr-core/src/builder/cfg.rs +++ b/hugr-core/src/builder/cfg.rs @@ -155,7 +155,7 @@ impl CFGBuilder { signature: signature.clone(), }; - let base = Hugr::new(cfg_op.into()); + let base = Hugr::new(cfg_op); let cfg_node = base.root(); CFGBuilder::create(base, cfg_node, signature.input, signature.output) } @@ -369,7 +369,7 @@ impl BlockBuilder { extension_delta, }; - let base = Hugr::new(op.into()); + let base = Hugr::new(op); let root = base.root(); Self::create(base, root) } diff --git a/hugr-core/src/builder/conditional.rs b/hugr-core/src/builder/conditional.rs index 9ecc5652d..7a3e6105b 100644 --- a/hugr-core/src/builder/conditional.rs +++ b/hugr-core/src/builder/conditional.rs @@ -172,7 +172,7 @@ impl ConditionalBuilder { outputs, extension_delta, }; - let base = Hugr::new(op.into()); + let base = Hugr::new(op); let conditional_node = base.root(); Ok(ConditionalBuilder { @@ -190,7 +190,7 @@ impl CaseBuilder { let op = ops::Case { signature: signature.clone(), }; - let base = Hugr::new(op.into()); + let base = Hugr::new(op); let root = base.root(); let dfg_builder = DFGBuilder::create_with_io(base, root, signature)?; diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 8dd743175..f5c8db00f 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -71,7 +71,7 @@ impl DFGBuilder { let dfg_op = ops::DFG { signature: signature.clone(), }; - let base = Hugr::new(dfg_op.into()); + let base = Hugr::new(dfg_op); let root = base.root(); DFGBuilder::create_with_io(base, root, signature) } @@ -145,7 +145,7 @@ impl FunctionBuilder { name: name.into(), }; - let base = Hugr::new(op.into()); + let base = Hugr::new(op); let root = base.root(); let db = DFGBuilder::create_with_io(base, root, body)?; diff --git a/hugr-core/src/builder/tail_loop.rs b/hugr-core/src/builder/tail_loop.rs index 24804dc15..fc7eeb864 100644 --- a/hugr-core/src/builder/tail_loop.rs +++ b/hugr-core/src/builder/tail_loop.rs @@ -83,7 +83,7 @@ impl TailLoopBuilder { rest: inputs_outputs.into(), extension_delta, }; - let base = Hugr::new(tail_loop.clone().into()); + let base = Hugr::new(tail_loop.clone()); let root = base.root(); Self::create_with_io(base, root, &tail_loop) } diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index a191f38c3..25aa28214 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -52,7 +52,7 @@ pub struct Hugr { impl Default for Hugr { fn default() -> Self { - Self::new(crate::ops::Module.into()) + Self::new(crate::ops::Module) } } @@ -79,8 +79,8 @@ pub type NodeMetadataMap = serde_json::Map; /// Public API for HUGRs. impl Hugr { /// Create a new Hugr, with a single root node. - pub fn new(root_node: OpType) -> Self { - Self::with_capacity(root_node, 0, 0) + pub fn new(root_node: impl Into) -> Self { + Self::with_capacity(root_node.into(), 0, 0) } /// Resolve extension ops, infer extensions used, and pass the closure into validation diff --git a/hugr-core/src/hugr/rewrite/replace.rs b/hugr-core/src/hugr/rewrite/replace.rs index 5a938b685..08bea175c 100644 --- a/hugr-core/src/hugr/rewrite/replace.rs +++ b/hugr-core/src/hugr/rewrite/replace.rs @@ -523,12 +523,9 @@ mod test { // Replacement: one BB with two DFGs inside. // Use Hugr rather than Builder because DFGs must be empty (not even Input/Output). - let mut replacement = Hugr::new( - ops::CFG { - signature: FunctionType::new_endo(just_list.clone()), - } - .into(), - ); + let mut replacement = Hugr::new(ops::CFG { + signature: FunctionType::new_endo(just_list.clone()), + }); let r_bb = replacement.add_node_with_parent( replacement.root(), DataflowBlock { diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index a7c318b8c..d4d311170 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -683,16 +683,13 @@ fn no_outer_row_variables(#[case] connect: bool) -> Result<(), Box Date: Mon, 10 Jun 2024 14:05:45 +0100 Subject: [PATCH 37/39] Rm add_child_op, duplicates add_child_node --- hugr-core/src/builder/build_traits.rs | 5 ----- hugr-core/src/builder/conditional.rs | 2 +- hugr-core/src/builder/module.rs | 4 ++-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index 243d890f9..1b4945afc 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -44,11 +44,6 @@ pub trait Container { /// Immutable reference to HUGR being built fn hugr(&self) -> &Hugr; /// Add an [`OpType`] as the final child of the container. - fn add_child_op(&mut self, op: impl Into) -> Node { - let parent = self.container_node(); - self.hugr_mut().add_node_with_parent(parent, op) - } - /// Add an [`OpType`] as the final child of the container. fn add_child_node(&mut self, node: impl Into) -> Node { let parent = self.container_node(); self.hugr_mut().add_node_with_parent(parent, node) diff --git a/hugr-core/src/builder/conditional.rs b/hugr-core/src/builder/conditional.rs index 7a3e6105b..ec9566f5f 100644 --- a/hugr-core/src/builder/conditional.rs +++ b/hugr-core/src/builder/conditional.rs @@ -126,7 +126,7 @@ impl + AsRef> ConditionalBuilder { if let Some(&sibling_node) = self.case_nodes[case + 1..].iter().flatten().next() { self.hugr_mut().add_node_before(sibling_node, case_op) } else { - self.add_child_op(case_op) + self.add_child_node(case_op) }; self.case_nodes[case] = Some(case_node); diff --git a/hugr-core/src/builder/module.rs b/hugr-core/src/builder/module.rs index 84ca942d3..d5fbf3823 100644 --- a/hugr-core/src/builder/module.rs +++ b/hugr-core/src/builder/module.rs @@ -128,7 +128,7 @@ impl + AsRef> ModuleBuilder { // every 0-input node. let name: SmolStr = name.into(); let bound = typ.least_upper_bound(); - let node = self.add_child_op(ops::AliasDefn { + let node = self.add_child_node(ops::AliasDefn { name: name.clone(), definition: typ, }); @@ -146,7 +146,7 @@ impl + AsRef> ModuleBuilder { bound: TypeBound, ) -> Result, BuildError> { let name: SmolStr = name.into(); - let node = self.add_child_op(ops::AliasDecl { + let node = self.add_child_node(ops::AliasDecl { name: name.clone(), bound, }); From 8a9549b2d364950a1558362ae8a33b1e6e6ac02d Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 10 Jun 2024 14:29:13 +0100 Subject: [PATCH 38/39] ruff --- hugr-py/src/hugr/serialization/tys.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hugr-py/src/hugr/serialization/tys.py b/hugr-py/src/hugr/serialization/tys.py index e5bf047d5..ed64e8f54 100644 --- a/hugr-py/src/hugr/serialization/tys.py +++ b/hugr-py/src/hugr/serialization/tys.py @@ -368,6 +368,7 @@ class Signature(ConfiguredBaseModel): # ALAN is it worth keeping this now/still? (Also, surprised this is "Poly"...??) + # Now that all classes are defined, we need to update the ForwardRefs in all type # annotations. We use some inspect magic to find all classes defined in this file # and call model_rebuild() From 52f9c661f607622184dfff005cb8f58f3673faa5 Mon Sep 17 00:00:00 2001 From: Alan Lawrence Date: Mon, 10 Jun 2024 14:32:37 +0100 Subject: [PATCH 39/39] update schema --- .../schema/hugr_schema_strict_v1.json | 375 ------------------ specification/schema/hugr_schema_v1.json | 375 ------------------ .../schema/testing_hugr_schema_strict_v1.json | 375 ------------------ .../schema/testing_hugr_schema_v1.json | 375 ------------------ 4 files changed, 1500 deletions(-) diff --git a/specification/schema/hugr_schema_strict_v1.json b/specification/schema/hugr_schema_strict_v1.json index 08f948996..87c29ac88 100644 --- a/specification/schema/hugr_schema_strict_v1.json +++ b/specification/schema/hugr_schema_strict_v1.json @@ -35,21 +35,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDecl", "default": "AliasDecl", @@ -82,21 +67,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDefn", "default": "AliasDefn", @@ -211,21 +181,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CFG", "default": "CFG", @@ -253,21 +208,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Call", "default": "Call", @@ -308,21 +248,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CallIndirect", "default": "CallIndirect", @@ -350,21 +275,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Case", "default": "Case", @@ -392,21 +302,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Conditional", "default": "Conditional", @@ -463,21 +358,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Const", "default": "Const", @@ -524,21 +404,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CustomOp", "default": "CustomOp", @@ -588,21 +453,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DFG", "default": "DFG", @@ -630,21 +480,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DataflowBlock", "default": "DataflowBlock", @@ -701,21 +536,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "ExitBlock", "default": "ExitBlock", @@ -825,21 +645,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDecl", "default": "FuncDecl", @@ -873,21 +678,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDefn", "default": "FuncDefn", @@ -1025,21 +815,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Input", "default": "Input", @@ -1071,21 +846,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Lift", "default": "Lift", @@ -1145,21 +905,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadConstant", "default": "LoadConstant", @@ -1188,21 +933,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadFunction", "default": "LoadFunction", @@ -1243,21 +973,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "MakeTuple", "default": "MakeTuple", @@ -1289,21 +1004,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Module", "default": "Module", @@ -1328,21 +1028,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Noop", "default": "Noop", @@ -1575,21 +1260,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Output", "default": "Output", @@ -1771,21 +1441,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Tag", "default": "Tag", @@ -1826,21 +1481,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "TailLoop", "default": "TailLoop", @@ -2175,21 +1815,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "UnpackTuple", "default": "UnpackTuple", diff --git a/specification/schema/hugr_schema_v1.json b/specification/schema/hugr_schema_v1.json index 8399c4feb..24d59c149 100644 --- a/specification/schema/hugr_schema_v1.json +++ b/specification/schema/hugr_schema_v1.json @@ -35,21 +35,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDecl", "default": "AliasDecl", @@ -82,21 +67,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDefn", "default": "AliasDefn", @@ -211,21 +181,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CFG", "default": "CFG", @@ -253,21 +208,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Call", "default": "Call", @@ -308,21 +248,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CallIndirect", "default": "CallIndirect", @@ -350,21 +275,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Case", "default": "Case", @@ -392,21 +302,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Conditional", "default": "Conditional", @@ -463,21 +358,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Const", "default": "Const", @@ -524,21 +404,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CustomOp", "default": "CustomOp", @@ -588,21 +453,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DFG", "default": "DFG", @@ -630,21 +480,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DataflowBlock", "default": "DataflowBlock", @@ -701,21 +536,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "ExitBlock", "default": "ExitBlock", @@ -825,21 +645,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDecl", "default": "FuncDecl", @@ -873,21 +678,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDefn", "default": "FuncDefn", @@ -1025,21 +815,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Input", "default": "Input", @@ -1071,21 +846,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Lift", "default": "Lift", @@ -1145,21 +905,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadConstant", "default": "LoadConstant", @@ -1188,21 +933,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadFunction", "default": "LoadFunction", @@ -1243,21 +973,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "MakeTuple", "default": "MakeTuple", @@ -1289,21 +1004,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Module", "default": "Module", @@ -1328,21 +1028,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Noop", "default": "Noop", @@ -1575,21 +1260,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Output", "default": "Output", @@ -1771,21 +1441,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Tag", "default": "Tag", @@ -1826,21 +1481,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "TailLoop", "default": "TailLoop", @@ -2175,21 +1815,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "UnpackTuple", "default": "UnpackTuple", diff --git a/specification/schema/testing_hugr_schema_strict_v1.json b/specification/schema/testing_hugr_schema_strict_v1.json index 567c481dd..0199ce591 100644 --- a/specification/schema/testing_hugr_schema_strict_v1.json +++ b/specification/schema/testing_hugr_schema_strict_v1.json @@ -35,21 +35,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDecl", "default": "AliasDecl", @@ -82,21 +67,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDefn", "default": "AliasDefn", @@ -211,21 +181,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CFG", "default": "CFG", @@ -253,21 +208,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Call", "default": "Call", @@ -308,21 +248,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CallIndirect", "default": "CallIndirect", @@ -350,21 +275,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Case", "default": "Case", @@ -392,21 +302,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Conditional", "default": "Conditional", @@ -463,21 +358,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Const", "default": "Const", @@ -524,21 +404,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CustomOp", "default": "CustomOp", @@ -588,21 +453,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DFG", "default": "DFG", @@ -630,21 +480,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DataflowBlock", "default": "DataflowBlock", @@ -701,21 +536,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "ExitBlock", "default": "ExitBlock", @@ -846,21 +666,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDecl", "default": "FuncDecl", @@ -894,21 +699,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDefn", "default": "FuncDefn", @@ -1046,21 +836,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Input", "default": "Input", @@ -1092,21 +867,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Lift", "default": "Lift", @@ -1166,21 +926,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadConstant", "default": "LoadConstant", @@ -1209,21 +954,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadFunction", "default": "LoadFunction", @@ -1264,21 +994,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "MakeTuple", "default": "MakeTuple", @@ -1310,21 +1025,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Module", "default": "Module", @@ -1349,21 +1049,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Noop", "default": "Noop", @@ -1652,21 +1337,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Output", "default": "Output", @@ -1848,21 +1518,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Tag", "default": "Tag", @@ -1903,21 +1558,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "TailLoop", "default": "TailLoop", @@ -2252,21 +1892,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "UnpackTuple", "default": "UnpackTuple", diff --git a/specification/schema/testing_hugr_schema_v1.json b/specification/schema/testing_hugr_schema_v1.json index 47ed10526..f06ad5300 100644 --- a/specification/schema/testing_hugr_schema_v1.json +++ b/specification/schema/testing_hugr_schema_v1.json @@ -35,21 +35,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDecl", "default": "AliasDecl", @@ -82,21 +67,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "AliasDefn", "default": "AliasDefn", @@ -211,21 +181,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CFG", "default": "CFG", @@ -253,21 +208,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Call", "default": "Call", @@ -308,21 +248,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CallIndirect", "default": "CallIndirect", @@ -350,21 +275,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Case", "default": "Case", @@ -392,21 +302,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Conditional", "default": "Conditional", @@ -463,21 +358,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Const", "default": "Const", @@ -524,21 +404,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "CustomOp", "default": "CustomOp", @@ -588,21 +453,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DFG", "default": "DFG", @@ -630,21 +480,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "DataflowBlock", "default": "DataflowBlock", @@ -701,21 +536,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "ExitBlock", "default": "ExitBlock", @@ -846,21 +666,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDecl", "default": "FuncDecl", @@ -894,21 +699,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "FuncDefn", "default": "FuncDefn", @@ -1046,21 +836,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Input", "default": "Input", @@ -1092,21 +867,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Lift", "default": "Lift", @@ -1166,21 +926,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadConstant", "default": "LoadConstant", @@ -1209,21 +954,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "LoadFunction", "default": "LoadFunction", @@ -1264,21 +994,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "MakeTuple", "default": "MakeTuple", @@ -1310,21 +1025,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Module", "default": "Module", @@ -1349,21 +1049,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Noop", "default": "Noop", @@ -1652,21 +1337,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Output", "default": "Output", @@ -1848,21 +1518,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "Tag", "default": "Tag", @@ -1903,21 +1558,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "TailLoop", "default": "TailLoop", @@ -2252,21 +1892,6 @@ "title": "Parent", "type": "integer" }, - "input_extensions": { - "anyOf": [ - { - "items": { - "type": "string" - }, - "type": "array" - }, - { - "type": "null" - } - ], - "default": null, - "title": "Input Extensions" - }, "op": { "const": "UnpackTuple", "default": "UnpackTuple",