CODE: Eigen Matrices and Arrays — Types, Initialization, and Operations

Eigen matrices are the core abstraction for linear algebra in C++. This post covers fixed and dynamic matrix types, zero and comma initialization, coefficient access, and converting between Eigen matrices and STL vectors — the essential toolkit for numerical computing in C++.

Matrices are the workhorse of linear algebra. Eigen's Matrix and Array classes provide a comprehensive, type-safe API for creating, initializing, and manipulating matrices of any size and scalar type. Understanding the difference between Matrix (linear algebra operations) and Array (coefficient-wise operations) is key to writing correct and efficient numerical code.


Matrix Types in Eigen

Eigen distinguishes between two kinds of 2D containers:

TypeOperationsUse Case
MatrixLinear algebra (matrix multiply, decompositions)Geometry, linear systems, transformations
ArrayCoefficient-wise (element-by-element)Signal processing, elementwise math

Both support fixed-size and dynamic-size variants.


Fixed-Size Matrices

Fixed-size matrices have dimensions known at compile time:

cpp
Eigen::Matrix2d  // 2x2 matrix of double
Eigen::Matrix3d  // 3x3 matrix of double
Eigen::Matrix4d  // 4x4 matrix of double (common for 3D transforms)

Eigen::Matrix2f  // 2x2 matrix of float
Eigen::Matrix2cf // 2x2 matrix of complex<float>

Naming convention: Matrix<N><scalar> where N is the dimension (same rows and cols) and scalar is the type suffix.


Dynamic Matrices

Dynamic matrices have their dimensions set at runtime:

cpp
Eigen::MatrixXd  // Dynamic matrix of double
Eigen::MatrixXf  // Dynamic matrix of float
Eigen::MatrixXi  // Dynamic matrix of int

cpp
// Create a 3x4 dynamic matrix
Eigen::MatrixXd m(3, 4);


Initializing Matrices

Zero Initialization

cpp
// Dynamic zero matrix
Eigen::MatrixXd X = Eigen::MatrixXd::Zero(3, 2);
// 3x2 matrix of all zeros

// Fixed-size zero matrix
Eigen::Matrix3d Z = Eigen::Matrix3d::Zero();

Comma Initialization

The comma initializer fills the matrix row by row, left to right:

cpp
// Fixed-size 2x2 matrix
Eigen::Matrix2d a;
a << 1, 2,
     3, 4;
// a = | 1  2 |
//     | 3  4 |

// Dynamic 2x2 matrix
Eigen::MatrixXd b(2, 2);
b << 2, 3,
     1, 4;

Other Initialization Methods

cpp
// All ones
Eigen::MatrixXd ones = Eigen::MatrixXd::Ones(3, 3);

// Identity matrix
Eigen::MatrixXd I = Eigen::MatrixXd::Identity(4, 4);

// Random values
Eigen::MatrixXd rand = Eigen::MatrixXd::Random(3, 3);

// Constant value
Eigen::MatrixXd constant = Eigen::MatrixXd::Constant(2, 3, 5.0);
// 2x3 matrix where every element is 5.0


Coefficient Accessors

Access matrix elements using the (row, col) syntax (0-indexed):

cpp
#include <Eigen/Dense>
#include <iostream>

int main() {
    Eigen::MatrixXd m(2, 2);
    m(0, 0) = 3;
    m(1, 0) = 2.5;
    m(0, 1) = -1;
    m(1, 1) = m(1, 0) + m(0, 1);  // 2.5 + (-1) = 1.5

    std::cout << "Matrix m:\n" << m << std::endl;
    // Matrix m:
    // 3   -1
    // 2.5  1.5

    Eigen::VectorXd v(2);
    v(0) = 4;
    v(1) = v(0) - 1;  // 3

    std::cout << "Vector v:\n" << v << std::endl;
    // Vector v:
    // 4
    // 3

    return 0;
}


Matrix Information

cpp
Eigen::MatrixXd m(3, 4);

std::cout << m.rows()  << std::endl;   // 3
std::cout << m.cols()  << std::endl;   // 4
std::cout << m.size()  << std::endl;   // 12 (total elements)
std::cout << m.norm()  << std::endl;   // Frobenius norm


Matrix Operations

cpp
Eigen::MatrixXd A(2, 2);
A << 1, 2, 3, 4;

Eigen::MatrixXd B(2, 2);
B << 5, 6, 7, 8;

// Matrix addition and subtraction
Eigen::MatrixXd C = A + B;  // element-wise addition
Eigen::MatrixXd D = A - B;  // element-wise subtraction

// Matrix multiplication (linear algebra, not element-wise)
Eigen::MatrixXd E = A * B;
// E(0,0) = 1*5 + 2*7 = 19
// E(0,1) = 1*6 + 2*8 = 22
// E(1,0) = 3*5 + 4*7 = 43
// E(1,1) = 3*6 + 4*8 = 50

// Scalar multiplication
Eigen::MatrixXd F = 2.0 * A;

// Transpose
Eigen::MatrixXd T = A.transpose();
// T = | 1  3 |
//     | 2  4 |

// Inverse (square matrices only)
Eigen::MatrixXd Inv = A.inverse();

// Determinant
double det = A.determinant();


Arrays vs. Matrices: Coefficient-wise vs. Linear Algebra

cpp
Eigen::MatrixXd m1(2, 2);
m1 << 1, 2, 3, 4;

Eigen::MatrixXd m2(2, 2);
m2 << 5, 6, 7, 8;

// Matrix multiplication (linear algebra)
Eigen::MatrixXd product = m1 * m2;
// [[19, 22], [43, 50]]

// Coefficient-wise multiplication (element by element)
Eigen::MatrixXd cwise = m1.cwiseProduct(m2);
// [[5, 12], [21, 32]]

// Using Array for coefficient-wise
Eigen::ArrayXXd a1 = m1.array();
Eigen::ArrayXXd a2 = m2.array();
Eigen::ArrayXXd cwise2 = a1 * a2;  // element-wise *

Converting Between Matrix and Array

cpp
Eigen::MatrixXd mat = some_matrix;
Eigen::ArrayXXd arr = mat.array();   // Matrix -> Array
Eigen::MatrixXd mat2 = arr.matrix(); // Array -> Matrix


2D Arrays

Array types are directly available for 2D coefficient-wise work:

cpp
Eigen::ArrayXXd b1(2, 3);
b1 << 1, 2, 3,
      4, 5, 6;

std::cout << "b1 =\n" << b1 << std::endl;
// b1 =
// 1 2 3
// 4 5 6

std::cout << "rows: " << b1.rows() << ", cols: " << b1.cols() << std::endl;
// rows: 2, cols: 3

// Coefficient-wise square
Eigen::ArrayXXd b1_sq = b1 * b1;
// b1_sq = [[1, 4, 9], [16, 25, 36]]

// Coefficient-wise sqrt
Eigen::ArrayXXd b1_sqrt = b1.sqrt();


Converting from STL Vector to Eigen Matrix

cpp
#include <Eigen/Dense>
#include <vector>

// Flat STL vector mapped as a column vector
int N = 100;
std::vector<float> v(N, 1.0f);
Eigen::VectorXf ev = Eigen::VectorXf::Map(&v[0], N);

// Flat STL vector mapped as a 2D matrix (row-major)
std::vector<double> flat(6, 1.0);
Eigen::Map<Eigen::MatrixXd> mat(flat.data(), 2, 3);  // 2 rows, 3 cols


Converting from Eigen Matrix to STL Vector

cpp
Eigen::VectorXd ev(5);
ev << 1.0, 2.0, 3.0, 4.0, 5.0;

// Method 1: constructor from pointer range
std::vector<double> v1(ev.data(), ev.data() + ev.size());

// Method 2: resize and Map
std::vector<double> v2;
v2.resize(ev.size());
Eigen::Map<Eigen::VectorXd>(v2.data(), v2.size()) = ev;

Generic Template for Column Matrix to STL Vector

cpp
template<typename T>
std::vector<T> EigenColumnToVector(
    const Eigen::Matrix<T, Eigen::Dynamic, Eigen::Dynamic>& matrix)
{
    if (matrix.cols() != 1) {
        throw std::invalid_argument("Matrix must have exactly one column");
    }

    std::vector<T> result;
    result.resize(static_cast<std::size_t>(matrix.rows()));

    for (Eigen::Index i = 0; i < matrix.rows(); ++i) {
        result[static_cast<std::size_t>(i)] = matrix(i, 0);
    }

    return result;
}


Matrix Decompositions

Eigen provides a full set of matrix decompositions for solving linear systems:

cpp
Eigen::MatrixXd A(3, 3);
A << 2, 1, 1,
     4, 3, 3,
     8, 7, 9;

Eigen::VectorXd b(3);
b << 1, 1, 1;

// Solve Ax = b using LU decomposition (general case)
Eigen::VectorXd x = A.lu().solve(b);

// Solve using QR (more numerically stable)
Eigen::VectorXd x2 = A.colPivHouseholderQr().solve(b);

// Solve symmetric positive definite system (fastest)
Eigen::MatrixXd S = A.transpose() * A;  // S is positive definite
Eigen::VectorXd x3 = S.llt().solve(A.transpose() * b);


Summary

text
Eigen Matrix Quick Reference:
+-------------------------+-------------------------------------------+
| Type                    | Description                               |
+-------------------------+-------------------------------------------+
| Matrix2d/3d/4d          | Fixed N×N double matrix                   |
| MatrixXd                | Dynamic double matrix                     |
| ArrayXXd                | 2D array for coefficient-wise operations  |
| ::Zero(r, c)            | Zero matrix                               |
| ::Ones(r, c)            | Ones matrix                               |
| ::Identity(n, n)        | Identity matrix                           |
| ::Random(r, c)          | Random matrix                             |
| m(i, j)                 | Element access (0-indexed)                |
| m.rows() / m.cols()     | Dimensions                                |
| m.transpose()           | Transposed matrix                         |
| m.inverse()             | Matrix inverse                            |
| m.determinant()         | Determinant                               |
| m * n                   | Matrix multiplication                     |
| m.array() * n.array()   | Coefficient-wise multiplication           |
| Map(&v[0], r, c)        | Zero-copy view of raw data                |
+-------------------------+-------------------------------------------+


Conclusion

Eigen's Matrix and Array types provide a complete toolkit for 2D numerical data. Fixed-size matrices offer compile-time optimization and stack allocation. Dynamic matrices handle arbitrary dimensions at runtime. The distinction between Matrix (linear algebra semantics) and Array (coefficient-wise semantics) ensures you always get the mathematical operation you intend.

Combined with the vector operations from the previous post, you now have the full foundation for implementing any linear algebra algorithm — from simple matrix transformations to complex decompositions — using Eigen in C++.