LSLOpt  1.0
BoxConstrainedAlgorithm.hpp
1 #pragma once
2 
3 #include <vector>
4 
5 #include "../ScalarTraits.hpp"
6 #include "../Types.hpp"
7 #include "DefineOutputMacros.hpp"
8 #include "ProblemTraits.hpp"
9 #include "SubspaceUtils.hpp"
10 
11 
12 namespace LSLOpt {
13 
14 namespace Implementation {
15 
22 template<typename Problem, typename Scalar, typename Storage>
24 
25  // we must have an lower bound
27  "Problem must provide lower bounds function: Vector<Scalar> lower_bounds()");
28 
29  // we must have an upper bound
31  "Problem must provide upper bounds function: Vector<Scalar> upper_bounds()");
32 
38  static bool check_bounds(Problem& problem);
39 
47  Problem& problem,
48  const Vector<Scalar>& x);
49 
57  static Scalar gradient_norm(
58  Problem& problem,
59  const Vector<Scalar>& x,
60  const Vector<Scalar>& g);
61 
69  static Scalar max_step_length(
70  Problem& problem,
71  const Vector<Scalar>& x,
72  const Vector<Scalar>& p);
73 
94  template<typename OutputFunction>
96  Problem& problem,
97  Storage& storage,
98  const Vector<Scalar>& x,
99  const Vector<Scalar>& g,
100  const Scalar& check_epsilon,
101  const Scalar& machine_epsilon,
102  OutputFunction& output_function);
103 };
104 
105 template<typename Problem, typename Scalar, typename Storage>
107 {
108  return (problem.lower_bounds().array() <= problem.upper_bounds().array()).all();
109 }
110 
111 template<typename Problem, typename Scalar, typename Storage>
113  Problem& problem,
114  const Vector<Scalar>& x)
115 {
116  Vector<Scalar> r(x.size());
117 
118  auto lb = problem.lower_bounds();
119  auto ub = problem.upper_bounds();
120 
121  // ensure that no bound is violated
122  r.array() = x.array().max(lb.array()).min(ub.array());
123 
124  return r;
125 }
126 
127 template<typename Problem, typename Scalar, typename Storage>
129  Problem& problem,
130  const Vector<Scalar>& x,
131  const Vector<Scalar>& g)
132 {
133  // calculate projected gradient norm
134  return projected_gradient_norm(x, g, problem.lower_bounds(), problem.upper_bounds());
135 }
136 
137 template<typename Problem, typename Scalar, typename Storage>
139  Problem& problem,
140  const Vector<Scalar>& x,
141  const Vector<Scalar>& p)
142 {
143  Scalar alpha_max = scalar_traits<Scalar>::infinity();
144 
145  typename problem_traits<Problem, Scalar>::lower_bound_type lower_bounds {problem.lower_bounds()};
146  typename problem_traits<Problem, Scalar>::upper_bound_type upper_bounds {problem.upper_bounds()};
147 
148  for (Eigen::Index i = 0; i < p.size(); ++i) {
149  const Scalar& pi = p(i);
150 
151  if (pi < Scalar{0}) {
152  Scalar lower_dist = lower_bounds(i) - x(i);
153  // if value is at or over bound, no progress in this direction
154  if (lower_dist >= Scalar{0}) {
155  alpha_max = Scalar{0};
156  }
157  else if (pi * alpha_max < lower_dist) {
158  alpha_max = lower_dist / pi;
159  }
160  }
161  else if (pi > Scalar{0}) {
162  Scalar upper_dist = upper_bounds(i) - x(i);
163  // if value is at or over bound, no progress in this direction
164  if (upper_dist <= Scalar{0}) {
165  alpha_max = Scalar{0};
166  }
167  else if (pi * alpha_max > upper_dist) {
168  alpha_max = upper_dist / pi;
169  }
170  }
171  }
172 
173  /*
174  * We need this additional check so the function is really never
175  * evaluated outside the bounds!
176  *
177  * The alpha_max calculation above does not guarantee
178  * the conditions here because of numerical inaccuracies.
179  */
180  for (Eigen::Index i = 0; i < p.size(); ++i) {
181  const Scalar& pi = p(i);
182  if (pi < Scalar{0}) {
183  if (x(i) + alpha_max * p(i) < lower_bounds(i)) {
184  alpha_max /= Scalar{2};
185  }
186  }
187  else if (pi > Scalar{0}) {
188  if (x(i) + alpha_max * p(i) > upper_bounds(i)) {
189  alpha_max /= Scalar{2};
190  }
191  }
192  }
193 
194  return alpha_max;
195 }
196 
197 template<typename Problem, typename Scalar, typename Storage>
198 template<typename OutputFunction>
200  Problem& problem,
201  Storage& storage,
202  const Vector<Scalar>& x,
203  const Vector<Scalar>& g,
204  const Scalar& check_epsilon,
205  const Scalar& machine_epsilon,
206  OutputFunction& output_function)
207 {
208  // calculate the generalized cauchy point
209  Vector<Scalar> x_cp = cauchy_point(x, g, storage,
210  problem.lower_bounds(), problem.upper_bounds(), machine_epsilon);
211  // calculate the subspace minimum in the direction of the cauchy point
212  Vector<Scalar> ssm = subspace_min(x, g, x_cp, storage,
213  problem.lower_bounds(), problem.upper_bounds(), check_epsilon);
214 
215  // The subspace minimum has been set to NaN if numerical problems
216  // occurred during subspace minimization, see \ref subspace_min for details.
217  if (ssm.unaryExpr(&scalar_traits<Scalar>::is_nan).any()) {
222  LSL_OUTPUT(output_function, OutputLevel::Warning,
223  "Detected numerical instability during subspace minimization!");
224  }
225 
226  // the search direction is the vector from the current x to the subspace minimum
227  Vector<Scalar> p = ssm - x;
228 
229  return p;
230 }
231 
232 }
233 }
decltype(upper_bound_probe(*dummy_problem)) upper_bound_type
return type of the upper_bounds function; void if not provided
Definition: ProblemTraits.hpp:91
Traits for scalar values.
Definition: ScalarTraits.hpp:16
static Scalar max_step_length(Problem &problem, const Vector< Scalar > &x, const Vector< Scalar > &p)
Calculate the maximum allowed step length.
Definition: BoxConstrainedAlgorithm.hpp:138
static Vector< Scalar > search_direction(Problem &problem, Storage &storage, const Vector< Scalar > &x, const Vector< Scalar > &g, const Scalar &check_epsilon, const Scalar &machine_epsilon, OutputFunction &output_function)
Calculate the new search direction.
Definition: BoxConstrainedAlgorithm.hpp:199
Generic implementation of box constrained quasi-Newton algorithms.
Definition: BoxConstrainedAlgorithm.hpp:23
Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > Vector
Vector type used.
Definition: Types.hpp:15
static Scalar gradient_norm(Problem &problem, const Vector< Scalar > &x, const Vector< Scalar > &g)
Calculate the projected gradient norm ( norm)
Definition: BoxConstrainedAlgorithm.hpp:128
Traits for the problem.
Definition: ProblemTraits.hpp:31
BFGS optimizations.
Definition: BFGS.hpp:24
static Vector< Scalar > truncate_x(Problem &problem, const Vector< Scalar > &x)
Truncate vector such that it fulfills the box constraints.
Definition: BoxConstrainedAlgorithm.hpp:112
static bool check_bounds(Problem &problem)
Verify that the bounds are correct.
Definition: BoxConstrainedAlgorithm.hpp:106
decltype(lower_bound_probe(*dummy_problem)) lower_bound_type
return type of the lower_bounds function; void if not provided
Definition: ProblemTraits.hpp:84