0

I'm currently building a C++ code that uses matrix inversion. Looking for a library to do this, I noticed that Boost and Eigen3 give different results when inverting my 18x18 matrix ! The difference is not negligible, and therefore leads my algorithm to finally deliver very different results.

Did anyone have the same issue ? What is the most reliable library for matrix inversion ?

Please see below a piece of code that shows how I build the 18x18 matrix, and the difference between the inverted matrices. The infinity-norm of the difference matrix is about 130 with my data. Tested with Eigen-3.3.9 and Boost-1.73.

Note that I already read the following thread : Converting from boost::ublas to Eigen gives different results, but didn't find a real answer to my issue, since I would really like to make a code that is not so dependent on the matrix inversion library I use.

Any help would be greatly appreciated !

#include <Eigen/Dense>

#include <boost/numeric/ublas/lu.hpp>
using namespace boost::numeric;

#include <cmath>
#include <stdexcept>
#include <iostream>
#include <iomanip>

#define SIZE 18

Eigen::MatrixXd initMatrix_Eigen() {
    Eigen::MatrixXd matrix(SIZE, SIZE);
    for (size_t i = 0; i < SIZE / 2; i++) {
        for (size_t j = 0; j < SIZE; j++) {
            matrix(2 * i, j) = pow((double)i + 1, (double)j + 2);
            matrix(2 * i + 1, j) = (j + 2) * pow((double)i + 1, (double)j + 1);
        }
    }
    return matrix;
}

ublas::matrix<double> initMatrix_Boost() {
    ublas::matrix<double> matrix(SIZE, SIZE);
    for (size_t i = 0; i < SIZE / 2; i++) {
        for (size_t j = 0; j < SIZE; j++) {
            matrix(2 * i, j) = pow((double)i + 1, (double)j + 2);
            matrix(2 * i + 1, j) = (j + 2) * pow((double)i + 1, (double)j + 1);
        }
    }
    return matrix;
}

Eigen::MatrixXd invertMatrix_Eigen(const Eigen::MatrixXd& matrix) {
    return matrix.inverse();
}

ublas::matrix<double> invertMatrix_Boost(const ublas::matrix<double>& matrix) {
    if (matrix.size1() != matrix.size2()) throw("Input matrix must be square");
    ublas::matrix<double> tmpMatrix(matrix);
    ublas::permutation_matrix<size_t> permutationMatrix(tmpMatrix.size1());
    if (ublas::lu_factorize(tmpMatrix, permutationMatrix) != 0) throw std::invalid_argument("LU factorization failed");
    ublas::matrix<double> inverse(tmpMatrix.size1(), tmpMatrix.size2());
    inverse.assign(boost::numeric::ublas::identity_matrix<double>(tmpMatrix.size1()));
    ublas::lu_substitute(tmpMatrix, permutationMatrix, inverse);
    return inverse;
}

void main(int argc, char** argv)
{
    std::cout << std::setprecision(12);

    Eigen::MatrixXd matrix_Eigen = initMatrix_Eigen();
    Eigen::MatrixXd inverse_Eigen = invertMatrix_Eigen(matrix_Eigen);
    std::cout << "Eigen inverse matrix :" << std::endl;
    std::cout << inverse_Eigen << std::endl;

    ublas::matrix<double> matrix_Boost = initMatrix_Boost();
    ublas::matrix<double> inverse_Boost_tmp = invertMatrix_Boost(matrix_Boost);
    // Convert inverse_Boost_tmp into an Eigen matrix called inverse_Boost :
    const size_t size = inverse_Boost_tmp.size1();
    Eigen::MatrixXd inverse_Boost(size, size);
    for (size_t i = 0; i < size; i++)
        for (size_t j = 0; j < size; j++)
            inverse_Boost(i, j) = inverse_Boost_tmp(i, j);
    std::cout << "Boost inverse matrix :" << std::endl;
    std::cout << inverse_Boost << std::endl;

    Eigen::MatrixXd difference = inverse_Eigen - inverse_Boost;
    std::cout << "Difference between inverse matrices :" << std::endl;
    std::cout << difference << std::endl;
    std::cout << "NormInf(difference) = " << difference.lpNorm<Eigen::Infinity>() << std::endl;
}
ConanLord
  • 73
  • 5
  • 2
    If your matrix is numerically unstable, different inversion algorithms will give very different results. You should probably post this question to a more math-oriented stackexchange. – n. m. could be an AI Sep 20 '22 at 17:34
  • 4
    Note that Eigen actually provides several different ways to invert a matrix, you could try some of the others. The same may be true for ublas, but I am less familiar with it. Do they actually give consistent results on small "nice" matrices, as a sanity check of your code? – Marc Glisse Sep 20 '22 at 18:08
  • 3
    Have you checked whether your matrix is even invertible? – Homer512 Sep 20 '22 at 18:45
  • @Homer512 Yes, I am sure that the matrix is invertible. I tried to compute "matrix * inverseMatrix" and checked that the result is "close" to the identity matrix – ConanLord Sep 21 '22 at 08:14
  • @Marc Glisse Yes, I tried the example given by "sehe" here : https://stackoverflow.com/questions/65991639/converting-from-boostublas-to-eigen-gives-different-results and I get the expected result with both Eigen and Boost. Would you be kind enough to post a link to different ways to invert a matrix with Eigen ? – ConanLord Sep 21 '22 at 08:33
  • 1
    When I solve this example with a different online matrix tool I get a warning that the matrix is singular or badly scaled. It goes on to solve it and I can to inv(M) * M = I, but it is poorly scaled. – Matt Sep 21 '22 at 13:49
  • 3
    Search for "inverse" in https://eigen.tuxfamily.org/dox/classEigen_1_1FullPivLU.html , https://eigen.tuxfamily.org/dox/classEigen_1_1ColPivHouseholderQR.html , https://eigen.tuxfamily.org/dox/classEigen_1_1CompleteOrthogonalDecomposition.html , etc. Also, do make sure you actually need the inverse. If you are only going to use it to solve linear systems, you should use `solve` instead. – Marc Glisse Sep 21 '22 at 14:29

0 Answers0