ndarray
0.11.0 was just released. ndarray
is a Rust crate that
provides an n-dimensional array type ArrayBase
for general elements and
for numerics. Arrays are n-dimensional, so they can represent vectors (1
axis), matrices (2 axes), etc., to n axes. ndarray
provides methods for
n-dimensional slicing, iteration, view-taking, and some mathematical
operations.
ndarray
0.11 has an exciting new feature: combined slicing and subviews!
Combined slicing and subviews
The biggest new feature in ndarray
0.11 is the ability to combine slicing and
subviews in a single operation. To get the same result in earlier versions of
ndarray
, you’d have to chain together multiple .slice()
and
.into_subview()
calls while being careful with the order
of the calls. For example,
let arr = array![[[ 1, 2, 3, 4],
[ 5, 6, 7, 8]],
[[ 9, 10, 11, 12],
[13, 14, 15, 16]]];
assert_eq!(arr.shape(), &[2, 2, 4]);
// old
let slice = arr.slice(s![.., .., 1..;2])
.into_subview(Axis(1), 0)
.into_subview(Axis(0), 1);
// new
let slice = arr.slice(s![1, 0, 1..;2]);
assert_eq!(slice.shape(), &[2]);
assert_eq!(slice, array![10, 12]);
In this example, the 1..;2
indicates a slice of axis 2 (starting at index 1
and stepping by 2), and the 1
and 0
indicate subviews of axes 0 and 1,
respectively. The resulting slice
is a view of a subset of arr
(without
copying). The new version is more concise and is easier to use correctly than a
series of .slice()
and .into_subview()
calls.
You’ll still get a compile-time error if the dimensionality of the array/view
being sliced doesn’t match the number of arguments in the s![]
macro, and the dimensionality of the sliced array/view is still determined at
compile-time.
You may be interested in how this feature was implemented. It’s a story of type hacking: zero-sized types, associated types, traits for compile-time type manipulation, and pointer casting.
Other new features
ArrayBase
has new .slice_axis()
, .slice_axis_mut()
, and
.slice_axis_inplace()
methods to slice individual axes. These methods
simplify functions that slice generic-dimensional arrays/views. For example,
// old
fn reverse_first_axis<A, D: Dimension>(view: ArrayView<A, D>) -> ArrayView<A, D> {
let mut slice_info = vec![Si(0, None, 1); view.ndim()];
slice_info[0].2 = -1;
let mut view_dyn = view.into_dyn();
view_dyn.islice(&slice_info);
view_dyn.into_dimensionality().unwrap()
}
// new
fn reverse_first_axis<A, D: Dimension>(mut view: ArrayView<A, D>) -> ArrayView<A, D> {
view.slice_axis_inplace(Axis(0), Slice::new(0, None, -1));
view
}
ArrayBase
has a new .slice_move()
method which consumes the array/view
and returns a new one. This method is necessary to be able to change the number
of dimensions when slicing an owned array into a smaller owned array. (The
.slice_inplace()
method (called .islice()
in 0.10) can’t change the
number of dimensions because it can’t change the type of its argument.) Adding
.slice_move()
also has the nice side-effect that some uses of slicing are now
a little more ergonomic, especially when taking ownership of ArrayView
s. For
example,
// old
fn reverse_view<A>(mut view: ArrayView1<A>) -> ArrayView1<A> {
view.islice(s![..;-1]);
view
}
// new
fn reverse_view<A>(view: ArrayView1<A>) -> ArrayView1<A> {
view.slice_move(s![..;-1])
}
The s![]
macro now supports more index types, such as usize
, so
you no longer have to manually cast usize
indices to isize
.
We’ve added many other new features since the last release announcement. See the full release notes for more!
Updating existing code
There are breaking changes in ndarray
0.11, but updating most code written
for 0.10 is fairly straightforward. The most common updates will be:
- Rename
.islice()
to.slice_inplace()
. - Rename
.isubview()
to.subview_inplace()
.
If you explicitly used the old Si
type, switch to using the
s![]
macro if possible. If you need to pass around slicing
information for a single axis, you can use the new Slice
type, which is
very similar to the old Si
type and can be used as an argument to the s![]
macro and the .slice_axis*()
methods. If s![]
doesn’t work for your
use-case, SliceInfo
is analogous to the old [Si; n]
type and can be
constructed manually from an array/slice/Vec
of SliceOrIndex
.
The output of s![]
may not automatically coerce into the parameter type for
.slice*()
in all of the places it did in earlier versions of ndarray
(primarily when dealing with dynamic-dimensional arrays). You can call
.as_ref()
on the output of s![]
to perform the necessary conversion.
See the full release notes for more.
Thanks
Many thanks to bluss for maintaining ndarray
and helping me (jturner314)
refine the design of the new features, and, of course, for creating ndarray
in the first place! Thanks also to various people on IRC for answering my Rust
questions.