| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | /* ----------------------------------------------------------------------------
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * GTSAM Copyright 2010, Georgia Tech Research Corporation, | 
					
						
							|  |  |  |  * Atlanta, Georgia 30332-0415 | 
					
						
							|  |  |  |  * All Rights Reserved | 
					
						
							|  |  |  |  * Authors: Frank Dellaert, et al. (see THANKS for the full author list) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * See LICENSE for the license information | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |  * -------------------------------------------------------------------------- */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /**
 | 
					
						
							|  |  |  |  * @file    testGncOptimizer.cpp | 
					
						
							|  |  |  |  * @brief   Unit tests for GncOptimizer class | 
					
						
							| 
									
										
										
										
											2020-11-26 09:11:04 +08:00
										 |  |  |  * @author  Jingnan Shi | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  |  * @author  Luca Carlone | 
					
						
							|  |  |  |  * @author  Frank Dellaert | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |  * Implementation of the paper: Yang, Antonante, Tzoumas, Carlone, "Graduated | 
					
						
							|  |  |  |  * Non-Convexity for Robust Spatial Perception: From Non-Minimal Solvers to | 
					
						
							|  |  |  |  * Global Outlier Rejection", ICRA/RAL, 2020. (arxiv version: | 
					
						
							|  |  |  |  * https://arxiv.org/pdf/1909.08605.pdf)
 | 
					
						
							| 
									
										
										
										
											2020-12-06 02:47:40 +08:00
										 |  |  |  * | 
					
						
							|  |  |  |  * See also: | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |  * Antonante, Tzoumas, Yang, Carlone, "Outlier-Robust Estimation: Hardness, | 
					
						
							|  |  |  |  * Minimally-Tuned Algorithms, and Applications", arxiv: | 
					
						
							|  |  |  |  * https://arxiv.org/pdf/2007.15109.pdf, 2020.
 | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  | #include <CppUnitLite/TestHarness.h>
 | 
					
						
							| 
									
										
										
										
											2020-12-06 02:47:40 +08:00
										 |  |  | #include <gtsam/nonlinear/GncOptimizer.h>
 | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  | #include <gtsam/nonlinear/LinearContainerFactor.h>
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  | #include <gtsam/slam/dataset.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | #include <tests/smallExample.h>
 | 
					
						
							| 
									
										
										
										
											2020-11-26 09:11:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 12:24:28 +08:00
										 |  |  | #include <gtsam/sam/BearingFactor.h>
 | 
					
						
							|  |  |  | #include <gtsam/geometry/Pose2.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 09:11:04 +08:00
										 |  |  | using namespace std; | 
					
						
							|  |  |  | using namespace gtsam; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | using symbol_shorthand::L; | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  | using symbol_shorthand::X; | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | static double tol = 1e-7; | 
					
						
							| 
									
										
										
										
											2020-11-26 09:11:04 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 02:05:54 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, gncParamsConstructor) { | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // check params are correctly parsed
 | 
					
						
							| 
									
										
										
										
											2020-11-28 02:05:54 +08:00
										 |  |  |   LevenbergMarquardtParams lmParams; | 
					
						
							|  |  |  |   GncParams<LevenbergMarquardtParams> gncParams1(lmParams); | 
					
						
							|  |  |  |   CHECK(lmParams.equals(gncParams1.baseOptimizerParams)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // check also default constructor
 | 
					
						
							|  |  |  |   GncParams<LevenbergMarquardtParams> gncParams1b; | 
					
						
							|  |  |  |   CHECK(lmParams.equals(gncParams1b.baseOptimizerParams)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // and check params become different if we change lmParams
 | 
					
						
							|  |  |  |   lmParams.setVerbosity("DELTA"); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   CHECK(!lmParams.equals(gncParams1.baseOptimizerParams)); | 
					
						
							| 
									
										
										
										
											2020-11-28 02:05:54 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // and same for GN
 | 
					
						
							|  |  |  |   GaussNewtonParams gnParams; | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams2(gnParams); | 
					
						
							|  |  |  |   CHECK(gnParams.equals(gncParams2.baseOptimizerParams)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // check default constructor
 | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams2b; | 
					
						
							|  |  |  |   CHECK(gnParams.equals(gncParams2b.baseOptimizerParams)); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // change something at the gncParams level
 | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams2c(gncParams2b); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams2c.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   CHECK(!gncParams2c.equals(gncParams2b.baseOptimizerParams)); | 
					
						
							| 
									
										
										
										
											2020-11-28 02:05:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | TEST(GncOptimizer, gncConstructor) { | 
					
						
							|  |  |  |   // has to have Gaussian noise models !
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto fg = example::createReallyNonlinearFactorGraph();  // just a unary factor
 | 
					
						
							|  |  |  |                                                           // on a 2D point
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   CHECK(gnc.getFactors().equals(fg)); | 
					
						
							|  |  |  |   CHECK(gnc.getState().equals(initial)); | 
					
						
							|  |  |  |   CHECK(gnc.getParams().equals(gncParams)); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   auto gnc2 = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // check the equal works
 | 
					
						
							|  |  |  |   CHECK(gnc.equals(gnc2)); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-19 09:23:45 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, solverParameterParsing) { | 
					
						
							|  |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph();  // just a unary factor
 | 
					
						
							|  |  |  |                                                           // on a 2D point
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   LevenbergMarquardtParams lmParams; | 
					
						
							|  |  |  |   lmParams.setMaxIterations(0); // forces not to perform optimization
 | 
					
						
							|  |  |  |   GncParams<LevenbergMarquardtParams> gncParams(lmParams); | 
					
						
							|  |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							|  |  |  |   Values result = gnc.optimize(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // check that LM did not perform optimization and result is the same as the initial guess
 | 
					
						
							|  |  |  |   DOUBLES_EQUAL(fg.error(initial), fg.error(result), tol); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // also check the params:
 | 
					
						
							|  |  |  |   DOUBLES_EQUAL(0.0, gncParams.baseOptimizerParams.maxIterations, tol); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 06:14:34 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, gncConstructorWithRobustGraphAsInput) { | 
					
						
							| 
									
										
										
										
											2020-11-28 08:00:08 +08:00
										 |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							| 
									
										
										
										
											2020-11-28 06:14:34 +08:00
										 |  |  |   // same graph with robust noise model
 | 
					
						
							|  |  |  |   auto fg_robust = example::sharedRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg_robust, | 
					
						
							|  |  |  |                                                                initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:07:16 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // make sure that when parsing the graph is transformed into one without
 | 
					
						
							|  |  |  |   // robust loss
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   CHECK(fg.equals(gnc.getFactors())); | 
					
						
							| 
									
										
										
										
											2020-11-28 06:14:34 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, initializeMu) { | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // testing GM mu initialization
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc_gm = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                   gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 11:27:47 +08:00
										 |  |  |   gnc_gm.setInlierCostThresholds(1.0); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // according to rmk 5 in the gnc paper: m0 = 2 rmax^2 / barcSq
 | 
					
						
							|  |  |  |   // (barcSq=1 in this example)
 | 
					
						
							|  |  |  |   EXPECT_DOUBLES_EQUAL(gnc_gm.initializeMu(), 2 * 198.999, 1e-3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // testing TLS mu initialization
 | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc_tls = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                    gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 11:27:47 +08:00
										 |  |  |   gnc_tls.setInlierCostThresholds(1.0); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // according to rmk 5 in the gnc paper: m0 =  barcSq / (2 * rmax^2 - barcSq)
 | 
					
						
							|  |  |  |   // (barcSq=1 in this example)
 | 
					
						
							| 
									
										
										
										
											2020-12-08 06:28:35 +08:00
										 |  |  |   EXPECT_DOUBLES_EQUAL(gnc_tls.initializeMu(), 1 / (2 * 198.999 - 1), 1e-3); | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-12-08 05:16:21 +08:00
										 |  |  | TEST(GncOptimizer, updateMuGM) { | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:16:21 +08:00
										 |  |  |   gncParams.setMuStep(1.4); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   double mu = 5.0; | 
					
						
							|  |  |  |   EXPECT_DOUBLES_EQUAL(gnc.updateMu(mu), mu / 1.4, tol); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // check it correctly saturates to 1 for GM
 | 
					
						
							|  |  |  |   mu = 1.2; | 
					
						
							|  |  |  |   EXPECT_DOUBLES_EQUAL(gnc.updateMu(mu), 1.0, tol); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:16:21 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, updateMuTLS) { | 
					
						
							|  |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-08 05:16:21 +08:00
										 |  |  |   gncParams.setMuStep(1.4); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:16:21 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   double mu = 5.0; | 
					
						
							|  |  |  |   EXPECT_DOUBLES_EQUAL(gnc.updateMu(mu), mu * 1.4, tol); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  | TEST(GncOptimizer, checkMuConvergence) { | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     double mu = 1.0; | 
					
						
							|  |  |  |     CHECK(gnc.checkMuConvergence(mu)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType( | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |         GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     double mu = 1.0; | 
					
						
							|  |  |  |     CHECK(!gnc.checkMuConvergence(mu));  //always false for TLS
 | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, checkCostConvergence) { | 
					
						
							|  |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setRelativeCostTol(0.49); | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     double prev_cost = 1.0; | 
					
						
							|  |  |  |     double cost = 0.5; | 
					
						
							|  |  |  |     // relative cost reduction = 0.5 > 0.49, hence checkCostConvergence = false
 | 
					
						
							|  |  |  |     CHECK(!gnc.checkCostConvergence(cost, prev_cost)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setRelativeCostTol(0.51); | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     double prev_cost = 1.0; | 
					
						
							|  |  |  |     double cost = 0.5; | 
					
						
							|  |  |  |     // relative cost reduction = 0.5 < 0.51, hence checkCostConvergence = true
 | 
					
						
							|  |  |  |     CHECK(gnc.checkCostConvergence(cost, prev_cost)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, checkWeightsConvergence) { | 
					
						
							|  |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     Vector weights = Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     CHECK(!gnc.checkWeightsConvergence(weights));  //always false for GM
 | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType( | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |         GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     // weights are binary, so checkWeightsConvergence = true
 | 
					
						
							|  |  |  |     CHECK(gnc.checkWeightsConvergence(weights)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType( | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |         GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     weights[0] = 0.9;  // more than weightsTol = 1e-4 from 1, hence checkWeightsConvergence = false
 | 
					
						
							|  |  |  |     CHECK(!gnc.checkWeightsConvergence(weights)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType( | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |         GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     gncParams.setWeightsTol(0.1); | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                  gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     weights[0] = 0.9;  // exactly weightsTol = 0.1 from 1, hence checkWeightsConvergence = true
 | 
					
						
							|  |  |  |     CHECK(gnc.checkWeightsConvergence(weights)); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-12-08 06:28:35 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-12-23 10:08:42 +08:00
										 |  |  | TEST(GncOptimizer, checkConvergenceTLS) { | 
					
						
							| 
									
										
										
										
											2020-12-08 06:28:35 +08:00
										 |  |  |   // has to have Gaussian noise models !
 | 
					
						
							|  |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							| 
									
										
										
										
											2020-12-08 02:24:49 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-23 10:08:42 +08:00
										 |  |  |   gncParams.setRelativeCostTol(1e-5); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-12-08 06:28:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-23 10:08:42 +08:00
										 |  |  |   CHECK(gnc.checkCostConvergence(1.0, 1.0)); | 
					
						
							|  |  |  |   CHECK(!gnc.checkCostConvergence(1.0, 2.0)); | 
					
						
							| 
									
										
										
										
											2020-11-28 05:10:03 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 05:31:32 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-12-08 09:20:51 +08:00
										 |  |  | TEST(GncOptimizer, calculateWeightsGM) { | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(0, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // we have 4 factors, 3 with zero errors (inliers), 1 with error 50 = 0.5 *
 | 
					
						
							|  |  |  |   // 1/sigma^2 || [1;0] - [0;0] ||^2 (outlier)
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   Vector weights_expected = Vector::Zero(4); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   weights_expected[0] = 1.0;                             // zero error
 | 
					
						
							|  |  |  |   weights_expected[1] = 1.0;                             // zero error
 | 
					
						
							|  |  |  |   weights_expected[2] = 1.0;                             // zero error
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   weights_expected[3] = std::pow(1.0 / (50.0 + 1.0), 2);  // outlier, error = 50
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   GaussNewtonParams gnParams; | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 11:27:47 +08:00
										 |  |  |   gnc.setInlierCostThresholds(1.0); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   double mu = 1.0; | 
					
						
							|  |  |  |   Vector weights_actual = gnc.calculateWeights(initial, mu); | 
					
						
							|  |  |  |   CHECK(assert_equal(weights_expected, weights_actual, tol)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   mu = 2.0; | 
					
						
							|  |  |  |   double barcSq = 5.0; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   weights_expected[3] = std::pow(mu * barcSq / (50.0 + mu * barcSq), 2);  // outlier, error = 50
 | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc2 = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |                                                          gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |   gnc2.setInlierCostThresholds(barcSq); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   weights_actual = gnc2.calculateWeights(initial, mu); | 
					
						
							|  |  |  |   CHECK(assert_equal(weights_expected, weights_actual, tol)); | 
					
						
							| 
									
										
										
										
											2020-12-08 09:20:51 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, calculateWeightsTLS) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(0, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // we have 4 factors, 3 with zero errors (inliers), 1 with error
 | 
					
						
							|  |  |  |   Vector weights_expected = Vector::Zero(4); | 
					
						
							|  |  |  |   weights_expected[0] = 1.0;                             // zero error
 | 
					
						
							|  |  |  |   weights_expected[1] = 1.0;                             // zero error
 | 
					
						
							|  |  |  |   weights_expected[2] = 1.0;                             // zero error
 | 
					
						
							|  |  |  |   weights_expected[3] = 0;                               // outliers
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   GaussNewtonParams gnParams; | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |   gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-08 09:20:51 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, gncParams); | 
					
						
							|  |  |  |   double mu = 1.0; | 
					
						
							|  |  |  |   Vector weights_actual = gnc.calculateWeights(initial, mu); | 
					
						
							|  |  |  |   CHECK(assert_equal(weights_expected, weights_actual, tol)); | 
					
						
							| 
									
										
										
										
											2020-11-28 05:31:32 +08:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, calculateWeightsTLS2) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // create values
 | 
					
						
							|  |  |  |   Point2 x_val(0.0, 0.0); | 
					
						
							|  |  |  |   Point2 x_prior(1.0, 0.0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), x_val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // create very simple factor graph with a single factor 0.5 * 1/sigma^2 * || x - [1;0] ||^2
 | 
					
						
							|  |  |  |   double sigma = 1; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   SharedDiagonal noise = noiseModel::Diagonal::Sigmas(Vector2(sigma, sigma)); | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  |   NonlinearFactorGraph nfg; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   nfg.add(PriorFactor<Point2>(X(1), x_prior, noise)); | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // cost of the factor:
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   DOUBLES_EQUAL(0.5 * 1 / (sigma * sigma), nfg.error(initial), tol); | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // check the TLS weights are correct: CASE 1: residual below barcsq
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     // expected:
 | 
					
						
							|  |  |  |     Vector weights_expected = Vector::Zero(1); | 
					
						
							|  |  |  |     weights_expected[0] = 1.0;  // inlier
 | 
					
						
							|  |  |  |     // actual:
 | 
					
						
							|  |  |  |     GaussNewtonParams gnParams; | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(nfg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |     gnc.setInlierCostThresholds(0.51);  // if inlier threshold is slightly larger than 0.5, then measurement is inlier
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     double mu = 1e6; | 
					
						
							|  |  |  |     Vector weights_actual = gnc.calculateWeights(initial, mu); | 
					
						
							|  |  |  |     CHECK(assert_equal(weights_expected, weights_actual, tol)); | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // check the TLS weights are correct: CASE 2: residual above barcsq
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     // expected:
 | 
					
						
							|  |  |  |     Vector weights_expected = Vector::Zero(1); | 
					
						
							|  |  |  |     weights_expected[0] = 0.0;  // outlier
 | 
					
						
							|  |  |  |     // actual:
 | 
					
						
							|  |  |  |     GaussNewtonParams gnParams; | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(nfg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |     gnc.setInlierCostThresholds(0.49);  // if inlier threshold is slightly below 0.5, then measurement is outlier
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     double mu = 1e6;  // very large mu recovers original TLS cost
 | 
					
						
							|  |  |  |     Vector weights_actual = gnc.calculateWeights(initial, mu); | 
					
						
							|  |  |  |     CHECK(assert_equal(weights_expected, weights_actual, tol)); | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   // check the TLS weights are correct: CASE 2: residual at barcsq
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     // expected:
 | 
					
						
							|  |  |  |     Vector weights_expected = Vector::Zero(1); | 
					
						
							|  |  |  |     weights_expected[0] = 0.5;  // undecided
 | 
					
						
							|  |  |  |     // actual:
 | 
					
						
							|  |  |  |     GaussNewtonParams gnParams; | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(nfg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |     gnc.setInlierCostThresholds(0.5);  // if inlier threshold is slightly below 0.5, then measurement is outlier
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     double mu = 1e6;  // very large mu recovers original TLS cost
 | 
					
						
							| 
									
										
										
										
											2020-12-22 11:28:07 +08:00
										 |  |  |     Vector weights_actual = gnc.calculateWeights(initial, mu); | 
					
						
							|  |  |  |     CHECK(assert_equal(weights_expected, weights_actual, 1e-5)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 06:14:34 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | TEST(GncOptimizer, makeWeightedGraph) { | 
					
						
							|  |  |  |   // create original factor
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   double sigma1 = 0.1; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   NonlinearFactorGraph nfg = example::nonlinearFactorGraphWithGivenSigma( | 
					
						
							|  |  |  |       sigma1); | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   // create expected
 | 
					
						
							|  |  |  |   double sigma2 = 10; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   NonlinearFactorGraph expected = example::nonlinearFactorGraphWithGivenSigma( | 
					
						
							|  |  |  |       sigma2); | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   // create weights
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   Vector weights = Vector::Ones(1);  // original info:1/0.1^2 = 100. New info: 1/10^2 = 0.01. Ratio is 10-4
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   weights[0] = 1e-4; | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   // create actual
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   LevenbergMarquardtParams lmParams; | 
					
						
							|  |  |  |   GncParams<LevenbergMarquardtParams> gncParams(lmParams); | 
					
						
							|  |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(nfg, initial, | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   NonlinearFactorGraph actual = gnc.makeWeightedGraph(weights); | 
					
						
							| 
									
										
										
										
											2020-11-28 08:29:42 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   // check it's all good
 | 
					
						
							|  |  |  |   CHECK(assert_equal(expected, actual)); | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  | TEST(GncOptimizer, optimizeSimple) { | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  |   auto fg = example::createReallyNonlinearFactorGraph(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(3, 3); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   LevenbergMarquardtParams lmParams; | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  |   GncParams<LevenbergMarquardtParams> gncParams(lmParams); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<LevenbergMarquardtParams>>(fg, initial, | 
					
						
							|  |  |  |                                                                gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  |   Values actual = gnc.optimize(); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:14:41 +08:00
										 |  |  |   DOUBLES_EQUAL(0, fg.error(actual), tol); | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, optimize) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // try with nonrobust cost function and standard GN
 | 
					
						
							|  |  |  |   GaussNewtonParams gnParams; | 
					
						
							|  |  |  |   GaussNewtonOptimizer gn(fg, initial, gnParams); | 
					
						
							|  |  |  |   Values gn_results = gn.optimize(); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // converges to incorrect point due to lack of robustness to an outlier, ideal
 | 
					
						
							|  |  |  |   // solution is Point2(0,0)
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   CHECK(assert_equal(Point2(0.25, 0.0), gn_results.at<Point2>(X(1)), 1e-3)); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // try with robust loss function and standard GN
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto fg_robust = example::sharedRobustFactorGraphWithOutliers();  // same as fg, but with
 | 
					
						
							|  |  |  |                                                                     // factors wrapped in
 | 
					
						
							|  |  |  |                                                                     // Geman McClure losses
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  |   GaussNewtonOptimizer gn2(fg_robust, initial, gnParams); | 
					
						
							|  |  |  |   Values gn2_results = gn2.optimize(); | 
					
						
							|  |  |  |   // converges to incorrect point, this time due to the nonconvexity of the loss
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   CHECK(assert_equal(Point2(0.999706, 0.0), gn2_results.at<Point2>(X(1)), 1e-3)); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   // .. but graduated nonconvexity ensures both robustness and convergence in
 | 
					
						
							|  |  |  |   // the face of nonconvexity
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  |   GncParams<GaussNewtonParams> gncParams(gnParams); | 
					
						
							| 
									
										
										
										
											2020-12-29 09:43:35 +08:00
										 |  |  |   // gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, gncParams); | 
					
						
							|  |  |  |   Values gnc_result = gnc.optimize(); | 
					
						
							| 
									
										
										
										
											2020-11-28 11:54:51 +08:00
										 |  |  |   CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							| 
									
										
										
										
											2020-11-28 04:46:12 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 11:50:41 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, optimizeWithKnownInliers) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |   GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2020-11-28 11:50:41 +08:00
										 |  |  |   knownInliers.push_back(0); | 
					
						
							|  |  |  |   knownInliers.push_back(1); | 
					
						
							|  |  |  |   knownInliers.push_back(2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nonconvexity with known inliers
 | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 11:27:47 +08:00
										 |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     // gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     // if we set the threshold large, they are all inliers
 | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							| 
									
										
										
										
											2020-12-30 10:59:21 +08:00
										 |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::VALUES);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |                                                           gncParams); | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |     gnc.setInlierCostThresholds(100.0); | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.25, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[3], tol); | 
					
						
							| 
									
										
										
										
											2020-12-24 11:08:44 +08:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2020-11-28 11:50:41 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 12:24:28 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, chi2inv) { | 
					
						
							|  |  |  |   DOUBLES_EQUAL(8.807468393511950, Chi2inv(0.997, 1), tol); // from MATLAB: chi2inv(0.997, 1) = 8.807468393511950
 | 
					
						
							|  |  |  |   DOUBLES_EQUAL(13.931422665512077, Chi2inv(0.997, 3), tol); // from MATLAB: chi2inv(0.997, 3) = 13.931422665512077
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, barcsq) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |   GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2021-01-23 12:24:28 +08:00
										 |  |  |   knownInliers.push_back(0); | 
					
						
							|  |  |  |   knownInliers.push_back(1); | 
					
						
							|  |  |  |   knownInliers.push_back(2); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |   gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |   gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |   //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |       gncParams); | 
					
						
							|  |  |  |   // expected: chi2inv(0.99, 2)/2
 | 
					
						
							|  |  |  |   CHECK(assert_equal(4.605170185988091 * Vector::Ones(fg.size()), gnc.getInlierCostThresholds(), 1e-3)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, barcsq_heterogeneousFactors) { | 
					
						
							|  |  |  |   NonlinearFactorGraph fg; | 
					
						
							|  |  |  |   // specify noise model, otherwise it segfault if we leave default noise model
 | 
					
						
							|  |  |  |    SharedNoiseModel model3D(noiseModel::Isotropic::Sigma(3, 0.5)); | 
					
						
							|  |  |  |    fg.add( PriorFactor<Pose2>(  0, Pose2(0.0, 0.0, 0.0) , model3D )); // size 3
 | 
					
						
							|  |  |  |   SharedNoiseModel model2D(noiseModel::Isotropic::Sigma(2, 0.5)); | 
					
						
							|  |  |  |   fg.add( PriorFactor<Point2>(  1, Point2(0.0,0.0), model2D )); // size 2
 | 
					
						
							|  |  |  |   SharedNoiseModel model1D(noiseModel::Isotropic::Sigma(1, 0.5)); | 
					
						
							|  |  |  |   fg.add( BearingFactor<Pose2, Point2>(  0, 1, 1.0, model1D) ); // size 1
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(0, Pose2(0.0, 0.0, 0.0)); | 
					
						
							|  |  |  |   initial.insert(1, Point2(0.0,0.0)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial); | 
					
						
							|  |  |  |   CHECK(assert_equal(Vector3(5.672433365072185, 4.605170185988091, 3.317448300510607), | 
					
						
							|  |  |  |                      gnc.getInlierCostThresholds(), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // extra test:
 | 
					
						
							|  |  |  |   // fg.add( PriorFactor<Pose2>(  0, Pose2(0.0, 0.0, 0.0) )); // works if we add model3D as noise model
 | 
					
						
							|  |  |  |   // std::cout <<  "fg[3]->dim() " << fg[3]->dim() << std::endl; // this segfaults?
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2021-05-11 08:06:31 +08:00
										 |  |  | TEST(GncOptimizer, setInlierCostThresholds) { | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |   GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2021-01-23 10:04:28 +08:00
										 |  |  |   knownInliers.push_back(0); | 
					
						
							|  |  |  |   knownInliers.push_back(1); | 
					
						
							|  |  |  |   knownInliers.push_back(2); | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(2.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     CHECK(assert_equal(2.0 * Vector::Ones(fg.size()), gnc.getInlierCostThresholds(), 1e-3)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(2.0 * Vector::Ones(fg.size())); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     CHECK(assert_equal(2.0 * Vector::Ones(fg.size()), gnc.getInlierCostThresholds(), 1e-3)); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, optimizeSmallPoseGraph) { | 
					
						
							|  |  |  |   /// load small pose graph
 | 
					
						
							|  |  |  |   const string filename = findExampleDataFile("w100.graph"); | 
					
						
							| 
									
										
										
										
											2023-02-05 01:42:39 +08:00
										 |  |  |   const auto [graph, initial] = load2D(filename); | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  |   // Add a Gaussian prior on first poses
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   Pose2 priorMean(0.0, 0.0, 0.0);  // prior at origin
 | 
					
						
							|  |  |  |   SharedDiagonal priorNoise = noiseModel::Diagonal::Sigmas( | 
					
						
							|  |  |  |       Vector3(0.01, 0.01, 0.01)); | 
					
						
							| 
									
										
										
										
											2020-12-08 05:04:36 +08:00
										 |  |  |   graph->addPrior(0, priorMean, priorNoise); | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// get expected values by optimizing outlier-free graph
 | 
					
						
							|  |  |  |   Values expected = LevenbergMarquardtOptimizer(*graph, *initial).optimize(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // add a few outliers
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   SharedDiagonal betweenNoise = noiseModel::Diagonal::Sigmas( | 
					
						
							|  |  |  |       Vector3(0.1, 0.1, 0.01)); | 
					
						
							| 
									
										
										
										
											2023-07-11 01:43:44 +08:00
										 |  |  |   // some arbitrary and incorrect between factor
 | 
					
						
							|  |  |  |   graph->push_back(BetweenFactor<Pose2>(90, 50, Pose2(), betweenNoise)); | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   /// get expected values by optimizing outlier-free graph
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   Values expectedWithOutliers = LevenbergMarquardtOptimizer(*graph, *initial) | 
					
						
							|  |  |  |       .optimize(); | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  |   // as expected, the following test fails due to the presence of an outlier!
 | 
					
						
							|  |  |  |   // CHECK(assert_equal(expected, expectedWithOutliers, 1e-3));
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // GNC
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:43:44 +08:00
										 |  |  |   // NOTE: in difficult instances, we set the odometry measurements to be
 | 
					
						
							|  |  |  |   // inliers, but this problem is simple enough to succeed even without that
 | 
					
						
							|  |  |  |   // assumption.
 | 
					
						
							| 
									
										
										
										
											2020-12-06 02:47:40 +08:00
										 |  |  |   GncParams<GaussNewtonParams> gncParams; | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(*graph, *initial, | 
					
						
							|  |  |  |                                                         gncParams); | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  |   Values actual = gnc.optimize(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // compare
 | 
					
						
							| 
									
										
										
										
											2020-12-29 10:03:20 +08:00
										 |  |  |   CHECK(assert_equal(expected, actual, 1e-3));  // yay! we are robust to outliers!
 | 
					
						
							| 
									
										
										
										
											2020-11-28 12:12:26 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-10 22:30:32 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, knownInliersAndOutliers) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |   // nonconvexity with known inliers and known outliers (check early stopping
 | 
					
						
							|  |  |  |   // when all measurements are known to be inliers or outliers)
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     knownInliers.push_back(0); | 
					
						
							|  |  |  |     knownInliers.push_back(1); | 
					
						
							|  |  |  |     knownInliers.push_back(2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownOutliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     knownOutliers.push_back(3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |     gncParams.setKnownOutliers(knownOutliers); | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							| 
									
										
										
										
											2021-05-10 22:30:32 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // nonconvexity with known inliers and known outliers
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     knownInliers.push_back(2); | 
					
						
							|  |  |  |     knownInliers.push_back(0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownOutliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     knownOutliers.push_back(3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |     gncParams.setKnownOutliers(knownOutliers); | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], 1e-5); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // only known outliers
 | 
					
						
							|  |  |  |   { | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownOutliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 06:23:12 +08:00
										 |  |  |     knownOutliers.push_back(3); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setKnownOutliers(knownOutliers); | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::GM); | 
					
						
							|  |  |  |     //gncParams.setVerbosityGNC(GncParams<GaussNewtonParams>::Verbosity::SUMMARY);
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], 1e-5); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2021-05-10 22:30:32 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-11 08:06:31 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | TEST(GncOptimizer, setWeights) { | 
					
						
							|  |  |  |   auto fg = example::sharedNonRobustFactorGraphWithOutliers(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   Point2 p0(1, 0); | 
					
						
							|  |  |  |   Values initial; | 
					
						
							|  |  |  |   initial.insert(X(1), p0); | 
					
						
							|  |  |  |   // initialize weights to be the same
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = 0.5 * Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setWeights(weights); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // try a more challenging initialization
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = Vector::Zero(fg.size()); | 
					
						
							|  |  |  |     weights(2) = 1.0; | 
					
						
							|  |  |  |     weights(3) = 1.0; // bad initialization: we say the outlier is inlier
 | 
					
						
							|  |  |  |     // GNC can still recover (but if you omit weights(2) = 1.0, then it would fail)
 | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setWeights(weights); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   // initialize weights and also set known inliers/outliers
 | 
					
						
							|  |  |  |   { | 
					
						
							|  |  |  |     GncParams<GaussNewtonParams> gncParams; | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownInliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 08:06:31 +08:00
										 |  |  |     knownInliers.push_back(2); | 
					
						
							|  |  |  |     knownInliers.push_back(0); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-07-11 01:40:14 +08:00
										 |  |  |     GncParams<GaussNewtonParams>::IndexVector knownOutliers; | 
					
						
							| 
									
										
										
										
											2021-05-11 08:06:31 +08:00
										 |  |  |     knownOutliers.push_back(3); | 
					
						
							|  |  |  |     gncParams.setKnownInliers(knownInliers); | 
					
						
							|  |  |  |     gncParams.setKnownOutliers(knownOutliers); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     gncParams.setLossType(GncLossType::TLS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     Vector weights = 0.5 * Vector::Ones(fg.size()); | 
					
						
							|  |  |  |     auto gnc = GncOptimizer<GncParams<GaussNewtonParams>>(fg, initial, | 
					
						
							|  |  |  |         gncParams); | 
					
						
							|  |  |  |     gnc.setWeights(weights); | 
					
						
							|  |  |  |     gnc.setInlierCostThresholds(1.0); | 
					
						
							|  |  |  |     Values gnc_result = gnc.optimize(); | 
					
						
							|  |  |  |     CHECK(assert_equal(Point2(0.0, 0.0), gnc_result.at<Point2>(X(1)), 1e-3)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // check weights were actually fixed:
 | 
					
						
							|  |  |  |     Vector finalWeights = gnc.getWeights(); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[0], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[1], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(1.0, finalWeights[2], tol); | 
					
						
							|  |  |  |     DOUBLES_EQUAL(0.0, finalWeights[3], tol); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-26 00:02:01 +08:00
										 |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | int main() { | 
					
						
							|  |  |  |   TestResult tr; | 
					
						
							|  |  |  |   return TestRegistry::runAllTests(tr); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | /* ************************************************************************* */ |