hugr_core/std_extensions/collections/
array.rs

1//! Fixed-length array type and operations extension.
2
3mod array_clone;
4mod array_conversion;
5mod array_discard;
6mod array_kind;
7mod array_op;
8mod array_repeat;
9mod array_scan;
10mod array_value;
11pub mod op_builder;
12
13use std::sync::{Arc, LazyLock};
14
15use delegate::delegate;
16
17use crate::builder::{BuildError, Dataflow};
18use crate::extension::resolution::{ExtensionResolutionError, WeakExtensionRegistry};
19use crate::extension::simple_op::{HasConcrete, MakeOpDef, MakeRegisteredOp};
20use crate::extension::{ExtensionId, SignatureError, TypeDef, TypeDefBound};
21use crate::ops::constant::{CustomConst, ValueName};
22use crate::ops::{ExtensionOp, OpName};
23use crate::types::type_param::{TypeArg, TypeParam};
24use crate::types::{CustomCheckFailure, Type, TypeBound, TypeName};
25use crate::{Extension, Wire};
26
27pub use array_clone::{ARRAY_CLONE_OP_ID, GenericArrayClone, GenericArrayCloneDef};
28pub use array_conversion::{Direction, FROM, GenericArrayConvert, GenericArrayConvertDef, INTO};
29pub use array_discard::{ARRAY_DISCARD_OP_ID, GenericArrayDiscard, GenericArrayDiscardDef};
30pub use array_kind::ArrayKind;
31pub use array_op::{GenericArrayOp, GenericArrayOpDef};
32pub use array_repeat::{ARRAY_REPEAT_OP_ID, GenericArrayRepeat, GenericArrayRepeatDef};
33pub use array_scan::{ARRAY_SCAN_OP_ID, GenericArrayScan, GenericArrayScanDef};
34pub use array_value::GenericArrayValue;
35
36use op_builder::GenericArrayOpBuilder;
37
38/// Reported unique name of the array type.
39pub const ARRAY_TYPENAME: TypeName = TypeName::new_inline("array");
40/// Reported unique name of the array value.
41pub const ARRAY_VALUENAME: TypeName = TypeName::new_inline("array");
42/// Reported unique name of the extension
43pub const EXTENSION_ID: ExtensionId = ExtensionId::new_unchecked("collections.array");
44/// Extension version.
45pub const VERSION: semver::Version = semver::Version::new(0, 1, 1);
46
47/// A linear, fixed-length collection of values.
48///
49/// Arrays are linear, even if their elements are copyable.
50#[derive(Clone, Copy, Debug, derive_more::Display, Eq, PartialEq, Default)]
51pub struct Array;
52
53impl ArrayKind for Array {
54    const EXTENSION_ID: ExtensionId = EXTENSION_ID;
55    const TYPE_NAME: TypeName = ARRAY_TYPENAME;
56    const VALUE_NAME: ValueName = ARRAY_VALUENAME;
57
58    fn extension() -> &'static Arc<Extension> {
59        &EXTENSION
60    }
61
62    fn type_def() -> &'static TypeDef {
63        EXTENSION.get_type(&ARRAY_TYPENAME).unwrap()
64    }
65}
66
67/// Array operation definitions.
68pub type ArrayOpDef = GenericArrayOpDef<Array>;
69/// Array clone operation definition.
70pub type ArrayCloneDef = GenericArrayCloneDef<Array>;
71/// Array discard operation definition.
72pub type ArrayDiscardDef = GenericArrayDiscardDef<Array>;
73/// Array repeat operation definition.
74pub type ArrayRepeatDef = GenericArrayRepeatDef<Array>;
75/// Array scan operation definition.
76pub type ArrayScanDef = GenericArrayScanDef<Array>;
77
78/// Array operations.
79pub type ArrayOp = GenericArrayOp<Array>;
80/// The array clone operation.
81pub type ArrayClone = GenericArrayClone<Array>;
82/// The array discard operation.
83pub type ArrayDiscard = GenericArrayDiscard<Array>;
84/// The array repeat operation.
85pub type ArrayRepeat = GenericArrayRepeat<Array>;
86/// The array scan operation.
87pub type ArrayScan = GenericArrayScan<Array>;
88
89/// An array extension value.
90pub type ArrayValue = GenericArrayValue<Array>;
91
92/// Extension for array operations.
93pub static EXTENSION: LazyLock<Arc<Extension>> = LazyLock::new(|| {
94    Extension::new_arc(EXTENSION_ID, VERSION, |extension, extension_ref| {
95        extension
96            .add_type(
97                ARRAY_TYPENAME,
98                vec![TypeParam::max_nat_type(), TypeBound::Linear.into()],
99                "Fixed-length array".into(),
100                // Default array is linear, even if the elements are copyable
101                TypeDefBound::any(),
102                extension_ref,
103            )
104            .unwrap();
105
106        ArrayOpDef::load_all_ops(extension, extension_ref).unwrap();
107        ArrayCloneDef::new()
108            .add_to_extension(extension, extension_ref)
109            .unwrap();
110        ArrayDiscardDef::new()
111            .add_to_extension(extension, extension_ref)
112            .unwrap();
113        ArrayRepeatDef::new()
114            .add_to_extension(extension, extension_ref)
115            .unwrap();
116        ArrayScanDef::new()
117            .add_to_extension(extension, extension_ref)
118            .unwrap();
119    })
120});
121
122impl ArrayValue {
123    /// Name of the constructor for creating constant arrays.
124    pub(crate) const CTR_NAME: &'static str = "collections.array.const";
125}
126
127#[typetag::serde(name = "ArrayValue")]
128impl CustomConst for ArrayValue {
129    delegate! {
130        to self {
131            fn name(&self) -> ValueName;
132            fn validate(&self) -> Result<(), CustomCheckFailure>;
133            fn update_extensions(
134                &mut self,
135                extensions: &WeakExtensionRegistry,
136            ) -> Result<(), ExtensionResolutionError>;
137            fn get_type(&self) -> Type;
138        }
139    }
140
141    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
142        crate::ops::constant::downcast_equal_consts(self, other)
143    }
144}
145
146/// Gets the [`TypeDef`] for arrays. Note that instantiations are more easily
147/// created via [`array_type`] and [`array_type_parametric`]
148#[must_use]
149pub fn array_type_def() -> &'static TypeDef {
150    Array::type_def()
151}
152
153/// Instantiate a new array type given a size argument and element type.
154///
155/// This method is equivalent to [`array_type_parametric`], but uses concrete
156/// arguments types to ensure no errors are possible.
157#[must_use]
158pub fn array_type(size: u64, element_ty: Type) -> Type {
159    Array::ty(size, element_ty)
160}
161
162/// Instantiate a new array type given the size and element type parameters.
163///
164/// This is a generic version of [`array_type`].
165pub fn array_type_parametric(
166    size: impl Into<TypeArg>,
167    element_ty: impl Into<TypeArg>,
168) -> Result<Type, SignatureError> {
169    Array::ty_parametric(size, element_ty)
170}
171
172/// Name of the operation in the prelude for creating new arrays.
173pub const NEW_ARRAY_OP_ID: OpName = OpName::new_inline("new_array");
174
175/// Initialize a new array op of element type `element_ty` of length `size`
176#[must_use]
177pub fn new_array_op(element_ty: Type, size: u64) -> ExtensionOp {
178    let op = ArrayOpDef::new_array.to_concrete(element_ty, size);
179    op.to_extension_op().unwrap()
180}
181
182/// Trait for building array operations in a dataflow graph.
183pub trait ArrayOpBuilder: GenericArrayOpBuilder {
184    /// Adds a new array operation to the dataflow graph and return the wire
185    /// representing the new array.
186    ///
187    /// # Arguments
188    ///
189    /// * `elem_ty` - The type of the elements in the array.
190    /// * `values` - An iterator over the values to initialize the array with.
191    ///
192    /// # Errors
193    ///
194    /// If building the operation fails.
195    ///
196    /// # Returns
197    ///
198    /// The wire representing the new array.
199    fn add_new_array(
200        &mut self,
201        elem_ty: Type,
202        values: impl IntoIterator<Item = Wire>,
203    ) -> Result<Wire, BuildError> {
204        self.add_new_generic_array::<Array>(elem_ty, values)
205    }
206    /// Adds an array unpack operation to the dataflow graph.
207    ///
208    /// This operation unpacks an array into individual elements.
209    ///
210    /// # Arguments
211    ///
212    /// * `elem_ty` - The type of the elements in the array.
213    /// * `size` - The size of the array.
214    /// * `input` - The wire representing the array to unpack.
215    ///
216    /// # Errors
217    ///
218    /// If building the operation fails.
219    ///
220    /// # Returns
221    ///
222    /// A vector of wires representing the individual elements from the array.
223    fn add_array_unpack(
224        &mut self,
225        elem_ty: Type,
226        size: u64,
227        input: Wire,
228    ) -> Result<Vec<Wire>, BuildError> {
229        self.add_generic_array_unpack::<Array>(elem_ty, size, input)
230    }
231    /// Adds an array clone operation to the dataflow graph and return the wires
232    /// representing the original and cloned array.
233    ///
234    /// # Arguments
235    ///
236    /// * `elem_ty` - The type of the elements in the array.
237    /// * `size` - The size of the array.
238    /// * `input` - The wire representing the array.
239    ///
240    /// # Errors
241    ///
242    /// If building the operation fails.
243    ///
244    /// # Returns
245    ///
246    /// The wires representing the original and cloned array.
247    fn add_array_clone(
248        &mut self,
249        elem_ty: Type,
250        size: u64,
251        input: Wire,
252    ) -> Result<(Wire, Wire), BuildError> {
253        self.add_generic_array_clone::<Array>(elem_ty, size, input)
254    }
255
256    /// Adds an array discard operation to the dataflow graph.
257    ///
258    /// # Arguments
259    ///
260    /// * `elem_ty` - The type of the elements in the array.
261    /// * `size` - The size of the array.
262    /// * `input` - The wire representing the array.
263    ///
264    /// # Errors
265    ///
266    /// If building the operation fails.
267    fn add_array_discard(
268        &mut self,
269        elem_ty: Type,
270        size: u64,
271        input: Wire,
272    ) -> Result<(), BuildError> {
273        self.add_generic_array_discard::<Array>(elem_ty, size, input)
274    }
275
276    /// Adds an array get operation to the dataflow graph.
277    ///
278    /// # Arguments
279    ///
280    /// * `elem_ty` - The type of the elements in the array.
281    /// * `size` - The size of the array.
282    /// * `input` - The wire representing the array.
283    /// * `index` - The wire representing the index to get.
284    ///
285    /// # Errors
286    ///
287    /// If building the operation fails.
288    ///
289    /// # Returns
290    ///
291    /// * The wire representing the value at the specified index in the array
292    /// * The wire representing the array
293    fn add_array_get(
294        &mut self,
295        elem_ty: Type,
296        size: u64,
297        input: Wire,
298        index: Wire,
299    ) -> Result<(Wire, Wire), BuildError> {
300        self.add_generic_array_get::<Array>(elem_ty, size, input, index)
301    }
302
303    /// Adds an array set operation to the dataflow graph.
304    ///
305    /// This operation sets the value at a specified index in the array.
306    ///
307    /// # Arguments
308    ///
309    /// * `elem_ty` - The type of the elements in the array.
310    /// * `size` - The size of the array.
311    /// * `input` - The wire representing the array.
312    /// * `index` - The wire representing the index to set.
313    /// * `value` - The wire representing the value to set at the specified index.
314    ///
315    /// # Errors
316    ///
317    /// Returns an error if building the operation fails.
318    ///
319    /// # Returns
320    ///
321    /// The wire representing the updated array after the set operation.
322    fn add_array_set(
323        &mut self,
324        elem_ty: Type,
325        size: u64,
326        input: Wire,
327        index: Wire,
328        value: Wire,
329    ) -> Result<Wire, BuildError> {
330        self.add_generic_array_set::<Array>(elem_ty, size, input, index, value)
331    }
332
333    /// Adds an array swap operation to the dataflow graph.
334    ///
335    /// This operation swaps the values at two specified indices in the array.
336    ///
337    /// # Arguments
338    ///
339    /// * `elem_ty` - The type of the elements in the array.
340    /// * `size` - The size of the array.
341    /// * `input` - The wire representing the array.
342    /// * `index1` - The wire representing the first index to swap.
343    /// * `index2` - The wire representing the second index to swap.
344    ///
345    /// # Errors
346    ///
347    /// Returns an error if building the operation fails.
348    ///
349    /// # Returns
350    ///
351    /// The wire representing the updated array after the swap operation.
352    fn add_array_swap(
353        &mut self,
354        elem_ty: Type,
355        size: u64,
356        input: Wire,
357        index1: Wire,
358        index2: Wire,
359    ) -> Result<Wire, BuildError> {
360        let op = GenericArrayOpDef::<Array>::swap.instantiate(&[size.into(), elem_ty.into()])?;
361        let [out] = self
362            .add_dataflow_op(op, vec![input, index1, index2])?
363            .outputs_arr();
364        Ok(out)
365    }
366
367    /// Adds an array pop-left operation to the dataflow graph.
368    ///
369    /// This operation removes the leftmost element from the array.
370    ///
371    /// # Arguments
372    ///
373    /// * `elem_ty` - The type of the elements in the array.
374    /// * `size` - The size of the array.
375    /// * `input` - The wire representing the array.
376    ///
377    /// # Errors
378    ///
379    /// Returns an error if building the operation fails.
380    ///
381    /// # Returns
382    ///
383    /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
384    fn add_array_pop_left(
385        &mut self,
386        elem_ty: Type,
387        size: u64,
388        input: Wire,
389    ) -> Result<Wire, BuildError> {
390        self.add_generic_array_pop_left::<Array>(elem_ty, size, input)
391    }
392
393    /// Adds an array pop-right operation to the dataflow graph.
394    ///
395    /// This operation removes the rightmost element from the array.
396    ///
397    /// # Arguments
398    ///
399    /// * `elem_ty` - The type of the elements in the array.
400    /// * `size` - The size of the array.
401    /// * `input` - The wire representing the array.
402    ///
403    /// # Errors
404    ///
405    /// Returns an error if building the operation fails.
406    ///
407    /// # Returns
408    ///
409    /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
410    fn add_array_pop_right(
411        &mut self,
412        elem_ty: Type,
413        size: u64,
414        input: Wire,
415    ) -> Result<Wire, BuildError> {
416        self.add_generic_array_pop_right::<Array>(elem_ty, size, input)
417    }
418
419    /// Adds an operation to discard an empty array from the dataflow graph.
420    ///
421    /// # Arguments
422    ///
423    /// * `elem_ty` - The type of the elements in the array.
424    /// * `input` - The wire representing the array.
425    ///
426    /// # Errors
427    ///
428    /// Returns an error if building the operation fails.
429    fn add_array_discard_empty(&mut self, elem_ty: Type, input: Wire) -> Result<(), BuildError> {
430        self.add_generic_array_discard_empty::<Array>(elem_ty, input)
431    }
432}
433
434impl<D: Dataflow> ArrayOpBuilder for D {}
435
436#[cfg(test)]
437mod test {
438    use crate::builder::{DFGBuilder, Dataflow, DataflowHugr, inout_sig};
439    use crate::extension::prelude::qb_t;
440
441    use super::{array_type, new_array_op};
442
443    #[test]
444    /// Test building a HUGR involving a `new_array` operation.
445    fn test_new_array() {
446        let mut b =
447            DFGBuilder::new(inout_sig(vec![qb_t(), qb_t()], array_type(2, qb_t()))).unwrap();
448
449        let [q1, q2] = b.input_wires_arr();
450
451        let op = new_array_op(qb_t(), 2);
452
453        let out = b.add_dataflow_op(op, [q1, q2]).unwrap();
454
455        b.finish_hugr_with_outputs(out.outputs()).unwrap();
456    }
457}