Vectors and matrices

Vector and matrices are the fundamental building blocks of any linear algebra library. Their sizes can either be known at compile-time or only at run-time. In either case, they allow common operations (additions, multiplications, etc.) to be used through operator overloading.

The generic Matrix type#

nalgebra represents both matrices and vectors of arbitrary dimensions (known at compile-time or at runtime), using the same generic Matrix structure. Vectors are just matrices with only one column. But in practice, users will generally rely on some more convenient aliased for this generic matrix type:

  • SMatrix<T, R, C> are statically-sized matrices with R rows and C columns, e.g., SMatrix<f32, 24, 10>.
  • SVector<T, D> is a statically-sized column vector with D rows, e.g., SVector<f32, 10>.
  • DVector<T> and DMatrix<T>: are respectively a dynamically-sized column vector and a dynamically-sized matrix.

Aliases exist for statically-sized vectors and matrices with small dimensions:

  • Vector1<T> .. Vector6<T>: are column vectors of dimension 1 to 6.
  • Matrix1<T> .. Matrix6<T>: are square matrices of dimension 1x1 to 6x6.
  • Rectangular matrices have the form MatrixIxJ<T> where I and J are any value from 1 to 6, e.g., Matrix4x5<T>.

In its most general form, the Matrix<T, R, C, S> type takes four type parameters:

  • T: the scalar type, i.e., the type of the matrix components. Typical values are f32 or f64.
  • R: a type characterizing the number of rows on this matrix.
  • C: a type characterizing the number of columns on this matrix.
  • S: the buffer that contains all the matrix components.

The type parameters R and C completely determine whether or not the matrix shape is known at compile-time or only at run-time. They can have two kinds of values:

  1. Constant integers: Represented by the type Const<const D: usize>. For example, Matrix<T, Const<2>, Const<4>, S> represents a matrix with 2 rows and 4 columns.
  2. The Dynamic type: using Dynamic instead of a Const<N> integer indicates that the corresponding matrix dimension is not known at compile-time. For example, Matrix<T, Dynamic, Dynamic, S> has a number of rows and number of columns that can only be known at runtime. Another typical example is a dynamically-sized column vector: Matrix<T, Dynamic, Const<1>, S>.
note

For all constant integers in [0; 127], you may use U1, U2, ..., U127 which are shorter aliases for Const<1>, Const<2>, ..., Const<127>.

use na::{U2, U3, Dynamic, ArrayStorage, VecStorage, Matrix};
// Statically sized and statically allocated 2x3 matrix using 32-bit floats.
type Matrix2x3f = SMatrix<f32, 2, 3>;
// Half-dynamically sized and dynamically allocated matrix with
// two rows using 64-bit floats.
//
// The `OMatrix` alias is a matrix that owns its data (as opposed to
// matrix view which borrow their data from another matrix).
type Matrix2xXf64 = OMatrix<f64, U2, Dynamic>;
// Dynamically sized and dynamically allocated matrix with
// two rows and using 32-bit signed integers.
type DMatrixi32 = OMatrix<i32, Dynamic, Dynamic>;
// Statically sized and statically allocated 1000x3 matrix using 32-bit floats.
type Matrix1000x3f = SMatrix<f32, 1000, 3>;

Internally, dynamically- and statically-sized matrices do not use the same data storage type. While the former is always allocated on the heap using a Vec, the latter prefers static allocation indirectly using a column-major 2D array [[T; R]; C] for better performances.

Matrix construction#

All matrices and vectors with shapes known at compile-time can be created from the values of their components given in conventional mathematical notation, i.e., row-by-rows, using the usual ::new method:

// A vector with three components.
let v = Vector3::new(1, 2, 3);
// A matrix with three lines and four columns.
// We chose values such that, for example, 23 is at the row 2 and column 3.
let m = Matrix3x4::new(11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34);

Other construction methods include:

MethodDescription
::from_rows(...)Creates a matrix filled with the given array of rows. Panics if any two rows provided do not have the same size.
::from_columns(...)Creates a matrix filled with the given array of columns. Panics if any two columns provided do not have the same size.
::from_diagonal(...)Creates a diagonal matrix with its diagonal equal to the provided vector. All off-diagonal elements are set to 0.
::repeat(...)Creates a matrix filled with the given element (same as ::from_element(...)).
::from_element(...)Creates a matrix filled with the given element (same as ::repeat(...)).
::from_iterator(...)Creates a matrix filled with the content of the given iterator. The iterator must provide the matrix components in column-major order.
::from_row_slice(...)Creates a matrix filled with the content of the given slice. Elements of the slice are provided in row-major order (which is the usual mathematical notation.)
::from_column_slice(...)Creates a matrix filled with the content of the given slice. Elements of the slice are provided in column-major order.
::from_vec(...)Creates a matrix filled with the content of the given Vec. Elements of the vec are provided in column-major order. Constructing a dynamically-sized matrix this way consumes the vec to avoid allocations.
::from_fn(...)Creates a matrix filled with the values returned by the given closure of type FnMut(usize, usize) -> T. This closure is called exactly once per matrix component and is given as parameter each matrix component's 0-based indices.
::identity(...)Creates a matrix with 1 on its diagonal and 0 elsewhere. If the matrix to be constructed is not square, only the largest square submatrix formed by its first rows and columns is set to the identity matrix. All the other components are 0.
::from_diagonal_element(...)Creates a matrix with its diagonal filled with the given element and 0 elsewhere. If the matrix to be constructed is not square, only the largest square submatrix formed by its first rows and columns is set to the identity matrix. All the other components are set to 0.
::new_random(...)Creates a matrix with all its components initialized at random using the default random generator of the rand crate, i.e., the rand::random() function.
// All the following matrices are equal but constructed in different ways.
let m = Matrix2x3::new(1.1, 1.2, 1.3,
2.1, 2.2, 2.3);
let m1 = Matrix2x3::from_rows(&[
RowVector3::new(1.1, 1.2, 1.3),
RowVector3::new(2.1, 2.2, 2.3)
]);
let m2 = Matrix2x3::from_columns(&[
Vector2::new(1.1, 2.1),
Vector2::new(1.2, 2.2),
Vector2::new(1.3, 2.3)
]);
let m3 = Matrix2x3::from_row_slice(&[
1.1, 1.2, 1.3,
2.1, 2.2, 2.3
]);
let m4 = Matrix2x3::from_column_slice(&[
1.1, 2.1,
1.2, 2.2,
1.3, 2.3
]);
let m5 = Matrix2x3::from_fn(|r, c| (r + 1) as f32 + (c + 1) as f32 / 10.0);
let m6 = Matrix2x3::from_iterator([ 1.1f32, 2.1, 1.2, 2.2, 1.3, 2.3 ].iter().cloned());
assert_eq!(m, m1); assert_eq!(m, m2); assert_eq!(m, m3);
assert_eq!(m, m4); assert_eq!(m, m5); assert_eq!(m, m6);
// All the following matrices are equal but constructed in different ways.
//
// This time, we used a dynamically-sized matrix so we must specify the
// dimensions of the matrix with the first two arguments.
let dm = DMatrix::from_row_slice(4, 3, &[
1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
0.0, 0.0, 1.0,
0.0, 0.0, 0.0
]);
let dm1 = DMatrix::from_vec(4, 3, vec![1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0]);
let dm2 = DMatrix::from_diagonal_element(4, 3, 1.0);
let dm3 = DMatrix::identity(4, 3);
let dm4 = DMatrix::from_fn(4, 3, |r, c| if r == c { 1.0 } else { 0.0 });
let dm5 = DMatrix::from_iterator(4, 3, [
// Components listed column-by-column.
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0
].iter().cloned());
// Here is a dynamically-sized vector. The first constructor argument specifies its number of elements.
let dm1 = DMatrix::from_vec(4, vec![1.0, 2.0, 3.0, 4.0]);
assert_eq!(dm, dm1); assert_eq!(dm, dm2);
assert_eq!(dm, dm3); assert_eq!(dm, dm4);
assert_eq!(dm, dm5);

Matrices with sizes known at compile-time implement some construction traits from the num crate at well:

Trait methodDescription
Zero::zero()Creates a matrix filled with zeros.
One::one()Creates a matrix with a diagonal set to 1 and off-diagonal elements set to 0.
Bounded::min_value()Creates a matrix filled with the minimal value of the matrix scalar type.
Bounded::max_value()Creates a matrix filled with the maximal value of the matrix scalar type.

Column vectors (which are just matrices with a single column) with low dimensions from 1 to 6 have additional constructors:

  • ::x(), ::y(), and ::z() create a vector with, respectively, the first, second, or third coordinate set to 1 and all others to 0.
  • ::a(), ::b(), and ::c() create a vector with, respectively, the fourth, fifth, or sixth coordinate set to 1 and all others to 0.
assert_eq!(Vector3::x(), Vector3::new(1.0, 0.0, 0.0));
assert_eq!(Vector3::y(), Vector3::new(0.0, 1.0, 0.0));
assert_eq!(Vector3::z(), Vector3::new(0.0, 0.0, 1.0));
assert_eq!(Vector6::a(), Vector6::new(0.0, 0.0, 0.0, 1.0, 0.0, 0.0));
assert_eq!(Vector6::b(), Vector6::new(0.0, 0.0, 0.0, 0.0, 1.0, 0.0));
assert_eq!(Vector6::c(), Vector6::new(0.0, 0.0, 0.0, 0.0, 0.0, 1.0));

Adding a _axis suffix to those constructors, e.g., ::y_axis(), will create a unit vector wrapped into the Unit structure. For example, Vector2::y_axis() will create a Unit<Vector2<T>> with its the second component of the underlying vector set to 1.

assert_eq!(Vector4::x_axis().unwrap(), Vector4::x());
assert_eq!(Vector4::y_axis().unwrap(), Vector4::y());
assert_eq!(Vector4::z_axis().unwrap(), Vector4::z());
assert_eq!(Vector5::a_axis().unwrap(), Vector5::a());
assert_eq!(Vector5::b_axis().unwrap(), Vector5::b());

Matrix operations#

Operations between two matrices like addition, division, and multiplication, require both matrices to have compatible shapes. In particular:

  • Addition require both matrices to have the same number of rows and the same number of columns.
  • Multiplication and division requires the matrix on the left-hand-side to have as many columns as the number of rows of the matrix on the right-hand-side.

Those restrictions are either checked at compile-time or at runtime, depending on the inputs types. In particular, if the matrix dimensions to be checked are const integers then the check is performed at compile-time. The following shows an example of compilation error for attempting to multiply a 2x3 matrix with a 4x4 matrix:

let a = Matrix2x3::zero();
let b = Matrix4::zero();
let _ = a * b; // Compile-time error here.
error[E0277]: the trait bound `na::constraint::ShapeConstraint: na::constraint::DimEq<na::U3, na::U4>` is not satisfied
--> tests/matrix.rs:27:13
|
27 | let _ = a * b;
| ^^^^^ the trait `na::constraint::DimEq<na::U3, na::U4>` is not implemented for `na::constraint::ShapeConstraint`
|
= help: the following implementations were found:
= help: <na::constraint::ShapeConstraint as na::constraint::DimEq<D, D>>
= help: <na::constraint::ShapeConstraint as na::constraint::DimEq<D, na::Dynamic>>
= help: <na::constraint::ShapeConstraint as na::constraint::DimEq<na::Dynamic, D>>
= note: required because of the requirements on the impl of `na::constraint::AreMultipliable<na::U2, na::U3, na::U4, na::U4>` for `na::constraint::ShapeConstraint`
= note: required because of the requirements on the impl of `std::ops::Mul<na::Matrix<{float}, na::U4, na::U4, na::ArrayStorage<{float}, na::U4, na::U4>>>` for `na::Matrix<{float}, na::U2, na::U3, na::ArrayStorage<{float}, na::U2, na::U3>>`

If at least one matrix dimension to be checked is Dynamic then the check is performed at run-time and panics in case of mismatch. The following example shows the run-time error for attempting to multiply a statically-sized 2x3 matrix with a dynamically-sized 4x4 matrix:

let a = Matrix2x3::zero();
let b = DMatrix::from_element(4, 4, 0.0);
let _ = a * b; // Compiles fine but panics here.
thread 'main' panicked at 'Matrix multiplication dimensions mismatch.', [...]/nalgebra/src/core/ops.rs:328
note: Run with `RUST_BACKTRACE=1` for a backtrace

The return type of a matrix operation is automatically deduced from the matrix dimensions:

  • If both matrices have dimensions known at compile-time then the result also has dimensions known at compile-time.
  • If both matrices have dimensions known at run-time only then the result also has dimensions known at run-time.
  • If one matrix has dimensions known at run-time and the other has dimensions known at compile-time then the result will have dimensions known at compile-time if they can be statically deduced from the arguments. For example, adding a Matrix2x3 to a DMatrix will return a Matrix2x3. However, multiplying a Matrix2x3 to a DMatrix will return a matrix with one dimension known at compile-time, and a second one known at run-time, i.e., Matrix<T, U2, Dynamic, S> (where T and S are some types not detailed here). Indeed, the number of rows can be deduced from the first argument but the number of columns depends on the run-time value stored by the second argument.
let static_m = Matrix2::zero();
let dynamic_m = DMatrix::from_element(2, 2, 0.0);
let static_v = Vector2::zero();
let dynamic_v = DVector::from_element(2, 0.0);
// We know at compile-time that the sum will be a 2x2 matrix
// because of the first argument. The result is thus a `Matrix2`.
let static_plus_dynamic: Matrix2<_> = static_m + dynamic_m;
// We don't know anything about the matrix dimensions at
// compile-time. The result is thus a `DMatrix`.
let dynamic_plus_dynamic: DMatrix<_> = dynamic_m + dynamic_m;
// The result is a static vector (even if the second argument
// has a dynamic size) because we know at compile-time that
// `static_m` has two rows and that `dynamic_v` has one column.
let static_times_dynamic: Vector2<_> = static_m * dynamic_v;
// The result is a dynamic vector because we do not know at
// compile-time its number of rows.
let dynamic_times_static: DVector<_> = dynamic_m * static_v;

Matrix element modification#

It is possible to modify elements of a matrix when it is mutable. The following examples show in particular how to:

  • Modify a single element.
  • Modify an entire row/column.
let mut m = Matrix3x4::new(11, 12, 13, 14,
21, 22, 23, 24,
31, 32, 33, 34);
m[(0, 2)] = -13;
assert_eq!(m, Matrix3x4::new(11, 12, -13, 14,
21, 22, 23, 24,
31, 32, 33, 34));
m.set_column(1, &Vector3::new(-12, -22, -32));
assert_eq!(m, Matrix3x4::new(11, -12, -13, 14,
21, -22, 23, 24,
31, -32, 33, 34));
m.set_row(2, &RowVector4::new(-31, -32, -33, -34));
assert_eq!(m, Matrix3x4::new(11, -12, -13, 14,
21, -22, 23, 24,
-31, -32, -33, -34));

Because types like Vector3 are column vectors, i.e., matrices with dimension 3×1, they can only be used with .set_column(...). For setting a row with .set_row(...) it is necessary to use a row vector. For example RowVector4 is a 4D row vector, i.e., a matrix with dimensions 4×1.

Matrix views#

Matrix (and vector) views allow you to take a reference to a part of any matrix. A view does not perform any copy, move, or allocation of the original matrix data. Instead, it stores a pointer to that data together with some metadata about the view size and strides. Note that taking a view of a matrix view is allowed!

Because a matrix view is also just a special case of the generic Matrix<T, R, C, S> type, it can usually be used just like a plain, non-view matrix besides three exceptions:

  1. Methods that require a &mut self cannot be called on non-mutable views.
  2. Matrix views cannot be created out of thin air using the methods shown in the Matrix construction section. One must already have an instance of a matrix or another view and use one of the dedicated methods shown thereafter.
  3. Assignment operators do not work on any kind of view, i.e., one cannot write a *= b even if a is a mutable matrix view.

There are three variations of matrix view methods. Mutable views follow the same semantics, except that the method names end with _mut:

  • "Fixed" views: views with numbers of rows and columns known at compile-time. The name of the corresponding view methods usually start with the prefix fixed_.
MethodDescription
.row(i)Reference to the i-th row of self.
.column(i)Reference to the i-th column of self.
.fixed_rows::<D>(i)Reference to the submatrix with D consecutive rows of self, starting with the i-th. D must be an integer.
.fixed_columns::<D>(i)Reference to the submatrix with D consecutive columns of self, starting with the i-th. D must be an integer.
.fixed_view::<R, C>(irow, icol)Reference to the submatrix with R consecutive rows and C consecutive columns, starting with the irow-th row and icol-th column. R and C are integers.

Fixed views


  • "Dynamic" views: views with numbers of rows and columns known at run-time only.
MethodDescription
.rows(i, size)Reference to size rows of self, starting with the i-th.
.columns(i, size)Reference to size columns of self, starting with the i-th.
.view(start, shape)Reference to the submatrix with shape.0 rows and shape.1 columns, starting with the start.0-th row and start.1-th column. start and shape are both tuples.

Dynamic views


  • Views with strides: fixed or dynamic views that reference non-consecutive (but regularly spaced) rows and columns of the original matrix. The name of the corresponding view methods end with _with_step.
MethodDescription
.fixed_rows_with_step::<D>(i, step)Reference to D non-consecutive rows of self, starting with the i-th. step rows of self are skipped between each referenced row.
.fixed_columns_with_step::<D>(i, step)Reference to D non-consecutive columns of self, starting with the i-th. step columns of self are skipped between each referenced column.
.fixed_view_with_steps::<R, C>(start, step)Reference to R and C non-consecutive rows and columns, starting with the component (start.0, start.1). step.0 (resp. step.1) rows (resp. columns) are skipped between each referenced row (resp. column).
.rows_with_step(i, size, step)Reference to size rows of self, starting with the i-th. step rows are skipped between each referenced row.
.columns_with_step(i, size, step)Reference to size columns of self, starting with the i-th. step columns are skipped between each reference column.
.view_with_steps(start, shape, steps)Reference to shape.0 rows and shape.1 columns, starting with the (start.0, start.1)-th component. step.0 (resp. step.1) rows (resp. columns) are skipped between each referenced row (resp. column).

Views with strides

Note that the method .clone_owned() may be used to create a plain matrix from a view, i.e., actually copying the referenced components into a new matrix structure that owns its data. Whether or not the result of this cloning is a dynamically- or statically-sized matrix depends on the kind of view. Fixed views will yield a statically-sized matrix while dynamic views yield a dynamically-sized matrix.

view.clone_owned()

Matrix resizing#

The number of rows or columns of a matrix can be modified by adding or removing some of them. Similarly to views, two variants exist:

  • "Fixed resizing" where the number of rows or columns to be removed or inserted are known at compile-time. This allows the compiler to output a statically-sized matrix when the input is also statically-sized.
MethodDescription
.remove_row(i)Removes the i-th row.
.remove_column(i)Removes the i-th column.
.remove_fixed_rows::<D>(i)Removes D consecutive rows, starting with the i-th.
.remove_fixed_columns::<D>(i)Removes D consecutive columns, starting with the i-th.
.insert_row(i, val)Adds one row filled with val at the i-th row position.
.insert_column(i, val)Adds one column filled with val at the i-th row position.
.insert_fixed_rows::<D>(i, val)Adds D consecutive rows filled with val starting at the i-th row position.
.insert_fixed_columns::<D>(i, val)Adds D consecutive columns filled with val starting at the i-th column position.
.fixed_resize::<R2, C2>(val)Resizes the matrix so that it contains R2 rows and C2 columns. Components are copied such that result[(i, j)] == input[(i, j)]. If the result matrix has more rows or more columns, then the extra components are initialized to val.

  • "Dynamic resizing" where the number of rows or columns to be removed or inserted are not known at compile-time. The result matrix will always be dynamically-sized (the affected dimension-related type parameter of Matrix<...> is set to Dynamic).
MethodDescription
.remove_rows(i, n)Removes n rows, starting with the i-th.
.remove_columns(i, n)Removes n columns, starting with the i-th.
.insert_rows(i, n, val)Inserts n rows filled with val starting at the i-th row position.
.insert_columns(i, n, val)Inserts n columns filled with val starting at the i-th row position.
.resize(new_nrows, new_ncols, val)Resizes the matrix so that it contains new_nrows rows and new_ncols columns. Components are copied such that result[(i, j)] == input[(i, j)]. If the result matrix has more rows or more columns, then the extra components are initialized to val.

The implicit self argument of those methods is always consumed in order to re-use the input data storage to construct the output. Fixed resizing should be preferred whenever the number of rows/columns to be inserted or removed is known at compile-time.

It is strongly recommended to use fixed resizing whenever possible, especially when the matrix being resize has a size known at compile-time (and is thus statically allocated). Indeed, dynamic resizing will produce heap-allocated results because the size of the output matrix cannot be deduced at compile-time.

Fixed resizing