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

Open
wants to merge 32 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
99722fa
Adds an array reference type.
akern40 Oct 4, 2024
44079cc
Adds some documentation
akern40 Oct 4, 2024
3d32c08
Fixes some CI/CD issues
akern40 Oct 5, 2024
8bf86df
More CI/CD fixes
akern40 Oct 5, 2024
dda4b66
Last CI/CD fix?
akern40 Oct 5, 2024
3c75150
Switches to the "same-repr" approach to allow array views to still be…
akern40 Oct 6, 2024
28fea66
Changes back to Copy-capable ArrayBase and unchanged ArrayBase internals
akern40 Oct 6, 2024
3a180c1
Removes unused import
akern40 Oct 6, 2024
6b64678
Introduces LayoutRef and starts to move functionality from ArrayBase …
akern40 Oct 11, 2024
4c440a8
Working version of the conversion to reference types
akern40 Oct 13, 2024
c183903
Uses a new design with two reference types
akern40 Oct 13, 2024
476dac5
Satisfying CI/CD
akern40 Oct 13, 2024
68d6f53
Adds Index, equality, IntoIterator, and Hash traits, plus approximates
akern40 Oct 13, 2024
2fde998
Finishes conversions of all possible ArrayBase impls to ArrayRef
akern40 Oct 14, 2024
d63d26e
Satisfying CI/CD
akern40 Oct 14, 2024
b7281f5
Adds some aliases to avoid breaking changes, and adds an example of h…
akern40 Oct 14, 2024
663e2f9
Adds Borrow and ToOwned
akern40 Oct 14, 2024
5204f68
Tests that the *Assign operators work for slices via deref
akern40 Oct 14, 2024
6a3d131
Somehow missed a `use` for `ToOwned`
akern40 Oct 14, 2024
289130d
Implicitly use deref
akern40 Oct 14, 2024
db52eab
Adds documentation and aliases for `LayoutRef`
akern40 Oct 20, 2024
2b34bf8
Fixes doc links
akern40 Oct 20, 2024
a40307b
Adds formatting for ArrayRef
akern40 Oct 20, 2024
5adbb31
Change some examples over to ArrayRef
akern40 Oct 20, 2024
6e61e83
Adds missed #[repr(transparent)] for RawRef
akern40 Oct 20, 2024
0cd4334
Simplifies deref logic and avoids null check
akern40 Oct 20, 2024
f77ad96
Adds documentation to ArrayRef
akern40 Oct 20, 2024
3888399
Adds missing aliases to ArrayBase from RawRef and LayoutRef
akern40 Oct 21, 2024
0a3cad3
Adds a short snippet of documentation for `RawRef` and some top-level…
akern40 Oct 21, 2024
cbed837
Makes as_ref more explicit through methods on ArrayBase
akern40 Oct 26, 2024
945f70d
Restore remove_index to DataOwned
akern40 Oct 26, 2024
93dfb38
Fixes unused imports
akern40 Oct 26, 2024
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 @@ -5,3 +5,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;
nilgoyette marked this conversation as resolved.
Show resolved Hide resolved
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.
nilgoyette marked this conversation as resolved.
Show resolved Hide resolved
//!
//! `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