Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
SparseInverse.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2022 Julian Kent <jkflying@gmail.com>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9// SPDX-License-Identifier: MPL-2.0
10
11#ifndef EIGEN_SPARSEINVERSE_H
12#define EIGEN_SPARSEINVERSE_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17#include "../../../../Eigen/Sparse"
18#include "../../../../Eigen/SparseLU"
19
20namespace Eigen {
21
35template <typename Scalar>
36class KahanSum {
37 // Straightforward Kahan summation for accurate accumulation of a sum of numbers
38 Scalar _sum{};
39 Scalar _correction{};
40
41 public:
42 Scalar value() const { return _sum; }
43
44 void operator+=(Scalar increment) {
45 const Scalar correctedIncrement = increment + _correction;
46 const Scalar previousSum = _sum;
47 _sum += correctedIncrement;
48 _correction = correctedIncrement - (_sum - previousSum);
49 }
50};
51template <typename Scalar, Index Width = 16>
52class FABSum {
53 // https://epubs.siam.org/doi/pdf/10.1137/19M1257780
54 // Fast and Accurate Blocked Summation
55 // Uses naive summation for the fast sum, and Kahan summation for the accurate sum
56 // Theoretically SIMD sum could be changed to a tree sum which would improve accuracy
57 // over naive summation
58 KahanSum<Scalar> _totalSum;
60 Index _blockUsed{};
61
62 public:
63 Scalar value() const { return _block.topRows(_blockUsed).sum() + _totalSum.value(); }
64
65 void operator+=(Scalar increment) {
66 _block(_blockUsed++, 0) = increment;
67 if (_blockUsed == Width) {
68 _totalSum += _block.sum();
69 _blockUsed = 0;
70 }
71 }
72};
73
81template <typename Derived, typename OtherDerived>
82typename Derived::Scalar accurateDot(const SparseMatrixBase<Derived>& A, const SparseMatrixBase<OtherDerived>& other) {
83 typedef typename Derived::Scalar Scalar;
84 EIGEN_STATIC_ASSERT_VECTOR_ONLY(Derived)
85 EIGEN_STATIC_ASSERT_VECTOR_ONLY(OtherDerived)
86 EIGEN_STATIC_ASSERT_SAME_VECTOR_SIZE(Derived, OtherDerived)
87 static_assert(std::is_same<Scalar, typename OtherDerived::Scalar>::value, "mismatched types");
88
89 internal::evaluator<Derived> thisEval(A.derived());
90 typename Derived::ReverseInnerIterator i(thisEval, 0);
91
92 internal::evaluator<OtherDerived> otherEval(other.derived());
93 typename OtherDerived::ReverseInnerIterator j(otherEval, 0);
94
95 FABSum<Scalar> res;
96 while (i && j) {
97 if (i.index() == j.index()) {
98 res += numext::conj(i.value()) * j.value();
99 --i;
100 --j;
101 } else if (i.index() > j.index())
102 --i;
103 else
104 --j;
105 }
106 return res.value();
107}
108
128template <typename Scalar>
129class SparseInverse {
130 public:
131 typedef SparseMatrix<Scalar, ColMajor> MatrixType;
132 typedef SparseMatrix<Scalar, RowMajor> RowMatrixType;
133
134 SparseInverse() {}
135
143 SparseInverse(const SparseLU<MatrixType>& slu) { _result = computeInverse(slu); }
144
148 SparseInverse& compute(const SparseMatrix<Scalar>& A) {
150 slu.compute(A);
151 _result = computeInverse(slu);
152 return *this;
153 }
154
158 const MatrixType& inverse() const { return _result; }
159
164 static MatrixType computeInverse(const SparseLU<MatrixType>& slu) {
165 if (slu.info() != Success) {
166 return MatrixType(0, 0);
167 }
168
169 // Extract from SparseLU and decompose into L, inverse D and U terms
171 RowMatrixType Upper;
172 {
173 RowMatrixType DU = slu.matrixU().toSparse();
174 invD = DU.diagonal().cwiseInverse();
175 Upper = invD.asDiagonal() * DU.template triangularView<StrictlyUpper>();
176 }
177 MatrixType Lower = slu.matrixL().toSparse().template triangularView<StrictlyLower>();
178
179 // Compute the inverse and reapply the permutation matrix from the LU decomposition
180 return slu.colsPermutation().transpose() * computeInverse(Upper, invD, Lower) * slu.rowsPermutation();
181 }
182
186 static MatrixType computeInverse(const RowMatrixType& Upper, const Matrix<Scalar, Dynamic, 1>& inverseDiagonal,
187 const MatrixType& Lower) {
188 // Calculate the 'minimal set', which is the nonzeros of (L+U).transpose()
189 // It could be zeroed, but we will overwrite all non-zeros anyways.
190 MatrixType colInv = Lower.transpose().template triangularView<UnitUpper>();
191 colInv += Upper.transpose();
192
193 // We also need rowmajor representation in order to do efficient row-wise dot products
194 RowMatrixType rowInv = Upper.transpose().template triangularView<UnitLower>();
195 rowInv += Lower.transpose();
196
197 // Use the Takahashi algorithm to build the supporting elements of the inverse
198 // upwards and to the left, from the bottom right element, 1 col/row at a time
199 for (Index recurseLevel = Upper.cols() - 1; recurseLevel >= 0; recurseLevel--) {
200 const auto& col = Lower.col(recurseLevel);
201 const auto& row = Upper.row(recurseLevel);
202
203 // Calculate the inverse values for the nonzeros in this column
204 typename MatrixType::ReverseInnerIterator colIter(colInv, recurseLevel);
205 for (; recurseLevel < colIter.index(); --colIter) {
206 const Scalar element = -accurateDot(col, rowInv.row(colIter.index()));
207 colIter.valueRef() = element;
208 rowInv.coeffRef(colIter.index(), recurseLevel) = element;
209 }
210
211 // Calculate the inverse values for the nonzeros in this row
212 typename RowMatrixType::ReverseInnerIterator rowIter(rowInv, recurseLevel);
213 for (; recurseLevel < rowIter.index(); --rowIter) {
214 const Scalar element = -accurateDot(row, colInv.col(rowIter.index()));
215 rowIter.valueRef() = element;
216 colInv.coeffRef(recurseLevel, rowIter.index()) = element;
217 }
218
219 // And finally the diagonal, which corresponds to both row and col iterator now
220 const Scalar diag = inverseDiagonal(recurseLevel) - accurateDot(row, colInv.col(recurseLevel));
221 rowIter.valueRef() = diag;
222 colIter.valueRef() = diag;
223 }
224
225 return colInv;
226 }
227
228 private:
229 MatrixType _result;
230};
231
232} // namespace Eigen
233#endif
Kahan algorithm based accumulator.
Definition SparseInverse.h:36
static MatrixType computeInverse(const RowMatrixType &Upper, const Matrix< Scalar, Dynamic, 1 > &inverseDiagonal, const MatrixType &Lower)
Internal function to calculate the inverse from strictly upper, diagonal and strictly lower component...
Definition SparseInverse.h:186
SparseInverse(const SparseLU< MatrixType > &slu)
This Constructor is for if you already have a factored SparseLU and would like to use it to calculate...
Definition SparseInverse.h:143
static MatrixType computeInverse(const SparseLU< MatrixType > &slu)
Internal function to calculate the sparse inverse in a functional way.
Definition SparseInverse.h:164
const MatrixType & inverse() const
return the already-calculated sparse inverse, or a 0x0 matrix if it could not be computed
Definition SparseInverse.h:158
SparseInverse & compute(const SparseMatrix< Scalar > &A)
Calculate the sparse inverse from a given sparse input.
Definition SparseInverse.h:148
SparseLUMatrixUReturnType< SCMatrix, Map< SparseMatrix< Scalar, ColMajor, StorageIndex > > > matrixU() const
SparseLUMatrixLReturnType< SCMatrix > matrixL() const
void compute(const MatrixType &matrix)
ComputationInfo info() const
const PermutationType & colsPermutation() const
const PermutationType & rowsPermutation() const
constexpr RowXpr row(Index i)
constexpr ColXpr col(Index i)
Scalar & coeffRef(Index row, Index col)
DiagonalReturnType diagonal()
Namespace containing all symbols from the Eigen library.
Derived::Scalar accurateDot(const SparseMatrixBase< Derived > &A, const SparseMatrixBase< OtherDerived > &other)
computes an accurate dot product on two sparse vectors
Definition SparseInverse.h:82
constexpr Derived & derived()