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