gtsam
Loading...
Searching...
No Matches
GncOptimizer.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
26
27#pragma once
28
29#include <algorithm>
30#include <chrono>
31
32#include <gtsam/linear/LossFunctions.h>
33#include <gtsam/nonlinear/GncParams.h>
35
36namespace gtsam {
37/*
38 * Quantile of chi-squared distribution with given degrees of freedom at probability alpha.
39 * Equivalent to chi2inv in Matlab.
40 */
41GTSAM_EXPORT double Chi2inv(const double alpha, const size_t dofs);
42
54
55GTSAM_EXPORT bool isNullType(GncFactorType type);
56
57GTSAM_EXPORT bool isNonNoiseModelType(GncFactorType type);
58
59GTSAM_EXPORT bool needsWeightUpdate(GncFactorType type);
60
61GTSAM_EXPORT bool hasNoise(GncFactorType type);
62
65 double weightsUpdateElapsed = 0.0;
66 double makeGraphElapsed = 0.0;
67 double baseOptimizeElapsed = 0.0;
68 double costEvaluationElapsed = 0.0;
69 double totalElapsed = 0.0;
70};
71
73struct GncTiming {
75 double totalElapsed = 0.0;
76 std::vector<GncIterationTiming> iterations;
77
78 double sumWeightsUpdate() const {
79 double sum = 0.0;
80 for (const auto& it : iterations) sum += it.weightsUpdateElapsed;
81 return sum;
82 }
83 double sumMakeGraph() const {
84 double sum = 0.0;
85 for (const auto& it : iterations) sum += it.makeGraphElapsed;
86 return sum;
87 }
88 double sumBaseOptimize() const {
89 double sum = 0.0;
90 for (const auto& it : iterations) sum += it.baseOptimizeElapsed;
91 return sum;
92 }
93 double sumCostEvaluation() const {
94 double sum = 0.0;
95 for (const auto& it : iterations) sum += it.costEvaluationElapsed;
96 return sum;
97 }
98};
99
100/* ************************************************************************* */
101template<class GncParameters>
103 public:
105 typedef typename GncParameters::OptimizerType BaseOptimizer;
106
107 private:
110
112 Values state_;
113
115 GncParameters params_;
116
118 Vector weights_;
119
122 Vector barcSq_;
123
125 std::vector<GncFactorType> factorTypes_;
126
128 GncTiming timing_;
129
130 public:
132 GncOptimizer(const NonlinearFactorGraph& graph, const Values& initialValues,
133 const GncParameters& params = GncParameters())
134 : state_(initialValues),
135 params_(params) {
136
137 // make sure all noiseModels are Gaussian or convert to Gaussian
138 nfg_.resize(graph.size());
139 factorTypes_.assign(graph.size(), GncFactorType::NullPointer);
140 for (size_t i = 0; i < graph.size(); i++) {
141 if (!graph[i]) {
142 factorTypes_[i] = GncFactorType::NullPointer;
143 continue;
144 }
146 if (!factor) {
147 if (!params.allowNonNoiseModelFactors) {
148 throw std::runtime_error("GncOptimizer::constructor: the user must set allowNonNoiseModelFactors as"
149 " true if the factor graph contains factors without noise model.");
150 }
151 nfg_[i] = graph[i];
152 factorTypes_[i] = GncFactorType::NonNoiseModel;
153 continue;
154 }
155 auto robust =
156 std::dynamic_pointer_cast<noiseModel::Robust>(factor->noiseModel());
157 // if the factor has a robust loss, we remove the robust loss
158 nfg_[i] = robust ? factor-> cloneWithNewNoiseModel(robust->noise()) : factor;
159 factorTypes_[i] = GncFactorType::Normal;
160 }
161
162 // check that known inliers are in the graph
163 for (size_t i = 0; i < params.knownInliers.size(); i++){
164 if( params.knownInliers[i] > nfg_.size()-1 || isNullType(factorTypes_[params.knownInliers[i]])) { // outside graph
165 throw std::runtime_error("GncOptimizer::constructor: the user has selected one or more measurements"
166 "that are not in the factor graph to be known inliers.");
167 }
168 if (!isNonNoiseModelType(factorTypes_[params.knownInliers[i]])) {
169 factorTypes_[params.knownInliers[i]] = GncFactorType::Inlier;
170 }
171 }
172 // check that known outliers are in the graph
173 for (size_t i = 0; i < params.knownOutliers.size(); i++){
174 if( params.knownOutliers[i] > nfg_.size()-1 || isNullType(factorTypes_[params.knownOutliers[i]])) { // outside graph
175 throw std::runtime_error("GncOptimizer::constructor: the user has selected one or more measurements"
176 "that are not in the factor graph to be known outliers.");
177 }
178 if (!needsWeightUpdate(factorTypes_[params.knownOutliers[i]])) {
179 // it can only be Normal, Inlier, or NonNoiseModel here so this works
180 throw std::runtime_error("GncOptimizer::constructor: the user has selected one or more measurements"
181 " to be an outlier that is either an inlier or a non noise model factor.");
182 }
183 factorTypes_[params.knownOutliers[i]] = GncFactorType::Outlier;
184 }
185
186 // initialize weights (if we don't have prior knowledge of inliers/outliers
187 // the weights are all initialized to 1.
188 weights_ = initializeWeightsFromKnownInliersAndOutliers();
189
190 // set default barcSq_ (inlier threshold)
191 double alpha = 0.99; // with this (default) probability, inlier residuals are smaller than barcSq_
193 }
194
201 void setInlierCostThresholds(const double inth) {
202 barcSq_ = inth * Vector::Ones(nfg_.size());
203 }
204
209 void setInlierCostThresholds(const Vector& inthVec) {
210 barcSq_ = inthVec;
211 }
212
216 void setInlierCostThresholdsAtProbability(const double alpha) {
217 barcSq_ = Vector::Ones(nfg_.size()); // initialize
218 for (size_t k = 0; k < nfg_.size(); k++) {
219 if (hasNoise(factorTypes_[k])) {
220 barcSq_[k] = 0.5 * Chi2inv(alpha, nfg_[k]->dim()); // 0.5 derives from the error definition in gtsam
221 }
222 }
223 }
224
228 void setWeights(const Vector w) {
229 if (size_t(w.size()) != nfg_.size()) {
230 throw std::runtime_error(
231 "GncOptimizer::setWeights: the number of specified weights"
232 " does not match the size of the factor graph.");
233 }
234 weights_ = w;
235 }
236
238 const NonlinearFactorGraph& getFactors() const { return nfg_; }
239
241 const Values& getState() const { return state_; }
242
244 const GncParameters& getParams() const { return params_;}
245
247 const Vector& getWeights() const { return weights_;}
248
250 const Vector& getInlierCostThresholds() const {return barcSq_;}
251
253 const GncTiming& getTiming() const { return timing_; }
254
256 bool equals(const GncOptimizer& other, double tol = 1e-9) const {
257 return nfg_.equals(other.getFactors())
258 && equal(weights_, other.getWeights())
259 && params_.equals(other.getParams())
260 && equal(barcSq_, other.getInlierCostThresholds());
261 }
262
263 Vector initializeWeightsFromKnownInliersAndOutliers() const{
264 Vector weights = Vector::Ones(nfg_.size());
265 // we do not loop through the factorTypes_ vector because in general params_.knownOutliers will always be smaller
266 for (size_t i = 0; i < params_.knownOutliers.size(); i++){
267 weights[ params_.knownOutliers[i] ] = 0.0; // known to be outliers
268 }
269 return weights;
270 }
271
274 using Clock = std::chrono::steady_clock;
275 const auto elapsedSince = [](Clock::time_point start) {
276 return std::chrono::duration<double>(Clock::now() - start).count();
277 };
278 timing_ = GncTiming();
279 const auto totalStart = Clock::now();
280
281 validateLossSchedulerCombination();
282 NonlinearFactorGraph graph_initial = this->makeWeightedGraph(weights_);
283 BaseOptimizer baseOptimizer(
284 graph_initial, state_, params_.baseOptimizerParams);
285 Values result = baseOptimizer.optimize();
286 timing_.initialOptimizeElapsed = elapsedSince(totalStart);
287 double lambda = initializeLambda();
288 double prev_cost = graph_initial.error(result);
289 double cost = 0.0; // this will be updated in the main loop
290
291 // handle the degenerate case that corresponds to small
292 // maximum residual errors at initialization
293 // For GM: if residual error is small, lambda -> 0
294 // For TLS: if residual error is small, lambda -> -1
295 int nrUnknownInOrOut = 0;
296 for (GncFactorType t : factorTypes_) {
297 if (needsWeightUpdate(t)) {
298 nrUnknownInOrOut++;
299 }
300 }
301 // ^^ number of measurements that are not known to be inliers or outliers (GNC will need to figure them out)
302 if (lambda <= 0 || nrUnknownInOrOut == 0) { // no need to even call GNC in this case
303 if (lambda <= 0 && params_.verbosity >= GncParameters::Verbosity::SUMMARY) {
304 std::cout << "GNC Optimizer stopped because maximum residual at "
305 "initialization is small."
306 << std::endl;
307 }
308 if (nrUnknownInOrOut==0 && params_.verbosity >= GncParameters::Verbosity::SUMMARY) {
309 std::cout << "GNC Optimizer stopped because all measurements are already known to be inliers or outliers"
310 << std::endl;
311 }
312 if (params_.verbosity >= GncParameters::Verbosity::LAMBDA) {
313 std::cout << "lambda: " << lambda << std::endl;
314 }
315 if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
316 result.print("result\n");
317 }
318 timing_.totalElapsed = elapsedSince(totalStart);
319 return result;
320 }
321
322 size_t iter;
323 for (iter = 0; iter < params_.maxIterations; iter++) {
324 const auto iterationStart = Clock::now();
325 GncIterationTiming iterationTiming;
326
327 // display info
328 if (params_.verbosity >= GncParameters::Verbosity::LAMBDA) {
329 std::cout << "iter: " << iter << std::endl;
330 std::cout << "lambda: " << lambda << std::endl;
331 }
332 if (params_.verbosity >= GncParameters::Verbosity::WEIGHTS) {
333 std::cout << "weights: " << weights_ << std::endl;
334 }
335 if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
336 result.print("result\n");
337 }
338 // weights update
339 auto stageStart = Clock::now();
340 weights_ = calculateWeights(result, lambda);
341 iterationTiming.weightsUpdateElapsed = elapsedSince(stageStart);
342
343 // variable/values update
344 stageStart = Clock::now();
345 NonlinearFactorGraph graph_iter = this->makeWeightedGraph(weights_);
346 iterationTiming.makeGraphElapsed = elapsedSince(stageStart);
347 stageStart = Clock::now();
348 BaseOptimizer baseOptimizer_iter(
349 graph_iter, state_, params_.baseOptimizerParams);
350 result = baseOptimizer_iter.optimize();
351 iterationTiming.baseOptimizeElapsed = elapsedSince(stageStart);
352
353 // stopping condition
354 stageStart = Clock::now();
355 cost = graph_iter.error(result);
356 iterationTiming.costEvaluationElapsed = elapsedSince(stageStart);
357 iterationTiming.totalElapsed = elapsedSince(iterationStart);
358 timing_.iterations.push_back(iterationTiming);
359 if (checkConvergence(lambda, weights_, cost, prev_cost)) {
360 break;
361 }
362
363 // update lambda
364 lambda = updateLambda(lambda);
365
366 // get ready for next iteration
367 prev_cost = cost;
368
369 // display info
370 if (params_.verbosity >= GncParameters::Verbosity::VALUES) {
371 std::cout << "previous cost: " << prev_cost << std::endl;
372 std::cout << "current cost: " << cost << std::endl;
373 }
374 }
375 // display info
376 if (params_.verbosity >= GncParameters::Verbosity::SUMMARY) {
377 std::cout << "final iterations: " << iter << std::endl;
378 std::cout << "final lambda: " << lambda << std::endl;
379 std::cout << "previous cost: " << prev_cost << std::endl;
380 std::cout << "current cost: " << cost << std::endl;
381 }
382 if (params_.verbosity >= GncParameters::Verbosity::WEIGHTS) {
383 std::cout << "final weights: " << weights_ << std::endl;
384 }
385 timing_.totalElapsed = elapsedSince(totalStart);
386 return result;
387 }
388
389 void validateLossSchedulerCombination() const {
390 if (params_.lossType == GncLossType::GM &&
391 params_.scheduler != GncScheduler::Linear) {
392 throw std::runtime_error(
393 "GncOptimizer::optimize: scheduler must be Linear for GM.");
394 }
395 if (params_.lossType == GncLossType::TLS) {
396 // Linear and SuperLinear are both valid for TLS.
397 return;
398 }
399 }
400
402 double initializeLambda() const {
403
404 double lambdaInit = 0.0;
405 // initialize lambda to the value specified in Remark 5 in GNC paper.
406 switch (params_.lossType) {
407 case GncLossType::GM:
408 /* surrogate cost is convex for large lambda. initialize as in remark 5 in GNC paper.
409 Since barcSq_ can be different for each factor, we compute the max of the quantity in remark 5 in GNC paper
410 */
411 for (size_t k = 0; k < nfg_.size(); k++) {
412 if (hasNoise(factorTypes_[k])) {
413 lambdaInit = std::max(lambdaInit, 2 * nfg_[k]->error(state_) / barcSq_[k]);
414 }
415 }
416 return lambdaInit; // initial lambda
417 case GncLossType::TLS:
418 /* surrogate cost is convex for lambda close to zero. initialize as in remark 5 in GNC paper.
419 degenerate case: 2 * rmax_sq - params_.barcSq < 0 (handled in the main loop)
420 according to remark lambda = params_.barcSq / (2 * rmax_sq - params_.barcSq) = params_.barcSq/ excessResidual
421 however, if the denominator is 0 or negative, we return lambda = -1 which leads to termination of the main GNC loop.
422 Since barcSq_ can be different for each factor, we look for the minimimum (positive) quantity in remark 5 in GNC paper
423 */
424 lambdaInit = std::numeric_limits<double>::infinity();
425 for (size_t k = 0; k < nfg_.size(); k++) {
426 if (hasNoise(factorTypes_[k])) {
427 double rk = nfg_[k]->error(state_);
428 lambdaInit = (2 * rk - barcSq_[k]) > 0 ? // if positive, update lambda, otherwise keep same
429 std::min(lambdaInit, barcSq_[k] / (2 * rk - barcSq_[k]) ) : lambdaInit;
430 }
431 }
432 if (lambdaInit >= 0 && lambdaInit < 1e-6){
433 lambdaInit = 1e-6; // if lambda ~ 0 (but positive), that means we have measurements with large errors,
434 // i.e., rk > barcSq_[k] and rk very large, hence we threshold to 1e-6 to avoid lambda = 0
435 }
436
437 return lambdaInit > 0 && !std::isinf(lambdaInit) ? lambdaInit : -1; // if lambda <= 0 or lambda = inf, return -1,
438 // which leads to termination of the main gnc loop. In this case, all residuals are already below the threshold
439 // and there is no need to robustify (TLS = least squares)
440 default:
441 throw std::runtime_error(
442 "GncOptimizer::initializeLambda: called with unknown loss type.");
443 }
444 }
445
447 double updateLambda(const double lambda) const {
448 switch (params_.lossType) {
449 case GncLossType::GM:
450 // reduce lambda, but saturate at 1 (original cost is recovered for lambda -> 1)
451 return std::max(1.0, lambda / params_.lambdaStep);
452 case GncLossType::TLS:
453 // increases lambda at each iteration (original cost is recovered for lambda -> inf)
454 switch (params_.scheduler) {
455 case GncScheduler::SuperLinear: {
456 if (lambda < 1) return std::min(std::sqrt(lambda) * params_.lambdaStep, params_.lambdaMax);
457 return std::min(lambda * params_.lambdaStep, params_.lambdaMax);
458 }
459 case GncScheduler::Linear: {
460 return lambda * params_.lambdaStep;
461 }
462 default:
463 throw std::runtime_error("GncOptimizer::updateLambda: unknown scheduler type.");
464 }
465 default:
466 throw std::runtime_error(
467 "GncOptimizer::updateLambda: called with unknown loss type.");
468 }
469 }
470
472 bool checkLambdaConvergence(const double lambda) const {
473 bool lambdaConverged = false;
474 switch (params_.lossType) {
475 case GncLossType::GM:
476 lambdaConverged = std::fabs(lambda - 1.0) < 1e-9; // lambda=1 recovers the original GM function
477 break;
478 case GncLossType::TLS:
479 lambdaConverged = false; // for TLS there is no stopping condition on lambda (it must tend to infinity)
480 break;
481 default:
482 throw std::runtime_error(
483 "GncOptimizer::checkLambdaConvergence: called with unknown loss type.");
484 }
485 if (lambdaConverged && params_.verbosity >= GncParameters::Verbosity::SUMMARY)
486 std::cout << "lambdaConverged = true " << std::endl;
487 return lambdaConverged;
488 }
489
491 bool checkCostConvergence(const double cost, const double prev_cost) const {
492 bool costConverged = std::fabs(cost - prev_cost) / std::max(prev_cost, 1e-7)
493 < params_.relativeCostTol;
494 if (costConverged && params_.verbosity >= GncParameters::Verbosity::SUMMARY){
495 std::cout << "checkCostConvergence = true (prev. cost = " << prev_cost
496 << ", curr. cost = " << cost << ")" << std::endl;
497 }
498 return costConverged;
499 }
500
502 bool checkWeightsConvergence(const Vector& weights) const {
503 bool weightsConverged = false;
504 switch (params_.lossType) {
505 case GncLossType::GM:
506 weightsConverged = false; // for GM, there is no clear binary convergence for the weights
507 break;
508 case GncLossType::TLS:
509 weightsConverged = true;
510 for (int i = 0; i < weights.size(); i++) {
511 if (std::fabs(weights[i] - std::round(weights[i]))
512 > params_.weightsTol) {
513 weightsConverged = false;
514 break;
515 }
516 }
517 break;
518 default:
519 throw std::runtime_error(
520 "GncOptimizer::checkWeightsConvergence: called with unknown loss type.");
521 }
522 if (weightsConverged
523 && params_.verbosity >= GncParameters::Verbosity::SUMMARY)
524 std::cout << "weightsConverged = true " << std::endl;
525 return weightsConverged;
526 }
527
529 bool checkConvergence(const double lambda, const Vector& weights,
530 const double cost, const double prev_cost) const {
531 return checkCostConvergence(cost, prev_cost)
532 || checkWeightsConvergence(weights) || checkLambdaConvergence(lambda);
533 }
534
536 NonlinearFactorGraph makeWeightedGraph(const Vector& weights) const {
537 // make sure all noiseModels are Gaussian or convert to Gaussian
538 NonlinearFactorGraph newGraph;
539 newGraph.resize(nfg_.size());
540 for (size_t i = 0; i < nfg_.size(); i++) {
541 if (!isNullType(factorTypes_[i])) {
542 if (!hasNoise(factorTypes_[i])) {
543 // Keep non NoiseModel factors same.
544 newGraph[i] = nfg_[i];
545 continue;
546 }
547 auto factor = std::static_pointer_cast<NoiseModelFactor>(nfg_[i]);
548 auto noiseModel = std::dynamic_pointer_cast<noiseModel::Gaussian>(
549 factor->noiseModel());
550 if (noiseModel) {
551 Matrix newInfo = weights[i] * noiseModel->information();
552 auto newNoiseModel = noiseModel::Gaussian::Information(newInfo);
553 newGraph[i] = factor->cloneWithNewNoiseModel(newNoiseModel);
554 } else {
555 throw std::runtime_error(
556 "GncOptimizer::makeWeightedGraph: unexpected non-Gaussian noise model.");
557 }
558 }
559 }
560 return newGraph;
561 }
562
575 static double NormalizedMu(GncLossType lossType, const double lambda) {
576 switch (lossType) {
577 case GncLossType::GM:
578 // lambda saturates at 1, which is the final GM loss (mu = 1).
579 return std::min(1.0, 1.0 / lambda);
580 case GncLossType::TLS:
581 return lambda / (1.0 + lambda);
582 default:
583 throw std::runtime_error(
584 "GncOptimizer::NormalizedMu: called with unknown loss type.");
585 }
586 }
587
589 Vector calculateWeights(const Values& currentEstimate, const double lambda) {
590 Vector weights = initializeWeightsFromKnownInliersAndOutliers();
591 const double mu = NormalizedMu(params_.lossType, lambda);
592
593 // update weights of unknown measurements
594 switch (params_.lossType) {
595 case GncLossType::GM: { // use eq (12) in GNC paper
596 for (size_t k = 0; k < nfg_.size(); k++) {
597 if (needsWeightUpdate(factorTypes_[k])) {
598 // squared (and whitened) residual
599 double u2_k = nfg_[k]->error(currentEstimate);
601 u2_k, barcSq_[k], mu,
602 noiseModel::mEstimator::GemanMcClure::GradScheme::STANDARD);
603 }
604 }
605 return weights;
606 }
607 case GncLossType::TLS: {
608 for (size_t k = 0; k < nfg_.size(); k++) {
609 if (needsWeightUpdate(factorTypes_[k])) {
610 double u2_k = nfg_[k]->error(
611 currentEstimate); // squared (and whitened) residual
612 switch (params_.scheduler) {
613 case GncScheduler::SuperLinear: {
616 u2_k, barcSq_[k], mu,
618 GradScheme::GNC_SUPERLINEAR);
619 break;
620 }
621 case GncScheduler::Linear: { // use eq (14) in GNC paper
624 u2_k, barcSq_[k], mu,
626 GradScheme::GNC_LINEAR);
627 break;
628 }
629 default:
630 throw std::runtime_error(
631 "GncOptimizer::calculateWeights: unknown scheduler type.");
632 }
633 }
634 }
635 return weights;
636 }
637 default:
638 throw std::runtime_error(
639 "GncOptimizer::calculateWeights: called with unknown loss type.");
640 }
641 }
642};
643}
Factor Graph consisting of non-linear factors.
Global functions in a separate testing namespace.
Definition chartTesting.h:28
GncLossType
Choice of robust loss function for GNC.
Definition GncParams.h:43
bool equal(const T &obj1, const T &obj2, double tol)
Call equal on the object.
Definition Testable.h:85
GncFactorType
Enum to classify factor types in GNC optimization.
Definition GncOptimizer.h:47
@ NullPointer
Factor pointer is null.
Definition GncOptimizer.h:52
@ NonNoiseModel
Factor does not have a noise model.
Definition GncOptimizer.h:51
@ Outlier
Factor is a known outlier.
Definition GncOptimizer.h:50
@ Normal
Normal case.
Definition GncOptimizer.h:48
@ Inlier
Factor is a known inlier.
Definition GncOptimizer.h:49
All noise models live in the noiseModel namespace.
Definition LossFunctions.cpp:33
virtual void resize(size_t size)
Directly resize the number of factors in the graph.
Definition FactorGraph.h:367
size_t size() const
return the number of factors (including any null factors set by remove() ).
Definition FactorGraph.h:297
const sharedFactor at(size_t i) const
Get a specific factor by index (this checks array bounds and may throw an exception,...
Definition FactorGraph.h:306
static double GraduatedWeight(double distance2, double c2, double mu, GradScheme graduation)
Static implementation of GemanMcClure Graduated Weight.
Definition LossFunctions.cpp:419
Truncated Least Squares (TLS) robust error model.
Definition LossFunctions.h:573
static double GraduatedWeight(double distance2, double c2, double mu, GradScheme graduation)
Static implementation of TLS Graduated Weight.
Definition LossFunctions.cpp:543
static shared_ptr Information(const Matrix &M, bool smart=true)
A Gaussian noise model created by specifying an information matrix.
Definition NoiseModel.cpp:99
Timing of one GNC outer iteration (all in seconds).
Definition GncOptimizer.h:64
double costEvaluationElapsed
weighted graph error for convergence check
Definition GncOptimizer.h:68
double baseOptimizeElapsed
inner optimizer construction + optimize()
Definition GncOptimizer.h:67
double makeGraphElapsed
makeWeightedGraph (factor cloning)
Definition GncOptimizer.h:66
double weightsUpdateElapsed
calculateWeights (per-factor errors)
Definition GncOptimizer.h:65
Timing of a full GncOptimizer::optimize() call.
Definition GncOptimizer.h:73
double initialOptimizeElapsed
optimize before the GNC loop
Definition GncOptimizer.h:74
const GncTiming & getTiming() const
Get the timing of the last optimize() call.
Definition GncOptimizer.h:253
bool checkWeightsConvergence(const Vector &weights) const
Check convergence of weights to binary values.
Definition GncOptimizer.h:502
bool checkLambdaConvergence(const double lambda) const
Check if we have reached the value of lambda for which the surrogate loss matches the original loss.
Definition GncOptimizer.h:472
void setWeights(const Vector w)
Set weights for each factor.
Definition GncOptimizer.h:228
GncParameters::OptimizerType BaseOptimizer
For each parameter, specify the corresponding optimizer: e.g., GaussNewtonParams -> GaussNewtonOptimi...
Definition GncOptimizer.h:105
double updateLambda(const double lambda) const
Update the gnc parameter lambda to gradually increase nonconvexity.
Definition GncOptimizer.h:447
Values optimize()
Compute optimal solution using graduated non-convexity.
Definition GncOptimizer.h:273
const Vector & getInlierCostThresholds() const
Get the inlier threshold.
Definition GncOptimizer.h:250
double initializeLambda() const
Initialize the gnc parameter lambda such that loss is approximately convex (remark 5 in GNC paper).
Definition GncOptimizer.h:402
Vector calculateWeights(const Values &currentEstimate, const double lambda)
Calculate gnc weights.
Definition GncOptimizer.h:589
bool checkCostConvergence(const double cost, const double prev_cost) const
Check convergence of relative cost differences.
Definition GncOptimizer.h:491
const Values & getState() const
Access a copy of the internal values.
Definition GncOptimizer.h:241
void setInlierCostThresholds(const Vector &inthVec)
Set the maximum weighted residual error for an inlier (one for each factor).
Definition GncOptimizer.h:209
bool equals(const GncOptimizer &other, double tol=1e-9) const
Equals.
Definition GncOptimizer.h:256
void setInlierCostThresholds(const double inth)
Set the maximum weighted residual error for an inlier (same for all factors).
Definition GncOptimizer.h:201
void setInlierCostThresholdsAtProbability(const double alpha)
Set the maximum weighted residual error threshold by specifying the probability alpha that the inlier...
Definition GncOptimizer.h:216
NonlinearFactorGraph makeWeightedGraph(const Vector &weights) const
Create a graph where each factor is weighted by the gnc weights.
Definition GncOptimizer.h:536
static double NormalizedMu(GncLossType lossType, const double lambda)
Map this optimizer's historical graduation parameter to the normalized \mu in [0,1] expected by the r...
Definition GncOptimizer.h:575
const NonlinearFactorGraph & getFactors() const
Access a copy of the internal factor graph.
Definition GncOptimizer.h:238
const Vector & getWeights() const
Access a copy of the GNC weights.
Definition GncOptimizer.h:247
const GncParameters & getParams() const
Access a copy of the parameters.
Definition GncOptimizer.h:244
bool checkConvergence(const double lambda, const Vector &weights, const double cost, const double prev_cost) const
Check for convergence between consecutive GNC iterations.
Definition GncOptimizer.h:529
GncOptimizer(const NonlinearFactorGraph &graph, const Values &initialValues, const GncParameters &params=GncParameters())
Constructor.
Definition GncOptimizer.h:132
A nonlinear sum-of-squares factor with a zero-mean noise model implementing the density Templated on...
Definition NonlinearFactor.h:208
std::shared_ptr< This > shared_ptr
Noise model.
Definition NonlinearFactor.h:220
Definition NonlinearFactorGraph.h:57
virtual double error(const Values &values) const
unnormalized error, in the most common case
Definition NonlinearFactorGraph.cpp:171
A non-templated config holding any types of Manifold-group elements.
Definition Values.h:65
void print(const std::string &str="", const KeyFormatter &keyFormatter=DefaultKeyFormatter) const
print method for testing and debugging
Definition Values.cpp:68