From b38750585c19b41cc486095186f72d70ff11980c Mon Sep 17 00:00:00 2001 From: Seyon Sivarajah Date: Wed, 28 Aug 2024 15:32:59 +0100 Subject: [PATCH] feat!: Move `Lift`, `MakeTuple`, `UnpackTuple` and `Lift` to prelude (#1475) Much of the noise is adding PRELUDE to extension inference tests. Add a convenience `with_prelude()` method to Signature. `Tag` left as a core operation as it is paramaterized by a variable number of type rows. Also drive-by get rid of custom signature use for print. Closes #816 Closes #1373 BREAKING CHANGE: Move `Lift`, `MakeTuple`, `UnpackTuple` and `Lift` from core operations to prelude. Rename `ops::leaf` module to `ops::sum`. --- hugr-core/src/builder/build_traits.rs | 5 +- hugr-core/src/builder/dataflow.rs | 49 +- hugr-core/src/builder/tail_loop.rs | 17 +- hugr-core/src/extension.rs | 2 +- hugr-core/src/extension/const_fold.rs | 10 + hugr-core/src/extension/op_def.rs | 1 - hugr-core/src/extension/prelude.rs | 556 ++++++++++++++++-- hugr-core/src/hugr.rs | 29 +- hugr-core/src/hugr/hugrmut.rs | 14 +- hugr-core/src/hugr/rewrite/consts.rs | 21 +- hugr-core/src/hugr/rewrite/inline_dfg.rs | 13 +- hugr-core/src/hugr/rewrite/insert_identity.rs | 10 +- hugr-core/src/hugr/rewrite/simple_replace.rs | 2 +- hugr-core/src/hugr/serialize/test.rs | 25 +- hugr-core/src/hugr/validate/test.rs | 76 +-- hugr-core/src/hugr/views/root_checked.rs | 5 +- hugr-core/src/ops.rs | 23 +- hugr-core/src/ops/leaf.rs | 230 -------- hugr-core/src/ops/sum.rs | 55 ++ hugr-core/src/ops/validate.rs | 12 +- hugr-core/src/types/signature.rs | 7 +- hugr-passes/src/const_fold.rs | 30 +- hugr-passes/src/const_fold/test.rs | 3 +- hugr-passes/src/merge_bbs.rs | 23 +- hugr-passes/src/non_local.rs | 13 +- hugr-py/src/hugr/_serialization/ops.py | 63 -- hugr-py/src/hugr/ops.py | 78 +-- hugr-py/src/hugr/std/__init__.py | 3 + hugr-py/src/hugr/std/_json_defs/prelude.json | 199 ++++++- hugr-py/tests/test_hugr_build.py | 9 - specification/schema/hugr_schema_live.json | 145 +---- .../schema/hugr_schema_strict_live.json | 145 +---- .../schema/testing_hugr_schema_live.json | 145 +---- .../testing_hugr_schema_strict_live.json | 145 +---- specification/std_extensions/prelude.json | 199 ++++++- 35 files changed, 1168 insertions(+), 1194 deletions(-) delete mode 100644 hugr-core/src/ops/leaf.rs create mode 100644 hugr-core/src/ops/sum.rs diff --git a/hugr-core/src/builder/build_traits.rs b/hugr-core/src/builder/build_traits.rs index d04e44d79..2950bdc47 100644 --- a/hugr-core/src/builder/build_traits.rs +++ b/hugr-core/src/builder/build_traits.rs @@ -1,7 +1,8 @@ +use crate::extension::prelude::MakeTuple; use crate::hugr::hugrmut::InsertionResult; use crate::hugr::views::HugrView; use crate::hugr::{NodeMetadata, ValidationError}; -use crate::ops::{self, MakeTuple, OpTag, OpTrait, OpType, Tag}; +use crate::ops::{self, OpTag, OpTrait, OpType, Tag}; use crate::utils::collect_array; use crate::{IncomingPort, Node, OutgoingPort}; @@ -579,7 +580,7 @@ pub trait Dataflow: Container { .map(|&wire| self.get_wire_type(wire)) .collect(); let types = types?.into(); - let make_op = self.add_dataflow_op(MakeTuple { tys: types }, values)?; + let make_op = self.add_dataflow_op(MakeTuple(types), values)?; Ok(make_op.out_wire(0)) } diff --git a/hugr-core/src/builder/dataflow.rs b/hugr-core/src/builder/dataflow.rs index 72dbee910..bf70268d8 100644 --- a/hugr-core/src/builder/dataflow.rs +++ b/hugr-core/src/builder/dataflow.rs @@ -206,10 +206,11 @@ pub(crate) mod test { use crate::builder::{ endo_sig, inout_sig, BuilderWiringError, DataflowSubContainer, ModuleBuilder, }; + use crate::extension::prelude::{Lift, Noop}; use crate::extension::prelude::{BOOL_T, USIZE_T}; use crate::extension::{ExtensionId, SignatureError, EMPTY_REG, PRELUDE_REGISTRY}; use crate::hugr::validate::InterGraphEdgeError; - use crate::ops::{handle::NodeHandle, Lift, Noop, OpTag}; + use crate::ops::{handle::NodeHandle, OpTag}; use crate::ops::{OpTrait, Value}; use crate::std_extensions::logic::test::and_op; @@ -319,21 +320,25 @@ pub(crate) mod test { #[test] fn simple_inter_graph_edge() { let builder = || -> Result { - let mut f_build = - FunctionBuilder::new("main", Signature::new(type_row![BIT], type_row![BIT]))?; + let mut f_build = FunctionBuilder::new( + "main", + Signature::new(type_row![BIT], type_row![BIT]).with_prelude(), + )?; let [i1] = f_build.input_wires_arr(); - let noop = f_build.add_dataflow_op(Noop { ty: BIT }, [i1])?; + let noop = f_build.add_dataflow_op(Noop(BIT), [i1])?; let i1 = noop.out_wire(0); - let mut nested = - f_build.dfg_builder(Signature::new(type_row![], type_row![BIT]), [])?; + let mut nested = f_build.dfg_builder( + Signature::new(type_row![], type_row![BIT]).with_prelude(), + [], + )?; - let id = nested.add_dataflow_op(Noop { ty: BIT }, [i1])?; + let id = nested.add_dataflow_op(Noop(BIT), [i1])?; let nested = nested.finish_with_outputs([id.out_wire(0)])?; - f_build.finish_hugr_with_outputs([nested.out_wire(0)], &EMPTY_REG) + f_build.finish_prelude_hugr_with_outputs([nested.out_wire(0)]) }; assert_matches!(builder(), Ok(_)); @@ -345,12 +350,12 @@ pub(crate) mod test { FunctionBuilder::new("main", Signature::new(type_row![QB], type_row![QB]))?; let [i1] = f_build.input_wires_arr(); - let noop = f_build.add_dataflow_op(Noop { ty: QB }, [i1])?; + let noop = f_build.add_dataflow_op(Noop(QB), [i1])?; let i1 = noop.out_wire(0); let mut nested = f_build.dfg_builder(Signature::new(type_row![], type_row![QB]), [])?; - let id_res = nested.add_dataflow_op(Noop { ty: QB }, [i1]); + let id_res = nested.add_dataflow_op(Noop(QB), [i1]); // The error would anyway be caught in validation when we finish the Hugr, // but the builder catches it earlier @@ -417,22 +422,10 @@ pub(crate) mod test { let mut add_ab = parent.dfg_builder(endo_sig(BIT), [w])?; let [w] = add_ab.input_wires_arr(); - let lift_a = add_ab.add_dataflow_op( - Lift { - type_row: type_row![BIT], - new_extension: xa.clone(), - }, - [w], - )?; + let lift_a = add_ab.add_dataflow_op(Lift::new(type_row![BIT], xa.clone()), [w])?; let [w] = lift_a.outputs_arr(); - let lift_b = add_ab.add_dataflow_op( - Lift { - type_row: type_row![BIT], - new_extension: xb, - }, - [w], - )?; + let lift_b = add_ab.add_dataflow_op(Lift::new(type_row![BIT], xb), [w])?; let [w] = lift_b.outputs_arr(); let add_ab = add_ab.finish_with_outputs([w])?; @@ -442,13 +435,7 @@ pub(crate) mod test { // via a child lift node let mut add_c = parent.dfg_builder(endo_sig(BIT), [w])?; let [w] = add_c.input_wires_arr(); - let lift_c = add_c.add_dataflow_op( - Lift { - type_row: type_row![BIT], - new_extension: xc, - }, - [w], - )?; + let lift_c = add_c.add_dataflow_op(Lift::new(type_row![BIT], xc), [w])?; let wires: Vec = lift_c.outputs().collect(); let add_c = add_c.finish_with_outputs(wires)?; diff --git a/hugr-core/src/builder/tail_loop.rs b/hugr-core/src/builder/tail_loop.rs index 577960354..29134ce03 100644 --- a/hugr-core/src/builder/tail_loop.rs +++ b/hugr-core/src/builder/tail_loop.rs @@ -111,7 +111,7 @@ mod test { test::{BIT, NAT}, DataflowSubContainer, HugrBuilder, ModuleBuilder, SubContainer, }, - extension::prelude::{ConstUsize, PRELUDE_ID, USIZE_T}, + extension::prelude::{ConstUsize, Lift, PRELUDE_ID, USIZE_T}, hugr::ValidationError, ops::Value, type_row, @@ -142,17 +142,11 @@ mod test { let mut module_builder = ModuleBuilder::new(); let mut fbuild = module_builder.define_function( "main", - Signature::new(type_row![BIT], type_row![NAT]).with_extension_delta(PRELUDE_ID), + Signature::new(type_row![BIT], type_row![NAT]).with_prelude(), )?; let _fdef = { let [b1] = fbuild - .add_dataflow_op( - ops::Lift { - type_row: type_row![BIT], - new_extension: PRELUDE_ID, - }, - fbuild.input_wires(), - )? + .add_dataflow_op(Lift::new(type_row![BIT], PRELUDE_ID), fbuild.input_wires())? .outputs_arr(); let loop_id = { let mut loop_b = @@ -161,10 +155,7 @@ mod test { let const_val = Value::true_val(); let const_wire = loop_b.add_load_const(Value::true_val()); let lift_node = loop_b.add_dataflow_op( - ops::Lift { - type_row: vec![const_val.get_type().clone()].into(), - new_extension: PRELUDE_ID, - }, + Lift::new(vec![const_val.get_type().clone()].into(), PRELUDE_ID), [const_wire], )?; let [const_wire] = lift_node.outputs_arr(); diff --git a/hugr-core/src/extension.rs b/hugr-core/src/extension.rs index 702813ba5..a0ed1542d 100644 --- a/hugr-core/src/extension.rs +++ b/hugr-core/src/extension.rs @@ -30,7 +30,7 @@ pub use type_def::{TypeDef, TypeDefBound}; mod const_fold; pub mod prelude; pub mod simple_op; -pub use const_fold::{ConstFold, ConstFoldResult, Folder}; +pub use const_fold::{fold_out_row, ConstFold, ConstFoldResult, Folder}; pub use prelude::{PRELUDE, PRELUDE_REGISTRY}; #[cfg(feature = "declarative")] diff --git a/hugr-core/src/extension/const_fold.rs b/hugr-core/src/extension/const_fold.rs index a3aae93eb..a2cd66f42 100644 --- a/hugr-core/src/extension/const_fold.rs +++ b/hugr-core/src/extension/const_fold.rs @@ -15,6 +15,16 @@ use crate::ops; /// successful and no values are output. pub type ConstFoldResult = Option>; +/// Tag some output constants with [`OutgoingPort`] inferred from the ordering. +pub fn fold_out_row(consts: impl IntoIterator) -> ConstFoldResult { + let vec = consts + .into_iter() + .enumerate() + .map(|(i, c)| (i.into(), c)) + .collect(); + Some(vec) +} + /// Trait implemented by extension operations that can perform constant folding. pub trait ConstFold: Send + Sync { /// Given type arguments `type_args` and diff --git a/hugr-core/src/extension/op_def.rs b/hugr-core/src/extension/op_def.rs index 897fe9dd3..de114fed0 100644 --- a/hugr-core/src/extension/op_def.rs +++ b/hugr-core/src/extension/op_def.rs @@ -244,7 +244,6 @@ impl SignatureFunc { // TODO raise warning: https://github.com/CQCL/hugr/issues/1432 SignatureFunc::MissingValidateFunc(ts) => (ts, args), }; - let mut res = pf.instantiate(args, exts)?; res.extension_reqs.insert(&def.extension); diff --git a/hugr-core/src/extension/prelude.rs b/hugr-core/src/extension/prelude.rs index f4b01cfd8..0c5ddb55b 100644 --- a/hugr-core/src/extension/prelude.rs +++ b/hugr-core/src/extension/prelude.rs @@ -2,9 +2,10 @@ //! operations and constants. use lazy_static::lazy_static; +use crate::extension::simple_op::MakeOpDef; use crate::ops::constant::{CustomCheckFailure, ValueName}; use crate::ops::{ExtensionOp, OpName}; -use crate::types::{FuncValueType, SumType, TypeName}; +use crate::types::{FuncValueType, SumType, TypeName, TypeRV}; use crate::{ extension::{ExtensionId, TypeDefBound}, ops::constant::CustomConst, @@ -16,7 +17,20 @@ use crate::{ Extension, }; -use super::{ExtensionRegistry, ExtensionSet, SignatureError, SignatureFromArgs}; +use strum_macros::{EnumIter, EnumString, IntoStaticStr}; + +use crate::{ + extension::{ + const_fold::fold_out_row, + simple_op::{try_from_name, MakeExtensionOp, MakeRegisteredOp, OpLoadError}, + ConstFold, ExtensionSet, OpDef, SignatureError, SignatureFunc, + }, + ops::{NamedOp, Value}, + types::{PolyFuncType, TypeRow}, + utils::sorted_consts, +}; + +use super::{ExtensionRegistry, SignatureFromArgs}; struct ArrayOpCustom; const MAX: &[TypeParam; 1] = &[TypeParam::max_nat()]; @@ -41,48 +55,6 @@ impl SignatureFromArgs for ArrayOpCustom { } } -struct GenericOpCustom; -impl SignatureFromArgs for GenericOpCustom { - fn compute_signature(&self, arg_values: &[TypeArg]) -> Result { - let [arg0, arg1] = arg_values else { - return Err(SignatureError::InvalidTypeArgs); - }; - let TypeArg::Sequence { elems: inp_args } = arg0 else { - return Err(SignatureError::InvalidTypeArgs); - }; - let TypeArg::Sequence { elems: out_args } = arg1 else { - return Err(SignatureError::InvalidTypeArgs); - }; - let mut inps: Vec = vec![Type::new_extension(ERROR_CUSTOM_TYPE)]; - for inp_arg in inp_args.iter() { - let TypeArg::Type { ty } = inp_arg else { - return Err(SignatureError::InvalidTypeArgs); - }; - inps.push(ty.clone()); - } - let mut outs: Vec = vec![]; - for out_arg in out_args.iter() { - let TypeArg::Type { ty } = out_arg else { - return Err(SignatureError::InvalidTypeArgs); - }; - outs.push(ty.clone()); - } - Ok(FuncValueType::new(inps, outs).into()) - } - - fn static_params(&self) -> &[TypeParam] { - fn list_of_type() -> TypeParam { - TypeParam::List { - param: Box::new(TypeParam::Type { b: TypeBound::Any }), - } - } - lazy_static! { - static ref PARAMS: [TypeParam; 2] = [list_of_type(), list_of_type()]; - } - PARAMS.as_slice() - } -} - /// Name of prelude extension. pub const PRELUDE_ID: ExtensionId = ExtensionId::new_unchecked("prelude"); /// Extension version. @@ -142,13 +114,25 @@ lazy_static! { TypeDefBound::copyable(), ) .unwrap(); + + prelude .add_op( PANIC_OP_ID, "Panic with input error".to_string(), - GenericOpCustom, + PolyFuncTypeRV::new( + [TypeParam::new_list(TypeBound::Any), TypeParam::new_list(TypeBound::Any)], + FuncValueType::new( + vec![TypeRV::new_extension(ERROR_CUSTOM_TYPE), TypeRV::new_row_var_use(0, TypeBound::Any)], + vec![TypeRV::new_row_var_use(1, TypeBound::Any)], + ), + ), ) .unwrap(); + + TupleOpDef::load_all_ops(&mut prelude).unwrap(); + NoopDef.add_to_extension(&mut prelude).unwrap(); + LiftDef.add_to_extension(&mut prelude).unwrap(); prelude }; /// An extension registry containing only the prelude @@ -403,6 +387,423 @@ impl CustomConst for ConstExternalSymbol { } } +/// Logic extension operation definitions. +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)] +#[allow(missing_docs)] +#[non_exhaustive] +pub enum TupleOpDef { + MakeTuple, + UnpackTuple, +} + +impl ConstFold for TupleOpDef { + fn fold( + &self, + _type_args: &[TypeArg], + consts: &[(crate::IncomingPort, Value)], + ) -> crate::extension::ConstFoldResult { + match self { + TupleOpDef::MakeTuple => { + fold_out_row([Value::tuple(sorted_consts(consts).into_iter().cloned())]) + } + TupleOpDef::UnpackTuple => { + let c = &consts.first()?.1; + let Some(vs) = c.as_tuple() else { + panic!("This op always takes a Tuple input."); + }; + fold_out_row(vs.iter().cloned()) + } + } + } +} +impl MakeOpDef for TupleOpDef { + fn signature(&self) -> SignatureFunc { + let rv = TypeRV::new_row_var_use(0, TypeBound::Any); + let tuple_type = TypeRV::new_tuple(vec![rv.clone()]); + + let param = TypeParam::new_list(TypeBound::Any); + match self { + TupleOpDef::MakeTuple => { + PolyFuncTypeRV::new([param], FuncValueType::new(rv, tuple_type)) + } + TupleOpDef::UnpackTuple => { + PolyFuncTypeRV::new([param], FuncValueType::new(tuple_type, rv)) + } + } + .into() + } + + fn description(&self) -> String { + match self { + TupleOpDef::MakeTuple => "MakeTuple operation", + TupleOpDef::UnpackTuple => "UnpackTuple operation", + } + .to_string() + } + + fn from_def(op_def: &OpDef) -> Result { + try_from_name(op_def.name(), op_def.extension()) + } + + fn extension(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn post_opdef(&self, def: &mut OpDef) { + def.set_constant_folder(*self); + } +} +/// An operation that packs all its inputs into a tuple. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +#[non_exhaustive] +pub struct MakeTuple(pub TypeRow); + +impl MakeTuple { + /// Create a new MakeTuple operation. + pub fn new(tys: TypeRow) -> Self { + Self(tys) + } +} + +impl NamedOp for MakeTuple { + fn name(&self) -> OpName { + TupleOpDef::MakeTuple.name() + } +} + +impl MakeExtensionOp for MakeTuple { + fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result + where + Self: Sized, + { + let def = TupleOpDef::from_def(ext_op.def())?; + if def != TupleOpDef::MakeTuple { + return Err(OpLoadError::NotMember(ext_op.def().name().to_string()))?; + } + let [TypeArg::Sequence { elems }] = ext_op.args() else { + return Err(SignatureError::InvalidTypeArgs)?; + }; + let tys: Result, _> = elems + .iter() + .map(|a| match a { + TypeArg::Type { ty } => Ok(ty.clone()), + _ => Err(SignatureError::InvalidTypeArgs), + }) + .collect(); + Ok(Self(tys?.into())) + } + + fn type_args(&self) -> Vec { + vec![TypeArg::Sequence { + elems: self + .0 + .iter() + .map(|t| TypeArg::Type { ty: t.clone() }) + .collect(), + }] + } +} + +impl MakeRegisteredOp for MakeTuple { + fn extension_id(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn registry<'s, 'r: 's>(&'s self) -> &'r crate::extension::ExtensionRegistry { + &PRELUDE_REGISTRY + } +} + +/// An operation that unpacks a tuple into its components. +#[derive(Debug, Clone, Default, PartialEq, Eq)] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +#[non_exhaustive] +pub struct UnpackTuple(pub TypeRow); + +impl UnpackTuple { + /// Create a new UnpackTuple operation. + pub fn new(tys: TypeRow) -> Self { + Self(tys) + } +} + +impl NamedOp for UnpackTuple { + fn name(&self) -> OpName { + TupleOpDef::UnpackTuple.name() + } +} + +impl MakeExtensionOp for UnpackTuple { + fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result + where + Self: Sized, + { + let def = TupleOpDef::from_def(ext_op.def())?; + if def != TupleOpDef::UnpackTuple { + return Err(OpLoadError::NotMember(ext_op.def().name().to_string()))?; + } + let [TypeArg::Sequence { elems }] = ext_op.args() else { + return Err(SignatureError::InvalidTypeArgs)?; + }; + let tys: Result, _> = elems + .iter() + .map(|a| match a { + TypeArg::Type { ty } => Ok(ty.clone()), + _ => Err(SignatureError::InvalidTypeArgs), + }) + .collect(); + Ok(Self(tys?.into())) + } + + fn type_args(&self) -> Vec { + vec![TypeArg::Sequence { + elems: self + .0 + .iter() + .map(|t| TypeArg::Type { ty: t.clone() }) + .collect(), + }] + } +} + +impl MakeRegisteredOp for UnpackTuple { + fn extension_id(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn registry<'s, 'r: 's>(&'s self) -> &'r crate::extension::ExtensionRegistry { + &PRELUDE_REGISTRY + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +/// A no-op operation definition. +pub struct NoopDef; + +impl NamedOp for NoopDef { + fn name(&self) -> OpName { + "Noop".into() + } +} + +impl std::str::FromStr for NoopDef { + type Err = (); + + fn from_str(s: &str) -> Result { + if s == NoopDef.name() { + Ok(Self) + } else { + Err(()) + } + } +} +impl MakeOpDef for NoopDef { + fn signature(&self) -> SignatureFunc { + let tv = Type::new_var_use(0, TypeBound::Any); + PolyFuncType::new([TypeBound::Any.into()], Signature::new_endo(tv)).into() + } + + fn description(&self) -> String { + "Noop gate".to_string() + } + + fn from_def(op_def: &OpDef) -> Result { + try_from_name(op_def.name(), op_def.extension()) + } + + fn extension(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn post_opdef(&self, def: &mut OpDef) { + def.set_constant_folder(*self); + } +} + +impl ConstFold for NoopDef { + fn fold( + &self, + _type_args: &[TypeArg], + consts: &[(crate::IncomingPort, Value)], + ) -> crate::extension::ConstFoldResult { + fold_out_row([consts.first()?.1.clone()]) + } +} + +/// A no-op operation. +#[derive(Debug, Clone, PartialEq, Eq)] +#[non_exhaustive] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +pub struct Noop(pub Type); + +impl Noop { + /// Create a new Noop operation. + pub fn new(ty: Type) -> Self { + Self(ty) + } +} + +impl Default for Noop { + fn default() -> Self { + Self(Type::UNIT) + } +} +impl NamedOp for Noop { + fn name(&self) -> OpName { + NoopDef.name() + } +} + +impl MakeExtensionOp for Noop { + fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result + where + Self: Sized, + { + let _def = NoopDef::from_def(ext_op.def())?; + let [TypeArg::Type { ty }] = ext_op.args() else { + return Err(SignatureError::InvalidTypeArgs)?; + }; + Ok(Self(ty.clone())) + } + + fn type_args(&self) -> Vec { + vec![TypeArg::Type { ty: self.0.clone() }] + } +} + +impl MakeRegisteredOp for Noop { + fn extension_id(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn registry<'s, 'r: 's>(&'s self) -> &'r crate::extension::ExtensionRegistry { + &PRELUDE_REGISTRY + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +/// A lift operation definition. +pub struct LiftDef; + +impl NamedOp for LiftDef { + fn name(&self) -> OpName { + "Lift".into() + } +} + +impl std::str::FromStr for LiftDef { + type Err = (); + + fn from_str(s: &str) -> Result { + if s == LiftDef.name() { + Ok(Self) + } else { + Err(()) + } + } +} + +impl MakeOpDef for LiftDef { + fn signature(&self) -> SignatureFunc { + PolyFuncTypeRV::new( + vec![TypeParam::Extensions, TypeParam::new_list(TypeBound::Any)], + FuncValueType::new_endo(TypeRV::new_row_var_use(1, TypeBound::Any)) + .with_extension_delta(ExtensionSet::type_var(0)), + ) + .into() + } + + fn description(&self) -> String { + "Add extension requirements to a row of values".to_string() + } + + fn from_def(op_def: &OpDef) -> Result { + try_from_name(op_def.name(), op_def.extension()) + } + + fn extension(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } +} + +/// A node which adds a extension req to the types of the wires it is passed +/// It has no effect on the values passed along the edge +#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +#[non_exhaustive] +pub struct Lift { + /// The types of the edges + pub type_row: TypeRow, + /// The extensions which we're adding to the inputs + pub new_extensions: ExtensionSet, +} + +impl Lift { + /// Create a new Lift operation with the extensions to add. + pub fn new(type_row: TypeRow, set: impl Into) -> Self { + Self { + type_row, + new_extensions: set.into(), + } + } +} + +impl NamedOp for Lift { + fn name(&self) -> OpName { + LiftDef.name() + } +} + +impl MakeExtensionOp for Lift { + fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result + where + Self: Sized, + { + let _def = LiftDef::from_def(ext_op.def())?; + + let [TypeArg::Extensions { es }, TypeArg::Sequence { elems }] = ext_op.args() else { + return Err(SignatureError::InvalidTypeArgs)?; + }; + let tys: Result, _> = elems + .iter() + .map(|a| match a { + TypeArg::Type { ty } => Ok(ty.clone()), + _ => Err(SignatureError::InvalidTypeArgs), + }) + .collect(); + Ok(Self { + type_row: tys?.into(), + new_extensions: es.clone(), + }) + } + + fn type_args(&self) -> Vec { + vec![ + TypeArg::Extensions { + es: self.new_extensions.clone(), + }, + TypeArg::Sequence { + elems: self + .type_row + .iter() + .map(|t| TypeArg::Type { ty: t.clone() }) + .collect(), + }, + ] + } +} + +impl MakeRegisteredOp for Lift { + fn extension_id(&self) -> ExtensionId { + PRELUDE_ID.to_owned() + } + + fn registry<'s, 'r: 's>(&'s self) -> &'r crate::extension::ExtensionRegistry { + &PRELUDE_REGISTRY + } +} + #[cfg(test)] mod test { use crate::{ @@ -412,6 +813,71 @@ mod test { }; use super::*; + use crate::{ + ops::{OpTrait, OpType}, + type_row, + }; + + #[test] + fn test_make_tuple() { + let op = MakeTuple::new(type_row![Type::UNIT]); + let optype: OpType = op.clone().into(); + assert_eq!( + optype.dataflow_signature().unwrap().io(), + ( + &type_row![Type::UNIT], + &vec![Type::new_tuple(type_row![Type::UNIT])].into(), + ) + ); + + let new_op = MakeTuple::from_extension_op(optype.as_extension_op().unwrap()).unwrap(); + assert_eq!(new_op, op); + } + + #[test] + fn test_unmake_tuple() { + let op = UnpackTuple::new(type_row![Type::UNIT]); + let optype: OpType = op.clone().into(); + assert_eq!( + optype.dataflow_signature().unwrap().io(), + ( + &vec![Type::new_tuple(type_row![Type::UNIT])].into(), + &type_row![Type::UNIT], + ) + ); + + let new_op = UnpackTuple::from_extension_op(optype.as_extension_op().unwrap()).unwrap(); + assert_eq!(new_op, op); + } + + #[test] + fn test_noop() { + let op = Noop::new(Type::UNIT); + let optype: OpType = op.clone().into(); + assert_eq!( + optype.dataflow_signature().unwrap().io(), + (&type_row![Type::UNIT], &type_row![Type::UNIT]) + ); + + let new_op = Noop::from_extension_op(optype.as_extension_op().unwrap()).unwrap(); + assert_eq!(new_op, op); + } + + #[test] + fn test_lift() { + const XA: ExtensionId = ExtensionId::new_unchecked("xa"); + let op = Lift::new(type_row![Type::UNIT], ExtensionSet::singleton(&XA)); + let optype: OpType = op.clone().into(); + assert_eq!( + optype.dataflow_signature().unwrap(), + Signature::new_endo(type_row![Type::UNIT]) + .with_extension_delta(XA) + .with_prelude() + ); + + let new_op = Lift::from_extension_op(optype.as_extension_op().unwrap()).unwrap(); + assert_eq!(new_op, op); + } #[test] /// Test building a HUGR involving a new_array operation. diff --git a/hugr-core/src/hugr.rs b/hugr-core/src/hugr.rs index fcf358bc2..2e21a2358 100644 --- a/hugr-core/src/hugr.rs +++ b/hugr-core/src/hugr.rs @@ -301,6 +301,8 @@ mod test { #[cfg(feature = "extension_inference")] use super::ValidationError; use super::{ExtensionError, Hugr, HugrMut, HugrView, Node}; + use crate::extension::prelude::Lift; + use crate::extension::prelude::PRELUDE_ID; use crate::extension::{ ExtensionId, ExtensionSet, EMPTY_REG, PRELUDE_REGISTRY, TO_BE_INFERRED, }; @@ -395,18 +397,21 @@ mod test { let parent = ExtensionSet::from_iter(parent).union(TO_BE_INFERRED.into()); let (mut h, _) = build_ext_dfg(parent); h.infer_extensions(remove).unwrap(); - assert_eq!(h, build_ext_dfg(result).0); + assert_eq!(h, build_ext_dfg(result.union(PRELUDE_ID.into())).0); } #[test] fn infer_removes_from_delta() { - let parent = ExtensionSet::from_iter([XA, XB]); + let parent = ExtensionSet::from_iter([XA, XB, PRELUDE_ID]); let mut h = build_ext_dfg(parent.clone()).0; let backup = h.clone(); h.infer_extensions(false).unwrap(); assert_eq!(h, backup); // did nothing h.infer_extensions(true).unwrap(); - assert_eq!(h, build_ext_dfg(XA.into()).0); + assert_eq!( + h, + build_ext_dfg(ExtensionSet::from_iter([XA, PRELUDE_ID])).0 + ); } #[test] @@ -420,7 +425,7 @@ mod test { parent: h.root(), parent_extensions: XB.into(), child: mid, - child_extensions: XA.into(), + child_extensions: ExtensionSet::from_iter([XA, PRELUDE_ID]), }; #[cfg(feature = "extension_inference")] assert_eq!( @@ -457,13 +462,7 @@ mod test { types: ty.clone().into(), }, ); - let mid = h.add_node_with_parent( - p, - ops::Lift { - type_row: ty.into(), - new_extension: XA, - }, - ); + let mid = h.add_node_with_parent(p, Lift::new(ty.into(), XA)); h.connect(inp, 0, mid, 0); h.connect(mid, 0, out, 0); mid @@ -493,8 +492,9 @@ mod test { #[case] result: impl IntoIterator, ) { let ty = Type::new_function(Signature::new_endo(type_row![])); - let grandparent = ExtensionSet::from_iter(grandparent); - let result = ExtensionSet::from_iter(result); + let grandparent = ExtensionSet::from_iter(grandparent).union(PRELUDE_ID.into()); + let parent = ExtensionSet::from_iter(parent).union(PRELUDE_ID.into()); + let result = ExtensionSet::from_iter(result).union(PRELUDE_ID.into()); let root_ty = ops::Conditional { sum_rows: vec![type_row![]], other_inputs: ty.clone().into(), @@ -505,8 +505,7 @@ mod test { let p = h.add_node_with_parent( h.root(), ops::Case { - signature: Signature::new_endo(ty.clone()) - .with_extension_delta(ExtensionSet::from_iter(parent)), + signature: Signature::new_endo(ty.clone()).with_extension_delta(parent), }, ); add_inliftout(&mut h, p, ty.clone()); diff --git a/hugr-core/src/hugr/hugrmut.rs b/hugr-core/src/hugr/hugrmut.rs index 42b8964a5..16f8749b2 100644 --- a/hugr-core/src/hugr/hugrmut.rs +++ b/hugr-core/src/hugr/hugrmut.rs @@ -506,10 +506,12 @@ pub(super) fn panic_invalid_port( #[cfg(test)] mod test { use crate::{ - extension::prelude::USIZE_T, - extension::PRELUDE_REGISTRY, + extension::{ + prelude::{Noop, USIZE_T}, + PRELUDE_REGISTRY, + }, macros::type_row, - ops::{self, dataflow::IOTrait, Noop}, + ops::{self, dataflow::IOTrait}, types::{Signature, Type}, }; @@ -529,14 +531,16 @@ mod test { module, ops::FuncDefn { name: "main".into(), - signature: Signature::new(type_row![NAT], type_row![NAT, NAT]).into(), + signature: Signature::new(type_row![NAT], type_row![NAT, NAT]) + .with_prelude() + .into(), }, ); { 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 }); + let noop = hugr.add_node_with_parent(f, Noop(NAT)); hugr.connect(f_in, 0, noop, 0); hugr.connect(noop, 0, f_out, 0); diff --git a/hugr-core/src/hugr/rewrite/consts.rs b/hugr-core/src/hugr/rewrite/consts.rs index 05122af46..945bddd33 100644 --- a/hugr-core/src/hugr/rewrite/consts.rs +++ b/hugr-core/src/hugr/rewrite/consts.rs @@ -113,13 +113,12 @@ impl Rewrite for RemoveConst { #[cfg(test)] mod test { use super::*; + + use crate::extension::prelude::PRELUDE_ID; use crate::{ builder::{Container, Dataflow, HugrBuilder, ModuleBuilder, SubContainer}, - extension::{ - prelude::{ConstUsize, USIZE_T}, - PRELUDE_REGISTRY, - }, - ops::{handle::NodeHandle, MakeTuple, Value}, + extension::{prelude::ConstUsize, PRELUDE_REGISTRY}, + ops::{handle::NodeHandle, Value}, type_row, types::Signature, }; @@ -128,15 +127,13 @@ mod test { let mut build = ModuleBuilder::new(); let con_node = build.add_constant(Value::extension(ConstUsize::new(2))); - let mut dfg_build = build.define_function("main", Signature::new_endo(type_row![]))?; + let mut dfg_build = build.define_function( + "main", + Signature::new_endo(type_row![]).with_extension_delta(PRELUDE_ID.clone()), + )?; let load_1 = dfg_build.load_const(&con_node); let load_2 = dfg_build.load_const(&con_node); - let tup = dfg_build.add_dataflow_op( - MakeTuple { - tys: type_row![USIZE_T, USIZE_T], - }, - [load_1, load_2], - )?; + let tup = dfg_build.make_tuple([load_1, load_2])?; dfg_build.finish_sub_container()?; let mut h = build.finish_prelude_hugr()?; diff --git a/hugr-core/src/hugr/rewrite/inline_dfg.rs b/hugr-core/src/hugr/rewrite/inline_dfg.rs index b786d5b40..ca3f39cd3 100644 --- a/hugr-core/src/hugr/rewrite/inline_dfg.rs +++ b/hugr-core/src/hugr/rewrite/inline_dfg.rs @@ -141,7 +141,7 @@ mod test { use crate::hugr::rewrite::inline_dfg::InlineDFGError; use crate::hugr::HugrMut; use crate::ops::handle::{DfgID, NodeHandle}; - use crate::ops::{Lift, OpType, Value}; + use crate::ops::{OpType, Value}; use crate::std_extensions::arithmetic::float_types; use crate::std_extensions::arithmetic::int_ops::{self, IntOpDef}; use crate::std_extensions::arithmetic::int_types::{self, ConstInt}; @@ -167,6 +167,8 @@ mod test { #[case(true)] #[case(false)] fn inline_add_load_const(#[case] nonlocal: bool) -> Result<(), Box> { + use crate::extension::prelude::Lift; + let reg = ExtensionRegistry::try_new([ PRELUDE.to_owned(), int_ops::EXTENSION.to_owned(), @@ -185,10 +187,7 @@ mod test { let c1 = d.add_load_const(cst); let [lifted] = d .add_dataflow_op( - Lift { - type_row: vec![int_ty.clone()].into(), - new_extension: int_ops::EXTENSION_ID, - }, + Lift::new(vec![int_ty.clone()].into(), int_ops::EXTENSION_ID), [c1], )? .outputs_arr(); @@ -213,7 +212,7 @@ mod test { if nonlocal { 3 } else { 6 } ); // Input, Output, add; + const, load_const, lift assert_eq!(find_dfgs(&outer), vec![outer.root(), inner.node()]); - let [add, sub] = extension_ops(&outer).try_into().unwrap(); + let [_lift, add, sub] = extension_ops(&outer).try_into().unwrap(); assert_eq!( outer.get_parent(outer.get_parent(add).unwrap()), outer.get_parent(sub) @@ -233,7 +232,7 @@ mod test { outer.validate(®)?; assert_eq!(outer.nodes().len(), 8); assert_eq!(find_dfgs(&outer), vec![outer.root()]); - let [add, sub] = extension_ops(&outer).try_into().unwrap(); + let [_lift, add, sub] = extension_ops(&outer).try_into().unwrap(); assert_eq!(outer.get_parent(add), Some(outer.root())); assert_eq!(outer.get_parent(sub), Some(outer.root())); assert_eq!( diff --git a/hugr-core/src/hugr/rewrite/insert_identity.rs b/hugr-core/src/hugr/rewrite/insert_identity.rs index 7dbde932b..d3a63bf97 100644 --- a/hugr-core/src/hugr/rewrite/insert_identity.rs +++ b/hugr-core/src/hugr/rewrite/insert_identity.rs @@ -2,8 +2,10 @@ use std::iter; +use crate::extension::prelude::Noop; use crate::hugr::{HugrMut, Node}; -use crate::ops::{Noop, OpTag, OpTrait}; +use crate::ops::{OpTag, OpTrait}; + use crate::types::EdgeKind; use crate::{HugrView, IncomingPort}; @@ -80,7 +82,7 @@ impl Rewrite for IdentityInsertion { if !OpTag::DataflowParent.is_superset(h.get_optype(parent).tag()) { return Err(IdentityInsertionError::InvalidParentNode); } - let new_node = h.add_node_with_parent(parent, Noop { ty }); + let new_node = h.add_node_with_parent(parent, Noop(ty)); h.connect(pre_node, pre_port, new_node, 0); h.connect(new_node, 0, self.post_node, self.post_port); @@ -123,9 +125,9 @@ mod tests { assert_eq!(h.node_count(), 7); - let noop: Noop = h.get_optype(noop_node).clone().try_into().unwrap(); + let noop: Noop = h.get_optype(noop_node).cast().unwrap(); - assert_eq!(noop, Noop { ty: QB_T }); + assert_eq!(noop, Noop(QB_T)); h.update_validate(&PRELUDE_REGISTRY).unwrap(); } diff --git a/hugr-core/src/hugr/rewrite/simple_replace.rs b/hugr-core/src/hugr/rewrite/simple_replace.rs index 295e6b603..a9872be0a 100644 --- a/hugr-core/src/hugr/rewrite/simple_replace.rs +++ b/hugr-core/src/hugr/rewrite/simple_replace.rs @@ -293,7 +293,7 @@ pub(in crate::hugr::rewrite) mod test { /// ┤ H ├┤ X ├ /// └───┘└───┘ fn make_dfg_hugr() -> Result { - let mut dfg_builder = DFGBuilder::new(endo_sig(type_row![QB, QB]))?; + let mut dfg_builder = DFGBuilder::new(endo_sig(type_row![QB, QB]).with_prelude())?; let [wire0, wire1] = dfg_builder.input_wires_arr(); let wire2 = dfg_builder.add_dataflow_op(h_gate(), vec![wire0])?; let wire3 = dfg_builder.add_dataflow_op(h_gate(), vec![wire1])?; diff --git a/hugr-core/src/hugr/serialize/test.rs b/hugr-core/src/hugr/serialize/test.rs index 22f526c58..67ed0a057 100644 --- a/hugr-core/src/hugr/serialize/test.rs +++ b/hugr-core/src/hugr/serialize/test.rs @@ -3,13 +3,14 @@ use crate::builder::{ endo_sig, inout_sig, test::closed_dfg_root_hugr, Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, HugrBuilder, ModuleBuilder, }; +use crate::extension::prelude::Noop; use crate::extension::prelude::{BOOL_T, PRELUDE_ID, QB_T, USIZE_T}; use crate::extension::simple_op::MakeRegisteredOp; use crate::extension::{test::SimpleOpDef, ExtensionSet, EMPTY_REG, PRELUDE_REGISTRY}; use crate::hugr::internal::HugrMutInternals; use crate::hugr::validate::ValidationError; use crate::ops::custom::{ExtensionOp, OpaqueOp, OpaqueOpError}; -use crate::ops::{self, dataflow::IOTrait, Input, Module, Noop, Output, Value, DFG}; +use crate::ops::{self, dataflow::IOTrait, Input, Module, 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::{ConstInt, INT_TYPES}; @@ -296,19 +297,14 @@ fn weighted_hugr_ser() { let t_row = vec![Type::new_sum([type_row![NAT], type_row![QB]])]; let mut f_build = module_builder - .define_function("main", Signature::new(t_row.clone(), t_row)) + .define_function("main", Signature::new(t_row.clone(), t_row).with_prelude()) .unwrap(); let outputs = f_build .input_wires() .map(|in_wire| { f_build - .add_dataflow_op( - Noop { - ty: f_build.get_wire_type(in_wire).unwrap(), - }, - [in_wire], - ) + .add_dataflow_op(Noop(f_build.get_wire_type(in_wire).unwrap()), [in_wire]) .unwrap() .out_wire(0) }) @@ -325,13 +321,10 @@ fn weighted_hugr_ser() { #[test] fn dfg_roundtrip() -> Result<(), Box> { let tp: Vec = vec![BOOL_T; 2]; - let mut dfg = DFGBuilder::new(Signature::new(tp.clone(), tp))?; + let mut dfg = DFGBuilder::new(Signature::new(tp.clone(), tp).with_prelude())?; let mut params: [_; 2] = dfg.input_wires_arr(); for p in params.iter_mut() { - *p = dfg - .add_dataflow_op(Noop { ty: BOOL_T }, [*p]) - .unwrap() - .out_wire(0); + *p = dfg.add_dataflow_op(Noop(BOOL_T), [*p]).unwrap().out_wire(0); } let hugr = dfg.finish_hugr_with_outputs(params, &EMPTY_REG)?; @@ -391,9 +384,9 @@ fn opaque_ops() -> Result<(), Box> { #[test] fn function_type() -> Result<(), Box> { - let fn_ty = Type::new_function(Signature::new_endo(type_row![BOOL_T])); - let mut bldr = DFGBuilder::new(Signature::new_endo(vec![fn_ty.clone()]))?; - let op = bldr.add_dataflow_op(Noop { ty: fn_ty }, bldr.input_wires())?; + let fn_ty = Type::new_function(Signature::new_endo(type_row![BOOL_T]).with_prelude()); + let mut bldr = DFGBuilder::new(Signature::new_endo(vec![fn_ty.clone()]).with_prelude())?; + let op = bldr.add_dataflow_op(Noop(fn_ty), bldr.input_wires())?; let h = bldr.finish_prelude_hugr_with_outputs(op.outputs())?; check_hugr_roundtrip(&h, true); diff --git a/hugr-core/src/hugr/validate/test.rs b/hugr-core/src/hugr/validate/test.rs index cc30ec7fc..05c60fb1b 100644 --- a/hugr-core/src/hugr/validate/test.rs +++ b/hugr-core/src/hugr/validate/test.rs @@ -9,14 +9,14 @@ use crate::builder::{ inout_sig, BuildError, Container, DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer, FunctionBuilder, HugrBuilder, ModuleBuilder, SubContainer, }; +use crate::extension::prelude::Noop; 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; use crate::ops::dataflow::IOTrait; use crate::ops::handle::NodeHandle; -use crate::ops::leaf::MakeTuple; -use crate::ops::{self, Noop, OpType, Value}; +use crate::ops::{self, OpType, Value}; use crate::std_extensions::logic::test::{and_op, or_op}; use crate::std_extensions::logic::LogicOp; use crate::std_extensions::logic::{self}; @@ -35,7 +35,9 @@ const NAT: Type = crate::extension::prelude::USIZE_T; fn make_simple_hugr(copies: usize) -> (Hugr, Node) { let def_op: OpType = ops::FuncDefn { name: "main".into(), - signature: Signature::new(type_row![BOOL_T], vec![BOOL_T; copies]).into(), + signature: Signature::new(type_row![BOOL_T], vec![BOOL_T; copies]) + .with_prelude() + .into(), } .into(); @@ -54,7 +56,7 @@ fn make_simple_hugr(copies: usize) -> (Hugr, Node) { fn add_df_children(b: &mut Hugr, parent: Node, copies: usize) -> (Node, Node, Node) { 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![BOOL_T; copies])); - let copy = b.add_node_with_parent(parent, Noop { ty: BOOL_T }); + let copy = b.add_node_with_parent(parent, Noop(BOOL_T)); b.connect(input, 0, copy, 0); for i in 0..copies { @@ -100,16 +102,16 @@ fn invalid_root() { #[test] fn leaf_root() { - let leaf_op: OpType = Noop { ty: USIZE_T }.into(); + let leaf_op: OpType = Noop(USIZE_T).into(); let b = Hugr::new(leaf_op); - assert_eq!(b.validate(&EMPTY_REG), Ok(())); + assert_eq!(b.validate(&PRELUDE_REGISTRY), Ok(())); } #[test] fn dfg_root() { let dfg_op: OpType = ops::DFG { - signature: Signature::new_endo(type_row![BOOL_T]), + signature: Signature::new_endo(type_row![BOOL_T]).with_prelude(), } .into(); @@ -181,7 +183,7 @@ fn df_children_restrictions() { .unwrap(); // Replace the output operation of the df subgraph with a copy - b.replace_op(output, Noop { ty: NAT }).unwrap(); + b.replace_op(output, Noop(NAT)).unwrap(); assert_matches!( b.validate(&EMPTY_REG), Err(ValidationError::InvalidInitialChild { parent, .. }) => assert_eq!(parent, def) @@ -720,6 +722,7 @@ fn test_polymorphic_call() -> Result<(), Box> { vec![TypeParam::Extensions], Signature::new(vec![utou(es.clone()), int_pair.clone()], int_pair.clone()) .with_extension_delta(EXT_ID) + .with_prelude() .with_extension_delta(es.clone()), ), )?; @@ -740,13 +743,8 @@ fn test_polymorphic_call() -> Result<(), Box> { let [f2] = cc.add_dataflow_op(op, [func, i2])?.outputs_arr(); cc.finish_with_outputs([f1, f2])?; let res = c.finish_sub_container()?.outputs(); - let tup = f.add_dataflow_op( - MakeTuple { - tys: type_row![USIZE_T; 2], - }, - res, - )?; - f.finish_with_outputs(tup.outputs())? + let tup = f.make_tuple(res)?; + f.finish_with_outputs([tup])? }; let reg = ExtensionRegistry::try_new([e, PRELUDE.to_owned()])?; @@ -763,7 +761,7 @@ fn test_polymorphic_call() -> Result<(), Box> { let call_ty = h.get_optype(call.node()).dataflow_signature().unwrap(); let exp_fun_ty = Signature::new(vec![utou(PRELUDE_ID), int_pair.clone()], int_pair) .with_extension_delta(EXT_ID) - .with_extension_delta(PRELUDE_ID); + .with_prelude(); assert_eq!(call_ty, exp_fun_ty); Ok(()) } @@ -961,10 +959,11 @@ mod extension_tests { use super::*; use crate::builder::handle::Outputs; use crate::builder::{BlockBuilder, BuildHandle, CFGBuilder, DFGWrapper, TailLoopBuilder}; + use crate::extension::prelude::Lift; + use crate::extension::prelude::PRELUDE_ID; use crate::extension::ExtensionSet; use crate::macros::const_extension_ids; use crate::Wire; - const_extension_ids! { const XA: ExtensionId = "A"; const XB: ExtensionId = "BOOL_EXT"; @@ -997,13 +996,7 @@ mod extension_tests { }, ); - let lift = hugr.add_node_with_parent( - hugr.root(), - ops::Lift { - type_row: type_row![USIZE_T], - new_extension: XB, - }, - ); + let lift = hugr.add_node_with_parent(hugr.root(), Lift::new(type_row![USIZE_T], XB)); hugr.connect(input, 0, lift, 0); hugr.connect(lift, 0, output, 0); @@ -1015,7 +1008,7 @@ mod extension_tests { parent: hugr.root(), parent_extensions, child: lift, - child_extensions: XB.into() + child_extensions: ExtensionSet::from_iter([PRELUDE_ID, XB]), })) ); } @@ -1058,7 +1051,7 @@ mod extension_tests { #[rstest] #[case(XA.into(), false)] #[case(ExtensionSet::new(), false)] - #[case(ExtensionSet::from_iter([XA, XB]), true)] + #[case(ExtensionSet::from_iter([XA, XB, PRELUDE_ID]), true)] fn conditional_extension_mismatch( #[case] parent_extensions: ExtensionSet, #[case] success: bool, @@ -1075,11 +1068,15 @@ mod extension_tests { // 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_exts = if let Some(ex) = &case_ext { + ExtensionSet::from_iter([ex.clone(), PRELUDE_ID]) + } else { + ExtensionSet::new() + }; let case = hugr.add_node_with_parent( hugr.root(), ops::Case { - signature: Signature::new_endo(USIZE_T).with_extension_delta(case_exts.clone()), + signature: Signature::new_endo(USIZE_T).with_extension_delta(case_exts), }, ); @@ -1098,13 +1095,8 @@ mod extension_tests { let res = match case_ext { None => input, Some(new_ext) => { - let lift = hugr.add_node_with_parent( - case, - ops::Lift { - type_row: type_row![USIZE_T], - new_extension: new_ext, - }, - ); + let lift = + hugr.add_node_with_parent(case, Lift::new(type_row![USIZE_T], new_ext)); hugr.connect(input, 0, lift, 0); lift } @@ -1121,7 +1113,7 @@ mod extension_tests { parent: hugr.root(), parent_extensions, child, - child_extensions: XB.into(), + child_extensions: ExtensionSet::from_iter([XB, PRELUDE_ID]), })) }; assert_eq!(result, expected); @@ -1133,18 +1125,12 @@ mod extension_tests { 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))] + #[values((ExtensionSet::from_iter([XA,PRELUDE_ID]), false), (PRELUDE_ID.into(), false), (ExtensionSet::from_iter([XA,XB,PRELUDE_ID]), true))] parent_exts_success: (ExtensionSet, bool), ) -> Result<(), BuildError> { 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, - }, - dfg.input_wires(), - )?; + let lift = dfg.add_dataflow_op(Lift::new(USIZE_T.into(), XB), dfg.input_wires())?; let pred = make_pred(&mut dfg, lift.outputs())?; let root = dfg.hugr().root(); let res = dfg.finish_prelude_hugr_with_outputs([pred]); @@ -1158,7 +1144,7 @@ mod extension_tests { parent: root, parent_extensions, child: lift.node(), - child_extensions: XB.into() + child_extensions: ExtensionSet::from_iter([XB, PRELUDE_ID]) } ))) ); diff --git a/hugr-core/src/hugr/views/root_checked.rs b/hugr-core/src/hugr/views/root_checked.rs index 83d6aa46c..c98943966 100644 --- a/hugr-core/src/hugr/views/root_checked.rs +++ b/hugr-core/src/hugr/views/root_checked.rs @@ -70,11 +70,12 @@ impl, Root: NodeHandle> HugrMut for RootChecke #[cfg(test)] mod test { use super::RootChecked; + use crate::extension::prelude::MakeTuple; use crate::extension::ExtensionSet; use crate::hugr::internal::HugrMutInternals; use crate::hugr::{HugrError, HugrMut}; use crate::ops::handle::{BasicBlockID, CfgID, DataflowParentID, DfgID}; - use crate::ops::{DataflowBlock, MakeTuple, OpTag, OpType}; + use crate::ops::{DataflowBlock, OpTag, OpType}; use crate::{ops, type_row, types::Signature, Hugr, HugrView}; #[test] @@ -131,7 +132,7 @@ mod test { let mut bb_v = RootChecked::<_, BasicBlockID>::try_new(dfp_v).unwrap(); // And it's a HugrMut: - let nodetype = MakeTuple { tys: type_row![] }; + let nodetype = MakeTuple(type_row![]); bb_v.add_node_with_parent(bb_v.root(), nodetype); } } diff --git a/hugr-core/src/ops.rs b/hugr-core/src/ops.rs index 40bf4db16..14305020d 100644 --- a/hugr-core/src/ops.rs +++ b/hugr-core/src/ops.rs @@ -5,10 +5,11 @@ pub mod controlflow; pub mod custom; pub mod dataflow; pub mod handle; -pub mod leaf; pub mod module; +pub mod sum; pub mod tag; pub mod validate; +use crate::extension::simple_op::MakeExtensionOp; use crate::extension::ExtensionSet; use crate::types::{EdgeKind, Signature}; use crate::{Direction, OutgoingPort, Port}; @@ -26,9 +27,9 @@ pub use dataflow::{ Call, CallIndirect, DataflowOpTrait, DataflowParent, Input, LoadConstant, LoadFunction, Output, DFG, }; -pub use leaf::{Lift, MakeTuple, Noop, Tag, UnpackTuple}; pub use module::{AliasDecl, AliasDefn, FuncDecl, FuncDefn, Module}; use smol_str::SmolStr; +pub use sum::Tag; pub use tag::OpTag; #[enum_dispatch(OpTrait, NamedOp, ValidateOp, OpParent)] @@ -57,11 +58,7 @@ pub enum OpType { ExtensionOp, #[serde(rename = "Extension")] OpaqueOp, - Noop, - MakeTuple, - UnpackTuple, Tag, - Lift, DataflowBlock, ExitBlock, TailLoop, @@ -116,11 +113,7 @@ impl_op_ref_try_into!(LoadConstant); impl_op_ref_try_into!(LoadFunction); impl_op_ref_try_into!(DFG, dfg); impl_op_ref_try_into!(ExtensionOp); -impl_op_ref_try_into!(Noop); -impl_op_ref_try_into!(MakeTuple); -impl_op_ref_try_into!(UnpackTuple); impl_op_ref_try_into!(Tag); -impl_op_ref_try_into!(Lift); impl_op_ref_try_into!(DataflowBlock); impl_op_ref_try_into!(ExitBlock); impl_op_ref_try_into!(TailLoop); @@ -295,6 +288,12 @@ impl OpType { pub fn is_container(&self) -> bool { self.validity_flags().allowed_children != OpTag::None } + + /// Cast to an extension operation. + pub fn cast(&self) -> Option { + self.as_extension_op() + .and_then(|o| T::from_extension_op(o).ok()) + } } /// Macro used by operations that want their @@ -432,11 +431,7 @@ impl OpParent for LoadConstant {} impl OpParent for LoadFunction {} impl OpParent for ExtensionOp {} impl OpParent for OpaqueOp {} -impl OpParent for Noop {} -impl OpParent for MakeTuple {} -impl OpParent for UnpackTuple {} impl OpParent for Tag {} -impl OpParent for Lift {} impl OpParent for CFG {} impl OpParent for Conditional {} impl OpParent for FuncDecl {} diff --git a/hugr-core/src/ops/leaf.rs b/hugr-core/src/ops/leaf.rs deleted file mode 100644 index ab6171901..000000000 --- a/hugr-core/src/ops/leaf.rs +++ /dev/null @@ -1,230 +0,0 @@ -//! Definition of dataflow operations with no children. - -use super::dataflow::DataflowOpTrait; -use super::{impl_op_name, OpTag}; - -use crate::extension::ExtensionSet; - -use crate::{ - extension::ExtensionId, - types::{EdgeKind, Signature, Type, TypeRow}, -}; - -/// A no-op operation. -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[non_exhaustive] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -pub struct Noop { - /// The type of edges connecting the Noop. - pub ty: Type, -} - -impl Noop { - /// Create a new Noop operation. - pub fn new(ty: Type) -> Self { - Self { ty } - } -} - -impl Default for Noop { - fn default() -> Self { - Self { ty: Type::UNIT } - } -} - -/// An operation that packs all its inputs into a tuple. -#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -#[non_exhaustive] -pub struct MakeTuple { - ///Tuple element types. - pub tys: TypeRow, -} - -impl MakeTuple { - /// Create a new MakeTuple operation. - pub fn new(tys: TypeRow) -> Self { - Self { tys } - } -} - -/// An operation that unpacks a tuple into its components. -#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -#[non_exhaustive] -pub struct UnpackTuple { - ///Tuple element types. - pub tys: TypeRow, -} - -impl UnpackTuple { - /// Create a new UnpackTuple operation. - pub fn new(tys: TypeRow) -> Self { - Self { tys } - } -} - -/// An operation that creates a tagged sum value from one of its variants. -#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[non_exhaustive] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -pub struct Tag { - /// The variant to create. - pub tag: usize, - /// The variants of the sum type. - /// TODO this allows *none* of the variants to contain row variables, but - /// we could allow variants *other than the tagged one* to contain rowvars. - pub variants: Vec, -} - -impl Tag { - /// Create a new Tag operation. - pub fn new(tag: usize, variants: Vec) -> Self { - Self { tag, variants } - } -} - -/// A node which adds a extension req to the types of the wires it is passed -/// It has no effect on the values passed along the edge -#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)] -#[cfg_attr(test, derive(proptest_derive::Arbitrary))] -#[non_exhaustive] -pub struct Lift { - /// The types of the edges - pub type_row: TypeRow, - /// The extensions which we're adding to the inputs - pub new_extension: ExtensionId, -} - -impl Lift { - /// Create a new Lift operation. - pub fn new(type_row: TypeRow, new_extension: ExtensionId) -> Self { - Self { - type_row, - new_extension, - } - } -} - -impl_op_name!(Noop); -impl_op_name!(MakeTuple); -impl_op_name!(UnpackTuple); -impl_op_name!(Tag); -impl_op_name!(Lift); - -impl DataflowOpTrait for Noop { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - "Noop gate" - } - - /// The signature of the operation. - fn signature(&self) -> Signature { - Signature::new(vec![self.ty.clone()], vec![self.ty.clone()]) - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} - -impl DataflowOpTrait for MakeTuple { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - "MakeTuple operation" - } - - /// The signature of the operation. - fn signature(&self) -> Signature { - Signature::new(self.tys.clone(), vec![Type::new_tuple(self.tys.clone())]) - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} - -impl DataflowOpTrait for UnpackTuple { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - "UnpackTuple operation" - } - - /// The signature of the operation. - fn signature(&self) -> Signature { - Signature::new(vec![Type::new_tuple(self.tys.clone())], self.tys.clone()) - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} - -impl DataflowOpTrait for Tag { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - "Tag Sum operation" - } - - /// The signature of the operation. - fn signature(&self) -> Signature { - Signature::new( - self.variants - .get(self.tag) - .expect("Not a valid tag") - .clone(), - vec![Type::new_sum(self.variants.clone())], - ) - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} - -impl DataflowOpTrait for Lift { - const TAG: OpTag = OpTag::Leaf; - - /// A human-readable description of the operation. - fn description(&self) -> &str { - "Add a extension requirement to an edge" - } - - /// The signature of the operation. - fn signature(&self) -> Signature { - Signature::new(self.type_row.clone(), self.type_row.clone()) - .with_extension_delta(ExtensionSet::singleton(&self.new_extension)) - } - - fn other_input(&self) -> Option { - Some(EdgeKind::StateOrder) - } - - fn other_output(&self) -> Option { - Some(EdgeKind::StateOrder) - } -} diff --git a/hugr-core/src/ops/sum.rs b/hugr-core/src/ops/sum.rs new file mode 100644 index 000000000..aa7119104 --- /dev/null +++ b/hugr-core/src/ops/sum.rs @@ -0,0 +1,55 @@ +//! Definition of dataflow operations with no children. + +use super::dataflow::DataflowOpTrait; +use super::{impl_op_name, OpTag}; +use crate::types::{EdgeKind, Signature, Type, TypeRow}; + +/// An operation that creates a tagged sum value from one of its variants. +#[derive(Debug, Clone, Default, PartialEq, Eq, serde::Serialize, serde::Deserialize)] +#[non_exhaustive] +#[cfg_attr(test, derive(proptest_derive::Arbitrary))] +pub struct Tag { + /// The variant to create. + pub tag: usize, + /// The variants of the sum type. + /// TODO this allows *none* of the variants to contain row variables, but + /// we could allow variants *other than the tagged one* to contain rowvars. + pub variants: Vec, +} + +impl Tag { + /// Create a new Tag operation. + pub fn new(tag: usize, variants: Vec) -> Self { + Self { tag, variants } + } +} + +impl_op_name!(Tag); + +impl DataflowOpTrait for Tag { + const TAG: OpTag = OpTag::Leaf; + + /// A human-readable description of the operation. + fn description(&self) -> &str { + "Tag Sum operation" + } + + /// The signature of the operation. + fn signature(&self) -> Signature { + Signature::new( + self.variants + .get(self.tag) + .expect("Not a valid tag") + .clone(), + vec![Type::new_sum(self.variants.clone())], + ) + } + + fn other_input(&self) -> Option { + Some(EdgeKind::StateOrder) + } + + fn other_output(&self) -> Option { + Some(EdgeKind::StateOrder) + } +} diff --git a/hugr-core/src/ops/validate.rs b/hugr-core/src/ops/validate.rs index 9a145d301..3b3fa8da9 100644 --- a/hugr-core/src/ops/validate.rs +++ b/hugr-core/src/ops/validate.rs @@ -351,7 +351,7 @@ fn validate_cfg_edge(edge: ChildrenEdgeData) -> Result<(), EdgeValidationError> #[cfg(test)] mod test { - use crate::extension::prelude::USIZE_T; + use crate::extension::prelude::{Noop, USIZE_T}; use crate::ops::dataflow::IOTrait; use crate::{ops, type_row}; use cool_asserts::assert_matches; @@ -365,7 +365,7 @@ mod test { let input_node: OpType = ops::Input::new(in_types.clone()).into(); let output_node = ops::Output::new(out_types.clone()).into(); - let leaf_node = ops::Noop { ty: USIZE_T }.into(); + let leaf_node = Noop(USIZE_T).into(); // Well-formed dataflow sibling nodes. Check the input and output node signatures. let children = vec![ @@ -409,8 +409,8 @@ mod test { } use super::{ - AliasDecl, AliasDefn, Call, CallIndirect, Const, ExtensionOp, FuncDecl, Input, Lift, - LoadConstant, LoadFunction, MakeTuple, Noop, OpaqueOp, Output, Tag, UnpackTuple, + AliasDecl, AliasDefn, Call, CallIndirect, Const, ExtensionOp, FuncDecl, Input, LoadConstant, + LoadFunction, OpaqueOp, Output, Tag, }; impl_validate_op!(FuncDecl); impl_validate_op!(AliasDecl); @@ -424,9 +424,5 @@ impl_validate_op!(LoadFunction); impl_validate_op!(CallIndirect); impl_validate_op!(ExtensionOp); impl_validate_op!(OpaqueOp); -impl_validate_op!(Noop); -impl_validate_op!(MakeTuple); -impl_validate_op!(UnpackTuple); impl_validate_op!(Tag); -impl_validate_op!(Lift); impl_validate_op!(ExitBlock); diff --git a/hugr-core/src/types/signature.rs b/hugr-core/src/types/signature.rs index cfcb8bd58..ee0164087 100644 --- a/hugr-core/src/types/signature.rs +++ b/hugr-core/src/types/signature.rs @@ -49,12 +49,17 @@ pub type Signature = FuncTypeBase; pub type FuncValueType = FuncTypeBase; impl FuncTypeBase { - /// Builder method, add extension_reqs to an FunctionType + /// Builder method, add extension_reqs to a FunctionType pub fn with_extension_delta(mut self, rs: impl Into) -> Self { self.extension_reqs = self.extension_reqs.union(rs.into()); self } + /// Shorthand for adding the prelude extension to a FunctionType. + pub fn with_prelude(self) -> Self { + self.with_extension_delta(crate::extension::prelude::PRELUDE_ID) + } + pub(crate) fn substitute(&self, tr: &Substitution) -> Self { Self { input: self.input.substitute(tr), diff --git a/hugr-passes/src/const_fold.rs b/hugr-passes/src/const_fold.rs index 9cbd9ada5..a82664ab0 100644 --- a/hugr-passes/src/const_fold.rs +++ b/hugr-passes/src/const_fold.rs @@ -11,16 +11,14 @@ use hugr_core::types::SumType; use hugr_core::Direction; use hugr_core::{ builder::{DFGBuilder, Dataflow, DataflowHugr}, - extension::{ConstFoldResult, ExtensionRegistry}, + extension::{fold_out_row, ConstFoldResult, ExtensionRegistry}, hugr::{ hugrmut::HugrMut, rewrite::consts::{RemoveConst, RemoveLoadConstant}, views::SiblingSubgraph, }, ops::{OpType, Value}, - type_row, - utils::sorted_consts, - Hugr, HugrView, IncomingPort, Node, SimpleReplacement, + type_row, Hugr, HugrView, IncomingPort, Node, SimpleReplacement, }; use crate::validation::{ValidatePassError, ValidationLevel}; @@ -90,32 +88,10 @@ impl ConstantFoldPass { } } -/// Tag some output constants with [`OutgoingPort`] inferred from the ordering. -fn out_row(consts: impl IntoIterator) -> ConstFoldResult { - let vec = consts - .into_iter() - .enumerate() - .map(|(i, c)| (i.into(), c)) - .collect(); - Some(vec) -} - /// For a given op and consts, attempt to evaluate the op. pub fn fold_leaf_op(op: &OpType, consts: &[(IncomingPort, Value)]) -> ConstFoldResult { let fold_result = match op { - OpType::Noop { .. } => out_row([consts.first()?.1.clone()]), - OpType::MakeTuple { .. } => { - out_row([Value::tuple(sorted_consts(consts).into_iter().cloned())]) - } - OpType::UnpackTuple { .. } => { - let c = &consts.first()?.1; - let Some(vs) = c.as_tuple() else { - panic!("This op always takes a Tuple input."); - }; - out_row(vs.iter().cloned()) - } - - OpType::Tag(t) => out_row([Value::sum( + OpType::Tag(t) => fold_out_row([Value::sum( t.tag, consts.iter().map(|(_, konst)| konst.clone()), SumType::new(t.variants.clone()), diff --git a/hugr-passes/src/const_fold/test.rs b/hugr-passes/src/const_fold/test.rs index 0898e93d5..06ba4131c 100644 --- a/hugr-passes/src/const_fold/test.rs +++ b/hugr-passes/src/const_fold/test.rs @@ -1,5 +1,6 @@ use crate::const_fold::constant_fold_pass; use hugr_core::builder::{DFGBuilder, Dataflow, DataflowHugr}; +use hugr_core::extension::prelude::UnpackTuple; 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; @@ -16,7 +17,7 @@ use lazy_static::lazy_static; use super::*; use hugr_core::builder::Container; -use hugr_core::ops::{OpType, UnpackTuple}; +use hugr_core::ops::OpType; use hugr_core::std_extensions::arithmetic::conversions::ConvertOpDef; use hugr_core::std_extensions::arithmetic::float_ops::FloatOps; use hugr_core::std_extensions::arithmetic::float_types::{ConstF64, FLOAT64_TYPE}; diff --git a/hugr-passes/src/merge_bbs.rs b/hugr-passes/src/merge_bbs.rs index 12ef38dbf..51fd07d9b 100644 --- a/hugr-passes/src/merge_bbs.rs +++ b/hugr-passes/src/merge_bbs.rs @@ -2,6 +2,7 @@ //! and the target BB has no other predecessors. use std::collections::HashMap; +use hugr_core::extension::prelude::UnpackTuple; use hugr_core::hugr::hugrmut::HugrMut; use itertools::Itertools; @@ -9,7 +10,6 @@ use hugr_core::hugr::rewrite::inline_dfg::InlineDFG; use hugr_core::hugr::rewrite::replace::{NewEdgeKind, NewEdgeSpec, Replacement}; use hugr_core::hugr::RootTagged; use hugr_core::ops::handle::CfgID; -use hugr_core::ops::leaf::UnpackTuple; use hugr_core::ops::{DataflowBlock, DataflowParent, Input, Output, DFG}; use hugr_core::{Hugr, HugrView, Node}; @@ -158,6 +158,7 @@ fn mk_rep( mod test { use std::collections::HashSet; + use hugr_core::extension::prelude::Lift; use itertools::Itertools; use rstest::rstest; @@ -167,7 +168,7 @@ mod test { use hugr_core::hugr::views::sibling::SiblingMut; use hugr_core::ops::constant::Value; use hugr_core::ops::handle::CfgID; - use hugr_core::ops::{Lift, LoadConstant, Noop, OpTrait, OpType}; + use hugr_core::ops::{LoadConstant, OpTrait, OpType}; use hugr_core::types::{Signature, Type, TypeRow}; use hugr_core::{const_extension_ids, type_row, Extension, Hugr, HugrView, Wire}; @@ -221,6 +222,8 @@ mod test { | | => / \ \--<--<--/ \--<-----<--/ */ + + use hugr_core::extension::prelude::Noop; let loop_variants = type_row![QB_T]; let exit_types = type_row![USIZE_T]; let e = extension(); @@ -271,7 +274,7 @@ mod test { // Check the Noop('s) is/are in the right block(s) let nops = h .nodes() - .filter(|n| matches!(h.get_optype(*n), OpType::Noop(_))); + .filter(|n| h.get_optype(*n).cast::().is_some()); let (entry_nop, expected_backedge_target) = if self_loop { assert_eq!(h.children(r).len(), 2); (nops.exactly_one().ok().unwrap(), entry) @@ -294,9 +297,10 @@ mod test { HashSet::from([expected_backedge_target, exit]) ); // And the Noop in the entry block is consumed by the custom Test op - let tst = find_unique(h.nodes(), |n| { - matches!(h.get_optype(*n), OpType::ExtensionOp(_)) - }); + let tst = find_unique( + h.nodes(), + |n| matches!(h.get_optype(*n), OpType::ExtensionOp(c) if c.def().extension() != &PRELUDE_ID), + ); assert_eq!(h.get_parent(tst), Some(entry)); assert_eq!( h.output_neighbours(entry_nop).collect::>(), @@ -359,9 +363,10 @@ mod test { // Should only be one BB left let [bb, _exit] = h.children(h.root()).collect::>().try_into().unwrap(); - let tst = find_unique(h.nodes(), |n| { - matches!(h.get_optype(*n), OpType::ExtensionOp(_)) - }); + let tst = find_unique( + h.nodes(), + |n| matches!(h.get_optype(*n), OpType::ExtensionOp(c) if c.def().extension() != &PRELUDE_ID), + ); assert_eq!(h.get_parent(tst), Some(bb)); let inp = find_unique(h.nodes(), |n| matches!(h.get_optype(*n), OpType::Input(_))); diff --git a/hugr-passes/src/non_local.rs b/hugr-passes/src/non_local.rs index 4b1b62e0d..347e5be27 100644 --- a/hugr-passes/src/non_local.rs +++ b/hugr-passes/src/non_local.rs @@ -42,8 +42,11 @@ pub fn ensure_no_nonlocal_edges(hugr: &impl HugrView) -> Result<(), NonLocalEdge mod test { use hugr_core::{ builder::{DFGBuilder, Dataflow, DataflowHugr, DataflowSubContainer}, - extension::{prelude::BOOL_T, EMPTY_REG}, - ops::{handle::NodeHandle, Noop}, + extension::{ + prelude::{Noop, BOOL_T}, + EMPTY_REG, + }, + ops::handle::NodeHandle, type_row, types::Signature, }; @@ -53,7 +56,7 @@ mod test { #[test] fn ensures_no_nonlocal_edges() { let hugr = { - let mut builder = DFGBuilder::new(Signature::new_endo(BOOL_T)).unwrap(); + let mut builder = DFGBuilder::new(Signature::new_endo(BOOL_T).with_prelude()).unwrap(); let [in_w] = builder.input_wires_arr(); let [out_w] = builder .add_dataflow_op(Noop::new(BOOL_T), [in_w]) @@ -69,11 +72,11 @@ mod test { #[test] fn find_nonlocal_edges() { let (hugr, edge) = { - let mut builder = DFGBuilder::new(Signature::new_endo(BOOL_T)).unwrap(); + let mut builder = DFGBuilder::new(Signature::new_endo(BOOL_T).with_prelude()).unwrap(); let [in_w] = builder.input_wires_arr(); let ([out_w], edge) = { let mut dfg_builder = builder - .dfg_builder(Signature::new(type_row![], BOOL_T), []) + .dfg_builder(Signature::new(type_row![], BOOL_T).with_prelude(), []) .unwrap(); let noop = dfg_builder .add_dataflow_op(Noop::new(BOOL_T), [in_w]) diff --git a/hugr-py/src/hugr/_serialization/ops.py b/hugr-py/src/hugr/_serialization/ops.py index 993a9b070..b2a899dc2 100644 --- a/hugr-py/src/hugr/_serialization/ops.py +++ b/hugr-py/src/hugr/_serialization/ops.py @@ -536,51 +536,6 @@ def deserialize(self) -> ops.Custom: ) -class Noop(DataflowOp): - """A no-op operation.""" - - op: Literal["Noop"] = "Noop" - ty: Type - - def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: - assert len(in_types) == 1 - assert len(out_types) == 1 - assert in_types[0] == out_types[0] - self.ty = in_types[0] - - def deserialize(self) -> ops.Noop: - return ops.Noop(self.ty.deserialize()) - - -class MakeTuple(DataflowOp): - """An operation that packs all its inputs into a tuple.""" - - op: Literal["MakeTuple"] = "MakeTuple" - tys: TypeRow = Field(default_factory=list) - - def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: - # If we have a single order edge as input, this is a unit - if in_types == [None]: - in_types = [] - self.tys = list(in_types) - - def deserialize(self) -> ops.MakeTuple: - return ops.MakeTuple(deser_it(self.tys)) - - -class UnpackTuple(DataflowOp): - """An operation that packs all its inputs into a tuple.""" - - op: Literal["UnpackTuple"] = "UnpackTuple" - tys: TypeRow = Field(default_factory=list) - - def insert_port_types(self, in_types: TypeRow, out_types: TypeRow) -> None: - self.tys = list(out_types) - - def deserialize(self) -> ops.UnpackTuple: - return ops.UnpackTuple(deser_it(self.tys)) - - class Tag(DataflowOp): """An operation that creates a tagged sum value from one of its variants.""" @@ -595,20 +550,6 @@ def deserialize(self) -> ops.Tag: ) -class Lift(DataflowOp): - """Fixes some TypeParams of a polymorphic type by providing TypeArgs.""" - - op: Literal["Lift"] = "Lift" - type_row: TypeRow - new_extension: ExtensionId - - def deserialize(self) -> ops.Lift: - return ops.Lift( - _type_row=deser_it(self.type_row), - new_extension=self.new_extension, - ) - - class AliasDecl(BaseOp): op: Literal["AliasDecl"] = "AliasDecl" name: str @@ -648,11 +589,7 @@ class OpType(RootModel): | LoadConstant | LoadFunction | ExtensionOp - | Noop - | MakeTuple - | UnpackTuple | Tag - | Lift | DFG | AliasDecl | AliasDefn diff --git a/hugr-py/src/hugr/ops.py b/hugr-py/src/hugr/ops.py index 1edcebffe..015385c00 100644 --- a/hugr-py/src/hugr/ops.py +++ b/hugr-py/src/hugr/ops.py @@ -407,7 +407,7 @@ def op_def(cls) -> ext.OpDef: @dataclass() -class MakeTuple(DataflowOp, _PartialOp): +class MakeTuple(AsExtOp, _PartialOp): """Operation to create a tuple from a sequence of wires.""" _types: tys.TypeRow | None = field(default=None, repr=False) @@ -422,18 +422,20 @@ def types(self) -> tys.TypeRow: """ return _check_complete(self, self._types) - def _to_serial(self, parent: Node) -> sops.MakeTuple: - return sops.MakeTuple( - parent=parent.idx, - tys=ser_it(self.types), - ) + def op_def(self) -> ext.OpDef: + from hugr import std # no circular import - def __call__(self, *elements: ComWire) -> Command: - return super().__call__(*elements) + return std.PRELUDE.get_op("MakeTuple") - def outer_signature(self) -> tys.FunctionType: + def cached_signature(self) -> tys.FunctionType | None: return tys.FunctionType(input=self.types, output=[tys.Tuple(*self.types)]) + def type_args(self) -> list[tys.TypeArg]: + return [tys.SequenceArg([t.type_arg() for t in self.types])] + + def __call__(self, *elements: ComWire) -> Command: + return super().__call__(*elements) + def _set_in_types(self, types: tys.TypeRow) -> None: self._types = types @@ -442,7 +444,7 @@ def __repr__(self) -> str: @dataclass() -class UnpackTuple(DataflowOp, _PartialOp): +class UnpackTuple(AsExtOp, _PartialOp): """Operation to unpack a tuple into its elements.""" _types: tys.TypeRow | None = field(default=None, repr=False) @@ -456,16 +458,21 @@ def types(self) -> tys.TypeRow: """ return _check_complete(self, self._types) + def op_def(self) -> ext.OpDef: + from hugr import std # no circular import + + return std.PRELUDE.get_op("UnpackTuple") + + def cached_signature(self) -> tys.FunctionType | None: + return tys.FunctionType(input=[tys.Tuple(*self.types)], output=self.types) + + def type_args(self) -> list[tys.TypeArg]: + return [tys.SequenceArg([t.type_arg() for t in self.types])] + @property def num_out(self) -> int: return len(self.types) - def _to_serial(self, parent: Node) -> sops.UnpackTuple: - return sops.UnpackTuple( - parent=parent.idx, - tys=ser_it(self.types), - ) - def __call__(self, tuple_: ComWire) -> Command: return super().__call__(tuple_) @@ -1163,7 +1170,7 @@ def port_kind(self, port: InPort | OutPort) -> tys.Kind: @dataclass -class Noop(DataflowOp, _PartialOp): +class Noop(AsExtOp, _PartialOp): """Identity operation that passes through its input.""" _type: tys.Type | None = None @@ -1174,8 +1181,13 @@ def type_(self) -> tys.Type: """The type of the input and output of the operation.""" return _check_complete(self, self._type) - def _to_serial(self, parent: Node) -> sops.Noop: - return sops.Noop(parent=parent.idx, ty=self.type_._to_serial_root()) + def op_def(self) -> ext.OpDef: + from hugr import std # no circular import + + return std.PRELUDE.get_op("Noop") + + def cached_signature(self) -> tys.FunctionType | None: + return tys.FunctionType.endo([self.type_]) def outer_signature(self) -> tys.FunctionType: return tys.FunctionType.endo([self.type_]) @@ -1188,34 +1200,6 @@ def __repr__(self) -> str: return "Noop" + (f"({self._type})" if self._type is not None else "") -@dataclass -class Lift(DataflowOp, _PartialOp): - """Add an extension requirement to input values and pass them through.""" - - #: Extension added. - new_extension: tys.ExtensionId - _type_row: tys.TypeRow | None = field(default=None, repr=False) - num_out: int = field(default=1, repr=False) - - @property - def type_row(self) -> tys.TypeRow: - """Types of the input and output of the operation.""" - return _check_complete(self, self._type_row) - - def _to_serial(self, parent: Node) -> sops.Lift: - return sops.Lift( - parent=parent.idx, - new_extension=self.new_extension, - type_row=ser_it(self.type_row), - ) - - def outer_signature(self) -> tys.FunctionType: - return tys.FunctionType.endo(self.type_row) - - def _set_in_types(self, types: tys.TypeRow) -> None: - self._type_row = types - - @dataclass class AliasDecl(Op): """Declare an external type alias.""" diff --git a/hugr-py/src/hugr/std/__init__.py b/hugr-py/src/hugr/std/__init__.py index aa97cf0b0..f5f3750e7 100644 --- a/hugr-py/src/hugr/std/__init__.py +++ b/hugr-py/src/hugr/std/__init__.py @@ -11,3 +11,6 @@ def _load_extension(name: str) -> Extension: json_str = pkgutil.get_data(__name__, f"_json_defs/{replacement}.json") assert json_str is not None return PdExtension.model_validate_json(json_str).deserialize() + + +PRELUDE = _load_extension("prelude") diff --git a/hugr-py/src/hugr/std/_json_defs/prelude.json b/hugr-py/src/hugr/std/_json_defs/prelude.json index 315c7c105..3ec1d8699 100644 --- a/hugr-py/src/hugr/std/_json_defs/prelude.json +++ b/hugr-py/src/hugr/std/_json_defs/prelude.json @@ -67,6 +67,160 @@ }, "values": {}, "operations": { + "Lift": { + "extension": "prelude", + "name": "Lift", + "description": "Add extension requirements to a row of values", + "signature": { + "params": [ + { + "tp": "Extensions" + }, + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "output": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "extension_reqs": [ + "0" + ] + } + }, + "binary": false + }, + "MakeTuple": { + "extension": "prelude", + "name": "MakeTuple", + "description": "MakeTuple operation", + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "Sum", + "s": "General", + "rows": [ + [ + { + "t": "R", + "i": 0, + "b": "A" + } + ] + ] + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "Noop": { + "extension": "prelude", + "name": "Noop", + "description": "Noop gate", + "signature": { + "params": [ + { + "tp": "Type", + "b": "A" + } + ], + "body": { + "input": [ + { + "t": "V", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "V", + "i": 0, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "UnpackTuple": { + "extension": "prelude", + "name": "UnpackTuple", + "description": "UnpackTuple operation", + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "Sum", + "s": "General", + "rows": [ + [ + { + "t": "R", + "i": 0, + "b": "A" + } + ] + ] + } + ], + "output": [ + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, "new_array": { "extension": "prelude", "name": "new_array", @@ -78,8 +232,49 @@ "extension": "prelude", "name": "panic", "description": "Panic with input error", - "signature": null, - "binary": true + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + }, + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "Opaque", + "extension": "prelude", + "id": "error", + "args": [], + "bound": "C" + }, + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false }, "print": { "extension": "prelude", diff --git a/hugr-py/tests/test_hugr_build.py b/hugr-py/tests/test_hugr_build.py index 88d177604..ae5b060ff 100644 --- a/hugr-py/tests/test_hugr_build.py +++ b/hugr-py/tests/test_hugr_build.py @@ -310,15 +310,6 @@ def test_higher_order() -> None: validate(d.hugr) -def test_lift() -> None: - d = Dfg(tys.Qubit) - d.parent_op._extension_delta = ["X"] - (q,) = d.inputs() - lift = d.add(ops.Lift("X")(q)) - d.set_outputs(lift) - validate(d.hugr) - - def test_alias() -> None: mod = Module() _dfn = mod.add_alias_defn("my_int", INT_T) diff --git a/specification/schema/hugr_schema_live.json b/specification/schema/hugr_schema_live.json index 18c8d6a40..4a8c48df6 100644 --- a/specification/schema/hugr_schema_live.json +++ b/specification/schema/hugr_schema_live.json @@ -977,43 +977,6 @@ "title": "Input", "type": "object" }, - "Lift": { - "additionalProperties": true, - "description": "Fixes some TypeParams of a polymorphic type by providing TypeArgs.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Lift", - "default": "Lift", - "enum": [ - "Lift" - ], - "title": "Op", - "type": "string" - }, - "type_row": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Type Row", - "type": "array" - }, - "new_extension": { - "title": "New Extension", - "type": "string" - } - }, - "required": [ - "parent", - "type_row", - "new_extension" - ], - "title": "Lift", - "type": "object" - }, "ListParam": { "additionalProperties": true, "properties": { @@ -1104,37 +1067,6 @@ "title": "LoadFunction", "type": "object" }, - "MakeTuple": { - "additionalProperties": true, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "MakeTuple", - "default": "MakeTuple", - "enum": [ - "MakeTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "MakeTuple", - "type": "object" - }, "Module": { "additionalProperties": true, "description": "The root of a module, parent of all other `ModuleOp`s.", @@ -1159,34 +1091,6 @@ "title": "Module", "type": "object" }, - "Noop": { - "additionalProperties": true, - "description": "A no-op operation.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Noop", - "default": "Noop", - "enum": [ - "Noop" - ], - "title": "Op", - "type": "string" - }, - "ty": { - "$ref": "#/$defs/Type" - } - }, - "required": [ - "parent", - "ty" - ], - "title": "Noop", - "type": "object" - }, "OpDef": { "description": "Serializable definition for dynamically loaded operations.", "properties": { @@ -1265,16 +1169,12 @@ "FuncDecl": "#/$defs/FuncDecl", "FuncDefn": "#/$defs/FuncDefn", "Input": "#/$defs/Input", - "Lift": "#/$defs/Lift", "LoadConstant": "#/$defs/LoadConstant", "LoadFunction": "#/$defs/LoadFunction", - "MakeTuple": "#/$defs/MakeTuple", "Module": "#/$defs/Module", - "Noop": "#/$defs/Noop", "Output": "#/$defs/Output", "Tag": "#/$defs/Tag", - "TailLoop": "#/$defs/TailLoop", - "UnpackTuple": "#/$defs/UnpackTuple" + "TailLoop": "#/$defs/TailLoop" }, "propertyName": "op" }, @@ -1330,21 +1230,9 @@ { "$ref": "#/$defs/ExtensionOp" }, - { - "$ref": "#/$defs/Noop" - }, - { - "$ref": "#/$defs/MakeTuple" - }, - { - "$ref": "#/$defs/UnpackTuple" - }, { "$ref": "#/$defs/Tag" }, - { - "$ref": "#/$defs/Lift" - }, { "$ref": "#/$defs/DFG" }, @@ -2182,37 +2070,6 @@ "title": "UnitSum", "type": "object" }, - "UnpackTuple": { - "additionalProperties": true, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "UnpackTuple", - "default": "UnpackTuple", - "enum": [ - "UnpackTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "UnpackTuple", - "type": "object" - }, "Value": { "description": "A constant Value.", "discriminator": { diff --git a/specification/schema/hugr_schema_strict_live.json b/specification/schema/hugr_schema_strict_live.json index 4a2f6b7d0..d153e7e98 100644 --- a/specification/schema/hugr_schema_strict_live.json +++ b/specification/schema/hugr_schema_strict_live.json @@ -977,43 +977,6 @@ "title": "Input", "type": "object" }, - "Lift": { - "additionalProperties": false, - "description": "Fixes some TypeParams of a polymorphic type by providing TypeArgs.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Lift", - "default": "Lift", - "enum": [ - "Lift" - ], - "title": "Op", - "type": "string" - }, - "type_row": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Type Row", - "type": "array" - }, - "new_extension": { - "title": "New Extension", - "type": "string" - } - }, - "required": [ - "parent", - "type_row", - "new_extension" - ], - "title": "Lift", - "type": "object" - }, "ListParam": { "additionalProperties": false, "properties": { @@ -1104,37 +1067,6 @@ "title": "LoadFunction", "type": "object" }, - "MakeTuple": { - "additionalProperties": false, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "MakeTuple", - "default": "MakeTuple", - "enum": [ - "MakeTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "MakeTuple", - "type": "object" - }, "Module": { "additionalProperties": false, "description": "The root of a module, parent of all other `ModuleOp`s.", @@ -1159,34 +1091,6 @@ "title": "Module", "type": "object" }, - "Noop": { - "additionalProperties": false, - "description": "A no-op operation.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Noop", - "default": "Noop", - "enum": [ - "Noop" - ], - "title": "Op", - "type": "string" - }, - "ty": { - "$ref": "#/$defs/Type" - } - }, - "required": [ - "parent", - "ty" - ], - "title": "Noop", - "type": "object" - }, "OpDef": { "description": "Serializable definition for dynamically loaded operations.", "properties": { @@ -1265,16 +1169,12 @@ "FuncDecl": "#/$defs/FuncDecl", "FuncDefn": "#/$defs/FuncDefn", "Input": "#/$defs/Input", - "Lift": "#/$defs/Lift", "LoadConstant": "#/$defs/LoadConstant", "LoadFunction": "#/$defs/LoadFunction", - "MakeTuple": "#/$defs/MakeTuple", "Module": "#/$defs/Module", - "Noop": "#/$defs/Noop", "Output": "#/$defs/Output", "Tag": "#/$defs/Tag", - "TailLoop": "#/$defs/TailLoop", - "UnpackTuple": "#/$defs/UnpackTuple" + "TailLoop": "#/$defs/TailLoop" }, "propertyName": "op" }, @@ -1330,21 +1230,9 @@ { "$ref": "#/$defs/ExtensionOp" }, - { - "$ref": "#/$defs/Noop" - }, - { - "$ref": "#/$defs/MakeTuple" - }, - { - "$ref": "#/$defs/UnpackTuple" - }, { "$ref": "#/$defs/Tag" }, - { - "$ref": "#/$defs/Lift" - }, { "$ref": "#/$defs/DFG" }, @@ -2182,37 +2070,6 @@ "title": "UnitSum", "type": "object" }, - "UnpackTuple": { - "additionalProperties": false, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "UnpackTuple", - "default": "UnpackTuple", - "enum": [ - "UnpackTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "UnpackTuple", - "type": "object" - }, "Value": { "description": "A constant Value.", "discriminator": { diff --git a/specification/schema/testing_hugr_schema_live.json b/specification/schema/testing_hugr_schema_live.json index f454c556c..eb2c9c4ef 100644 --- a/specification/schema/testing_hugr_schema_live.json +++ b/specification/schema/testing_hugr_schema_live.json @@ -977,43 +977,6 @@ "title": "Input", "type": "object" }, - "Lift": { - "additionalProperties": true, - "description": "Fixes some TypeParams of a polymorphic type by providing TypeArgs.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Lift", - "default": "Lift", - "enum": [ - "Lift" - ], - "title": "Op", - "type": "string" - }, - "type_row": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Type Row", - "type": "array" - }, - "new_extension": { - "title": "New Extension", - "type": "string" - } - }, - "required": [ - "parent", - "type_row", - "new_extension" - ], - "title": "Lift", - "type": "object" - }, "ListParam": { "additionalProperties": true, "properties": { @@ -1104,37 +1067,6 @@ "title": "LoadFunction", "type": "object" }, - "MakeTuple": { - "additionalProperties": true, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "MakeTuple", - "default": "MakeTuple", - "enum": [ - "MakeTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "MakeTuple", - "type": "object" - }, "Module": { "additionalProperties": true, "description": "The root of a module, parent of all other `ModuleOp`s.", @@ -1159,34 +1091,6 @@ "title": "Module", "type": "object" }, - "Noop": { - "additionalProperties": true, - "description": "A no-op operation.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Noop", - "default": "Noop", - "enum": [ - "Noop" - ], - "title": "Op", - "type": "string" - }, - "ty": { - "$ref": "#/$defs/Type" - } - }, - "required": [ - "parent", - "ty" - ], - "title": "Noop", - "type": "object" - }, "OpDef": { "description": "Serializable definition for dynamically loaded operations.", "properties": { @@ -1265,16 +1169,12 @@ "FuncDecl": "#/$defs/FuncDecl", "FuncDefn": "#/$defs/FuncDefn", "Input": "#/$defs/Input", - "Lift": "#/$defs/Lift", "LoadConstant": "#/$defs/LoadConstant", "LoadFunction": "#/$defs/LoadFunction", - "MakeTuple": "#/$defs/MakeTuple", "Module": "#/$defs/Module", - "Noop": "#/$defs/Noop", "Output": "#/$defs/Output", "Tag": "#/$defs/Tag", - "TailLoop": "#/$defs/TailLoop", - "UnpackTuple": "#/$defs/UnpackTuple" + "TailLoop": "#/$defs/TailLoop" }, "propertyName": "op" }, @@ -1330,21 +1230,9 @@ { "$ref": "#/$defs/ExtensionOp" }, - { - "$ref": "#/$defs/Noop" - }, - { - "$ref": "#/$defs/MakeTuple" - }, - { - "$ref": "#/$defs/UnpackTuple" - }, { "$ref": "#/$defs/Tag" }, - { - "$ref": "#/$defs/Lift" - }, { "$ref": "#/$defs/DFG" }, @@ -2260,37 +2148,6 @@ "title": "UnitSum", "type": "object" }, - "UnpackTuple": { - "additionalProperties": true, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "UnpackTuple", - "default": "UnpackTuple", - "enum": [ - "UnpackTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "UnpackTuple", - "type": "object" - }, "Value": { "description": "A constant Value.", "discriminator": { diff --git a/specification/schema/testing_hugr_schema_strict_live.json b/specification/schema/testing_hugr_schema_strict_live.json index 9fa982403..85297e69d 100644 --- a/specification/schema/testing_hugr_schema_strict_live.json +++ b/specification/schema/testing_hugr_schema_strict_live.json @@ -977,43 +977,6 @@ "title": "Input", "type": "object" }, - "Lift": { - "additionalProperties": false, - "description": "Fixes some TypeParams of a polymorphic type by providing TypeArgs.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Lift", - "default": "Lift", - "enum": [ - "Lift" - ], - "title": "Op", - "type": "string" - }, - "type_row": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Type Row", - "type": "array" - }, - "new_extension": { - "title": "New Extension", - "type": "string" - } - }, - "required": [ - "parent", - "type_row", - "new_extension" - ], - "title": "Lift", - "type": "object" - }, "ListParam": { "additionalProperties": false, "properties": { @@ -1104,37 +1067,6 @@ "title": "LoadFunction", "type": "object" }, - "MakeTuple": { - "additionalProperties": false, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "MakeTuple", - "default": "MakeTuple", - "enum": [ - "MakeTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "MakeTuple", - "type": "object" - }, "Module": { "additionalProperties": false, "description": "The root of a module, parent of all other `ModuleOp`s.", @@ -1159,34 +1091,6 @@ "title": "Module", "type": "object" }, - "Noop": { - "additionalProperties": false, - "description": "A no-op operation.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "Noop", - "default": "Noop", - "enum": [ - "Noop" - ], - "title": "Op", - "type": "string" - }, - "ty": { - "$ref": "#/$defs/Type" - } - }, - "required": [ - "parent", - "ty" - ], - "title": "Noop", - "type": "object" - }, "OpDef": { "description": "Serializable definition for dynamically loaded operations.", "properties": { @@ -1265,16 +1169,12 @@ "FuncDecl": "#/$defs/FuncDecl", "FuncDefn": "#/$defs/FuncDefn", "Input": "#/$defs/Input", - "Lift": "#/$defs/Lift", "LoadConstant": "#/$defs/LoadConstant", "LoadFunction": "#/$defs/LoadFunction", - "MakeTuple": "#/$defs/MakeTuple", "Module": "#/$defs/Module", - "Noop": "#/$defs/Noop", "Output": "#/$defs/Output", "Tag": "#/$defs/Tag", - "TailLoop": "#/$defs/TailLoop", - "UnpackTuple": "#/$defs/UnpackTuple" + "TailLoop": "#/$defs/TailLoop" }, "propertyName": "op" }, @@ -1330,21 +1230,9 @@ { "$ref": "#/$defs/ExtensionOp" }, - { - "$ref": "#/$defs/Noop" - }, - { - "$ref": "#/$defs/MakeTuple" - }, - { - "$ref": "#/$defs/UnpackTuple" - }, { "$ref": "#/$defs/Tag" }, - { - "$ref": "#/$defs/Lift" - }, { "$ref": "#/$defs/DFG" }, @@ -2260,37 +2148,6 @@ "title": "UnitSum", "type": "object" }, - "UnpackTuple": { - "additionalProperties": false, - "description": "An operation that packs all its inputs into a tuple.", - "properties": { - "parent": { - "title": "Parent", - "type": "integer" - }, - "op": { - "const": "UnpackTuple", - "default": "UnpackTuple", - "enum": [ - "UnpackTuple" - ], - "title": "Op", - "type": "string" - }, - "tys": { - "items": { - "$ref": "#/$defs/Type" - }, - "title": "Tys", - "type": "array" - } - }, - "required": [ - "parent" - ], - "title": "UnpackTuple", - "type": "object" - }, "Value": { "description": "A constant Value.", "discriminator": { diff --git a/specification/std_extensions/prelude.json b/specification/std_extensions/prelude.json index 315c7c105..3ec1d8699 100644 --- a/specification/std_extensions/prelude.json +++ b/specification/std_extensions/prelude.json @@ -67,6 +67,160 @@ }, "values": {}, "operations": { + "Lift": { + "extension": "prelude", + "name": "Lift", + "description": "Add extension requirements to a row of values", + "signature": { + "params": [ + { + "tp": "Extensions" + }, + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "output": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "extension_reqs": [ + "0" + ] + } + }, + "binary": false + }, + "MakeTuple": { + "extension": "prelude", + "name": "MakeTuple", + "description": "MakeTuple operation", + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "Sum", + "s": "General", + "rows": [ + [ + { + "t": "R", + "i": 0, + "b": "A" + } + ] + ] + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "Noop": { + "extension": "prelude", + "name": "Noop", + "description": "Noop gate", + "signature": { + "params": [ + { + "tp": "Type", + "b": "A" + } + ], + "body": { + "input": [ + { + "t": "V", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "V", + "i": 0, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, + "UnpackTuple": { + "extension": "prelude", + "name": "UnpackTuple", + "description": "UnpackTuple operation", + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "Sum", + "s": "General", + "rows": [ + [ + { + "t": "R", + "i": 0, + "b": "A" + } + ] + ] + } + ], + "output": [ + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false + }, "new_array": { "extension": "prelude", "name": "new_array", @@ -78,8 +232,49 @@ "extension": "prelude", "name": "panic", "description": "Panic with input error", - "signature": null, - "binary": true + "signature": { + "params": [ + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + }, + { + "tp": "List", + "param": { + "tp": "Type", + "b": "A" + } + } + ], + "body": { + "input": [ + { + "t": "Opaque", + "extension": "prelude", + "id": "error", + "args": [], + "bound": "C" + }, + { + "t": "R", + "i": 0, + "b": "A" + } + ], + "output": [ + { + "t": "R", + "i": 1, + "b": "A" + } + ], + "extension_reqs": [] + } + }, + "binary": false }, "print": { "extension": "prelude",