/** * @file testConstraintOptimizer.cpp * @brief Tests the optimization engine for SQP and BFGS Quadratic programming techniques * @author Alex Cunningham */ /** IMPORTANT NOTE: this file is only compiled when LDL is available */ #include #include #include #include #include #include #include #define GTSAM_MAGIC_KEY #include // for operator += using namespace boost::assign; using namespace std; using namespace gtsam; /* ********************************************************************* * Example from SQP testing: * * This example uses a nonlinear objective function and * nonlinear equality constraint. The formulation is actually * the Cholesky form that creates the full Hessian explicitly, * and isn't expecially compatible with our machinery. */ TEST (NonlinearConstraint, problem1_cholesky ) { bool verbose = false; // use a nonlinear function of f(x) = x^2+y^2 // nonlinear equality constraint: g(x) = x^2-5-y=0 // Lagrangian: f(x) + \lambda*g(x) // Symbols Symbol x1("x1"), y1("y1"), L1("L1"); // state structure: [x y \lambda] VectorValues init, state; init.insert(x1, Vector_(1, 1.0)); init.insert(y1, Vector_(1, 1.0)); init.insert(L1, Vector_(1, 1.0)); state = init; if (verbose) init.print("Initial State"); // loop until convergence int maxIt = 10; for (int i = 0; i g = boost::none, boost::optional B = boost::none) { double x1 = x(0), x2 = x(1); if (g) *g = Vector_(2, 2.0*x1, 2.0*(x2-2.0)); if (B) *B = 2.0 * eye(2,2); return (x2-2)*(x2-2) + x1*x1; } /** * constraint function with gradient and hessian * cx = 4*x1^2 + x2^2 - 1; */ Vector constraint(const Vector& x, boost::optional A = boost::none, boost::optional B = boost::none) { double x1 = x(0), x2 = x(1); if (A) *A = Matrix_(2,1, 8.0*x1, 2.0*x2); if (B) *B = Matrix_(2,2, 8.0, 0.0, 0.0, 2.0); return Vector_(1, 4.0*x1*x1 + x2*x2 - 1.0); } /** * evaluates a penalty function at a given point */ double penalty(const Vector& x) { double constraint_gain = 1.0; return objective(x) + constraint_gain*sum(abs(constraint(x))); } } /* ************************************************************************* */ /** SQP example from SQP tutorial (real saddle point) */ namespace sqp_example2 { /** * objective function with gradient and hessian * fx = (x2-2)^2 - x1^2; */ double objective(const Vector& x, boost::optional g = boost::none, boost::optional B = boost::none) { double x1 = x(0), x2 = x(1); if (g) *g = Vector_(2, -2.0*x1, 2.0*(x2-2.0)); if (B) *B = Matrix_(2,2, -2.0, 0.0, 0.0, 2.0); return (x2-2)*(x2-2) - x1*x1; } /** * constraint function with gradient and hessian * cx = 4*x1^2 + x2^2 - 1; */ Vector constraint(const Vector& x, boost::optional A = boost::none, boost::optional B = boost::none) { double x1 = x(0), x2 = x(1); if (A) *A = Matrix_(2,1, 8.0*x1, 2.0*x2); if (B) *B = Matrix_(2,2, 8.0, 0.0, 0.0, 2.0); return Vector_(1, 4.0*x1*x1 + x2*x2 - 1.0); } /** * evaluates a penalty function at a given point */ double penalty(const Vector& x) { double constraint_gain = 1.0; return objective(x) + constraint_gain*sum(abs(constraint(x))); } } /* ************************************************************************* */ TEST( matrix, SQP_simple_analytic ) { 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); // current state Vector x = x0, lam = lam0; for (size_t i =0; i0) { Vector Bis = Bi * step, y = dfx - prev_dfx; Bi = Bi + outer_prod(y, y) / inner_prod(y, step) - outer_prod(Bis, Bis) / inner_prod(step, Bis); } prev_dfx = dfx; // solve subproblem Vector delta, lambda; boost::tie(delta, lambda) = solveCQP(Bi, -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, SQP_simple_bfgs1 ) { using namespace sqp_example1; // parameters size_t maxIt = 25; // 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; i0) { hessian.update(dfx, step); } else { hessian.update(dfx); } // solve subproblem Vector delta, lambda; boost::tie(delta, lambda) = solveCQP(hessian.getB(), -dcx, dfx, -cx); // if (i == 0) print(delta, "delta1"); // update step = linesearch(x,delta,penalty); // 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, SQP_simple_bfgs2 ) { using namespace sqp_example2; // 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; i0) { hessian.update(dfx, step); } else { hessian.update(dfx); } // solve subproblem Vector delta, lambda; boost::tie(delta, lambda) = solveCQP(hessian.getB(), -dcx, dfx, -cx); // if (i == 0) print(delta, "delta2"); // update // step = linesearch(x,delta,penalty); step = stepsize * delta; x = x + step; lam = lambda; } // verify Vector expX = Vector_(2, 0.0, 1.0), expLambda = Vector_(1, -1.0); // should determine the actual values for this one // CHECK(assert_equal(expX, x, 1e-4)); // CHECK(assert_equal(expLambda, lam, 1e-4)); } /* ************************************************************************* */ TEST( matrix, line_search ) { using namespace sqp_example2; // initial conditions Vector x0 = Vector_(2, 2.0, 4.0), delta = Vector_(2, 0.85, -5.575); Vector actual = linesearch(x0,delta,penalty); // check that error goes down double init_error = penalty(x0), final_error = penalty(x0 + actual); //double actual_stepsize = dot(actual, delta)/dot(delta, delta); // cout << "actual_stepsize: " << actual_stepsize << endl; CHECK(final_error <= init_error); } /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr); } /* ************************************************************************* */