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::{GenericArrayClone, GenericArrayCloneDef, ARRAY_CLONE_OP_ID};
29pub use array_conversion::{Direction, GenericArrayConvert, GenericArrayConvertDef, FROM, INTO};
30pub use array_discard::{GenericArrayDiscard, GenericArrayDiscardDef, ARRAY_DISCARD_OP_ID};
31pub use array_kind::ArrayKind;
32pub use array_op::{GenericArrayOp, GenericArrayOpDef};
33pub use array_repeat::{GenericArrayRepeat, GenericArrayRepeatDef, ARRAY_REPEAT_OP_ID};
34pub use array_scan::{GenericArrayScan, GenericArrayScanDef, ARRAY_SCAN_OP_ID};
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]
142pub fn array_type_def() -> &'static TypeDef {
143 Array::type_def()
144}
145
146/// Instantiate a new array type given a size argument and element type.
147///
148/// This method is equivalent to [`array_type_parametric`], but uses concrete
149/// arguments types to ensure no errors are possible.
150pub fn array_type(size: u64, element_ty: Type) -> Type {
151 Array::ty(size, element_ty)
152}
153
154/// Instantiate a new array type given the size and element type parameters.
155///
156/// This is a generic version of [`array_type`].
157pub fn array_type_parametric(
158 size: impl Into<TypeArg>,
159 element_ty: impl Into<TypeArg>,
160) -> Result<Type, SignatureError> {
161 Array::ty_parametric(size, element_ty)
162}
163
164/// Name of the operation in the prelude for creating new arrays.
165pub const NEW_ARRAY_OP_ID: OpName = OpName::new_inline("new_array");
166
167/// Initialize a new array op of element type `element_ty` of length `size`
168pub fn new_array_op(element_ty: Type, size: u64) -> ExtensionOp {
169 let op = ArrayOpDef::new_array.to_concrete(element_ty, size);
170 op.to_extension_op().unwrap()
171}
172
173/// Trait for building array operations in a dataflow graph.
174pub trait ArrayOpBuilder: 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_array(
191 &mut self,
192 elem_ty: Type,
193 values: impl IntoIterator<Item = Wire>,
194 ) -> Result<Wire, BuildError> {
195 self.add_new_generic_array::<Array>(elem_ty, values)
196 }
197
198 /// Adds an array clone operation to the dataflow graph and return the wires
199 /// representing the originala and cloned array.
200 ///
201 /// # Arguments
202 ///
203 /// * `elem_ty` - The type of the elements in the array.
204 /// * `size` - The size of the array.
205 /// * `input` - The wire representing the array.
206 ///
207 /// # Errors
208 ///
209 /// If building the operation fails.
210 ///
211 /// # Returns
212 ///
213 /// The wires representing the original and cloned array.
214 fn add_array_clone(
215 &mut self,
216 elem_ty: Type,
217 size: u64,
218 input: Wire,
219 ) -> Result<(Wire, Wire), BuildError> {
220 self.add_generic_array_clone::<Array>(elem_ty, size, input)
221 }
222
223 /// Adds an array discard operation to the dataflow graph.
224 ///
225 /// # Arguments
226 ///
227 /// * `elem_ty` - The type of the elements in the array.
228 /// * `size` - The size of the array.
229 /// * `input` - The wire representing the array.
230 ///
231 /// # Errors
232 ///
233 /// If building the operation fails.
234 fn add_array_discard(
235 &mut self,
236 elem_ty: Type,
237 size: u64,
238 input: Wire,
239 ) -> Result<(), BuildError> {
240 self.add_generic_array_discard::<Array>(elem_ty, size, input)
241 }
242
243 /// Adds an array get operation to the dataflow graph.
244 ///
245 /// # Arguments
246 ///
247 /// * `elem_ty` - The type of the elements in the array.
248 /// * `size` - The size of the array.
249 /// * `input` - The wire representing the array.
250 /// * `index` - The wire representing the index to get.
251 ///
252 /// # Errors
253 ///
254 /// If building the operation fails.
255 ///
256 /// # Returns
257 ///
258 /// * The wire representing the value at the specified index in the array
259 /// * The wire representing the array
260 fn add_array_get(
261 &mut self,
262 elem_ty: Type,
263 size: u64,
264 input: Wire,
265 index: Wire,
266 ) -> Result<(Wire, Wire), BuildError> {
267 self.add_generic_array_get::<Array>(elem_ty, size, input, index)
268 }
269
270 /// Adds an array set operation to the dataflow graph.
271 ///
272 /// This operation sets the value at a specified index in the array.
273 ///
274 /// # Arguments
275 ///
276 /// * `elem_ty` - The type of the elements in the array.
277 /// * `size` - The size of the array.
278 /// * `input` - The wire representing the array.
279 /// * `index` - The wire representing the index to set.
280 /// * `value` - The wire representing the value to set at the specified index.
281 ///
282 /// # Errors
283 ///
284 /// Returns an error if building the operation fails.
285 ///
286 /// # Returns
287 ///
288 /// The wire representing the updated array after the set operation.
289 fn add_array_set(
290 &mut self,
291 elem_ty: Type,
292 size: u64,
293 input: Wire,
294 index: Wire,
295 value: Wire,
296 ) -> Result<Wire, BuildError> {
297 self.add_generic_array_set::<Array>(elem_ty, size, input, index, value)
298 }
299
300 /// Adds an array swap operation to the dataflow graph.
301 ///
302 /// This operation swaps the values at two specified indices in the array.
303 ///
304 /// # Arguments
305 ///
306 /// * `elem_ty` - The type of the elements in the array.
307 /// * `size` - The size of the array.
308 /// * `input` - The wire representing the array.
309 /// * `index1` - The wire representing the first index to swap.
310 /// * `index2` - The wire representing the second index to swap.
311 ///
312 /// # Errors
313 ///
314 /// Returns an error if building the operation fails.
315 ///
316 /// # Returns
317 ///
318 /// The wire representing the updated array after the swap operation.
319 fn add_array_swap(
320 &mut self,
321 elem_ty: Type,
322 size: u64,
323 input: Wire,
324 index1: Wire,
325 index2: Wire,
326 ) -> Result<Wire, BuildError> {
327 let op = GenericArrayOpDef::<Array>::swap.instantiate(&[size.into(), elem_ty.into()])?;
328 let [out] = self
329 .add_dataflow_op(op, vec![input, index1, index2])?
330 .outputs_arr();
331 Ok(out)
332 }
333
334 /// Adds an array pop-left operation to the dataflow graph.
335 ///
336 /// This operation removes the leftmost element from the array.
337 ///
338 /// # Arguments
339 ///
340 /// * `elem_ty` - The type of the elements in the array.
341 /// * `size` - The size of the array.
342 /// * `input` - The wire representing the array.
343 ///
344 /// # Errors
345 ///
346 /// Returns an error if building the operation fails.
347 ///
348 /// # Returns
349 ///
350 /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
351 fn add_array_pop_left(
352 &mut self,
353 elem_ty: Type,
354 size: u64,
355 input: Wire,
356 ) -> Result<Wire, BuildError> {
357 self.add_generic_array_pop_left::<Array>(elem_ty, size, input)
358 }
359
360 /// Adds an array pop-right operation to the dataflow graph.
361 ///
362 /// This operation removes the rightmost element from the array.
363 ///
364 /// # Arguments
365 ///
366 /// * `elem_ty` - The type of the elements in the array.
367 /// * `size` - The size of the array.
368 /// * `input` - The wire representing the array.
369 ///
370 /// # Errors
371 ///
372 /// Returns an error if building the operation fails.
373 ///
374 /// # Returns
375 ///
376 /// The wire representing the Option<elemty, array<SIZE-1, elemty>>
377 fn add_array_pop_right(
378 &mut self,
379 elem_ty: Type,
380 size: u64,
381 input: Wire,
382 ) -> Result<Wire, BuildError> {
383 self.add_generic_array_pop_right::<Array>(elem_ty, size, input)
384 }
385
386 /// Adds an operation to discard an empty array from the dataflow graph.
387 ///
388 /// # Arguments
389 ///
390 /// * `elem_ty` - The type of the elements in the array.
391 /// * `input` - The wire representing the array.
392 ///
393 /// # Errors
394 ///
395 /// Returns an error if building the operation fails.
396 fn add_array_discard_empty(&mut self, elem_ty: Type, input: Wire) -> Result<(), BuildError> {
397 self.add_generic_array_discard_empty::<Array>(elem_ty, input)
398 }
399}
400
401impl<D: Dataflow> ArrayOpBuilder for D {}
402
403#[cfg(test)]
404mod test {
405 use crate::builder::{inout_sig, DFGBuilder, Dataflow, DataflowHugr};
406 use crate::extension::prelude::qb_t;
407
408 use super::{array_type, new_array_op};
409
410 #[test]
411 /// Test building a HUGR involving a new_array operation.
412 fn test_new_array() {
413 let mut b =
414 DFGBuilder::new(inout_sig(vec![qb_t(), qb_t()], array_type(2, qb_t()))).unwrap();
415
416 let [q1, q2] = b.input_wires_arr();
417
418 let op = new_array_op(qb_t(), 2);
419
420 let out = b.add_dataflow_op(op, [q1, q2]).unwrap();
421
422 b.finish_hugr_with_outputs(out.outputs()).unwrap();
423 }
424}