Eigen Linear Algebra Library

repository·master·Indexed 22 days ago

https://github.com/px4/eigen

A high-performance C++ template library for linear algebra, providing matrices, vectors, and numerical solvers. This documentation covers the Eigen Tensor module, including resizable Tensor, compile-time sized TensorFixedSize, and memory-mapping via TensorMap. It details lazy evaluation, memory layouts (ColMajor and RowMajor), execution devices (DefaultDevice, ThreadPoolDevice, GpuDevice), and tensor initialization methods.

Tokens
20.5K
Snippets
67
Records
83
Agent score
77%

What's inside px4-eigen

  1. Overview of Eigen linear algebra library

    master
    Eigen is a C++ template library designed for linear algebra operations. It provides high-performance implementations for matrices, vectors, numerical solvers, and various related mathematical algorithms.
  2. How Tensor operations and lazy evaluation work

    master

    Most Tensor methods return non-evaluated Operations rather than immediate results. These operations can be chained together (e.g., a + b.constant(2.0f)).

    Lazy Evaluation: The chain of operations is evaluated lazily, meaning the actual computation typically only occurs when the operation is assigned to a Tensor object. This allows the library to optimize the execution of the entire expression chain.

  3. Avoid using C++ 'auto' with Tensor operations

    master

    Using auto with Tensor expressions does not result in a Tensor object; instead, it captures the non-evaluated expression tree (the "Operation"). You cannot access elements directly from an auto variable. To get the actual values, you must assign the expression to a concrete Tensor type.

    When to use auto: Only use auto when you intentionally want to delay evaluation to build a larger expression tree.

    Tensor<float, 3> t1(2, 3, 4);
    Tensor<float, 3> t2(2, 3, 4);
    
    auto t4 = t1 + t2; 
    // t4 is an expression tree, NOT a Tensor.
    // cout << t4(0, 0, 0); // This will cause a COMPILATION ERROR!
    
    Tensor<float, 3> t3 = t4; // Now it is evaluated and works.
  4. Understand Eigen Tensor Datatypes

    master

    When working with Eigen Tensors, the documentation uses several pseudo-types to describe methods and operations:

    • <Tensor-Type>::Dimensions: An array-like object of ints representing the dimensions of a tensor. It has a .size attribute and supports array-style indexing. See dimensions().
    • <Tensor-Type>::Index: An integer type used for indexing tensors along their dimensions. See operator(), dimension(), and size().
    • <Tensor-Type>::Scalar: The underlying data type of the individual tensor elements (e.g., float for Tensor<float>).
    • <Operation>: Indicates that a method returns a deferred tensor operation rather than a concrete tensor. An <Operation> must be evaluated (e.g., by assigning it to a tensor) before its values can be accessed, or it must be wrapped in a TensorRef.
  5. Understand Eigen Tensor lazy evaluation

    master

    Eigen Tensor operations (like +, *, .exp()) do not perform computations immediately. Instead, they construct a lightweight "tensor operator" object (e.g., TensorCwiseBinaryOp) that represents the expression tree. The actual computation is deferred until the expression is assigned to a concrete Tensor type (like Tensor, TensorFixedSize, or TensorMap). This mechanism enables lazy evaluation and high-performance optimizations.

    Tensor<float, 3> t1(2, 3, 4);
    Tensor<float, 3> t2(2, 3, 4);
    // t1 + t2 is a TensorCwiseBinaryOp object, NOT the sum itself.
    // The addition only happens during assignment to t3:
    Tensor<float, 3> t3 = t1 + t2;
  6. Configure and manage TensorLayout

    master

    Eigen Tensors support two memory layouts: ColMajor (default) and RowMajor.

    Important Constraints:

    • Support: Only ColMajor is currently fully supported; RowMajor is not recommended for all operations.
    • Compatibility: All operands in a tensor expression must use the same layout. Mixing layouts results in a compilation error.
    • Specifying Layout: Layout is specified as a template argument in the tensor type.

    Changing Layout: You can use the swap_layout() method to change the layout. Note that swap_layout() also reverses the order of the dimensions. To change the layout while preserving dimension order, combine swap_layout() with .shuffle() using a permutation array.

    // Explicitly specifying layouts
    Tensor<float, 3, ColMajor> col_major; // Default
    TensorMap<Tensor<float, 3, RowMajor>> row_major(data, ...);
    
    // Swapping layout (reverses dimension order)
    Tensor<float, 2, ColMajor> col_major(2, 4);
    Tensor<float, 2, RowMajor> row_major(2, 4);
    auto swapped = row_major.swap_layout(); // Dimensions are now 4x2
    
    // Swapping layout while preserving dimension order
    array<int, 2> shuffle(1, 0);
    auto preserved = row_major.swap_layout().shuffle(shuffle);
  7. How to use unsupported Eigen modules

    master

    The unsupported/ directory contains modules provided "as is" without official support. To use these modules in your project, you must ensure the compiler can find the headers. You can use one of two methods:

    1. Add to include path: Add the path_to_eigen/unsupported directory to your project's include path. You can then include modules using the standard Eigen syntax: #include <Eigen/ModuleHeader>

    2. Direct include: Keep your include path pointing to the main Eigen directory and include the unsupported path explicitly: #include <unsupported/Eigen/ModuleHeader>

    #include <Eigen/ModuleHeader>
    // OR
    #include <unsupported/Eigen/ModuleHeader>
  8. Compile the BLAS library module

    master
    The BLAS library module is built on top of Eigen but is not included in the default build. To compile this specific module, you must run the make blas command from within your build directory.
    make blas
  9. Control when Tensor expressions are evaluated

    master

    You can force the evaluation of a tensor expression using several methods:

    1. Assignment to a Tensor: Assigning an expression to Tensor, TensorFixedSize, or TensorMap triggers evaluation.
    2. Using .eval(): Inserts a call to materialize an intermediate value in the expression tree. This is useful for improving performance in complex expressions or avoiding aliasing issues.
    3. Assigning to a TensorRef: Allows accessing individual elements of an expression without materializing the entire tensor. This is efficient if you only need a subset of values, but slower if you access all values.
    // 1. Assignment to Tensor
    Tensor<float, 3> result = (t1 + t2).exp();
    
    // 2. Using .eval() to prevent redundant computation or aliasing
    // Example: preventing re-computation of maximum()
    Tensor<...> Y = ((X - X.maximum(depth_dim).eval().reshape(dims2d).broadcast(bcast)) * beta).exp();
    
    // Example: preventing aliasing in Y = Y / ...
    Y = Y / (Y.sum(depth_dim).eval().reshape(dims2d).broadcast(bcast));
    
    // 3. Using TensorRef for partial access
    TensorRef<Tensor<float, 3>> ref = ((t1 + t2) * 0.2f).exp();
    float val = ref(0, 0, 0); // Evaluates on the fly
  10. Choose a Tensor execution device

    master

    Eigen Tensors support different execution backends via the .device() method. If no device is specified, the default is a single-threaded CPU implementation optimized for Intel CPUs (SSE, AVX, FMA). To use a different device, you must declare the result Tensor explicitly and call .device() as the last call on the left of the assignment operator.

    Supported devices:

    • DefaultDevice: Single-threaded CPU (default).
    • ThreadPoolDevice: Multi-threaded CPU execution.
    • GpuDevice: GPU execution (requires explicit CUDA memory allocation).
    // Using a ThreadPoolDevice for multi-threaded CPU execution
    Eigen::ThreadPoolDevice my_device(4); // 4 threads
    Eigen::Tensor<float, 2> c(30, 50);
    
    // The .device() call must be the last call on the left of the operator=
    c.device(my_device) = a.contract(b, dot_product_dims);
  11. Use the Translation class for geometric transformations

    master

    The Translation<Scalar, Dim> class represents a geometric translation. It is primarily designed to simplify the construction and updating of Transform objects (like AffineTransformType or IsometryTransformType) rather than being used as a standalone storage for transformation matrices.

    Key features:

    • Constructors: Initialize via individual coordinates (for 2D or 3D), a VectorType, or an existing Translation object.
    • Accessors: Retrieve or modify translation components using .x(), .y(), and .z() or by accessing the underlying .vector() or .translation().
    • Composition: Use the * operator to concatenate translations with other transformations (Scaling, Rotation, or general Linear transformations) or to apply a translation to a vector.
    • Identity: Use Translation::Identity() to get a zero-translation object.
    // Example: Creating and applying a 3D translation
    Eigen::Translation3d t(1.0, 2.0, 3.0);
    Eigen::Vector3d v(0.0, 0.0, 0.0);
    
    // Apply translation to a vector
    Eigen::Vector3d translated_v = t * v;
    
    // Concatenate with an identity translation
    Eigen::Translation3d t_identity = Eigen::Translation3d::Identity();
  12. Use the AngleAxis class for 3D rotations

    master

    The AngleAxis class represents a 3D rotation as an angle (in radians) around an arbitrary 3D axis. It is primarily used as an intermediate object to facilitate the creation of other rotation representations like Quaternion or rotation matrices.

    Key Requirements

    • Axis Normalization: When constructing an AngleAxis object, the axis vector must be normalized. If the axis is not a unit vector, the resulting rotation will be invalid.
    • Precision Types: For convenience, Eigen provides AngleAxisf (float) and AngleAxisd (double).

    Common Operations

    • Conversion: You can convert an AngleAxis to a 3x3 rotation matrix using .toRotationMatrix() or to a Quaternion via multiplication or explicit conversion.
    • Concatenation: Use the * operator to concatenate an AngleAxis with another AngleAxis or a Quaternion. This returns a Quaternion.
    • Inversion: Use .inverse() to get an AngleAxis representing the opposite rotation.
    • Euler Angles: You can mimic Euler angles by combining AngleAxis with unit vectors (e.g., Matrix3::UnitX()).
    // Example: Creating a rotation of 0.5 radians around the Z-axis
    Eigen::AngleAxisd rotation(0.5, Eigen::Vector3d::UnitZ());
    
    // Convert to a rotation matrix
    Eigen::Matrix3d mat = rotation.toRotationMatrix();
    
    // Convert to a quaternion
    Eigen::Quaterniond q(rotation);