gtsam
Loading...
Searching...
No Matches
NonlinearConjugateGradientOptimizer.h
Go to the documentation of this file.
1/* ----------------------------------------------------------------------------
2
3 * GTSAM Copyright 2010, Georgia Tech Research Corporation,
4 * Atlanta, Georgia 30332-0415
5 * All Rights Reserved
6 * Authors: Frank Dellaert, et al. (see THANKS for the full author list)
7
8 * See LICENSE for the license information
9
10 * -------------------------------------------------------------------------- */
11
18
19#pragma once
20
21#include <gtsam/base/Manifold.h>
23
24#include <stdexcept>
25
26namespace gtsam {
27
29template <typename Gradient>
30double FletcherReeves(const Gradient &currentGradient,
31 const Gradient &prevGradient) {
32 // Fletcher-Reeves: beta = g_n'*g_n/g_n-1'*g_n-1
33 const double beta =
34 currentGradient.dot(currentGradient) / prevGradient.dot(prevGradient);
35 return beta;
36}
37
39template <typename Gradient>
40double PolakRibiere(const Gradient &currentGradient,
41 const Gradient &prevGradient) {
42 // Polak-Ribiere: beta = g_n'*(g_n-g_n-1)/g_n-1'*g_n-1
43 const double beta =
44 std::max(0.0, currentGradient.dot(currentGradient - prevGradient) /
45 prevGradient.dot(prevGradient));
46 return beta;
47}
48
51template <typename Gradient>
52double HestenesStiefel(const Gradient &currentGradient,
53 const Gradient &prevGradient,
54 const Gradient &direction) {
55 // Hestenes-Stiefel: beta = g_n'*(g_n-g_n-1)/(-s_n-1')*(g_n-g_n-1)
56 Gradient d = currentGradient - prevGradient;
57 const double beta = std::max(0.0, currentGradient.dot(d) / -direction.dot(d));
58 return beta;
59}
60
62template <typename Gradient>
63double DaiYuan(const Gradient &currentGradient, const Gradient &prevGradient,
64 const Gradient &direction) {
65 // Dai-Yuan: beta = g_n'*g_n/(-s_n-1')*(g_n-g_n-1)
66 const double beta =
67 std::max(0.0, currentGradient.dot(currentGradient) /
68 -direction.dot(currentGradient - prevGradient));
69 return beta;
70}
71
72enum class DirectionMethod {
77};
78
81 : public NonlinearOptimizer {
82 /* a class for the nonlinearConjugateGradient template */
83 class System {
84 public:
85 typedef Values State;
86 typedef VectorValues Gradient;
87 typedef NonlinearOptimizerParams Parameters;
88
89 protected:
91
92 public:
93 System(const NonlinearFactorGraph &graph) : graph_(graph) {}
94 double error(const State &state) const;
95 Gradient gradient(const State &state) const;
96 State advance(const State &current, const double alpha,
97 const Gradient &g) const;
98 };
99
100 public:
101 typedef NonlinearOptimizer Base;
102 typedef NonlinearOptimizerParams Parameters;
103 typedef std::shared_ptr<NonlinearConjugateGradientOptimizer> shared_ptr;
104
105 protected:
106 Parameters params_;
107 DirectionMethod directionMethod_ = DirectionMethod::PolakRibiere;
108
109 const NonlinearOptimizerParams &_params() const override { return params_; }
110
111 public:
114 const NonlinearFactorGraph &graph, const Values &initialValues,
115 const Parameters &params = Parameters(),
116 const DirectionMethod &directionMethod = DirectionMethod::PolakRibiere);
117
120
125 GaussianFactorGraph::shared_ptr iterate() override;
126
131 const Values &optimize() override;
132
133};
134
136template <class S, class V, class W>
137double lineSearch(const S &system, const V currentValues, const W &gradient) {
138 /* normalize it such that it becomes a unit vector */
139 const double g = gradient.norm();
140
141 // perform the golden section search algorithm to decide the the optimal step
142 // size detail refer to http://en.wikipedia.org/wiki/Golden_section_search
143 const double phi = 0.5 * (1.0 + std::sqrt(5.0)), resphi = 2.0 - phi,
144 tau = 1e-5;
145 double minStep = -1.0 / g, maxStep = 0,
146 newStep = minStep + (maxStep - minStep) / (phi + 1.0);
147
148 V newValues = system.advance(currentValues, newStep, gradient);
149 double newError = system.error(newValues);
150
151 while (true) {
152 const bool flag = (maxStep - newStep > newStep - minStep);
153 const double testStep = flag ? newStep + resphi * (maxStep - newStep)
154 : newStep - resphi * (newStep - minStep);
155
156 if ((maxStep - minStep) < tau * (std::abs(testStep) + std::abs(newStep))) {
157 return 0.5 * (minStep + maxStep);
158 }
159
160 const V testValues = system.advance(currentValues, testStep, gradient);
161 const double testError = system.error(testValues);
162
163 // update the working range
164 if (testError >= newError) {
165 if (flag)
166 maxStep = testStep;
167 else
168 minStep = testStep;
169 } else {
170 if (flag) {
171 minStep = newStep;
172 newStep = testStep;
173 newError = testError;
174 } else {
175 maxStep = newStep;
176 newStep = testStep;
177 newError = testError;
178 }
179 }
180 }
181 return 0.0;
182}
183
196template <class S, class V>
197std::tuple<V, int> nonlinearConjugateGradient(
198 const S &system, const V &initial, const NonlinearOptimizerParams &params,
199 const bool singleIteration,
200 const DirectionMethod &directionMethod = DirectionMethod::PolakRibiere,
201 const bool gradientDescent = false) {
202 // GTSAM_CONCEPT_MANIFOLD_TYPE(V)
203
204 size_t iteration = 0;
205
206 // check if we're already close enough
207 double currentError = system.error(initial);
208 if (currentError <= params.errorTol) {
209 if (params.verbosity >= NonlinearOptimizerParams::ERROR) {
210 std::cout << "Exiting, as error = " << currentError << " < "
211 << params.errorTol << std::endl;
212 }
213 return {initial, iteration};
214 }
215
216 V currentValues = initial;
217 typename S::Gradient currentGradient = system.gradient(currentValues),
218 prevGradient, direction = currentGradient;
219
220 /* do one step of gradient descent */
221 V prevValues = currentValues;
222 double prevError = currentError;
223 double alpha = lineSearch(system, currentValues, direction);
224 currentValues = system.advance(prevValues, alpha, direction);
225 currentError = system.error(currentValues);
226
227 // Maybe show output
228 if (params.verbosity >= NonlinearOptimizerParams::ERROR)
229 std::cout << "Initial error: " << currentError << std::endl;
230
231 // Iterative loop
232 do {
233 if (gradientDescent == true) {
234 direction = system.gradient(currentValues);
235 } else {
236 prevGradient = currentGradient;
237 currentGradient = system.gradient(currentValues);
238
239 double beta;
240 switch (directionMethod) {
241 case DirectionMethod::FletcherReeves:
242 beta = FletcherReeves(currentGradient, prevGradient);
243 break;
244 case DirectionMethod::PolakRibiere:
245 beta = PolakRibiere(currentGradient, prevGradient);
246 break;
247 case DirectionMethod::HestenesStiefel:
248 beta = HestenesStiefel(currentGradient, prevGradient, direction);
249 break;
250 case DirectionMethod::DaiYuan:
251 beta = DaiYuan(currentGradient, prevGradient, direction);
252 break;
253 default:
254 throw std::runtime_error(
255 "NonlinearConjugateGradientOptimizer: Invalid directionMethod");
256 }
257
258 direction = currentGradient + (beta * direction);
259 }
260
261 alpha = lineSearch(system, currentValues, direction);
262
263 prevValues = currentValues;
264 prevError = currentError;
265
266 currentValues = system.advance(prevValues, alpha, direction);
267 currentError = system.error(currentValues);
268
269 // User hook:
270 if (params.iterationHook)
271 params.iterationHook(iteration, prevError, currentError);
272
273 // Maybe show output
274 if (params.verbosity >= NonlinearOptimizerParams::ERROR)
275 std::cout << "iteration: " << iteration
276 << ", currentError: " << currentError << std::endl;
277 } while (++iteration < params.maxIterations && !singleIteration &&
279 params.errorTol, prevError, currentError,
280 params.verbosity));
281
282 // Printing if verbose
283 if (params.verbosity >= NonlinearOptimizerParams::ERROR &&
284 iteration >= params.maxIterations)
285 std::cout << "nonlinearConjugateGradient: Terminating because reached "
286 "maximum iterations"
287 << std::endl;
288
289 return {currentValues, iteration};
290}
291
292} // namespace gtsam
Base class and basic functions for Manifold types.
Base class and parameters for nonlinear optimization algorithms.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
std::tuple< V, int > nonlinearConjugateGradient(const S &system, const V &initial, const NonlinearOptimizerParams &params, const bool singleIteration, const DirectionMethod &directionMethod=DirectionMethod::PolakRibiere, const bool gradientDescent=false)
Implement the nonlinear conjugate gradient method using the Polak-Ribiere formula suggested in http:/...
Definition NonlinearConjugateGradientOptimizer.h:197
double lineSearch(const S &system, const V currentValues, const W &gradient)
Implement the golden-section line search algorithm.
Definition NonlinearConjugateGradientOptimizer.h:137
double HestenesStiefel(const Gradient &currentGradient, const Gradient &prevGradient, const Gradient &direction)
The Hestenes-Stiefel formula for computing β, the direction of steepest descent.
Definition NonlinearConjugateGradientOptimizer.h:52
double FletcherReeves(const Gradient &currentGradient, const Gradient &prevGradient)
Fletcher-Reeves formula for computing β, the direction of steepest descent.
Definition NonlinearConjugateGradientOptimizer.h:30
Point3 optimize(const NonlinearFactorGraph &graph, const Values &values, Key landmarkKey)
Optimize for triangulation.
Definition triangulation.cpp:178
double DaiYuan(const Gradient &currentGradient, const Gradient &prevGradient, const Gradient &direction)
The Dai-Yuan formula for computing β, the direction of steepest descent.
Definition NonlinearConjugateGradientOptimizer.h:63
double PolakRibiere(const Gradient &currentGradient, const Gradient &prevGradient)
Polak-Ribiere formula for computing β, the direction of steepest descent.
Definition NonlinearConjugateGradientOptimizer.h:40
bool checkConvergence(double relativeErrorThreshold, double absoluteErrorThreshold, double errorThreshold, double currentError, double newError, NonlinearOptimizerParams::Verbosity verbosity)
Check whether the relative error decrease is less than relativeErrorThreshold, the absolute error dec...
Definition NonlinearOptimizer.cpp:236
std::shared_ptr< This > shared_ptr
shared_ptr to this class
Definition GaussianFactorGraph.h:83
VectorValues represents a collection of vector-valued variables associated each with a unique integer...
Definition VectorValues.h:73
NonlinearConjugateGradientOptimizer(const NonlinearFactorGraph &graph, const Values &initialValues, const Parameters &params=Parameters(), const DirectionMethod &directionMethod=DirectionMethod::PolakRibiere)
Constructor.
Definition NonlinearConjugateGradientOptimizer.cpp:44
~NonlinearConjugateGradientOptimizer() override
Destructor.
Definition NonlinearConjugateGradientOptimizer.h:119
Definition NonlinearFactorGraph.h:57
const NonlinearFactorGraph & graph() const
return the graph with nonlinear factors
Definition NonlinearOptimizer.h:141
std::shared_ptr< const NonlinearFactorGraph > graph_
The graph with nonlinear factors.
Definition NonlinearOptimizer.h:86
double error() const
return error in current optimizer state
Definition NonlinearOptimizer.cpp:87
NonlinearOptimizer(const NonlinearFactorGraph &graph, std::unique_ptr< internal::NonlinearOptimizerState > state)
Constructor for initial construction of base classes.
Definition NonlinearOptimizer.cpp:79
The common parameters for Nonlinear optimizers.
Definition NonlinearOptimizerParams.h:37
double absoluteErrorTol
The maximum absolute error decrease to stop iterating (default 1e-5).
Definition NonlinearOptimizerParams.h:46
IterationHook iterationHook
Optional user-provided iteration hook to be called after each optimization iteration (Default: none).
Definition NonlinearOptimizerParams.h:97
size_t maxIterations
The maximum iterations to stop iterating (default 100).
Definition NonlinearOptimizerParams.h:44
Verbosity verbosity
The printing verbosity during optimization (default SILENT).
Definition NonlinearOptimizerParams.h:48
double relativeErrorTol
The maximum relative error decrease to stop iterating (default 1e-5).
Definition NonlinearOptimizerParams.h:45
double errorTol
The maximum total error to stop iterating (default 0.0).
Definition NonlinearOptimizerParams.h:47
A non-templated config holding any types of Manifold-group elements.
Definition Values.h:65