From a661433ce3b246e1c6af5259308a8ebef6056479 Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Wed, 24 Mar 2021 18:36:28 +0100 Subject: [PATCH 1/6] Add the possibility to solve unconstrained QP problem without setting the constraint matrix --- src/Data.cpp | 9 ++++++--- src/Solver.cpp | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/Data.cpp b/src/Data.cpp index 499d935..56aaa01 100644 --- a/src/Data.cpp +++ b/src/Data.cpp @@ -87,13 +87,16 @@ OSQPData* const & OsqpEigen::Data::getData() const bool OsqpEigen::Data::isSet() const { + const bool areConstraintsOk = (m_data->m == 0) || + m_isLinearConstraintsMatrixSet && + m_isLowerBoundSet && + m_isUpperBoundSet; + return m_isNumberOfVariablesSet && m_isNumberOfConstraintsSet && m_isHessianMatrixSet && m_isGradientSet && - m_isLinearConstraintsMatrixSet && - m_isLowerBoundSet && - m_isUpperBoundSet; + areConstraintsOk; } bool OsqpEigen::Data::setGradient(Eigen::Ref> gradient) diff --git a/src/Solver.cpp b/src/Solver.cpp index bed6479..f80a4ae 100644 --- a/src/Solver.cpp +++ b/src/Solver.cpp @@ -79,6 +79,25 @@ bool OsqpEigen::Solver::initSolver() return false; } + // if the number of constraints is equal to zero the user may not + // call setLinearConstraintsMatrix() + if(m_data->getData()->m == 0) + { + if(m_data->getData()->A == nullptr) + { + // let's create the matrix manually. This is required by osqp. Please check + // https://github.com/oxfordcontrol/osqp/issues/295 + Eigen::SparseMatrix A(m_data->getData()->m, m_data->getData()->n); + if(!m_data->setLinearConstraintsMatrix(A)) + { + debugStream() << "[OsqpEigen::Solver::initSolver] Unable to set the empty linear constraint " + << "matrix in case of unconstrained optimization problem" + << std::endl; + return false; + } + } + } + OSQPWorkspace* workspace; if(osqp_setup(&workspace, m_data->getData(), m_settings->getSettings()) != 0 ){ From f297814e3311127e0ae0b4bf362c12b980640fb8 Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Wed, 24 Mar 2021 18:36:51 +0100 Subject: [PATCH 2/6] Update the QPProblem test --- tests/QPTest.cpp | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/QPTest.cpp b/tests/QPTest.cpp index ccb4703..87764d9 100644 --- a/tests/QPTest.cpp +++ b/tests/QPTest.cpp @@ -12,18 +12,19 @@ TEST_CASE("QPProblem") { - Eigen::Matrix2d H; - H << 4, 1, - 1, 2; - Eigen::SparseMatrix H_s; - H_s = H.sparseView(); - - Eigen::Matrix A; - A << 1, 1, - 1, 0, - 0, 1; - Eigen::SparseMatrix A_s; - A_s = A.sparseView(); + constexpr double tolerance = 1e-4; + + Eigen::SparseMatrix H_s(2,2); + H_s.insert(0,0) = 4; + H_s.insert(0,1) = 1; + H_s.insert(1,0) = 1; + H_s.insert(1,1) = 2; + + Eigen::SparseMatrix A_s(3,2); + A_s.insert(0,0) = 1; + A_s.insert(0,1) = 1; + A_s.insert(1,0) = 1; + A_s.insert(2,1) = 1; Eigen::Vector2d gradient; gradient << 1, 1; @@ -36,6 +37,7 @@ TEST_CASE("QPProblem") OsqpEigen::Solver solver; solver.settings()->setVerbosity(false); + solver.settings()->setAlpha(1.0); REQUIRE_FALSE(solver.data()->setHessianMatrix(H_s)); solver.data()->setNumberOfVariables(2); @@ -47,12 +49,10 @@ TEST_CASE("QPProblem") REQUIRE(solver.data()->setLowerBound(lowerBound)); REQUIRE(solver.data()->setUpperBound(upperBound)); - REQUIRE(solver.initSolver()); REQUIRE(solver.solve()); - - std::cerr << "The solution of the QP problem is" << std::endl; - std::cerr << "[ " << solver.getSolution() << " ]" - << std::endl; + Eigen::Vector2d expectedSolution; + expectedSolution << 0.3, 0.7; + REQUIRE(solver.getSolution().isApprox(expectedSolution, tolerance)); } From 213c1bd0e4c7f2b4a58eb8e7542fd5151cc1d830 Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Wed, 24 Mar 2021 18:37:06 +0100 Subject: [PATCH 3/6] Add the Unconstrained QP test --- tests/QPTest.cpp | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/QPTest.cpp b/tests/QPTest.cpp index 87764d9..2c01240 100644 --- a/tests/QPTest.cpp +++ b/tests/QPTest.cpp @@ -10,6 +10,37 @@ #include +TEST_CASE("QPProblem - Unconstrained") +{ + constexpr double tolerance = 1e-4; + + Eigen::SparseMatrix H_s(2,2); + H_s.insert(0,0) = 3; + H_s.insert(0,1) = 2; + H_s.insert(1,0) = 2; + H_s.insert(1,1) = 4; + + Eigen::Vector2d gradient; + gradient << 3, 1; + + OsqpEigen::Solver solver; + solver.settings()->setVerbosity(false); + solver.data()->setNumberOfVariables(2); + solver.data()->setNumberOfConstraints(0); + + REQUIRE(solver.data()->setHessianMatrix(H_s)); + REQUIRE(solver.data()->setGradient(gradient)); + + REQUIRE(solver.initSolver()); + REQUIRE(solver.solve()); + + // expected solution + Eigen::Vector2d expectedSolution; + expectedSolution << -1.2500, 0.3750; + REQUIRE(solver.getSolution().isApprox(expectedSolution, tolerance)); +} + + TEST_CASE("QPProblem") { constexpr double tolerance = 1e-4; From 07c711783e56afdde18b42322241dcf72041399f Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Wed, 24 Mar 2021 21:42:23 +0100 Subject: [PATCH 4/6] Bump the version to 0.6.2 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2ee9138..0e749ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.5) project(OsqpEigen - VERSION 0.6.1) + VERSION 0.6.2) # ouptut paths set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") From 191439a09c0e52f55d7f60314ddfd2792b964ac6 Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Thu, 25 Mar 2021 10:27:55 +0100 Subject: [PATCH 5/6] Set alpha in QPTest --- tests/QPTest.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/QPTest.cpp b/tests/QPTest.cpp index 2c01240..8ed8a8f 100644 --- a/tests/QPTest.cpp +++ b/tests/QPTest.cpp @@ -24,7 +24,9 @@ TEST_CASE("QPProblem - Unconstrained") gradient << 3, 1; OsqpEigen::Solver solver; - solver.settings()->setVerbosity(false); + solver.settings()->setVerbosity(true); + solver.settings()->setAlpha(1.0); + solver.data()->setNumberOfVariables(2); solver.data()->setNumberOfConstraints(0); @@ -37,6 +39,7 @@ TEST_CASE("QPProblem - Unconstrained") // expected solution Eigen::Vector2d expectedSolution; expectedSolution << -1.2500, 0.3750; + REQUIRE(solver.getSolution().isApprox(expectedSolution, tolerance)); } @@ -67,7 +70,7 @@ TEST_CASE("QPProblem") upperBound << 1, 0.7, 0.7; OsqpEigen::Solver solver; - solver.settings()->setVerbosity(false); + solver.settings()->setVerbosity(true); solver.settings()->setAlpha(1.0); REQUIRE_FALSE(solver.data()->setHessianMatrix(H_s)); @@ -85,5 +88,6 @@ TEST_CASE("QPProblem") REQUIRE(solver.solve()); Eigen::Vector2d expectedSolution; expectedSolution << 0.3, 0.7; + REQUIRE(solver.getSolution().isApprox(expectedSolution, tolerance)); } From af7aa6d25703b8f353a36eff686a13b7ce8b0801 Mon Sep 17 00:00:00 2001 From: Giulio Romualdi Date: Thu, 25 Mar 2021 13:17:06 +0100 Subject: [PATCH 6/6] Bump the version to 0.6.3 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0e749ef..a8aca72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required(VERSION 3.5) project(OsqpEigen - VERSION 0.6.2) + VERSION 0.6.3) # ouptut paths set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}")