#include "drake/solvers/linear_system_solver.h" #include #include #include "drake/common/test_utilities/eigen_matrix_compare.h" #include "drake/common/test_utilities/expect_throws_message.h" #include "drake/solvers/solve.h" #include "drake/solvers/test/mathematical_program_test_util.h" #include "drake/solvers/test/optimization_examples.h" using ::testing::HasSubstr; namespace drake { namespace solvers { namespace test { namespace { void TestLinearSystemExample(LinearSystemExample1* example) { const MathematicalProgram& prog = *example->prog(); EXPECT_TRUE(LinearSystemSolver::ProgramAttributesSatisfied(prog)); EXPECT_EQ(LinearSystemSolver::UnsatisfiedProgramAttributes(prog), ""); const MathematicalProgramResult result = Solve(prog); EXPECT_TRUE(result.is_success()); example->CheckSolution(result); } } // namespace GTEST_TEST(testLinearSystemSolver, trivialExample) { LinearSystemExample1 example1{}; TestLinearSystemExample(&example1); LinearSystemExample2 example2{}; TestLinearSystemExample(&example2); LinearSystemExample3 example3{}; TestLinearSystemExample(&example3); } GTEST_TEST(testLinearSystemSolver, EmptyProblem) { MathematicalProgram prog; EXPECT_FALSE(LinearSystemSolver::ProgramAttributesSatisfied(prog)); EXPECT_THAT(LinearSystemSolver::UnsatisfiedProgramAttributes(prog), HasSubstr("LinearEqualityConstraint is required")); LinearSystemSolver solver; DRAKE_EXPECT_THROWS_MESSAGE(solver.Solve(prog), ".*LinearEqualityConstraint is required.*"); } GTEST_TEST(testLinearSystemSolver, QuadraticProblem) { MathematicalProgram prog; auto x = prog.NewContinuousVariables<1>(); prog.AddQuadraticCost(x(0) * x(0)); EXPECT_FALSE(LinearSystemSolver::ProgramAttributesSatisfied(prog)); EXPECT_THAT(LinearSystemSolver::UnsatisfiedProgramAttributes(prog), HasSubstr("QuadraticCost was declared")); LinearSystemSolver solver; DRAKE_EXPECT_THROWS_MESSAGE(solver.Solve(prog), ".*QuadraticCost was declared.*"); } /** * Simple linear system without a solution * 3 * x = 1 * 2 * x + y = 2 * x - y = 0 */ GTEST_TEST(testLinearSystemSolver, InfeasibleProblem) { MathematicalProgram prog; auto x = prog.NewContinuousVariables<2>(); prog.AddLinearConstraint(3 * x(0) == 1 && 2 * x(0) + x(1) == 2 && x(0) - x(1) == 0); const MathematicalProgramResult result = Solve(prog); EXPECT_FALSE(result.is_success()); // The solution should minimize the error ||b - A * x||₂ // x_expected is computed as (Aᵀ*A)⁻¹*(Aᵀ*b) Eigen::Vector2d x_expected(12.0 / 27, 21.0 / 27); EXPECT_TRUE(CompareMatrices(result.GetSolution(x), x_expected, 1E-12, MatrixCompareType::absolute)); EXPECT_EQ(result.get_optimal_cost(), MathematicalProgram::kGlobalInfeasibleCost); } /** * Under-determined linear system * 3 * x + y = 1 * x + z = 2 */ GTEST_TEST(testLinearSystemSolver, UnderDeterminedProblem) { MathematicalProgram prog; auto x = prog.NewContinuousVariables<3>(); prog.AddLinearConstraint(3 * x(0) + x(1) == 1 && x(0) + x(2) == 2); const MathematicalProgramResult result = Solve(prog); EXPECT_TRUE(result.is_success()); // The solution should minimize the norm ||x||₂ // x_expected is computed as the solution to // [2*I -Aᵀ] * [x] = [0] // [ A 0 ] [λ] [b] Eigen::Vector3d x_expected(5.0 / 11, -4.0 / 11, 17.0 / 11); EXPECT_TRUE(CompareMatrices(result.GetSolution(x), x_expected, 1E-12, MatrixCompareType::absolute)); } GTEST_TEST(testLinearSystemSolver, linearMatrixEqualityExample) { LinearMatrixEqualityExample example{}; const auto result = Solve(*(example.prog())); EXPECT_EQ(result.get_solver_id(), LinearSystemSolver::id()); example.CheckSolution(result); } } // namespace test } // namespace solvers } // namespace drake