Eigen  5.0.1
 
Loading...
Searching...
No Matches
ConditionEstimator.h
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2016 Rasmus Munk Larsen (rmlarsen@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_CONDITIONESTIMATOR_H
12#define EIGEN_CONDITIONESTIMATOR_H
13
14// IWYU pragma: private
15#include "./InternalHeaderCheck.h"
16
17namespace Eigen {
18
19namespace internal {
20
21template <typename Vector, typename RealVector, bool IsComplex>
22struct rcond_compute_sign {
23 static inline Vector run(const Vector& v) {
24 const RealVector v_abs = v.cwiseAbs();
25 return (v_abs.array() == static_cast<typename Vector::RealScalar>(0))
26 .select(Vector::Ones(v.size()), v.cwiseQuotient(v_abs));
27 }
28};
29
30// Partial specialization to avoid elementwise division for real vectors.
31template <typename Vector>
32struct rcond_compute_sign<Vector, Vector, false> {
33 static inline Vector run(const Vector& v) {
34 return (v.array() < static_cast<typename Vector::RealScalar>(0))
35 .select(-Vector::Ones(v.size()), Vector::Ones(v.size()));
36 }
37};
38
58template <typename Decomposition>
59typename Decomposition::RealScalar rcond_invmatrix_L1_norm_estimate(const Decomposition& dec) {
60 using MatrixType = typename Decomposition::MatrixType;
61 using Scalar = typename Decomposition::Scalar;
62 using RealScalar = typename Decomposition::RealScalar;
63 using Vector = typename internal::plain_col_type<MatrixType>::type;
64 using RealVector = typename internal::plain_col_type<MatrixType, RealScalar>::type;
65 const bool is_complex = (NumTraits<Scalar>::IsComplex != 0);
66
67 eigen_assert(dec.rows() == dec.cols());
68 const Index n = dec.rows();
69 if (n == 0) return RealScalar(0);
70
71 // Disable Index to float conversion warning
72#ifdef __INTEL_COMPILER
73#pragma warning push
74#pragma warning(disable : 2259)
75#endif
76 Vector v = dec.solve(Vector::Ones(n) / Scalar(n));
77#ifdef __INTEL_COMPILER
78#pragma warning pop
79#endif
80
81 // lower_bound is a lower bound on
82 // ||inv(matrix)||_1 = sup_v ||inv(matrix) v||_1 / ||v||_1
83 // and is the objective maximized by the supergradient ascent algorithm below.
84 RealScalar lower_bound = v.template lpNorm<1>();
85 if (n == 1) return lower_bound;
86
87 // Gradient ascent: the optimum is achieved at a unit vector e_j. Each
88 // iteration follows the supergradient to find which unit vector to probe next.
89 RealScalar old_lower_bound = lower_bound;
90 Vector sign_vector(n);
91 Vector old_sign_vector;
92 Index v_max_abs_index = -1;
93 Index old_v_max_abs_index = v_max_abs_index;
94 for (int k = 0; k < 4; ++k) {
95 sign_vector = internal::rcond_compute_sign<Vector, RealVector, is_complex>::run(v);
96 if (k > 0 && !is_complex && sign_vector == old_sign_vector) {
97 // Break if the sign vector stagnated.
98 break;
99 }
100 // Supergradient: z = A^{-T} * sign(v), pick argmax |z_i|.
101 v = dec.adjoint().solve(sign_vector);
102 v.real().cwiseAbs().maxCoeff(&v_max_abs_index);
103 if (v_max_abs_index == old_v_max_abs_index) {
104 // Optimality: supergradient points to the same unit vector.
105 break;
106 }
107 // Probe the best unit vector: v = A^{-1} * e_j.
108 v = dec.solve(Vector::Unit(n, v_max_abs_index));
109 lower_bound = v.template lpNorm<1>();
110 if (lower_bound <= old_lower_bound) {
111 // No improvement from the gradient step.
112 break;
113 }
114 if (!is_complex) {
115 old_sign_vector = sign_vector;
116 }
117 old_v_max_abs_index = v_max_abs_index;
118 old_lower_bound = lower_bound;
119 }
120 // Higham's alternating-sign estimate: an independent safety-net that catches
121 // cases where the gradient ascent converges to a local maximum due to exact
122 // cancellation patterns (especially with permutations and backsubstitutions).
123 // v_i = (-1)^i * (1 + i/(n-1)), then estimate = 2*||A^{-1}*v||_1 / (3*n).
124 Scalar alternating_sign(RealScalar(1));
125 for (Index i = 0; i < n; ++i) {
126 // The static_cast is needed when Scalar is complex and RealScalar uses expression templates.
127 v[i] = alternating_sign * static_cast<RealScalar>(RealScalar(1) + (RealScalar(i) / (RealScalar(n - 1))));
128 alternating_sign = -alternating_sign;
129 }
130 v = dec.solve(v);
131 const RealScalar alt_est = (RealScalar(2) * v.template lpNorm<1>()) / (RealScalar(3) * RealScalar(n));
132 return numext::maxi(lower_bound, alt_est);
133}
134
148template <typename Decomposition>
149typename Decomposition::RealScalar rcond_estimate_helper(typename Decomposition::RealScalar matrix_norm,
150 const Decomposition& dec) {
151 using RealScalar = typename Decomposition::RealScalar;
152 eigen_assert(dec.rows() == dec.cols());
153 if (dec.rows() == 0) return NumTraits<RealScalar>::infinity();
154 if (numext::is_exactly_zero(matrix_norm)) return RealScalar(0);
155 if (dec.rows() == 1) return RealScalar(1);
156 const RealScalar inverse_matrix_norm = rcond_invmatrix_L1_norm_estimate(dec);
157 return (numext::is_exactly_zero(inverse_matrix_norm) ? RealScalar(0)
158 : (RealScalar(1) / inverse_matrix_norm) / matrix_norm);
159}
160
161} // namespace internal
162
163} // namespace Eigen
164
165#endif
Matrix< Type, Size, 1 > Vector
SizeƗ1 vector of type Type.
Definition Matrix.h:532