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.
413 lines
17 KiB
413 lines
17 KiB
2 years ago
|
#include "drake/solvers/aggregate_costs_constraints.h"
|
||
|
|
||
|
#include <limits>
|
||
|
|
||
|
#include <fmt/format.h>
|
||
|
#include <gmock/gmock.h>
|
||
|
#include <gtest/gtest.h>
|
||
|
|
||
|
#include "drake/common/test_utilities/eigen_matrix_compare.h"
|
||
|
#include "drake/common/test_utilities/symbolic_test_util.h"
|
||
|
|
||
|
using ::testing::HasSubstr;
|
||
|
|
||
|
namespace drake {
|
||
|
namespace solvers {
|
||
|
const double kInf = std::numeric_limits<double>::infinity();
|
||
|
class TestAggregateCostsAndConstraints : public ::testing::Test {
|
||
|
public:
|
||
|
TestAggregateCostsAndConstraints() {
|
||
|
for (int i = 0; i < 4; ++i) {
|
||
|
x_.push_back(symbolic::Variable(fmt::format("x{}", i)));
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
std::vector<symbolic::Variable> x_;
|
||
|
};
|
||
|
|
||
|
TEST_F(TestAggregateCostsAndConstraints, TestQuadraticCostsOnly) {
|
||
|
// Test a single quadratic cost.
|
||
|
std::vector<Binding<QuadraticCost>> quadratic_costs;
|
||
|
Eigen::Matrix2d Q1;
|
||
|
// clang-format off
|
||
|
Q1 << 1, 2,
|
||
|
2, 3;
|
||
|
// clang-format on
|
||
|
const Eigen::Vector2d b1(5, 0.);
|
||
|
const double c1{2};
|
||
|
const Vector2<symbolic::Variable> vars1(x_[0], x_[2]);
|
||
|
quadratic_costs.emplace_back(std::make_shared<QuadraticCost>(Q1, b1, c1),
|
||
|
vars1);
|
||
|
|
||
|
Eigen::SparseMatrix<double> Q_lower;
|
||
|
Eigen::SparseVector<double> linear_coeff;
|
||
|
VectorX<symbolic::Variable> quadratic_vars;
|
||
|
VectorX<symbolic::Variable> linear_vars;
|
||
|
double constant_cost;
|
||
|
AggregateQuadraticAndLinearCosts(quadratic_costs, {}, &Q_lower,
|
||
|
&quadratic_vars, &linear_coeff, &linear_vars,
|
||
|
&constant_cost);
|
||
|
EXPECT_EQ(quadratic_vars.rows(), 2);
|
||
|
EXPECT_EQ(quadratic_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(1).get_id(), x_[2].get_id());
|
||
|
EXPECT_TRUE(
|
||
|
CompareMatrices(Eigen::MatrixXd(Q_lower),
|
||
|
Eigen::MatrixXd(Q1.triangularView<Eigen::Lower>())));
|
||
|
EXPECT_TRUE(CompareMatrices(Eigen::VectorXd(linear_coeff), Vector1d(5)));
|
||
|
EXPECT_EQ(linear_vars.rows(), 1);
|
||
|
EXPECT_EQ(linear_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(constant_cost, c1);
|
||
|
|
||
|
// Now test multiple costs. These costs share variables.
|
||
|
Eigen::Matrix3d Q2;
|
||
|
// clang-format off
|
||
|
Q2 << 10, 20, 30,
|
||
|
20, 40, 0,
|
||
|
30, 0, 50;
|
||
|
// clang-format on
|
||
|
const Eigen::Vector3d b2(10, 0, 50);
|
||
|
const double c2 = 40;
|
||
|
const Vector3<symbolic::Variable> vars2(x_[2], x_[1], x_[3]);
|
||
|
quadratic_costs.emplace_back(std::make_shared<QuadraticCost>(Q2, b2, c2),
|
||
|
vars2);
|
||
|
AggregateQuadraticAndLinearCosts(quadratic_costs, {}, &Q_lower,
|
||
|
&quadratic_vars, &linear_coeff, &linear_vars,
|
||
|
&constant_cost);
|
||
|
EXPECT_EQ(quadratic_vars.rows(), 4);
|
||
|
EXPECT_EQ(quadratic_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(1).get_id(), x_[2].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(2).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(3).get_id(), x_[3].get_id());
|
||
|
Eigen::Matrix4d Q_expected;
|
||
|
// [x(0); x(2)] * Q1 * [x0; x(2)] + [x(2), x(1), x(3)] * Q2 * [x(2), x(1),
|
||
|
// x(3)] = [x(0); x(2); x(1); x(3)] * Q_expected * [x(0); x(2); x(1); x(3)]
|
||
|
// clang-format off
|
||
|
Q_expected << 1, 0, 0, 0,
|
||
|
2, 13, 0, 0,
|
||
|
0, 20, 40, 0,
|
||
|
0, 30, 0, 50;
|
||
|
// clang-format on
|
||
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(Q_lower), Q_expected));
|
||
|
EXPECT_TRUE(CompareMatrices(Eigen::VectorXd(linear_coeff),
|
||
|
Eigen::Vector3d(5, 10, 50)));
|
||
|
EXPECT_EQ(linear_vars.rows(), 3);
|
||
|
EXPECT_EQ(linear_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(linear_vars(1).get_id(), x_[2].get_id());
|
||
|
EXPECT_EQ(linear_vars(2).get_id(), x_[3].get_id());
|
||
|
EXPECT_EQ(constant_cost, c1 + c2);
|
||
|
|
||
|
// Add another quadratic cost.
|
||
|
Eigen::Matrix2d Q3;
|
||
|
Q3 << 0, 0, 0, 1;
|
||
|
const Eigen::Vector2d b3(0.1, 0.5);
|
||
|
const double c3 = 0.5;
|
||
|
const Vector2<symbolic::Variable> vars3(x_[2], x_[1]);
|
||
|
quadratic_costs.emplace_back(std::make_shared<QuadraticCost>(Q3, b3, c3),
|
||
|
vars3);
|
||
|
AggregateQuadraticAndLinearCosts(quadratic_costs, {}, &Q_lower,
|
||
|
&quadratic_vars, &linear_coeff, &linear_vars,
|
||
|
&constant_cost);
|
||
|
// Check if the aggregated cost (in symbolic form) is correct.
|
||
|
EXPECT_PRED2(
|
||
|
symbolic::test::ExprEqual,
|
||
|
quadratic_vars
|
||
|
.dot(Eigen::MatrixXd(
|
||
|
Eigen::MatrixXd(Q_lower).selfadjointView<Eigen::Lower>()) *
|
||
|
quadratic_vars)
|
||
|
.Expand(),
|
||
|
(vars1.dot(Q1 * vars1) + vars2.dot(Q2 * vars2) + vars3.dot(Q3 * vars3))
|
||
|
.Expand());
|
||
|
|
||
|
EXPECT_PRED2(symbolic::test::ExprEqual,
|
||
|
Eigen::VectorXd(linear_coeff).dot(linear_vars),
|
||
|
(b1.dot(vars1) + b2.dot(vars2) + b3.dot(vars3)).Expand());
|
||
|
EXPECT_EQ(constant_cost, c1 + c2 + c3);
|
||
|
}
|
||
|
|
||
|
TEST_F(TestAggregateCostsAndConstraints, LinearCostsOnly) {
|
||
|
// Test a single cost. This cost has a sparse linear coefficient.
|
||
|
std::vector<Binding<LinearCost>> linear_costs;
|
||
|
const Eigen::Vector3d a1(1, 0, 2);
|
||
|
const double b1{3};
|
||
|
const Vector3<symbolic::Variable> var1(x_[0], x_[2], x_[1]);
|
||
|
linear_costs.emplace_back(std::make_shared<LinearCost>(a1, b1), var1);
|
||
|
Eigen::SparseVector<double> linear_coeff;
|
||
|
VectorX<symbolic::Variable> vars;
|
||
|
double constant_cost;
|
||
|
AggregateLinearCosts(linear_costs, &linear_coeff, &vars, &constant_cost);
|
||
|
EXPECT_TRUE(
|
||
|
CompareMatrices(Eigen::VectorXd(linear_coeff), Eigen::Vector2d(1, 2)));
|
||
|
EXPECT_EQ(vars.rows(), 2);
|
||
|
EXPECT_EQ(vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(vars(1).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(constant_cost, 3);
|
||
|
|
||
|
// Add a second cost. This second cost shares some variables with the first
|
||
|
// cost.
|
||
|
const Eigen::Vector3d a2(0, 20, 30);
|
||
|
const double b2{40};
|
||
|
const Vector3<symbolic::Variable> var2(x_[2], x_[0], x_[1]);
|
||
|
linear_costs.emplace_back(std::make_shared<LinearCost>(a2, b2), var2);
|
||
|
AggregateLinearCosts(linear_costs, &linear_coeff, &vars, &constant_cost);
|
||
|
EXPECT_TRUE(
|
||
|
CompareMatrices(Eigen::VectorXd(linear_coeff), Eigen::Vector2d(21, 32)));
|
||
|
EXPECT_EQ(vars.rows(), 2);
|
||
|
EXPECT_EQ(vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(vars(1).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(constant_cost, 43);
|
||
|
// Check if the symbolic expression is the same.
|
||
|
EXPECT_PRED2(symbolic::test::ExprEqual,
|
||
|
Eigen::VectorXd(linear_coeff).dot(vars),
|
||
|
(a1.dot(var1) + a2.dot(var2)).Expand());
|
||
|
}
|
||
|
|
||
|
TEST_F(TestAggregateCostsAndConstraints, QuadraticAndLinearCosts) {
|
||
|
std::vector<Binding<QuadraticCost>> quadratic_costs;
|
||
|
std::vector<Binding<LinearCost>> linear_costs;
|
||
|
// One quadratic and one linear cost.
|
||
|
Eigen::Matrix3d Q1;
|
||
|
// clang-format off
|
||
|
Q1 << 1, 2, 0,
|
||
|
2, 3, 4,
|
||
|
0, 4, 5;
|
||
|
// clang-format on
|
||
|
const Eigen::Vector3d b1(1, 0, 2);
|
||
|
const double c1{3};
|
||
|
const Vector3<symbolic::Variable> vars1(x_[0], x_[2], x_[1]);
|
||
|
quadratic_costs.emplace_back(std::make_shared<QuadraticCost>(Q1, b1, c1),
|
||
|
vars1);
|
||
|
|
||
|
const Eigen::Vector3d b2(2, -1, 0);
|
||
|
const double c2{1};
|
||
|
const Vector3<symbolic::Variable> vars2(x_[0], x_[1], x_[3]);
|
||
|
linear_costs.emplace_back(std::make_shared<LinearCost>(b2, c2), vars2);
|
||
|
|
||
|
Eigen::SparseMatrix<double> Q_lower;
|
||
|
VectorX<symbolic::Variable> quadratic_vars, linear_vars;
|
||
|
Eigen::SparseVector<double> linear_coeff;
|
||
|
double constant_cost;
|
||
|
AggregateQuadraticAndLinearCosts(quadratic_costs, linear_costs, &Q_lower,
|
||
|
&quadratic_vars, &linear_coeff, &linear_vars,
|
||
|
&constant_cost);
|
||
|
EXPECT_TRUE(
|
||
|
CompareMatrices(Eigen::MatrixXd(Q_lower),
|
||
|
Eigen::MatrixXd(Q1.triangularView<Eigen::Lower>())));
|
||
|
EXPECT_EQ(quadratic_vars.rows(), 3);
|
||
|
EXPECT_EQ(quadratic_vars(0).get_id(), vars1(0).get_id());
|
||
|
EXPECT_EQ(quadratic_vars(1).get_id(), vars1(1).get_id());
|
||
|
EXPECT_EQ(quadratic_vars(2).get_id(), vars1(2).get_id());
|
||
|
EXPECT_TRUE(
|
||
|
CompareMatrices(Eigen::VectorXd(linear_coeff), Eigen::Vector2d(3, 1)));
|
||
|
EXPECT_EQ(linear_vars.rows(), 2);
|
||
|
EXPECT_EQ(linear_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(linear_vars(1).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(constant_cost, c1 + c2);
|
||
|
|
||
|
// More quadratic and linear costs.
|
||
|
Eigen::Matrix2d Q3;
|
||
|
// clang-format off
|
||
|
Q3 << 10, 30,
|
||
|
30, 40;
|
||
|
// clang-format on
|
||
|
const Eigen::Vector2d b3(20, 30);
|
||
|
const double c3{40};
|
||
|
const Vector2<symbolic::Variable> vars3(x_[3], x_[1]);
|
||
|
quadratic_costs.emplace_back(std::make_shared<QuadraticCost>(Q3, b3, c3),
|
||
|
vars3);
|
||
|
const Eigen::Vector4d b4(20, 0, 30, 40);
|
||
|
const double c4{10};
|
||
|
const Vector4<symbolic::Variable> vars4(x_[2], x_[1], x_[3], x_[0]);
|
||
|
linear_costs.emplace_back(std::make_shared<LinearCost>(b4, c4), vars4);
|
||
|
AggregateQuadraticAndLinearCosts(quadratic_costs, linear_costs, &Q_lower,
|
||
|
&quadratic_vars, &linear_coeff, &linear_vars,
|
||
|
&constant_cost);
|
||
|
Eigen::Matrix4d Q_lower_expected;
|
||
|
// clang-format off
|
||
|
Q_lower_expected << 1, 0, 0, 0,
|
||
|
2, 3, 0, 0,
|
||
|
0, 4, 45, 0,
|
||
|
0, 0, 30, 10;
|
||
|
// clang-format on
|
||
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(Q_lower),
|
||
|
Eigen::MatrixXd(Q_lower_expected)));
|
||
|
EXPECT_EQ(quadratic_vars.rows(), 4);
|
||
|
EXPECT_EQ(quadratic_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(1).get_id(), x_[2].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(2).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(quadratic_vars(3).get_id(), x_[3].get_id());
|
||
|
|
||
|
EXPECT_TRUE(CompareMatrices(Eigen::VectorXd(linear_coeff),
|
||
|
Eigen::Vector4d(43, 31, 50, 20)));
|
||
|
EXPECT_EQ(linear_vars.rows(), 4);
|
||
|
EXPECT_EQ(linear_vars(0).get_id(), x_[0].get_id());
|
||
|
EXPECT_EQ(linear_vars(1).get_id(), x_[1].get_id());
|
||
|
EXPECT_EQ(linear_vars(2).get_id(), x_[3].get_id());
|
||
|
EXPECT_EQ(linear_vars(3).get_id(), x_[2].get_id());
|
||
|
EXPECT_EQ(constant_cost, c1 + c2 + c3 + c4);
|
||
|
}
|
||
|
|
||
|
TEST_F(TestAggregateCostsAndConstraints, AggregateBoundingBoxConstraints1) {
|
||
|
// Test AggregateBoundingBoxConstraints with input being
|
||
|
// std::vector<Binding<BoundingBoxConstraint>>
|
||
|
std::vector<Binding<BoundingBoxConstraint>> bounding_box_constraints{};
|
||
|
auto result = AggregateBoundingBoxConstraints(bounding_box_constraints);
|
||
|
EXPECT_EQ(result.size(), 0);
|
||
|
// 1 <= x0 <= 2
|
||
|
// 3 <= x2 <= 5
|
||
|
bounding_box_constraints.emplace_back(
|
||
|
std::make_shared<BoundingBoxConstraint>(Eigen::Vector2d(1, 3),
|
||
|
Eigen::Vector2d(2, 5)),
|
||
|
Vector2<symbolic::Variable>(x_[0], x_[2]));
|
||
|
result = AggregateBoundingBoxConstraints(bounding_box_constraints);
|
||
|
EXPECT_EQ(result.size(), 2);
|
||
|
EXPECT_EQ(result.at(x_[0]).lower, 1);
|
||
|
EXPECT_EQ(result.at(x_[0]).upper, 2);
|
||
|
EXPECT_EQ(result.at(x_[2]).lower, 3);
|
||
|
EXPECT_EQ(result.at(x_[2]).upper, 5);
|
||
|
|
||
|
// 2 <= x2 <= 4
|
||
|
// 1.5 <= x0 <= inf
|
||
|
// 4 <= x1 <= inf
|
||
|
bounding_box_constraints.emplace_back(
|
||
|
std::make_shared<BoundingBoxConstraint>(Eigen::Vector3d(2, 1.5, 4),
|
||
|
Eigen::Vector3d(4, kInf, kInf)),
|
||
|
Vector3<symbolic::Variable>(x_[2], x_[0], x_[1]));
|
||
|
result = AggregateBoundingBoxConstraints(bounding_box_constraints);
|
||
|
EXPECT_EQ(result.size(), 3);
|
||
|
EXPECT_EQ(result.at(x_[0]).lower, 1.5);
|
||
|
EXPECT_EQ(result.at(x_[0]).upper, 2);
|
||
|
EXPECT_EQ(result.at(x_[1]).lower, 4);
|
||
|
EXPECT_EQ(result.at(x_[1]).upper, kInf);
|
||
|
EXPECT_EQ(result.at(x_[2]).lower, 3);
|
||
|
EXPECT_EQ(result.at(x_[2]).upper, 4);
|
||
|
|
||
|
// -inf <= x1 <= 2. The returned bounds for x1 should be 4 <= x1 <= 2. This
|
||
|
// test that we can return the bounds even if the lower bound is higher than
|
||
|
// the upper bound.
|
||
|
bounding_box_constraints.emplace_back(
|
||
|
std::make_shared<BoundingBoxConstraint>(Vector1d(-kInf), Vector1d(2)),
|
||
|
Vector1<symbolic::Variable>(x_[1]));
|
||
|
result = AggregateBoundingBoxConstraints(bounding_box_constraints);
|
||
|
EXPECT_EQ(result.size(), 3);
|
||
|
// Only the bound of x_[1] should change, the rest should be the same.
|
||
|
EXPECT_EQ(result.at(x_[0]).lower, 1.5);
|
||
|
EXPECT_EQ(result.at(x_[0]).upper, 2);
|
||
|
EXPECT_EQ(result.at(x_[1]).lower, 4);
|
||
|
EXPECT_EQ(result.at(x_[1]).upper, 2);
|
||
|
EXPECT_EQ(result.at(x_[2]).lower, 3);
|
||
|
EXPECT_EQ(result.at(x_[2]).upper, 4);
|
||
|
}
|
||
|
|
||
|
TEST_F(TestAggregateCostsAndConstraints, AggregateBoundingBoxConstraints2) {
|
||
|
// Test AggregateBoundingBoxConstraints with input being MathematicalPorgram.
|
||
|
MathematicalProgram prog;
|
||
|
auto x = prog.NewContinuousVariables<4>();
|
||
|
prog.AddBoundingBoxConstraint(1, 3, x.tail<2>());
|
||
|
prog.AddBoundingBoxConstraint(-1, 2, x(0));
|
||
|
prog.AddBoundingBoxConstraint(-kInf, 2, x(3));
|
||
|
Eigen::VectorXd lower, upper;
|
||
|
AggregateBoundingBoxConstraints(prog, &lower, &upper);
|
||
|
EXPECT_TRUE(CompareMatrices(lower, Eigen::Vector4d(-1, -kInf, 1, 1)));
|
||
|
EXPECT_TRUE(CompareMatrices(upper, Eigen::Vector4d(2, kInf, 3, 2)));
|
||
|
}
|
||
|
|
||
|
void CheckAggregateDuplicateVariables(const Eigen::SparseMatrix<double>& A,
|
||
|
const VectorX<symbolic::Variable>& vars) {
|
||
|
Eigen::SparseMatrix<double> A_new;
|
||
|
VectorX<symbolic::Variable> vars_new;
|
||
|
AggregateDuplicateVariables(A, vars, &A_new, &vars_new);
|
||
|
EXPECT_EQ(A.rows(), A_new.rows());
|
||
|
for (int i = 0; i < A.rows(); ++i) {
|
||
|
EXPECT_PRED2(symbolic::test::ExprEqual,
|
||
|
A.toDense().row(i).dot(vars).Expand(),
|
||
|
A_new.toDense().row(i).dot(vars_new).Expand());
|
||
|
}
|
||
|
// Make sure vars_new doesn't have duplicated variables.
|
||
|
std::unordered_set<symbolic::Variable::Id> vars_new_set;
|
||
|
for (int i = 0; i < vars_new.rows(); ++i) {
|
||
|
EXPECT_EQ(vars_new_set.find(vars_new(i).get_id()), vars_new_set.end());
|
||
|
vars_new_set.insert(vars_new(i).get_id());
|
||
|
}
|
||
|
}
|
||
|
TEST_F(TestAggregateCostsAndConstraints, AggregateDuplicateVariables) {
|
||
|
Eigen::SparseMatrix<double> A = Eigen::RowVector3d(1, 2, 3).sparseView();
|
||
|
// No duplication.
|
||
|
CheckAggregateDuplicateVariables(
|
||
|
A, Vector3<symbolic::Variable>(x_[0], x_[1], x_[2]));
|
||
|
|
||
|
// A is a row vector, vars has duplication.
|
||
|
CheckAggregateDuplicateVariables(
|
||
|
A, Vector3<symbolic::Variable>(x_[0], x_[1], x_[0]));
|
||
|
CheckAggregateDuplicateVariables(
|
||
|
A, Vector3<symbolic::Variable>(x_[0], x_[0], x_[0]));
|
||
|
|
||
|
// A is a matrix.
|
||
|
A = (Eigen::Matrix<double, 2, 3>() << 0, 1, 2, 3, 4, 0)
|
||
|
.finished()
|
||
|
.sparseView();
|
||
|
CheckAggregateDuplicateVariables(
|
||
|
A, Vector3<symbolic::Variable>(x_[1], x_[0], x_[1]));
|
||
|
}
|
||
|
|
||
|
GTEST_TEST(TestFindNonconvexQuadraticCost, Test) {
|
||
|
MathematicalProgram prog;
|
||
|
auto x = prog.NewContinuousVariables<2>();
|
||
|
auto convex_cost1 = prog.AddQuadraticCost(x(0) * x(0) + 1);
|
||
|
auto convex_cost2 = prog.AddQuadraticCost(x(1) * x(1) + 2 * x(0) + 2);
|
||
|
auto nonconvex_cost1 = prog.AddQuadraticCost(-x(0) * x(0) + 2 * x(1));
|
||
|
auto nonconvex_cost2 = prog.AddQuadraticCost(-x(0) * x(0) + x(1) * x(1));
|
||
|
EXPECT_EQ(FindNonconvexQuadraticCost({convex_cost1, convex_cost2}), nullptr);
|
||
|
EXPECT_EQ(FindNonconvexQuadraticCost({convex_cost1, nonconvex_cost1})
|
||
|
->evaluator()
|
||
|
.get(),
|
||
|
nonconvex_cost1.evaluator().get());
|
||
|
EXPECT_EQ(FindNonconvexQuadraticCost(
|
||
|
{convex_cost1, nonconvex_cost1, nonconvex_cost2})
|
||
|
->evaluator()
|
||
|
.get(),
|
||
|
nonconvex_cost1.evaluator().get());
|
||
|
}
|
||
|
|
||
|
namespace internal {
|
||
|
GTEST_TEST(CheckConvexSolverAttributes, Test) {
|
||
|
MathematicalProgram prog;
|
||
|
auto x = prog.NewContinuousVariables<2>();
|
||
|
auto convex_cost = prog.AddQuadraticCost(x(0) * x(0) + x(1));
|
||
|
// Requires linear cost, but program has a quadratic cost.
|
||
|
ProgramAttributes required_capabilities{ProgramAttribute::kLinearCost};
|
||
|
EXPECT_FALSE(
|
||
|
CheckConvexSolverAttributes(prog, required_capabilities, "foo", nullptr));
|
||
|
std::string explanation;
|
||
|
EXPECT_FALSE(CheckConvexSolverAttributes(prog, required_capabilities, "foo",
|
||
|
&explanation));
|
||
|
EXPECT_THAT(explanation,
|
||
|
HasSubstr("foo is unable to solve because a QuadraticCost was "
|
||
|
"declared but is not supported"));
|
||
|
|
||
|
// Test when the required capabilities matches with the program.
|
||
|
required_capabilities = {ProgramAttribute::kQuadraticCost};
|
||
|
EXPECT_TRUE(
|
||
|
CheckConvexSolverAttributes(prog, required_capabilities, "foo", nullptr));
|
||
|
EXPECT_TRUE(CheckConvexSolverAttributes(prog, required_capabilities, "foo",
|
||
|
&explanation));
|
||
|
EXPECT_TRUE(explanation.empty());
|
||
|
|
||
|
// program has a non-convex quadratic cost.
|
||
|
auto nonconvex_cost = prog.AddQuadraticCost(-x(1) * x(1));
|
||
|
// Use a description that would never be mistaken as any other part of the
|
||
|
// error message.
|
||
|
const std::string description{"lorem ipsum"};
|
||
|
nonconvex_cost.evaluator()->set_description(description);
|
||
|
EXPECT_FALSE(
|
||
|
CheckConvexSolverAttributes(prog, required_capabilities, "foo", nullptr));
|
||
|
EXPECT_FALSE(CheckConvexSolverAttributes(prog, required_capabilities, "foo",
|
||
|
&explanation));
|
||
|
EXPECT_THAT(explanation, HasSubstr("is non-convex"));
|
||
|
EXPECT_THAT(explanation, HasSubstr("foo"));
|
||
|
EXPECT_THAT(explanation, HasSubstr(description));
|
||
|
}
|
||
|
} // namespace internal
|
||
|
} // namespace solvers
|
||
|
} // namespace drake
|