gtsam
Loading...
Searching...
No Matches
ConjugateGradientSolver.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
20
21#pragma once
22
24
25#include <cmath>
26#include <limits>
27#include <utility>
28#include <vector>
29
30namespace gtsam {
31
35struct GTSAM_EXPORT ConjugateGradientParameters
36 : public IterativeOptimizationParameters {
37 typedef IterativeOptimizationParameters Base;
38 typedef std::shared_ptr<ConjugateGradientParameters> shared_ptr;
39
42 size_t reset;
43 double epsilon_rel;
44 double epsilon_abs;
45
46 /* Matrix Operation Kernel */
48 GTSAM = 0,
49 } blas_kernel;
50
52 : minIterations(1),
53 maxIterations(500),
54 reset(501),
55 epsilon_rel(1e-3),
56 epsilon_abs(1e-3),
57 blas_kernel(GTSAM) {}
58
59 ConjugateGradientParameters(size_t minIterations, size_t maxIterations,
60 size_t reset, double epsilon_rel,
61 double epsilon_abs, BLASKernel blas)
62 : minIterations(minIterations),
63 maxIterations(maxIterations),
64 reset(reset),
65 epsilon_rel(epsilon_rel),
66 epsilon_abs(epsilon_abs),
67 blas_kernel(blas) {}
68
69 ConjugateGradientParameters(const ConjugateGradientParameters& p)
70 : Base(p),
71 minIterations(p.minIterations),
72 maxIterations(p.maxIterations),
73 reset(p.reset),
74 epsilon_rel(p.epsilon_rel),
75 epsilon_abs(p.epsilon_abs),
76 blas_kernel(GTSAM) {}
77
78 ConjugateGradientParameters& operator=(
79 const ConjugateGradientParameters& other) = default;
80
81#ifdef GTSAM_ALLOW_DEPRECATED_SINCE_V43
82 inline size_t getMinIterations() const { return minIterations; }
83 inline size_t getMaxIterations() const { return maxIterations; }
84 inline size_t getReset() const { return reset; }
85 inline double getEpsilon() const { return epsilon_rel; }
86 inline double getEpsilon_rel() const { return epsilon_rel; }
87 inline double getEpsilon_abs() const { return epsilon_abs; }
88
89 inline void setMinIterations(size_t value) { minIterations = value; }
90 inline void setMaxIterations(size_t value) { maxIterations = value; }
91 inline void setReset(size_t value) { reset = value; }
92 inline void setEpsilon(double value) { epsilon_rel = value; }
93 inline void setEpsilon_rel(double value) { epsilon_rel = value; }
94 inline void setEpsilon_abs(double value) { epsilon_abs = value; }
95#endif
96
97 void print() const { Base::print(); }
98 void print(std::ostream& os) const override;
99
100 static std::string blasTranslator(const BLASKernel k);
101 static BLASKernel blasTranslator(const std::string& s);
102};
103
110
129
131template <class V>
136
158template <class S, class V>
160 const S& system, const V& initial,
161 const ConjugateGradientParameters& parameters,
162 bool collectResidualHistory = true) {
163 V estimate, residual, direction, q1, q2;
164 estimate = residual = direction = q1 = q2 = initial;
165
166 // Initialize the split-preconditioned residual and search direction.
167 system.residual(estimate, q1); /* q1 = b-Ax */
168 system.leftPrecondition(q1, residual); /* r = L^{-1} (b-Ax) */
169 system.rightPrecondition(residual, direction); /* p = L^{-T} r */
170
171 double currentGamma = system.dot(residual, residual);
172 const double initialGamma = currentGamma;
173
174 const size_t iMaxIterations = parameters.maxIterations,
175 iMinIterations = parameters.minIterations,
176 iReset = parameters.reset;
177 const double threshold =
178 std::max(parameters.epsilon_abs,
179 parameters.epsilon_rel * parameters.epsilon_rel * initialGamma);
180
183 std::isfinite(initialGamma) && initialGamma >= 0.0
184 ? std::sqrt(initialGamma)
185 : std::numeric_limits<double>::quiet_NaN();
188 if (collectResidualHistory) {
189 stats.preconditionedResidualNormHistory.reserve(iMaxIterations + 1);
190 stats.preconditionedResidualNormHistory.push_back(
192 }
193
194 if (parameters.verbosity() >= ConjugateGradientParameters::COMPLEXITY)
195 std::cout << "[PCG] epsilon = " << parameters.epsilon_rel
196 << ", max = " << parameters.maxIterations
197 << ", reset = " << parameters.reset
198 << ", ||r0||^2 = " << currentGamma
199 << ", threshold = " << threshold << std::endl;
200
201 // Classify invalid and already-converged initial states before iterating.
202 if (!std::isfinite(currentGamma) || currentGamma < 0.0) {
203 stats.terminationReason =
205 } else if (currentGamma == 0.0 ||
206 (currentGamma <= threshold && iMinIterations == 0)) {
208 } else {
209 while (stats.iterations < iMaxIterations &&
210 (currentGamma > threshold || stats.iterations < iMinIterations)) {
211 const size_t iteration = stats.iterations + 1;
212
213 // Periodically replace the recursive residual with the exact residual.
214 if (iReset != 0 && iteration % iReset == 0) {
215 system.residual(estimate, q1); /* q1 = b-Ax */
216 system.leftPrecondition(q1, residual); /* r = L^{-1} (b-Ax) */
217 system.rightPrecondition(residual, direction); /* p = L^{-T} r */
218 currentGamma = system.dot(residual, residual);
219 if (!std::isfinite(currentGamma) || currentGamma < 0.0) {
221 std::numeric_limits<double>::quiet_NaN();
222 if (collectResidualHistory) {
225 }
226 stats.terminationReason =
228 break;
229 }
230 stats.finalPreconditionedResidualNorm = std::sqrt(currentGamma);
231 if (collectResidualHistory) {
234 }
235 if (currentGamma == 0.0 ||
236 (currentGamma <= threshold && stats.iterations >= iMinIterations)) {
237 stats.terminationReason =
239 break;
240 }
241 }
242
243 // Apply one PCG step, rejecting invalid or non-positive curvature.
244 system.multiply(direction, q1); /* q1 = A p */
245 const double directionCurvature = system.dot(direction, q1);
246 if (!std::isfinite(directionCurvature) || directionCurvature <= 0.0) {
247 stats.terminationReason =
249 break;
250 }
251
252 const double alpha = currentGamma / directionCurvature;
253 if (!std::isfinite(alpha)) {
254 stats.terminationReason =
256 break;
257 }
258
259 system.axpy(alpha, direction, estimate); /* estimate += alpha * p */
260 system.leftPrecondition(q1, q2); /* q2 = L^{-1} * q1 */
261 system.axpy(-alpha, q2, residual); /* r -= alpha * q2 */
262 const double previousGamma = currentGamma;
263 currentGamma = system.dot(residual, residual); /* gamma = |r|^2 */
264 ++stats.iterations;
265
266 if (!std::isfinite(currentGamma) || currentGamma < 0.0) {
268 std::numeric_limits<double>::quiet_NaN();
269 if (collectResidualHistory) {
270 stats.preconditionedResidualNormHistory.push_back(
272 }
273 stats.terminationReason =
275 break;
276 }
277
279 std::sqrt(std::max(0.0, currentGamma));
280 if (collectResidualHistory) {
281 stats.preconditionedResidualNormHistory.push_back(
283 }
284
285 if (parameters.verbosity() >= ConjugateGradientParameters::ERROR)
286 std::cout << "[PCG] k = " << iteration << ", alpha = " << alpha
287 << ", ||r||^2 = "
288 << currentGamma
289 // << "\nx =\n" << estimate
290 // << "\nr =\n" << residual
291 << std::endl;
292
293 if (currentGamma == 0.0 ||
294 (currentGamma <= threshold && stats.iterations >= iMinIterations)) {
295 stats.terminationReason =
297 break;
298 }
299
300 // Update the conjugate search direction for the next iteration.
301 const double beta = currentGamma / previousGamma;
302 if (!std::isfinite(beta)) {
303 stats.terminationReason =
305 break;
306 }
307 system.rightPrecondition(residual, q1); /* q1 = L^{-T} r */
308 system.scal(beta, direction);
309 system.axpy(1.0, q1, direction); /* p = q1 + beta * p */
310 }
311
312 if (stats.terminationReason !=
314 stats.terminationReason !=
316 stats.terminationReason =
317 currentGamma <= threshold && stats.iterations >= iMinIterations
320 }
321 }
322
323 if (std::isfinite(currentGamma) && currentGamma >= 0.0) {
324 stats.finalPreconditionedResidualNorm = std::sqrt(currentGamma);
325 }
326
327 if (parameters.verbosity() >= ConjugateGradientParameters::COMPLEXITY)
328 std::cout << "[PCG] iterations = " << stats.iterations
329 << ", ||r||^2 = " << currentGamma << std::endl;
330
331 return {std::move(estimate), std::move(stats)};
332}
333
347template <class S, class V>
349 const S& system, const V& initial,
350 const ConjugateGradientParameters& parameters) {
351 return preconditionedConjugateGradientDetailed(system, initial, parameters,
352 false)
353 .solution;
354}
355
356} // namespace gtsam
Some support classes for iterative solvers.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
void print(const Matrix &A, const string &s, ostream &stream)
print without optional string, must specify cout yourself
Definition Matrix.cpp:143
ConjugateGradientTerminationReason
Reason a conjugate-gradient solve stopped.
Definition ConjugateGradientSolver.h:105
@ kMaxIterations
The iteration limit was reached first.
Definition ConjugateGradientSolver.h:107
@ kNumericalBreakdown
The recurrence encountered invalid numerics.
Definition ConjugateGradientSolver.h:108
@ kConverged
The requested residual tolerance was reached.
Definition ConjugateGradientSolver.h:106
ConjugateGradientResult< V > preconditionedConjugateGradientDetailed(const S &system, const V &initial, const ConjugateGradientParameters &parameters, bool collectResidualHistory=true)
Solve a linear system with split-preconditioned conjugate gradients.
Definition ConjugateGradientSolver.h:159
V preconditionedConjugateGradient(const S &system, const V &initial, const ConjugateGradientParameters &parameters)
Solve a preconditioned linear system and return only the estimate.
Definition ConjugateGradientSolver.h:348
Parameters for the Conjugate Gradient method.
Definition ConjugateGradientSolver.h:36
size_t maxIterations
maximum number of cg iterations
Definition ConjugateGradientSolver.h:41
size_t reset
number of iterations before reset
Definition ConjugateGradientSolver.h:42
size_t minIterations
minimum number of cg iterations
Definition ConjugateGradientSolver.h:40
BLASKernel
Definition ConjugateGradientSolver.h:47
@ GTSAM
Jacobian Factor Graph of GTSAM.
Definition ConjugateGradientSolver.h:48
double epsilon_rel
threshold for relative error decrease
Definition ConjugateGradientSolver.h:43
double epsilon_abs
threshold for absolute error decrease
Definition ConjugateGradientSolver.h:44
Diagnostics collected during a conjugate-gradient solve.
Definition ConjugateGradientSolver.h:112
ConjugateGradientTerminationReason terminationReason
Reason the solve stopped.
Definition ConjugateGradientSolver.h:121
double initialPreconditionedResidualNorm
Norm of the initial split-preconditioned residual.
Definition ConjugateGradientSolver.h:114
double finalPreconditionedResidualNorm
Norm of the final split-preconditioned residual.
Definition ConjugateGradientSolver.h:116
bool converged() const
Return whether the requested residual tolerance was reached.
Definition ConjugateGradientSolver.h:125
std::vector< double > preconditionedResidualNormHistory
Initial norm followed by one entry per completed PCG update, when enabled.
Definition ConjugateGradientSolver.h:119
size_t iterations
Number of completed PCG updates.
Definition ConjugateGradientSolver.h:113
Solution and diagnostics returned by the detailed CG interface.
Definition ConjugateGradientSolver.h:132
V solution
Final estimate in the caller's vector type.
Definition ConjugateGradientSolver.h:133
ConjugateGradientStats stats
Convergence diagnostics for the solve.
Definition ConjugateGradientSolver.h:134