C++ - Armadillo
Table of Contents
Introduction
Armadillo1 is an excellent C++ library, which provides efficient linear algebra related operations, based on a series of classes, e.g., for vectors, matrices, etc. Thanks to its friendly interfaces, similar to MATLAB, the library is very easy to use.
Armadillo is developped by Conrad Sanderson and Ryan Curtin, and released under Apache 2.0 license.
Installation
Matrix decomposition related operations are developped based on the existing third-party libraries, e.g., the open source OpenBLAS or LAPACK, or even the commercial MKL. In my opinion, openBLAS is the best choice, and can be installed in ArchLinux.
pacman -S openblas
The latest codes resides in a git repository2. Based on the source codes can be compiled and installed as usual.
Besides, the library can be easily installed as system package. Taking ArchLinux for instance, armadillo can be installed by
pacman -S armadillo
Usage
Note that the detailed usage of the libraries can be found in its online manual, and this post just provides a brief and incomplete summary as a quick lookup reference after my walking through the manual.
Typedef for basic elements
uword
is a typedef forunsigned int
.sword
is a typedef for(signed) int
.cx_float
is a typedef forstd::complex<float>
.cx_double
is a typedef forstd::complex<double>
.
Fill form
The library defines a series of fill_form
, which significantly facilitates the initialization of the data type classes.
fill::zeros
sets all elements to 0.fill::ones
sets all elements to 1.fill::eye
initializes a matrix as an identity matrix.fill::randu
sets all elements to uniform distributed random values, i.e., \(\mathbb{U}(0, 1)\).fill::randn
sets all elements to standard Gaussian distributed random values, i.e., \(\mathbb{N}(0, 1)\).fill::value(scalar)
sets all elements to a specified scalar.fill::none
does not initialize the elements.
Classes
Matrix
Considering the type of component elements, matrix is defined as a template Mat<type>
, where type
can be signed or unsigned, real or complex, int or float or even double. A matrix is column-wise stored in the memory.
Typedef
Moreover, following types have been defined for convenience.
imat
is a typedef forMat<sword>
.umat
is a typedef forMat<uword>
.fmat
is a typedef forMat<float>
.mat
ordmat
are both typedefs forMat<double>
.cx_fmat
is a typedef forMat<cx_float>
.cx_mat
andcx_dmat
are both typedefs forMat<cx_double>
.
Constructor
A rich number of constructors have been defined, e.g., taking mat
for instance,
mat()
mat(n_rows, n_cols)
mat(n_rows, n_cols, fill_form)
mat::fixed<n_rows, n_cols>
creates a matrix with fixed size.mat::fixed<n_rows, n_cols>(fill_form)
mat(size(X))
mat(size(X), fill_form)
mat(mat)
mat(vec)
mat(rowvec)
mat(initializer_list)
mat(string)
mat(std::vector)
creates a column vector, as a single-column matrix.mat(sp_mat)
creates a dense matrix based on a sparse matrix.cx_mat(mat, mat)
constructs a complex matrix based on a pair of real matrices, as real and imaginary parts respectively.
Submatrix view
Continuous
X.col(idx)
/X.row(idx)
X.cols(idx1, idx2)
/X.rows(idx1, idx2)
X.submat(row1, col1, row2, col2)
X(span(row1, row2), span(col1, col2))
X(row, col, size(n_rows, n_cols))
/X(row, col, size(Y))
X(span(row1, row2), col)
X(row, span(col1, col2))
X.head_cols(n_cols)
/X.head_rows(n_rows)
X.tail_cols(n_cols)
/X.tail_rows(n_rows)
span
defines a continuous range, e.g.,
span(a, b)
describes a contiguous range from \(a\) to \(b\), \(a < b\).span(a)
indicates a particular row/column/slice.span()
orspan::all
indicates the entire range.
Non-continuous
X.cols(idx_vec)
/X.rows(idx_vec)
X.submat(row_vec, col_vec)
X(row_vec, col_vec)
Member functions
.eye()
: Set the matrix to an identity matrix..diag(k = 0)
: Return a column vector of a matrix..as_col()
/.as_row()
: Return a column/row vector by concatenating all the columns/rows of the matrix..each_col(idx_vec)
/.each_row(idx_vec)
/.each_col(lambda)
/.each_row(lambda)
: Apply a vector operation to each column/row of a matrix. The argumentidx_vec
is optional, and by default all columns/rows are used. If the lambda functionlambda
is specified, then the function must accept a reference to aCol<type>
orRow<type>
object with the same element type as the underlying matrix. Following operations have been supported.+/-//
: Addition/subtraction/division%
: Element-wise multiplication=
: Assignment (copy)+=/-=//=
: In-place addition/subtraction/division%=
: In-place element-wise multiplication
.st()
: Return a transposed copy (only applicable for a complex matrix).t()
: Return a Hermitian transposed copy3..i()
: Return the inverse for a square matrix..is_square()
.is_vec()
: Returntrue
if the matrix is a column or row vector..is_colvec
/.is_rowvec
.is_trimatu()
/.is_trimatl()
.is_diagmat()
Vector
In the library, column vectors and row vectors are different, represented by Col<type>
and Row<type>
respectively.
Typedef
Column vector
icolvec
andivec
are both typedefs forCol<sword>
.ucolvec
anduvec
are both typedefs forCol<uword>
.fcolvec
andfvec
are both typedefs forCol<float>
.dcolvec
,dvec
,colvec
andvec
are all typedefs forCol<double>
.cx_fcolvec
andcx_fvec
are both typedefs forCol<cx_float>
.cx_dcolvec
,cx_dvec
,cx_colvec
andcx_vec
are all typedefs forCol<cx_double>
.
Row vector
irowvec
is a typedef forRow<sword>
.urowvec
is a typedef forRow<uword>
.frowvec
is a typedef forRow<float>
.drowvec
androwvec
are both typedefs forRow<double>
.
Constructor
Taking vec/rowvec
for instance, following constructs have been defined.
vec/rowvec()
vec/rowvec(n_elem)
vec/rowvec(n_elem, fill_form)
vec/rowvec::fixed<n_elem>
creates a column/row vector with fixed size.vec/rowvec::fixed<n_elem>(fill_form)
vec/rowvec(size(X))
vec/rowvec(size(X), fill_form)
vec/rowvec(vec/rowvec)
vec/rowvec(mat)
creates a column/row vector based on a single-column/row matrix.vec/rowvec(initializer_list)
vec/rowvec(string)
creates a column/row vector based on a string with space as delimitor.vec/rowvec(std::vector)
creates a column/row vector based on a vector container in STL.cx_vec/rowvec(vec/rowvec, vec/rowvec)
constructs a complex column/row vector based on a pair of real column/row vectors, as real and imaginary parts respectively.
Subvector view
Continuous
V(span(idx1, idx2))
V.subvec(idx1, idx2)
/V.subvec(idx, size(U))
V.head(n_elem)
/V.tail(n_elem)
Non-continuous
X.elem(idx_vec)
/X(idx_vec)
Cube
A cube is essentially a 3-dimension (3D) matrix, which is compriesd of a series of 2D matrices, a.k.a. slices. Cube class is also a template, i.e., Cube<type>
.
Typedef
icube
is a typedef forCube<sword>
.ucube
is a typedef forCube<uword>
.fcube
is a typedef forCube<float>
.dcube
andcube
are both typedefs forCube<double>
.cx_fcube
is a typedef forCube<cx_float>
.cx_dcube
andcx_cube
are both typedefs forCube<cx_double>
.
Constructor
cube()
cube(n_rows, n_cols, n_slices)
cube(n_rows, n_cols, n_slices, fill_form)
cube::fixed<n_rows, n_cols, n_slices>
creates a cube with fixed size.cube(size(X))
cube(size(X), fill_form)
cube(cube)
cx_cube(cube, cube)
constructs a complex cube based on a pair of real cubes, as real and imaginary parts respectively.
Subcube view
Contiguous
Q.slice(idx)
/Q.slices(idx1, idx2)
Q.col_as_mat(idx)
/Q.row_as_mat(idx)
returns a flattened matrix.Q.row(idx)
/Q.col(idx)
Q.rows(idx1, idx2)
/Q.cols(idx1, idx2)
Q.subcube(row1, col1, slice1, row2, col2, slice2)
Q(span(row1, row2), span(col1, col2), span(slice1, slice2))
Q(row1, col1, slice1, size(n_rows, n_cols, n_slices))
/Q(row, col, slice, size(R))
Q.head_slices(n_slices)
/Q.tail_slices(n_slices)
Q.tube(row, col)
4 /Q.tube(row1, col1, row2, col2)
/Q.tube(span(row1, row2), span(col1, col2))
/Q.tube(row, col, size(n_rows, n_cols))
Non-contiguous
Q.elem(idx_vec)
Q(idx_vec)
Q.slices(idx_vec)
Member functions
.each_slice(idx_vec)
/.each_slice(lambda)
/.each_slice(lambda, use_mp = false)
: Apply a matrix operation to each slice/matrix of a cube. The argumentidx_vec
is optional, and by default all slices are used. If the lambda functionlambda
is specified, then the function must accept a reference to aMat<type>
object with the same element type as the underlying cube.use_mp
is a boolean indicator of OpenMP for multithreading. If it is set totrue
, then thelambda
must be thread-safe, i.e., cannot write to variables outside of its scope..insert_slices(idx, X)
: InsertX
atidx
th slice..insert_slices(idx, slice_num)
: Insertslice_num
slices atidx
th slice, and theslice_num
slices inserted are all set to 0..shed_slice(idx)
: Removeidx
th slice from the underlying cube..shed_slices(first, last)
: Remove the slices betweenfirst
th andlast
th slices..shed_slices(idx_vec)
: Remove the slices with indices inidx_vec
.
Field
According to the online manual, a field is a class to store arbitrary objects in matrix-like or cube-like layouts. Given a vector or a matrix or a cube, all the elements (e.g., scalars in a vector, vectors in a matrix, matrices in a cube) must have the identical type and equal size. But in a field, the elements just have the identical type, but their lengths or sizes can be different from one element to another.
Constructor
A field object can be created by any one of the constructors below, where object_type
is the element type, which can be vector or matrix or cube.
field<object_type>()
field<object_type>(n_elem)
field<object_type>(n_rows, n_cols)
field<object_type>(n_rows, n_cols, n_slices)
field<object_type>(size(X))
field<object_type>(field<object_type>)
Subfield view
For a 2D field
F.row(idx)
F.col(idx)
F.rows(row1, row2)
F.cols(col1, col2)
F.subfield(row1, col1, row2, col2)
F(span(row1, row2), span(col1, col2))
F(row, col, size(G))
F(row, col, size(n_rows, n_cols))
For a 3D field
F.slice(idx)
F.slices(slice1, slice2)
F.subfield(row1, col1, slice1, row2, col2, slice2)
F(span(row1, row2), span(col1, col2), span(slice1, slice2))
F(row, col, slice, size(G))
F(row, col, slice, size(n_rows, n_cols, n_slices))
Length and size
Length and size related attributes of type uword
, can be listed as below.
.n_elem
: Total number of elements in a vector/matrix/cube/field.n_cols
: Number of columns in a vector/matrix/cube/field.n_rows
: Number of rows in a vector/matrix/cube/field.n_slices
: Number of slices in a cube/field.n_nonzeros
: Number of non-zero elements in a sparse matrix
The dimensions above are all read-only, but can be changed by
.set_size(n_elem)
for a vector/field.set_size(n_rows, n_cols)
for a matrix/field.set_size(n_rows, n_cols, n_slices)
for a cube/field.set_size(size(X))
for a vector/matrix/cube/field.copy_size(X)
set the size equal to that of object X.
Common member functions
.zeros()
: Set all elements to 0..ones()
: Set all elements to 1..randu()
: Set all elements to uniform distributed random values, i.e., \(\mathbb{U}(0, 1)\)..randn()
: Set all elements to standard Gaussian distributed random values, i.e., \(\mathbb{N}(0, 1)\)..fill(value)
: Set all the elements tovalue
..replace(old_value, new_value)
: For all elements equal toold_value
, set them tonew_value
..clean(threshold)
: Replace each element with absolute value no larger thanthreshold
by zero. For each complex element, its real and imaginary parts are individually replaced, irrespective of its absolute value and amplitude..reset()
: Reset the size to zero, i.e., empty the object..set_size(n_elem)
/.set_size(n_rows, n_cols)
/.set_size(n_rows, n_cols, n_slices)
/.set_size(size(X))
: Change the size of the object without preserving data..copy_size(X)
: Set the size of the object equal to that of object X without preserving data..resize(n_elem)
/.resize(n_rows, n_cols)
/.resize(n_rows, n_cols, n_slices)
/.resize(size(X))
: Grow/shrink the object while preserving the elements and their layout..reshape(n_rows, n_cols)
/.reshape(n_rows, n_cols, n_slices)
/.reshape(size(X))
: Recreate the object according to the given size using the elements in the previous version. The recreating is performed column by column..imbue(functor/lambda)
: Fill/set with values yielded by a functor/lambda expression..transform(functor/lambda)
: Transform each element using a functor/lambda expression..for_each(functor/lambda)
: Process the object by passing each element to a functor/lambda expression..set_real(X)
/.set_imag(X)
: Set the real/imaginary part of an object.X
must have the same size as the underlying object..insert_rows(idx, X)
: InsertX
atidx
th row for a column-vector/matrix/cube..insert_rows(idx, row_num)
: Insertrow_num
rows atidx
th row for a column-vector/matrix/cube, and therow_num
rows inserted are all set to 0..insert_cols(idx, X)
: InsertX
atidx
th column for a row-vector/matrix/cube..insert_cols(idx, col_num)
: Insertcol_num
columns atidx
th column for a column-vector/matrix/cube, and thecol_num
columns inserted are all set to 0..shed_row(idx)
: Removeidx
th row from the underlying column-vector/matrix/cube..shed_rows(first, last)
: Remove the rows betweenfirst
th andlast
th rows from the underlying column-vector/matrix/cube..shed_col(idx)
: Removeidx
th column from the underlying row-vector/matrix/cube..shed_cols(first, last)
: Remove the columns betweenfirst
th andlast
th columns from the underlying row-vector/matrix/cube..swap_rows(idx1, idx2)
: Swap 2 rows of the underlying column-vector/matrix..swap_cols(idx1, idx2)
: Swap 2 columns of the underlying row-vector/matrix..min()
/.max()
: Return the extreme5 value of a matrix or a cube..index_min()
/.index_max()
: Return the linear index of the extreme5 value of a matrix or a cube..in_range
: Check the validity of a given location, e.g., returnfalse
for an empty object, out-of-bounds..is_empty()
: Check the object whether it is empty..is_sorted(sort_direction = "ascend", dim = 0)
: Check a vector or a matrix..print()
/.print(header)
/.print(stream)
/.print(stream, header)
: Print the content of the object..save
/.load
Notes: Functions .zeros
, .ones
, .randu
, .randn
, .eye
above can also take dimensions as arguments to set the length and size of the object. In this case, it combines the operations of argument-free provoking and .set_size
.
Overloaded operators
A rich number of operators have been overloaded for column/row vector, matrix and cube classes, e.g.,
*
: Matrix multiplication of two objects, but not applicable to cube, unless multiplying by a scalar.%
: Element-wise multiplication, a.k.a. Schur product./
: Element-wise division of an object by another object or a scalar.==
/!=
: Element-wise equality/non-equality evaluation>
/<
/>=
/<=
: Element-wise comparsion&&
/||
: Element-wise logical AND/OR evaluation
Element access
(n)
: Access the \(n\) th element of a vector/matrix/cube or \(n\) th object of a field..at(n)
/[n]
: Similar to(n)
, but without a bound check6.(i, j)
: Access the element in \(i\) th row and \(j\) th column of a matrix, or the object in \(i\) th row and \(j\) th column of a 2D field..at(i, j)
: Similar to(i, j)
, but without a bound check.(i, j, k)
: Access the element in \(i\) th row, \(j\) th column, and \(k\) th slice; or the object in \(i\) th row, \(j\) th column, and \(k\) th slice for a 3D field..at(i, j, k)
: Similar to(i, j, k)
, but without bound check.
Note that, 2D index using rectangle bracket (e.g., \([i, j]\), \([i, j, k]\)) does not work correctly. We should use \((i, j)\), \((i, j, k)\) instead.
Iterators
In armadillo, matrices/vectors/cubes are somewhat similar to the containers in C++ STL, and consequently can be accessed via iterators.
Member functions
.begin()
: Iterator referring to the first element..end()
: Iterator referring to the past-the-end element..begin_col(idx)
/.begin_row(idx)
: Iterator referring toidx
th column/row of a row/column vector or a matrix..end_col(idx)
/.end_row(idx)
: Iterator referring to the past-the-end ofidx
th column/row of a row/column vector or a matrix..begin_slice(idx)
: Iterator referring toidx
th slice of a cube..end_slice(idx)
: Iterator referring to the past-the-end ofidx
th slice of a cube.
Iterator types
For read/write access
mat::iterator
/vec::iterator
/rowvec::iterator
/cube::iterator
mat::col_iterator
/vec::col_iterator
/rowvec::col_iterator
mat::row_iterator
/vec::row_iterator
/rowvec::row_iterator
cube::slice_iterator
For read-only access
mat::const_iterator
/vec::const_iterator
/rowvec::const_iterator
/cube::const_iterator
mat::const_col_iterator
/vec::const_col_iterator
/rowvec::const_col_iterator
mat::const_row_iterator
/vec::const_row_iterator
/rowvec::const_row_iterator
cube::const_slice_iterator
Container-compatible functions
.front()
/.back()
: Access the first/last element in a vector..clear()
: Empty the object..empty()
: Returntrue/false
for an empty/non-empty object..size()
: Total number of elements in the object, i.e.,.n_elem
.
Generation
ones(size(X))
/zeros(size(X))
randu
/randi
/randn
/randg
Matrix
eye(n_rows, n_cols)
/eye(size(X))
ones(n_rows, n_cols)
/zeros(n_rows, n_cols)
toeplitz
/circ_toeplitz
diagmat(X, k=0)
trimatu(X, k)
/trimatl(X, k)
Vector
ones(n_elem)
/zeros(n_elem)
linspace(start, end, N=100)
logspace(a, b, N=50)
: Generate a logarithmically spaced vector from \(10^a\) to \(10^b\) (included).regspace(start, end)
/regspace(start, step, end)
randperm(N)
/randperm(N, M)
: Generate a vector with random permutation of integers from0
toN-1
.diagvec(X, k=0)
Cube
ones(n_rows, n_cols, n_slices)
/zeros(n_rows, n_cols, n_slices)
Functions
abs(X)
/arg(X)
: Per-element absolute values or phase angles (in radians)conj(X)
: Per-element complex conjugateaccu(X)
: Sum all elements.sum(X, dim=0)
all(V)
/all(X, dim)
: Check whether all the elements (in dimensiondim
) are non-zero.any(V)
/any(X, dim)
: Check whether there is any non-zero element (dimensiondim
) exist.conv_to<type>::from(X)
: Type convertion/castdiff(V, k=0)
/diff(X, k, dim=0)
real(X)
/imag(X)
min
/max
norm(X, p=2)
pow
shuffle(X, dim=0)
sort
/sort_index
unique(X)
vectorise(X, dim=0)
: Concatenate all the columns (dim=0) or rows (dim=1).fft
/ifft
cor
/cov
: Correlation/covariance- Statistics:
mean
,median
,stddev
,var
,range
,hist
/histc
,quantile
.
Matrix
as_scalar(expression)
: Convert theexpression
(resulting a 1x1 matrix) to a scalar.cond(X)
/rcond(X)
: Calculate the (reciprocal) condition number of a matrix.det(X)
/log_det(X)
/log_det_sympd(X)
trace(X)
expmat(X)
/expmat_sym(X)
: Matrix exponential of a square (symmetric/Hermitian) matrix.trans(X)
/strans(X)
fliplr(X)
/flipud(X)
inv
/inv_sympd
/pinv
intersect(A, B)
join_rows(A, B, ...)
/join_horiz(A, B, ...)
join_cols(A, B, ...)
/join_vert(A, B, ...)
kron(A, B)
fft2
/ifft2
normalise(X, p=2, dim=0)
: Return the normalized version with unitp
norm along the dimensiondim
.powmat(X, n)
/sqrtmat(X)
/sqrtmat_sympd(X)
rank(X)
trimatu_ind(size(X), k)
/trimatl_ind(size(X), k)
: Return a column vector containing the indices of elements that form the upper/lower triangle part of matrix.orth(X)
: Find the orthonormal basis of the range space of the matrix.conv2(a, b)
: 2D convolution- Decomposition:
chol
,eig_sym
/eig_gen
,lu
,qr
,svd
,svd_econ
.
Vector
dot(a, b)
: \(a \cdot b\)cdot(a, b)
: \(a^{*} \cdot b\)norm_dot(a, b)
: \(\dfrac{a \cdot b}{\|a\|\|b\|}\)normalise(V, p=2)
: Return the normalized version with unitp
norm.roots(P)
: Calculate the complex roots of a polynormial function (represented byP
).conv(a, b)
: 1D convolution
Cube
join_slices(A, B)
Miscellaneous
Constant
The constants are stored in Datum<type>
class, where type
can be float or double.
datum
is a typedef forDatum<double>
.fdatum
is a typedef forDatum<float>
.
Expression | Constant |
---|---|
datum::pi |
\(\pi\) |
datum::tau |
\(2\pi\) |
datum::inf |
\(\infty\) |
datum::nan |
NaN7 |
datum::eps |
\(\epsilon\)8 |
datum::e |
\(e\) |
datum::sqrt2 |
\(\sqrt{2}\) |
datum::log_min=/=datum::log_max |
Log of minimum/maximum value (type and machine dependent) |
datum::gratio |
Golden ratio |
datum::F |
Faraday constant (in coulombs) |
datum::h=/=datum::h_bar |
Planck constant (\(h\)) / reduced Planck constant (\(\frac{h}{2\pi}\)) |
datum::c_0 |
Speed of light in vacuum (m/s) |
Timer
Class wall_clock
is simple timer to measure the elapsed seconds. It has 2 member functions.
.tic()
starts the timer..toc()
returns the number of seconds since the last call to.tic()
.
Footnotes:
Reduce to transposition for real matrices.
Tube is a subcube across all the slices.
For a complex matrix/cube, absolute values are compared.
For this reason, it is not recommended.
Not a number (NaN) does not equal anything, even itself.
Machine epsilon, i.e., difference between 1 and the next representable value.