creating matrices
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.
note
while MatRef
and MatMut
can hold a view over a matrix with arbitrary strides, Mat
's layout is always column-major.
creating an owned matrix
faer provides a few macros for quickly creating small matrices as well as column and row vectors:
use faer::prelude::*;
let A = mat![
[1.0, 0.0, 0.0],
[0.0, 2.0, 0.0],
[0.0, 0.0, 3.0],
[0.0, 8.0, 0.0],
];
let x = col![1.0, 2.0, 3.0];
let y = row![1.0, 2.0, 3.0, 4.0];
// Ax is also a column vector
let Ax = &A * &x;
// yA is also a row vector
let yA = &y * &A;
for a more systematic way of creating a matrix with arbitrary entries, one can use Mat::from_fn
.
let A = Mat::from_fn(/* nrows: */ 4, /* ncols */ 3, |i, j| {
(i + j) as f64
});
helpers for common cases are provided as well
// A[(i, j)] == 0.0
let zero43 = Mat::<f64>::zeros(4, 3);
// A[(i, j)] == 1.0
let one43 = Mat::<f64>::ones(4, 3);
// A[(i, j)] == 2.5
let constant43 = Mat::full(4, 3, 2.5);
// A[(i, j)] == if i == j { 1.0 } else { 0.0 }
let identity43 = Mat::<f64>::identity(4, 3);
similar constructors are provided for the Col
and Row
types as well.
in rare cases, users may wish to avoid the cost of initializing the matrix to zero. in that 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.
unsafe { *A.ptr_inbounds_at_mut(i, j) = 9.0 };
}
}
creating a matrix view
in some situations, it may be desirable to create a matrix view over existing data instead of allocating new storage with Mat
.
in that case, we can use MatRef
(or MatMut
for mutable views).
they can be created in a safe way using:
-
MatRef::from_column_major_slice
, -
MatRef::from_row_major_slice
, -
MatMut::from_column_major_slice_mut
, -
MatMut::from_row_major_slice_mut
, -
MatRef::from_column_major_array
, -
MatRef::from_row_major_array
, -
MatMut::from_column_major_array_mut
, -
MatMut::from_row_major_array_mut
,
for contiguous matrix storage, or:
MatRef::from_column_major_slice_with_stride
,MatRef::from_row_major_slice_with_stride
,MatMut::from_column_major_slice_with_stride_mut
,MatMut::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 MatRef::from_raw_parts
and MatMut::from_raw_parts_mut
.
borrowing a matrix as a view
a Mat
A
can be converted to MatRef
or MatMut
by writing A.as_ref()
or A.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(A: 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(A: MatRef<'_, f64>) {}
fn takes_view_mut(A: 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 parameters: 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.