hugr_core/std_extensions/collections/
value_array.rs

1//! A version of the standard fixed-length array extension where arrays of copyable types
2//! are copyable themselves.
3//!
4//! Supports all regular array operations apart from `clone` and `discard`.
5
6use std::sync::{Arc, LazyLock};
7
8use delegate::delegate;
9
10use crate::builder::{BuildError, Dataflow};
11use crate::extension::resolution::{ExtensionResolutionError, WeakExtensionRegistry};
12use crate::extension::simple_op::{HasConcrete, MakeOpDef};
13use crate::extension::{ExtensionId, SignatureError, TypeDef, TypeDefBound};
14use crate::ops::constant::{CustomConst, ValueName};
15use crate::types::type_param::{TypeArg, TypeParam};
16use crate::types::{CustomCheckFailure, Type, TypeBound, TypeName};
17use crate::{Extension, Wire};
18
19use super::array::op_builder::GenericArrayOpBuilder;
20use super::array::{
21    Array, ArrayKind, FROM, GenericArrayConvert, GenericArrayConvertDef, GenericArrayOp,
22    GenericArrayOpDef, GenericArrayRepeat, GenericArrayRepeatDef, GenericArrayScan,
23    GenericArrayScanDef, GenericArrayValue, INTO,
24};
25
26/// Reported unique name of the value array type.
27pub const VALUE_ARRAY_TYPENAME: TypeName = TypeName::new_inline("value_array");
28/// Reported unique name of the value array value.
29pub const VALUE_ARRAY_VALUENAME: TypeName = TypeName::new_inline("value_array");
30/// Reported unique name of the extension
31pub const EXTENSION_ID: ExtensionId = ExtensionId::new_static_unchecked("collections.value_array");
32/// Extension version.
33pub const VERSION: semver::Version = semver::Version::new(0, 1, 1);
34
35/// A fixed-length collection of values.
36///
37/// A value array inherits its linearity from its elements.
38#[derive(Clone, Copy, Debug, derive_more::Display, Eq, PartialEq, Default)]
39pub struct ValueArray;
40
41impl ArrayKind for ValueArray {
42    const EXTENSION_ID: ExtensionId = EXTENSION_ID;
43    const TYPE_NAME: TypeName = VALUE_ARRAY_TYPENAME;
44    const VALUE_NAME: ValueName = VALUE_ARRAY_VALUENAME;
45
46    fn extension() -> &'static Arc<Extension> {
47        &EXTENSION
48    }
49
50    fn type_def() -> &'static TypeDef {
51        EXTENSION.get_type(&VALUE_ARRAY_TYPENAME).unwrap()
52    }
53
54    fn build_clone<D: Dataflow>(
55        _builder: &mut D,
56        _elem_ty: Type,
57        _size: u64,
58        arr: Wire,
59    ) -> Result<(Wire, Wire), BuildError> {
60        Ok((arr, arr))
61    }
62
63    fn build_discard<D: Dataflow>(
64        _builder: &mut D,
65        _elem_ty: Type,
66        _size: u64,
67        _arr: Wire,
68    ) -> Result<(), BuildError> {
69        Ok(())
70    }
71}
72
73/// Value array operation definitions.
74pub type VArrayOpDef = GenericArrayOpDef<ValueArray>;
75/// Value array repeat operation definition.
76pub type VArrayRepeatDef = GenericArrayRepeatDef<ValueArray>;
77/// Value array scan operation definition.
78pub type VArrayScanDef = GenericArrayScanDef<ValueArray>;
79/// Value array to default array conversion operation definition.
80pub type VArrayToArrayDef = GenericArrayConvertDef<ValueArray, INTO, Array>;
81/// Value array from default array conversion operation definition.
82pub type VArrayFromArrayDef = GenericArrayConvertDef<ValueArray, FROM, Array>;
83
84/// Value array operations.
85pub type VArrayOp = GenericArrayOp<ValueArray>;
86/// The value array repeat operation.
87pub type VArrayRepeat = GenericArrayRepeat<ValueArray>;
88/// The value array scan operation.
89pub type VArrayScan = GenericArrayScan<ValueArray>;
90/// The value array to default array conversion operation.
91pub type VArrayToArray = GenericArrayConvert<ValueArray, INTO, Array>;
92/// The value array from default array conversion operation.
93pub type VArrayFromArray = GenericArrayConvert<ValueArray, FROM, Array>;
94
95/// A value array extension value.
96pub type VArrayValue = GenericArrayValue<ValueArray>;
97
98/// Extension for value array operations.
99pub static EXTENSION: LazyLock<Arc<Extension>> = LazyLock::new(|| {
100    Extension::new_arc(EXTENSION_ID, VERSION, |extension, extension_ref| {
101        extension
102            .add_type(
103                VALUE_ARRAY_TYPENAME,
104                vec![TypeParam::max_nat_type(), TypeBound::Linear.into()],
105                "Fixed-length value array".into(),
106                // Value arrays are copyable iff their elements are
107                TypeDefBound::from_params(vec![1]),
108                extension_ref,
109            )
110            .unwrap();
111
112        VArrayOpDef::load_all_ops(extension, extension_ref).unwrap();
113        VArrayRepeatDef::new()
114            .add_to_extension(extension, extension_ref)
115            .unwrap();
116        VArrayScanDef::new()
117            .add_to_extension(extension, extension_ref)
118            .unwrap();
119        VArrayToArrayDef::new()
120            .add_to_extension(extension, extension_ref)
121            .unwrap();
122        VArrayFromArrayDef::new()
123            .add_to_extension(extension, extension_ref)
124            .unwrap();
125    })
126});
127
128#[typetag::serde(name = "VArrayValue")]
129impl CustomConst for VArrayValue {
130    delegate! {
131        to self {
132            fn name(&self) -> ValueName;
133            fn validate(&self) -> Result<(), CustomCheckFailure>;
134            fn update_extensions(
135                &mut self,
136                extensions: &WeakExtensionRegistry,
137            ) -> Result<(), ExtensionResolutionError>;
138            fn get_type(&self) -> Type;
139        }
140    }
141
142    fn equal_consts(&self, other: &dyn CustomConst) -> bool {
143        crate::ops::constant::downcast_equal_consts(self, other)
144    }
145}
146
147/// Gets the [`TypeDef`] for value arrays. Note that instantiations are more easily
148/// created via [`value_array_type`] and [`value_array_type_parametric`]
149#[must_use]
150pub fn value_array_type_def() -> &'static TypeDef {
151    ValueArray::type_def()
152}
153
154/// Instantiate a new value array type given a size argument and element type.
155///
156/// This method is equivalent to [`value_array_type_parametric`], but uses concrete
157/// arguments types to ensure no errors are possible.
158#[must_use]
159pub fn value_array_type(size: u64, element_ty: Type) -> Type {
160    ValueArray::ty(size, element_ty)
161}
162
163/// Instantiate a new value array type given the size and element type parameters.
164///
165/// This is a generic version of [`value_array_type`].
166pub fn value_array_type_parametric(
167    size: impl Into<TypeArg>,
168    element_ty: impl Into<TypeArg>,
169) -> Result<Type, SignatureError> {
170    ValueArray::ty_parametric(size, element_ty)
171}
172
173/// Trait for building value array operations in a dataflow graph.
174pub trait VArrayOpBuilder: GenericArrayOpBuilder {
175    /// Adds a new array operation to the dataflow graph and return the wire
176    /// representing the new array.
177    ///
178    /// # Arguments
179    ///
180    /// * `elem_ty` - The type of the elements in the array.
181    /// * `values` - An iterator over the values to initialize the array with.
182    ///
183    /// # Errors
184    ///
185    /// If building the operation fails.
186    ///
187    /// # Returns
188    ///
189    /// The wire representing the new array.
190    fn add_new_value_array(
191        &mut self,
192        elem_ty: Type,
193        values: impl IntoIterator<Item = Wire>,
194    ) -> Result<Wire, BuildError> {
195        self.add_new_generic_array::<ValueArray>(elem_ty, values)
196    }
197
198    /// Adds an array get operation to the dataflow graph.
199    ///
200    /// # Arguments
201    ///
202    /// * `elem_ty` - The type of the elements in the array.
203    /// * `size` - The size of the array.
204    /// * `input` - The wire representing the array.
205    /// * `index` - The wire representing the index to get.
206    ///
207    /// # Errors
208    ///
209    /// If building the operation fails.
210    ///
211    /// # Returns
212    ///
213    /// * The wire representing the value at the specified index in the array
214    /// * The wire representing the array
215    fn add_value_array_get(
216        &mut self,
217        elem_ty: Type,
218        size: u64,
219        input: Wire,
220        index: Wire,
221    ) -> Result<(Wire, Wire), BuildError> {
222        self.add_generic_array_get::<ValueArray>(elem_ty, size, input, index)
223    }
224
225    /// Adds an array set operation to the dataflow graph.
226    ///
227    /// This operation sets the value at a specified index in the array.
228    ///
229    /// # Arguments
230    ///
231    /// * `elem_ty` - The type of the elements in the array.
232    /// * `size` - The size of the array.
233    /// * `input` - The wire representing the array.
234    /// * `index` - The wire representing the index to set.
235    /// * `value` - The wire representing the value to set at the specified index.
236    ///
237    /// # Errors
238    ///
239    /// Returns an error if building the operation fails.
240    ///
241    /// # Returns
242    ///
243    /// The wire representing the updated array after the set operation.
244    fn add_value_array_set(
245        &mut self,
246        elem_ty: Type,
247        size: u64,
248        input: Wire,
249        index: Wire,
250        value: Wire,
251    ) -> Result<Wire, BuildError> {
252        self.add_generic_array_set::<ValueArray>(elem_ty, size, input, index, value)
253    }
254
255    /// Adds an array swap operation to the dataflow graph.
256    ///
257    /// This operation swaps the values at two specified indices in the array.
258    ///
259    /// # Arguments
260    ///
261    /// * `elem_ty` - The type of the elements in the array.
262    /// * `size` - The size of the array.
263    /// * `input` - The wire representing the array.
264    /// * `index1` - The wire representing the first index to swap.
265    /// * `index2` - The wire representing the second index to swap.
266    ///
267    /// # Errors
268    ///
269    /// Returns an error if building the operation fails.
270    ///
271    /// # Returns
272    ///
273    /// The wire representing the updated array after the swap operation.
274    fn add_value_array_swap(
275        &mut self,
276        elem_ty: Type,
277        size: u64,
278        input: Wire,
279        index1: Wire,
280        index2: Wire,
281    ) -> Result<Wire, BuildError> {
282        let op =
283            GenericArrayOpDef::<ValueArray>::swap.instantiate(&[size.into(), elem_ty.into()])?;
284        let [out] = self
285            .add_dataflow_op(op, vec![input, index1, index2])?
286            .outputs_arr();
287        Ok(out)
288    }
289
290    /// Adds an array pop-left operation to the dataflow graph.
291    ///
292    /// This operation removes the leftmost element from the array.
293    ///
294    /// # Arguments
295    ///
296    /// * `elem_ty` - The type of the elements in the array.
297    /// * `size` - The size of the array.
298    /// * `input` - The wire representing the array.
299    ///
300    /// # Errors
301    ///
302    /// Returns an error if building the operation fails.
303    ///
304    /// # Returns
305    ///
306    /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
307    fn add_array_pop_left(
308        &mut self,
309        elem_ty: Type,
310        size: u64,
311        input: Wire,
312    ) -> Result<Wire, BuildError> {
313        self.add_generic_array_pop_left::<ValueArray>(elem_ty, size, input)
314    }
315
316    /// Adds an array pop-right operation to the dataflow graph.
317    ///
318    /// This operation removes the rightmost element from the array.
319    ///
320    /// # Arguments
321    ///
322    /// * `elem_ty` - The type of the elements in the array.
323    /// * `size` - The size of the array.
324    /// * `input` - The wire representing the array.
325    ///
326    /// # Errors
327    ///
328    /// Returns an error if building the operation fails.
329    ///
330    /// # Returns
331    ///
332    /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
333    fn add_array_pop_right(
334        &mut self,
335        elem_ty: Type,
336        size: u64,
337        input: Wire,
338    ) -> Result<Wire, BuildError> {
339        self.add_generic_array_pop_right::<ValueArray>(elem_ty, size, input)
340    }
341
342    /// Adds an operation to discard an empty array from the dataflow graph.
343    ///
344    /// # Arguments
345    ///
346    /// * `elem_ty` - The type of the elements in the array.
347    /// * `input` - The wire representing the array.
348    ///
349    /// # Errors
350    ///
351    /// Returns an error if building the operation fails.
352    fn add_array_discard_empty(&mut self, elem_ty: Type, input: Wire) -> Result<(), BuildError> {
353        self.add_generic_array_discard_empty::<ValueArray>(elem_ty, input)
354    }
355}
356
357impl<D: Dataflow> VArrayOpBuilder for D {}