/** * @file testConstraintOptimizer.cpp * @brief Tests the optimization engine for SQP and BFGS Quadratic programming techniques * @author Alex Cunningham */ #include #include #include #include #include #include #include #define GTSAM_MAGIC_KEY #include // for operator += using namespace boost::assign; using namespace std; using namespace gtsam; #include using namespace example; /* ************************************************************************* */ TEST( matrix, unconstrained_fg_ata ) { // create a graph GaussianFactorGraph fg = createGaussianFactorGraph(); Matrix A; Vector b; Ordering ordering; ordering += Symbol('l', 1), Symbol('x', 1), Symbol('x', 2); boost::tie(A, b) = fg.matrix(ordering); Matrix B_ata = prod(trans(A), A); // solve subproblem Vector actual = solve_ldl(B_ata, prod(trans(A), b)); // verify Vector expected = createCorrectDelta().vector(); CHECK(assert_equal(expected,actual)); } ///* ************************************************************************* */ //TEST( matrix, unconstrained_fg ) { // // create a graph // GaussianFactorGraph fg = createGaussianFactorGraph(); // // Matrix A; Vector b; // Ordering ordering; // ordering += Symbol('l', 1), Symbol('x', 1), Symbol('x', 2); // boost::tie(A, b) = fg.matrix(ordering); // Matrix B_ata = prod(trans(A), A); //// print(B_ata, "B_ata"); //// print(b, " b"); // // // parameters // size_t maxIt = 50; // double stepsize = 0.1; // // // iterate to solve // VectorConfig x = createZeroDelta(); // BFGSEstimator B(x.dim()); // // Vector step; // // for (size_t i=0; i0) { // B.update(dfx, step); // } else { // B.update(dfx); // } // // // solve subproblem //// print(B.getB(), " B_bfgs"); // Vector delta = solve_ldl(B.getB(), -dfx); //// Vector delta = solve_ldl(B_ata, -dfx); // //// print(delta, " delta"); // // // update // step = stepsize * delta; //// step = linesearch(x, delta, penalty); // TODO: switch here // x = expmap(x, step); //// print(step, " step"); // } // // // verify // VectorConfig expected = createCorrectDelta(); // CHECK(assert_equal(expected,x, 1e-4)); //} SharedDiagonal probModel1 = sharedSigma(1,1.0); SharedDiagonal probModel2 = sharedSigma(2,1.0); SharedDiagonal constraintModel1 = noiseModel::Constrained::All(1); /* ********************************************************************* * This example uses a nonlinear objective function and * nonlinear equality constraint. The formulation is actually * the Cholesky form that creates the full Hessian explicitly, * which should really be avoided with our QR-based machinery. * * Note: the update equation used here has a fixed step size * and gain that is rather arbitrarily chosen, and as such, * will take a silly number of iterations. */ TEST (SQP, 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] VectorConfig 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 ||Ax-b||^2 * where: * h(x) simply returns the inputs * z zeros(2) * A identity * b linearization point */ Matrix A = eye(2); Vector b = Vector_(2, x, y); GaussianFactor::shared_ptr f1( new GaussianFactor(x1, sub(A, 0,2, 0,1), // A(:,1) y1, sub(A, 0,2, 1,2), // A(:,2) b, // rhs of f(x) probModel2)); // arbitrary sigma /** create the constraint-linear factor * Provides a mechanism to use variable gain to force the constraint * \lambda*gradG*dx + d\lambda = zero * formulated in matrix form as: * [\lambda*gradG eye(1)] [dx; d\lambda] = zero */ Matrix gradG = Matrix_(1, 2,2*x, -1.0); GaussianFactor::shared_ptr f2( new GaussianFactor(x1, lambda*sub(gradG, 0,1, 0,1), // scaled gradG(:,1) y1, lambda*sub(gradG, 0,1, 1,2), // scaled gradG(:,2) L1, eye(1), // dlambda term Vector_(1, 0.0), // rhs is zero probModel1)); // arbitrary sigma // create the actual constraint // [gradG] [x; y] - g = 0 Vector g = Vector_(1,x*x-y-5); GaussianFactor::shared_ptr c1( new GaussianFactor(x1, sub(gradG, 0,1, 0,1), // slice first part of gradG y1, sub(gradG, 0,1, 1,2), // slice second part of gradG g, // value of constraint function constraintModel1)); // force to constraint // construct graph GaussianFactorGraph fg; fg.push_back(f1); fg.push_back(f2); fg.push_back(c1); if (verbose) fg.print("Graph"); // solve Ordering ord; ord += x1, y1, L1; VectorConfig delta = fg.optimize(ord); if (verbose) delta.print("Delta"); // update initial estimate VectorConfig newState = expmap(state, delta.scale(-1.0)); // set the state to the updated state state = newState; if (verbose) state.print("Updated State"); } // verify that it converges to the nearest optimal point VectorConfig expected; expected.insert(x1, Vector_(1, 2.12)); expected.insert(y1, Vector_(1, -0.5)); CHECK(assert_equal(state[x1], expected[x1], 1e-2)); CHECK(assert_equal(state[y1], expected[y1], 1e-2)); } /* ************************************************************************* */ int main() { TestResult tr; return TestRegistry::runAllTests(tr); } /* ************************************************************************* */