aligator  0.14.0
A primal-dual augmented Lagrangian-type solver for nonlinear trajectory optimization.
 
Loading...
Searching...
No Matches
linesearch-armijo.hpp
Go to the documentation of this file.
1
4#pragma once
5
7#include "aligator/math.hpp"
8#include "linesearch-base.hpp"
9
10#include <Eigen/QR>
11
12namespace aligator {
13
16template <typename T> struct PolynomialTpl {
20 PolynomialTpl(const Eigen::Ref<const VectorXs> &c)
21 : coeffs(c) {}
22
23 Eigen::Index degree() const { return coeffs.size() - 1; }
24 inline T evaluate(T a) const {
25 T r = 0.0;
26 for (int i = 0; i < coeffs.size(); i++) {
27 r = r * a + coeffs(i);
28 }
29 return r;
30 }
32 if (degree() == 0) {
33 return PolynomialTpl(VectorXs::Zero(1));
34 }
35 VectorXs out(degree());
36 for (int i = 0; i < coeffs.size() - 1; i++) {
37 out(i) = coeffs(i) * (T(degree()) - i);
38 }
39 return PolynomialTpl(out);
40 }
41};
42
44template <typename Scalar>
45class ArmijoLinesearch final : public Linesearch<Scalar> {
46public:
48 using Base::options_;
49 using FunctionSample = typename Base::FunctionSample;
52 using Matrix2s = Eigen::Matrix<Scalar, 2, 2>;
53 using Vector2s = Eigen::Matrix<Scalar, 2, 1>;
54
55 ArmijoLinesearch(const typename Base::Options &options)
56 : Base(options) {}
57
58 using fun_t = std::function<Scalar(Scalar)>;
59
60 Scalar run(fun_t phi, const Scalar phi0, const Scalar dphi0,
61 Scalar &alpha_try) {
62 const FunctionSample lower_bound(0., phi0, dphi0);
63
64 alpha_try = 1.;
65 FunctionSample latest;
66 FunctionSample previous;
67
68 // try the full step; if failure encountered, aggressively
69 // backtrack until no exception is raised
70 while (true) {
71 try {
72 latest = FunctionSample(alpha_try, phi(alpha_try));
73 break;
74 } catch (const std::runtime_error &e) {
75 alpha_try *= 0.5;
76 if (alpha_try <= options_.alpha_min) {
77 alpha_try = options_.alpha_min;
78 break;
79 }
80 }
81 }
82
83 if (std::abs(dphi0) < options_.dphi_thresh) {
84 return latest.phi;
85 }
86
87 for (std::size_t i = 0; i < options_.max_num_steps; i++) {
88
89 const Scalar dM = latest.phi - phi0;
90 if (dM <= options_.armijo_c1 * alpha_try * dphi0) {
91 break;
92 }
93
94 // compute next alpha try
95 LSInterpolation strat = options_.interp_type;
96 if (strat == LSInterpolation::BISECTION) {
97 alpha_try *= 0.5;
98 } else {
99 samples.reserve(3);
100 samples = {lower_bound};
101
102 // interpolation routines
103 switch (strat) {
105 // 2-point interp: value, derivative at 0 and latest value
106 samples.push_back(latest);
107 break;
108 }
110 // 3-point interp: phi(0), phi'(0) and last two values
111 samples.push_back(latest);
112 if (previous.valid) {
113 samples.push_back(previous);
114 }
115 break;
116 }
117 default:
119 "Unrecognized interpolation type in this context.\n");
120 break;
121 }
122
123 alpha_try = this->minimize_interpolant(
124 strat, options_.contraction_min * alpha_try,
125 options_.contraction_max * alpha_try);
126 }
127
128 if (std::isnan(alpha_try)) {
129 // handle NaN case
130 alpha_try = options_.contraction_min * previous.alpha;
131 } else {
132 alpha_try = std::max(alpha_try, options_.alpha_min);
133 }
134
135 try {
136 previous = latest;
137 latest = FunctionSample(alpha_try, phi(alpha_try));
138 } catch (const std::runtime_error &e) {
139 continue;
140 }
141
142 if (alpha_try <= options_.alpha_min) {
143 break;
144 }
145 }
146 alpha_try = std::max(alpha_try, options_.alpha_min);
147 return latest.phi;
148 }
149
151 Scalar minimize_interpolant(LSInterpolation strat, Scalar min_step_size,
152 Scalar max_step_size) {
153 Scalar anext = 0.0;
154 VectorXs &coeffs = interpolant.coeffs;
155
156 assert(samples.size() >= 2);
157 const FunctionSample &lower_bound = samples[0];
158 const Scalar &phi0 = lower_bound.phi;
159 const Scalar &dphi0 = lower_bound.dphi;
160
161 if (samples.size() == 2) {
163 }
164
165 switch (strat) {
167 assert(samples.size() >= 2);
168 const FunctionSample &cand0 = samples[1];
169 Scalar a = (cand0.phi - phi0 - cand0.alpha * dphi0) /
170 (cand0.alpha * cand0.alpha);
171 coeffs.conservativeResize(3);
172 coeffs << a, dphi0, phi0;
173 assert(interpolant.degree() == 2);
174 anext = -dphi0 / (2. * a);
175 break;
176 }
178 assert(samples.size() >= 3);
179 const FunctionSample &cand0 = samples[1];
180 const FunctionSample &cand1 = samples[2];
181 const Scalar &a0 = cand0.alpha;
182 const Scalar &a1 = cand1.alpha;
183 Matrix2s alph_mat;
184 Vector2s coeffs_cubic_interpolant;
186 alph_mat(0, 0) = a0 * a0 * a0;
187 alph_mat(0, 1) = a0 * a0;
188 alph_mat(1, 0) = a1 * a1 * a1;
189 alph_mat(1, 1) = a1 * a1;
190
191 Vector2s alph_rhs{cand1.phi - phi0 - dphi0 * a1,
192 cand0.phi - phi0 - dphi0 * a0};
193
194 Eigen::HouseholderQR<Matrix2s> decomp(alph_mat);
195 coeffs_cubic_interpolant = decomp.solve(alph_rhs);
196
197 const Scalar c3 = coeffs_cubic_interpolant(0);
198 const Scalar c2 = coeffs_cubic_interpolant(1);
199 coeffs.conservativeResize(4);
200 coeffs << c3, c2, dphi0, phi0;
201 assert(interpolant.degree() == 3);
202
203 // minimizer of cubic interpolant -> solve dinterp/da = 0
204 anext = (-c2 + std::sqrt(c2 * c2 - 3.0 * c3 * dphi0)) / (3.0 * c3);
205 break;
206 }
207 default:
208 break;
209 }
210
211 if ((anext > max_step_size) || (anext < min_step_size)) {
212 // if min outside of (amin; amax), look at the edges
213 Scalar pleft = interpolant.evaluate(min_step_size);
214 Scalar pright = interpolant.evaluate(max_step_size);
215 if (pleft < pright) {
216 anext = min_step_size;
217 } else {
218 anext = max_step_size;
219 }
220 }
221
222 return anext;
223 }
224
225protected:
227 std::vector<FunctionSample> samples; // interpolation samples
228};
229
230} // namespace aligator
Scalar minimize_interpolant(LSInterpolation strat, Scalar min_step_size, Scalar max_step_size)
Propose a new candidate step size through safeguarded interpolation.
Linesearch::Options options_
typename math_types< Scalar >::VectorXs VectorXs
std::vector< FunctionSample > samples
PolynomialTpl< Scalar > Polynomial
typename Base::FunctionSample FunctionSample
Scalar run(fun_t phi, const Scalar phi0, const Scalar dphi0, Scalar &alpha_try)
std::function< Scalar(Scalar)> fun_t
Eigen::Matrix< Scalar, 2, 1 > Vector2s
ArmijoLinesearch(const typename Base::Options &options)
Eigen::Matrix< Scalar, 2, 2 > Matrix2s
Linesearch(const Linesearch::Options &options)
#define ALIGATOR_RUNTIME_ERROR(...)
Definition exceptions.hpp:7
Base structs for linesearch algorithms.
Math utilities.
Main package namespace.
Polynomials represented by their coefficients in decreasing order of degree.
PolynomialTpl derivative() const
PolynomialTpl(const Eigen::Ref< const VectorXs > &c)
typename math_types< T >::VectorXs VectorXs
Eigen::Index degree() const
Polynomial degree (number of coefficients minus one).
Typedefs for math (Eigen vectors, matrices) depending on scalar type.
Definition math.hpp:100