| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | /**
 | 
					
						
							| 
									
										
										
										
											2009-09-13 12:13:03 +08:00
										 |  |  |  * NonlinearOptimizer-inl.h | 
					
						
							|  |  |  |  * This is a template definition file, include it where needed (only!) | 
					
						
							|  |  |  |  * so that the appropriate code is generated and link errors avoided. | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  |  * @brief: Encapsulates nonlinear optimization state | 
					
						
							|  |  |  |  * @Author: Frank Dellaert | 
					
						
							|  |  |  |  * Created on: Sep 7, 2009 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-13 12:13:03 +08:00
										 |  |  | #pragma once
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | #include <iostream>
 | 
					
						
							|  |  |  | #include <boost/tuple/tuple.hpp>
 | 
					
						
							|  |  |  | #include "NonlinearOptimizer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-16 09:16:59 +08:00
										 |  |  | #define INSTANTIATE_NONLINEAR_OPTIMIZER(G,C) \
 | 
					
						
							|  |  |  |   template class NonlinearOptimizer<G,C>; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | using namespace std; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace gtsam { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-10-07 02:25:04 +08:00
										 |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2009-12-14 14:10:51 +08:00
										 |  |  | 	inline bool check_convergence(double relativeErrorTreshold, | 
					
						
							| 
									
										
										
										
											2009-10-07 02:25:04 +08:00
										 |  |  | 			double absoluteErrorTreshold, double currentError, double newError, | 
					
						
							|  |  |  | 			int verbosity) { | 
					
						
							|  |  |  | 		// check if diverges
 | 
					
						
							|  |  |  | 		double absoluteDecrease = currentError - newError; | 
					
						
							|  |  |  | 		if (verbosity >= 2) | 
					
						
							|  |  |  | 			cout << "absoluteDecrease: " << absoluteDecrease << endl; | 
					
						
							| 
									
										
										
										
											2010-01-19 12:18:59 +08:00
										 |  |  | //		if (absoluteDecrease < 0)
 | 
					
						
							|  |  |  | //			throw overflow_error(
 | 
					
						
							|  |  |  | //					"NonlinearFactorGraph::optimize: error increased, diverges.");
 | 
					
						
							| 
									
										
										
										
											2009-10-07 02:25:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// calculate relative error decrease and update currentError
 | 
					
						
							|  |  |  | 		double relativeDecrease = absoluteDecrease / currentError; | 
					
						
							|  |  |  | 		if (verbosity >= 2) | 
					
						
							|  |  |  | 			cout << "relativeDecrease: " << relativeDecrease << endl; | 
					
						
							|  |  |  | 		bool converged = (relativeDecrease < relativeErrorTreshold) | 
					
						
							|  |  |  | 				|| (absoluteDecrease < absoluteErrorTreshold); | 
					
						
							|  |  |  | 		if (verbosity >= 1 && converged) | 
					
						
							|  |  |  | 			cout << "converged" << endl; | 
					
						
							|  |  |  | 		return converged; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-18 13:51:19 +08:00
										 |  |  | 	// Constructors without the solver
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W>::NonlinearOptimizer(shared_graph graph, | 
					
						
							| 
									
										
										
										
											2010-01-20 10:28:23 +08:00
										 |  |  | 			shared_config config, shared_solver solver, double lambda) : | 
					
						
							|  |  |  | 		graph_(graph), config_(config), error_(graph->error( | 
					
						
							| 
									
										
										
										
											2010-01-18 13:51:19 +08:00
										 |  |  | 				*config)), lambda_(lambda), solver_(solver) { | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-18 13:51:19 +08:00
										 |  |  | 	/* ************************************************************************* */ | 
					
						
							|  |  |  | 	// linearize and optimize
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	VectorConfig NonlinearOptimizer<G, C, L, S, W>::linearizeAndOptimizeForDelta() const { | 
					
						
							| 
									
										
										
										
											2010-01-23 08:57:54 +08:00
										 |  |  | 		boost::shared_ptr<L> linearized = solver_->linearize(*graph_, *config_); | 
					
						
							|  |  |  | 		return solver_->optimize(*linearized); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							|  |  |  | 	// One iteration of Gauss Newton
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W> NonlinearOptimizer<G, C, L, S, W>::iterate( | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 			verbosityLevel verbosity) const { | 
					
						
							|  |  |  | 		// linearize and optimize
 | 
					
						
							| 
									
										
										
										
											2009-10-20 03:12:48 +08:00
										 |  |  | 		VectorConfig delta = linearizeAndOptimizeForDelta(); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// maybe show output
 | 
					
						
							|  |  |  | 		if (verbosity >= DELTA) | 
					
						
							|  |  |  | 			delta.print("delta"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// take old config and update it
 | 
					
						
							| 
									
										
										
										
											2010-01-08 08:40:17 +08:00
										 |  |  | 		shared_config newConfig(new C(expmap(*config_,delta))); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// maybe show output
 | 
					
						
							|  |  |  | 		if (verbosity >= CONFIG) | 
					
						
							|  |  |  | 			newConfig->print("newConfig"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 		NonlinearOptimizer newOptimizer = NonlinearOptimizer(graph_, newConfig, solver_, lambda_); | 
					
						
							| 
									
										
										
										
											2010-01-16 13:08:29 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (verbosity >= ERROR) | 
					
						
							|  |  |  | 			cout << "error: " << newOptimizer.error_ << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return newOptimizer; | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W> NonlinearOptimizer<G, C, L, S, W>::gaussNewton( | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 			double relativeThreshold, double absoluteThreshold, | 
					
						
							|  |  |  | 			verbosityLevel verbosity, int maxIterations) const { | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 		static W writer(error_); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		// linearize, solve, update
 | 
					
						
							|  |  |  | 		NonlinearOptimizer next = iterate(verbosity); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 		writer.write(next.error_); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		// check convergence
 | 
					
						
							|  |  |  | 		bool converged = gtsam::check_convergence(relativeThreshold, | 
					
						
							|  |  |  | 				absoluteThreshold, error_, next.error_, verbosity); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 				// return converged state or iterate
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		if (converged) | 
					
						
							|  |  |  | 			return next; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			return next.gaussNewton(relativeThreshold, absoluteThreshold, verbosity); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							|  |  |  | 	// Recursively try to do tempered Gauss-Newton steps until we succeed.
 | 
					
						
							|  |  |  | 	// Form damped system with given lambda, and return a new, more optimistic
 | 
					
						
							|  |  |  | 	// optimizer if error decreased or recurse with a larger lambda if not.
 | 
					
						
							|  |  |  | 	// TODO: in theory we can't infinitely recurse, but maybe we should put a max.
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W> NonlinearOptimizer<G, C, L, S, W>::try_lambda( | 
					
						
							| 
									
										
										
										
											2010-01-19 18:46:12 +08:00
										 |  |  | 			const L& linear, verbosityLevel verbosity, double factor) const { | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (verbosity >= TRYLAMBDA) | 
					
						
							|  |  |  | 			cout << "trying lambda = " << lambda_ << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// add prior-factors
 | 
					
						
							| 
									
										
										
										
											2010-01-19 18:46:12 +08:00
										 |  |  | 		L damped = linear.add_priors(1.0/sqrt(lambda_)); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		if (verbosity >= DAMPED) | 
					
						
							|  |  |  | 			damped.print("damped"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// solve
 | 
					
						
							| 
									
										
										
										
											2010-01-20 10:28:23 +08:00
										 |  |  | 		VectorConfig delta = solver_->optimize(damped); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		if (verbosity >= TRYDELTA) | 
					
						
							|  |  |  | 			delta.print("delta"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// update config
 | 
					
						
							| 
									
										
										
										
											2010-01-08 08:40:17 +08:00
										 |  |  | 		shared_config newConfig(new C(expmap(*config_,delta))); // TODO: updateConfig
 | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		if (verbosity >= TRYCONFIG) | 
					
						
							|  |  |  | 			newConfig->print("config"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// create new optimization state with more adventurous lambda
 | 
					
						
							| 
									
										
										
										
											2010-01-20 10:28:23 +08:00
										 |  |  | 		NonlinearOptimizer next(graph_, newConfig, solver_, lambda_ / factor); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// if error decreased, return the new state
 | 
					
						
							|  |  |  | 		if (next.error_ <= error_) | 
					
						
							|  |  |  | 			return next; | 
					
						
							|  |  |  | 		else { | 
					
						
							|  |  |  | 			// TODO: can we avoid copying the config ?
 | 
					
						
							| 
									
										
										
										
											2010-01-20 10:28:23 +08:00
										 |  |  | 			NonlinearOptimizer cautious(graph_, config_, solver_, lambda_ * factor); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 			return cautious.try_lambda(linear, verbosity, factor); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							|  |  |  | 	// One iteration of Levenberg Marquardt
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W> NonlinearOptimizer<G, C, L, S, W>::iterateLM( | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 			verbosityLevel verbosity, double lambdaFactor) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// maybe show output
 | 
					
						
							|  |  |  | 		if (verbosity >= CONFIG) | 
					
						
							|  |  |  | 			config_->print("config"); | 
					
						
							|  |  |  | 		if (verbosity >= ERROR) | 
					
						
							|  |  |  | 			cout << "error: " << error_ << endl; | 
					
						
							|  |  |  | 		if (verbosity >= LAMBDA) | 
					
						
							|  |  |  | 			cout << "lambda = " << lambda_ << endl; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// linearize all factors once
 | 
					
						
							| 
									
										
										
										
											2010-01-23 08:57:54 +08:00
										 |  |  | 		boost::shared_ptr<L> linear = solver_->linearize(*graph_, *config_); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 		if (verbosity >= LINEAR) | 
					
						
							| 
									
										
										
										
											2010-01-23 08:57:54 +08:00
										 |  |  | 			linear->print("linear"); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// try lambda steps with successively larger lambda until we achieve descent
 | 
					
						
							| 
									
										
										
										
											2010-01-23 08:57:54 +08:00
										 |  |  | 		return try_lambda(*linear, verbosity, lambdaFactor); | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* ************************************************************************* */ | 
					
						
							| 
									
										
										
										
											2010-01-22 16:13:54 +08:00
										 |  |  | 	template<class G, class C, class L, class S, class W> | 
					
						
							|  |  |  | 	NonlinearOptimizer<G, C, L, S, W> NonlinearOptimizer<G, C, L, S, W>::levenbergMarquardt( | 
					
						
							| 
									
										
										
										
											2009-09-09 12:43:04 +08:00
										 |  |  | 			double relativeThreshold, double absoluteThreshold, | 
					
						
							|  |  |  | 			verbosityLevel verbosity, int maxIterations, double lambdaFactor) const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// do one iteration of LM
 | 
					
						
							|  |  |  | 		NonlinearOptimizer next = iterateLM(verbosity, lambdaFactor); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// check convergence
 | 
					
						
							|  |  |  | 		// TODO: move convergence checks here and incorporate in verbosity levels
 | 
					
						
							|  |  |  | 		// TODO: build into iterations somehow as an instance variable
 | 
					
						
							|  |  |  | 		bool converged = gtsam::check_convergence(relativeThreshold, | 
					
						
							|  |  |  | 				absoluteThreshold, error_, next.error_, verbosity); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		// return converged state or iterate
 | 
					
						
							|  |  |  | 		if (converged || maxIterations <= 1) { | 
					
						
							|  |  |  | 			// maybe show output
 | 
					
						
							|  |  |  | 			if (verbosity >= CONFIG) | 
					
						
							|  |  |  | 				next.config_->print("final config"); | 
					
						
							|  |  |  | 			if (verbosity >= ERROR) | 
					
						
							|  |  |  | 				cout << "final error: " << next.error_ << endl; | 
					
						
							|  |  |  | 			if (verbosity >= LAMBDA) | 
					
						
							|  |  |  | 				cout << "final lambda = " << next.lambda_ << endl; | 
					
						
							|  |  |  | 			return next; | 
					
						
							|  |  |  | 		} else | 
					
						
							|  |  |  | 			return next.levenbergMarquardt(relativeThreshold, absoluteThreshold, | 
					
						
							|  |  |  | 					verbosity, lambdaFactor); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* ************************************************************************* */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } |