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