Eigen-Contrib  5.0.1
 
Loading...
Searching...
No Matches
DiagonalPlusLowRank.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// This Source Code Form is subject to the terms of the Mozilla
5// Public License v. 2.0. If a copy of the MPL was not distributed
6// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
7// SPDX-FileCopyrightText: The Eigen Authors
8// SPDX-License-Identifier: MPL-2.0
9
10// References:
11// [1] M. A. Woodbury, "Inverting Modified Matrices", Memorandum Report 42,
12// Statistical Research Group, Princeton University, 1950. The Woodbury
13// identity behind solve() and inverse().
14// [2] W. W. Hager, "Updating the Inverse of a Matrix", SIAM Review, 31(2),
15// pp. 221-239, 1989. Review of the Woodbury identity and of the matrix
16// determinant lemma used by determinant().
17// [3] J. J. Dongarra, J. R. Bunch, C. B. Moler and G. W. Stewart, "LINPACK
18// Users' Guide", SIAM, 1979. determinant()'s balanced accumulation follows
19// the convention of its xGEDI routines, which return determinants as a
20// (fraction, exponent) pair to avoid spurious overflow/underflow.
21// [4] P. H. Sterbenz, "Floating-Point Computation", Prentice-Hall, 1974.
22// Scaling by a power of two is exact, the property the balanced
23// accumulation relies on.
24// [5] E. L. Yip, "A Note on the Stability of Solving a Rank-p Modification of
25// a Linear System by the Sherman-Morrison-Woodbury Formula", SIAM Journal
26// on Scientific and Statistical Computing, 7(3), pp. 507-513, 1986. The
27// accuracy limitation of the Woodbury solve noted on solve().
28
29#ifndef EIGEN_STRUCTURED_DIAGONAL_PLUS_LOW_RANK_H
30#define EIGEN_STRUCTURED_DIAGONAL_PLUS_LOW_RANK_H
31
32// IWYU pragma: private
33#include "./InternalHeaderCheck.h"
34
35namespace Eigen {
36
37template <typename Scalar_, int Size_ = Dynamic, int Rank_ = Dynamic>
39
40namespace internal {
41
42template <typename Scalar_, int Size_, int Rank_>
43struct traits<DiagonalPlusLowRank<Scalar_, Size_, Rank_>> {
44 using Scalar = Scalar_;
45 using StorageKind = Dense;
46 using XprKind = MatrixXpr;
47 using StorageIndex = int;
48 static constexpr int RowsAtCompileTime = Size_;
49 static constexpr int ColsAtCompileTime = Size_;
50 static constexpr int MaxRowsAtCompileTime = Size_;
51 static constexpr int MaxColsAtCompileTime = Size_;
52 // Deliberately no NestByRefBit: transpose(), conjugate(), adjoint(), inverse()
53 // and makeDiagonalPlusLowRank() return owning temporaries, so Product must nest
54 // the operator by value for a delayed-evaluated product expression to keep its
55 // left factor alive. The copy is O(nk), on par with a single product evaluation.
56 static constexpr unsigned int Flags = 0;
57};
58
59template <typename Scalar_, int Size_, int Rank_>
60struct evaluator_traits<DiagonalPlusLowRank<Scalar_, Size_, Rank_>> {
61 using Kind = IndexBased;
62 using Shape = StructuredShape;
63};
64
65// Compile-time dispatch keeps fixed rank zero from instantiating a 0 x 0 LU;
66// C++14 cannot express this branch with if constexpr.
67template <int Rank_>
68struct dplr_capacitance_impl {
71 template <typename Op, typename Dinv, typename Workspace>
72 static void subtractSolveCorrection(const Op& op, const Dinv& dinv, Workspace& x) {
73 if (op.correctionRank() == 0 || x.size() == 0) return;
74 PartialPivLU<typename Op::CapacitanceType> cap(op.capacitance());
75 x.noalias() -= dinv.asDiagonal() * (op.factorU() * cap.solve(op.factorV().adjoint() * x));
76 }
78 template <typename Op, typename Dinv, typename Factor>
79 static void inverseFactor(const Op& op, const Dinv& dinv, Factor& Up) {
80 if (op.correctionRank() == 0) return;
81 PartialPivLU<typename Op::CapacitanceType> cap(op.capacitance());
82 const typename Op::CapacitanceType K = cap.inverse();
83 Up.noalias() = -(dinv.asDiagonal() * op.factorU() * K);
84 }
89 template <typename Op>
90 static typename Op::Scalar balancedCapacitanceDeterminant(const Op& op, Index& exponent) {
91 using Scalar = typename Op::Scalar;
92 if (op.correctionRank() == 0) return Scalar(1);
93 const PartialPivLU<typename Op::CapacitanceType> lu(op.capacitance());
94 Scalar det(static_cast<typename Op::RealScalar>(lu.permutationP().determinant()));
95 for (Index i = 0; i < lu.matrixLU().rows(); ++i)
96 det = structured_balance(det * structured_balance(lu.matrixLU().coeff(i, i), exponent), exponent);
97 return det;
98 }
99};
100
101template <>
102struct dplr_capacitance_impl<0> {
103 template <typename Op, typename Dinv, typename Workspace>
104 static void subtractSolveCorrection(const Op&, const Dinv&, Workspace&) {}
105 template <typename Op, typename Dinv, typename Factor>
106 static void inverseFactor(const Op&, const Dinv&, Factor&) {}
107 template <typename Op>
108 static typename Op::Scalar balancedCapacitanceDeterminant(const Op&, Index&) {
109 return typename Op::Scalar(1);
110 }
111};
112
113} // namespace internal
114
148template <typename Scalar_, int Size_, int Rank_>
149class DiagonalPlusLowRank : public EigenBase<DiagonalPlusLowRank<Scalar_, Size_, Rank_>> {
150 public:
151 using Scalar = Scalar_;
152 using RealScalar = typename NumTraits<Scalar>::Real;
153 using StorageIndex = int;
154 using DiagonalVector = Matrix<Scalar, Size_, 1>;
155 using FactorType = Matrix<Scalar, Size_, Rank_>;
156 using CapacitanceType = Matrix<Scalar, Rank_, Rank_>;
157
158 static constexpr int RowsAtCompileTime = Size_;
159 static constexpr int ColsAtCompileTime = Size_;
160 static constexpr int MaxRowsAtCompileTime = Size_;
161 static constexpr int MaxColsAtCompileTime = Size_;
162 static constexpr int SizeAtCompileTime = internal::size_at_compile_time(Size_, Size_);
163 static constexpr int MaxSizeAtCompileTime = SizeAtCompileTime;
164 static constexpr bool IsRowMajor = false;
165 // Deliberately no IsVectorAtCompileTime: Ref<const DiagonalPlusLowRank>'s default
166 // StrideType argument reads it, so its absence makes internal::is_ref_compatible
167 // SFINAE to false and keeps the iterative solvers on their matrix-free path.
168
172 template <typename DDerived, typename UDerived, typename VDerived>
174 : m_d(d), m_U(U), m_V(V) {
175 EIGEN_STATIC_ASSERT_VECTOR_ONLY(DDerived)
176 eigen_assert(m_d.size() > 0 && "DiagonalPlusLowRank must be non-empty");
177 eigen_assert(m_U.rows() == m_d.size() && m_V.rows() == m_d.size() && m_U.cols() == m_V.cols() &&
178 "factor dimensions do not match");
179 }
180
181 EIGEN_DEVICE_FUNC Index rows() const { return m_d.size(); }
182 EIGEN_DEVICE_FUNC Index cols() const { return m_d.size(); }
184 Index correctionRank() const { return m_U.cols(); }
185
187 const DiagonalVector& diagonal() const { return m_d; }
189 const FactorType& factorU() const { return m_U; }
191 const FactorType& factorV() const { return m_V; }
192
194 Scalar coeff(Index row, Index col) const {
195 Scalar r = m_V.row(col).dot(m_U.row(row)); // dot() conjugates its first argument
196 if (row == col) r += m_d.coeff(row);
197 return r;
198 }
199
202 DiagonalPlusLowRank transpose() const { return DiagonalPlusLowRank(m_d, m_V.conjugate(), m_U.conjugate()); }
203
207 return DiagonalPlusLowRank(m_d.conjugate(), m_U.conjugate(), m_V.conjugate());
208 }
209
212 DiagonalPlusLowRank adjoint() const { return DiagonalPlusLowRank(m_d.conjugate(), m_V, m_U); }
213
220 CapacitanceType capacitance() const {
221 const Index k = correctionRank();
222 CapacitanceType c = CapacitanceType::Identity(k, k);
223 if (k == 0) return c;
224 const DiagonalVector dinv = m_d.cwiseInverse();
225 c.noalias() += m_V.adjoint() * dinv.asDiagonal() * m_U;
226 return c;
227 }
228
239 template <typename Rhs>
241 EIGEN_STATIC_ASSERT(RowsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
242 int(RowsAtCompileTime) == int(Rhs::RowsAtCompileTime),
243 YOU_MIXED_MATRICES_OF_DIFFERENT_SIZES)
244 eigen_assert(b.rows() == rows() && "right-hand side has the wrong number of rows");
245 const DiagonalVector dinv = m_d.cwiseInverse();
246 Matrix<Scalar, Size_, Rhs::ColsAtCompileTime> x = dinv.asDiagonal() * b;
247 internal::dplr_capacitance_impl<Rank_>::subtractSolveCorrection(*this, dinv, x);
248 return x;
249 }
250
256 const DiagonalVector dinv = m_d.cwiseInverse();
257 FactorType Up(rows(), correctionRank());
258 internal::dplr_capacitance_impl<Rank_>::inverseFactor(*this, dinv, Up);
259 FactorType Vp = dinv.conjugate().asDiagonal() * m_V;
260 return DiagonalPlusLowRank(dinv, Up, Vp);
261 }
262
282 Scalar determinant() const {
283 Scalar det(1);
284 Index exponent = 0;
285 for (Index i = 0; i < rows(); ++i)
286 det = internal::structured_balance(det * internal::structured_balance(m_d.coeff(i), exponent), exponent);
287 const Scalar capDet = internal::dplr_capacitance_impl<Rank_>::balancedCapacitanceDeterminant(*this, exponent);
288 det = internal::structured_balance(det * capDet, exponent);
289 return internal::structured_ldexp_clamped(det, exponent);
290 }
291
294 template <typename Dest>
295 void evalTo(Dest& dst) const {
296 dst.noalias() = m_U * m_V.adjoint();
297 dst.diagonal() += m_d;
298 }
299
301 template <typename Dest>
302 void addTo(Dest& dst) const {
303 dst.noalias() += m_U * m_V.adjoint();
304 dst.diagonal() += m_d;
305 }
306
308 template <typename Dest>
309 void subTo(Dest& dst) const {
310 dst.noalias() -= m_U * m_V.adjoint();
311 dst.diagonal() -= m_d;
312 }
313
319 template <typename Rhs>
321 EIGEN_STATIC_ASSERT(ColsAtCompileTime == Dynamic || Rhs::RowsAtCompileTime == Dynamic ||
322 int(ColsAtCompileTime) == int(Rhs::RowsAtCompileTime),
323 INVALID_MATRIX_PRODUCT)
324 eigen_assert(v.rows() == cols() && "invalid product: dimensions do not match");
325 return Product<DiagonalPlusLowRank, Rhs>(*this, v.derived());
326 }
327
332 template <typename Dest, typename Rhs, typename ProductScalar>
333 void addProduct(Dest& dst, const Rhs& rhs, const ProductScalar& alpha) const {
334 eigen_assert(rhs.rows() == rows() && "invalid product: dimensions do not match");
336 dst += alpha * (m_d.asDiagonal() * r);
337 if (correctionRank() > 0) {
339 dst.noalias() += alpha * (m_U * t);
340 }
341 }
342
343 private:
344 DiagonalVector m_d;
345 FactorType m_U;
346 FactorType m_V;
347};
348
352template <typename DDerived, typename UDerived, typename VDerived>
358
359namespace internal {
360
361// Single product specialization covering every product tag; see the note in
362// Circulant.h.
363template <typename Scalar_, int Size_, int Rank_, typename Rhs, int ProductTag>
364struct generic_product_impl<DiagonalPlusLowRank<Scalar_, Size_, Rank_>, Rhs, StructuredShape, DenseShape, ProductTag>
365 : structured_product_impl<DiagonalPlusLowRank<Scalar_, Size_, Rank_>, Rhs> {};
366
367} // namespace internal
368
369} // namespace Eigen
370
371#endif // EIGEN_STRUCTURED_DIAGONAL_PLUS_LOW_RANK_H
An n x n operator : a diagonal matrix plus a rank-k correction, stored as its diagonal and the two n ...
Definition DiagonalPlusLowRank.h:149
const FactorType & factorV() const
Definition DiagonalPlusLowRank.h:191
Scalar coeff(Index row, Index col) const
Definition DiagonalPlusLowRank.h:194
Matrix< Scalar, Size_, Rhs::ColsAtCompileTime > solve(const MatrixBase< Rhs > &b) const
Definition DiagonalPlusLowRank.h:240
DiagonalPlusLowRank conjugate() const
Definition DiagonalPlusLowRank.h:206
DiagonalPlusLowRank adjoint() const
Definition DiagonalPlusLowRank.h:212
const FactorType & factorU() const
Definition DiagonalPlusLowRank.h:189
Product< DiagonalPlusLowRank, Rhs > operator*(const MatrixBase< Rhs > &v) const
Definition DiagonalPlusLowRank.h:320
DiagonalPlusLowRank inverse() const
Definition DiagonalPlusLowRank.h:255
const DiagonalVector & diagonal() const
Definition DiagonalPlusLowRank.h:187
DiagonalPlusLowRank(const MatrixBase< DDerived > &d, const MatrixBase< UDerived > &U, const MatrixBase< VDerived > &V)
Definition DiagonalPlusLowRank.h:173
CapacitanceType capacitance() const
Definition DiagonalPlusLowRank.h:220
DiagonalPlusLowRank transpose() const
Definition DiagonalPlusLowRank.h:202
Index correctionRank() const
Definition DiagonalPlusLowRank.h:184
Scalar determinant() const
Definition DiagonalPlusLowRank.h:282
DiagonalPlusLowRank< typename DDerived::Scalar, DDerived::SizeAtCompileTime, UDerived::ColsAtCompileTime > makeDiagonalPlusLowRank(const MatrixBase< DDerived > &d, const MatrixBase< UDerived > &U, const MatrixBase< VDerived > &V)
Definition DiagonalPlusLowRank.h:354
Namespace containing all symbols from the Eigen library.