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}