Eigen  5.0.1
 
Loading...
Searching...
No Matches
LLT.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
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_LLT_H
12#define EIGEN_LLT_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17// Smallest n at which LLT::inverse() runs the POTRI sequence on a real scalar rather than solving
18// against an explicit identity. POTRI's n^3/3 runs in the unblocked TRTRI and LAUUM kernels at small
19// n, the solve's 3x at the blocked TRSM rate, so a wider ISA helps the competitor more and moves the
20// crossover right: on Zen 4 it is n = 32 under SSE2 and AVX2, 128 (double) to 256 (float) under
21// AVX-512. Complex is not thresholded: POTRI leads from n = 8 in all twelve configurations measured.
22#ifndef EIGEN_LLT_INVERSE_POTRI_THRESHOLD
23#if defined(EIGEN_VECTORIZE_AVX512)
24#define EIGEN_LLT_INVERSE_POTRI_THRESHOLD 256
25#else
26#define EIGEN_LLT_INVERSE_POTRI_THRESHOLD 32
27#endif
28#endif
29
30namespace Eigen {
31
32namespace internal {
33
34template <typename MatrixType_, int UpLo_>
35struct traits<LLT<MatrixType_, UpLo_> > : traits<MatrixType_> {
36 using XprKind = MatrixXpr;
37 using StorageKind = SolverStorage;
38 using StorageIndex = int;
39 enum { Flags = 0 };
40};
41
42template <typename MatrixType, int UpLo>
43struct LLT_Traits;
44} // namespace internal
45
84template <typename MatrixType_, int UpLo_>
85class LLT : public SolverBase<LLT<MatrixType_, UpLo_> > {
86 public:
87 using MatrixType = MatrixType_;
88 using Base = SolverBase<LLT>;
89 friend class SolverBase<LLT>;
90
91 EIGEN_GENERIC_PUBLIC_INTERFACE(LLT)
92 enum { MaxColsAtCompileTime = MatrixType::MaxColsAtCompileTime };
93
94 enum { PacketSize = internal::packet_traits<Scalar>::size, AlignmentMask = int(PacketSize) - 1, UpLo = UpLo_ };
95
96 using Traits = internal::LLT_Traits<MatrixType, UpLo>;
97 using PlainObject = typename MatrixType::PlainObject;
98
105 LLT() : m_matrix(), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {}
106
113 explicit LLT(Index size) : m_matrix(size, size), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {}
114
115 template <typename InputType>
116 explicit LLT(const EigenBase<InputType>& matrix)
117 : m_matrix(matrix.rows(), matrix.cols()), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {
118 compute(matrix.derived());
119 }
120
128 template <typename InputType>
129 explicit LLT(EigenBase<InputType>& matrix)
130 : m_matrix(matrix.derived()), m_l1_norm(0), m_isInitialized(false), m_info(InvalidInput) {
131 compute(matrix.derived());
132 }
133
135 inline typename Traits::MatrixU matrixU() const {
136 eigen_assert(m_isInitialized && "LLT is not initialized.");
137 return Traits::getU(m_matrix);
138 }
139
141 inline typename Traits::MatrixL matrixL() const {
142 eigen_assert(m_isInitialized && "LLT is not initialized.");
143 return Traits::getL(m_matrix);
144 }
145
146#ifdef EIGEN_PARSED_BY_DOXYGEN
157 template <typename Rhs>
158 inline Solve<LLT, Rhs> solve(const MatrixBase<Rhs>& b) const;
159#endif
160
161 template <typename Derived>
162 void solveInPlace(const MatrixBase<Derived>& bAndX) const;
163
164 template <typename InputType>
165 LLT& compute(const EigenBase<InputType>& matrix);
166
170 RealScalar rcond() const {
171 eigen_assert(m_isInitialized && "LLT is not initialized.");
172 eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
173 return internal::rcond_estimate_helper(m_l1_norm, *this);
174 }
175
180 inline const MatrixType& matrixLLT() const {
181 eigen_assert(m_isInitialized && "LLT is not initialized.");
182 return m_matrix;
183 }
184
206 inline Inverse<LLT> inverse() const {
207 eigen_assert(m_isInitialized && "LLT is not initialized.");
208 return Inverse<LLT>(*this);
209 }
210
211 MatrixType reconstructedMatrix() const;
212
219 eigen_assert(m_isInitialized && "LLT is not initialized.");
220 return m_info;
221 }
222
223 /** \returns the adjoint of \c *this, that is, a const reference to the decomposition itself as the underlying matrix
224 * is self-adjoint.
225 *
226 * This method is provided for compatibility with other matrix decompositions, thus enabling generic code such as:
227 * \code x = decomposition.adjoint().solve(b) \endcode
228 */
229 const LLT& adjoint() const noexcept { return *this; }
230
231 constexpr Index rows() const noexcept { return m_matrix.rows(); }
232 constexpr Index cols() const noexcept { return m_matrix.cols(); }
233
234 template <typename VectorType>
235 LLT& rankUpdate(const VectorType& vec, const RealScalar& sigma = 1);
236
250 Scalar determinant() const;
251
267 RealScalar absDeterminant() const;
268
282 RealScalar logAbsDeterminant() const;
283
293 Scalar signDeterminant() const;
294
295#ifndef EIGEN_PARSED_BY_DOXYGEN
296 template <typename RhsType, typename DstType>
297 void _solve_impl(const RhsType& rhs, DstType& dst) const;
298
299 template <bool Conjugate, typename RhsType, typename DstType>
300 void _solve_impl_transposed(const RhsType& rhs, DstType& dst) const;
301#endif
302
303 protected:
304 EIGEN_STATIC_ASSERT_NON_INTEGER(Scalar)
305
306
310 MatrixType m_matrix;
311 RealScalar m_l1_norm;
312 bool m_isInitialized;
313 ComputationInfo m_info;
314};
315
316namespace internal {
317
318template <typename Scalar, int UpLo>
319struct llt_inplace;
320
321template <typename MatrixType, typename VectorType>
322static Index llt_rank_update_lower(MatrixType& mat, const VectorType& vec,
323 const typename MatrixType::RealScalar& sigma) {
324 using std::sqrt;
325 using Scalar = typename MatrixType::Scalar;
326 using RealScalar = typename MatrixType::RealScalar;
327 using ColXpr = typename MatrixType::ColXpr;
328 using ColXprCleaned = internal::remove_all_t<ColXpr>;
329 using ColXprSegment = typename ColXprCleaned::SegmentReturnType;
331 using TempVecSegment = typename TempVectorType::SegmentReturnType;
332
333 Index n = mat.cols();
334 eigen_assert(mat.rows() == n && vec.size() == n);
335
336 TempVectorType temp;
337
338 if (sigma > 0) {
339 // This version is based on Givens rotations.
340 // It is faster than the other one below, but only works for updates,
341 // i.e., for sigma > 0
342 temp = sqrt(sigma) * vec;
343
344 for (Index i = 0; i < n; ++i) {
346 g.makeGivens(mat(i, i), -temp(i), &mat(i, i));
347
348 Index rs = n - i - 1;
349 if (rs > 0) {
350 ColXprSegment x(mat.col(i).tail(rs));
351 TempVecSegment y(temp.tail(rs));
352 apply_rotation_in_the_plane(x, y, g);
353 }
354 }
355 } else {
356 temp = vec;
357 RealScalar beta = 1;
358 for (Index j = 0; j < n; ++j) {
359 RealScalar Ljj = numext::real(mat.coeff(j, j));
360 RealScalar dj = numext::abs2(Ljj);
361 Scalar wj = temp.coeff(j);
362 RealScalar swj2 = sigma * numext::abs2(wj);
363 RealScalar gamma = dj * beta + swj2;
364
365 RealScalar x = dj + swj2 / beta;
366 if (x <= RealScalar(0)) return j;
367 RealScalar nLjj = sqrt(x);
368 mat.coeffRef(j, j) = nLjj;
369 beta += swj2 / dj;
370
371 // Update the terms of L
372 Index rs = n - j - 1;
373 if (rs) {
374 temp.tail(rs) -= (wj / Ljj) * mat.col(j).tail(rs);
375 if (!numext::is_exactly_zero(gamma))
376 mat.col(j).tail(rs) =
377 (nLjj / Ljj) * mat.col(j).tail(rs) + (nLjj * sigma * numext::conj(wj) / gamma) * temp.tail(rs);
378 }
379 }
380 }
381 return -1;
382}
383
384template <typename Scalar>
385struct llt_inplace<Scalar, Lower> {
386 using RealScalar = typename NumTraits<Scalar>::Real;
387 template <typename MatrixType>
388 static Index unblocked(MatrixType& mat) {
389 using std::sqrt;
390
391 eigen_assert(mat.rows() == mat.cols());
392 const Index size = mat.rows();
393 for (Index k = 0; k < size; ++k) {
394 Index rs = size - k - 1; // remaining size
395
396 Block<MatrixType, Dynamic, 1> A21(mat, k + 1, k, rs, 1);
397 Block<MatrixType, 1, Dynamic> A10(mat, k, 0, 1, k);
398 Block<MatrixType, Dynamic, Dynamic> A20(mat, k + 1, 0, rs, k);
399
400 RealScalar x = numext::real(mat.coeff(k, k));
401 if (k > 0) x -= A10.squaredNorm();
402 if (x <= RealScalar(0)) return k;
403 mat.coeffRef(k, k) = x = sqrt(x);
404 if (k > 0 && rs > 0) A21.noalias() -= A20 * A10.adjoint();
405 if (rs > 0) A21 /= x;
406 }
407 return -1;
408 }
409
410 template <typename MatrixType>
411 static Index blocked(MatrixType& m) {
412 eigen_assert(m.rows() == m.cols());
413 Index size = m.rows();
414 // The unblocked kernel walks row k of the factor, which strides through memory: it wins up to about 48 columns,
415 // and beyond that 16-column blocks keep those walks short while amortizing the level-3 updates.
416 if (size <= 48) return unblocked(m);
417
418 Index blockSize = size / 8;
419 blockSize = (blockSize / 16) * 16;
420 blockSize = (std::min)((std::max)(blockSize, Index(16)), Index(128));
421
422 for (Index k = 0; k < size; k += blockSize) {
423 // partition the matrix:
424 // A00 | - | -
425 // lu = A10 | A11 | -
426 // A20 | A21 | A22
427 Index bs = (std::min)(blockSize, size - k);
428 Index rs = size - k - bs;
429 Block<MatrixType, Dynamic, Dynamic> A11(m, k, k, bs, bs);
430 Block<MatrixType, Dynamic, Dynamic> A21(m, k + bs, k, rs, bs);
431 Block<MatrixType, Dynamic, Dynamic> A22(m, k + bs, k + bs, rs, rs);
432
433 Index ret;
434 if ((ret = unblocked(A11)) >= 0) return k + ret;
435 if (rs > 0) A11.adjoint().template triangularView<Upper>().template solveInPlace<OnTheRight>(A21);
436 if (rs > 0)
437 A22.template selfadjointView<Lower>().rankUpdate(A21,
438 typename NumTraits<RealScalar>::Literal(-1)); // bottleneck
439 }
440 return -1;
441 }
442
443 template <typename MatrixType, typename VectorType>
444 static Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) {
445 return Eigen::internal::llt_rank_update_lower(mat, vec, sigma);
446 }
447};
448
449template <typename Scalar>
450struct llt_inplace<Scalar, Upper> {
451 using RealScalar = typename NumTraits<Scalar>::Real;
452
453 template <typename MatrixType>
454 static EIGEN_STRONG_INLINE Index unblocked(MatrixType& mat) {
455 Transpose<MatrixType> matt(mat);
456 return llt_inplace<Scalar, Lower>::unblocked(matt);
457 }
458 template <typename MatrixType>
459 static EIGEN_STRONG_INLINE Index blocked(MatrixType& mat) {
460 Transpose<MatrixType> matt(mat);
461 return llt_inplace<Scalar, Lower>::blocked(matt);
462 }
463 template <typename MatrixType, typename VectorType>
464 static Index rankUpdate(MatrixType& mat, const VectorType& vec, const RealScalar& sigma) {
465 Transpose<MatrixType> matt(mat);
466 return llt_inplace<Scalar, Lower>::rankUpdate(matt, vec.conjugate(), sigma);
467 }
468};
469
470template <typename MatrixType>
471struct LLT_Traits<MatrixType, Lower> {
472 using MatrixL = const TriangularView<const MatrixType, Lower>;
473 using MatrixU = const TriangularView<const typename MatrixType::AdjointReturnType, Upper>;
474 static inline MatrixL getL(const MatrixType& m) { return MatrixL(m); }
475 static inline MatrixU getU(const MatrixType& m) { return MatrixU(m.adjoint()); }
476 static bool inplace_decomposition(MatrixType& m) {
477 return llt_inplace<typename MatrixType::Scalar, Lower>::blocked(m) == -1;
478 }
479};
480
481template <typename MatrixType>
482struct LLT_Traits<MatrixType, Upper> {
483 using MatrixL = const TriangularView<const typename MatrixType::AdjointReturnType, Lower>;
484 using MatrixU = const TriangularView<const MatrixType, Upper>;
485 static inline MatrixL getL(const MatrixType& m) { return MatrixL(m.adjoint()); }
486 static inline MatrixU getU(const MatrixType& m) { return MatrixU(m); }
487 static bool inplace_decomposition(MatrixType& m) {
488 return llt_inplace<typename MatrixType::Scalar, Upper>::blocked(m) == -1;
489 }
490};
491
492} // end namespace internal
493
501template <typename MatrixType, int UpLo_>
502template <typename InputType>
503LLT<MatrixType, UpLo_>& LLT<MatrixType, UpLo_>::compute(const EigenBase<InputType>& a) {
504 eigen_assert(a.rows() == a.cols());
505 const Index size = a.rows();
506 m_matrix.resize(size, size);
507 if (!internal::is_same_dense(m_matrix, a.derived())) m_matrix = a.derived();
508
509 // Compute matrix L1 norm = max abs column sum over the implicit self-adjoint matrix.
510 m_l1_norm = m_matrix.template selfadjointView<UpLo_>().l1Norm();
511
512 m_isInitialized = true;
513 bool ok = Traits::inplace_decomposition(m_matrix);
514 m_info = ok ? Success : NumericalIssue;
515
516 return *this;
517}
518
524template <typename MatrixType_, int UpLo_>
525template <typename VectorType>
526LLT<MatrixType_, UpLo_>& LLT<MatrixType_, UpLo_>::rankUpdate(const VectorType& v, const RealScalar& sigma) {
527 EIGEN_STATIC_ASSERT_VECTOR_ONLY(VectorType);
528 eigen_assert(v.size() == m_matrix.cols());
529 eigen_assert(m_isInitialized);
530 if (internal::llt_inplace<typename MatrixType::Scalar, UpLo>::rankUpdate(m_matrix, v, sigma) >= 0)
531 m_info = NumericalIssue;
532 else
533 m_info = Success;
534
535 return *this;
536}
537
538// A = L L^*, with L real and positive on the diagonal, so det(A) = prod(L_ii)^2 > 0.
539
540template <typename MatrixType_, int UpLo_>
541typename LLT<MatrixType_, UpLo_>::Scalar LLT<MatrixType_, UpLo_>::determinant() const {
542 return Scalar(absDeterminant());
543}
544
545template <typename MatrixType_, int UpLo_>
547 eigen_assert(m_isInitialized && "LLT is not initialized.");
548 eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
549 return numext::abs2(m_matrix.diagonal().real().prod());
550}
551
552template <typename MatrixType_, int UpLo_>
554 eigen_assert(m_isInitialized && "LLT is not initialized.");
555 eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
556 return RealScalar(2) * m_matrix.diagonal().real().array().log().sum();
557}
558
559template <typename MatrixType_, int UpLo_>
560typename LLT<MatrixType_, UpLo_>::Scalar LLT<MatrixType_, UpLo_>::signDeterminant() const {
561 eigen_assert(m_isInitialized && "LLT is not initialized.");
562 eigen_assert(m_info == Success && "LLT failed because matrix appears to be negative");
563 return Scalar(1);
564}
565
566#ifndef EIGEN_PARSED_BY_DOXYGEN
567template <typename MatrixType_, int UpLo_>
568template <typename RhsType, typename DstType>
569void LLT<MatrixType_, UpLo_>::_solve_impl(const RhsType& rhs, DstType& dst) const {
570 _solve_impl_transposed<true>(rhs, dst);
571}
572
573template <typename MatrixType_, int UpLo_>
574template <bool Conjugate, typename RhsType, typename DstType>
575void LLT<MatrixType_, UpLo_>::_solve_impl_transposed(const RhsType& rhs, DstType& dst) const {
576 dst = rhs;
577
578 matrixL().template conjugateIf<!Conjugate>().solveInPlace(dst);
579 matrixU().template conjugateIf<!Conjugate>().solveInPlace(dst);
580}
581#endif
582
596template <typename MatrixType, int UpLo_>
597template <typename Derived>
598void LLT<MatrixType, UpLo_>::solveInPlace(const MatrixBase<Derived>& bAndX) const {
599 eigen_assert(m_isInitialized && "LLT is not initialized.");
600 eigen_assert(m_matrix.rows() == bAndX.rows());
601 matrixL().solveInPlace(bAndX);
602 matrixU().solveInPlace(bAndX);
603}
604
605namespace internal {
606
607/***** Implementation of inverse() *****************************************************/
608template <typename DstXprType, typename MatrixType, int UpLo_>
609struct Assignment<DstXprType, Inverse<LLT<MatrixType, UpLo_> >,
610 internal::assign_op<typename DstXprType::Scalar, typename LLT<MatrixType, UpLo_>::Scalar>,
611 Dense2Dense> {
612 using LltType = LLT<MatrixType, UpLo_>;
613 using SrcXprType = Inverse<LltType>;
614 static constexpr unsigned int kMirrorMode = UpLo_ == Lower ? StrictlyUpper : StrictlyLower;
615 static void run(DstXprType& dst, const SrcXprType& src,
616 const internal::assign_op<typename DstXprType::Scalar, typename LltType::Scalar>&) {
617 const LltType& llt = src.nestedExpression();
618 eigen_assert(llt.info() == Success && "LLT::inverse(): the factorization failed.");
619 const Index size = llt.rows();
620 if ((dst.rows() != size) || (dst.cols() != size)) dst.resize(size, size);
621
622 // Complex is never thresholded; see EIGEN_LLT_INVERSE_POTRI_THRESHOLD.
623 constexpr Index kPotriThreshold =
624 NumTraits<typename LltType::Scalar>::IsComplex ? Index(0) : Index(EIGEN_LLT_INVERSE_POTRI_THRESHOLD);
625 // An in-place LLT<Ref<...>> may be asked to overwrite its own factor. The POTRI sequence does exactly
626 // that, so it takes the alias at every size; the solve fallback would read a factor that setIdentity()
627 // had already destroyed. extract_data() is null for a destination whose inner stride is not known to
628 // be 1 at compile time, which leaves the alias unknown rather than excluded, so that goes the same way.
629 const typename DstXprType::Scalar* dst_data = extract_data(dst);
630 const bool overwrites_factor = dst_data == nullptr || dst_data == llt.matrixLLT().data();
631 if (overwrites_factor || size >= kPotriThreshold) {
632 // A = L L^*, hence A^-1 = L^-* L^-1: invert the factor (xTRTRI), then square it (xLAUUM).
633 dst.template triangularView<UpLo_>() = llt.matrixLLT().template triangularView<UpLo_>();
634 dst.template triangularView<UpLo_>().inverseInPlace();
635 internal::triangular_adjoint_square_in_place<UpLo_>(dst);
636 } else {
637 dst.setIdentity();
638 llt.solveInPlace(dst);
639 }
640 // Mirror; (i, j) reads (j, i), which lies in the computed triangle and is never written here, so
641 // the aliasing is benign. LAUUM makes the computed diagonal exactly real, as does the real-scalar
642 // solve below the threshold, so the result is exactly self-adjoint.
643 dst.template triangularView<kMirrorMode>() = dst.adjoint();
644 }
645};
646
647} // end namespace internal
648
652template <typename MatrixType, int UpLo_>
654 eigen_assert(m_isInitialized && "LLT is not initialized.");
655 return matrixL() * matrixL().adjoint().toDenseMatrix();
656}
657
662template <typename Derived>
666
671template <typename MatrixType, unsigned int UpLo>
676
677} // end namespace Eigen
678
679#endif // EIGEN_LLT_H
Expression of the inverse of another expression.
Definition Inverse.h:44
Rotation given by a cosine-sine pair.
Definition Jacobi.h:39
void makeGivens(const Scalar &p, const Scalar &q, Scalar *r=0)
Definition Jacobi.h:165
Standard Cholesky decomposition (LL^T) of a matrix and associated features.
Definition LLT.h:85
ComputationInfo info() const
Reports whether previous computation was successful.
Definition LLT.h:218
Solve< LLT, Rhs > solve(const MatrixBase< Rhs > &b) const
const LLT & adjoint() const noexcept
Definition LLT.h:229
LLT(Index size)
Default Constructor with memory preallocation.
Definition LLT.h:113
Scalar determinant() const
Definition LLT.h:541
RealScalar rcond() const
Definition LLT.h:170
Inverse< LLT > inverse() const
Definition LLT.h:206
const MatrixType & matrixLLT() const
Definition LLT.h:180
LLT(EigenBase< InputType > &matrix)
Constructs a LLT factorization from a given matrix.
Definition LLT.h:129
RealScalar absDeterminant() const
Definition LLT.h:546
RealScalar logAbsDeterminant() const
Definition LLT.h:553
Scalar signDeterminant() const
Definition LLT.h:560
Traits::MatrixU matrixU() const
Definition LLT.h:135
LLT()
Default Constructor.
Definition LLT.h:105
MatrixType reconstructedMatrix() const
Definition LLT.h:653
Traits::MatrixL matrixL() const
Definition LLT.h:141
Base class for all dense matrices, vectors, and expressions.
Definition MatrixBase.h:53
LLT< PlainObject > llt() const
Definition LLT.h:663
const MatrixSquareRootReturnValue< MatrixWrapper< ExpressionType > > sqrt() const
The matrix class, also used for vectors and row-vectors.
Definition Matrix.h:188
LLT< PlainObject, UpLo > llt() const
Definition LLT.h:672
Pseudo expression representing a solving operation.
Definition Solve.h:63
constexpr LLT< MatrixType, UpLo_ > & derived()
ComputationInfo
Definition Constants.h:455
@ StrictlyLower
Definition Constants.h:224
@ StrictlyUpper
Definition Constants.h:226
@ Lower
Definition Constants.h:212
@ Upper
Definition Constants.h:214
@ NumericalIssue
Definition Constants.h:459
@ InvalidInput
Definition Constants.h:464
@ Success
Definition Constants.h:457
Definition EigenBase.h:34
constexpr Derived & derived()
Definition EigenBase.h:50
constexpr Index size() const noexcept
Definition EigenBase.h:65