10

Eigen is a well known matrix Library in c++. I am having trouble finding an in built function to simply push an item on to the end of a matrix. Currently I know that it can be done like this:

Eigen::MatrixXd matrix(10, 3);
long int count = 0;
long int topCount = 10;
for (int i = 0; i < listLength; ++i) {
    matrix(count, 0) = list.x;
    matrix(count, 1) = list.y;
    matrix(count, 2) = list.z;
    count++;
    if (count == topCount) {
        topCount *= 2;
        matrix.conservativeResize(topCount, 3);
    }
}
matrix.conservativeResize(count, 3);

And this will work (some of the syntax may be out). But its pretty convoluted for a simple thing to do. Is there already an in built function?

Pavan Yalamanchili
  • 12,021
  • 2
  • 35
  • 55
Fantastic Mr Fox
  • 32,495
  • 27
  • 95
  • 175

2 Answers2

19

There is no such function for Eigen matrices. The reason for this is such a function would either be very slow or use excessive memory.

For a push_back function to not be prohibitively expensive it must increase the matrix's capacity by some factor when it runs out of space as you have done. However when dealing with matrices, memory usage is often a concern so having a matrix's capacity be larger than necessary could be problematic. If it instead increased the size by rows() or cols() each time the operation would be O(n*m). Doing this to fill an entire matrix would be O(n*n*m*m) which for even moderately sized matrices would be quite slow.

Additionally, in linear algebra matrix and vector sizes are nearly always constant and known beforehand. Often when resizeing a matrix you don't care about the previous values in the matrix. This is why Eigen's resize function does not retain old values, unlike std::vector's resize.

The only case I can think of where you wouldn't know the matrix's size beforehand is when reading from a file. In this case I would either load the data first into a standard container such as std::vector using push_back and then copy it into an already sized matrix, or if memory is tight run through the file once to get the size and then a second time to copy the values.

David Brown
  • 13,336
  • 4
  • 38
  • 55
  • 1
    Well Eigen lets you define MaxRows MaxCols at compile time, so even if you don't know the exact size till run time the memory requirements remain bounded (no memory allocations would be required for the resize/push_back operations). It would be nice if you could push_back elements, rows, and cols as long as size < MaxRows * MaxCols. – gnzlbg Mar 07 '14 at 14:05
  • 1
    "Additionally, in linear algebra matrix and vector sizes are nearly always constant and known beforehand." In most iterative algorithms you do not know how many iterations will be required until a convergence criterion is satisfied. – Lindon Jul 12 '15 at 15:09
  • Modulo the caveats above, if I really needed this, I would allocate a largish matrix and then expose a block that represents the used portion of the matrix. To push, you fill in values of the storage matrix and create a new expanded block that covers the new used portion. When you exhaust the size of your storage matrix, reallocate it doubling each dimension. This is amortized constant, same as std::vector. Once could wrap this as a custom class. (I don't know the particular limitations on blocks compared to matrices. I've only used them a few times.) – THK Jul 01 '16 at 16:54
  • "The only case I can think of where you wouldn't know the matrix's size beforehand is when reading from a file" Dynamic applications can have varying matrix sizes for a variety of reasons. For example using a matrix to represent an array of points is more performant than using the stl:;vector. – Makogan Jun 10 '20 at 20:20
11

There is no such function, however, you can build something like this yourself:

using Eigen::MatrixXd;
using Eigen::Vector3d;

template <typename DynamicEigenMatrix>
void push_back(DynamicEigenMatrix& m, Vector3d&& values, std::size_t row)
{
    if(row >= m.rows()) {
        m.conservativeResize(row + 1, Eigen::NoChange);
    }
    m.row(row) = values;
}


int main()
{
    MatrixXd matrix(10, 3);
    for (std::size_t i = 0; i < 10; ++i) {
       push_back(matrix, Vector3d(1,2,3), i);
    }
    std::cout << matrix << "\n";
    return 0;
}

If this needs to perform too many resizes though, it's going to be horrendously slow.

Yuushi
  • 25,132
  • 7
  • 63
  • 81