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
|
// solve subproblem
|
||||||
Matrix B = Bi;
|
|
||||||
|
|
||||||
// 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