Eigen is a very versatile library in C++ that helps to solve matrix-related problems in efficient approaches. The functions it supports includes but not limited to:
- Process arbitrary fixed-size or dynamic-size (unknown in compile-time) dense matrices and sparse matrices of all standard numeric types.
- Perform matrix decompositions and geometry transforms.
- Other extendable modules like non-linear optimization, a polynomial solver, FFT etc.
For further details, please visit http://eigen.tuxfamily.org/dox/
Installation
Eigen is a header-only library. There is only two steps before using it:
- Download the latest released package
- Unzip, and add the path into include directory
A library is called header-only if the full definitions of all macros, functions and classes comprising the library are visible to the compiler in a header file form.
Modules and Headers
Module | Header File | Contents |
---|---|---|
Core | <Eigen/Core> |
Definition of Matrix and Array classes; basic linear algebra operations, and array manipulation |
Geometry | <Eigen/Geometry> |
Geometry-featured transformations |
LU | <Eigen/LU> |
Inverse, determinant, LU decompositions |
Cholesky | <Eigen/Cholesky> |
LLT and LDLT Cholesky factorization |
Householder | <Eigen/Householder> |
Householder transformations |
SVD | <Eigen/SVD> |
SVD decompositions with least-squares solver |
QR | <Eigen/QR> |
QR decomposition |
Eigenvalues | Eigenvalue, eigenvector decompositions | |
Sparse | <Eigen/Sparse> |
Sparse matrix storage and its basic linear algebra |
A Householder transformation (also called Householder reflection or elementary reflector) is a linear transformation that describes a reflection about a plane or hyperplane containing the origin
There are two all-in-one headers that commonly used by developers for convenience. Depending on the context, one may choose:
<Eigen/Dense>
:Core
,Geometry
,LU
,Cholesky
,SVD
,QR
, andEigenvalues
<Eigen/Eigen>
:Dense
andSparse
(the whole library)
Matrix Class
Dense and Sparse Matrix
Dense matrix is the commonly used in most cases, which stores whole matrix in memory.
Under some contexts, for example finite, element analysis, where developers are expected to deal with very large matrices but only a few non-zero coefficients, one may store the non-zero coefficients only, in order to reduce memory consumption and improve performance. Such matrix is called a Sparse matrix.
Here we mainly focus on the construction and usage of dense matrix. For details of sparse matrix, please visit https://eigen.tuxfamily.org/dox/group__TutorialSparse.html.
Matrix and Array
The APIs of Array
class provide coefficient-wise operations, while the APIs of the Matrix
class provide linear algebra operations.
Template parameters
The templates of Matrix
and Array
are shown below. 1
2
3
4
5
6
7
8
9Matrix<typename Scalar,
int RowsAtCompileTime,
int ColsAtCompileTime,
int Options = 0,
int MaxRowsAtCompileTime = RowsAtCompileTime,
int MaxColsAtCompileTime = ColsAtCompileTime>
// The parameters of Array is as same as above.
Array<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols>
Scalar
is the scalar type. Eigen currently supports all standard floating-point types (float
,double
,complex<float>
,complex<double>
,long double
), as well as all native integer types (int
,unsigned int
,short, etc
.), andbool
.RowsAtCompileTime
andColsAtCompileTime
are the number of rows and columns at compile time. When developer cannot tell to the compiler the exact dimensions of the matrix, he could fill with the special valueDynamic
, and indicates the size in constructor.Options
is a bit field to indicate the storage order; By default, it isColMajor
.MaxRowsAtCompileTime
andMaxColsAtCompileTime
may be useful if a small dynamic-size matrix is required. After specifing these two params, compiler will allocate a memory on stack according to the upper bound, then avoid dynamic memory allocation.
Storage Order
RowMajor
andColMajor
matrices can be mixed in expressions.- Other libraries may require a certain storage order. In such cases, user could construct the objects with the expcted order in the whole program.
- Algorithms traversing a matrix
row by row
will go faster in a row-major matrix because of better data locality; Column-by-column traversal, similarly. - The default in Eigen is
ColMajor
. Naturally,Eigen
work best with column-major matrices.
Fixed-size and Dynamic-size
- A fixed-size matrix is just a plain array, which is treated as a local variable and allocated on the
stack
; So a large fixed-size matrix may cause a stack overflow. For matrices with small sizes (typically smaller than \(4 \times 4\), up to \(16 \times 16\)), fixed-size usually performs better, as there's no run-time cost, and it is easy to be optimized with loop unwinding. - Dynamic-size matrices are allocated on heap; Their number of rows and columns are stored as member variables. Eigen will be more aggressive trying to vectorize (use SIMD instructions) when operating dynamic-size matrices.
- Fixed-size matrices allow compiler to do more rigorous checking towards the validity of the operation, at the costs of longer compilation time and larger executable.
Loop unrolling, also known as loop unwinding, is a loop transformation technique that attempts to optimize a program's execution speed at the expense of its binary size, which is an approach known as space–time tradeoff.
For example, suppose a normal loop:
1 |
int x; |
1 |
int x; |
Convenience typedefs
Eigen defines the following Matrix
typedefs:
MatrixNt
forMatrix<t, N, N>
.VectorNt
forMatrix<t, N, 1>
.RowVectorNt
forMatrix<t, 1, N>
.
Where N
can be any one of 2
, 3
, 4
, or X
(for Dynamic); t
can be any one of i
(int), f
(float), d
(double), cf
(complexcd
(complex
For example:
MatrixXd
meanstypedef Matrix<double, Dynamic, Dynamic> MatrixXd;
Vector3f
meanstypedef Matrix<float, 3, 1> Vector3f;
Some commonly used predefined type of Array
includes
Type | Typedef |
---|---|
Array<float,Dynamic,1> |
ArrayXf |
Array<float,3,1> |
Array3f |
Array<double,Dynamic,Dynamic> |
ArrayXXd |
Array<double,3,3> |
Array33d |
Array<int,1,Dynamic> |
RowArrayXi |
Constructor
Default constructor
1 | Matrix3f a; |
Fixed-size matrix a
is a 3-by-3 matrix, with a plain float[9]
array of uninitialized coefficients; Dynamic-size matrix b
is a null matrix with no memory allocated.
Constructor with sizes
1 | MatrixXf a(10,15); |
For matrices, pass the number of rows first then number of columns; For vectors, pass the length only. They will allocate the memory with the given size and leave the coefficients uninitialized.
In order to offer a uniform API across fixed-size and dynamic-size matrices, it is allowed to pass the sizes to the constructor for fixed-size matrix.
Matrix3f a(3,3);
is a legal no-operation.
Constructor with coefficients
Vector2d
, Vector3d
, and Vector4d
, it is allowed to pass the coefficients directly.
1 | Vector2d a(5.0, 6.0); |
Initializer
Comma initializer
Eigen offers a comma initializer which allows users to pass values using <<
; Values are seperated by ,
. The size of the matrix needs to be defined beforehand; Otherwise, compiler will complain if the size of matrix mismatchs the number of coefficients.
Comma with numeric values
1 | Matrix3f m; |
\[m = \begin{bmatrix} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{bmatrix}\]
Comma with vectors or matrices.
This will join vectors or matrices together.
1 | RowVectorXd vec1(3); |
\[joined = \begin{bmatrix} 1 & 2 & 3 & 1 & 4 & 9 & 16 \end{bmatrix}\]
1 | MatrixXf matA(2, 2); |
\[matB = \begin{bmatrix} 1 & 2 & 0.1 & 0.2 \\ 3 & 4 & 0.3 & 0.4 \\ 0.1 & 0.2 & 1 & 2 \\ 0.3 & 0.4 & 3 & 4 \end{bmatrix}\]
Temporary Objects
Comma initializer can be used to construct temporary objects. finished()
after initialization is required to indicate the accomplishment of it. 1
2MatrixXf mat = MatrixXf::Random(2, 3);
mat = (MatrixXf(2,2) << 0, 1, 1, 0).finished() * mat;
Pre-defined matrices and arrays
The Matrix and Array classes have static methods like Zero()
, Random()
, Constant()
etc. for initializing special matrices accordingly. Depending on the size at compile time, the syntax of initialization varies. Suppose XX
is a data type and xx
is an object. Func
will return an object and setFunc()
will modify the values in place.
- For
fixed-size
objects,XX::Func([values])
,xx::setFunc([values])
- For 1D
dynamic-size
objects,XX::Func(length, [values])
,xx::setFunc(length, [values])
- For 2D
dynamic-size
objects,XX::Func(rows, cols, [values])
,xx::setFunc(rows, cols, [values])
Func
could be
Zero()
Ones()
Constant(value)
Random() \\ within range [-1, 1]
LinSpaced(size, low, high) \\only available for 1D objects
Identity() \\only available for 2D objects
Basis Vectors
It return an expression of the i-th unit (basis) vector.
Fixed-Size Vectors | Dynamic-Size Vectors |
---|---|
XX::UnitX() \(=>\begin{bmatrix} 1 & \{,0\}^* \end{bmatrix}\) XX::UnitY() \(=>\begin{bmatrix} 0 & 1 & \{,0\}^* \end{bmatrix}\) XX::UnitZ() \(=>\begin{bmatrix} 0 & 0 & 1 & \{,0\}^* \end{bmatrix}\) |
XX::Unit(size, i) e.g. VectorXf::Unit(4,1) \(=>\begin{bmatrix} 0 & 1 & 0 & 0 \end{bmatrix}\) |
Accessor
Those accessors could be used as both lvalues and rvalues. As usual with other expressions, it has no runtime cost before evaluation. More detailed information are demostrated in
- http://eigen.tuxfamily.org/dox/group__QuickRefPage.html
- http://eigen.tuxfamily.org/dox/group__TutorialBlockOperations.html
Coefficient Accessor
Both operator ()
and[]
is overloaded for accessing the coefficients; Same as normal array index in C++, it is zero-based. For vectors, both ()
and []
is valid; For matrices, since m[i, j]
will be treated as m[j]
, only ()
is valid.
1 | MatrixXf a(2, 2); |
Block
A block is a rectangular part of matrix or array. It could be selected along a corner or a boundary. For each type of selection, only one example is listed for simplicity's sake.
Operation | Dynamic-size | Fixed-size |
---|---|---|
Block of size (p,q) starting at (i,j) |
m.block(i,j,p,q) |
m.block<p,q>(i,j) |
Corner: Top-Left | m.topLeftCorner(p,q) |
m.topLeftCorner<p,q>() |
Half: Upper q Rows |
m.topRows(q) |
m.topRows<q>() |
Half: Left p Columns |
m.leftCols(p) |
m.leftCols<p>() |
Rows and Columns
Individual columns or rows could be selected by
1 | m.col(idx); // the idx-th column |
Sub-vectors
Operation | Dynamic-size | Fixed-size |
---|---|---|
First n elements of vector |
v.head(n) |
v.head<n>() |
Last n elements of vector |
v.tail(n) |
v.tail<n>() |
n elements starting at i |
vector.segment(i, n) |
vector.segment<n>(i) |
Diagonals
m.diagonal([idx])
Slicing and Map
Map
It can be used to process non-eigen data without any overhead.
1 | Map< typename PlainObjectType, |
PlainObjectType
: the type after mappingMapOptions
: specifies the pointer alignment in bytes. It can be:Aligned128
,Aligned64
,Aligned32
,Aligned16
,Aligned8
orUnaligned
. By default is Unaligned.StrideType
: optional. By default, Map assumes the memory layout of an ordinary, contiguous array. This can be overridden by specifying strides. The type passed here must be a specialization of the Stride template.
Stride
1 | Stride< int _OuterStrideAtCompileTime, |
InnerStride
is the pointer increment between two consecutive entries within a given row of a row-major matrix or within a given column of a column-major matrix.OuterStride
is the pointer increment between two consecutive rows of a row-major matrix or between two consecutive columns of a column-major matrix.
1 | int array[24]; for(int i = 0; i < 24; ++i) array[i] = i; |
The output will be 1
2
30 8 16
2 10 18
4 12 20
Slicing
- For fixed-size
matrix
:Map(dataPtr, stride)
- For dynamic-size
vector
:Map(dataPtr, size, stride)
- For dynamic-size
matrix
:Map(dataPtr, rows, cols, stride)
Operation
Arithmetic
Eigen doesn't provide implicit type promotion; Therefore, the scalar type of left-hand side and right-hand side must match.
Addition and Subtraction
Array-Array and Matrix-Matrix
- binary operator:
+
as ina + b
;-
as ina - b
- unary operator:
-
as in- a
- compound operator:
+=
as ina += b
;-=
as ina -= b
Array-Scalar
The operators are same as above.
A 'matrix-scalar' addition and subtraction is not supported; You are expected to explicitly convert the data type into array
first.
Multiplication and Division
Matrix-Scalar and Array-Scalar Multiplication and Division
- binary operator:
*
as inmatrix * scalar
orscalar * matrix
;/
as inmatrix / scalar
- compound operator:
*=
as inmatrix *= scalar
;/=
as inmatrix /= scalar
Matrix-Matrix
- binary operator:
*
as ina*b
- compound operator
*=
as ina*=b
(which is equivalent toa = a*b
)
Coefficient-wise Multiplication and Division
Array
class naturally provides coefficient-wise product and divition with operator*
and/
.Matrix
have.cwiseProduct(.)
only.
Coefficient-wise Operations
Array-Array and Array-Scalar Comparison
<
,<=
,>
,>=
.min(.)
,.max(.)
Matrix-Matrix and Matrix-Scalar Comparison
.cwiseMin(.)
,.cwiseMax(.)
.cwiseEqual(.)
,.cwiseNotEqual(.)
.min(.)
and .max(.)
construct an array whose coefficients are the minimum/maximum of the given two arrays.
Other STL-like operatons
Below could be applied to both Matrix
and Array
Array | Matrix | Array | Matrix |
---|---|---|---|
a.abs() , abs(a) |
m.cwiseAbs() |
a.abs2() , abs2(a) |
m.cwiseAbs2() |
a.inverse() , inverse(a) |
m.cwiseInverse() |
a.conjugate() , conj(a) |
m.conjugate() |
a.real() , real(a) |
real(m) |
a.imag() , imag(a) |
imag(m) |
a.sqrt() , sqrt(a) |
m.cwiseSqrt() |
Below are Array
only
Array | Array | Array | Array |
---|---|---|---|
a.exp() , exp(a) |
a.log() , log(a) |
a.log1p() , log1p(a) |
a.log10() , log10(a) |
a.pow(e) , pow(a,e) |
a.rsqrt() , rsqrt(a) |
a.square() , square(a) |
a.cube() , cube(a) |
a.sin() , sin(a) |
a.cos() , cos(a) |
a.tan() , tan(a) |
a.asin() , asin(a) |
a.acos() , acos(a) |
a.atan() , atan(a) |
a.sinh() , sinh(a) |
a.cosh() , cosh(a) |
a.tanh() , tanh(a) |
a.ceil() , ceil(a) |
a.floor() , floor(a) |
a.round() , round(a) |
a.isFinite() , isfinite(a) |
a.isInf() , isinf(a) |
a.isNaN() , isnan(a) |
Linear Algebra
Dot and Cross Product
1 | Vector3f m, n; |
Cross product is only for vectors of size 3.
Transpose, Conjugate, and Adjoint
Normal | Modifying In-place | |
---|---|---|
Transpose $ a^T $ | .transpose() |
.transposeInPlace() |
Conjugate $ {a} $ | .conjugate() |
.conjugateInPlace() |
Adjoint $ a^* $ | .adjoint() |
.adjointInPlace() |
Reverse $ a^{-1} $ | .reverse() |
.reverseInPlace() |
Norm and Trace
.squaredNorm()
and.norm()
- For vectors, \(\ell^{2}\) norm.
- For matrices, "Frobenius" or "Hilbert-Schmidt" norm.
.lpNorm<p>()
p
could be integers orInfinity
for computing \(l^{\infty}\) norm.
.trace()
Decompositions and Problem Solving
Please visit http://eigen.tuxfamily.org/dox/group__DenseLinearSolvers__chapter.html
Reduction
Reduction operations are operations that reduce a matrix or vector into a single value.
Given an array or matrix, below will return single values.
1 | Matrix3f m; |
Furthermore, for minCoeff
and maxCoeff
, the index of the value is also accessible by passing the address. 1
2
3Matrix3f m = Matrix3f::Random();
std::ptrdiff_t i, j;
float minOfM = m.minCoeff(&i,&j);
Boolean Reductions
.all()
returnstrue
if all of the coefficients are evaluated totrue
..any()
returnstrue
if at least one of the coefficients are evaluated totrue
..count()
returns the number of coefficients are evaluated totrue
.
Broadcasting and Partial Reduction
Partial Reduction
Partial reductions apply the reduction operations on each column or row, and return a column or row vector with the corresponding values.
For example,
1 | Eigen::MatrixXf mat(2,4); |
Broadcasting
Broadcasting could be applied to Matrix-Vector
and Array-ArrayXN
(equivalent for vector under Array
context) expression that interprets the Vector
as a matrix via replicating.
1 | mat << 1, 2, 6, 9, |
This expression means
\[\begin{bmatrix} 1 & 2 & 6 & 9 \\ 3 & 1 & 7 & 2 \end{bmatrix} + \begin{bmatrix} 0 & 0 & 0 & 0 \\ 1 & 1 & 1 & 1 \end{bmatrix} = \begin{bmatrix} 1 & 2 & 6 & 9 \\ 4 & 2 & 8 & 3\end{bmatrix}\]
The vector operand must be of type Vector
or ArrayXN
; Otherwise, a compile-time error will be raised.
Replication
Replicate< MatrixType, RowFactor, ColFactor >
describes the multiple replication of a matrix or vector.
MatrixType
, the type of the object we are replicatingRowFactor
, number of repetitions at compile time along the vertical direction, can beDynamic
.ColFactor
, number of repetitions at compile time along the horizontal direction, can beDynamic
.
1 | MatrixXi m(2, 3); |
m.replicate<3,2>()
or m.replicate(3, 2)
will be
\[\begin{bmatrix} 7 & 6 & 9 & 7 & 6 & 9 \\ -2 & 6 & -6 & -2 & 6 & -6 \\ 7 & 6 & 9 & 7 & 6 & 9 \\ -2 & 6 & -6 & -2 & 6 & -6 \\ 7 & 6 & 9 & 7 & 6 & 9 \\ -2 & 6 & -6 & -2 & 6 & -6 \end{bmatrix}\]
This can also combine with broadcasting
. 1
2Vector3i v(7, 2, 6);
cout << v.rowwise().replicate(5) << endl;
The output will be \[\begin{bmatrix} 7 & 7 & 7 & 7 & 7 \\ -2 & -2 & -2 & -2 & -2 \\ 6 & 6 & 6 & 6 & 6 \end{bmatrix}\]
Array-Matrix Conversion
Mixing matrices and arrays in an expression is forbidden with Eigen; Therefore, eigen provides explict type conversion approaches .matrix()
and .array()
; Both .matrix()
and .array()
can be used as rvalues or lvalues.
An exception is that it is valid to assign a matrix expression to an array variable, or to assign an array expression to a matrix variable.
Resize
Trying to resize a fixed-size matrix will trigger an assertion failure.
Get the Shape info
The shape of a matrix can be retrieved via rows()
, cols()
and size()
. These methods return the number of rows, the number of columns and the number of coefficients, respectively.
Reshape by API
resize(nRows, nCols)
could change the shape of a dynamic matrix. If the size become consistent, resize
is a no-operation; Otherwise, the values of the coefficients may change. If you want to keep the values invariant, use conservativeResize()
instead.
Reshape by assignment
Assignment is the action of copying a matrix into another, using operator =
. If the left-hand size is a dynamic matrix, Eigen may resize it implicitly in order to match the size of the matrix on the right-hand side.
1 | MatrixXf a(2,2); |
Expression Object and Aliasing
Expression Object
In Eigen, arithmetic operators such as operator +
don't perform computation by themselves, they just return an "expression object" describing the computation to be performed. The actual computation happens later, when the whole expression is required to be evaluated, typically in operator =
. Then optimizing compiler will output a perfectly optimized code. Thus, you should not be afraid of using relatively large arithmetic expressions with Eigen.
For example,
1 | VectorXf a(50), b(50), c(50), d(50); |
Eigen will do one loop only, which will look like this:
1 | for(int i = 0; i < 50; ++i) |
Alias
In Eigen, aliasing refers to assignment statements that the same matrix/array/vector appears on both left and right size of the assignment operator =
. .eval()
may be required to avoid aliasing by evaluating the right-hand side fully into a temporary matrix/array/vector and then assign it to the left-hand side.
1 | Matrix2i a; a << 1, 2, 3, 4; |
Then a
will become
1 | 1 2 |
Aliasing cannot be detected at compile time. Most operations in Eigen assume that there are no aliasing problem, except for Squared Matrix multiplication
; By default, if matA is a squared matrix, matA * matA
will be interpreted as
1 | tmp = matA*matA; |
If you're sure that matrix product can be safely evaluated into the destination matrix without aliasing issue, then you can use the .noalias()
function to avoid the temporary, like matB.noalias() = matA * matA
. This allows Eigen to evaluate the matrix product matA * matA
directly into matB
.
Geometric Module
Geometry
module provides two different kinds of geometric transformations:
- Abstract transformations, which are not represented as matrices, such as
rotation
,translation
,scaling
. But it's valid to operate with matrix/vectors or convert them into matrices. Transform
, Projective or affine transformation matrices- It's allowed to construct
Transform
from abstract transformations, for example,Transform t (AngleAxis(angle,axis))
Accessor
- Transform matrix
t.matrix()
=> \((N + 1) \times (N + 1)\) matrix - Coefficients
t(i,j)
,t.matrix()(i,j)
=> scalar type - Translation part
t.translation()
=> \(N \times 1\) vector - Linear part
t.linear()
=> \(N \times N\) matrix - Rotation
t.rotation()
=> \(N \times N\) matrix
Compatibility with OpenGL
- For manipulating OpenGL
4x4
matrices thenAffine3X
are what you want. - Transform::data() method could pass the transformation matrix to OpenGL.
Conversion
Any transformations can be converted to any other types of the same nature, or to a more generic type. For example:
1 | AngleAxisf aa; aa = Quaternionf(..); |
Operations
2D rotation with angle |
Rotation2D<float> rot2(angle) |
3D rotation with angle and normalized axis |
AngleAxis<float> aa(angle, axis) |
3D rotation with quaternion |
Quaternion<float> q; q = AngleAxis<float>(angle, axis) |
Scaling | Scaling(sx, sy) Scaling(sx, sy, sz) Scaling(s) Scaling(vecN) |
Translation | Translation<float,2>(tx, ty) Translation<float,3>(tx, ty, tz) Translation<float,N>(s) Translation<float,N>(vecN) |
Affine transformation | Transform<float,N,Affine> t = composition_of_others |
Slerp | rot1.slerp(alpha,rot2) |
Normalize | vector.normalized() |
Hnormalize | hnormalized() , divided by the last coefficient first then normalize it |
Homogeneous | v.homogeneous() , add 1 at the end |
To transform a set of vectors, use rotation matrix
; Otherwise, usa Quaternion
as it is compact, fast and stable.
For further details please visit http://eigen.tuxfamily.org/dox/group__Geometry__Module.html
Composition
Type | Before | After |
---|---|---|
Translation | t.translate(vector) |
t.pretranslate(vector) |
Rotation | t.rotate(rotation) |
t.prerotate(rotation) |
Scaling | t.scale(vector) , t.scale(s) |
t.prescale(vector) , t.prescale(s) |
2D Shear | t.shear(sx,sy) |
t.preshear(sx,sy) |