Skip to content

feat: Add array linearisation pass #2125

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 36 commits into from
May 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
dc638c4
feat: Make arrays linear and add copyable value arrays
mark-koch Apr 22, 2025
9370dc7
feat: Add array clone and discard ops
mark-koch Apr 22, 2025
96ab540
feat: Add value_array to/from array conversion ops
mark-koch Apr 22, 2025
64cf8a4
Fix ops and export op names
mark-koch Apr 23, 2025
40582db
Return result instead of option and add to docs
mark-koch Apr 23, 2025
98a5691
Merge remote-tracking branch 'origin/feat/arrays' into arrays/clone-d…
mark-koch Apr 23, 2025
c04f921
Merge remote-tracking branch 'origin/arrays/clone-discard' into array…
mark-koch Apr 23, 2025
5e4698b
feat: Make array.get give back the passed array
mark-koch Apr 23, 2025
364c9d4
feat: Update array LLVM lowering to use malloc
mark-koch Apr 23, 2025
c9c9968
Use memcpy intrinsic
mark-koch Apr 24, 2025
ad36fcd
Move discard/clone logic into free functions
mark-koch Apr 24, 2025
8e6aa94
Rename to fat pointer
mark-koch Apr 24, 2025
9c7e486
Use i32 instead of usize to populate array values
mark-koch Apr 24, 2025
8a289df
Use ArrayCodegen::emit_free instead of libc version
mark-koch Apr 24, 2025
bd1305d
Use more neutral names instead of malloc/free
mark-koch Apr 24, 2025
531cf17
Add docs and make some functions public
mark-koch Apr 24, 2025
a7a355b
Don't forget to free the array after a scan
mark-koch Apr 24, 2025
4bc3f04
Pass op to emit_array_discard
mark-koch Apr 24, 2025
927a061
Add docs to ArrayCodegen
mark-koch Apr 24, 2025
5318ae6
Merge remote-tracking branch 'origin/feat/arrays' into arrays/conversion
mark-koch Apr 24, 2025
36ab2ed
Merge remote-tracking branch 'origin/arrays/conversion' into arrays/get
mark-koch Apr 24, 2025
64a032f
Merge branch 'feat/arrays' into arrays/conversion
mark-koch Apr 24, 2025
bf394b6
Merge branch 'arrays/conversion' into arrays/get
mark-koch Apr 24, 2025
ddb92ce
Merge branch 'arrays/get' into arrays/llvm
mark-koch Apr 24, 2025
03d5320
feat: Generalise array op builers to arbitrary array kinds
mark-koch Apr 28, 2025
fdaf8b0
feat: Add array linearisation pass
mark-koch Apr 28, 2025
f7d64e1
Error on invalid get
mark-koch Apr 29, 2025
ddaf7bf
Fix
mark-koch Apr 29, 2025
b3c8277
Take offset into account for pop_right
mark-koch Apr 29, 2025
d964b61
Clippy
mark-koch Apr 29, 2025
83a4de8
Merge remote-tracking branch 'origin/feat/arrays' into arrays/llvm
mark-koch Apr 29, 2025
ddb56b9
Merge branch 'arrays/llvm' into arrays/builder
mark-koch Apr 29, 2025
eb4bd39
Merge remote-tracking branch 'origin/feat/arrays' into arrays/builder
mark-koch Apr 29, 2025
4aa2560
Add LinearizeArrayPass::new
mark-koch Apr 30, 2025
2e972e6
Document panic conditions and clarify comment
mark-koch Apr 30, 2025
15048a9
Merge branch 'arrays/builder' into arrays/pass
mark-koch May 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

238 changes: 235 additions & 3 deletions hugr-core/src/std_extensions/collections/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ use std::sync::Arc;
use delegate::delegate;
use lazy_static::lazy_static;

use crate::builder::{BuildError, Dataflow};
use crate::extension::resolution::{ExtensionResolutionError, WeakExtensionRegistry};
use crate::extension::simple_op::{MakeOpDef, MakeRegisteredOp};
use crate::extension::simple_op::{HasConcrete, MakeOpDef, MakeRegisteredOp};
use crate::extension::{ExtensionId, ExtensionSet, SignatureError, TypeDef, TypeDefBound};
use crate::ops::constant::{CustomConst, ValueName};
use crate::ops::{ExtensionOp, OpName};
use crate::types::type_param::{TypeArg, TypeParam};
use crate::types::{CustomCheckFailure, Type, TypeBound, TypeName};
use crate::Extension;
use crate::{Extension, Wire};

pub use array_clone::{GenericArrayClone, GenericArrayCloneDef, ARRAY_CLONE_OP_ID};
pub use array_conversion::{Direction, GenericArrayConvert, GenericArrayConvertDef, FROM, INTO};
Expand All @@ -32,7 +33,8 @@ pub use array_op::{GenericArrayOp, GenericArrayOpDef};
pub use array_repeat::{GenericArrayRepeat, GenericArrayRepeatDef, ARRAY_REPEAT_OP_ID};
pub use array_scan::{GenericArrayScan, GenericArrayScanDef, ARRAY_SCAN_OP_ID};
pub use array_value::GenericArrayValue;
pub use op_builder::ArrayOpBuilder;

use op_builder::GenericArrayOpBuilder;

/// Reported unique name of the array type.
pub const ARRAY_TYPENAME: TypeName = TypeName::new_inline("array");
Expand Down Expand Up @@ -170,6 +172,236 @@ pub fn new_array_op(element_ty: Type, size: u64) -> ExtensionOp {
op.to_extension_op().unwrap()
}

/// Trait for building array operations in a dataflow graph.
pub trait ArrayOpBuilder: GenericArrayOpBuilder {
/// Adds a new array operation to the dataflow graph and return the wire
/// representing the new array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `values` - An iterator over the values to initialize the array with.
///
/// # Errors
///
/// If building the operation fails.
///
/// # Returns
///
/// The wire representing the new array.
fn add_new_array(
&mut self,
elem_ty: Type,
values: impl IntoIterator<Item = Wire>,
) -> Result<Wire, BuildError> {
self.add_new_generic_array::<Array>(elem_ty, values)
}

/// Adds an array clone operation to the dataflow graph and return the wires
/// representing the originala and cloned array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
///
/// # Errors
///
/// If building the operation fails.
///
/// # Returns
///
/// The wires representing the original and cloned array.
fn add_array_clone(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
) -> Result<(Wire, Wire), BuildError> {
self.add_generic_array_clone::<Array>(elem_ty, size, input)
}

/// Adds an array discard operation to the dataflow graph.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
///
/// # Errors
///
/// If building the operation fails.
fn add_array_discard(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
) -> Result<(), BuildError> {
self.add_generic_array_discard::<Array>(elem_ty, size, input)
}

/// Adds an array get operation to the dataflow graph.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
/// * `index` - The wire representing the index to get.
///
/// # Errors
///
/// If building the operation fails.
///
/// # Returns
///
/// * The wire representing the value at the specified index in the array
/// * The wire representing the array
fn add_array_get(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
index: Wire,
) -> Result<(Wire, Wire), BuildError> {
self.add_generic_array_get::<Array>(elem_ty, size, input, index)
}

/// Adds an array set operation to the dataflow graph.
///
/// This operation sets the value at a specified index in the array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
/// * `index` - The wire representing the index to set.
/// * `value` - The wire representing the value to set at the specified index.
///
/// # Errors
///
/// Returns an error if building the operation fails.
///
/// # Returns
///
/// The wire representing the updated array after the set operation.
fn add_array_set(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
index: Wire,
value: Wire,
) -> Result<Wire, BuildError> {
self.add_generic_array_set::<Array>(elem_ty, size, input, index, value)
}

/// Adds an array swap operation to the dataflow graph.
///
/// This operation swaps the values at two specified indices in the array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
/// * `index1` - The wire representing the first index to swap.
/// * `index2` - The wire representing the second index to swap.
///
/// # Errors
///
/// Returns an error if building the operation fails.
///
/// # Returns
///
/// The wire representing the updated array after the swap operation.
fn add_array_swap(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
index1: Wire,
index2: Wire,
) -> Result<Wire, BuildError> {
let op = GenericArrayOpDef::<Array>::swap.instantiate(&[size.into(), elem_ty.into()])?;
let [out] = self
.add_dataflow_op(op, vec![input, index1, index2])?
.outputs_arr();
Ok(out)
}

/// Adds an array pop-left operation to the dataflow graph.
///
/// This operation removes the leftmost element from the array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
///
/// # Errors
///
/// Returns an error if building the operation fails.
///
/// # Returns
///
/// The wire representing the Option<elemty, array<SIZE-1, elemty>>
fn add_array_pop_left(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
) -> Result<Wire, BuildError> {
self.add_generic_array_pop_left::<Array>(elem_ty, size, input)
}

/// Adds an array pop-right operation to the dataflow graph.
///
/// This operation removes the rightmost element from the array.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `size` - The size of the array.
/// * `input` - The wire representing the array.
///
/// # Errors
///
/// Returns an error if building the operation fails.
///
/// # Returns
///
/// The wire representing the Option<elemty, array<SIZE-1, elemty>>
fn add_array_pop_right(
&mut self,
elem_ty: Type,
size: u64,
input: Wire,
) -> Result<Wire, BuildError> {
self.add_generic_array_pop_right::<Array>(elem_ty, size, input)
}

/// Adds an operation to discard an empty array from the dataflow graph.
///
/// # Arguments
///
/// * `elem_ty` - The type of the elements in the array.
/// * `input` - The wire representing the array.
///
/// # Errors
///
/// Returns an error if building the operation fails.
fn add_array_discard_empty(&mut self, elem_ty: Type, input: Wire) -> Result<(), BuildError> {
self.add_generic_array_discard_empty::<Array>(elem_ty, input)
}
}

impl<D: Dataflow> ArrayOpBuilder for D {}

#[cfg(test)]
mod test {
use crate::builder::{inout_sig, DFGBuilder, Dataflow, DataflowHugr};
Expand Down
28 changes: 27 additions & 1 deletion hugr-core/src/std_extensions/collections/array/array_kind.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
use std::sync::Arc;

use crate::std_extensions::collections::array::op_builder::GenericArrayOpBuilder;
use crate::{
builder::{BuildError, Dataflow},
extension::{ExtensionId, SignatureError, TypeDef},
ops::constant::ValueName,
types::{CustomType, Type, TypeArg, TypeName},
Extension,
Extension, Wire,
};

/// Trait capturing a concrete array implementation in an extension.
Expand Down Expand Up @@ -90,4 +92,28 @@ pub trait ArrayKind:
) -> Result<Type, SignatureError> {
Self::instantiate_ty(Self::type_def(), size, element_ty)
}

/// Adds a operation to a dataflow graph that clones an array of copyable values.
///
/// The default implementation uses the array clone operation.
fn build_clone<D: Dataflow>(
builder: &mut D,
elem_ty: Type,
size: u64,
arr: Wire,
) -> Result<(Wire, Wire), BuildError> {
builder.add_generic_array_clone::<Self>(elem_ty, size, arr)
}

/// Adds a operation to a dataflow graph that clones an array of copyable values.
///
/// The default implementation uses the array clone operation.
fn build_discard<D: Dataflow>(
builder: &mut D,
elem_ty: Type,
size: u64,
arr: Wire,
) -> Result<(), BuildError> {
builder.add_generic_array_discard::<Self>(elem_ty, size, arr)
}
}
Loading
Loading