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.