SOPT
Sparse OPTimisation
power_method.h
Go to the documentation of this file.
1 #ifndef SOPT_POWER_METHO_H
2 #define SOPT_POWER_METHO_H
3 
4 #include "sopt/config.h"
5 #include <functional>
6 #include <limits>
7 #include <memory> // for std::shared_ptr<>
8 #include <tuple>
9 #include <utility> // for std::move<>
10 #include "sopt/exception.h"
11 #include "sopt/linear_transform.h"
12 #include "sopt/logging.h"
14 #include "sopt/types.h"
15 #ifdef SOPT_MPI
16 #include "sopt/mpi/communicator.h"
17 #endif
18 
19 namespace sopt::algorithm {
22 template <typename T>
23 std::tuple<t_real, T> power_method(const sopt::LinearTransform<T> &op, const t_uint niters,
24  const t_real relative_difference, const T &initial_vector) {
25  /*
26  Attempt at coding the power method, returns the sqrt of the largest eigen value of a linear
27  operator composed with its adjoint niters:: max number of iterations relative_difference::
28  percentage difference at which eigen value has converged
29  */
30  if (niters <= 0) return std::make_tuple(1., initial_vector);
31  t_real estimate_eigen_value = 1;
32  t_real old_value = 0;
33  T estimate_eigen_vector = initial_vector;
34  estimate_eigen_vector = estimate_eigen_vector / estimate_eigen_vector.matrix().stableNorm();
35  SOPT_DEBUG("Starting power method");
36  SOPT_DEBUG(" -[PM] Iteration: 0, norm = {}", estimate_eigen_value);
37  bool converged = false;
38  ScalarRelativeVariation<t_real> scalvar(relative_difference, 0., "Eigenvalue");
39  for (t_int i = 0; i < niters; ++i) {
40  estimate_eigen_vector = op.adjoint() * static_cast<T>(op * estimate_eigen_vector);
41  estimate_eigen_value = estimate_eigen_vector.matrix().stableNorm();
42  if (estimate_eigen_value <= 0) throw std::runtime_error("Error in operator.");
43  if (estimate_eigen_value != estimate_eigen_value)
44  throw std::runtime_error("Error in operator or data corrupted.");
45  estimate_eigen_vector = estimate_eigen_vector / estimate_eigen_value;
46  SOPT_DEBUG(" -[PM] Iteration: {}, norm = {}", i + 1, estimate_eigen_value);
47  converged = scalvar(std::sqrt(estimate_eigen_value));
48  old_value = estimate_eigen_value;
49  if (converged) {
50  SOPT_DEBUG("Converged to norm = {}, relative difference < {}", std::sqrt(old_value),
51  relative_difference);
52  break;
53  }
54  }
55  return std::make_tuple(std::sqrt(old_value), estimate_eigen_vector);
56 }
57 
58 template <typename T>
59 std::tuple<t_real, T, std::shared_ptr<sopt::LinearTransform<T>>> normalise_operator(
60  const std::shared_ptr<sopt::LinearTransform<T> const> &op, const t_uint &niters,
61  const t_real &relative_difference, const T &initial_vector) {
62  const auto result = power_method(*op, niters, relative_difference, initial_vector.derived());
63  const t_real norm = std::get<0>(result);
64  return std::make_tuple(
65  std::get<0>(result), std::get<1>(result),
66  std::make_shared<sopt::LinearTransform<T>>(
67  [op, norm](T &output, const T &input) { output = static_cast<T>(*op * input) / norm; },
68  op->sizes(),
69  [op, norm](T &output, const T &input) {
70  output = static_cast<T>(op->adjoint() * input) / norm;
71  },
72  op->adjoint().sizes()));
73 }
74 template <typename T>
75 std::tuple<t_real, T, sopt::LinearTransform<T>> normalise_operator(
76  const sopt::LinearTransform<T> &op, const t_uint &niters, const t_real &relative_difference,
77  const T &initial_vector) {
78  const std::shared_ptr<sopt::LinearTransform<T>> shared_op =
79  std::make_shared<sopt::LinearTransform<T>>(std::move(op));
80  const auto result = normalise_operator<T>(shared_op, niters, relative_difference, initial_vector);
81  const auto normed_shared_op = std::get<2>(result);
82  return std::make_tuple(std::get<0>(result), std::get<1>(result), *normed_shared_op);
83 }
84 #ifdef SOPT_MPI
86 template <typename T>
87 std::tuple<t_real, T> all_sum_all_power_method(const sopt::mpi::Communicator &comm,
88  const sopt::LinearTransform<T> &op,
89  const t_uint &niters,
90  const t_real &relative_difference,
91  const T &initial_vector) {
92  const auto all_sum_all_op = sopt::LinearTransform<T>(
93  [&op](T &output, const T &input) { output = static_cast<T>(op * input); }, op.sizes(),
94  [&op, comm](T &output, const T &input) {
95  output = comm.all_sum_all(static_cast<T>(op.adjoint() * input));
96  },
97  op.adjoint().sizes());
98  return power_method(all_sum_all_op, niters, relative_difference, initial_vector.derived());
99 }
100 template <typename T>
101 std::tuple<t_real, T, std::shared_ptr<sopt::LinearTransform<T>>> all_sum_all_normalise_operator(
102  const sopt::mpi::Communicator &comm, const std::shared_ptr<sopt::LinearTransform<T> const> &op,
103  const t_uint &niters, const t_real &relative_difference, const T &initial_vector) {
104  const auto all_sum_all_op = sopt::LinearTransform<T>(
105  [op](T &output, const T &input) { output = static_cast<T>(*op * input); }, op->sizes(),
106  [op, comm](T &output, const T &input) {
107  output = comm.all_sum_all(static_cast<T>(op->adjoint() * input));
108  },
109  op->adjoint().sizes());
110  const auto result =
111  power_method(all_sum_all_op, niters, relative_difference, initial_vector.derived());
112  const t_real norm = std::get<0>(result);
113  return std::make_tuple(
114  std::get<0>(result), std::get<1>(result),
115  std::make_shared<sopt::LinearTransform<T>>(
116  [op, norm](T &output, const T &input) { output = static_cast<T>(*op * input) / norm; },
117  op->sizes(),
118  [op, norm](T &output, const T &input) {
119  output = static_cast<T>(op->adjoint() * input) / norm;
120  },
121  op->adjoint().sizes()));
122 }
123 template <typename T>
124 std::tuple<t_real, T, sopt::LinearTransform<T>> all_sum_all_normalise_operator(
125  const sopt::mpi::Communicator &comm, const sopt::LinearTransform<T> &op, const t_uint &niters,
126  const t_real &relative_difference, const T &initial_vector) {
127  const std::shared_ptr<sopt::LinearTransform<T>> shared_op =
128  std::make_shared<sopt::LinearTransform<T>>(std::move(op));
129  const auto result =
130  normalise_operator<T>(comm, shared_op, niters, relative_difference, initial_vector);
131  const auto normed_shared_op = std::get<2>(result);
132  return std::make_tuple(std::get<0>(result), std::get<1>(result), *normed_shared_op);
133 }
134 #endif
136 template <typename SCALAR>
137 class PowerMethod {
138  public:
140  using value_type = SCALAR;
144  using Real = typename real_type<Scalar>::type;
149 
155  bool good;
160  };
161 
163  PowerMethod() : itermax_(std::numeric_limits<t_uint>::max()), tolerance_(1e-8) {}
164  virtual ~PowerMethod() {}
165 
166 // Macro helps define properties that can be initialized as in
167 // auto sdmm = ProximalADMM<float>().prop0(value).prop1(value);
168 #define SOPT_MACRO(NAME, TYPE) \
169  TYPE const &NAME() const { return NAME##_; } \
170  PowerMethod<SCALAR> &NAME(TYPE const &(NAME)) { \
171  NAME##_ = NAME; \
172  return *this; \
173  } \
174  \
175  protected: \
176  TYPE NAME##_; \
177  \
178  public:
179 
181  SOPT_MACRO(itermax, t_uint)
183  SOPT_MACRO(tolerance, Real)
184 #undef SOPT_MACRO
186  DiagnosticAndResult AtA(t_LinearTransform const &A, t_Vector const &input) const;
187 
189  template <typename DERIVED>
190  DiagnosticAndResult operator()(Eigen::DenseBase<DERIVED> const &A, t_Vector const &input) const;
191 
193  DiagnosticAndResult operator()(OperatorFunction<t_Vector> const &op, t_Vector const &input) const;
194 
195  protected:
196 };
197 
198 template <typename SCALAR>
200  t_LinearTransform const &A, t_Vector const &input) const {
201  auto const op = [&A](t_Vector &out, t_Vector const &input) -> void {
202  out = A.adjoint() * static_cast<t_Vector>(A * input);
203  };
204  return operator()(op, input);
205 }
206 
207 template <typename SCALAR>
208 template <typename DERIVED>
210  Eigen::DenseBase<DERIVED> const &A, t_Vector const &input) const {
211  Matrix<Scalar> const Ad = A.derived();
212  auto const op = [&Ad](t_Vector &out, t_Vector const &input) -> void { out = Ad * input; };
213  return operator()(op, input);
214 }
215 
216 template <typename SCALAR>
218  OperatorFunction<t_Vector> const &op, t_Vector const &input) const {
219  SOPT_INFO("Computing the upper bound of a given operator");
220  SOPT_INFO(" - input vector {}", input.transpose());
221  t_Vector eigenvector = input.normalized();
222  SOPT_INFO(" - eigenvector norm {}", eigenvector.stableNorm());
223  typename t_Vector::Scalar previous_magnitude = 1;
224  bool converged = false;
225  t_uint niters = 0;
226 
227  for (; niters < itermax() and converged == false; ++niters) {
228  op(eigenvector, eigenvector);
229  typename t_Vector::Scalar const magnitude =
230  eigenvector.stableNorm() / static_cast<Real>(eigenvector.size());
231  auto const rel_val = std::abs((magnitude - previous_magnitude) / previous_magnitude);
232  converged = rel_val < tolerance();
233  SOPT_INFO(" - [PM] iteration {}/{} -- norm: {}", niters, itermax(), magnitude);
234 
235  eigenvector /= magnitude;
236  previous_magnitude = magnitude;
237  }
238  // check function exists, otherwise, don't know if convergence is meaningful
239  if (not converged) {
240  SOPT_WARN(" - [PM] did not converge within {} iterations", itermax());
241  } else {
242  SOPT_INFO(" - [PM] converged in {} of {} iterations", niters, itermax());
243  }
244  return DiagnosticAndResult{itermax(), converged, previous_magnitude, eigenvector.normalized()};
245 }
246 } // namespace sopt::algorithm
247 #endif
sopt::Vector< Scalar > t_Vector
sopt::t_real Scalar
Joins together direct and indirect operators.
LinearTransform< VECTOR > adjoint() const
Indirect transform.
Eigenvalue and eigenvector for eigenvalue with largest magnitude.
Definition: power_method.h:137
typename real_type< Scalar >::type Real
Real type.
Definition: power_method.h:144
value_type Scalar
Scalar type.
Definition: power_method.h:142
PowerMethod()
Setups ProximalADMM.
Definition: power_method.h:163
DiagnosticAndResult AtA(t_LinearTransform const &A, t_Vector const &input) const
Maximum number of iterations.
Definition: power_method.h:199
DiagnosticAndResult operator()(Eigen::DenseBase< DERIVED > const &A, t_Vector const &input) const
Calls the power method for A, with A a matrix.
Definition: power_method.h:209
SCALAR value_type
Scalar type.
Definition: power_method.h:140
Vector< Scalar > t_Vector
Type of then underlying vectors.
Definition: power_method.h:146
std::array< t_int, 3 > const & sizes() const
Defines relation-ship between input and output sizes.
Definition: wrapper.h:106
Computes inner-most element type.
Definition: real_type.h:42
sopt::t_real t_real
#define SOPT_WARN(...)
\macro Something might be going wrong
Definition: logging.h:213
#define SOPT_INFO(...)
\macro Verbose informational message about normal condition
Definition: logging.h:215
#define SOPT_DEBUG(...)
\macro Output some debugging
Definition: logging.h:217
std::tuple< t_real, T, std::shared_ptr< sopt::LinearTransform< T > > > normalise_operator(const std::shared_ptr< sopt::LinearTransform< T > const > &op, const t_uint &niters, const t_real &relative_difference, const T &initial_vector)
Definition: power_method.h:59
std::tuple< t_real, T > power_method(const sopt::LinearTransform< T > &op, const t_uint niters, const t_real relative_difference, const T &initial_vector)
Returns the eigenvalue and eigenvector for eigenvalue of the Linear Transform with largest magnitude.
Definition: power_method.h:23
int t_int
Root of the type hierarchy for signed integers.
Definition: types.h:13
double t_real
Root of the type hierarchy for real numbers.
Definition: types.h:17
size_t t_uint
Root of the type hierarchy for unsigned integers.
Definition: types.h:15
std::function< void(VECTOR &, VECTOR const &)> OperatorFunction
Typical function out = A*x.
Definition: types.h:43
Eigen::Matrix< T, Eigen::Dynamic, 1 > Vector
A vector of a given type.
Definition: types.h:24
Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic > Matrix
A matrix of a given type.
Definition: types.h:29
#define SOPT_MACRO(NAME, TYPE)
Definition: power_method.h:168
bool good
Wether convergence was achieved.
Definition: power_method.h:155
Vector< Scalar > eigenvector
Corresponding eigenvector if converged.
Definition: power_method.h:159
Scalar magnitude
Magnitude of the eigenvalue.
Definition: power_method.h:157