我在 Eigen 库的 API 上遇到了一些困难,即用于稀疏矩阵 Cholesky 分解的 SimplcialLLT 类。
我需要分解三个矩阵,然后用它们来求解许多方程组(仅更改右侧) - 因此我只想将这些矩阵分解一次,然后重新使用它们。此外,它们都具有相同的稀疏模式,因此我只想进行一次符号分解,然后将其用于所有三个矩阵的数值分解。根据文档,这正是SimplicialLLT::analyzePattern
and SimplicialLLT::factor
方法是为了。然而,我似乎无法找到一种方法来将所有三个因素都保留在记忆中。这是我的代码:
我的类中有这些成员变量,我想填充以下因素:
Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> choleskyA;
Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> choleskyB;
Eigen::SimplicialLLT<Eigen::SparseMatrix<double>> choleskyC;
然后我创建三个稀疏矩阵 A、B 和 C 并希望对它们进行因式分解:
choleskyA.analyzePattern(A);
choleskyA.factorize(A);
choleskyB.analyzePattern(B); // this has already been done!
choleskyB.factorize(B);
choleskyC.analyzePattern(C); // this has already been done!
choleskyC.factorize(C);
之后我可以一遍又一遍地使用它们来解决问题,只需更改右侧的 b 向量:
xA = choleskyA.solve(bA);
xB = choleskyB.solve(bB);
xC = choleskyC.solve(bC);
这是可行的(我认为),但是对analyzePattern的第二次和第三次调用是多余的。我想做的是这样的:
choleskyA.analyzePattern(A);
choleskyA.factorize(A);
choleskyB = choleskyA.factorize(B);
choleskyC = choleskyA.factorize(C);
但这不是当前 API 的一个选项(我们使用 Eigen 3.2.3,但如果我没看错的话,3.3.2 在这方面没有任何变化)。这里的问题是,对 SimplcialLLT 的同一实例进行因式分解的后续调用将覆盖之前计算的因式,同时,我无法找到一种方法来复制它以保留。我查看了源代码,但我必须承认这没有多大帮助,因为我看不到任何复制底层数据结构的简单方法。在我看来,这是一个相当常见的用法,所以我觉得我遗漏了一些明显的东西,请帮助。
我尝试过的:
我尝试简单地使用choleskyB = choleskyA
希望默认的复制构造函数能够完成任务,但我发现基类被设计为不可复制的。
我可以从以下位置获取 L 和 U 矩阵(它们有一个 getter)choleskyA
,复制它们并仅存储它们,然后基本上复制粘贴 SimplicialCholeskyBase::_solve_impl() 的内容(复制粘贴在下面),以直接使用先前存储的 L 和 U 编写求解自己的方法。
template<typename Rhs,typename Dest>
void _solve_impl(const MatrixBase<Rhs> &b, MatrixBase<Dest> &dest) const
{
eigen_assert(m_factorizationIsOk && "The decomposition is not in a valid state for solving, you must first call either compute() or symbolic()/numeric()");
eigen_assert(m_matrix.rows()==b.rows());
if(m_info!=Success)
return;
if(m_P.size()>0)
dest = m_P * b;
else
dest = b;
if(m_matrix.nonZeros()>0) // otherwise L==I
derived().matrixL().solveInPlace(dest);
if(m_diag.size()>0)
dest = m_diag.asDiagonal().inverse() * dest;
if (m_matrix.nonZeros()>0) // otherwise U==I
derived().matrixU().solveInPlace(dest);
if(m_P.size()>0)
dest = m_Pinv * dest;
}
...但这是一个相当丑陋的解决方案,而且我可能会把它搞砸,因为我对这个过程没有很好的理解(我不需要上面代码中的 m_diag,因为我正在做 LLT,对吧?那会仅当我使用 LDLT 时才相关?)。我希望这不是我需要做的......
最后一点 - 将必要的 getter/setter 添加到 Eigen 类并编译“我自己的”Eigen 不是一种选择(好吧,不是一个好的选择),因为此代码将(希望)作为开源进一步重新分发,因此它会麻烦点。