Skip to content
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

Adds an array reference type #1440

Merged
merged 34 commits into from
Mar 18, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
e1ba6ca
Adds an array reference type.
akern40 Oct 4, 2024
cdf5012
Adds some documentation
akern40 Oct 4, 2024
5a5a39c
Fixes some CI/CD issues
akern40 Oct 5, 2024
1acf0e1
More CI/CD fixes
akern40 Oct 5, 2024
09800eb
Last CI/CD fix?
akern40 Oct 5, 2024
0d0a96a
Switches to the "same-repr" approach to allow array views to still be…
akern40 Oct 6, 2024
0f3f8d9
Changes back to Copy-capable ArrayBase and unchanged ArrayBase internals
akern40 Oct 6, 2024
fbafbb2
Removes unused import
akern40 Oct 6, 2024
52b276e
Introduces LayoutRef and starts to move functionality from ArrayBase …
akern40 Oct 11, 2024
a504217
Working version of the conversion to reference types
akern40 Oct 13, 2024
aa58113
Uses a new design with two reference types
akern40 Oct 13, 2024
9a2edb1
Satisfying CI/CD
akern40 Oct 13, 2024
e739d04
Adds Index, equality, IntoIterator, and Hash traits, plus approximates
akern40 Oct 13, 2024
fb7d87c
Finishes conversions of all possible ArrayBase impls to ArrayRef
akern40 Oct 14, 2024
fb24680
Satisfying CI/CD
akern40 Oct 14, 2024
bdc24bd
Adds some aliases to avoid breaking changes, and adds an example of h…
akern40 Oct 14, 2024
c72bf8f
Adds Borrow and ToOwned
akern40 Oct 14, 2024
2cce543
Tests that the *Assign operators work for slices via deref
akern40 Oct 14, 2024
7f70e9e
Somehow missed a `use` for `ToOwned`
akern40 Oct 14, 2024
c835680
Implicitly use deref
akern40 Oct 14, 2024
03fda7f
Adds documentation and aliases for `LayoutRef`
akern40 Oct 20, 2024
407b6c4
Fixes doc links
akern40 Oct 20, 2024
a9b5c37
Adds formatting for ArrayRef
akern40 Oct 20, 2024
8a26c34
Change some examples over to ArrayRef
akern40 Oct 20, 2024
8bb8f39
Adds missed #[repr(transparent)] for RawRef
akern40 Oct 20, 2024
775f06b
Simplifies deref logic and avoids null check
akern40 Oct 20, 2024
6eed17d
Adds documentation to ArrayRef
akern40 Oct 20, 2024
9c9d8c2
Adds missing aliases to ArrayBase from RawRef and LayoutRef
akern40 Oct 21, 2024
4e0bcc1
Adds a short snippet of documentation for `RawRef` and some top-level…
akern40 Oct 21, 2024
1bb06e7
Makes as_ref more explicit through methods on ArrayBase
akern40 Oct 26, 2024
435a9e7
Restore remove_index to DataOwned
akern40 Oct 26, 2024
6fd61c1
Fixes unused imports
akern40 Oct 26, 2024
0dedc3e
Re-fixes the covariant arrayview with the new reference structure
akern40 Mar 18, 2025
a71b509
Fixes the clippy lint and docs broken link
akern40 Mar 18, 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
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@ target/
# Editor settings
.vscode
.idea

# Apple details
**/.DS_Store
2 changes: 1 addition & 1 deletion crates/blas-tests/tests/oper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ fn gen_mat_mul()
cv = c.view_mut();
}

let answer_part = alpha * reference_mat_mul(&av, &bv) + beta * &cv;
let answer_part: Array<f64, _> = alpha * reference_mat_mul(&av, &bv) + beta * &cv;
answer.slice_mut(s![..;s1, ..;s2]).assign(&answer_part);

general_mat_mul(alpha, &av, &bv, beta, &mut cv);
Expand Down
2 changes: 1 addition & 1 deletion examples/axis_ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use ndarray::prelude::*;
/// it corresponds to their order in memory.
///
/// Errors if array has a 0-stride axis
fn regularize<A, D>(a: &mut Array<A, D>) -> Result<(), &'static str>
fn regularize<A, D>(a: &mut ArrayRef<A, D>) -> Result<(), &'static str>
where
D: Dimension,
A: ::std::fmt::Debug,
Expand Down
2 changes: 1 addition & 1 deletion examples/convo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type Kernel3x3<A> = [[A; 3]; 3];

#[inline(never)]
#[cfg(feature = "std")]
fn conv_3x3<F>(a: &ArrayView2<'_, F>, out: &mut ArrayViewMut2<'_, F>, kernel: &Kernel3x3<F>)
fn conv_3x3<F>(a: &ArrayRef2<F>, out: &mut ArrayRef2<F>, kernel: &Kernel3x3<F>)
where F: Float
{
let (n, m) = a.dim();
Expand Down
178 changes: 178 additions & 0 deletions examples/functions_and_traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
//! Examples of how to write functions and traits that operate on `ndarray` types.
//!
//! `ndarray` has four kinds of array types that users may interact with:
//! 1. [`ArrayBase`], the owner of the layout that describes an array in memory;
//! this includes [`ndarray::Array`], [`ndarray::ArcArray`], [`ndarray::ArrayView`],
//! [`ndarray::RawArrayView`], and other variants.
//! 2. [`ArrayRef`], which represents a read-safe, uniquely-owned look at an array.
//! 3. [`RawRef`], which represents a read-unsafe, possibly-shared look at an array.
//! 4. [`LayoutRef`], which represents a look at an array's underlying structure,
//! but does not allow data reading of any kind.
//!
//! Below, we illustrate how to write functions and traits for most variants of these types.

use ndarray::{ArrayBase, ArrayRef, Data, DataMut, Dimension, LayoutRef, RawData, RawDataMut, RawRef};

/// Take an array with the most basic requirements.
///
/// This function takes its data as owning. It is very rare that a user will need to specifically
/// take a reference to an `ArrayBase`, rather than to one of the other four types.
#[rustfmt::skip]
fn takes_base_raw<S: RawData, D>(arr: ArrayBase<S, D>) -> ArrayBase<S, D>
{
// These skip from a possibly-raw array to `RawRef` and `LayoutRef`, and so must go through `AsRef`
takes_rawref(arr.as_ref()); // Caller uses `.as_ref`
takes_rawref_asref(&arr); // Implementor uses `.as_ref`
takes_layout(arr.as_ref()); // Caller uses `.as_ref`
takes_layout_asref(&arr); // Implementor uses `.as_ref`

arr
}

/// Similar to above, but allow us to read the underlying data.
#[rustfmt::skip]
fn takes_base_raw_mut<S: RawDataMut, D>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D>
{
// These skip from a possibly-raw array to `RawRef` and `LayoutRef`, and so must go through `AsMut`
takes_rawref_mut(arr.as_mut()); // Caller uses `.as_mut`
takes_rawref_asmut(&mut arr); // Implementor uses `.as_mut`
takes_layout_mut(arr.as_mut()); // Caller uses `.as_mut`
takes_layout_asmut(&mut arr); // Implementor uses `.as_mut`

arr
}

/// Now take an array whose data is safe to read.
#[allow(dead_code)]
fn takes_base<S: Data, D>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D>
{
// Raw call
arr = takes_base_raw(arr);

// No need for AsRef, since data is safe
takes_arrref(&arr);
takes_rawref(&arr);
takes_rawref_asref(&arr);
takes_layout(&arr);
takes_layout_asref(&arr);

arr
}

/// Now, an array whose data is safe to read and that we can mutate.
///
/// Notice that we include now a trait bound on `D: Dimension`; this is necessary in order
/// for the `ArrayBase` to dereference to an `ArrayRef` (or to any of the other types).
#[allow(dead_code)]
fn takes_base_mut<S: DataMut, D: Dimension>(mut arr: ArrayBase<S, D>) -> ArrayBase<S, D>
{
// Raw call
arr = takes_base_raw_mut(arr);

// No need for AsMut, since data is safe
takes_arrref_mut(&mut arr);
takes_rawref_mut(&mut arr);
takes_rawref_asmut(&mut arr);
takes_layout_mut(&mut arr);
takes_layout_asmut(&mut arr);

arr
}

/// Now for new stuff: we want to read (but not alter) any array whose data is safe to read.
///
/// This is probably the most common functionality that one would want to write.
/// As we'll see below, calling this function is very simple for `ArrayBase<S: Data, D>`.
fn takes_arrref<A, D>(arr: &ArrayRef<A, D>)
{
// No need for AsRef, since data is safe
takes_rawref(arr);
takes_rawref_asref(arr);
takes_layout(arr);
takes_layout_asref(arr);
}

/// Now we want any array whose data is safe to mutate.
///
/// **Importantly**, any array passed to this function is guaranteed to uniquely point to its data.
/// As a result, passing a shared array to this function will **silently** un-share the array.
#[allow(dead_code)]
fn takes_arrref_mut<A, D>(arr: &mut ArrayRef<A, D>)
{
// Immutable call
takes_arrref(arr);

// No need for AsMut, since data is safe
takes_rawref_mut(arr);
takes_rawref_asmut(arr);
takes_layout_mut(arr);
takes_rawref_asmut(arr);
}

/// Now, we no longer care about whether we can safely read data.
///
/// This is probably the rarest type to deal with, since `LayoutRef` can access and modify an array's
/// shape and strides, and even do in-place slicing. As a result, `RawRef` is only for functionality
/// that requires unsafe data access, something that `LayoutRef` can't do.
///
/// Writing functions and traits that deal with `RawRef`s and `LayoutRef`s can be done two ways:
/// 1. Directly on the types; calling these functions on arrays whose data are not known to be safe
/// to dereference (i.e., raw array views or `ArrayBase<S: RawData, D>`) must explicitly call `.as_ref()`.
/// 2. Via a generic with `: AsRef<RawRef<A, D>>`; doing this will allow direct calling for all `ArrayBase` and
/// `ArrayRef` instances.
/// We'll demonstrate #1 here for both immutable and mutable references, then #2 directly below.
#[allow(dead_code)]
fn takes_rawref<A, D>(arr: &RawRef<A, D>)
{
takes_layout(arr);
takes_layout_asref(arr);
}

/// Mutable, directly take `RawRef`
#[allow(dead_code)]
fn takes_rawref_mut<A, D>(arr: &mut RawRef<A, D>)
{
takes_layout(arr);
takes_layout_asmut(arr);
}

/// Immutable, take a generic that implements `AsRef` to `RawRef`
#[allow(dead_code)]
fn takes_rawref_asref<T, A, D>(_arr: &T)
where T: AsRef<RawRef<A, D>>
{
takes_layout(_arr.as_ref());
takes_layout_asref(_arr.as_ref());
}

/// Mutable, take a generic that implements `AsMut` to `RawRef`
#[allow(dead_code)]
fn takes_rawref_asmut<T, A, D>(_arr: &mut T)
where T: AsMut<RawRef<A, D>>
{
takes_layout_mut(_arr.as_mut());
takes_layout_asmut(_arr.as_mut());
}

/// Finally, there's `LayoutRef`: this type provides read and write access to an array's *structure*, but not its *data*.
///
/// Practically, this means that functions that only read/modify an array's shape or strides,
/// such as checking dimensionality or slicing, should take `LayoutRef`.
///
/// Like `RawRef`, functions can be written either directly on `LayoutRef` or as generics with `: AsRef<LayoutRef<A, D>>>`.
#[allow(dead_code)]
fn takes_layout<A, D>(_arr: &LayoutRef<A, D>) {}

/// Mutable, directly take `LayoutRef`
#[allow(dead_code)]
fn takes_layout_mut<A, D>(_arr: &mut LayoutRef<A, D>) {}

/// Immutable, take a generic that implements `AsRef` to `LayoutRef`
#[allow(dead_code)]
fn takes_layout_asref<T: AsRef<LayoutRef<A, D>>, A, D>(_arr: &T) {}

/// Mutable, take a generic that implements `AsMut` to `LayoutRef`
#[allow(dead_code)]
fn takes_layout_asmut<T: AsMut<LayoutRef<A, D>>, A, D>(_arr: &mut T) {}

fn main() {}
Loading
Loading