Added BFGS class, as well as a (currently failing) test in testConstraintOptimizer that uses the LDL machinery to solve an unconstrained example
							parent
							
								
									9c97550218
								
							
						
					
					
						commit
						66caac3c1c
					
				|  | @ -8,6 +8,16 @@ | |||
| using namespace std; | ||||
| using namespace gtsam; | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| void gtsam::BFGSEstimator::update(const Vector& dfx, const boost::optional<Vector&> step) { | ||||
| 	if (step) { | ||||
| 		Vector Bis = B_ * *step, | ||||
| 				y = dfx - prev_dfx_; | ||||
| 		B_ = B_ + outer_prod(y, y) / inner_prod(y, *step) | ||||
|                 - outer_prod(Bis, Bis) / inner_prod(*step, Bis); | ||||
| 	} | ||||
| 	prev_dfx_ = dfx; | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| pair<Vector, Vector> gtsam::solveCQP(const Matrix& B, const Matrix& A, | ||||
|  |  | |||
|  | @ -6,10 +6,49 @@ | |||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include <boost/optional.hpp> | ||||
| #include <Matrix.h> | ||||
| 
 | ||||
| namespace gtsam { | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * BFGS Hessian estimator | ||||
| 	 * Maintains an estimate for a hessian matrix using | ||||
| 	 * only derivatives and the step | ||||
| 	 */ | ||||
| 	class BFGSEstimator { | ||||
| 	protected: | ||||
| 		size_t n_; // dimension of square matrix
 | ||||
| 		Matrix B_; // current estimate
 | ||||
| 		Vector prev_dfx_; // previous gradient value
 | ||||
| 	public: | ||||
| 
 | ||||
| 		/**
 | ||||
| 		 * Creates an estimator of a particular dimension | ||||
| 		 */ | ||||
| 		BFGSEstimator(size_t n) : n_(n), B_(eye(2,2)) {} | ||||
| 
 | ||||
| 		~BFGSEstimator() {} | ||||
| 
 | ||||
| 		/**
 | ||||
| 		 * Direct vector interface - useful for small problems | ||||
| 		 * | ||||
| 		 * Update will set the previous gradient and update the | ||||
| 		 * estimate for the Hessian.  When specified without | ||||
| 		 * the step, this is the initialization phase, and will not | ||||
| 		 * change the Hessian estimate | ||||
| 		 * @param df is the gradient of the function | ||||
| 		 * @param step is the step vector applied at the last iteration | ||||
| 		 */ | ||||
| 		void update(const Vector& df, const boost::optional<Vector&> step = boost::none); | ||||
| 
 | ||||
| 		// access functions
 | ||||
| 		const Matrix& getB() const { return B_; } | ||||
| 		size_t dim() const { return n_; } | ||||
| 
 | ||||
| 	}; | ||||
| 
 | ||||
| 
 | ||||
| 	/**
 | ||||
| 	 * Basic function that uses LDL factorization to solve a | ||||
| 	 * KKT system (state and lagrange multipliers) of the form: | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
|  */ | ||||
| 
 | ||||
| #include <iostream> | ||||
| #include <limits> | ||||
| 
 | ||||
| #include <boost/tuple/tuple.hpp> | ||||
| #include <boost/optional.hpp> | ||||
|  | @ -12,9 +13,11 @@ | |||
| #include <CppUnitLite/TestHarness.h> | ||||
| 
 | ||||
| #include <ConstraintOptimizer.h> | ||||
| #include <smallExample.h> | ||||
| 
 | ||||
| using namespace std; | ||||
| using namespace gtsam; | ||||
| using namespace example; | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| // Example of a single Constrained QP problem from the matlab testCQP.m file.
 | ||||
|  | @ -181,12 +184,9 @@ TEST( matrix, SQP_simple_manual_bfgs ) { | |||
| 	    } | ||||
| 	    prev_dfx = dfx; | ||||
| 
 | ||||
| 	    // use bfgs
 | ||||
| 		Matrix B = Bi; | ||||
| 
 | ||||
| 	    // solve subproblem
 | ||||
| 		Vector delta, lambda; | ||||
| 		boost::tie(delta, lambda) = solveCQP(B, -dcx, dfx, -cx); | ||||
| 		boost::tie(delta, lambda) = solveCQP(Bi, -dcx, dfx, -cx); | ||||
| 
 | ||||
| 		// update
 | ||||
| 		step = stepsize * delta; | ||||
|  | @ -202,6 +202,95 @@ TEST( matrix, SQP_simple_manual_bfgs ) { | |||
| 	CHECK(assert_equal(expLambda, lam, 1e-4)); | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| TEST( matrix, SQP_simple_bfgs ) { | ||||
| 	using namespace sqp_example1; | ||||
| 
 | ||||
| 	// parameters
 | ||||
| 	double stepsize = 0.25; | ||||
| 	size_t maxIt = 50; | ||||
| 
 | ||||
| 	// initial conditions
 | ||||
| 	Vector x0 = Vector_(2, 2.0, 4.0), | ||||
| 		   lam0 = Vector_(1, -0.5); | ||||
| 
 | ||||
| 	// create a BFGSEstimator
 | ||||
| 	BFGSEstimator hessian(2); | ||||
| 
 | ||||
| 	// current state
 | ||||
| 	Vector x = x0, lam = lam0; | ||||
| 	Vector step; | ||||
| 
 | ||||
| 	for (size_t i=0; i<maxIt; ++i) { | ||||
| 
 | ||||
| 		// evaluate functions
 | ||||
| 		Vector dfx; Matrix dcx; | ||||
| 		Vector fx = objective(x, dfx); | ||||
| 		Vector cx = constraint(x, dcx); | ||||
| 
 | ||||
| 		// Just use dfx for the Hessian
 | ||||
| 	    if (i>0) { | ||||
| 	    	hessian.update(dfx, step); | ||||
| 	    } else { | ||||
| 	    	hessian.update(dfx); | ||||
| 	    } | ||||
| 
 | ||||
| 		// solve subproblem
 | ||||
| 		Vector delta, lambda; | ||||
| 		boost::tie(delta, lambda) = solveCQP(hessian.getB(), -dcx, dfx, -cx); | ||||
| 
 | ||||
| 		// update
 | ||||
| 		step = stepsize * delta; | ||||
| 		x = x + step; | ||||
| 		lam = lambda; | ||||
| 	} | ||||
| 
 | ||||
| 	// verify
 | ||||
| 	Vector expX = Vector_(2, 0.0, 1.0), | ||||
| 		   expLambda = Vector_(1, -1.0); | ||||
| 
 | ||||
| 	CHECK(assert_equal(expX, x, 1e-4)); | ||||
| 	CHECK(assert_equal(expLambda, lam, 1e-4)); | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| TEST( matrix, unconstrained_fg ) { | ||||
| 	// create a graph
 | ||||
| 	GaussianFactorGraph fg = createGaussianFactorGraph(); | ||||
| 
 | ||||
| 	// parameters
 | ||||
| 	size_t maxIt = 5; | ||||
| 	double stepsize = 0.1; | ||||
| 
 | ||||
| 	// iterate to solve
 | ||||
| 	VectorConfig x = createZeroDelta(); | ||||
| 	BFGSEstimator B(x.dim()); | ||||
| 
 | ||||
| 	Vector step; | ||||
| 
 | ||||
| 	for (size_t i=0; i<maxIt; ++i) { | ||||
| 		// find the gradient
 | ||||
| 		Vector dfx = fg.gradient(x).vector(); | ||||
| 
 | ||||
| 		// update hessian
 | ||||
| 	    if (i>0) { | ||||
| 	    	B.update(dfx, step); | ||||
| 	    } else { | ||||
| 	    	B.update(dfx); | ||||
| 	    } | ||||
| 
 | ||||
| 	    // solve subproblem
 | ||||
| 	    Vector delta = solve_ldl(B.getB(), -dfx); | ||||
| 
 | ||||
| 	    // update
 | ||||
| 		step = stepsize * delta; | ||||
| 	    x = x.vectorUpdate(step); | ||||
| 	} | ||||
| 
 | ||||
| 	// verify
 | ||||
| 	VectorConfig expected = createCorrectDelta(); | ||||
| 	CHECK(assert_equal(expected,x)); | ||||
| } | ||||
| 
 | ||||
| /* ************************************************************************* */ | ||||
| int main() { TestResult tr; return TestRegistry::runAllTests(tr); } | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue