非常小方阵的特征线性解算器

时间:2018-06-18 12:19:55

标签: c++ linear-algebra eigen

我在C ++程序中使用Eigen来求解非常小的方阵(4X4)的线性方程。

我的测试代码就像

template<template <typename MatrixType> typename EigenSolver>
Vertor3d solve(){
   //Solve Ax = b and A is a real symmetric matrix and positive semidefinite

   ... // Construct 4X4 square matrix A and 4X1 vector b

   EigenSolver<Matrix4d> solver(A);
   auto x = solver.solve(b);

   ... // Compute relative error for validating
}

我测试了一些EigenSolver,包括:

  1. FullPixLU
  2. PartialPivLU
  3. HouseholderQR
  4. ColPivHouseholderQR
  5. ColPivHouseholderQR
  6. CompleteOrthogonalDecomposition
  7. 活体肝移植
  8. 直接反向
  9. 直接反转是:

    template<typename MatrixType>
    struct InverseSolve
    {
    private:
        MatrixType  inv;
    public:
        InverseSolve(const MatrixType &matrix) :inv(matrix.inverse()) {
        }
        template<typename VectorType>
        auto solve(const VectorType & b) {
            return inv * b;
        }
    };
    

    我发现快速方法是 DirectInverse ,即使我将Eigen与MKL联系起来,结果也没有改变。

    这是测试结果

    1. FullPixLU:477 ms
    2. PartialPivLU:468 ms
    3. HouseholderQR:849 ms
    4. ColPivHouseholderQR:766 ms
    5. ColPivHouseholderQR:857 ms
    6. CompleteOrthogonalDecomposition:832 ms
    7. LDLT:477 ms
    8. 直接反转:88毫秒
    9. 全部使用1000000矩阵,随机双倍均匀分布[0,100]。我先构造上三角,然后复制到下三角。

      DirectInverse 的唯一问题是它的相对误差比其他解算器略大但可接受。

      我的程序有更快或更优雅的解决方案吗?DirectInverse是我程序的快速解决方案吗?

      DirectInverse不使用对称信息,为什么DirectInverse比LDLT快得多?

1 个答案:

答案 0 :(得分:4)

尽管许多人建议在你只想解决一个线性系统时从不明确地计算逆,但对于非常小的矩阵,这实际上是有益的,因为存在使用辅因子的封闭形式的解决方案。

您测试的所有其他替代方案都会变慢,因为它们会进行旋转(这意味着分支),即使对于小型固定大小的矩阵也是如此。此外,它们中的大多数将导致更多的划分,并且不像直接计算那样可以矢量化。

为了提高准确度(如果需要,这种技术实际上可以独立于求解器使用),您可以通过使用残差再次求解系统来优化初始解决方案:

Eigen::Vector4d solveDirect(const Eigen::Matrix4d& A, const Eigen::Vector4d& b)
{
    Eigen::Matrix4d inv = A.inverse();
    Eigen::Vector4d x = inv * b;
    x += inv*(b-A*x);
    return x;
}

我不认为Eigen直接提供了一种方法来利用A的对称性(对于直接计算的逆)。您可以尝试通过将A的selfadjoint视图显式复制到临时文件中来暗示,并希望编译器足够聪明以找到公共子表达式:

Eigen::Matrix4d tmp = A.selfadjointView<Eigen::Upper>();
Eigen::Matrix4d inv = tmp.inverse();

为了减少某些分歧,您还可以使用-freciprocal-math(在gcc或clang上)进行编译,这会稍微降低准确性。

如果这对性能至关重要,请尝试实施手动调整的inverse_4x4_symmetric方法。 利用inv * b的对称性对于这样小的矩阵来说不太可能有益。