You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
252 lines
7.3 KiB
252 lines
7.3 KiB
#include <iomanip>
|
|
#include <iostream>
|
|
|
|
#include <catch2/catch.hpp>
|
|
|
|
#include <autodiff/autodiff_types.hpp>
|
|
#include <opt/constrained_problem.hpp>
|
|
#include <solvers/ncp_solver.hpp>
|
|
#include <utils/not_implemented_error.hpp>
|
|
|
|
#include <logger.hpp>
|
|
|
|
// ---------------------------------------------------
|
|
// SETUP
|
|
// ---------------------------------------------------
|
|
static const int NUM_VARS = 2;
|
|
static const int NUM_CONSTRAINTS = 2;
|
|
|
|
// differentiable helpers
|
|
template <typename T> using VectorXT = Eigen::Matrix<T, Eigen::Dynamic, 1>;
|
|
|
|
typedef Eigen::Matrix<double, 2, 1> Vector2d;
|
|
typedef DScalar1<double, Eigen::Matrix<double, NUM_VARS, 1>> DScalar;
|
|
typedef VectorXT<DScalar> DVector;
|
|
|
|
// ---------------------------------------------------
|
|
// Tests
|
|
// ---------------------------------------------------
|
|
|
|
TEST_CASE("NCP", "[opt][NCP][NCP-Interface]")
|
|
{
|
|
using namespace ccd::opt;
|
|
DiffScalarBase::setVariableCount(size_t(NUM_VARS));
|
|
|
|
Eigen::SparseMatrix<double> A(NUM_VARS, NUM_VARS);
|
|
Eigen::VectorXd b(NUM_VARS), expected(NUM_VARS);
|
|
|
|
std::function<DVector(const Eigen::VectorXd& x)> g_diff;
|
|
|
|
// ------------------------------------------------------------------------
|
|
// PROBLEM SETUP
|
|
// ------------------------------------------------------------------------
|
|
A.setIdentity();
|
|
b << -1, -2.5;
|
|
|
|
SECTION("Linear Case")
|
|
{
|
|
g_diff = [](const Eigen::VectorXd& x) -> DVector {
|
|
DVector gx(NUM_CONSTRAINTS);
|
|
DScalar x0(0, x[0]);
|
|
DScalar x1(1, x[1]);
|
|
|
|
gx(0) = x0;
|
|
gx(1) = x1;
|
|
return gx;
|
|
};
|
|
expected << 0.0, 0.0;
|
|
}
|
|
|
|
SECTION("Quadratic Case")
|
|
{
|
|
g_diff = [](const Eigen::VectorXd& x) -> DVector {
|
|
DVector gx(NUM_CONSTRAINTS);
|
|
DScalar x0(0, x[0]);
|
|
DScalar x1(1, x[1]);
|
|
|
|
gx(0) = 0.04 - x0 * x0;
|
|
gx(1) = 0.09 - x1 * x1;
|
|
return gx;
|
|
};
|
|
expected << -0.2, -0.3;
|
|
}
|
|
|
|
SECTION("Abs Value Case")
|
|
{
|
|
|
|
g_diff = [](const Eigen::VectorXd& x) -> DVector {
|
|
DVector gx(NUM_CONSTRAINTS);
|
|
DScalar x0(0, x[0]);
|
|
DScalar x1(1, x[1]);
|
|
|
|
gx(0) = 0.2 - (x0 > 0 ? x0 : -x0);
|
|
gx(1) = 0.3 - (x1 > 0 ? x1 : -x1);
|
|
return gx;
|
|
};
|
|
expected << -0.2, -0.3;
|
|
}
|
|
|
|
SECTION("Circle Case")
|
|
{
|
|
g_diff = [](const Eigen::VectorXd& x) -> DVector {
|
|
DVector gx(NUM_CONSTRAINTS);
|
|
DScalar x0(0, x[0]);
|
|
DScalar x1(1, x[1]);
|
|
|
|
gx(0) = 1.0 - (x0 - 1.0) * (x0 - 1.0);
|
|
gx(1) = 1.0 - (x1 - 2.5) * (x1 - 2.5);
|
|
return gx;
|
|
};
|
|
expected << 0.0, 1.5;
|
|
}
|
|
|
|
class AdHocProblem : public virtual ConstrainedProblem {
|
|
public:
|
|
AdHocProblem(
|
|
Eigen::SparseMatrix<double>& _A,
|
|
Eigen::VectorXd& _b,
|
|
std::function<DVector(const Eigen::VectorXd& x)>& _gdiff)
|
|
: A(_A)
|
|
, b(_b)
|
|
, gdiff(_gdiff)
|
|
{
|
|
is_dof_fixed_ = Eigen::VectorXb::Zero(NUM_VARS);
|
|
}
|
|
|
|
virtual double compute_objective(
|
|
const Eigen::VectorXd& x,
|
|
Eigen::VectorXd& grad_fx,
|
|
Eigen::SparseMatrix<double>& hess_fx,
|
|
bool compute_grad = true,
|
|
bool compute_hess = true) override
|
|
{
|
|
grad_fx = A * x - b;
|
|
if (compute_hess) {
|
|
hess_fx = A;
|
|
}
|
|
return (grad_fx).squaredNorm() / 2.0;
|
|
}
|
|
|
|
virtual void compute_constraints(
|
|
const Eigen::VectorXd& x,
|
|
Eigen::VectorXd& gx,
|
|
Eigen::MatrixXd& jac_gx,
|
|
std::vector<Eigen::SparseMatrix<double>>& hess_gx,
|
|
bool compute_grad = true,
|
|
bool compute_hess = true) override
|
|
{
|
|
DVector g = gdiff(x);
|
|
gx = Eigen::VectorXd(g.rows());
|
|
jac_gx = Eigen::MatrixXd(gx.rows(), NUM_VARS);
|
|
for (int i = 0; i < g.rows(); ++i) {
|
|
gx(i) = g(i).getValue();
|
|
jac_gx.row(i) = g(i).getGradient();
|
|
}
|
|
|
|
if (compute_hess) {
|
|
throw "not computing hess_gx";
|
|
}
|
|
}
|
|
|
|
using ConstrainedProblem::compute_constraints;
|
|
|
|
void compute_constraints_using_normals(
|
|
const Eigen::VectorXd& x,
|
|
Eigen::VectorXd& gx,
|
|
Eigen::MatrixXd& jac_gx) override
|
|
{
|
|
return compute_constraints(x, gx, jac_gx);
|
|
}
|
|
|
|
const Eigen::VectorXd& starting_point() const { return b; }
|
|
const Eigen::VectorXb& is_dof_fixed() const override
|
|
{
|
|
return is_dof_fixed_;
|
|
}
|
|
|
|
virtual int num_vars() const override { return NUM_VARS; }
|
|
|
|
virtual bool has_collisions(
|
|
const Eigen::VectorXd& xi, const Eigen::VectorXd& xj) override
|
|
{
|
|
return false;
|
|
}
|
|
virtual double compute_earliest_toi(
|
|
const Eigen::VectorXd& xi, const Eigen::VectorXd& xj) override
|
|
{
|
|
return std::numeric_limits<double>::infinity();
|
|
}
|
|
virtual bool is_ccd_aligned_with_newton_update() override
|
|
{
|
|
return true;
|
|
}
|
|
virtual double
|
|
compute_min_distance(const Eigen::VectorXd& x) const override
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/// Get the world coordinates of the vertices
|
|
Eigen::MatrixXd world_vertices(const Eigen::VectorXd& x) const override
|
|
{
|
|
throw ccd::NotImplementedError("no vertices");
|
|
}
|
|
|
|
/// Get the length of the diagonal of the worlds bounding box
|
|
double world_bbox_diagonal() const override
|
|
{
|
|
throw ccd::NotImplementedError("no world bbox diagonal");
|
|
}
|
|
|
|
Eigen::DiagonalMatrixXd mass_matrix() const override
|
|
{
|
|
Eigen::DiagonalMatrixXd I(NUM_VARS);
|
|
I.setIdentity();
|
|
return I;
|
|
}
|
|
double average_mass() const override { return 1; }
|
|
|
|
double timestep() const override { return 1; }
|
|
|
|
Eigen::SparseMatrix<double> A;
|
|
Eigen::VectorXd b;
|
|
std::function<DVector(const Eigen::VectorXd& x)> gdiff;
|
|
Eigen::VectorXb is_dof_fixed_;
|
|
};
|
|
|
|
Eigen::VectorXd x(NUM_VARS), alpha(NUM_CONSTRAINTS);
|
|
AdHocProblem problem(A, b, g_diff);
|
|
|
|
NCPSolver solver;
|
|
solver.set_problem(problem);
|
|
solver.max_iterations = 3000;
|
|
solver.convergence_tolerance = 1E-8;
|
|
solver.do_line_search = false;
|
|
solver.solve_for_active_cstr = false;
|
|
solver.update_type = NCPUpdate::G_GRADIENT;
|
|
|
|
OptimizationResults results;
|
|
|
|
// Solve using Guass-Seidel
|
|
solver.lcp_solver = LCPSolver::LCP_GAUSS_SEIDEL;
|
|
results = solver.solve(x);
|
|
CHECK(results.finished);
|
|
CHECK(results.success);
|
|
CHECK((expected - results.x).squaredNorm() < 1E-6);
|
|
|
|
// Solve using Fischer-Newton
|
|
solver.lcp_solver = LCPSolver::LCP_NEWTON;
|
|
results = solver.solve(x);
|
|
CHECK(results.finished);
|
|
CHECK(results.success);
|
|
CHECK((expected - results.x).squaredNorm() < 1E-6);
|
|
|
|
#ifdef BUILD_WITH_MOSEK
|
|
// Solve using Mosek QP
|
|
solver.lcp_solver = LCPSolver::LCP_MOSEK;
|
|
results = solver.solve(x);
|
|
CHECK(results.finished);
|
|
CHECK(results.success);
|
|
CHECK((expected - results.x).squaredNorm() < 1E-6);
|
|
#endif
|
|
}
|
|
|