1
votes

I'm using Eigen to write some C++ linear algebra code. I've got to manipulate some not-so-small matrices (greater than 4x4, but smaller than 50x50, if that matters), whose sizes are all entirely known at compile time.

I would love to benefit from the compile-time size checks that Eigen library can perform around matrix operations, which trigger an error whenever, say, a sum between different-size matrices happens in the code, however I'm also scared about the possible abuse of the stack that I could make if I don't dinamically allocate those relatively big objects. Performance concerns doesn't bother me.

The Eigen documentation has a brief paragraph about fixed and dynamic size matrices, where my previous concerns are discussed, but unfortunately there is no emphasys on the compile time checks which I would love to maintain. The documentation suggests:

Use fixed sizes for very small sizes where you can, and use dynamic sizes for larger sizes or where you have to.

In the end, my question: is there a way in Eigen to have a dynamically allocated Matrix with compile-time known size, in a way to retain the usual compile-time checks that we have for standard fixed-size matrices?

Something like this:

using MyMatrix = MatrixXd<12, 15>; // Currently I can only do Matrix<double, 12, 15>
using MyVector = MatrixXd<14, 1>;
MyMatrix M;
MyVector v;
auto w = M * v; // This should trigger an INVALID_MATRIX_PRODUCT error

where MatrixXd<n, m> is the hypothetical dynamically allocated, compile-time known size matrix I would like to use!

2
Fixed-sized matrices with heap-allocated storage are indeed not supported (it could actually also be beneficial when moving or swapping matrix objects). Maybe do a feature request for that (API-wise this could be achieved by adding a flag to the Options template parameter)chtz
Also, if you don't have hundreds of these matrices or working on some embedded systems, you should be fine with occupying a few KiB on the stack.chtz

2 Answers

3
votes

You can abuse inheritance from Eigen classes that provide Matrix-like wrappers:

  1. Eigen::Block

    struct MatrixHelper { Eigen::MatrixXd mat; };
    
    template <int Rows, int Cols>
    struct CheckedDynamicMatrix
      : MatrixHelper, Eigen::Block<Eigen::MatrixXd, Rows, Cols>
    {
      using Block = Eigen::Block<Eigen::MatrixXd, Rows, Cols>;
      CheckedDynamicMatrix() :
        MatrixHelper { Eigen::MatrixXd(Rows, Cols) },
        Block { mat.topLeftCorner<Rows, Cols>() }
      {}
    };
    
  2. Eigen::Map

    template <int Rows, int Cols>
    struct CheckedDynamicMatrix
      : Eigen::Map<Eigen::Matrix<double, Rows, Cols>>
    {
      using Map = Eigen::Map<Eigen::Matrix<double, Rows, Cols>>;
      std::unique_ptr<double[]> data;
      CheckedDynamicMatrix() :
        Map { nullptr },
        data { new double[Rows * Cols] }
      {
        new (static_cast<Map*>(this)) Map(data.get(), Rows, Cols);
      }
    };
    

For full functionality, you should also add the necessary constructor and assignment operator as described in Eigen: Inheriting from Matrix

2
votes

You can't persuade1 Eigen to do both, but you can dynamically allocate a fixed-size matrix.

using MyMatrix = Matrix<double, 12, 15>;
using MyVector = Matrix<double, 14, 1>;
auto M = std::make_unique<MyMatrix>();
auto v = std::make_unique<MyVector>();
auto w = *M * *v; // compile time error, as desired

Footnote 1: There isn't documentation as to what happens if you set MaxRows and MaxCols with a fixed size Matrix, so you could try Matrix<double, 12, 15, 0, Dynamic, Dynamic> and see what happens, but beware the behaviour of that may change without warning, or could lead to undefined behaviour.