forked from pz4kybsvg/Conception
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.
145 lines
5.4 KiB
145 lines
5.4 KiB
#include <gtest/gtest.h>
|
|
|
|
#include "drake/common/test_utilities/eigen_matrix_compare.h"
|
|
#include "drake/solvers/mathematical_program.h"
|
|
#include "drake/solvers/snopt_solver.h"
|
|
#include "drake/solvers/solve.h"
|
|
|
|
namespace drake {
|
|
namespace solvers {
|
|
GTEST_TEST(DiagonallyDominantMatrixConstraint, ReturnYTest) {
|
|
// Test the returned variables Y.
|
|
MathematicalProgram prog;
|
|
auto X = prog.NewSymmetricContinuousVariables<5>();
|
|
auto Y = prog.AddPositiveDiagonallyDominantMatrixConstraint(
|
|
X.cast<symbolic::Expression>());
|
|
EXPECT_EQ(Y.rows(), 5);
|
|
EXPECT_EQ(Y.cols(), 5);
|
|
for (int i = 0; i < 5; ++i) {
|
|
for (int j = 0; j < 5; ++j) {
|
|
if (i != j) {
|
|
EXPECT_EQ(Y(i, j), Y(j, i));
|
|
} else {
|
|
EXPECT_EQ(Y(i, i), X(i, i));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
GTEST_TEST(DiagonallyDominantMatrixConstraint, FeasibilityCheck) {
|
|
MathematicalProgram prog;
|
|
auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
auto Y = prog.AddPositiveDiagonallyDominantMatrixConstraint(
|
|
X.cast<symbolic::Expression>());
|
|
EXPECT_EQ(Y(0, 1), Y(1, 0));
|
|
EXPECT_EQ(Y(0, 0), X(0, 0));
|
|
EXPECT_EQ(Y(1, 1), X(1, 1));
|
|
|
|
auto X_constraint = prog.AddBoundingBoxConstraint(
|
|
Eigen::Vector3d::Zero(), Eigen::Vector3d::Zero(),
|
|
VectorDecisionVariable<3>(X(0, 0), X(0, 1), X(1, 1)));
|
|
|
|
auto set_X_value = [&X_constraint](const Eigen::Vector3d& x_upper_triangle) {
|
|
X_constraint.evaluator()->UpdateLowerBound(x_upper_triangle);
|
|
X_constraint.evaluator()->UpdateUpperBound(x_upper_triangle);
|
|
};
|
|
|
|
// [1 0.9;0.9 2] is diagonally dominant
|
|
set_X_value(Eigen::Vector3d(1, 0.9, 2));
|
|
MathematicalProgramResult result = Solve(prog);
|
|
EXPECT_TRUE(result.is_success());
|
|
|
|
// [1 -0.9; -0.9 2] is diagonally dominant
|
|
set_X_value(Eigen::Vector3d(1, -0.9, 2));
|
|
result = Solve(prog);
|
|
EXPECT_TRUE(result.is_success());
|
|
|
|
// [1 1.1; 1.1 2] is not diagonally dominant
|
|
set_X_value(Eigen::Vector3d(1, 1.1, 2));
|
|
result = Solve(prog);
|
|
EXPECT_FALSE(result.is_success());
|
|
EXPECT_TRUE(
|
|
result.get_solution_result() == SolutionResult::kInfeasibleConstraints ||
|
|
result.get_solution_result() == SolutionResult::kInfeasibleOrUnbounded);
|
|
// [1 -1.1; -1.1 2] is not diagonally dominant
|
|
set_X_value(Eigen::Vector3d(1, -1.1, 2));
|
|
result = Solve(prog);
|
|
EXPECT_FALSE(result.is_success());
|
|
EXPECT_TRUE(
|
|
result.get_solution_result() == SolutionResult::kInfeasibleConstraints ||
|
|
result.get_solution_result() == SolutionResult::kInfeasibleOrUnbounded);
|
|
}
|
|
|
|
GTEST_TEST(DiagonallyDominantMatrixConstraint, three_by_three_vertices) {
|
|
// I can manually compute the polytope of (a, b, c) to make
|
|
// [1 a b]
|
|
// A = [a 2 c]
|
|
// [b c 3]
|
|
// to be a diagonally dominant matrix. The vertices of the polytope are
|
|
// (0, ±1, 0), (±1, 0, 0), (0, ±1, ±2), (±1, 0, ±1), (0, 0, ±2)
|
|
// By optimizing the LP
|
|
// min nᵀ* (a, b, c)
|
|
// s.t A is diagonally dominant
|
|
// with different vector n, we can recover the vertices of the polytope as
|
|
// the optimal solution to this LP.
|
|
MathematicalProgram prog;
|
|
auto X = prog.NewSymmetricContinuousVariables<3>();
|
|
prog.AddBoundingBoxConstraint(Eigen::Vector3d(1, 2, 3),
|
|
Eigen::Vector3d(1, 2, 3), X.diagonal());
|
|
prog.AddPositiveDiagonallyDominantMatrixConstraint(
|
|
X.cast<symbolic::Expression>());
|
|
|
|
auto cost =
|
|
prog.AddLinearCost(Eigen::Vector3d::Zero(), 0,
|
|
VectorDecisionVariable<3>(X(0, 1), X(0, 2), X(1, 2)));
|
|
|
|
auto solve_and_check = [&prog, &X](const Eigen::Vector3d& sol_expected,
|
|
double tol) {
|
|
const auto result = Solve(prog);
|
|
if (result.get_solver_id() != SnoptSolver::id()) {
|
|
// Do not check when we use SNOPT. It is known that our SnoptSolver
|
|
// wrapper doesn't solve this problem correctly, see
|
|
// https://github.com/RobotLocomotion/drake/pull/9382
|
|
// TODO(hongkai.dai): fix the problem in SnoptSolver wrapper and enable
|
|
// this test with Snopt.
|
|
EXPECT_TRUE(result.is_success());
|
|
EXPECT_TRUE(CompareMatrices(result.GetSolution(VectorDecisionVariable<3>(
|
|
X(0, 1), X(0, 2), X(1, 2))),
|
|
sol_expected, tol));
|
|
// The matrix should be positive semidefinite.
|
|
const Eigen::Matrix3d X_sol = result.GetSolution(X);
|
|
Eigen::SelfAdjointEigenSolver<Eigen::Matrix3d> eigen_solver(X_sol);
|
|
EXPECT_TRUE((eigen_solver.eigenvalues().array() >= -tol).all());
|
|
}
|
|
};
|
|
|
|
const double tol{1E-6};
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(1, 0, 0));
|
|
solve_and_check(Eigen::Vector3d(-1, 0, 0), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(-1, 0, 0));
|
|
solve_and_check(Eigen::Vector3d(1, 0, 0), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(0, 1, 0));
|
|
solve_and_check(Eigen::Vector3d(0, -1, 0), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(0, -1, 0));
|
|
solve_and_check(Eigen::Vector3d(0, 1, 0), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(0, 0, 1));
|
|
solve_and_check(Eigen::Vector3d(0, 0, -2), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(0, 0, -1));
|
|
solve_and_check(Eigen::Vector3d(0, 0, 2), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(1, 1, 1));
|
|
solve_and_check(Eigen::Vector3d(0, -1, -2), tol);
|
|
|
|
cost.evaluator()->UpdateCoefficients(Eigen::Vector3d(2, 0, 1));
|
|
solve_and_check(Eigen::Vector3d(-1, 0, -1), tol);
|
|
}
|
|
|
|
} // namespace solvers
|
|
} // namespace drake
|