ConditionEstimator.h
Go to the documentation of this file.
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@google.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 
10 #ifndef EIGEN_CONDITIONESTIMATOR_H
11 #define EIGEN_CONDITIONESTIMATOR_H
12 
13 // IWYU pragma: private
14 #include "./InternalHeaderCheck.h"
15 
16 namespace Eigen {
17 
18 namespace internal {
19 
20 template <typename Vector, typename RealVector, bool IsComplex>
22  static inline Vector run(const Vector& v) {
23  const RealVector v_abs = v.cwiseAbs();
24  return (v_abs.array() == static_cast<typename Vector::RealScalar>(0))
25  .select(Vector::Ones(v.size()), v.cwiseQuotient(v_abs));
26  }
27 };
28 
29 // Partial specialization to avoid elementwise division for real vectors.
30 template <typename Vector>
31 struct rcond_compute_sign<Vector, Vector, false> {
32  static inline Vector run(const Vector& v) {
33  return (v.array() < static_cast<typename Vector::RealScalar>(0))
34  .select(-Vector::Ones(v.size()), Vector::Ones(v.size()));
35  }
36 };
37 
58 template <typename Decomposition>
59 typename Decomposition::RealScalar rcond_invmatrix_L1_norm_estimate(const Decomposition& dec) {
60  typedef typename Decomposition::MatrixType MatrixType;
61  typedef typename Decomposition::Scalar Scalar;
62  typedef typename Decomposition::RealScalar RealScalar;
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 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 ("super-") gradient ascent
84  // algorithm below.
85  RealScalar lower_bound = v.template lpNorm<1>();
86  if (n == 1) return lower_bound;
87 
88  // Gradient ascent algorithm follows: We know that the optimum is achieved at
89  // one of the simplices v = e_i, so in each iteration we follow a
90  // super-gradient to move towards the optimal one.
91  RealScalar old_lower_bound = lower_bound;
92  Vector sign_vector(n);
93  Vector old_sign_vector;
94  Index v_max_abs_index = -1;
95  Index old_v_max_abs_index = v_max_abs_index;
96  for (int k = 0; k < 4; ++k) {
98  if (k > 0 && !is_complex && sign_vector == old_sign_vector) {
99  // Break if the solution stagnated.
100  break;
101  }
102  // v_max_abs_index = argmax |real( inv(matrix)^T * sign_vector )|
103  v = dec.adjoint().solve(sign_vector);
104  v.real().cwiseAbs().maxCoeff(&v_max_abs_index);
105  if (v_max_abs_index == old_v_max_abs_index) {
106  // Break if the solution stagnated.
107  break;
108  }
109  // Move to the new simplex e_j, where j = v_max_abs_index.
110  v = dec.solve(Vector::Unit(n, v_max_abs_index)); // v = inv(matrix) * e_j.
111  lower_bound = v.template lpNorm<1>();
112  if (lower_bound <= old_lower_bound) {
113  // Break if the gradient step did not increase the lower_bound.
114  break;
115  }
116  if (!is_complex) {
117  old_sign_vector = sign_vector;
118  }
119  old_v_max_abs_index = v_max_abs_index;
120  old_lower_bound = lower_bound;
121  }
122  // The following calculates an independent estimate of ||matrix||_1 by
123  // multiplying matrix by a vector with entries of slowly increasing
124  // magnitude and alternating sign:
125  // v_i = (-1)^{i} (1 + (i / (dim-1))), i = 0,...,dim-1.
126  // This improvement to Hager's algorithm above is due to Higham. It was
127  // added to make the algorithm more robust in certain corner cases where
128  // large elements in the matrix might otherwise escape detection due to
129  // exact cancellation (especially when op and op_adjoint correspond to a
130  // sequence of backsubstitutions and permutations), which could cause
131  // Hager's algorithm to vastly underestimate ||matrix||_1.
132  Scalar alternating_sign(RealScalar(1));
133  for (Index i = 0; i < n; ++i) {
134  // The static_cast is needed when Scalar is a complex and RealScalar implements expression templates
135  v[i] = alternating_sign * static_cast<RealScalar>(RealScalar(1) + (RealScalar(i) / (RealScalar(n - 1))));
136  alternating_sign = -alternating_sign;
137  }
138  v = dec.solve(v);
139  const RealScalar alternate_lower_bound = (2 * v.template lpNorm<1>()) / (3 * RealScalar(n));
140  return numext::maxi(lower_bound, alternate_lower_bound);
141 }
142 
156 template <typename Decomposition>
158  const Decomposition& dec) {
159  typedef typename Decomposition::RealScalar RealScalar;
160  eigen_assert(dec.rows() == dec.cols());
161  if (dec.rows() == 0) return NumTraits<RealScalar>::infinity();
162  if (numext::is_exactly_zero(matrix_norm)) return RealScalar(0);
163  if (dec.rows() == 1) return RealScalar(1);
164  const RealScalar inverse_matrix_norm = rcond_invmatrix_L1_norm_estimate(dec);
165  return (numext::is_exactly_zero(inverse_matrix_norm) ? RealScalar(0)
166  : (RealScalar(1) / inverse_matrix_norm) / matrix_norm);
167 }
168 
169 } // namespace internal
170 
171 } // namespace Eigen
172 
173 #endif
Array< int, Dynamic, 1 > v
Definition: Array_initializer_list_vector_cxx11.cpp:1
int i
Definition: BiCGSTAB_step_by_step.cpp:9
const unsigned n
Definition: CG3DPackingUnitTest.cpp:11
#define eigen_assert(x)
Definition: Macros.h:910
SCALAR Scalar
Definition: bench_gemm.cpp:45
NumTraits< Scalar >::Real RealScalar
Definition: bench_gemm.cpp:46
MatrixXf MatrixType
Definition: benchmark-blocking-sizes.cpp:52
The matrix class, also used for vectors and row-vectors.
Definition: Eigen/Eigen/src/Core/Matrix.h:186
NumTraits< Scalar >::Real RealScalar
Definition: PlainObjectBase.h:130
Matrix< Type, Size, 1 > Vector
\cpp11 SizeƗ1 vector of type Type.
Definition: Eigen/Eigen/src/Core/Matrix.h:515
char char char int int * k
Definition: level2_impl.h:374
Decomposition::RealScalar rcond_invmatrix_L1_norm_estimate(const Decomposition &dec)
Definition: ConditionEstimator.h:59
Decomposition::RealScalar rcond_estimate_helper(typename Decomposition::RealScalar matrix_norm, const Decomposition &dec)
Reciprocal condition number estimator.
Definition: ConditionEstimator.h:157
EIGEN_DEVICE_FUNC EIGEN_ALWAYS_INLINE T maxi(const T &x, const T &y)
Definition: MathFunctions.h:926
EIGEN_STRONG_INLINE EIGEN_DEVICE_FUNC bool is_exactly_zero(const X &x)
Definition: Meta.h:592
Namespace containing all symbols from the Eigen library.
Definition: bench_norm.cpp:70
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:83
Definition: Eigen_Colamd.h:49
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition: NumTraits.h:217
std::conditional_t< is_same< typename traits< ExpressionType >::XprKind, MatrixXpr >::value, MatrixColType, ArrayColType > type
Definition: XprHelper.h:782
static Vector run(const Vector &v)
Definition: ConditionEstimator.h:32
Definition: ConditionEstimator.h:21
static Vector run(const Vector &v)
Definition: ConditionEstimator.h:22