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 std;
 | 
				
			||||||
using namespace gtsam;
 | 
					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,
 | 
					pair<Vector, Vector> gtsam::solveCQP(const Matrix& B, const Matrix& A,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -6,10 +6,49 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#pragma once
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <boost/optional.hpp>
 | 
				
			||||||
#include <Matrix.h>
 | 
					#include <Matrix.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
namespace gtsam {
 | 
					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
 | 
						 * Basic function that uses LDL factorization to solve a
 | 
				
			||||||
	 * KKT system (state and lagrange multipliers) of the form:
 | 
						 * KKT system (state and lagrange multipliers) of the form:
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -5,6 +5,7 @@
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <limits>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <boost/tuple/tuple.hpp>
 | 
					#include <boost/tuple/tuple.hpp>
 | 
				
			||||||
#include <boost/optional.hpp>
 | 
					#include <boost/optional.hpp>
 | 
				
			||||||
| 
						 | 
					@ -12,9 +13,11 @@
 | 
				
			||||||
#include <CppUnitLite/TestHarness.h>
 | 
					#include <CppUnitLite/TestHarness.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <ConstraintOptimizer.h>
 | 
					#include <ConstraintOptimizer.h>
 | 
				
			||||||
 | 
					#include <smallExample.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
using namespace gtsam;
 | 
					using namespace gtsam;
 | 
				
			||||||
 | 
					using namespace example;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* ************************************************************************* */
 | 
					/* ************************************************************************* */
 | 
				
			||||||
// Example of a single Constrained QP problem from the matlab testCQP.m file.
 | 
					// 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;
 | 
						    prev_dfx = dfx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	    // use bfgs
 | 
					 | 
				
			||||||
		Matrix B = Bi;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	    // solve subproblem
 | 
						    // solve subproblem
 | 
				
			||||||
		Vector delta, lambda;
 | 
							Vector delta, lambda;
 | 
				
			||||||
		boost::tie(delta, lambda) = solveCQP(B, -dcx, dfx, -cx);
 | 
							boost::tie(delta, lambda) = solveCQP(Bi, -dcx, dfx, -cx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// update
 | 
							// update
 | 
				
			||||||
		step = stepsize * delta;
 | 
							step = stepsize * delta;
 | 
				
			||||||
| 
						 | 
					@ -202,6 +202,95 @@ TEST( matrix, SQP_simple_manual_bfgs ) {
 | 
				
			||||||
	CHECK(assert_equal(expLambda, lam, 1e-4));
 | 
						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); }
 | 
					int main() { TestResult tr; return TestRegistry::runAllTests(tr); }
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue