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.
1275 lines
51 KiB
1275 lines
51 KiB
#include "drake/solvers/sdpa_free_format.h"
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <limits>
|
|
#include <utility>
|
|
|
|
#include <gtest/gtest.h>
|
|
|
|
#include "drake/common/temp_directory.h"
|
|
#include "drake/common/test_utilities/eigen_matrix_compare.h"
|
|
#include "drake/common/test_utilities/expect_throws_message.h"
|
|
#include "drake/solvers/csdp_solver_internal.h"
|
|
#include "drake/solvers/test/csdp_test_examples.h"
|
|
|
|
namespace drake {
|
|
namespace solvers {
|
|
const double kInf = std::numeric_limits<double>::infinity();
|
|
namespace internal {
|
|
|
|
void CompareProgVarInSdpa(const SdpaFreeFormat& sdpa_free_format,
|
|
int variable_index, double val_expected) {
|
|
const double val =
|
|
std::get<double>(sdpa_free_format.prog_var_in_sdpa()[variable_index]);
|
|
EXPECT_EQ(val, val_expected);
|
|
}
|
|
|
|
void CompareProgVarInSdpa(const SdpaFreeFormat& sdpa_free_format,
|
|
int variable_index,
|
|
SdpaFreeFormat::FreeVariableIndex s_index_expected) {
|
|
const SdpaFreeFormat::FreeVariableIndex s_index =
|
|
std::get<SdpaFreeFormat::FreeVariableIndex>(
|
|
sdpa_free_format.prog_var_in_sdpa()[variable_index]);
|
|
EXPECT_EQ(s_index, s_index_expected);
|
|
}
|
|
|
|
void CompareProgVarInSdpa(const SdpaFreeFormat& sdpa_free_format,
|
|
int variable_index,
|
|
const DecisionVariableInSdpaX& val_expected) {
|
|
const auto val = std::get<DecisionVariableInSdpaX>(
|
|
sdpa_free_format.prog_var_in_sdpa()[variable_index]);
|
|
EXPECT_EQ(val.coeff_sign, val_expected.coeff_sign);
|
|
EXPECT_EQ(val.offset, val_expected.offset);
|
|
EXPECT_EQ(val.entry_in_X.block_index, val_expected.entry_in_X.block_index);
|
|
EXPECT_EQ(val.entry_in_X.row_index_in_block,
|
|
val_expected.entry_in_X.row_index_in_block);
|
|
EXPECT_EQ(val.entry_in_X.column_index_in_block,
|
|
val_expected.entry_in_X.column_index_in_block);
|
|
EXPECT_EQ(val.entry_in_X.X_start_row, val_expected.entry_in_X.X_start_row);
|
|
}
|
|
|
|
void CompareBlockInX(const BlockInX& block1, const BlockInX& block2) {
|
|
EXPECT_EQ(block1.block_type, block2.block_type);
|
|
EXPECT_EQ(block1.num_rows, block2.num_rows);
|
|
}
|
|
|
|
void CompareTriplets(const std::vector<Eigen::Triplet<double>>& triplets1,
|
|
const std::vector<Eigen::Triplet<double>>& triplets2,
|
|
int matrix_rows, int matrix_cols) {
|
|
EXPECT_EQ(triplets1.size(), triplets2.size());
|
|
Eigen::SparseMatrix<double> mat1(matrix_rows, matrix_cols);
|
|
mat1.setFromTriplets(triplets1.begin(), triplets1.end());
|
|
Eigen::SparseMatrix<double> mat2(matrix_rows, matrix_cols);
|
|
mat2.setFromTriplets(triplets2.begin(), triplets2.end());
|
|
EXPECT_TRUE(
|
|
CompareMatrices(Eigen::MatrixXd(mat1), Eigen::MatrixXd(mat2), 1E-12));
|
|
}
|
|
|
|
TEST_F(SDPwithOverlappingVariables1, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
ASSERT_EQ(dut.num_X_rows(), 6);
|
|
ASSERT_EQ(dut.num_free_variables(), 0);
|
|
ASSERT_EQ(dut.X_blocks().size(), 3);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 2));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kMatrix, 2));
|
|
CompareBlockInX(dut.X_blocks()[2], BlockInX(BlockType::kDiagonal, 2));
|
|
ASSERT_EQ(dut.prog_var_in_sdpa().size(), 3);
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(x_(0)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 0, 0));
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(x_(1)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 1, 0));
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(x_(2)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 1, 0, 1, 2));
|
|
|
|
// Check A_triplets.
|
|
ASSERT_EQ(dut.A_triplets().size(), 6);
|
|
CompareTriplets(
|
|
dut.A_triplets()[0],
|
|
{Eigen::Triplet<double>(0, 0, 1.0), Eigen::Triplet<double>(1, 1, -1.0)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(0, 0, 1.0), Eigen::Triplet<double>(2, 2, -1.0)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
CompareTriplets(
|
|
dut.A_triplets()[2],
|
|
{Eigen::Triplet<double>(0, 0, 1.0), Eigen::Triplet<double>(3, 3, -1.0)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
// The constraint x0 >= 0.5, we add a slack variable y0 >= 0 with constraint
|
|
// x0 - y0 = 0.5
|
|
CompareTriplets(dut.A_triplets()[3],
|
|
{Eigen::Triplet<double>(0, 0, 1.), Eigen::Triplet(4, 4, -1.)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
// The constraint that x1 = 1
|
|
CompareTriplets(
|
|
dut.A_triplets()[4],
|
|
{Eigen::Triplet<double>(0, 1, 0.5), Eigen::Triplet<double>(1, 0, 0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
// The constraint x2 <= 2. We add a slack variable y2 >= 0 with the constraint
|
|
// x2 + y2 = 2
|
|
CompareTriplets(
|
|
dut.A_triplets()[5],
|
|
{Eigen::Triplet<double>(2, 3, 0.5), Eigen::Triplet<double>(3, 2, 0.5),
|
|
Eigen::Triplet<double>(5, 5, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
|
|
Vector6d g_expected;
|
|
g_expected << 0, 0, 0, 0.5, 1, 2;
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
EXPECT_EQ(dut.B_triplets().size(), 0);
|
|
|
|
// Now check the cost.
|
|
CompareTriplets(
|
|
dut.C_triplets(),
|
|
{Eigen::Triplet<double>(0, 0, -2), Eigen::Triplet<double>(2, 3, -0.5),
|
|
Eigen::Triplet<double>(3, 2, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
EXPECT_EQ(dut.d_triplets().size(), 0);
|
|
EXPECT_EQ(dut.constant_min_cost_term(), 0);
|
|
}
|
|
|
|
TEST_F(SDPwithOverlappingVariables2, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
ASSERT_EQ(dut.num_X_rows(), 5);
|
|
ASSERT_EQ(dut.num_free_variables(), 0);
|
|
ASSERT_EQ(dut.X_blocks().size(), 2);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 2));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kDiagonal, 3));
|
|
ASSERT_EQ(dut.prog_var_in_sdpa().size(), 2);
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(x_(0)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 0, 0));
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(x_(1)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 1, 0));
|
|
|
|
// Check A_triplets.
|
|
ASSERT_EQ(dut.A_triplets().size(), 4);
|
|
CompareTriplets(
|
|
dut.A_triplets()[0],
|
|
{Eigen::Triplet<double>(0, 0, 1.0), Eigen::Triplet<double>(1, 1, -1.0)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
// The constraint 2 <= x0 <= 3, we add a slack variable y0 >= 0 with
|
|
// constraint x0 - y0 = 2, and slack variable y1 >= 0 with constraint x0 + y1
|
|
// = 3
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(0, 0, 1.), Eigen::Triplet<double>(2, 2, -1.)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
CompareTriplets(
|
|
dut.A_triplets()[2],
|
|
{Eigen::Triplet<double>(0, 0, 1.), Eigen::Triplet<double>(3, 3, 1.)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
// The constraint that x1 >= 1
|
|
CompareTriplets(
|
|
dut.A_triplets()[3],
|
|
{Eigen::Triplet<double>(0, 1, 0.5), Eigen::Triplet<double>(1, 0, 0.5),
|
|
Eigen::Triplet<double>(4, 4, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
|
|
Eigen::Vector4d g_expected(0, 2, 3, 1);
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
EXPECT_EQ(dut.B_triplets().size(), 0);
|
|
|
|
// Now check the cost.
|
|
CompareTriplets(
|
|
dut.C_triplets(),
|
|
{Eigen::Triplet<double>(0, 0, -2), Eigen::Triplet<double>(0, 1, -0.5),
|
|
Eigen::Triplet<double>(1, 0, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
EXPECT_EQ(dut.d_triplets().size(), 0);
|
|
EXPECT_EQ(dut.constant_min_cost_term(), 0);
|
|
}
|
|
|
|
TEST_F(CsdpDocExample, TestSdpaFreeFormatConstructor) {
|
|
SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 7);
|
|
EXPECT_EQ(dut.num_free_variables(), 0);
|
|
EXPECT_EQ(dut.X_blocks().size(), 3);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 2));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kMatrix, 3));
|
|
CompareBlockInX(dut.X_blocks()[2], BlockInX(BlockType::kDiagonal, 2));
|
|
EXPECT_EQ(dut.prog_var_in_sdpa().size(), prog_->num_vars());
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(X1_(0, 0)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 0, 0));
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(X1_(1, 0)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 1, 0));
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(X1_(1, 1)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 1, 1, 0));
|
|
for (int j = 0; j < 3; ++j) {
|
|
for (int i = 0; i <= j; ++i) {
|
|
CompareProgVarInSdpa(
|
|
dut, prog_->FindDecisionVariableIndex(X2_(i, j)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 1, i, j, 2));
|
|
}
|
|
}
|
|
for (int i = 0; i < 2; ++i) {
|
|
CompareProgVarInSdpa(
|
|
dut, prog_->FindDecisionVariableIndex(y_(i)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 2, i, i, 5));
|
|
}
|
|
|
|
// Check the cost.
|
|
CompareTriplets(
|
|
dut.C_triplets(),
|
|
{Eigen::Triplet<double>(0, 0, 2), Eigen::Triplet<double>(0, 1, 1),
|
|
Eigen::Triplet<double>(1, 0, 1), Eigen::Triplet<double>(1, 1, 2),
|
|
Eigen::Triplet<double>(2, 2, 3), Eigen::Triplet<double>(3, 3, 2),
|
|
Eigen::Triplet<double>(2, 4, 1), Eigen::Triplet<double>(4, 2, 1),
|
|
Eigen::Triplet<double>(4, 4, 3)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
EXPECT_EQ(dut.d_triplets().size(), 0);
|
|
EXPECT_EQ(dut.constant_min_cost_term(), 0);
|
|
|
|
// Check the constraints.
|
|
EXPECT_EQ(dut.A_triplets().size(), 2);
|
|
CompareTriplets(
|
|
dut.A_triplets()[0],
|
|
{Eigen::Triplet<double>(0, 0, 3), Eigen::Triplet<double>(0, 1, 1),
|
|
Eigen::Triplet<double>(1, 0, 1), Eigen::Triplet<double>(1, 1, 3),
|
|
Eigen::Triplet<double>(5, 5, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(2, 2, 3), Eigen::Triplet<double>(3, 3, 4),
|
|
Eigen::Triplet<double>(2, 4, 1), Eigen::Triplet<double>(4, 2, 1),
|
|
Eigen::Triplet<double>(4, 4, 5), Eigen::Triplet<double>(6, 6, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
EXPECT_EQ(dut.B_triplets().size(), 0);
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), Eigen::Vector2d(1, 2)));
|
|
}
|
|
|
|
TEST_F(LinearProgramBoundingBox1, TestSdpaFreeFormatConstructor) {
|
|
// Test if we can correctly register decision variables with bounding box
|
|
// constraints.
|
|
SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 7);
|
|
EXPECT_EQ(dut.num_free_variables(), 1);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 1);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kDiagonal, 7));
|
|
EXPECT_EQ(dut.prog_var_in_sdpa().size(), prog_->num_vars());
|
|
CompareProgVarInSdpa(dut, 0,
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 0, 0, 0));
|
|
CompareProgVarInSdpa(dut, 1,
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, 1, 1, 0));
|
|
CompareProgVarInSdpa(
|
|
dut, 2, DecisionVariableInSdpaX(Sign::kPositive, -1, 0, 3, 3, 0));
|
|
CompareProgVarInSdpa(
|
|
dut, 3, DecisionVariableInSdpaX(Sign::kNegative, 10, 0, 4, 4, 0));
|
|
CompareProgVarInSdpa(
|
|
dut, 4, DecisionVariableInSdpaX(Sign::kPositive, -2, 0, 5, 5, 0));
|
|
CompareProgVarInSdpa(dut, 5, 0.0);
|
|
CompareProgVarInSdpa(dut, 6, 1.0);
|
|
CompareProgVarInSdpa(dut, 7, SdpaFreeFormat::FreeVariableIndex(0));
|
|
|
|
// Constraining 0 <= x(1) <= 5
|
|
Eigen::Vector2d g_expected;
|
|
CompareTriplets(
|
|
dut.A_triplets()[0],
|
|
{Eigen::Triplet<double>(1, 1, 1), Eigen::Triplet<double>(2, 2, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
g_expected(0) = 5;
|
|
// Constraining -2 <= x(4) <= 5
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(5, 5, 1), Eigen::Triplet<double>(6, 6, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
g_expected(1) = 7;
|
|
EXPECT_EQ(dut.B_triplets().size(), 0);
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
|
|
// Check the cost max -x(0) + x(1) - 2 * x(2) + 3 * x(3) + x(4) + 1
|
|
CompareTriplets(
|
|
dut.C_triplets(),
|
|
{Eigen::Triplet<double>(0, 0, -1), Eigen::Triplet<double>(1, 1, 1),
|
|
Eigen::Triplet<double>(3, 3, -2), Eigen::Triplet<double>(4, 4, -3),
|
|
Eigen::Triplet<double>(5, 5, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
EXPECT_EQ(dut.constant_min_cost_term(), -31);
|
|
}
|
|
|
|
TEST_F(CsdpLinearProgram2, TestSdpaFreeFormatConstructor) {
|
|
// This tests adding linear constraint.
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 4);
|
|
EXPECT_EQ(dut.num_free_variables(), 3);
|
|
EXPECT_EQ(dut.prog_var_in_sdpa().size(), 3);
|
|
for (int i = 0; i < 3; ++i) {
|
|
CompareProgVarInSdpa(dut, i, SdpaFreeFormat::FreeVariableIndex(i));
|
|
}
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 1);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kDiagonal, 4));
|
|
|
|
// Check the linear constraints.
|
|
EXPECT_EQ(dut.A_triplets().size(), 6);
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
Vector6<double> g_expected;
|
|
// 2 * x(0) + 3 * x(1) + x(2) = 1
|
|
B_triplets_expected.emplace_back(0, 0, 2);
|
|
B_triplets_expected.emplace_back(0, 1, 3);
|
|
B_triplets_expected.emplace_back(0, 2, 1);
|
|
g_expected(0) = 1;
|
|
// -2 * x(2) + x(0) <= -1
|
|
EXPECT_EQ(dut.A_triplets()[0].size(), 0);
|
|
std::vector<Eigen::Triplet<double>> A1_triplets_expected;
|
|
A1_triplets_expected.emplace_back(0, 0, 1);
|
|
CompareTriplets(dut.A_triplets()[1], A1_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(1, 0, 1);
|
|
B_triplets_expected.emplace_back(1, 2, -2);
|
|
g_expected(1) = -1;
|
|
// 2 * x(1) + x(0) >= -2
|
|
std::vector<Eigen::Triplet<double>> A2_triplets_expected;
|
|
A2_triplets_expected.emplace_back(1, 1, -1);
|
|
CompareTriplets(dut.A_triplets()[2], A2_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(2, 0, 1);
|
|
B_triplets_expected.emplace_back(2, 1, 2);
|
|
g_expected(2) = -2;
|
|
// -2 <= -x(0) + 3 * x(2) <= 3
|
|
std::vector<Eigen::Triplet<double>> A3_triplets_expected;
|
|
A3_triplets_expected.emplace_back(2, 2, -1);
|
|
CompareTriplets(dut.A_triplets()[3], A3_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(3, 0, -1);
|
|
B_triplets_expected.emplace_back(3, 2, 3);
|
|
g_expected(3) = -2;
|
|
std::vector<Eigen::Triplet<double>> A4_triplets_expected;
|
|
A4_triplets_expected.emplace_back(3, 3, 1);
|
|
CompareTriplets(dut.A_triplets()[4], A4_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(4, 0, -1);
|
|
B_triplets_expected.emplace_back(4, 2, 3);
|
|
g_expected(4) = 3;
|
|
// x(0) + x(1) + 4 * x(2) = 3
|
|
EXPECT_EQ(dut.A_triplets()[5].size(), 0);
|
|
B_triplets_expected.emplace_back(5, 0, 1);
|
|
B_triplets_expected.emplace_back(5, 1, 1);
|
|
B_triplets_expected.emplace_back(5, 2, 4);
|
|
g_expected(5) = 3;
|
|
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, 6,
|
|
dut.num_free_variables());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
|
|
// Check the cost min x(0) + 2 * x(1) + 3 * x(2)
|
|
EXPECT_EQ(dut.C_triplets().size(), 0);
|
|
std::vector<Eigen::Triplet<double>> d_triplets_expected;
|
|
d_triplets_expected.emplace_back(0, 0, -1);
|
|
d_triplets_expected.emplace_back(1, 0, -2);
|
|
d_triplets_expected.emplace_back(2, 0, -3);
|
|
CompareTriplets(dut.d_triplets(), d_triplets_expected, 3, 1);
|
|
}
|
|
|
|
TEST_F(CsdpLinearProgram3, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 7);
|
|
EXPECT_EQ(dut.num_free_variables(), 1);
|
|
EXPECT_EQ(dut.prog_var_in_sdpa().size(), 3);
|
|
CompareProgVarInSdpa(
|
|
dut, 0, DecisionVariableInSdpaX(Sign::kPositive, -1, 0, 0, 0, 0));
|
|
CompareProgVarInSdpa(dut, 1,
|
|
DecisionVariableInSdpaX(Sign::kNegative, 8, 0, 2, 2, 0));
|
|
CompareProgVarInSdpa(dut, 2, SdpaFreeFormat::FreeVariableIndex(0));
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 2);
|
|
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kDiagonal, 3));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kDiagonal, 4));
|
|
|
|
// Check the cost 2x(0) + 3x(1) + 4x(2) + 3
|
|
CompareTriplets(
|
|
dut.C_triplets(),
|
|
{Eigen::Triplet<double>(0, 0, 2), Eigen::Triplet<double>(2, 2, -3)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
CompareTriplets(dut.d_triplets(), {Eigen::Triplet<double>(0, 0, 4)}, 1, 1);
|
|
EXPECT_EQ(dut.constant_min_cost_term(), -25);
|
|
EXPECT_EQ(dut.A_triplets().size(), 6);
|
|
Vector6<double> g_expected;
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
// Check the constraint x(0) <= 10
|
|
CompareTriplets(
|
|
dut.A_triplets()[0],
|
|
{Eigen::Triplet<double>(0, 0, 1), Eigen::Triplet<double>(1, 1, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
g_expected(0) = 11;
|
|
|
|
// Check the constraint x(0) + 2x(1) + 3x(2) = 3
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(0, 0, 1), Eigen::Triplet<double>(2, 2, -2)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(1, 0, 3);
|
|
g_expected(1) = -12;
|
|
|
|
// Check the constraint 2x(0) - x(2)>= -1
|
|
CompareTriplets(
|
|
dut.A_triplets()[2],
|
|
{Eigen::Triplet<double>(0, 0, 2), Eigen::Triplet<double>(3, 3, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(2, 0, -1);
|
|
g_expected(2) = 1;
|
|
|
|
// Check the constraint x(1) - 3x(2) <= 5
|
|
CompareTriplets(
|
|
dut.A_triplets()[3],
|
|
{Eigen::Triplet<double>(2, 2, -1), Eigen::Triplet<double>(4, 4, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(3, 0, -3);
|
|
g_expected(3) = -3;
|
|
|
|
// Check the constraint -4 <= x(0) + x(2) <= 9
|
|
CompareTriplets(
|
|
dut.A_triplets()[4],
|
|
{Eigen::Triplet<double>(0, 0, 1), Eigen::Triplet<double>(5, 5, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(4, 0, 1);
|
|
g_expected(4) = -3;
|
|
|
|
CompareTriplets(
|
|
dut.A_triplets()[5],
|
|
{Eigen::Triplet<double>(0, 0, 1), Eigen::Triplet<double>(6, 6, 1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(5, 0, 1);
|
|
g_expected(5) = 10;
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, dut.g().rows(),
|
|
dut.num_free_variables());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
}
|
|
|
|
TEST_F(TrivialSDP1, TestSdpaFreeFormatConstructor) {
|
|
// Test SdpaFreeFormat constructor with both PSD constraint and linear
|
|
// constraint.
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 4);
|
|
EXPECT_EQ(dut.num_free_variables(), 0);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 2);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 3));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kDiagonal, 1));
|
|
for (int j = 0; j < 3; ++j) {
|
|
for (int i = 0; i <= j; ++i) {
|
|
CompareProgVarInSdpa(
|
|
dut, prog_->FindDecisionVariableIndex(X1_(i, j)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, i, j, 0));
|
|
}
|
|
}
|
|
|
|
EXPECT_EQ(dut.A_triplets().size(), 2);
|
|
Eigen::Vector2d g_expected(1, 0);
|
|
// X1(0, 0) + X1(1, 1) + X1(2, 2) = 1
|
|
std::vector<Eigen::Triplet<double>> A0_triplets_expected;
|
|
A0_triplets_expected.emplace_back(0, 0, 1);
|
|
A0_triplets_expected.emplace_back(1, 1, 1);
|
|
A0_triplets_expected.emplace_back(2, 2, 1);
|
|
CompareTriplets(dut.A_triplets()[0], A0_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
|
|
// X1(0, 1) + X1(1, 2) - 2 * X1(0, 2) <= 0
|
|
std::vector<Eigen::Triplet<double>> A1_triplets_expected;
|
|
A1_triplets_expected.emplace_back(0, 1, 0.5);
|
|
A1_triplets_expected.emplace_back(1, 0, 0.5);
|
|
A1_triplets_expected.emplace_back(1, 2, 0.5);
|
|
A1_triplets_expected.emplace_back(2, 1, 0.5);
|
|
A1_triplets_expected.emplace_back(0, 2, -1);
|
|
A1_triplets_expected.emplace_back(2, 0, -1);
|
|
A1_triplets_expected.emplace_back(3, 3, 1);
|
|
CompareTriplets(dut.A_triplets()[1], A1_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
EXPECT_EQ(dut.B_triplets().size(), 0);
|
|
|
|
// Cost max X1(0, 1) + X1(1, 2)
|
|
std::vector<Eigen::Triplet<double>> C_triplets_expected;
|
|
C_triplets_expected.emplace_back(0, 1, 0.5);
|
|
C_triplets_expected.emplace_back(1, 0, 0.5);
|
|
C_triplets_expected.emplace_back(1, 2, 0.5);
|
|
C_triplets_expected.emplace_back(2, 1, 0.5);
|
|
CompareTriplets(dut.C_triplets(), C_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
EXPECT_EQ(dut.d_triplets().size(), 0);
|
|
}
|
|
|
|
TEST_F(TrivialSDP2, TestSdpaFreeFormatConstructor) {
|
|
// Test constructor with linear matrix inequality constraint.
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 4);
|
|
EXPECT_EQ(dut.num_free_variables(), 1);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 2);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 2));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kMatrix, 2));
|
|
for (int j = 0; j < 2; ++j) {
|
|
for (int i = 0; i <= j; ++i) {
|
|
CompareProgVarInSdpa(
|
|
dut, prog_->FindDecisionVariableIndex(X1_(i, j)),
|
|
DecisionVariableInSdpaX(Sign::kPositive, 0, 0, i, j, 0));
|
|
}
|
|
}
|
|
CompareProgVarInSdpa(dut, prog_->FindDecisionVariableIndex(y_),
|
|
SdpaFreeFormat::FreeVariableIndex(0));
|
|
|
|
// Check linear constraint
|
|
EXPECT_EQ(dut.A_triplets().size(), 4);
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
Eigen::Vector4d g_expected;
|
|
// X1(0, 0) + 2 * X1(1, 1) + 3 * y = 1
|
|
std::vector<Eigen::Triplet<double>> A0_triplets_expected;
|
|
A0_triplets_expected.emplace_back(0, 0, 1);
|
|
A0_triplets_expected.emplace_back(1, 1, 2);
|
|
CompareTriplets(dut.A_triplets()[0], A0_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(0, 0, 3);
|
|
g_expected(0) = 1;
|
|
|
|
// I + F1 * y + F2 * X1(0, 0) is psd.
|
|
// where F1 = [1 2; 2 3], F2 = [2 0; 0 4]
|
|
// 1 + y + 2 * X1(0, 0) = X_slack(0, 0)
|
|
std::vector<Eigen::Triplet<double>> A1_triplets_expected;
|
|
A1_triplets_expected.emplace_back(0, 0, 2);
|
|
A1_triplets_expected.emplace_back(2, 2, -1);
|
|
CompareTriplets(dut.A_triplets()[1], A1_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(1, 0, 1);
|
|
g_expected(1) = -1;
|
|
|
|
// 2*y = X_slack(0, 1)
|
|
std::vector<Eigen::Triplet<double>> A2_triplets_expected;
|
|
A2_triplets_expected.emplace_back(2, 3, -0.5);
|
|
A2_triplets_expected.emplace_back(3, 2, -0.5);
|
|
CompareTriplets(dut.A_triplets()[2], A2_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(2, 0, 2);
|
|
g_expected(2) = 0;
|
|
|
|
// 1 + 3*y + 4*X1_(0, 0) = X_slack(1, 1)
|
|
std::vector<Eigen::Triplet<double>> A3_triplets_expected;
|
|
A3_triplets_expected.emplace_back(0, 0, 4);
|
|
A3_triplets_expected.emplace_back(3, 3, -1);
|
|
CompareTriplets(dut.A_triplets()[3], A3_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(3, 0, 3);
|
|
g_expected(3) = -1;
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, 4, 1);
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
|
|
// Check the cost max y
|
|
EXPECT_EQ(dut.C_triplets().size(), 0);
|
|
std::vector<Eigen::Triplet<double>> d_triplets_expected;
|
|
d_triplets_expected.emplace_back(0, 0, 1);
|
|
CompareTriplets(dut.d_triplets(), d_triplets_expected, 1, 1);
|
|
}
|
|
|
|
TEST_F(TrivialSOCP1, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 5);
|
|
EXPECT_EQ(dut.num_free_variables(), 1);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 2);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kDiagonal, 2));
|
|
CompareBlockInX(dut.X_blocks()[1], BlockInX(BlockType::kMatrix, 3));
|
|
|
|
CompareProgVarInSdpa(dut, 0, SdpaFreeFormat::FreeVariableIndex(0));
|
|
for (int i = 0; i < 2; ++i) {
|
|
CompareProgVarInSdpa(
|
|
dut, i + 1, DecisionVariableInSdpaX(Sign::kPositive, 0, 0, i, i, 0));
|
|
}
|
|
|
|
// Check the cost max x(0).
|
|
EXPECT_EQ(dut.C_triplets().size(), 0);
|
|
EXPECT_TRUE(CompareMatrices(Eigen::VectorXd(dut.d()), Vector1d(1)));
|
|
|
|
EXPECT_EQ(dut.A_triplets().size(), 7);
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
Eigen::Matrix<double, 7, 1> g_expected;
|
|
// Check the first constraint x(0) + x(1) + x(2) = 10
|
|
std::vector<Eigen::Triplet<double>> A0_triplets_expected;
|
|
A0_triplets_expected.emplace_back(0, 0, 1);
|
|
A0_triplets_expected.emplace_back(1, 1, 1);
|
|
CompareTriplets(dut.A_triplets()[0], A0_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(0, 0, 1);
|
|
g_expected(0) = 10;
|
|
|
|
// The equality constraints arising from the Lorentz cone. 2x(0) + 1 ==
|
|
// X_blocks()[1](i, i) for i = 0, 1, 2.
|
|
std::vector<Eigen::Triplet<double>> A_diagonal_triplets_expected;
|
|
for (int i = 0; i < 3; ++i) {
|
|
if (!A_diagonal_triplets_expected.empty()) {
|
|
A_diagonal_triplets_expected.pop_back();
|
|
}
|
|
A_diagonal_triplets_expected.emplace_back(2 + i, 2 + i, -1);
|
|
CompareTriplets(dut.A_triplets()[1 + i], A_diagonal_triplets_expected,
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(1 + i, 0, 2);
|
|
g_expected(1 + i) = -1;
|
|
}
|
|
|
|
// The equality constraint arising from the Lorentz cone. 3x(1) + 2 =
|
|
// X_blocks()[1](0, 1).
|
|
std::vector<Eigen::Triplet<double>> A4_triplets_expected;
|
|
A4_triplets_expected.emplace_back(0, 0, 3);
|
|
A4_triplets_expected.emplace_back(2, 3, -0.5);
|
|
A4_triplets_expected.emplace_back(3, 2, -0.5);
|
|
CompareTriplets(dut.A_triplets()[4], A4_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
g_expected(4) = -2;
|
|
|
|
// The equality constraint arising from the Lorentz cone. x(0) + x(2) + 3 =
|
|
// X_blocks()[1](0, 2).
|
|
std::vector<Eigen::Triplet<double>> A5_triplets_expected;
|
|
A5_triplets_expected.emplace_back(1, 1, 1);
|
|
A5_triplets_expected.emplace_back(2, 4, -0.5);
|
|
A5_triplets_expected.emplace_back(4, 2, -0.5);
|
|
CompareTriplets(dut.A_triplets()[5], A5_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(5, 0, 1);
|
|
g_expected(5) = -3;
|
|
|
|
// The equality constraint arising from the Lorentz cone X_blocks()[1](1, 2) =
|
|
// 0.
|
|
std::vector<Eigen::Triplet<double>> A6_triplets_expected;
|
|
A6_triplets_expected.emplace_back(3, 4, 0.5);
|
|
A6_triplets_expected.emplace_back(4, 3, 0.5);
|
|
CompareTriplets(dut.A_triplets()[6], A6_triplets_expected, dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
g_expected(6) = 0;
|
|
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, dut.g().rows(),
|
|
dut.num_free_variables());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
}
|
|
|
|
TEST_F(TrivialSOCP2, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 3);
|
|
EXPECT_EQ(dut.num_free_variables(), 2);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 1);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 3));
|
|
for (int i = 0; i < 2; ++i) {
|
|
CompareProgVarInSdpa(dut, i, SdpaFreeFormat::FreeVariableIndex(i));
|
|
}
|
|
|
|
// Check the cost.
|
|
EXPECT_EQ(dut.C_triplets().size(), 0);
|
|
CompareTriplets(dut.d_triplets(), {Eigen::Triplet<double>(1, 0, 1)}, 2, 1);
|
|
|
|
EXPECT_EQ(dut.A_triplets().size(), 6);
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
Vector6<double> g_expected;
|
|
// Check the linear equality constraint arising from Lorentz cone. X(i, i) =
|
|
// x(0) + 2 for i = 0, 1, 2.
|
|
for (int i = 0; i < 3; ++i) {
|
|
CompareTriplets(dut.A_triplets()[i], {Eigen::Triplet<double>(i, i, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(i, 0, 1);
|
|
g_expected(i) = -2;
|
|
}
|
|
// Check the linear equality constraint arising from Lorentz cone, X(0, 1) =
|
|
// x(0) + x(1) + 1.
|
|
CompareTriplets(
|
|
dut.A_triplets()[3],
|
|
{Eigen::Triplet<double>(0, 1, -0.5), Eigen::Triplet<double>(1, 0, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(3, 0, 1);
|
|
B_triplets_expected.emplace_back(3, 1, 1);
|
|
g_expected(3) = -1;
|
|
// Check the linear equality constraint arising from Lorentz cone, X(0, 2) =
|
|
// x(0) - x(1) + 1.
|
|
CompareTriplets(
|
|
dut.A_triplets()[4],
|
|
{Eigen::Triplet<double>(0, 2, -0.5), Eigen::Triplet<double>(2, 0, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(4, 0, 1);
|
|
B_triplets_expected.emplace_back(4, 1, -1);
|
|
g_expected(4) = -1;
|
|
// Check the linear equality constraint arising from Lorentz cone, X(1, 2) =
|
|
// 0.
|
|
CompareTriplets(
|
|
dut.A_triplets()[5],
|
|
{Eigen::Triplet<double>(1, 2, 0.5), Eigen::Triplet<double>(2, 1, 0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
g_expected(5) = 0;
|
|
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, dut.g().rows(),
|
|
dut.num_free_variables());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
}
|
|
|
|
TEST_F(TrivialSOCP3, TestSdpaFreeFormatConstructor) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
EXPECT_EQ(dut.num_X_rows(), 3);
|
|
EXPECT_EQ(dut.num_free_variables(), 2);
|
|
|
|
EXPECT_EQ(dut.X_blocks().size(), 1);
|
|
CompareBlockInX(dut.X_blocks()[0], BlockInX(BlockType::kMatrix, 3));
|
|
|
|
for (int i = 0; i < 2; ++i) {
|
|
CompareProgVarInSdpa(dut, i, SdpaFreeFormat::FreeVariableIndex(i));
|
|
}
|
|
|
|
// Check the cost max -x(1).
|
|
EXPECT_EQ(dut.C_triplets().size(), 0);
|
|
CompareTriplets(dut.d_triplets(), {Eigen::Triplet<double>(1, 0, -1)}, 2, 1);
|
|
|
|
EXPECT_EQ(dut.A_triplets().size(), 6);
|
|
std::vector<Eigen::Triplet<double>> B_triplets_expected;
|
|
Vector6<double> g_expected;
|
|
// Check the equality constraint arising from rotated Lorentz cone
|
|
// 2x(0) + 2 = X(0, 0).
|
|
CompareTriplets(dut.A_triplets()[0], {Eigen::Triplet<double>(0, 0, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(0, 0, 2);
|
|
g_expected(0) = -2;
|
|
|
|
// Check the equality constraint arising from rotated Lorentz cone
|
|
// x(0) + 2 = X(0, 1).
|
|
CompareTriplets(
|
|
dut.A_triplets()[1],
|
|
{Eigen::Triplet<double>(0, 1, -0.5), Eigen::Triplet<double>(1, 0, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(1, 0, 1);
|
|
g_expected(1) = -2;
|
|
|
|
// Check the equality constraint arising from rotated Lorentz cone
|
|
// 3x(0) + x(1) + 1 = X(0, 2).
|
|
CompareTriplets(
|
|
dut.A_triplets()[2],
|
|
{Eigen::Triplet<double>(0, 2, -0.5), Eigen::Triplet<double>(2, 0, -0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(2, 0, 3);
|
|
B_triplets_expected.emplace_back(2, 1, 1);
|
|
g_expected(2) = -1;
|
|
|
|
// Check the equality constraint arising from rotated Lorentz cone
|
|
// 3x(1) + 4 = X(1, 1) and 3x(1) + 4 = X(2, 2).
|
|
for (int i = 0; i < 2; ++i) {
|
|
CompareTriplets(dut.A_triplets()[3 + i],
|
|
{Eigen::Triplet<double>(i + 1, i + 1, -1)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
B_triplets_expected.emplace_back(3 + i, 1, 3);
|
|
g_expected(3 + i) = -4;
|
|
}
|
|
|
|
// Check the equality constraint arising from rotated Lorentz cone
|
|
// X(1, 2) = 0.
|
|
CompareTriplets(
|
|
dut.A_triplets()[5],
|
|
{Eigen::Triplet<double>(1, 2, 0.5), Eigen::Triplet<double>(2, 1, 0.5)},
|
|
dut.num_X_rows(), dut.num_X_rows());
|
|
g_expected(5) = 0;
|
|
CompareTriplets(dut.B_triplets(), B_triplets_expected, dut.g().rows(),
|
|
dut.num_X_rows());
|
|
EXPECT_TRUE(CompareMatrices(dut.g(), g_expected));
|
|
}
|
|
|
|
void CheckRemoveFreeVariableByNullspaceApproach(
|
|
const SdpaFreeFormat& dut, const Eigen::SparseMatrix<double>& C_hat,
|
|
const std::vector<Eigen::SparseMatrix<double>>& A_hat,
|
|
const Eigen::VectorXd& rhs_hat, const Eigen::VectorXd& y_hat,
|
|
const Eigen::SparseQR<Eigen::SparseMatrix<double>,
|
|
Eigen::COLAMDOrdering<int>>& QR_B,
|
|
double tol) {
|
|
EXPECT_EQ(y_hat.rows(), static_cast<int>(dut.A().size()));
|
|
// Check Bᵀ * ŷ = d
|
|
EXPECT_TRUE(CompareMatrices(Eigen::VectorXd(dut.B().transpose() * y_hat),
|
|
Eigen::VectorXd(dut.d()), tol));
|
|
// Check Ĉ = C -∑ᵢ ŷᵢAᵢ
|
|
Eigen::SparseMatrix<double> C_hat_expected = dut.C();
|
|
for (int i = 0; i < y_hat.rows(); ++i) {
|
|
C_hat_expected -= y_hat(i) * dut.A()[i];
|
|
}
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(C_hat),
|
|
Eigen::MatrixXd(C_hat_expected), tol));
|
|
// N is the null space of Bᵀ. Namely if we do a QR decomposition on B, then
|
|
// N = Q₂.
|
|
Eigen::SparseMatrix<double> Q;
|
|
Q = QR_B.matrixQ();
|
|
const Eigen::SparseMatrix<double> N =
|
|
Q.rightCols(dut.B().rows() - QR_B.rank());
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(dut.B().transpose() * N),
|
|
Eigen::MatrixXd::Zero(dut.B().cols(), N.cols()),
|
|
tol));
|
|
// Check rhs_hat = Nᵀ * rhs
|
|
EXPECT_TRUE(
|
|
CompareMatrices(rhs_hat, Eigen::VectorXd(N.transpose() * dut.g()), tol));
|
|
// Check Âᵢ = ∑ⱼNⱼᵢAⱼ
|
|
EXPECT_EQ(static_cast<int>(A_hat.size()), N.cols());
|
|
for (int i = 0; i < N.cols(); ++i) {
|
|
Eigen::SparseMatrix<double> A_hat_expected(dut.num_X_rows(),
|
|
dut.num_X_rows());
|
|
A_hat_expected.setZero();
|
|
for (int j = 0; j < static_cast<int>(dut.A().size()); ++j) {
|
|
A_hat_expected += N.coeff(j, i) * dut.A()[j];
|
|
}
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(A_hat[i]),
|
|
Eigen::MatrixXd(A_hat_expected), tol));
|
|
}
|
|
}
|
|
|
|
void TestRemoveFreeVariableByNullspaceApproach(
|
|
const MathematicalProgram& prog) {
|
|
const SdpaFreeFormat dut(prog);
|
|
Eigen::SparseMatrix<double> C_hat;
|
|
std::vector<Eigen::SparseMatrix<double>> A_hat;
|
|
Eigen::VectorXd rhs_hat, y_hat;
|
|
Eigen::SparseQR<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> QR_B;
|
|
dut.RemoveFreeVariableByNullspaceApproach(&C_hat, &A_hat, &rhs_hat, &y_hat,
|
|
&QR_B);
|
|
CheckRemoveFreeVariableByNullspaceApproach(dut, C_hat, A_hat, rhs_hat, y_hat,
|
|
QR_B, 1E-10);
|
|
}
|
|
|
|
TEST_F(LinearProgramBoundingBox1, RemoveFreeVariableByNullspaceApproach) {
|
|
const SdpaFreeFormat dut(*prog_);
|
|
Eigen::SparseMatrix<double> C_hat;
|
|
std::vector<Eigen::SparseMatrix<double>> A_hat;
|
|
Eigen::VectorXd rhs_hat, y_hat;
|
|
Eigen::SparseQR<Eigen::SparseMatrix<double>, Eigen::COLAMDOrdering<int>> QR_B;
|
|
dut.RemoveFreeVariableByNullspaceApproach(&C_hat, &A_hat, &rhs_hat, &y_hat,
|
|
&QR_B);
|
|
CheckRemoveFreeVariableByNullspaceApproach(dut, C_hat, A_hat, rhs_hat, y_hat,
|
|
QR_B, 1E-10);
|
|
|
|
// Now try to call CSDP to solve this problem.
|
|
csdp::blockmatrix C_csdp;
|
|
double* rhs_csdp{nullptr};
|
|
csdp::constraintmatrix* constraints_csdp{nullptr};
|
|
ConvertSparseMatrixFormatToCsdpProblemData(dut.X_blocks(), C_hat, A_hat,
|
|
rhs_hat, &C_csdp, &rhs_csdp,
|
|
&constraints_csdp);
|
|
struct csdp::blockmatrix X_csdp, Z;
|
|
double* y{nullptr};
|
|
csdp::cpp_initsoln(dut.num_X_rows(), rhs_hat.rows(), C_csdp, rhs_csdp,
|
|
constraints_csdp, &X_csdp, &y, &Z);
|
|
double pobj{0};
|
|
double dobj{0};
|
|
const int ret = csdp::cpp_easy_sdp(
|
|
nullptr, dut.num_X_rows(), rhs_hat.rows(), C_csdp, rhs_csdp,
|
|
constraints_csdp, -dut.constant_min_cost_term() + dut.g().dot(y_hat),
|
|
&X_csdp, &y, &Z, &pobj, &dobj);
|
|
EXPECT_EQ(ret, 0 /* 0 is for success */);
|
|
Eigen::SparseMatrix<double> X_hat(dut.num_X_rows(), dut.num_X_rows());
|
|
ConvertCsdpBlockMatrixtoEigen(X_csdp, &X_hat);
|
|
// Now compute the free variable values.
|
|
Eigen::VectorXd AX(dut.A().size());
|
|
for (int i = 0; i < AX.rows(); ++i) {
|
|
AX(i) = (dut.A()[i].cwiseProduct(X_hat)).sum();
|
|
}
|
|
Eigen::VectorXd s_val;
|
|
s_val = QR_B.solve(dut.g() - AX);
|
|
const double tol = 1E-6;
|
|
EXPECT_NEAR(pobj, 43, tol);
|
|
EXPECT_EQ(X_csdp.nblocks, 1);
|
|
EXPECT_EQ(X_csdp.blocks[1].blockcategory, csdp::blockcat::DIAG);
|
|
EXPECT_EQ(X_csdp.blocks[1].blocksize, 7);
|
|
std::vector<double> block_val({0, 5, 0, 0, 0, 7, 0});
|
|
for (int i = 0; i < 7; ++i) {
|
|
EXPECT_NEAR(X_csdp.blocks[1].data.vec[i + 1], block_val[i], tol);
|
|
}
|
|
|
|
csdp::cpp_free_prob(dut.num_X_rows(), rhs_hat.rows(), C_csdp, rhs_csdp,
|
|
constraints_csdp, X_csdp, y, Z);
|
|
}
|
|
|
|
TEST_F(CsdpLinearProgram2, RemoveFreeVariableByNullspaceApproach) {
|
|
TestRemoveFreeVariableByNullspaceApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSDP2, RemoveFreeVariableByNullspaceApproach) {
|
|
TestRemoveFreeVariableByNullspaceApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSOCP1, RemoveFreeVariableByNullspaceApproach) {
|
|
TestRemoveFreeVariableByNullspaceApproach(*prog_);
|
|
}
|
|
|
|
void TestRemoveFreeVariableByTwoSlackVariablesApproach(
|
|
const MathematicalProgram& prog) {
|
|
const SdpaFreeFormat dut(prog);
|
|
std::vector<internal::BlockInX> X_hat_blocks;
|
|
std::vector<Eigen::SparseMatrix<double>> A_hat;
|
|
Eigen::SparseMatrix<double> C_hat;
|
|
dut.RemoveFreeVariableByTwoSlackVariablesApproach(&X_hat_blocks, &A_hat,
|
|
&C_hat);
|
|
EXPECT_EQ(X_hat_blocks.size(), dut.X_blocks().size() + 1);
|
|
for (int i = 0; i < static_cast<int>(dut.X_blocks().size()); ++i) {
|
|
EXPECT_EQ(X_hat_blocks[i].block_type, dut.X_blocks()[i].block_type);
|
|
EXPECT_EQ(X_hat_blocks[i].num_rows, dut.X_blocks()[i].num_rows);
|
|
}
|
|
EXPECT_EQ(X_hat_blocks[X_hat_blocks.size() - 1].block_type,
|
|
BlockType::kDiagonal);
|
|
EXPECT_EQ(X_hat_blocks[X_hat_blocks.size() - 1].num_rows,
|
|
dut.num_free_variables() * 2);
|
|
EXPECT_EQ(A_hat.size(), dut.A().size());
|
|
const int num_X_hat_rows = dut.num_X_rows() + 2 * dut.num_free_variables();
|
|
for (int i = 0; i < static_cast<int>(A_hat.size()); ++i) {
|
|
Eigen::MatrixXd A_hat_expected =
|
|
Eigen::MatrixXd::Zero(num_X_hat_rows, num_X_hat_rows);
|
|
A_hat_expected.block(0, 0, dut.A()[i].rows(), dut.A()[i].cols()) =
|
|
dut.A()[i];
|
|
Eigen::VectorXd bi = dut.B().row(i).transpose();
|
|
A_hat_expected.block(dut.A()[i].rows(), dut.A()[i].cols(), dut.B().cols(),
|
|
dut.B().cols()) = bi.asDiagonal();
|
|
A_hat_expected.bottomRightCorner(dut.num_free_variables(),
|
|
dut.num_free_variables()) =
|
|
(-bi).asDiagonal();
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(A_hat[i]), A_hat_expected));
|
|
}
|
|
|
|
Eigen::MatrixXd C_hat_expected =
|
|
Eigen::MatrixXd::Zero(num_X_hat_rows, num_X_hat_rows);
|
|
C_hat_expected.topLeftCorner(dut.num_X_rows(), dut.num_X_rows()) = dut.C();
|
|
C_hat_expected.block(dut.num_X_rows(), dut.num_X_rows(),
|
|
dut.num_free_variables(), dut.num_free_variables()) =
|
|
Eigen::VectorXd(dut.d()).asDiagonal();
|
|
C_hat_expected.bottomRightCorner(dut.num_free_variables(),
|
|
dut.num_free_variables()) =
|
|
Eigen::VectorXd(-dut.d()).asDiagonal();
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(C_hat), C_hat_expected));
|
|
}
|
|
|
|
TEST_F(LinearProgramBoundingBox1,
|
|
RemoveFreeVariableByTwoSlackVariablesApproach) {
|
|
TestRemoveFreeVariableByTwoSlackVariablesApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(CsdpLinearProgram2, RemoveFreeVariableByTwoSlackVariablesApproach) {
|
|
TestRemoveFreeVariableByTwoSlackVariablesApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSDP2, RemoveFreeVariableByTwoSlackVariablesApproach) {
|
|
TestRemoveFreeVariableByTwoSlackVariablesApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSOCP1, RemoveFreeVariableByTwoSlackVariablesApproach) {
|
|
TestRemoveFreeVariableByTwoSlackVariablesApproach(*prog_);
|
|
}
|
|
|
|
void TestRemoveFreeVariableByLorentzConeSlackApproach(
|
|
const MathematicalProgram& prog) {
|
|
const SdpaFreeFormat dut(prog);
|
|
std::vector<internal::BlockInX> X_hat_blocks;
|
|
std::vector<Eigen::SparseMatrix<double>> A_hat;
|
|
Eigen::VectorXd rhs_hat;
|
|
Eigen::SparseMatrix<double> C_hat;
|
|
dut.RemoveFreeVariableByLorentzConeSlackApproach(&X_hat_blocks, &A_hat,
|
|
&rhs_hat, &C_hat);
|
|
EXPECT_EQ(X_hat_blocks.size(), dut.X_blocks().size() + 1);
|
|
for (int i = 0; i < static_cast<int>(dut.X_blocks().size()); ++i) {
|
|
EXPECT_EQ(X_hat_blocks[i].block_type, dut.X_blocks()[i].block_type);
|
|
EXPECT_EQ(X_hat_blocks[i].num_rows, dut.X_blocks()[i].num_rows);
|
|
}
|
|
EXPECT_EQ(X_hat_blocks[X_hat_blocks.size() - 1].block_type,
|
|
BlockType::kMatrix);
|
|
EXPECT_EQ(X_hat_blocks[X_hat_blocks.size() - 1].num_rows,
|
|
dut.num_free_variables() + 1);
|
|
EXPECT_EQ(A_hat.size(), dut.A().size() + dut.num_free_variables() *
|
|
(dut.num_free_variables() + 1) /
|
|
2);
|
|
EXPECT_EQ(rhs_hat.rows(), A_hat.size());
|
|
Eigen::VectorXd rhs_hat_expected = Eigen::VectorXd::Zero(A_hat.size());
|
|
rhs_hat_expected.head(dut.A().size()) = dut.g();
|
|
EXPECT_TRUE(CompareMatrices(rhs_hat, rhs_hat_expected));
|
|
const int num_X_hat_rows = dut.num_X_rows() + dut.num_free_variables() + 1;
|
|
for (int i = 0; i < static_cast<int>(dut.A().size()); ++i) {
|
|
Eigen::MatrixXd A_hat_expected =
|
|
Eigen::MatrixXd::Zero(num_X_hat_rows, num_X_hat_rows);
|
|
A_hat_expected.block(0, 0, dut.A()[i].rows(), dut.A()[i].cols()) =
|
|
dut.A()[i];
|
|
const Eigen::VectorXd bi = dut.B().row(i).transpose();
|
|
A_hat_expected.block(dut.A()[i].rows(), dut.A()[i].cols() + 1, 1,
|
|
dut.B().cols()) = bi.transpose() / 2;
|
|
A_hat_expected.block(dut.A()[i].rows() + 1, dut.A()[i].cols(),
|
|
dut.B().cols(), 1) = bi / 2;
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(A_hat[i]), A_hat_expected));
|
|
}
|
|
// Now check the newly added linear equality constraint.
|
|
// Y(i, i) = Y(0, 0) and Y(i, j) = 0 for j > i >= 1
|
|
int A_hat_count = dut.A().size();
|
|
for (int i = 1; i < dut.num_free_variables() + 1; ++i) {
|
|
Eigen::MatrixXd A_hat_expected =
|
|
Eigen::MatrixXd::Zero(num_X_hat_rows, num_X_hat_rows);
|
|
A_hat_expected(dut.num_X_rows() + i, dut.num_X_rows() + i) = 1;
|
|
A_hat_expected(dut.num_X_rows(), dut.num_X_rows()) = -1;
|
|
EXPECT_TRUE(
|
|
CompareMatrices(Eigen::MatrixXd(A_hat[A_hat_count++]), A_hat_expected));
|
|
for (int j = i + 1; j < dut.num_free_variables() + 1; ++j) {
|
|
A_hat_expected.setZero();
|
|
A_hat_expected(dut.num_X_rows() + i, dut.num_X_rows() + j) = 0.5;
|
|
A_hat_expected(dut.num_X_rows() + j, dut.num_X_rows() + i) = 0.5;
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(A_hat[A_hat_count++]),
|
|
A_hat_expected));
|
|
}
|
|
}
|
|
|
|
Eigen::MatrixXd C_hat_expected =
|
|
Eigen::MatrixXd::Zero(num_X_hat_rows, num_X_hat_rows);
|
|
C_hat_expected.topLeftCorner(dut.num_X_rows(), dut.num_X_rows()) = dut.C();
|
|
C_hat_expected.block(dut.num_X_rows(), dut.num_X_rows() + 1, 1,
|
|
dut.num_free_variables()) =
|
|
Eigen::VectorXd(dut.d()).transpose() / 2;
|
|
C_hat_expected.block(dut.num_X_rows() + 1, dut.num_X_rows(),
|
|
dut.num_free_variables(), 1) =
|
|
Eigen::VectorXd(dut.d()) / 2;
|
|
EXPECT_TRUE(CompareMatrices(Eigen::MatrixXd(C_hat), C_hat_expected));
|
|
}
|
|
|
|
TEST_F(LinearProgramBoundingBox1,
|
|
RemoveFreeVariableByLorentzConeSlackApproach) {
|
|
TestRemoveFreeVariableByLorentzConeSlackApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(CsdpLinearProgram2, RemoveFreeVariableByLorentzConeSlackApproach) {
|
|
TestRemoveFreeVariableByLorentzConeSlackApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSDP2, RemoveFreeVariableByLorentzConeSlackApproach) {
|
|
TestRemoveFreeVariableByLorentzConeSlackApproach(*prog_);
|
|
}
|
|
|
|
TEST_F(TrivialSOCP1, RemoveFreeVariableByLorentzConeSlackApproach) {
|
|
TestRemoveFreeVariableByLorentzConeSlackApproach(*prog_);
|
|
}
|
|
|
|
GTEST_TEST(SdpaFreeFormatTest, EmptyConstraint) {
|
|
// Tests a program that contains an empty constraint 0 == 0.
|
|
// Construct SdpaFreeFormat with and without the empty constraint, they should
|
|
// be the same.
|
|
MathematicalProgram prog{};
|
|
const auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
prog.AddPositiveSemidefiniteConstraint(X);
|
|
const auto x = prog.NewContinuousVariables<1>();
|
|
prog.AddBoundingBoxConstraint(-1, 1, x);
|
|
SdpaFreeFormat dut_no_empty_constraint(prog);
|
|
prog.AddLinearEqualityConstraint(Vector1d(0), 0, x);
|
|
SdpaFreeFormat dut_empty_constraint(prog);
|
|
EXPECT_EQ(dut_empty_constraint.num_X_rows(),
|
|
dut_no_empty_constraint.num_X_rows());
|
|
EXPECT_EQ(dut_empty_constraint.num_free_variables(),
|
|
dut_no_empty_constraint.num_free_variables());
|
|
EXPECT_EQ(dut_empty_constraint.constant_min_cost_term(),
|
|
dut_no_empty_constraint.constant_min_cost_term());
|
|
EXPECT_EQ(dut_empty_constraint.A().size(),
|
|
dut_no_empty_constraint.A().size());
|
|
for (int i = 0; i < static_cast<int>(dut_empty_constraint.A().size()); ++i) {
|
|
EXPECT_TRUE(CompareMatrices(dut_empty_constraint.A()[i].toDense(),
|
|
dut_no_empty_constraint.A()[i].toDense()));
|
|
}
|
|
EXPECT_TRUE(CompareMatrices(dut_empty_constraint.C().toDense(),
|
|
dut_no_empty_constraint.C().toDense()));
|
|
EXPECT_TRUE(CompareMatrices(dut_empty_constraint.B().toDense(),
|
|
dut_no_empty_constraint.B().toDense()));
|
|
EXPECT_TRUE(CompareMatrices(dut_empty_constraint.d().toDense(),
|
|
dut_no_empty_constraint.d().toDense()));
|
|
|
|
// Now add an infeasible empty constraint, expect an exception.
|
|
prog.AddLinearConstraint(Eigen::RowVector2d(0, 0), 1, kInf,
|
|
Vector2<symbolic::Variable>(X(0, 0), X(1, 1)));
|
|
DRAKE_EXPECT_THROWS_MESSAGE(SdpaFreeFormat(prog),
|
|
".* the problem is infeasible as it contains.*");
|
|
}
|
|
|
|
} // namespace internal
|
|
GTEST_TEST(SdpaFreeFormatTest, GenerateSDPA1) {
|
|
// This is the sample program from http://plato.asu.edu/ftp/sdpa_format.txt
|
|
MathematicalProgram prog;
|
|
auto x = prog.NewContinuousVariables<2>();
|
|
auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
prog.AddBoundingBoxConstraint(0, kInf, x);
|
|
prog.AddPositiveSemidefiniteConstraint(X);
|
|
prog.AddLinearCost(-x(0) - 2 * x(1) - 3 * X(0, 0) - 4 * X(1, 1));
|
|
prog.AddLinearEqualityConstraint(x(0) + x(1), 10);
|
|
prog.AddLinearEqualityConstraint(
|
|
x(1) + 5 * X(0, 0) + 4 * X(0, 1) + 6 * X(1, 1), 20);
|
|
|
|
const std::string file_name = temp_directory() + "/sdpa";
|
|
EXPECT_TRUE(GenerateSDPA(prog, file_name));
|
|
EXPECT_TRUE(std::filesystem::exists({file_name + ".dat-s"}));
|
|
|
|
std::ifstream infile(file_name + ".dat-s");
|
|
ASSERT_TRUE(infile.is_open());
|
|
std::string line;
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 -2 ");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "10.0 20.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 1 1 1 3.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 1 2 2 4.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 2 1 1 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 2 2 2 2.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 1 1 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 2 2 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 1 1 1 5.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 1 1 2 2.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 1 2 2 6.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 2 2 2 1.0");
|
|
EXPECT_FALSE(std::getline(infile, line));
|
|
|
|
infile.close();
|
|
}
|
|
|
|
GTEST_TEST(SdpaFreeFormatTest, GenerateInvalidSDPA) {
|
|
// Test the program that cannot be formulated in SDPA format.
|
|
MathematicalProgram prog1;
|
|
prog1.NewBinaryVariables<2>();
|
|
EXPECT_THROW(GenerateSDPA(prog1, "tmp"), std::invalid_argument);
|
|
}
|
|
|
|
GTEST_TEST(SdpaFreeFormatTest, GenerateSDPA_remove_free_variables_two_slack) {
|
|
// Test GenerateSDPA with prog that has free variables.
|
|
MathematicalProgram prog;
|
|
auto x = prog.NewContinuousVariables<2>();
|
|
auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
prog.AddPositiveSemidefiniteConstraint(X);
|
|
prog.AddLinearEqualityConstraint(X(0, 0) + x(0) + x(1), 1);
|
|
prog.AddLinearCost(X(0, 1) + 2 * x(1));
|
|
|
|
internal::SdpaFreeFormat dut(prog);
|
|
EXPECT_GT(dut.num_free_variables(), 0);
|
|
const std::string file_name = temp_directory() + "/sdpa_free1";
|
|
EXPECT_TRUE(GenerateSDPA(prog, file_name,
|
|
RemoveFreeVariableMethod::kTwoSlackVariables));
|
|
EXPECT_TRUE(std::filesystem::exists({file_name + ".dat-s"}));
|
|
|
|
std::ifstream infile(file_name + ".dat-s");
|
|
ASSERT_TRUE(infile.is_open());
|
|
std::string line;
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 -4 ");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 1 1 2 -0.5");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 2 2 2 -2.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 2 4 4 2.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 1 1 1 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 1 1 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 2 2 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 3 3 -1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 4 4 -1.0");
|
|
EXPECT_FALSE(std::getline(infile, line));
|
|
|
|
infile.close();
|
|
}
|
|
|
|
GTEST_TEST(SdpaFreeFormatTest, GenerateSDPA_remove_free_variables_null_space) {
|
|
// Test GenerateSDPA with prog that has free variables.
|
|
MathematicalProgram prog;
|
|
auto x = prog.NewContinuousVariables<2>();
|
|
auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
prog.AddPositiveSemidefiniteConstraint(X);
|
|
prog.AddLinearEqualityConstraint(X(0, 0) + x(0) + x(1), 1);
|
|
|
|
internal::SdpaFreeFormat dut(prog);
|
|
EXPECT_GT(dut.num_free_variables(), 0);
|
|
const std::string file_name = temp_directory() + "/sdpa_free2";
|
|
EXPECT_TRUE(
|
|
GenerateSDPA(prog, file_name, RemoveFreeVariableMethod::kNullspace));
|
|
EXPECT_TRUE(std::filesystem::exists({file_name + ".dat-s"}));
|
|
std::ifstream infile(file_name + ".dat-s");
|
|
ASSERT_TRUE(infile.is_open());
|
|
std::string line;
|
|
std::getline(infile, line);
|
|
// The null space approach completely removes the constraint.
|
|
EXPECT_EQ(line, "0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 ");
|
|
std::getline(infile, line);
|
|
// The constraint is empty.
|
|
EXPECT_EQ(line, "");
|
|
|
|
infile.close();
|
|
}
|
|
|
|
GTEST_TEST(SdpaFreeFormatTest,
|
|
GenerateSDPA_remove_free_variables_lorentz_slack) {
|
|
// Test GenerateSDPA with prog that has free variables.
|
|
MathematicalProgram prog;
|
|
auto x = prog.NewContinuousVariables<2>();
|
|
auto X = prog.NewSymmetricContinuousVariables<2>();
|
|
prog.AddPositiveSemidefiniteConstraint(X);
|
|
prog.AddLinearEqualityConstraint(X(0, 0) + x(0) + x(1), 1);
|
|
prog.AddLinearCost(X(0, 1) + 2 * x(1));
|
|
|
|
internal::SdpaFreeFormat dut(prog);
|
|
EXPECT_GT(dut.num_free_variables(), 0);
|
|
const std::string file_name = temp_directory() + "/sdpa_free3";
|
|
EXPECT_TRUE(GenerateSDPA(prog, file_name,
|
|
RemoveFreeVariableMethod::kLorentzConeSlack));
|
|
EXPECT_TRUE(std::filesystem::exists({file_name + ".dat-s"}));
|
|
|
|
std::ifstream infile(file_name + ".dat-s");
|
|
ASSERT_TRUE(infile.is_open());
|
|
std::string line;
|
|
std::getline(infile, line);
|
|
// number of constraints.
|
|
EXPECT_EQ(line, "4");
|
|
std::getline(infile, line);
|
|
// nblocks
|
|
EXPECT_EQ(line, "2");
|
|
std::getline(infile, line);
|
|
// block sizes
|
|
EXPECT_EQ(line, "2 3 ");
|
|
std::getline(infile, line);
|
|
// constraint rhs
|
|
EXPECT_EQ(line, "1.0 0.0 0.0 0.0");
|
|
// Each non-zero entry in C
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 1 1 2 -0.5");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "0 2 1 3 -1.0");
|
|
// Each non-zero entry in Ai
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 1 1 1 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 1 2 0.5");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "1 2 1 3 0.5");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 2 1 1 -1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "2 2 2 2 1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "3 2 2 3 0.5");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "4 2 1 1 -1.0");
|
|
std::getline(infile, line);
|
|
EXPECT_EQ(line, "4 2 3 3 1.0");
|
|
EXPECT_FALSE(std::getline(infile, line));
|
|
|
|
infile.close();
|
|
}
|
|
|
|
} // namespace solvers
|
|
} // namespace drake
|