mdarray/
lib.rs

1//! # Multidimensional array for Rust
2//!
3//! ## Overview
4//!
5//! The mdarray crate provides a multidimensional array for Rust. Its main target
6//! is for numeric types, however generic types are supported as well. The purpose
7//! is to provide a generic container type that is simple and flexible to use,
8//! with interworking to other crates for e.g. BLAS/LAPACK functionality.
9//!
10//! Here are the main features of mdarray:
11//!
12//! - Dense array type, where the rank is known at compile time.
13//! - Static or dynamic array dimensions, with optional inline storage.
14//! - Standard Rust mechanisms are used for e.g. indexing and iteration.
15//! - Generic expressions for multidimensional iteration.
16//!
17//! The design is inspired from other Rust crates (ndarray, nalgebra, bitvec, dfdx
18//! and candle), the proposed C++ mdarray and mdspan types, and multidimensional
19//! arrays in other languages.
20//!
21//! ## Array types
22//!
23//! The basic array type is `Tensor` for a dense array that owns the storage,
24//! similar to the Rust `Vec` type. It is parameterized by the element type,
25//! the shape (i.e. the size of each dimension) and optionally an allocator.
26//!
27//! `Array` is a dense array which stores elements inline, similar to the Rust
28//! `array` type. The shape must consist of dimensions with constant size.
29//!
30//! `View` and `ViewMut` are array types that refer to a parent array. They are
31//! used for example when creating array views without duplicating elements.
32//!
33//! `Slice` is a generic array reference, similar to the Rust `slice` type.
34//! It consists of a pointer to an internal structure that holds the storage
35//! and the layout mapping. All arrays can be dereferenced to an array slice.
36//!
37//! The following type aliases are provided:
38//!
39//! - `DTensor<T, const N: usize, ...>` for a dense array with a given rank.
40//! - `DSlice<T, const N: usize, ...>` for an array slice with a given rank.
41//!
42//! The rank can be dynamic using the `DynRank` shape type. This is the default
43//! for array types if no shape is specified.
44//!
45//! The layout mapping describes how elements are stored in memory. The mapping
46//! is parameterized by the shape and the layout. It contains the dynamic size
47//! and stride per dimension when needed.
48//!
49//! The layout is `Dense` if elements are stored contiguously without gaps, and
50//! it is `Strided` if all dimensions can have arbitrary strides.
51//!
52//! The array elements are stored in row-major or C order, where the first
53//! dimension is the outermost one.
54//!
55//! ## Indexing and views
56//!
57//! Scalar indexing is done using the normal square-bracket index operator and
58//! an array of `usize` per dimension as index. A scalar `usize` can be used for
59//! linear indexing. If the layout is `Dense`, a range can also be used to select
60//! a slice.
61//!
62//! An array view can be created with the `view` and `view_mut` methods, which
63//! take indices per dimension as arguments. Each index can be either a range
64//! or `usize`. The resulting array layout depends on both the layout inferred
65//! from the indices and the input layout.
66//!
67//! For two-dimensional arrays, a view of one column or row can be created with
68//! the `col`, `col_mut`, `row` and `row_mut` methods, and a view of the diagonal
69//! with `diag` and `diag_mut`.
70//!
71//! If the array layout is not known, `remap`, `remap_mut` and `into_mapping` can
72//! be used to change layout.
73//!
74//! ## Iteration
75//!
76//! An iterator can be created from an array with the `iter`, `iter_mut` and
77//! `into_iter` methods like for `Vec` and `slice`.
78//!
79//! Expressions are similar to iterators, but support multidimensional iteration
80//! and have consistency checking of shapes. An expression is created with the
81//! `expr`, `expr_mut` and `into_expr` methods. Note that the array types `View`
82//! and `ViewMut` are also expressions.
83//!
84//! There are methods for for evaluating expressions or converting into other
85//! expressions, such as `eval`, `for_each` and `map`. Two expressions can be
86//! merged to an expression of tuples with the `zip` method or free function.
87//!
88//! When merging expressions, if the rank differs the expression with the lower
89//! rank is broadcast into the larger shape by adding outer dimensions. It is not
90//! possible to broadcast mutable arrays or when moving elements out of an array.
91//!
92//! For multidimensional arrays, iteration over a single dimension can be done
93//! with `outer_expr`, `outer_expr_mut`, `axis_expr` and `axis_expr_mut`.
94//! The resulting expressions give array views of the remaining dimensions.
95//!
96//! It is also possible to iterate over all except one dimension with `cols`,
97//! `cols_mut`, `lanes`, `lanes_mut`, `rows` and `rows_mut`.
98//!
99//! ## Operators
100//!
101//! Arithmetic, logical, negation, comparison and compound assignment operators
102//! are supported for arrays and expressions.
103//!
104//! If at least one of the inputs is an array that is passed by value, the
105//! operation is evaluated directly and the input array is reused for the result.
106//! Otherwise, if all input parameters are array references or expressions, an
107//! expression is returned. In the latter case, the result may have a different
108//! element type.
109//!
110//! For comparison operators, the parameters must always be arrays that are passed
111//! by reference. For compound assignment operators, the first parameter is always
112//! a mutable reference to an array where the result is stored.
113//!
114//! Scalar parameters must passed using the `fill` function that wraps a value in
115//! an `Fill<T>` expression. If a type does not implement the `Copy` trait, the
116//! parameter must be passed by reference.
117//!
118//! ## Example
119//!
120//! This example implements matrix multiplication and addition `C = A * B + C`.
121//! The matrices use row-major ordering, and the inner loop runs over one row in
122//! `B` and `C`. By using iterator-like expressions the array bounds checking is
123//! avoided, and the compiler is able to vectorize the inner loop.
124//!
125//! ```
126//! use mdarray::{expr::Expression, tensor, view, DSlice};
127//!
128//! fn matmul(a: &DSlice<f64, 2>, b: &DSlice<f64, 2>, c: &mut DSlice<f64, 2>) {
129//!     for (mut ci, ai) in c.rows_mut().zip(a.rows()) {
130//!         for (aik, bk) in ai.expr().zip(b.rows()) {
131//!             for (cij, bkj) in ci.expr_mut().zip(bk) {
132//!                 *cij = aik.mul_add(*bkj, *cij);
133//!             }
134//!         }
135//!     }
136//! }
137//!
138//! let a = view![[1.0, 4.0], [2.0, 5.0], [3.0, 6.0]];
139//! let b = view![[0.0, 1.0], [1.0, 1.0]];
140//!
141//! let mut c = tensor![[0.0; 2]; 3];
142//!
143//! matmul(&a, &b, &mut c);
144//!
145//! assert_eq!(c, view![[4.0, 5.0], [5.0, 7.0], [6.0, 9.0]]);
146//! ```
147
148#![allow(clippy::comparison_chain)]
149#![allow(clippy::needless_range_loop)]
150#![cfg_attr(feature = "nightly", feature(allocator_api))]
151#![cfg_attr(feature = "nightly", feature(extern_types))]
152#![cfg_attr(feature = "nightly", feature(hasher_prefixfree_extras))]
153#![cfg_attr(feature = "nightly", feature(impl_trait_in_assoc_type))]
154#![cfg_attr(feature = "nightly", feature(macro_metavar_expr))]
155#![cfg_attr(feature = "nightly", feature(slice_range))]
156#![warn(missing_docs)]
157#![warn(unreachable_pub)]
158#![warn(unused_results)]
159
160pub mod expr;
161pub mod index;
162
163mod array;
164mod dim;
165mod layout;
166mod macros;
167mod mapping;
168mod ops;
169mod raw_slice;
170mod raw_tensor;
171mod shape;
172mod slice;
173mod tensor;
174mod traits;
175mod view;
176
177#[cfg(feature = "serde")]
178mod serde;
179
180#[cfg(not(feature = "nightly"))]
181mod alloc {
182    pub trait Allocator {}
183
184    #[derive(Copy, Clone, Default, Debug)]
185    pub struct Global;
186
187    impl Allocator for Global {}
188}
189
190pub use array::Array;
191pub use dim::{Const, Dim, Dyn};
192pub use layout::{Dense, Layout, Strided};
193pub use mapping::{DenseMapping, Mapping, StridedMapping};
194pub use ops::{StepRange, step};
195pub use shape::{ConstShape, DynRank, IntoShape, Rank, Shape};
196pub use slice::{DSlice, Slice};
197pub use tensor::{DTensor, Tensor};
198pub use traits::{IntoCloned, Owned};
199pub use view::{DView, DViewMut, View, ViewMut};