Matrix management
Creating a matrix
faer
provides several ways to create dense matrices and matrix views.
The main matrix types are Mat
, MatRef
and MatMut
,
which can be thought of as being analogous to Vec
, &[_]
and &mut [_]
.
Mat
owns its data, provides read-write access to it, and can be resized after creation.MatRef
provides read-only access to the underlying data.MatMut
provides read-write access to the underlying data.
The most flexible way to initialize a matrix is to initialize a zero matrix, then fill out the values by hand.
use faer::Mat;
let mut a = Mat::<f64>::zeros(4, 3);
for j in 0..a.ncols() {
for i in 0..a.nrows() {
a[(i, j)] = 9.0;
}
}
Given a callable object that outputs the matrix elements, Mat::from_fn
, can also be used.
use faer::Mat;
let a = Mat::from_fn(3, 4, |i, j| (i + j) as f64);
For common matrices such as the zero matrix and the identity matrix, shorthands are provided.
use faer::Mat;
// creates a 10×4 matrix whose values are all `0.0`.
let a = Mat::<f64>::zeros(10, 4);
// creates a 5×4 matrix containing `0.0` except on the main diagonal,
// which contains `1.0` instead.
let a = Mat::<f64>::identity(5, 4);
In some cases, users may wish to avoid the cost of initializing the matrix to zero, in which case, unsafe code may be used to allocate an uninitialized matrix, which can then be filled out before it's used.
// `a` is initially a 0×0 matrix.
let mut a = Mat::<f64>::with_capacity(4, 3);
// `a` is now a 4×3 matrix, whose values are uninitialized.
unsafe { a.set_dims(4, 3) };
for j in 0..a.ncols() {
for i in 0..a.nrows() {
// we cannot write `a[(i, j)] = 9.0`, as that would
// create a reference to uninitialized data,
// which is currently disallowed by Rust.
a.write(i, j, 9.0);
// we can also skip the bound checks using
// read_unchecked and write_unchecked
unsafe { a.write_unchecked(i, j, 9.0) };
}
}
Creating a matrix view
In some situations, it may be desirable to create a matrix view over existing
data.
In that case, we can use MatRef
(or MatMut
for
mutable views).
They can be created in a safe way using:
mat::from_column_major_slice
,mat::from_row_major_slice
,mat::from_column_major_slice_mut
,mat::from_row_major_slice_mut
,
for contiguous matrix storage, or:
mat::from_column_major_slice_with_stride
,mat::from_row_major_slice_with_stride
,mat::from_column_major_slice_with_stride_mut
,mat::from_row_major_slice_with_stride_mut
,
for strided matrix storage.
An unsafe lower level pointer API is also provided for handling uninitialized data
or arbitrary strides using mat::from_raw_parts
and mat::from_raw_parts_mut
.
Converting to a view
A Mat
instance m
can be converted to MatRef
or MatMut
by writing m.as_ref()
or m.as_mut()
.
Reborrowing a mutable view
Immutable matrix views can be freely copied around, since they are non-owning wrappers around a pointer and the matrix dimensions/strides.
Mutable matrices however are limited by Rust's borrow checker. Copying them would be unsound since only a single active mutable view is allowed at a time.
This means the following code does not compile.
use faer::{Mat, MatMut};
fn takes_view_mut(m: MatMut<f64>) {}
let mut a = Mat::<f64>::new();
let view = a.as_mut();
takes_view_mut(view);
// This would have failed to compile since `MatMut` is never `Copy`
// takes_view_mut(view);
The alternative is to temporarily give up ownership over the data, by creating a view with a shorter lifetime, then recovering the ownership when the view is no longer being used.
This is also called reborrowing.
use faer::{Mat, MatMut, MatRef};
use reborrow::*;
fn takes_view(m: MatRef<f64>) {}
fn takes_view_mut(m: MatMut<f64>) {}
let mut a = Mat::<f64>::new();
let mut view = a.as_mut();
takes_view_mut(view.rb_mut());
takes_view_mut(view.rb_mut());
takes_view(view.rb()); // We can also reborrow immutably
{
let short_view = view.rb_mut();
// This would have failed to compile since we can't use the original view
// while the reborrowed view is still being actively used
// takes_view_mut(view);
takes_view_mut(short_view);
}
// We can once again use the original view
takes_view_mut(view.rb_mut());
// Or consume it to convert it to an immutable view
takes_view(view.into_const());
Splitting a matrix view, slicing a submatrix
A matrix view can be split up along its row axis, column axis or both.
This is done using MatRef::split_at_row
, MatRef::split_at_col
or
MatRef::split_at
(or MatMut::split_at_row_mut
, MatMut::split_at_col_mut
or
MatMut::split_at_mut
).
These functions take the middle index at which the split is performed, and return the two sides (in top/bottom or left/right order) or the four corners (top left, top right, bottom left, bottom right)
We can also take a submatrix using MatRef::subrows
, MatRef::subcols
or
MatRef::submatrix
(or MatMut::subrows_mut
, MatMut::subcols_mut
or
MatMut::submatrix_mut
).
Alternatively, we can also use MatRef::get
or MatMut::get_mut
, which take
as parameters the row and column ranges.
⚠️Warning⚠️
Note that MatRef::submatrix
(and MatRef::subrows
, MatRef::subcols
) takes
as a parameter, the first row and column of the submatrix, then the number
of rows and columns of the submatrix.
On the other hand, MatRef::get
takes a range from the first row and column
to the last row and column.