hugr_core/extension/
prelude.rs

1//! Prelude extension - available in all contexts, defining common types,
2//! operations and constants.
3use std::str::FromStr;
4use std::sync::{Arc, LazyLock, Weak};
5
6use itertools::Itertools;
7
8use crate::extension::const_fold::fold_out_row;
9use crate::extension::simple_op::{
10    MakeExtensionOp, MakeOpDef, MakeRegisteredOp, OpLoadError, try_from_name,
11};
12use crate::extension::{
13    ConstFold, ExtensionId, OpDef, SignatureError, SignatureFunc, TypeDefBound,
14};
15use crate::ops::OpName;
16use crate::ops::constant::{CustomCheckFailure, CustomConst, ValueName};
17use crate::ops::{NamedOp, Value};
18use crate::types::type_param::{TypeArg, TypeParam};
19use crate::types::{
20    CustomType, FuncValueType, PolyFuncType, PolyFuncTypeRV, Signature, SumType, Term, Type,
21    TypeBound, TypeName, TypeRV, TypeRow, TypeRowRV,
22};
23use crate::utils::sorted_consts;
24use crate::{Extension, type_row};
25
26use strum::{EnumIter, EnumString, IntoStaticStr};
27
28use super::ExtensionRegistry;
29use super::resolution::{ExtensionResolutionError, WeakExtensionRegistry, resolve_type_extensions};
30
31mod unwrap_builder;
32
33pub use unwrap_builder::UnwrapBuilder;
34
35/// Operation to load generic bounded nat parameter.
36pub mod generic;
37
38/// Name of prelude extension.
39pub const PRELUDE_ID: ExtensionId = ExtensionId::new_unchecked("prelude");
40/// Extension version.
41pub const VERSION: semver::Version = semver::Version::new(0, 2, 1);
42/// Prelude extension, containing common types and operations.
43pub static PRELUDE: LazyLock<Arc<Extension>> = LazyLock::new(|| {
44    Extension::new_arc(PRELUDE_ID, VERSION, |prelude, extension_ref| {
45        // Construct the list and error types using the passed extension
46        // reference.
47        //
48        // If we tried to use `string_type()` or `error_type()` directly it
49        // would try to access the `PRELUDE` lazy static recursively,
50        // causing a deadlock.
51        let string_type: Type = string_custom_type(extension_ref).into();
52        let usize_type: Type = usize_custom_t(extension_ref).into();
53        let error_type: CustomType = error_custom_type(extension_ref);
54
55        prelude
56            .add_type(
57                TypeName::new_inline("usize"),
58                vec![],
59                "usize".into(),
60                TypeDefBound::copyable(),
61                extension_ref,
62            )
63            .unwrap();
64        prelude
65            .add_type(
66                STRING_TYPE_NAME,
67                vec![],
68                "string".into(),
69                TypeDefBound::copyable(),
70                extension_ref,
71            )
72            .unwrap();
73        prelude
74            .add_op(
75                PRINT_OP_ID,
76                "Print the string to standard output".to_string(),
77                Signature::new(vec![string_type.clone()], type_row![]),
78                extension_ref,
79            )
80            .unwrap();
81        prelude
82            .add_type(
83                TypeName::new_inline("qubit"),
84                vec![],
85                "qubit".into(),
86                TypeDefBound::any(),
87                extension_ref,
88            )
89            .unwrap();
90        prelude
91            .add_type(
92                ERROR_TYPE_NAME,
93                vec![],
94                "Simple opaque error type.".into(),
95                TypeDefBound::copyable(),
96                extension_ref,
97            )
98            .unwrap();
99        prelude
100            .add_op(
101                MAKE_ERROR_OP_ID,
102                "Create an error value".to_string(),
103                Signature::new(
104                    vec![usize_type, string_type],
105                    vec![error_type.clone().into()],
106                ),
107                extension_ref,
108            )
109            .unwrap();
110        prelude
111            .add_op(
112                PANIC_OP_ID,
113                "Panic with input error".to_string(),
114                PolyFuncTypeRV::new(
115                    [
116                        TypeParam::new_list_type(TypeBound::Linear),
117                        TypeParam::new_list_type(TypeBound::Linear),
118                    ],
119                    FuncValueType::new(
120                        vec![
121                            TypeRV::new_extension(error_type.clone()),
122                            TypeRV::new_row_var_use(0, TypeBound::Linear),
123                        ],
124                        vec![TypeRV::new_row_var_use(1, TypeBound::Linear)],
125                    ),
126                ),
127                extension_ref,
128            )
129            .unwrap();
130        prelude
131            .add_op(
132                EXIT_OP_ID,
133                "Exit with input error".to_string(),
134                PolyFuncTypeRV::new(
135                    [
136                        TypeParam::new_list_type(TypeBound::Linear),
137                        TypeParam::new_list_type(TypeBound::Linear),
138                    ],
139                    FuncValueType::new(
140                        vec![
141                            TypeRV::new_extension(error_type),
142                            TypeRV::new_row_var_use(0, TypeBound::Linear),
143                        ],
144                        vec![TypeRV::new_row_var_use(1, TypeBound::Linear)],
145                    ),
146                ),
147                extension_ref,
148            )
149            .unwrap();
150
151        TupleOpDef::load_all_ops(prelude, extension_ref).unwrap();
152        NoopDef.add_to_extension(prelude, extension_ref).unwrap();
153        BarrierDef.add_to_extension(prelude, extension_ref).unwrap();
154        generic::LoadNatDef
155            .add_to_extension(prelude, extension_ref)
156            .unwrap();
157    })
158});
159
160/// An extension registry containing only the prelude
161pub static PRELUDE_REGISTRY: LazyLock<ExtensionRegistry> =
162    LazyLock::new(|| ExtensionRegistry::new([PRELUDE.clone()]));
163
164pub(crate) fn usize_custom_t(extension_ref: &Weak<Extension>) -> CustomType {
165    CustomType::new(
166        TypeName::new_inline("usize"),
167        vec![],
168        PRELUDE_ID,
169        TypeBound::Copyable,
170        extension_ref,
171    )
172}
173
174pub(crate) fn qb_custom_t(extension_ref: &Weak<Extension>) -> CustomType {
175    CustomType::new(
176        TypeName::new_inline("qubit"),
177        vec![],
178        PRELUDE_ID,
179        TypeBound::Linear,
180        extension_ref,
181    )
182}
183
184/// Qubit type.
185#[must_use]
186pub fn qb_t() -> Type {
187    qb_custom_t(&Arc::downgrade(&PRELUDE)).into()
188}
189/// Unsigned size type.
190#[must_use]
191pub fn usize_t() -> Type {
192    usize_custom_t(&Arc::downgrade(&PRELUDE)).into()
193}
194/// Boolean type - Sum of two units.
195#[must_use]
196pub fn bool_t() -> Type {
197    Type::new_unit_sum(2)
198}
199
200/// Name of the prelude `MakeError` operation.
201///
202/// This operation can be used to dynamically create error values.
203pub const MAKE_ERROR_OP_ID: OpName = OpName::new_inline("MakeError");
204
205/// Name of the prelude panic operation.
206///
207/// This operation can have any input and any output wires; it is instantiated
208/// with two [`TypeArg::List`]s representing these. The first input to the
209/// operation is always an error type; the remaining inputs correspond to the
210/// first sequence of types in its instantiation; the outputs correspond to the
211/// second sequence of types in its instantiation. Note that the inputs and
212/// outputs only exist so that structural constraints such as linearity can be
213/// satisfied.
214///
215/// Panic immediately halts a multi-shot program. It is intended to be used in
216/// cases where an unrecoverable error is detected.
217pub const PANIC_OP_ID: OpName = OpName::new_inline("panic");
218
219/// Name of the prelude exit operation.
220///
221/// This operation can have any input and any output wires; it is instantiated
222/// with two [`TypeArg::List`]s representing these. The first input to the
223/// operation is always an error type; the remaining inputs correspond to the
224/// first sequence of types in its instantiation; the outputs correspond to the
225/// second sequence of types in its instantiation. Note that the inputs and
226/// outputs only exist so that structural constraints such as linearity can be
227/// satisfied.
228///
229/// Exit immediately halts a single shot's execution. It is intended to be used
230/// when the user wants to exit the program early.
231pub const EXIT_OP_ID: OpName = OpName::new_inline("exit");
232
233/// Name of the string type.
234pub const STRING_TYPE_NAME: TypeName = TypeName::new_inline("string");
235
236/// Custom type for strings.
237///
238/// Receives a reference to the prelude extensions as a parameter.
239/// This avoids deadlocks when we are in the process of creating the prelude.
240fn string_custom_type(extension_ref: &Weak<Extension>) -> CustomType {
241    CustomType::new(
242        STRING_TYPE_NAME,
243        vec![],
244        PRELUDE_ID,
245        TypeBound::Copyable,
246        extension_ref,
247    )
248}
249
250/// String type.
251#[must_use]
252pub fn string_type() -> Type {
253    string_custom_type(&Arc::downgrade(&PRELUDE)).into()
254}
255
256#[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
257/// Structure for holding constant string values.
258pub struct ConstString(String);
259
260impl ConstString {
261    /// Creates a new [`ConstString`].
262    #[must_use]
263    pub fn new(value: String) -> Self {
264        Self(value)
265    }
266
267    /// Returns the value of the constant.
268    #[must_use]
269    pub fn value(&self) -> &str {
270        &self.0
271    }
272}
273
274#[typetag::serde]
275impl CustomConst for ConstString {
276    fn name(&self) -> ValueName {
277        format!("ConstString({:?})", self.0).into()
278    }
279
280    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
281        crate::ops::constant::downcast_equal_consts(self, other)
282    }
283
284    fn get_type(&self) -> Type {
285        string_type()
286    }
287}
288
289/// Name of the print operation
290pub const PRINT_OP_ID: OpName = OpName::new_inline("print");
291
292/// The custom type for Errors.
293///
294/// Receives a reference to the prelude extensions as a parameter.
295/// This avoids deadlocks when we are in the process of creating the prelude.
296fn error_custom_type(extension_ref: &Weak<Extension>) -> CustomType {
297    CustomType::new(
298        ERROR_TYPE_NAME,
299        vec![],
300        PRELUDE_ID,
301        TypeBound::Copyable,
302        extension_ref,
303    )
304}
305
306/// Unspecified opaque error type.
307#[must_use]
308pub fn error_type() -> Type {
309    error_custom_type(&Arc::downgrade(&PRELUDE)).into()
310}
311
312/// The string name of the error type.
313pub const ERROR_TYPE_NAME: TypeName = TypeName::new_inline("error");
314
315/// Return a Sum type with the second variant as the given type and the first an Error.
316pub fn sum_with_error(ty: impl Into<TypeRowRV>) -> SumType {
317    either_type(error_type(), ty)
318}
319
320/// An optional type, i.e. a Sum type with the second variant as the given type and the first as an empty tuple.
321#[inline]
322pub fn option_type(ty: impl Into<TypeRowRV>) -> SumType {
323    either_type(TypeRow::new(), ty)
324}
325
326/// An "either" type, i.e. a Sum type with a "left" and a "right" variant.
327///
328/// When used as a fallible value, the "right" variant represents a successful computation,
329/// and the "left" variant represents a failure.
330#[inline]
331pub fn either_type(ty_left: impl Into<TypeRowRV>, ty_right: impl Into<TypeRowRV>) -> SumType {
332    SumType::new([ty_left.into(), ty_right.into()])
333}
334
335/// A constant optional value with a given value.
336///
337/// See [`option_type`].
338#[must_use]
339pub fn const_some(value: Value) -> Value {
340    const_some_tuple([value])
341}
342
343/// A constant optional value with a row of values.
344///
345/// For single values, use [`const_some`].
346///
347/// See [`option_type`].
348pub fn const_some_tuple(values: impl IntoIterator<Item = Value>) -> Value {
349    const_right_tuple(TypeRow::new(), values)
350}
351
352/// A constant optional value with no value.
353///
354/// See [`option_type`].
355pub fn const_none(ty: impl Into<TypeRowRV>) -> Value {
356    const_left_tuple([], ty)
357}
358
359/// A constant Either value with a left variant.
360///
361/// In fallible computations, this represents a failure.
362///
363/// See [`either_type`].
364pub fn const_left(value: Value, ty_right: impl Into<TypeRowRV>) -> Value {
365    const_left_tuple([value], ty_right)
366}
367
368/// A constant Either value with a row of left values.
369///
370/// In fallible computations, this represents a failure.
371///
372/// See [`either_type`].
373pub fn const_left_tuple(
374    values: impl IntoIterator<Item = Value>,
375    ty_right: impl Into<TypeRowRV>,
376) -> Value {
377    let values = values.into_iter().collect_vec();
378    let types: TypeRowRV = values
379        .iter()
380        .map(|v| TypeRV::from(v.get_type()))
381        .collect_vec()
382        .into();
383    let typ = either_type(types, ty_right);
384    Value::sum(0, values, typ).unwrap()
385}
386
387/// A constant Either value with a right variant.
388///
389/// In fallible computations, this represents a successful result.
390///
391/// See [`either_type`].
392pub fn const_right(ty_left: impl Into<TypeRowRV>, value: Value) -> Value {
393    const_right_tuple(ty_left, [value])
394}
395
396/// A constant Either value with a row of right values.
397///
398/// In fallible computations, this represents a successful result.
399///
400/// See [`either_type`].
401pub fn const_right_tuple(
402    ty_left: impl Into<TypeRowRV>,
403    values: impl IntoIterator<Item = Value>,
404) -> Value {
405    let values = values.into_iter().collect_vec();
406    let types: TypeRowRV = values
407        .iter()
408        .map(|v| TypeRV::from(v.get_type()))
409        .collect_vec()
410        .into();
411    let typ = either_type(ty_left, types);
412    Value::sum(1, values, typ).unwrap()
413}
414
415/// A constant Either value with a success variant.
416///
417/// Alias for [`const_right`].
418pub fn const_ok(value: Value, ty_fail: impl Into<TypeRowRV>) -> Value {
419    const_right(ty_fail, value)
420}
421
422/// A constant Either with a row of success values.
423///
424/// Alias for [`const_right_tuple`].
425pub fn const_ok_tuple(
426    values: impl IntoIterator<Item = Value>,
427    ty_fail: impl Into<TypeRowRV>,
428) -> Value {
429    const_right_tuple(ty_fail, values)
430}
431
432/// A constant Either value with a failure variant.
433///
434/// Alias for [`const_left`].
435pub fn const_fail(value: Value, ty_ok: impl Into<TypeRowRV>) -> Value {
436    const_left(value, ty_ok)
437}
438
439/// A constant Either with a row of failure values.
440///
441/// Alias for [`const_left_tuple`].
442pub fn const_fail_tuple(
443    values: impl IntoIterator<Item = Value>,
444    ty_ok: impl Into<TypeRowRV>,
445) -> Value {
446    const_left_tuple(values, ty_ok)
447}
448
449#[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
450/// Structure for holding constant usize values.
451pub struct ConstUsize(u64);
452
453impl ConstUsize {
454    /// Creates a new [`ConstUsize`].
455    #[must_use]
456    pub fn new(value: u64) -> Self {
457        Self(value)
458    }
459
460    /// Returns the value of the constant.
461    #[must_use]
462    pub fn value(&self) -> u64 {
463        self.0
464    }
465}
466
467#[typetag::serde]
468impl CustomConst for ConstUsize {
469    fn name(&self) -> ValueName {
470        format!("ConstUsize({})", self.0).into()
471    }
472
473    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
474        crate::ops::constant::downcast_equal_consts(self, other)
475    }
476
477    fn get_type(&self) -> Type {
478        usize_t()
479    }
480}
481
482#[derive(Debug, Clone, PartialEq, Hash, serde::Serialize, serde::Deserialize)]
483/// Structure for holding constant [error types].
484///
485/// [error types]: crate::extension::prelude::error_type
486pub struct ConstError {
487    /// Integer tag/signal for the error.
488    pub signal: u32,
489    /// Error message.
490    pub message: String,
491}
492
493/// Default error signal.
494pub const DEFAULT_ERROR_SIGNAL: u32 = 1;
495
496impl ConstError {
497    /// Define a new error value.
498    pub fn new(signal: u32, message: impl ToString) -> Self {
499        Self {
500            signal,
501            message: message.to_string(),
502        }
503    }
504
505    /// Define a new error value with the [default signal].
506    ///
507    /// [default signal]: DEFAULT_ERROR_SIGNAL
508    pub fn new_default_signal(message: impl ToString) -> Self {
509        Self::new(DEFAULT_ERROR_SIGNAL, message)
510    }
511    /// Returns an "either" value with a failure variant.
512    ///
513    /// args:
514    ///     `ty_ok`: The type of the success variant.
515    pub fn as_either(self, ty_ok: impl Into<TypeRowRV>) -> Value {
516        const_fail(self.into(), ty_ok)
517    }
518}
519
520#[typetag::serde]
521impl CustomConst for ConstError {
522    fn name(&self) -> ValueName {
523        format!("ConstError({}, {:?})", self.signal, self.message).into()
524    }
525
526    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
527        crate::ops::constant::downcast_equal_consts(self, other)
528    }
529
530    fn get_type(&self) -> Type {
531        error_type()
532    }
533}
534
535impl FromStr for ConstError {
536    type Err = ();
537
538    fn from_str(s: &str) -> Result<Self, Self::Err> {
539        Ok(Self::new_default_signal(s))
540    }
541}
542
543impl From<String> for ConstError {
544    fn from(s: String) -> Self {
545        Self::new_default_signal(s)
546    }
547}
548
549#[derive(Debug, Clone, PartialEq, Eq, Hash, serde::Serialize, serde::Deserialize)]
550/// A structure for holding references to external symbols.
551pub struct ConstExternalSymbol {
552    /// The symbol name that this value refers to. Must be nonempty.
553    pub symbol: String,
554    /// The type of the value found at this symbol reference.
555    pub typ: Type,
556    /// Whether the value at the symbol reference is constant or mutable.
557    pub constant: bool,
558}
559
560impl ConstExternalSymbol {
561    /// Construct a new [`ConstExternalSymbol`].
562    pub fn new(symbol: impl Into<String>, typ: impl Into<Type>, constant: bool) -> Self {
563        Self {
564            symbol: symbol.into(),
565            typ: typ.into(),
566            constant,
567        }
568    }
569}
570
571impl PartialEq<dyn CustomConst> for ConstExternalSymbol {
572    fn eq(&self, other: &dyn CustomConst) -> bool {
573        self.equal_consts(other)
574    }
575}
576
577#[typetag::serde]
578impl CustomConst for ConstExternalSymbol {
579    fn name(&self) -> ValueName {
580        format!("@{}", &self.symbol).into()
581    }
582
583    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
584        crate::ops::constant::downcast_equal_consts(self, other)
585    }
586
587    fn get_type(&self) -> Type {
588        self.typ.clone()
589    }
590
591    fn update_extensions(
592        &mut self,
593        extensions: &WeakExtensionRegistry,
594    ) -> Result<(), ExtensionResolutionError> {
595        resolve_type_extensions(&mut self.typ, extensions)
596    }
597
598    fn validate(&self) -> Result<(), CustomCheckFailure> {
599        if self.symbol.is_empty() {
600            Err(CustomCheckFailure::Message(
601                "External symbol name is empty.".into(),
602            ))
603        } else {
604            Ok(())
605        }
606    }
607}
608
609/// Logic extension operation definitions.
610#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, EnumIter, IntoStaticStr, EnumString)]
611#[allow(missing_docs)]
612#[non_exhaustive]
613pub enum TupleOpDef {
614    MakeTuple,
615    UnpackTuple,
616}
617
618impl ConstFold for TupleOpDef {
619    fn fold(
620        &self,
621        _type_args: &[TypeArg],
622        consts: &[(crate::IncomingPort, Value)],
623    ) -> crate::extension::ConstFoldResult {
624        match self {
625            TupleOpDef::MakeTuple => {
626                fold_out_row([Value::tuple(sorted_consts(consts).into_iter().cloned())])
627            }
628            TupleOpDef::UnpackTuple => {
629                let c = &consts.first()?.1;
630                let Some(vs) = c.as_tuple() else {
631                    panic!("This op always takes a Tuple input.");
632                };
633                fold_out_row(vs.iter().cloned())
634            }
635        }
636    }
637}
638
639impl MakeOpDef for TupleOpDef {
640    fn opdef_id(&self) -> OpName {
641        <&'static str>::from(self).into()
642    }
643
644    fn init_signature(&self, _extension_ref: &Weak<Extension>) -> SignatureFunc {
645        let rv = TypeRV::new_row_var_use(0, TypeBound::Linear);
646        let tuple_type = TypeRV::new_tuple(vec![rv.clone()]);
647
648        let param = TypeParam::new_list_type(TypeBound::Linear);
649        match self {
650            TupleOpDef::MakeTuple => {
651                PolyFuncTypeRV::new([param], FuncValueType::new(rv, tuple_type))
652            }
653            TupleOpDef::UnpackTuple => {
654                PolyFuncTypeRV::new([param], FuncValueType::new(tuple_type, rv))
655            }
656        }
657        .into()
658    }
659
660    fn description(&self) -> String {
661        match self {
662            TupleOpDef::MakeTuple => "MakeTuple operation",
663            TupleOpDef::UnpackTuple => "UnpackTuple operation",
664        }
665        .to_string()
666    }
667
668    fn from_def(op_def: &OpDef) -> Result<Self, OpLoadError> {
669        try_from_name(op_def.name(), op_def.extension_id())
670    }
671
672    fn extension(&self) -> ExtensionId {
673        PRELUDE_ID.clone()
674    }
675
676    fn extension_ref(&self) -> Weak<Extension> {
677        Arc::downgrade(&PRELUDE)
678    }
679
680    fn post_opdef(&self, def: &mut OpDef) {
681        def.set_constant_folder(*self);
682    }
683}
684/// An operation that packs all its inputs into a tuple.
685#[derive(Debug, Clone, Default, PartialEq, Eq)]
686#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
687#[non_exhaustive]
688pub struct MakeTuple(pub TypeRow);
689
690impl MakeTuple {
691    /// Create a new `MakeTuple` operation.
692    #[must_use]
693    pub fn new(tys: TypeRow) -> Self {
694        Self(tys)
695    }
696}
697
698impl MakeExtensionOp for MakeTuple {
699    fn op_id(&self) -> OpName {
700        TupleOpDef::MakeTuple.opdef_id()
701    }
702
703    fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result<Self, OpLoadError>
704    where
705        Self: Sized,
706    {
707        let def = TupleOpDef::from_def(ext_op.def())?;
708        if def != TupleOpDef::MakeTuple {
709            return Err(OpLoadError::NotMember(ext_op.unqualified_id().to_string()))?;
710        }
711        let [TypeArg::List(elems)] = ext_op.args() else {
712            return Err(SignatureError::InvalidTypeArgs)?;
713        };
714        let tys: Result<Vec<Type>, _> = elems
715            .iter()
716            .map(|a| match a {
717                TypeArg::Runtime(ty) => Ok(ty.clone()),
718                _ => Err(SignatureError::InvalidTypeArgs),
719            })
720            .collect();
721        Ok(Self(tys?.into()))
722    }
723
724    fn type_args(&self) -> Vec<TypeArg> {
725        vec![Term::new_list(self.0.iter().map(|t| t.clone().into()))]
726    }
727}
728
729impl MakeRegisteredOp for MakeTuple {
730    fn extension_id(&self) -> ExtensionId {
731        PRELUDE_ID.clone()
732    }
733
734    fn extension_ref(&self) -> Weak<Extension> {
735        Arc::downgrade(&PRELUDE)
736    }
737}
738
739/// An operation that unpacks a tuple into its components.
740#[derive(Debug, Clone, Default, PartialEq, Eq)]
741#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
742#[non_exhaustive]
743pub struct UnpackTuple(pub TypeRow);
744
745impl UnpackTuple {
746    /// Create a new `UnpackTuple` operation.
747    #[must_use]
748    pub fn new(tys: TypeRow) -> Self {
749        Self(tys)
750    }
751}
752
753impl MakeExtensionOp for UnpackTuple {
754    fn op_id(&self) -> OpName {
755        TupleOpDef::UnpackTuple.opdef_id()
756    }
757
758    fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result<Self, OpLoadError>
759    where
760        Self: Sized,
761    {
762        let def = TupleOpDef::from_def(ext_op.def())?;
763        if def != TupleOpDef::UnpackTuple {
764            return Err(OpLoadError::NotMember(ext_op.unqualified_id().to_string()))?;
765        }
766        let [Term::List(elems)] = ext_op.args() else {
767            return Err(SignatureError::InvalidTypeArgs)?;
768        };
769        let tys: Result<Vec<Type>, _> = elems
770            .iter()
771            .map(|a| match a {
772                Term::Runtime(ty) => Ok(ty.clone()),
773                _ => Err(SignatureError::InvalidTypeArgs),
774            })
775            .collect();
776        Ok(Self(tys?.into()))
777    }
778
779    fn type_args(&self) -> Vec<Term> {
780        vec![Term::new_list(self.0.iter().map(|t| t.clone().into()))]
781    }
782}
783
784impl MakeRegisteredOp for UnpackTuple {
785    fn extension_id(&self) -> ExtensionId {
786        PRELUDE_ID.clone()
787    }
788
789    fn extension_ref(&self) -> Weak<Extension> {
790        Arc::downgrade(&PRELUDE)
791    }
792}
793
794/// Name of the no-op operation.
795pub const NOOP_OP_ID: OpName = OpName::new_inline("Noop");
796
797#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
798/// A no-op operation definition.
799pub struct NoopDef;
800
801impl std::str::FromStr for NoopDef {
802    type Err = ();
803
804    fn from_str(s: &str) -> Result<Self, Self::Err> {
805        if s == NoopDef.op_id() {
806            Ok(Self)
807        } else {
808            Err(())
809        }
810    }
811}
812
813impl MakeOpDef for NoopDef {
814    fn opdef_id(&self) -> OpName {
815        NOOP_OP_ID
816    }
817
818    fn init_signature(&self, _extension_ref: &Weak<Extension>) -> SignatureFunc {
819        let tv = Type::new_var_use(0, TypeBound::Linear);
820        PolyFuncType::new([TypeBound::Linear.into()], Signature::new_endo(tv)).into()
821    }
822
823    fn description(&self) -> String {
824        "Noop gate".to_string()
825    }
826
827    fn from_def(op_def: &OpDef) -> Result<Self, OpLoadError> {
828        try_from_name(op_def.name(), op_def.extension_id())
829    }
830
831    fn extension(&self) -> ExtensionId {
832        PRELUDE_ID.clone()
833    }
834
835    fn extension_ref(&self) -> Weak<Extension> {
836        Arc::downgrade(&PRELUDE)
837    }
838
839    fn post_opdef(&self, def: &mut OpDef) {
840        def.set_constant_folder(*self);
841    }
842}
843
844impl ConstFold for NoopDef {
845    fn fold(
846        &self,
847        _type_args: &[TypeArg],
848        consts: &[(crate::IncomingPort, Value)],
849    ) -> crate::extension::ConstFoldResult {
850        fold_out_row([consts.first()?.1.clone()])
851    }
852}
853
854/// A no-op operation.
855#[derive(Debug, Clone, PartialEq, Eq)]
856#[non_exhaustive]
857#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
858pub struct Noop(pub Type);
859
860impl Noop {
861    /// Create a new Noop operation.
862    #[must_use]
863    pub fn new(ty: Type) -> Self {
864        Self(ty)
865    }
866}
867
868impl Default for Noop {
869    fn default() -> Self {
870        Self(Type::UNIT)
871    }
872}
873
874impl MakeExtensionOp for Noop {
875    fn op_id(&self) -> OpName {
876        NoopDef.op_id()
877    }
878
879    fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result<Self, OpLoadError>
880    where
881        Self: Sized,
882    {
883        let _def = NoopDef::from_def(ext_op.def())?;
884        let [TypeArg::Runtime(ty)] = ext_op.args() else {
885            return Err(SignatureError::InvalidTypeArgs)?;
886        };
887        Ok(Self(ty.clone()))
888    }
889
890    fn type_args(&self) -> Vec<TypeArg> {
891        vec![self.0.clone().into()]
892    }
893}
894
895impl MakeRegisteredOp for Noop {
896    fn extension_id(&self) -> ExtensionId {
897        PRELUDE_ID.clone()
898    }
899
900    fn extension_ref(&self) -> Weak<Extension> {
901        Arc::downgrade(&PRELUDE)
902    }
903}
904
905#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
906/// A barrier operation definition.
907pub struct BarrierDef;
908
909/// Name of the barrier operation.
910pub const BARRIER_OP_ID: OpName = OpName::new_inline("Barrier");
911
912impl std::str::FromStr for BarrierDef {
913    type Err = ();
914
915    fn from_str(s: &str) -> Result<Self, Self::Err> {
916        if s == BarrierDef.op_id() {
917            Ok(Self)
918        } else {
919            Err(())
920        }
921    }
922}
923
924impl MakeOpDef for BarrierDef {
925    fn opdef_id(&self) -> OpName {
926        BARRIER_OP_ID
927    }
928
929    fn init_signature(&self, _extension_ref: &Weak<Extension>) -> SignatureFunc {
930        PolyFuncTypeRV::new(
931            vec![TypeParam::new_list_type(TypeBound::Linear)],
932            FuncValueType::new_endo(TypeRV::new_row_var_use(0, TypeBound::Linear)),
933        )
934        .into()
935    }
936
937    fn description(&self) -> String {
938        "Add a barrier to a row of values".to_string()
939    }
940
941    fn from_def(op_def: &OpDef) -> Result<Self, OpLoadError> {
942        try_from_name(op_def.name(), op_def.extension_id())
943    }
944
945    fn extension(&self) -> ExtensionId {
946        PRELUDE_ID.clone()
947    }
948
949    fn extension_ref(&self) -> Weak<Extension> {
950        Arc::downgrade(&PRELUDE)
951    }
952}
953
954/// A barrier across a row of values. This operation has no effect on the values,
955/// except to enforce some ordering between operations before and after the barrier.
956#[derive(Debug, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
957#[cfg_attr(test, derive(proptest_derive::Arbitrary))]
958#[non_exhaustive]
959pub struct Barrier {
960    /// The types of the edges
961    pub type_row: TypeRow,
962}
963
964impl Barrier {
965    /// Create a new Barrier operation over the specified row.
966    pub fn new(type_row: impl Into<TypeRow>) -> Self {
967        Self {
968            type_row: type_row.into(),
969        }
970    }
971}
972
973impl NamedOp for Barrier {
974    fn name(&self) -> OpName {
975        BarrierDef.op_id()
976    }
977}
978
979impl MakeExtensionOp for Barrier {
980    fn op_id(&self) -> OpName {
981        BarrierDef.op_id()
982    }
983
984    fn from_extension_op(ext_op: &crate::ops::ExtensionOp) -> Result<Self, OpLoadError>
985    where
986        Self: Sized,
987    {
988        let _def = BarrierDef::from_def(ext_op.def())?;
989
990        let [TypeArg::List(elems)] = ext_op.args() else {
991            return Err(SignatureError::InvalidTypeArgs)?;
992        };
993        let tys: Result<Vec<Type>, _> = elems
994            .iter()
995            .map(|a| match a {
996                TypeArg::Runtime(ty) => Ok(ty.clone()),
997                _ => Err(SignatureError::InvalidTypeArgs),
998            })
999            .collect();
1000        Ok(Self {
1001            type_row: tys?.into(),
1002        })
1003    }
1004
1005    fn type_args(&self) -> Vec<TypeArg> {
1006        vec![TypeArg::new_list(
1007            self.type_row.iter().map(|t| t.clone().into()),
1008        )]
1009    }
1010}
1011
1012impl MakeRegisteredOp for Barrier {
1013    fn extension_id(&self) -> ExtensionId {
1014        PRELUDE_ID.clone()
1015    }
1016
1017    fn extension_ref(&self) -> Weak<Extension> {
1018        Arc::downgrade(&PRELUDE)
1019    }
1020}
1021
1022#[cfg(test)]
1023mod test {
1024    use crate::builder::inout_sig;
1025    use crate::std_extensions::arithmetic::float_types::{ConstF64, float64_type};
1026    use crate::types::Term;
1027    use crate::{
1028        Hugr, Wire,
1029        builder::{DFGBuilder, Dataflow, DataflowHugr, endo_sig},
1030        utils::test_quantum_extension::cx_gate,
1031    };
1032
1033    use super::*;
1034    use crate::{
1035        ops::{OpTrait, OpType},
1036        type_row,
1037    };
1038
1039    use crate::hugr::views::HugrView;
1040
1041    #[test]
1042    fn test_make_tuple() {
1043        let op = MakeTuple::new(type_row![Type::UNIT]);
1044        let optype: OpType = op.clone().into();
1045        assert_eq!(
1046            optype.dataflow_signature().unwrap().io(),
1047            (
1048                &type_row![Type::UNIT],
1049                &vec![Type::new_tuple(type_row![Type::UNIT])].into(),
1050            )
1051        );
1052
1053        let new_op = MakeTuple::from_extension_op(optype.as_extension_op().unwrap()).unwrap();
1054        assert_eq!(new_op, op);
1055    }
1056
1057    #[test]
1058    fn test_unmake_tuple() {
1059        let op = UnpackTuple::new(type_row![Type::UNIT]);
1060        let optype: OpType = op.clone().into();
1061        assert_eq!(
1062            optype.dataflow_signature().unwrap().io(),
1063            (
1064                &vec![Type::new_tuple(type_row![Type::UNIT])].into(),
1065                &type_row![Type::UNIT],
1066            )
1067        );
1068
1069        let new_op = UnpackTuple::from_extension_op(optype.as_extension_op().unwrap()).unwrap();
1070        assert_eq!(new_op, op);
1071    }
1072
1073    #[test]
1074    fn test_noop() {
1075        let op = Noop::new(Type::UNIT);
1076        let optype: OpType = op.clone().into();
1077        assert_eq!(
1078            optype.dataflow_signature().unwrap().io(),
1079            (&type_row![Type::UNIT], &type_row![Type::UNIT])
1080        );
1081
1082        let new_op = Noop::from_extension_op(optype.as_extension_op().unwrap()).unwrap();
1083        assert_eq!(new_op, op);
1084    }
1085
1086    #[test]
1087    fn test_lift() {
1088        let op = Barrier::new(type_row![Type::UNIT]);
1089        let optype: OpType = op.clone().into();
1090        assert_eq!(
1091            optype.dataflow_signature().unwrap().as_ref(),
1092            &Signature::new_endo(type_row![Type::UNIT])
1093        );
1094
1095        let new_op = Barrier::from_extension_op(optype.as_extension_op().unwrap()).unwrap();
1096        assert_eq!(new_op, op);
1097    }
1098
1099    #[test]
1100    fn test_option() {
1101        let typ: Type = option_type(bool_t()).into();
1102        let const_val1 = const_some(Value::true_val());
1103        let const_val2 = const_none(bool_t());
1104
1105        let mut b = DFGBuilder::new(inout_sig(type_row![], vec![typ.clone(), typ])).unwrap();
1106
1107        let some = b.add_load_value(const_val1);
1108        let none = b.add_load_value(const_val2);
1109
1110        b.finish_hugr_with_outputs([some, none]).unwrap();
1111    }
1112
1113    #[test]
1114    fn test_result() {
1115        let typ: Type = either_type(bool_t(), float64_type()).into();
1116        let const_bool = const_left(Value::true_val(), float64_type());
1117        let const_float = const_right(bool_t(), ConstF64::new(0.5).into());
1118
1119        let mut b = DFGBuilder::new(inout_sig(type_row![], vec![typ.clone(), typ])).unwrap();
1120
1121        let bool = b.add_load_value(const_bool);
1122        let float = b.add_load_value(const_float);
1123
1124        b.finish_hugr_with_outputs([bool, float]).unwrap();
1125    }
1126
1127    #[test]
1128    /// test the prelude error type and panic op.
1129    fn test_error_type() {
1130        let ext_def = PRELUDE
1131            .get_type(&ERROR_TYPE_NAME)
1132            .unwrap()
1133            .instantiate([])
1134            .unwrap();
1135
1136        let ext_type = Type::new_extension(ext_def);
1137        assert_eq!(ext_type, error_type());
1138
1139        let error_val = ConstError::new(2, "my message");
1140
1141        assert_eq!(error_val.name(), "ConstError(2, \"my message\")");
1142
1143        assert!(error_val.validate().is_ok());
1144
1145        assert!(error_val.equal_consts(&ConstError::new(2, "my message")));
1146        assert!(!error_val.equal_consts(&ConstError::new(3, "my message")));
1147
1148        let mut b = DFGBuilder::new(endo_sig(type_row![])).unwrap();
1149
1150        let err = b.add_load_value(error_val);
1151
1152        let op = PRELUDE
1153            .instantiate_extension_op(&EXIT_OP_ID, [Term::new_list([]), Term::new_list([])])
1154            .unwrap();
1155
1156        b.add_dataflow_op(op, [err]).unwrap();
1157
1158        b.finish_hugr_with_outputs([]).unwrap();
1159    }
1160
1161    #[test]
1162    /// test the prelude make error op with the panic op.
1163    fn test_make_error() {
1164        let err_op = PRELUDE
1165            .instantiate_extension_op(&MAKE_ERROR_OP_ID, [])
1166            .unwrap();
1167        let panic_op = PRELUDE
1168            .instantiate_extension_op(&EXIT_OP_ID, [Term::new_list([]), Term::new_list([])])
1169            .unwrap();
1170
1171        let mut b =
1172            DFGBuilder::new(Signature::new(vec![usize_t(), string_type()], type_row![])).unwrap();
1173        let [signal, message] = b.input_wires_arr();
1174        let err_value = b.add_dataflow_op(err_op, [signal, message]).unwrap();
1175        b.add_dataflow_op(panic_op, err_value.outputs()).unwrap();
1176
1177        let h = b.finish_hugr_with_outputs([]).unwrap();
1178        h.validate().unwrap();
1179    }
1180
1181    #[test]
1182    /// test the panic operation with input and output wires
1183    fn test_panic_with_io() {
1184        let error_val = ConstError::new(42, "PANIC");
1185        let type_arg_q: Term = qb_t().into();
1186        let type_arg_2q: Term = Term::new_list([type_arg_q.clone(), type_arg_q]);
1187        let panic_op = PRELUDE
1188            .instantiate_extension_op(&PANIC_OP_ID, [type_arg_2q.clone(), type_arg_2q.clone()])
1189            .unwrap();
1190
1191        let mut b = DFGBuilder::new(endo_sig(vec![qb_t(), qb_t()])).unwrap();
1192        let [q0, q1] = b.input_wires_arr();
1193        let [q0, q1] = b
1194            .add_dataflow_op(cx_gate(), [q0, q1])
1195            .unwrap()
1196            .outputs_arr();
1197        let err = b.add_load_value(error_val);
1198        let [q0, q1] = b
1199            .add_dataflow_op(panic_op, [err, q0, q1])
1200            .unwrap()
1201            .outputs_arr();
1202        b.finish_hugr_with_outputs([q0, q1]).unwrap();
1203    }
1204
1205    #[test]
1206    /// Test string type.
1207    fn test_string_type() {
1208        let string_custom_type: CustomType = PRELUDE
1209            .get_type(&STRING_TYPE_NAME)
1210            .unwrap()
1211            .instantiate([])
1212            .unwrap();
1213        let string_ty: Type = Type::new_extension(string_custom_type);
1214        assert_eq!(string_ty, string_type());
1215        let string_const: ConstString = ConstString::new("Lorem ipsum".into());
1216        assert_eq!(string_const.name(), "ConstString(\"Lorem ipsum\")");
1217        assert!(string_const.validate().is_ok());
1218        assert!(string_const.equal_consts(&ConstString::new("Lorem ipsum".into())));
1219        assert!(!string_const.equal_consts(&ConstString::new("Lorem ispum".into())));
1220    }
1221
1222    #[test]
1223    /// Test print operation
1224    fn test_print() {
1225        let mut b: DFGBuilder<Hugr> = DFGBuilder::new(endo_sig(vec![])).unwrap();
1226        let greeting: ConstString = ConstString::new("Hello, world!".into());
1227        let greeting_out: Wire = b.add_load_value(greeting);
1228        let print_op = PRELUDE.instantiate_extension_op(&PRINT_OP_ID, []).unwrap();
1229        b.add_dataflow_op(print_op, [greeting_out]).unwrap();
1230        b.finish_hugr_with_outputs([]).unwrap();
1231    }
1232
1233    #[test]
1234    fn test_external_symbol() {
1235        let subject = ConstExternalSymbol::new("foo", Type::UNIT, false);
1236        assert_eq!(subject.get_type(), Type::UNIT);
1237        assert_eq!(subject.name(), "@foo");
1238        assert!(subject.validate().is_ok());
1239        assert!(subject.equal_consts(&ConstExternalSymbol::new("foo", Type::UNIT, false)));
1240        assert!(!subject.equal_consts(&ConstExternalSymbol::new("bar", Type::UNIT, false)));
1241        assert!(!subject.equal_consts(&ConstExternalSymbol::new("foo", string_type(), false)));
1242        assert!(!subject.equal_consts(&ConstExternalSymbol::new("foo", Type::UNIT, true)));
1243
1244        assert!(
1245            ConstExternalSymbol::new("", Type::UNIT, true)
1246                .validate()
1247                .is_err()
1248        );
1249    }
1250}