From bab20edb5a7419910eb168becc86a5c8552666e2 Mon Sep 17 00:00:00 2001 From: Arjun Jagdish Ram <arjun.ram@tier4.jp> Date: Mon, 17 Mar 2025 14:14:28 +0900 Subject: [PATCH 1/2] fix Signed-off-by: Arjun Jagdish Ram <arjun.ram@tier4.jp> --- .../design/osqp_interface-design.md | 2 +- .../osqp_interface/csc_matrix_conv.hpp | 38 +++++++++++++ .../osqp_interface/osqp_interface.hpp | 21 ++++++-- .../src/osqp_interface.cpp | 25 +++++---- .../test/test_osqp_interface.cpp | 53 +++++++++---------- 5 files changed, 96 insertions(+), 43 deletions(-) diff --git a/common/autoware_osqp_interface/design/osqp_interface-design.md b/common/autoware_osqp_interface/design/osqp_interface-design.md index 887a4b4d9a..31dd5625b6 100644 --- a/common/autoware_osqp_interface/design/osqp_interface-design.md +++ b/common/autoware_osqp_interface/design/osqp_interface-design.md @@ -54,7 +54,7 @@ The interface can be used in several ways: ```cpp std::tuple<std::vector<double>, std::vector<double>> result = osqp_interface.optimize(); - std::vector<double> param = std::get<0>(result); + std::vector<double> param = result.primal_solution; double x_0 = param[0]; double x_1 = param[1]; ``` diff --git a/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp b/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp index 8c21553152..d13d8ccb94 100644 --- a/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp +++ b/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp @@ -33,6 +33,44 @@ struct OSQP_INTERFACE_PUBLIC CSC_Matrix std::vector<c_int> m_row_idxs; /// Vector of 'val' indices where each column starts. Ex: [0, 2, 4] (Eigen: 'outer') std::vector<c_int> m_col_idxs; + + friend std::ostream & operator<<(std::ostream & os, const CSC_Matrix & matrix) + { + os << "CSC_Matrix: {\n"; + os << "\tm_vals: ["; + + // Iterator-based loop for m_vals + for (auto it = std::begin(matrix.m_vals); it != std::end(matrix.m_vals); ++it) { + os << *it; // Print the current element (dereference iterator) + if (std::next(it) != std::end(matrix.m_vals)) { // Check if not the last element + os << ", "; + } + } + os << "],\n"; + + os << "\tm_row_idxs: ["; + // Iterator-based loop for m_row_idxs + for (auto it = std::begin(matrix.m_row_idxs); it != std::end(matrix.m_row_idxs); ++it) { + os << *it; + if (std::next(it) != std::end(matrix.m_row_idxs)) { + os << ", "; + } + } + os << "],\n"; + + os << "\tm_col_idxs: ["; + // Iterator-based loop for m_col_idxs + for (auto it = std::begin(matrix.m_col_idxs); it != std::end(matrix.m_col_idxs); ++it) { + os << *it; + if (std::next(it) != std::end(matrix.m_col_idxs)) { + os << ", "; + } + } + os << "]\n"; + + os << "}\n"; + return os; + } }; /// \brief Calculate CSC matrix from Eigen matrix diff --git a/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp b/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp index ff3013bd61..60a8202e3c 100644 --- a/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp +++ b/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp @@ -32,6 +32,17 @@ namespace autoware::osqp_interface { constexpr c_float INF = 1e30; + +struct OSQPResult +{ + std::vector<double> primal_solution; + std::vector<double> lagrange_multipliers; + int polish_status; + int solution_status; + int iteration_status; + int exit_flag; +}; + /** * Implementation of a native C++ interface for the OSQP solver. * @@ -52,7 +63,7 @@ class OSQP_INTERFACE_PUBLIC OSQPInterface int64_t m_exitflag; // Runs the solver on the stored problem. - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> solve(); + OSQPResult solve(); static void OSQPWorkspaceDeleter(OSQPWorkspace * ptr) noexcept; @@ -93,10 +104,10 @@ class OSQP_INTERFACE_PUBLIC OSQPInterface /// \details std::tuple<std::vector<double>, std::vector<double>> result; /// \details result = osqp_interface.optimize(); /// \details 4. Access the optimized parameters. - /// \details std::vector<float> param = std::get<0>(result); + /// \details std::vector<float> param = result.primal_solution; /// \details double x_0 = param[0]; /// \details double x_1 = param[1]; - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> optimize(); + OSQPResult optimize(); /// \brief Solves convex quadratic programs (QPs) using the OSQP solver. /// \return The function returns a tuple containing the solution as two float vectors. @@ -111,10 +122,10 @@ class OSQP_INTERFACE_PUBLIC OSQPInterface /// \details std::tuple<std::vector<double>, std::vector<double>> result; /// \details result = osqp_interface.optimize(P, A, q, l, u, 1e-6); /// \details 4. Access the optimized parameters. - /// \details std::vector<float> param = std::get<0>(result); + /// \details std::vector<float> param = result.primal_solution; /// \details double x_0 = param[0]; /// \details double x_1 = param[1]; - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> optimize( + OSQPResult optimize( const Eigen::MatrixXd & P, const Eigen::MatrixXd & A, const std::vector<double> & q, const std::vector<double> & l, const std::vector<double> & u); diff --git a/common/autoware_osqp_interface/src/osqp_interface.cpp b/common/autoware_osqp_interface/src/osqp_interface.cpp index ceeae62622..c528c20012 100644 --- a/common/autoware_osqp_interface/src/osqp_interface.cpp +++ b/common/autoware_osqp_interface/src/osqp_interface.cpp @@ -359,12 +359,12 @@ int64_t OSQPInterface::initializeProblem( return m_exitflag; } -std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> +OSQPResult OSQPInterface::solve() { // Solve Problem - osqp_solve(m_work.get()); - + int32_t exit_flag = osqp_solve(m_work.get()); + /******************** * EXTRACT SOLUTION ********************/ @@ -378,24 +378,29 @@ OSQPInterface::solve() int64_t status_iteration = m_work->info->iter; // Result tuple - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> result = - std::make_tuple( - sol_primal, sol_lagrange_multiplier, status_polish, status_solution, status_iteration); + OSQPResult result; + + result.primal_solution = sol_primal; + result.lagrange_multipliers = sol_lagrange_multiplier; + result.polish_status = status_polish; + result.solution_status = status_solution; + result.iteration_status = status_iteration; + result.exit_flag = exit_flag; m_latest_work_info = *(m_work->info); return result; } -std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> +OSQPResult OSQPInterface::optimize() { // Run the solver on the stored problem representation. - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> result = solve(); + OSQPResult result = solve(); return result; } -std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> +OSQPResult OSQPInterface::optimize( const Eigen::MatrixXd & P, const Eigen::MatrixXd & A, const std::vector<double> & q, const std::vector<double> & l, const std::vector<double> & u) @@ -404,7 +409,7 @@ OSQPInterface::optimize( initializeProblem(P, A, q, l, u); // Run the solver on the stored problem representation. - std::tuple<std::vector<double>, std::vector<double>, int64_t, int64_t, int64_t> result = solve(); + OSQPResult result = solve(); m_work.reset(); m_work_initialized = false; diff --git a/common/autoware_osqp_interface/test/test_osqp_interface.cpp b/common/autoware_osqp_interface/test/test_osqp_interface.cpp index 763baa1135..7305c9709a 100644 --- a/common/autoware_osqp_interface/test/test_osqp_interface.cpp +++ b/common/autoware_osqp_interface/test/test_osqp_interface.cpp @@ -44,25 +44,24 @@ TEST(TestOsqpInterface, BasicQp) using autoware::osqp_interface::calCSCMatrixTrapezoidal; using autoware::osqp_interface::CSC_Matrix; - auto check_result = - [](const std::tuple<std::vector<double>, std::vector<double>, int, int, int> & result) { - EXPECT_EQ(std::get<2>(result), 1); // polish succeeded - EXPECT_EQ(std::get<3>(result), 1); // solution succeeded - - static const auto ep = 1.0e-8; - - const auto prime_val = std::get<0>(result); - ASSERT_EQ(prime_val.size(), size_t(2)); - EXPECT_NEAR(prime_val[0], 0.3, ep); - EXPECT_NEAR(prime_val[1], 0.7, ep); - - const auto dual_val = std::get<1>(result); - ASSERT_EQ(dual_val.size(), size_t(4)); - EXPECT_NEAR(dual_val[0], -2.9, ep); - EXPECT_NEAR(dual_val[1], 0.0, ep); - EXPECT_NEAR(dual_val[2], 0.2, ep); - EXPECT_NEAR(dual_val[3], 0.0, ep); - }; + auto check_result = [](const autoware::osqp_interface::OSQPResult & result) { + EXPECT_EQ(result.polish_status, 1); // polish succeeded + EXPECT_EQ(result.solution_status, 1); // solution succeeded + + static const auto ep = 1.0e-8; + + const auto prime_val = result.primal_solution; + ASSERT_EQ(prime_val.size(), size_t(2)); + EXPECT_NEAR(prime_val[0], 0.3, ep); + EXPECT_NEAR(prime_val[1], 0.7, ep); + + const auto dual_val = result.lagrange_multipliers; + ASSERT_EQ(dual_val.size(), size_t(4)); + EXPECT_NEAR(dual_val[0], -2.9, ep); + EXPECT_NEAR(dual_val[1], 0.0, ep); + EXPECT_NEAR(dual_val[2], 0.2, ep); + EXPECT_NEAR(dual_val[3], 0.0, ep); + }; const Eigen::MatrixXd P = (Eigen::MatrixXd(2, 2) << 4, 1, 1, 2).finished(); const Eigen::MatrixXd A = (Eigen::MatrixXd(4, 2) << 1, 1, 1, 0, 0, 1, 0, 1).finished(); @@ -73,7 +72,7 @@ TEST(TestOsqpInterface, BasicQp) { // Define problem during optimization autoware::osqp_interface::OSQPInterface osqp; - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result = + autoware::osqp_interface::OSQPResult result = osqp.optimize(P, A, q, l, u); check_result(result); } @@ -81,12 +80,12 @@ TEST(TestOsqpInterface, BasicQp) { // Define problem during initialization autoware::osqp_interface::OSQPInterface osqp(P, A, q, l, u, 1e-6); - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result = osqp.optimize(); + autoware::osqp_interface::OSQPResult result = osqp.optimize(); check_result(result); } { - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result; + autoware::osqp_interface::OSQPResult result; // Dummy initial problem Eigen::MatrixXd P_ini = Eigen::MatrixXd::Zero(2, 2); Eigen::MatrixXd A_ini = Eigen::MatrixXd::Zero(4, 2); @@ -107,12 +106,12 @@ TEST(TestOsqpInterface, BasicQp) CSC_Matrix P_csc = calCSCMatrixTrapezoidal(P); CSC_Matrix A_csc = calCSCMatrix(A); autoware::osqp_interface::OSQPInterface osqp(P_csc, A_csc, q, l, u, 1e-6); - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result = osqp.optimize(); + autoware::osqp_interface::OSQPResult result = osqp.optimize(); check_result(result); } { - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result; + autoware::osqp_interface::OSQPResult result; // Dummy initial problem with csc matrix CSC_Matrix P_ini_csc = calCSCMatrixTrapezoidal(Eigen::MatrixXd::Zero(2, 2)); CSC_Matrix A_ini_csc = calCSCMatrix(Eigen::MatrixXd::Zero(4, 2)); @@ -132,7 +131,7 @@ TEST(TestOsqpInterface, BasicQp) // add warm startup { - std::tuple<std::vector<double>, std::vector<double>, int, int, int> result; + autoware::osqp_interface::OSQPResult result; // Dummy initial problem with csc matrix CSC_Matrix P_ini_csc = calCSCMatrixTrapezoidal(Eigen::MatrixXd::Zero(2, 2)); CSC_Matrix A_ini_csc = calCSCMatrix(Eigen::MatrixXd::Zero(4, 2)); @@ -150,8 +149,8 @@ TEST(TestOsqpInterface, BasicQp) check_result(result); osqp.updateCheckTermination(1); - const auto primal_val = std::get<0>(result); - const auto dual_val = std::get<1>(result); + const auto primal_val = result.primal_solution; + const auto dual_val = result.lagrange_multipliers; for (size_t i = 0; i < primal_val.size(); ++i) { std::cerr << primal_val.at(i) << std::endl; } From 8c266a15befe398827f0de10cfb19df4bf1a655d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 17 Mar 2025 05:19:54 +0000 Subject: [PATCH 2/2] style(pre-commit): autofix --- .../autoware/osqp_interface/csc_matrix_conv.hpp | 2 +- .../autoware/osqp_interface/osqp_interface.hpp | 1 - common/autoware_osqp_interface/src/osqp_interface.cpp | 11 ++++------- .../test/test_osqp_interface.cpp | 3 +-- 4 files changed, 6 insertions(+), 11 deletions(-) diff --git a/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp b/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp index d13d8ccb94..62138933ce 100644 --- a/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp +++ b/common/autoware_osqp_interface/include/autoware/osqp_interface/csc_matrix_conv.hpp @@ -33,7 +33,7 @@ struct OSQP_INTERFACE_PUBLIC CSC_Matrix std::vector<c_int> m_row_idxs; /// Vector of 'val' indices where each column starts. Ex: [0, 2, 4] (Eigen: 'outer') std::vector<c_int> m_col_idxs; - + friend std::ostream & operator<<(std::ostream & os, const CSC_Matrix & matrix) { os << "CSC_Matrix: {\n"; diff --git a/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp b/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp index 60a8202e3c..31b2b1ae70 100644 --- a/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp +++ b/common/autoware_osqp_interface/include/autoware/osqp_interface/osqp_interface.hpp @@ -32,7 +32,6 @@ namespace autoware::osqp_interface { constexpr c_float INF = 1e30; - struct OSQPResult { std::vector<double> primal_solution; diff --git a/common/autoware_osqp_interface/src/osqp_interface.cpp b/common/autoware_osqp_interface/src/osqp_interface.cpp index c528c20012..8b11ed148f 100644 --- a/common/autoware_osqp_interface/src/osqp_interface.cpp +++ b/common/autoware_osqp_interface/src/osqp_interface.cpp @@ -359,12 +359,11 @@ int64_t OSQPInterface::initializeProblem( return m_exitflag; } -OSQPResult -OSQPInterface::solve() +OSQPResult OSQPInterface::solve() { // Solve Problem int32_t exit_flag = osqp_solve(m_work.get()); - + /******************** * EXTRACT SOLUTION ********************/ @@ -392,16 +391,14 @@ OSQPInterface::solve() return result; } -OSQPResult -OSQPInterface::optimize() +OSQPResult OSQPInterface::optimize() { // Run the solver on the stored problem representation. OSQPResult result = solve(); return result; } -OSQPResult -OSQPInterface::optimize( +OSQPResult OSQPInterface::optimize( const Eigen::MatrixXd & P, const Eigen::MatrixXd & A, const std::vector<double> & q, const std::vector<double> & l, const std::vector<double> & u) { diff --git a/common/autoware_osqp_interface/test/test_osqp_interface.cpp b/common/autoware_osqp_interface/test/test_osqp_interface.cpp index 7305c9709a..fcb252d052 100644 --- a/common/autoware_osqp_interface/test/test_osqp_interface.cpp +++ b/common/autoware_osqp_interface/test/test_osqp_interface.cpp @@ -72,8 +72,7 @@ TEST(TestOsqpInterface, BasicQp) { // Define problem during optimization autoware::osqp_interface::OSQPInterface osqp; - autoware::osqp_interface::OSQPResult result = - osqp.optimize(P, A, q, l, u); + autoware::osqp_interface::OSQPResult result = osqp.optimize(P, A, q, l, u); check_result(result); }