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 #include "./InternalHeaderCheck.h"
14 
15 namespace Eigen {
16 
17 namespace internal {
18 
19 template <typename Vector, typename RealVector, bool IsComplex>
20 struct rcond_compute_sign {
21  static inline Vector run(const Vector& v) {
22  const RealVector v_abs = v.cwiseAbs();
23  return (v_abs.array() == static_cast<typename Vector::RealScalar>(0))
24  .select(Vector::Ones(v.size()), v.cwiseQuotient(v_abs));
25  }
26 };
27 
28 // Partial specialization to avoid elementwise division for real vectors.
29 template <typename Vector>
30 struct rcond_compute_sign<Vector, Vector, false> {
31  static inline Vector run(const Vector& v) {
32  return (v.array() < static_cast<typename Vector::RealScalar>(0))
33  .select(-Vector::Ones(v.size()), Vector::Ones(v.size()));
34  }
35 };
36 
57 template <typename Decomposition>
58 typename Decomposition::RealScalar rcond_invmatrix_L1_norm_estimate(const Decomposition& dec)
59 {
60  typedef typename Decomposition::MatrixType MatrixType;
61  typedef typename Decomposition::Scalar Scalar;
62  typedef typename Decomposition::RealScalar RealScalar;
63  typedef typename internal::plain_col_type<MatrixType>::type Vector;
64  typedef typename internal::plain_col_type<MatrixType, RealScalar>::type RealVector;
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)
70  return 0;
71 
72  // Disable Index to float conversion warning
73 #ifdef __INTEL_COMPILER
74  #pragma warning push
75  #pragma warning ( disable : 2259 )
76 #endif
77  Vector v = dec.solve(Vector::Ones(n) / Scalar(n));
78 #ifdef __INTEL_COMPILER
79  #pragma warning pop
80 #endif
81 
82  // lower_bound is a lower bound on
83  // ||inv(matrix)||_1 = sup_v ||inv(matrix) v||_1 / ||v||_1
84  // and is the objective maximized by the ("super-") gradient ascent
85  // algorithm below.
86  RealScalar lower_bound = v.template lpNorm<1>();
87  if (n == 1)
88  return lower_bound;
89 
90  // Gradient ascent algorithm follows: We know that the optimum is achieved at
91  // one of the simplices v = e_i, so in each iteration we follow a
92  // super-gradient to move towards the optimal one.
93  RealScalar old_lower_bound = lower_bound;
94  Vector sign_vector(n);
95  Vector old_sign_vector;
96  Index v_max_abs_index = -1;
97  Index old_v_max_abs_index = v_max_abs_index;
98  for (int k = 0; k < 4; ++k)
99  {
100  sign_vector = internal::rcond_compute_sign<Vector, RealVector, is_complex>::run(v);
101  if (k > 0 && !is_complex && sign_vector == old_sign_vector) {
102  // Break if the solution stagnated.
103  break;
104  }
105  // v_max_abs_index = argmax |real( inv(matrix)^T * sign_vector )|
106  v = dec.adjoint().solve(sign_vector);
107  v.real().cwiseAbs().maxCoeff(&v_max_abs_index);
108  if (v_max_abs_index == old_v_max_abs_index) {
109  // Break if the solution stagnated.
110  break;
111  }
112  // Move to the new simplex e_j, where j = v_max_abs_index.
113  v = dec.solve(Vector::Unit(n, v_max_abs_index)); // v = inv(matrix) * e_j.
114  lower_bound = v.template lpNorm<1>();
115  if (lower_bound <= old_lower_bound) {
116  // Break if the gradient step did not increase the lower_bound.
117  break;
118  }
119  if (!is_complex) {
120  old_sign_vector = sign_vector;
121  }
122  old_v_max_abs_index = v_max_abs_index;
123  old_lower_bound = lower_bound;
124  }
125  // The following calculates an independent estimate of ||matrix||_1 by
126  // multiplying matrix by a vector with entries of slowly increasing
127  // magnitude and alternating sign:
128  // v_i = (-1)^{i} (1 + (i / (dim-1))), i = 0,...,dim-1.
129  // This improvement to Hager's algorithm above is due to Higham. It was
130  // added to make the algorithm more robust in certain corner cases where
131  // large elements in the matrix might otherwise escape detection due to
132  // exact cancellation (especially when op and op_adjoint correspond to a
133  // sequence of backsubstitutions and permutations), which could cause
134  // Hager's algorithm to vastly underestimate ||matrix||_1.
135  Scalar alternating_sign(RealScalar(1));
136  for (Index i = 0; i < n; ++i) {
137  // The static_cast is needed when Scalar is a complex and RealScalar implements expression templates
138  v[i] = alternating_sign * static_cast<RealScalar>(RealScalar(1) + (RealScalar(i) / (RealScalar(n - 1))));
139  alternating_sign = -alternating_sign;
140  }
141  v = dec.solve(v);
142  const RealScalar alternate_lower_bound = (2 * v.template lpNorm<1>()) / (3 * RealScalar(n));
143  return numext::maxi(lower_bound, alternate_lower_bound);
144 }
145 
159 template <typename Decomposition>
160 typename Decomposition::RealScalar
161 rcond_estimate_helper(typename Decomposition::RealScalar matrix_norm, const Decomposition& dec)
162 {
163  typedef typename Decomposition::RealScalar RealScalar;
164  eigen_assert(dec.rows() == dec.cols());
165  if (dec.rows() == 0) return NumTraits<RealScalar>::infinity();
166  if (numext::is_exactly_zero(matrix_norm)) return RealScalar(0);
167  if (dec.rows() == 1) return RealScalar(1);
168  const RealScalar inverse_matrix_norm = rcond_invmatrix_L1_norm_estimate(dec);
169  return (numext::is_exactly_zero(inverse_matrix_norm) ? RealScalar(0)
170  : (RealScalar(1) / inverse_matrix_norm) / matrix_norm);
171 }
172 
173 } // namespace internal
174 
175 } // namespace Eigen
176 
177 #endif
Array< int, Dynamic, 1 > v
int n
#define eigen_assert(x)
Definition: Macros.h:902
Matrix< float, 1, Dynamic > MatrixType
static const ConstantReturnType Ones()
static const BasisReturnType Unit(Index size, Index i)
The matrix class, also used for vectors and row-vectors.
Definition: Matrix.h:182
Matrix< Type, Size, 1 > Vector
[c++11] SizeƗ1 vector of type Type.
Definition: Matrix.h:545
Decomposition::RealScalar rcond_invmatrix_L1_norm_estimate(const Decomposition &dec)
Decomposition::RealScalar rcond_estimate_helper(typename Decomposition::RealScalar matrix_norm, const Decomposition &dec)
Reciprocal condition number estimator.
EIGEN_ALWAYS_INLINE T maxi(const T &x, const T &y)
bool is_exactly_zero(const X &x)
Definition: Meta.h:475
: InteropHeaders
Definition: Core:139
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index
The Index type as used for the API.
Definition: Meta.h:82
Holds information about the various numeric (i.e. scalar) types allowed by Eigen.
Definition: NumTraits.h:231