From f004de9da9c5a6d860e569a38190ec9c38cd8107 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 15:38:38 +0200 Subject: [PATCH 01/44] [python/external] swap out pybind11 for nanobind --- .gitmodules | 6 +++--- bindings/python/external/nanobind | 1 + bindings/python/external/pybind11 | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) create mode 160000 bindings/python/external/nanobind delete mode 160000 bindings/python/external/pybind11 diff --git a/.gitmodules b/.gitmodules index 69ac5fe4e..2867bf33b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ -[submodule "external/pybind11"] - path = bindings/python/external/pybind11 - url = https://github.com/pybind/pybind11 +[submodule "bindings/python/external/nanobind"] + path = bindings/python/external/nanobind + url = https://github.com/wjakob/nanobind [submodule "cmake-module"] path = cmake-module url = https://github.com/jrl-umi3218/jrl-cmakemodules.git diff --git a/bindings/python/external/nanobind b/bindings/python/external/nanobind new file mode 160000 index 000000000..8d7f1ee06 --- /dev/null +++ b/bindings/python/external/nanobind @@ -0,0 +1 @@ +Subproject commit 8d7f1ee0621c17fa370b704b2100ffa0243d5bfb diff --git a/bindings/python/external/pybind11 b/bindings/python/external/pybind11 deleted file mode 160000 index 849322806..000000000 --- a/bindings/python/external/pybind11 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 849322806cd4b3697ad1d35eedd6d0352c5f267a From d28e4ebbd4b65dbfab00d74840259f6e588d7c71 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 18:11:00 +0200 Subject: [PATCH 02/44] [bindings/python] update CMake listfile, targets and instruction-set.cpp for nanobind [bindings/python] cmake : nanobind's helper function already sets prefix and suffix for us. --- bindings/python/CMakeLists.txt | 25 +++++++++------------ bindings/python/helpers/instruction-set.cpp | 4 ++-- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 9f2f93d30..d6b3cf216 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -6,6 +6,7 @@ include(${JRL_CMAKE_MODULES}/python.cmake) include(${JRL_CMAKE_MODULES}/python-helpers.cmake) findpython(REQUIRED Development.Module) +find_package(Python 3.8 COMPONENTS ${PYTHON_COMPONENTS}) if(IS_ABSOLUTE ${PYTHON_SITELIB}) set(${PYWRAP}_INSTALL_DIR ${PYTHON_SITELIB}/${PROJECT_NAME}) @@ -14,12 +15,9 @@ else() ${CMAKE_INSTALL_PREFIX}/${PYTHON_SITELIB}/${PROJECT_NAME}) endif() -find_package(pybind11 CONFIG) -if(NOT pybind11_FOUND) - file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/external/pybind11) - add_subdirectory(external/pybind11 - ${CMAKE_CURRENT_BINARY_DIR}/external/pybind11) -endif() +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) +add_subdirectory(external/nanobind + ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) add_custom_target(${PROJECT_NAME}_python) @@ -29,14 +27,12 @@ file(GLOB_RECURSE PYWRAP_SOURCES ${CMAKE_CURRENT_LIST_DIR}/src/*.cpp) # Add simd feature detectors for current intel CPU if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(x86)|(X86)|(amd64)|(AMD64)") - python3_add_library(instructionset MODULE helpers/instruction-set.cpp) + nanobind_add_module(instructionset helpers/instruction-set.cpp) add_dependencies(${PROJECT_NAME}_python instructionset) - target_link_libraries(instructionset PRIVATE proxsuite pybind11::module) + target_link_libraries(instructionset PRIVATE proxsuite) set_target_properties( instructionset PROPERTIES OUTPUT_NAME instructionset - PREFIX "" - SUFFIX ${PYTHON_EXT_SUFFIX} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_RELEASE @@ -72,12 +68,13 @@ function(list_filter list regular_expression dest_list) endfunction(list_filter) function(CREATE_PYTHON_TARGET target_name COMPILE_OPTIONS dependencies) - python3_add_library(${target_name} MODULE ${PYWRAP_SOURCES} ${PYWRAP_HEADERS}) + nanobind_add_module(${target_name} ${PYWRAP_SOURCES} + ${PYWRAP_HEADERS}) add_dependencies(${PROJECT_NAME}_python ${target_name}) - target_link_libraries(${target_name} PUBLIC ${dependencies} pybind11::module) + target_link_libraries(${target_name} PUBLIC ${dependencies}) target_compile_options(${target_name} PRIVATE ${COMPILE_OPTIONS}) - target_link_libraries(${target_name} PRIVATE proxsuite pybind11::module) + target_link_libraries(${target_name} PRIVATE proxsuite) target_compile_definitions(${target_name} PRIVATE PYTHON_MODULE_NAME=${target_name}) @@ -104,8 +101,6 @@ function(CREATE_PYTHON_TARGET target_name COMPILE_OPTIONS dependencies) set_target_properties( ${target_name} PROPERTIES OUTPUT_NAME ${target_name} - PREFIX "" - SUFFIX ${PYTHON_EXT_SUFFIX} LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_RELEASE diff --git a/bindings/python/helpers/instruction-set.cpp b/bindings/python/helpers/instruction-set.cpp index c99c43672..40342bb85 100644 --- a/bindings/python/helpers/instruction-set.cpp +++ b/bindings/python/helpers/instruction-set.cpp @@ -2,7 +2,7 @@ // Copyright (c) 2022 INRIA // -#include +#include #include @@ -10,7 +10,7 @@ namespace proxsuite { namespace helpers { namespace python { -PYBIND11_MODULE(instructionset, m) +NB_MODULE(instructionset, m) { m.doc() = R"pbdoc( CPU info library From 7168637f2d27e7a467875b2dce92210b7d10cf6d Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 19:59:01 +0200 Subject: [PATCH 03/44] [bindings/python] instruction-set.cpp : fix crashes because of lack of std::string support + add relevant nanobind stl support header --- bindings/python/helpers/instruction-set.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/bindings/python/helpers/instruction-set.cpp b/bindings/python/helpers/instruction-set.cpp index 40342bb85..cc92bf214 100644 --- a/bindings/python/helpers/instruction-set.cpp +++ b/bindings/python/helpers/instruction-set.cpp @@ -3,6 +3,7 @@ // #include +#include #include From 14ab3a38fdb69d4633be06c72c3fa4b55d4cc3b3 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:00:02 +0200 Subject: [PATCH 04/44] [proxqp/sparse] explicitly delete copy ctor and operator, fixing confusion with is_copy_constructible-type traits --- include/proxsuite/proxqp/sparse/wrapper.hpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/proxsuite/proxqp/sparse/wrapper.hpp b/include/proxsuite/proxqp/sparse/wrapper.hpp index b9884bc7e..6cfa48641 100644 --- a/include/proxsuite/proxqp/sparse/wrapper.hpp +++ b/include/proxsuite/proxqp/sparse/wrapper.hpp @@ -147,6 +147,11 @@ struct QP } } + QP(const QP&) = delete; + QP& operator=(const QP&) = delete; + QP(QP&&) = default; + QP& operator=(QP&&) = default; + /*! * Setups the QP model (with sparse matrix format) and equilibrates it. * @param H quadratic cost input defining the QP model. @@ -814,6 +819,10 @@ struct BatchQP } m_size = 0; } + BatchQP(const BatchQP&) = delete; + BatchQP& operator=(const BatchQP&) = delete; + BatchQP(BatchQP&&) = default; + BatchQP& operator=(BatchQP&&) = default; /*! * Init a QP in place and return a reference to it From a0a97fa7d65630ade9da5b42b7127e06910d64cb Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:01:15 +0200 Subject: [PATCH 05/44] [bindings/python] expose-all.cpp : do pybind11 -> nanobind swap --- bindings/python/src/expose-all.cpp | 38 ++++++++++++++++-------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/bindings/python/src/expose-all.cpp b/bindings/python/src/expose-all.cpp index 78545620b..cdfc4f87c 100644 --- a/bindings/python/src/expose-all.cpp +++ b/bindings/python/src/expose-all.cpp @@ -1,10 +1,12 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #include -#include -#include +#include +#include +#include +#include #include "algorithms.hpp" #include @@ -16,7 +18,7 @@ namespace python { template void -exposeCommon(pybind11::module_ m) +exposeCommon(nanobind::module_ m) { exposeResults(m); exposeSettings(m); @@ -29,7 +31,7 @@ exposeCommon(pybind11::module_ m) template void -exposeSparseAlgorithms(pybind11::module_ m) +exposeSparseAlgorithms(nanobind::module_ m) { sparse::python::exposeSparseModel(m); sparse::python::exposeQpObjectSparse(m); @@ -40,7 +42,7 @@ exposeSparseAlgorithms(pybind11::module_ m) template void -exposeDenseAlgorithms(pybind11::module_ m) +exposeDenseAlgorithms(nanobind::module_ m) { dense::python::exposeWorkspaceDense(m); dense::python::exposeDenseModel(m); @@ -51,7 +53,7 @@ exposeDenseAlgorithms(pybind11::module_ m) } template void -exposeBackward(pybind11::module_ m) +exposeBackward(nanobind::module_ m) { dense::python::backward(m); } @@ -59,19 +61,19 @@ exposeBackward(pybind11::module_ m) #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP template void -exposeDenseParallel(pybind11::module_ m) +exposeDenseParallel(nanobind::module_ m) { dense::python::solveDenseQpParallel(m); } template void -exposeSparseParallel(pybind11::module_ m) +exposeSparseParallel(nanobind::module_ m) { sparse::python::solveSparseQpParallel(m); } #endif -PYBIND11_MODULE(PYTHON_MODULE_NAME, m) +NB_MODULE(PYTHON_MODULE_NAME, m) { m.doc() = R"pbdoc( The proxSuite library @@ -84,17 +86,17 @@ PYBIND11_MODULE(PYTHON_MODULE_NAME, m) proxsuite )pbdoc"; - pybind11::module_ proxqp_module = + nanobind::module_ proxqp_module = m.def_submodule("proxqp", "The proxQP solvers of the proxSuite library"); exposeCommon(proxqp_module); - pybind11::module_ dense_module = + nanobind::module_ dense_module = proxqp_module.def_submodule("dense", "Dense solver of proxQP"); exposeDenseAlgorithms(dense_module); exposeBackward(dense_module); #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP exposeDenseParallel(dense_module); #endif - pybind11::module_ sparse_module = + nanobind::module_ sparse_module = proxqp_module.def_submodule("sparse", "Sparse solver of proxQP"); exposeSparseAlgorithms(sparse_module); #ifdef PROXSUITE_PYTHON_INTERFACE_WITH_OPENMP @@ -105,17 +107,17 @@ PYBIND11_MODULE(PYTHON_MODULE_NAME, m) m.attr("__version__") = helpers::printVersion(); // Add helpers - pybind11::module_ helpers_module = + nanobind::module_ helpers_module = m.def_submodule("helpers", "Helper module"); helpers_module.def("printVersion", helpers::printVersion, - pybind11::arg("delimiter") = ".", + nanobind::arg("delimiter") = ".", "Print the current version of the package."); helpers_module.def("checkVersionAtLeast", helpers::checkVersionAtLeast, - pybind11::arg("major_version"), - pybind11::arg("minor_version"), - pybind11::arg("patch_version"), + nanobind::arg("major_version"), + nanobind::arg("minor_version"), + nanobind::arg("patch_version"), "Check version of the package is at least greater than " "the one provided as input."); } From 86863f25792ce44db659d392c58eb50a36c32566 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:02:27 +0200 Subject: [PATCH 06/44] [bindings/python] expose-workspace.hpp : do pybind11 -> nanobind swap + only concerns def_* methods and pickling (now managed manually through setting the relevant special methods) --- bindings/python/src/expose-workspace.hpp | 130 +++++++++++------------ 1 file changed, 62 insertions(+), 68 deletions(-) diff --git a/bindings/python/src/expose-workspace.hpp b/bindings/python/src/expose-workspace.hpp index f827f4f34..9fa9fde65 100644 --- a/bindings/python/src/expose-workspace.hpp +++ b/bindings/python/src/expose-workspace.hpp @@ -1,87 +1,81 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // -#include -#include +#include +#include +#include #include #include #include #include #include -#include "helpers.hpp" + namespace proxsuite { namespace proxqp { namespace dense { namespace python { template void -exposeWorkspaceDense(pybind11::module_ m) +exposeWorkspaceDense(nanobind::module_ m) { - ::pybind11::class_>( - m, "workspace", pybind11::module_local()) - .def(::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + ::nanobind::class_>(m, "workspace") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Constructor using QP model dimensions.") // constructor) - .def_readonly("H_scaled", &Workspace::H_scaled) - .def_readonly("g_scaled", &Workspace::g_scaled) - .def_readonly("A_scaled", &Workspace::A_scaled) - .def_readonly("C_scaled", &Workspace::C_scaled) - .def_readonly("b_scaled", &Workspace::b_scaled) - .def_readonly("u_scaled", &Workspace::u_scaled) - .def_readonly("l_scaled", &Workspace::l_scaled) - .def_readonly("x_prev", &Workspace::x_prev) - .def_readonly("y_prev", &Workspace::y_prev) - .def_readonly("z_prev", &Workspace::z_prev) - .def_readonly("kkt", &Workspace::kkt) - .def_readonly("current_bijection_map", &Workspace::current_bijection_map) - .def_readonly("new_bijection_map", &Workspace::new_bijection_map) - .def_readonly("active_set_up", &Workspace::active_set_up) - .def_readonly("active_set_low", &Workspace::active_set_low) - .def_readonly("active_inequalities", &Workspace::active_inequalities) - .def_readonly("Hdx", &Workspace::Hdx) - .def_readonly("Cdx", &Workspace::Cdx) - .def_readonly("Adx", &Workspace::Adx) - .def_readonly("active_part_z", &Workspace::active_part_z) - .def_readonly("alphas", &Workspace::alphas) - .def_readonly("dw_aug", &Workspace::dw_aug) - .def_readonly("rhs", &Workspace::rhs) - .def_readonly("err", &Workspace::err) - .def_readonly("dual_feasibility_rhs_2", - &Workspace::dual_feasibility_rhs_2) - .def_readonly("correction_guess_rhs_g", - &Workspace::correction_guess_rhs_g) - .def_readonly("correction_guess_rhs_b", - &Workspace::correction_guess_rhs_b) - .def_readonly("alpha", &Workspace::alpha) - .def_readonly("dual_residual_scaled", &Workspace::dual_residual_scaled) - .def_readonly("primal_residual_in_scaled_up", - &Workspace::primal_residual_in_scaled_up) - .def_readonly("primal_residual_in_scaled_up_plus_alphaCdx", - &Workspace::primal_residual_in_scaled_up_plus_alphaCdx) - .def_readonly("primal_residual_in_scaled_low_plus_alphaCdx", - &Workspace::primal_residual_in_scaled_low_plus_alphaCdx) - .def_readonly("CTz", &Workspace::CTz) - .def_readonly("constraints_changed", &Workspace::constraints_changed) - .def_readonly("dirty", &Workspace::dirty) - .def_readonly("refactorize", &Workspace::refactorize) - .def_readonly("proximal_parameter_update", - &Workspace::proximal_parameter_update) - .def_readonly("is_initialized", &Workspace::is_initialized) - .def_readonly("n_c", &Workspace::n_c) - .def(pybind11::pickle( - - [](const Workspace& workspace) { - return pybind11::bytes( - proxsuite::serialization::saveToString(workspace)); - }, - [](pybind11::bytes& s) { - Workspace workspace; - proxsuite::serialization::loadFromString(workspace, s); - return workspace; - })); + .def_ro("H_scaled", &Workspace::H_scaled) + .def_ro("g_scaled", &Workspace::g_scaled) + .def_ro("A_scaled", &Workspace::A_scaled) + .def_ro("C_scaled", &Workspace::C_scaled) + .def_ro("b_scaled", &Workspace::b_scaled) + .def_ro("u_scaled", &Workspace::u_scaled) + .def_ro("l_scaled", &Workspace::l_scaled) + .def_ro("x_prev", &Workspace::x_prev) + .def_ro("y_prev", &Workspace::y_prev) + .def_ro("z_prev", &Workspace::z_prev) + .def_ro("kkt", &Workspace::kkt) + .def_ro("current_bijection_map", &Workspace::current_bijection_map) + .def_ro("new_bijection_map", &Workspace::new_bijection_map) + .def_ro("active_set_up", &Workspace::active_set_up) + .def_ro("active_set_low", &Workspace::active_set_low) + .def_ro("active_inequalities", &Workspace::active_inequalities) + .def_ro("Hdx", &Workspace::Hdx) + .def_ro("Cdx", &Workspace::Cdx) + .def_ro("Adx", &Workspace::Adx) + .def_ro("active_part_z", &Workspace::active_part_z) + .def_ro("alphas", &Workspace::alphas) + .def_ro("dw_aug", &Workspace::dw_aug) + .def_ro("rhs", &Workspace::rhs) + .def_ro("err", &Workspace::err) + .def_ro("dual_feasibility_rhs_2", &Workspace::dual_feasibility_rhs_2) + .def_ro("correction_guess_rhs_g", &Workspace::correction_guess_rhs_g) + .def_ro("correction_guess_rhs_b", &Workspace::correction_guess_rhs_b) + .def_ro("alpha", &Workspace::alpha) + .def_ro("dual_residual_scaled", &Workspace::dual_residual_scaled) + .def_ro("primal_residual_in_scaled_up", + &Workspace::primal_residual_in_scaled_up) + .def_ro("primal_residual_in_scaled_up_plus_alphaCdx", + &Workspace::primal_residual_in_scaled_up_plus_alphaCdx) + .def_ro("primal_residual_in_scaled_low_plus_alphaCdx", + &Workspace::primal_residual_in_scaled_low_plus_alphaCdx) + .def_ro("CTz", &Workspace::CTz) + .def_ro("constraints_changed", &Workspace::constraints_changed) + .def_ro("dirty", &Workspace::dirty) + .def_ro("refactorize", &Workspace::refactorize) + .def_ro("proximal_parameter_update", + &Workspace::proximal_parameter_update) + .def_ro("is_initialized", &Workspace::is_initialized) + .def_ro("n_c", &Workspace::n_c) + .def("__getstate__", + [](const Workspace& workspace) { + return proxsuite::serialization::saveToString(workspace); + }) + .def("__setstate__", [](Workspace& workspace, nanobind::bytes& s) { + new (&workspace) Workspace{}; + proxsuite::serialization::loadFromString(workspace, s.c_str()); + }); ; } From 82d372fced20b26da6ac1a805f40dca5c52f26c1 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:03:28 +0200 Subject: [PATCH 07/44] [bindings/python] helpers.hpp : do pybind11 -> nanobind swap --- bindings/python/src/helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/helpers.hpp b/bindings/python/src/helpers.hpp index cd0fe716a..fc7c37de6 100644 --- a/bindings/python/src/helpers.hpp +++ b/bindings/python/src/helpers.hpp @@ -5,7 +5,7 @@ #define proxsuite_python_helpers_hpp #define PROXSUITE_PYTHON_EIGEN_READWRITE(class, field_name, doc) \ - def_property( \ + def_prop_rw( \ #field_name, \ [](class& self) { return self.field_name; }, \ [](class& self, const decltype(class ::field_name)& value) { \ From 887b5c3b03ddd790992db26c57b78177fd6069c3 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:04:42 +0200 Subject: [PATCH 08/44] [bindings/python] expose-settings.hpp : do pybind11 -> nanobind swap + concerns def_* methods and pickling (now managed manually through setting the relevant special methods) + remove module_local(), as it is not supported in nanobind: https://nanobind.readthedocs.io/en/latest/porting.html#removed-features --- bindings/python/src/expose-settings.hpp | 139 +++++++++++------------- 1 file changed, 65 insertions(+), 74 deletions(-) diff --git a/bindings/python/src/expose-settings.hpp b/bindings/python/src/expose-settings.hpp index c9188ca1d..4569f7d3e 100644 --- a/bindings/python/src/expose-settings.hpp +++ b/bindings/python/src/expose-settings.hpp @@ -1,9 +1,10 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // -#include -#include -#include +#include +#include +#include +#include #include #include @@ -15,11 +16,10 @@ namespace proxqp { namespace python { template void -exposeSettings(pybind11::module_ m) +exposeSettings(nanobind::module_ m) { - ::pybind11::enum_( - m, "InitialGuess", pybind11::module_local()) + ::nanobind::enum_(m, "InitialGuess") .value("NO_INITIAL_GUESS", InitialGuessStatus::NO_INITIAL_GUESS) .value("EQUALITY_CONSTRAINED_INITIAL_GUESS", InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS) @@ -30,85 +30,76 @@ exposeSettings(pybind11::module_ m) InitialGuessStatus::COLD_START_WITH_PREVIOUS_RESULT) .export_values(); - ::pybind11::enum_( - m, "MeritFunctionType", pybind11::module_local()) + ::nanobind::enum_(m, "MeritFunctionType") .value("GPDAL", MeritFunctionType::GPDAL) .value("PDAL", MeritFunctionType::PDAL) .export_values(); - ::pybind11::enum_(m, "SparseBackend", pybind11::module_local()) + ::nanobind::enum_(m, "SparseBackend") .value("Automatic", SparseBackend::Automatic) .value("MatrixFree", SparseBackend::MatrixFree) .value("SparseCholesky", SparseBackend::SparseCholesky) .export_values(); - ::pybind11::enum_( - m, "EigenValueEstimateMethodOption", pybind11::module_local()) + ::nanobind::enum_( + m, "EigenValueEstimateMethodOption") .value("PowerIteration", EigenValueEstimateMethodOption::PowerIteration) .value("ExactMethod", EigenValueEstimateMethodOption::ExactMethod) .export_values(); - ::pybind11::class_>(m, "Settings", pybind11::module_local()) - .def(::pybind11::init(), "Default constructor.") // constructor - .def_readwrite("default_rho", &Settings::default_rho) - .def_readwrite("default_mu_eq", &Settings::default_mu_eq) - .def_readwrite("default_mu_in", &Settings::default_mu_in) - .def_readwrite("alpha_bcl", &Settings::alpha_bcl) - .def_readwrite("beta_bcl", &Settings::beta_bcl) - .def_readwrite("refactor_dual_feasibility_threshold", - &Settings::refactor_dual_feasibility_threshold) - .def_readwrite("refactor_rho_threshold", - &Settings::refactor_rho_threshold) - .def_readwrite("mu_min_eq", &Settings::mu_min_eq) - .def_readwrite("mu_min_in", &Settings::mu_min_in) - .def_readwrite("mu_max_eq_inv", &Settings::mu_max_eq_inv) - .def_readwrite("mu_max_in_inv", &Settings::mu_max_in_inv) - .def_readwrite("mu_update_factor", &Settings::mu_update_factor) - .def_readwrite("cold_reset_mu_eq", &Settings::cold_reset_mu_eq) - .def_readwrite("cold_reset_mu_in", &Settings::cold_reset_mu_in) - .def_readwrite("max_iter", &Settings::max_iter) - .def_readwrite("max_iter_in", &Settings::max_iter_in) - .def_readwrite("eps_abs", &Settings::eps_abs) - .def_readwrite("eps_rel", &Settings::eps_rel) - .def_readwrite("eps_primal_inf", &Settings::eps_primal_inf) - .def_readwrite("eps_dual_inf", &Settings::eps_dual_inf) - .def_readwrite("nb_iterative_refinement", - &Settings::nb_iterative_refinement) - .def_readwrite("initial_guess", &Settings::initial_guess) - .def_readwrite("sparse_backend", &Settings::sparse_backend) - .def_readwrite("preconditioner_accuracy", - &Settings::preconditioner_accuracy) - .def_readwrite("preconditioner_max_iter", - &Settings::preconditioner_max_iter) - .def_readwrite("compute_timings", &Settings::compute_timings) - .def_readwrite("compute_preconditioner", - &Settings::compute_preconditioner) - .def_readwrite("update_preconditioner", &Settings::update_preconditioner) - .def_readwrite("check_duality_gap", &Settings::check_duality_gap) - .def_readwrite("eps_duality_gap_abs", &Settings::eps_duality_gap_abs) - .def_readwrite("eps_duality_gap_rel", &Settings::eps_duality_gap_rel) - .def_readwrite("verbose", &Settings::verbose) - .def_readwrite("bcl_update", &Settings::bcl_update) - .def_readwrite("merit_function_type", &Settings::merit_function_type) - .def_readwrite("alpha_gpdal", &Settings::alpha_gpdal) - .def_readwrite("primal_infeasibility_solving", - &Settings::primal_infeasibility_solving) - .def_readwrite("frequence_infeasibility_check", - &Settings::frequence_infeasibility_check) - .def_readwrite("default_H_eigenvalue_estimate", - &Settings::default_H_eigenvalue_estimate) - .def(pybind11::self == pybind11::self) - .def(pybind11::self != pybind11::self) - .def(pybind11::pickle( - - [](const Settings& settings) { - return pybind11::bytes( - proxsuite::serialization::saveToString(settings)); - }, - [](pybind11::bytes& s) { - Settings settings; - proxsuite::serialization::loadFromString(settings, s); - return settings; - })); + ::nanobind::class_>(m, "Settings") + .def(::nanobind::init(), "Default constructor.") // constructor + .def_rw("default_rho", &Settings::default_rho) + .def_rw("default_mu_eq", &Settings::default_mu_eq) + .def_rw("default_mu_in", &Settings::default_mu_in) + .def_rw("alpha_bcl", &Settings::alpha_bcl) + .def_rw("beta_bcl", &Settings::beta_bcl) + .def_rw("refactor_dual_feasibility_threshold", + &Settings::refactor_dual_feasibility_threshold) + .def_rw("refactor_rho_threshold", &Settings::refactor_rho_threshold) + .def_rw("mu_min_eq", &Settings::mu_min_eq) + .def_rw("mu_min_in", &Settings::mu_min_in) + .def_rw("mu_max_eq_inv", &Settings::mu_max_eq_inv) + .def_rw("mu_max_in_inv", &Settings::mu_max_in_inv) + .def_rw("mu_update_factor", &Settings::mu_update_factor) + .def_rw("cold_reset_mu_eq", &Settings::cold_reset_mu_eq) + .def_rw("cold_reset_mu_in", &Settings::cold_reset_mu_in) + .def_rw("max_iter", &Settings::max_iter) + .def_rw("max_iter_in", &Settings::max_iter_in) + .def_rw("eps_abs", &Settings::eps_abs) + .def_rw("eps_rel", &Settings::eps_rel) + .def_rw("eps_primal_inf", &Settings::eps_primal_inf) + .def_rw("eps_dual_inf", &Settings::eps_dual_inf) + .def_rw("nb_iterative_refinement", &Settings::nb_iterative_refinement) + .def_rw("initial_guess", &Settings::initial_guess) + .def_rw("sparse_backend", &Settings::sparse_backend) + .def_rw("preconditioner_accuracy", &Settings::preconditioner_accuracy) + .def_rw("preconditioner_max_iter", &Settings::preconditioner_max_iter) + .def_rw("compute_timings", &Settings::compute_timings) + .def_rw("compute_preconditioner", &Settings::compute_preconditioner) + .def_rw("update_preconditioner", &Settings::update_preconditioner) + .def_rw("check_duality_gap", &Settings::check_duality_gap) + .def_rw("eps_duality_gap_abs", &Settings::eps_duality_gap_abs) + .def_rw("eps_duality_gap_rel", &Settings::eps_duality_gap_rel) + .def_rw("verbose", &Settings::verbose) + .def_rw("bcl_update", &Settings::bcl_update) + .def_rw("merit_function_type", &Settings::merit_function_type) + .def_rw("alpha_gpdal", &Settings::alpha_gpdal) + .def_rw("primal_infeasibility_solving", + &Settings::primal_infeasibility_solving) + .def_rw("frequence_infeasibility_check", + &Settings::frequence_infeasibility_check) + .def_rw("default_H_eigenvalue_estimate", + &Settings::default_H_eigenvalue_estimate) + .def(nanobind::self == nanobind::self) + .def(nanobind::self != nanobind::self) + .def("__getstate__", + [](const Settings& settings) { + return proxsuite::serialization::saveToString(settings); + }) + .def("__setstate__", [](Settings& settings, nanobind::bytes& s) { + new (&settings) Settings{}; + proxsuite::serialization::loadFromString(settings, s.c_str()); + }); ; } } // namespace python From ad7e215a13bdd0d0874255e303d18276cae2802f Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:06:11 +0200 Subject: [PATCH 09/44] [bindings/python] workspace, model : do pybind11 -> nanobind swap + concerns def_* methods and pickling (now managed manually through setting the relevant special methods) + remove module_local(), as it is not supported in nanobind: https://nanobind.readthedocs.io/en/latest/porting.html#removed-features + ::arg_v() no longer behaves the same. had to strip per-argument docstrings --- bindings/python/src/expose-helpers.hpp | 35 +++---- bindings/python/src/expose-model.hpp | 133 ++++++++++++------------- bindings/python/src/expose-results.hpp | 105 ++++++++++--------- 3 files changed, 130 insertions(+), 143 deletions(-) diff --git a/bindings/python/src/expose-helpers.hpp b/bindings/python/src/expose-helpers.hpp index 6dc8768e5..76cba0e70 100644 --- a/bindings/python/src/expose-helpers.hpp +++ b/bindings/python/src/expose-helpers.hpp @@ -2,9 +2,9 @@ // Copyright (c) 2022 INRIA // -#include -#include -#include +#include +#include +#include #include #include @@ -18,7 +18,7 @@ namespace python { template void -exposeDenseHelpers(pybind11::module_ m) +exposeDenseHelpers(nanobind::module_ m) { m.def( "estimate_minimal_eigen_value_of_symmetric_matrix", @@ -38,17 +38,11 @@ exposeDenseHelpers(pybind11::module_ m) "SelfAdjointEigenSolver from Eigen, " "or a Power Iteration algorithm (with parameters : " "power_iteration_accuracy and nb_power_iteration).", - pybind11::arg("H"), - pybind11::arg_v("estimate_method_option", - EigenValueEstimateMethodOption::ExactMethod, - "Two options are available for " - "estimating smallest eigenvalue: either a power " - "iteration algorithm, or an exact method from Eigen."), - pybind11::arg_v( - "power_iteration_accuracy", T(1.E-3), "power iteration accuracy."), - pybind11::arg_v("nb_power_iteration", - 1000, - "maximal number of power iteration executed.")); + nanobind::arg("H"), + nanobind::arg("estimate_method_option") = + EigenValueEstimateMethodOption::ExactMethod, + nanobind::arg("power_iteration_accuracy") = T(1.E-3), + nanobind::arg("nb_power_iteration") = 1000); } } // namespace python } // namespace dense @@ -59,7 +53,7 @@ namespace python { template void -exposeSparseHelpers(pybind11::module_ m) +exposeSparseHelpers(nanobind::module_ m) { m.def("estimate_minimal_eigen_value_of_symmetric_matrix", &sparse::estimate_minimal_eigen_value_of_symmetric_matrix, @@ -67,12 +61,9 @@ exposeSparseHelpers(pybind11::module_ m) "matrix, " " using aPower Iteration algorithm (with parameters : " "power_iteration_accuracy and nb_power_iteration).", - pybind11::arg("H"), - pybind11::arg_v( - "power_iteration_accuracy", T(1.E-3), "power iteration accuracy."), - pybind11::arg_v("nb_power_iteration", - 1000, - "maximal number of power iteration executed.")); + nanobind::arg("H"), + nanobind::arg("power_iteration_accuracy") = T(1.E-3), + nanobind::arg("nb_power_iteration") = 1000); } } // namespace python diff --git a/bindings/python/src/expose-model.hpp b/bindings/python/src/expose-model.hpp index 7e6da20a8..2b4a37d4a 100644 --- a/bindings/python/src/expose-model.hpp +++ b/bindings/python/src/expose-model.hpp @@ -1,10 +1,10 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // -#include -#include -#include +#include +#include +#include #include #include @@ -19,18 +19,18 @@ namespace dense { namespace python { template void -exposeDenseModel(pybind11::module_ m) +exposeDenseModel(nanobind::module_ m) { - ::pybind11::class_>( - m, "BackwardData", pybind11::module_local()) - .def(::pybind11::init(), "Default constructor.") + ::nanobind::class_>(m, + "BackwardData") + .def(::nanobind::init(), "Default constructor.") .def( "initialize", &proxsuite::proxqp::dense::BackwardData::initialize, - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Initialize the jacobians (allocate memory if not already done) and set" " by default their value to zero.") .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dH, "dL_dH.") @@ -41,56 +41,53 @@ exposeDenseModel(pybind11::module_ m) .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_du, "dL_du.") .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dl, "dL_dl.") .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dl, "dL_dl."); - // .def_readonly("dL_dH", &proxsuite::proxqp::dense::BackwardData::dL_dH) - // .def_readonly("dL_dg", &proxsuite::proxqp::dense::BackwardData::dL_dg) - // .def_readonly("dL_dA", &proxsuite::proxqp::dense::BackwardData::dL_dA) - // .def_readonly("dL_db", + // .def_ro("dL_dH", &proxsuite::proxqp::dense::BackwardData::dL_dH) + // .def_ro("dL_dg", &proxsuite::proxqp::dense::BackwardData::dL_dg) + // .def_ro("dL_dA", &proxsuite::proxqp::dense::BackwardData::dL_dA) + // .def_ro("dL_db", // &proxsuite::proxqp::dense::BackwardData::dL_db) - // .def_readonly("dL_dC", + // .def_ro("dL_dC", // &proxsuite::proxqp::dense::BackwardData::dL_dC) - // .def_readonly("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du) - // .def_readonly("dL_dl", + // .def_ro("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du) + // .def_ro("dL_dl", // &proxsuite::proxqp::dense::BackwardData::dL_dl) - // .def_readonly("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du); - // .def_readonly("dL_dse", &proxsuite::proxqp::dense::BackwardData::dL_dse) - // .def_readonly("dL_dsi", + // .def_ro("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du); + // .def_ro("dL_dse", &proxsuite::proxqp::dense::BackwardData::dL_dse) + // .def_ro("dL_dsi", // &proxsuite::proxqp::dense::BackwardData::dL_dsi); - ::pybind11::class_>(m, "model") - .def(::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + ::nanobind::class_>(m, "model") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Constructor using QP model dimensions.") // constructor) - .def_readonly("H", &Model::H) - .def_readonly("g", &Model::g) - .def_readonly("A", &Model::A) - .def_readonly("b", &Model::b) - .def_readonly("C", &Model::C) - .def_readonly("l", &Model::l) - .def_readonly("u", &Model::u) - .def_readonly("dim", &Model::dim) - .def_readonly("n_eq", &Model::n_eq) - .def_readonly("n_in", &Model::n_in) - .def_readonly("n_total", &Model::n_total) - .def_readwrite("backward_data", &Model::backward_data) + .def_ro("H", &Model::H) + .def_ro("g", &Model::g) + .def_ro("A", &Model::A) + .def_ro("b", &Model::b) + .def_ro("C", &Model::C) + .def_ro("l", &Model::l) + .def_ro("u", &Model::u) + .def_ro("dim", &Model::dim) + .def_ro("n_eq", &Model::n_eq) + .def_ro("n_in", &Model::n_in) + .def_ro("n_total", &Model::n_total) + .def_rw("backward_data", &Model::backward_data) .def("is_valid", &Model::is_valid, "Check if model is containing valid data.") - .def(pybind11::self == pybind11::self) - .def(pybind11::self != pybind11::self) - .def(pybind11::pickle( - - [](const proxsuite::proxqp::dense::Model& model) { - return pybind11::bytes(proxsuite::serialization::saveToString(model)); - }, - [](pybind11::bytes& s) { - // create qp model which will be updated by loaded data - proxsuite::proxqp::dense::Model model(1, 1, 1); - proxsuite::serialization::loadFromString(model, s); - - return model; - })); + .def(nanobind::self == nanobind::self) + .def(nanobind::self != nanobind::self) + .def("__getstate__", + [](const proxsuite::proxqp::dense::Model& model) { + return proxsuite::serialization::saveToString(model); + }) + .def("__setstate__", [](dense::Model& model, nanobind::bytes& s) { + // create qp model which will be updated by loaded data + new (&model) dense::Model(1, 1, 1); + proxsuite::serialization::loadFromString(model, s.c_str()); + }); } } // namespace python } // namespace dense @@ -99,24 +96,24 @@ namespace sparse { namespace python { template void -exposeSparseModel(pybind11::module_ m) +exposeSparseModel(nanobind::module_ m) { - ::pybind11::class_>(m, "model") - .def(::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + ::nanobind::class_>(m, "model") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Constructor using QP model dimensions.") // constructor) - .def_readonly("g", &Model::g) - .def_readonly("b", &Model::b) - .def_readonly("l", &Model::l) - .def_readonly("u", &Model::u) - .def_readonly("dim", &Model::dim) - .def_readonly("n_eq", &Model::n_eq) - .def_readonly("n_in", &Model::n_in) - .def_readonly("H_nnz", &Model::H_nnz) - .def_readonly("A_nnz", &Model::A_nnz) - .def_readonly("C_nnz", &Model::C_nnz); + .def_ro("g", &Model::g) + .def_ro("b", &Model::b) + .def_ro("l", &Model::l) + .def_ro("u", &Model::u) + .def_ro("dim", &Model::dim) + .def_ro("n_eq", &Model::n_eq) + .def_ro("n_in", &Model::n_in) + .def_ro("H_nnz", &Model::H_nnz) + .def_ro("A_nnz", &Model::A_nnz) + .def_ro("C_nnz", &Model::C_nnz); } } // namespace python } // namespace sparse diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index 941cd32b5..a882bfa91 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -2,15 +2,17 @@ // Copyright (c) 2022-2023 INRIA // #include -#include -#include -#include +#include +#include +#include +#include +#include +#include #include #include #include "helpers.hpp" -#include "optional.hpp" namespace proxsuite { namespace proxqp { @@ -18,10 +20,9 @@ namespace python { template void -exposeResults(pybind11::module_ m) +exposeResults(nanobind::module_ m) { - ::pybind11::enum_( - m, "QPSolverOutput", pybind11::module_local()) + ::nanobind::enum_(m, "QPSolverOutput") .value("PROXQP_SOLVED", QPSolverOutput::PROXQP_SOLVED) .value("PROXQP_MAX_ITER_REACHED", QPSolverOutput::PROXQP_MAX_ITER_REACHED) .value("PROXQP_PRIMAL_INFEASIBLE", QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE) @@ -31,40 +32,40 @@ exposeResults(pybind11::module_ m) .value("PROXQP_NOT_RUN", QPSolverOutput::PROXQP_NOT_RUN) .export_values(); - ::pybind11::class_>(m, "Info", pybind11::module_local()) - .def(::pybind11::init(), "Default constructor.") - .def_readwrite("mu_eq", &Info::mu_eq) - .def_readwrite("mu_in", &Info::mu_in) - .def_readwrite("rho", &Info::rho) - .def_readwrite("iter", &Info::iter) - .def_readwrite("iter_ext", &Info::iter_ext) - .def_readwrite("run_time", &Info::run_time) - .def_readwrite("setup_time", &Info::setup_time) - .def_readwrite("solve_time", &Info::solve_time) - .def_readwrite("duality_gap", &Info::duality_gap) - .def_readwrite("pri_res", &Info::pri_res) - .def_readwrite("dua_res", &Info::dua_res) - .def_readwrite("duality_gap", &Info::duality_gap) - .def_readwrite("iterative_residual", &Info::iterative_residual) - .def_readwrite("objValue", &Info::objValue) - .def_readwrite("status", &Info::status) - .def_readwrite("rho_updates", &Info::rho_updates) - .def_readwrite("mu_updates", &Info::mu_updates) - .def_readwrite("sparse_backend", - &Info::sparse_backend, - "Sparse backend used to solve the qp, either SparseCholesky " - "or MatrixFree.") - .def_readwrite("minimal_H_eigenvalue_estimate", - &Info::minimal_H_eigenvalue_estimate, - "By default it equals 0, in order to get an estimate, set " - "appropriately the setting option " - "find_H_minimal_eigenvalue."); + ::nanobind::class_>(m, "Info") + .def(::nanobind::init(), "Default constructor.") + .def_rw("mu_eq", &Info::mu_eq) + .def_rw("mu_in", &Info::mu_in) + .def_rw("rho", &Info::rho) + .def_rw("iter", &Info::iter) + .def_rw("iter_ext", &Info::iter_ext) + .def_rw("run_time", &Info::run_time) + .def_rw("setup_time", &Info::setup_time) + .def_rw("solve_time", &Info::solve_time) + .def_rw("duality_gap", &Info::duality_gap) + .def_rw("pri_res", &Info::pri_res) + .def_rw("dua_res", &Info::dua_res) + .def_rw("duality_gap", &Info::duality_gap) + .def_rw("iterative_residual", &Info::iterative_residual) + .def_rw("objValue", &Info::objValue) + .def_rw("status", &Info::status) + .def_rw("rho_updates", &Info::rho_updates) + .def_rw("mu_updates", &Info::mu_updates) + .def_rw("sparse_backend", + &Info::sparse_backend, + "Sparse backend used to solve the qp, either SparseCholesky " + "or MatrixFree.") + .def_rw("minimal_H_eigenvalue_estimate", + &Info::minimal_H_eigenvalue_estimate, + "By default it equals 0, in order to get an estimate, set " + "appropriately the setting option " + "find_H_minimal_eigenvalue."); - ::pybind11::class_>(m, "Results", pybind11::module_local()) - .def(::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + ::nanobind::class_>(m, "Results") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Constructor from QP model dimensions.") // constructor .PROXSUITE_PYTHON_EIGEN_READWRITE(Results, x, "The primal solution.") .PROXSUITE_PYTHON_EIGEN_READWRITE( @@ -83,19 +84,17 @@ exposeResults(pybind11::module_ m) si, "Pptimal shift to the closest feasible " "problem wrt inequality constraints.") - .def_readwrite("info", &Results::info) - .def(pybind11::self == pybind11::self) - .def(pybind11::self != pybind11::self) - .def(pybind11::pickle( - - [](const Results& results) { - return pybind11::bytes(proxsuite::serialization::saveToString(results)); - }, - [](pybind11::bytes& s) { - Results results; - proxsuite::serialization::loadFromString(results, s); - return results; - })); + .def_rw("info", &Results::info) + .def(nanobind::self == nanobind::self) + .def(nanobind::self != nanobind::self) + .def("__getstate__", + [](const Results& results) { + return proxsuite::serialization::saveToString(results); + }) + .def("__setstate__", [](Results& results, nanobind::bytes& s) { + new (&results) Results{}; + proxsuite::serialization::loadFromString(results, s.c_str()); + }); ; } } // namespace python From 54040cd45764fc5f5b53779baedfb06687bf26f9 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:08:34 +0200 Subject: [PATCH 10/44] [bindings/python] remove optional.hpp + nanobind has no out-of-the-box utility for tl::optional AFAIK + nanobind requires C++17 - hence proxsuite will be using std::optional anyway --- bindings/python/src/optional.hpp | 23 ----------------------- 1 file changed, 23 deletions(-) delete mode 100644 bindings/python/src/optional.hpp diff --git a/bindings/python/src/optional.hpp b/bindings/python/src/optional.hpp deleted file mode 100644 index bbdd5a153..000000000 --- a/bindings/python/src/optional.hpp +++ /dev/null @@ -1,23 +0,0 @@ -// -// Copyright (c) 2022 INRIA -// -#ifndef proxsuite_python_optional_hpp -#define proxsuite_python_optional_hpp - -#include -#include -#include - -#if __cplusplus < 201703L -template -struct pybind11::detail::type_caster> - : public pybind11::detail::optional_caster> -{}; - -template<> -struct pybind11::detail::type_caster - : public pybind11::detail::void_caster -{}; -#endif - -#endif // ifndef proxsuite_python_optional_hpp From 16a882528a2880a16fa8206bca8cebd9f9824e36 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:09:08 +0200 Subject: [PATCH 11/44] [bindings/python] expose-solver.hpp : switch to nanobind + this nuked a bunch of argument docstrings... --- bindings/python/src/expose-solve.hpp | 275 +++++++++------------------ 1 file changed, 89 insertions(+), 186 deletions(-) diff --git a/bindings/python/src/expose-solve.hpp b/bindings/python/src/expose-solve.hpp index 038bccaec..8721ba570 100644 --- a/bindings/python/src/expose-solve.hpp +++ b/bindings/python/src/expose-solve.hpp @@ -1,11 +1,12 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #include #include -#include -#include -#include +#include +#include +#include +#include namespace proxsuite { namespace proxqp { @@ -16,11 +17,11 @@ namespace python { template void -solveDenseQp(pybind11::module_ m) +solveDenseQp(nanobind::module_ m) { m.def( "solve", - pybind11::overload_cast>, + nanobind::overload_cast>, optional>, optional>, optional>, @@ -50,69 +51,36 @@ solveDenseQp(pybind11::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - pybind11::arg_v("H", nullopt, "quadratic cost with dense format."), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v( - "A", nullopt, "equality constraint matrix with dense format."), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v( - "C", nullopt, "inequality constraint matrix with dense format."), - pybind11::arg_v("l", nullopt, "lower inequality constraint vector"), - pybind11::arg_v("u", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("x", nullopt, "primal warm start"), - pybind11::arg_v("y", nullopt, "dual equality warm start"), - pybind11::arg_v("z", nullopt, "dual inequality warm start"), - pybind11::arg_v( - "eps_abs", - nullopt, - "absolute accuracy level used for the solver stopping criterion."), - pybind11::arg_v("eps_rel", - nullopt, - "relative accuracy level used for the solver stopping " - "criterion. Deactivated in standard settings."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("verbose", - nullopt, - "verbose option to print information at each iteration."), - pybind11::arg_v("compute_preconditioner", - true, - "executes the default preconditioner for reducing ill " - "conditioning and speeding up the solver."), - pybind11::arg_v( - "compute_timings", false, "compute solver's timings in μs."), - pybind11::arg_v("max_iter", nullopt, "maximum number of iteration."), - pybind11::arg_v( - "initial_guess", - proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, - "maximum number of iteration."), - pybind11::arg_v( - "check_duality_gap", - false, - "if set to true, include the duality gap in absolute and relative " - "stopping criteria."), - pybind11::arg_v("eps_duality_gap_abs", - nullopt, - "absolute accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("eps_duality_gap_rel", - nullopt, - "relative accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("primal_infeasibility_solving", - false, - "solves the closest feasible problem in L2 sense " - "if the QP problem appears to be infeasible."), - pybind11::arg_v("default_H_eigenvalue_estimate", - 0., - "Default estimate of the minimal eigen value of H.")); + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("x") = nullopt, + nanobind::arg("y") = nullopt, + nanobind::arg("z") = nullopt, + nanobind::arg("eps_abs") = nullopt, + nanobind::arg("eps_rel") = nullopt, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("verbose") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nullopt, + nanobind::arg("initial_guess") = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nullopt, + nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0.); m.def( "solve", - pybind11::overload_cast>, + nanobind::overload_cast>, optional>, optional>, optional>, @@ -144,67 +112,34 @@ solveDenseQp(pybind11::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - pybind11::arg_v("H", nullopt, "quadratic cost with dense format."), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v( - "A", nullopt, "equality constraint matrix with dense format."), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v( - "C", nullopt, "inequality constraint matrix with dense format."), - pybind11::arg_v("l", nullopt, "lower inequality constraint vector"), - pybind11::arg_v("u", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("l_box", nullopt, "lower box inequality constraint vector"), - pybind11::arg_v("u_box", nullopt, "upper box inequality constraint vector"), - pybind11::arg_v("x", nullopt, "primal warm start"), - pybind11::arg_v("y", nullopt, "dual equality warm start"), - pybind11::arg_v("z", nullopt, "dual inequality warm start"), - pybind11::arg_v( - "eps_abs", - nullopt, - "absolute accuracy level used for the solver stopping criterion."), - pybind11::arg_v("eps_rel", - nullopt, - "relative accuracy level used for the solver stopping " - "criterion. Deactivated in standard settings."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("verbose", - nullopt, - "verbose option to print information at each iteration."), - pybind11::arg_v("compute_preconditioner", - true, - "executes the default preconditioner for reducing ill " - "conditioning and speeding up the solver."), - pybind11::arg_v( - "compute_timings", false, "compute solver's timings in μs."), - pybind11::arg_v("max_iter", nullopt, "maximum number of iteration."), - pybind11::arg_v( - "initial_guess", + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("l_box") = nullopt, + nanobind::arg("u_box") = nullopt, + nanobind::arg("x") = nullopt, + nanobind::arg("y") = nullopt, + nanobind::arg("z") = nullopt, + nanobind::arg("eps_abs") = nullopt, + nanobind::arg("eps_rel") = nullopt, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("verbose") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nullopt, + nanobind::arg("initial_guess") = proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, - "maximum number of iteration."), - pybind11::arg_v( - "check_duality_gap", - false, - "if set to true, include the duality gap in absolute and relative " - "stopping criteria."), - pybind11::arg_v("eps_duality_gap_abs", - nullopt, - "absolute accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("eps_duality_gap_rel", - nullopt, - "relative accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("primal_infeasibility_solving", - false, - "solves the closest feasible problem in L2 sense " - "if the QP problem appears to be infeasible."), - pybind11::arg_v("default_H_eigenvalue_estimate", - 0., - "Default estimate of the minimal eigen value of H.")); + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nullopt, + nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0.); } } // namespace python @@ -215,7 +150,7 @@ namespace python { template void -solveSparseQp(pybind11::module_ m) +solveSparseQp(nanobind::module_ m) { m.def( "solve", @@ -225,65 +160,33 @@ solveSparseQp(pybind11::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - pybind11::arg_v("H", nullopt, "quadratic cost with sparse format."), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v( - "A", nullopt, "equality constraint matrix with sparse format."), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v( - "C", nullopt, "inequality constraint matrix with sparse format."), - pybind11::arg_v("l", nullopt, "lower inequality constraint vector"), - pybind11::arg_v("u", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("x", nullopt, "primal warm start"), - pybind11::arg_v("y", nullopt, "dual equality warm start"), - pybind11::arg_v("z", nullopt, "dual inequality warm start"), - pybind11::arg_v( - "eps_abs", - nullopt, - "absolute accuracy level used for the solver stopping criterion."), - pybind11::arg_v("eps_rel", - nullopt, - "relative accuracy level used for the solver stopping " - "criterion. Deactivated in standard settings."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("verbose", - nullopt, - "verbose option to print information at each iteration."), - pybind11::arg_v("compute_preconditioner", - true, - "executes the default preconditioner for reducing ill " - "conditioning and speeding up the solver."), - pybind11::arg_v( - "compute_timings", false, "compute solver's timings in μs."), - pybind11::arg_v("max_iter", nullopt, "maximum number of iteration."), - pybind11::arg_v("initial_guess", - proxsuite::proxqp::InitialGuessStatus:: - EQUALITY_CONSTRAINED_INITIAL_GUESS), - pybind11::arg_v("sparse_backend", - proxsuite::proxqp::SparseBackend::Automatic), - pybind11::arg_v("check_duality_gap", - false, - "if set to true, include the duality gap in absolute and " - "relative stopping criteria."), - pybind11::arg_v("eps_duality_gap_abs", - nullopt, - "absolute accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("eps_duality_gap_rel", - nullopt, - "relative accuracy threshold used for the duality-gap " - "stopping criterion."), - pybind11::arg_v("primal_infeasibility_solving", - false, - "solves the closest feasible problem in L2 sense " - "if the QP problem appears to be infeasible."), - pybind11::arg_v("default_H_eigenvalue_estimate", - 0., - "Default estimate of the minimal eigen value of H.")); + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("x") = nullopt, + nanobind::arg("y") = nullopt, + nanobind::arg("z") = nullopt, + nanobind::arg("eps_abs") = nullopt, + nanobind::arg("eps_rel") = nullopt, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("verbose") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("compute_timings") = false, + nanobind::arg("max_iter") = nullopt, + nanobind::arg("initial_guess") = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + nanobind::arg("sparse_backend") = SparseBackend::Automatic, + nanobind::arg("check_duality_gap") = false, + nanobind::arg("eps_duality_gap_abs") = nullopt, + nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0.); } } // namespace python From 97909b416d4f9d26372ab4137d0d0cee70e630cd Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:09:42 +0200 Subject: [PATCH 12/44] [bindings/python] expose-{backward,parallel,qpobject}.hpp : switch to nanobind + this nuked a bunch of argument docstrings... --- bindings/python/src/expose-backward.hpp | 31 +- bindings/python/src/expose-parallel.hpp | 120 +++----- bindings/python/src/expose-qpobject.hpp | 381 ++++++++++-------------- 3 files changed, 215 insertions(+), 317 deletions(-) diff --git a/bindings/python/src/expose-backward.hpp b/bindings/python/src/expose-backward.hpp index 85716ece7..29db4b553 100644 --- a/bindings/python/src/expose-backward.hpp +++ b/bindings/python/src/expose-backward.hpp @@ -5,9 +5,9 @@ #include #include #include "proxsuite/proxqp/dense/compute_ECJ.hpp" -#include -#include -#include +#include +#include +#include namespace proxsuite { namespace proxqp { @@ -18,23 +18,16 @@ namespace python { template void -backward(pybind11::module_ m) +backward(nanobind::module_ m) { - m.def( - "compute_backward", - &compute_backward, - "Function for computing derivatives of solved QP.", - pybind11::arg_v("qp", "Solved dense QP."), - pybind11::arg_v("loss_derivative", "Derivate of loss wrt to qp solution."), - pybind11::arg_v( - "eps", 1e-4, "Backward pass accuracy for deriving solution Jacobians."), - pybind11::arg_v("rho_backward", - 1e-6, - "New primal proximal parameter for iterative refinement."), - pybind11::arg_v("mu_backward", - 1e-6, - "New dual proximal parameter used both for inequality and " - "equality for iterative refinement.")); + m.def("compute_backward", + &compute_backward, + "Function for computing derivatives of solved QP.", + nanobind::arg("qp"), + nanobind::arg("loss_derivative"), + nanobind::arg("eps") = 1e-4, + nanobind::arg("rho_backward") = 1e-6, + nanobind::arg("mu_backward") = 1e-6); } } // namespace python diff --git a/bindings/python/src/expose-parallel.hpp b/bindings/python/src/expose-parallel.hpp index 9b90186b7..11ffaa2d0 100644 --- a/bindings/python/src/expose-parallel.hpp +++ b/bindings/python/src/expose-parallel.hpp @@ -5,13 +5,13 @@ #include #include #include -#include -#include -#include -#include // For binding STL containers +#include +#include +#include +#include -PYBIND11_MAKE_OPAQUE(std::vector>) -PYBIND11_MAKE_OPAQUE(std::vector>) +NB_MAKE_OPAQUE(std::vector>) +NB_MAKE_OPAQUE(std::vector>) namespace proxsuite { namespace proxqp { using proxsuite::linalg::veg::isize; @@ -21,86 +21,65 @@ namespace python { template void -solveDenseQpParallel(pybind11::module_ m) +solveDenseQpParallel(nanobind::module_ m) { - pybind11::bind_vector>>( + nanobind::bind_vector>>( m, "VectorLossDerivatives"); - pybind11::bind_vector>>( + nanobind::bind_vector>>( m, "VectorQP"); m.def("solve_in_parallel", - pybind11::overload_cast>&, + nanobind::overload_cast>&, const optional>(&solve_in_parallel), "Function for solving a list of dense QPs in parallel.", - pybind11::arg_v("qps", "List of initialized dense Qps."), - pybind11::arg_v("num_threads", - nullopt, - "number of threads used for the computation.")); + nanobind::arg("qps"), + nanobind::arg("num_threads") = nullopt); m.def( "solve_in_parallel", - pybind11::overload_cast&, const optional>( + nanobind::overload_cast&, const optional>( &solve_in_parallel), "Function for solving a list of dense QPs in parallel.", - pybind11::arg_v("qps", "List of initialized dense Qps."), - pybind11::arg_v( - "num_threads", nullopt, "number of threads used for the computation.")); + nanobind::arg("qps"), + nanobind::arg("num_threads") = nullopt); // m.def("solve_in_parallel", // &qp_solve_in_parallel, // "Function for solving a list of dense QPs in parallel.", - // pybind11::arg_v("num_threads", - // nullopt, - // "number of threads used for the computation."), - // pybind11::arg_v("qps", "List of initialized dense Qps.")); + // nanobind::arg("num_threads") = nullopt, + // nanobind::arg("qps")); - m.def( - "solve_backward_in_parallel", - pybind11::overload_cast, - proxqp::dense::BatchQP&, - std::vector>&, - T, - T, - T>(&qp_solve_backward_in_parallel), - "Function for solving a list of dense QPs in parallel.", - pybind11::arg_v( - "num_threads", nullopt, "number of threads used for the computation."), - pybind11::arg_v("qps", "List of initialized dense Qps."), - pybind11::arg_v("loss_derivatives", "List of loss derivatives."), - pybind11::arg_v( - "eps", 1e-4, "Backward pass accuracy for deriving solution Jacobians."), - pybind11::arg_v("rho_backward", - 1e-6, - "New primal proximal parameter for iterative refinement."), - pybind11::arg_v("mu_backward", - 1e-6, - "New dual proximal parameter used both for inequality " - "and equality for iterative refinement.")); + m.def("solve_backward_in_parallel", + nanobind::overload_cast, + proxqp::dense::BatchQP&, + std::vector>&, + T, + T, + T>(&qp_solve_backward_in_parallel), + "Function for solving a list of dense QPs in parallel.", + nanobind::arg("num_threads") = nullopt, + nanobind::arg("qps"), + nanobind::arg("loss_derivatives"), + nanobind::arg("eps") = 1e-4, + nanobind::arg("rho_backward") = 1e-6, + nanobind::arg("mu_backward") = 1e-6); - m.def( - "solve_backward_in_parallel", - pybind11::overload_cast, - std::vector>&, - std::vector>&, - T, - T, - T>(&qp_solve_backward_in_parallel), - "Function for solving a list of dense QPs in parallel.", - pybind11::arg_v( - "num_threads", nullopt, "number of threads used for the computation."), - pybind11::arg_v("qps", "List of initialized dense Qps."), - pybind11::arg_v("loss_derivatives", "List of loss derivatives."), - pybind11::arg_v( - "eps", 1e-4, "Backward pass accuracy for deriving solution Jacobians."), - pybind11::arg_v("rho_backward", - 1e-6, - "New primal proximal parameter for iterative refinement."), - pybind11::arg_v("mu_backward", - 1e-6, - "New dual proximal parameter used both for inequality and " - "equality for iterative refinement.")); + m.def("solve_backward_in_parallel", + nanobind::overload_cast, + std::vector>&, + std::vector>&, + T, + T, + T>(&qp_solve_backward_in_parallel), + "Function for solving a list of dense QPs in parallel.", + nanobind::arg("num_threads") = nullopt, + nanobind::arg("qps"), + nanobind::arg("loss_derivatives"), + nanobind::arg("eps") = 1e-4, + nanobind::arg("rho_backward") = 1e-6, + nanobind::arg("mu_backward") = 1e-6); } } // namespace python @@ -110,16 +89,15 @@ namespace sparse { namespace python { template void -solveSparseQpParallel(pybind11::module_ m) +solveSparseQpParallel(nanobind::module_ m) { m.def( "solve_in_parallel", - pybind11::overload_cast&, + nanobind::overload_cast&, const optional>(&solve_in_parallel), "Function for solving a list of sparse QPs in parallel.", - pybind11::arg_v("qps", "List of initialized sparse Qps."), - pybind11::arg_v( - "num_threads", nullopt, "number of threads used for the computation.")); + nanobind::arg("qps"), + nanobind::arg("num_threads") = nullopt); } } // namespace python diff --git a/bindings/python/src/expose-qpobject.hpp b/bindings/python/src/expose-qpobject.hpp index f6824ed26..70e22f2b6 100644 --- a/bindings/python/src/expose-qpobject.hpp +++ b/bindings/python/src/expose-qpobject.hpp @@ -1,10 +1,11 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // -#include -#include -#include +#include +#include +#include +#include #include #include @@ -22,72 +23,62 @@ namespace python { template void -exposeQpObjectDense(pybind11::module_ m) +exposeQpObjectDense(nanobind::module_ m) { - ::pybind11::enum_(m, "DenseBackend", pybind11::module_local()) + ::nanobind::enum_(m, "DenseBackend") .value("Automatic", DenseBackend::Automatic) .value("PrimalDualLDLT", DenseBackend::PrimalDualLDLT) .value("PrimalLDLT", DenseBackend::PrimalLDLT) .export_values(); - ::pybind11::enum_(m, "HessianType", pybind11::module_local()) + ::nanobind::enum_(m, "HessianType") .value("Dense", proxsuite::proxqp::HessianType::Dense) .value("Zero", proxsuite::proxqp::HessianType::Zero) .value("Diagonal", proxsuite::proxqp::HessianType::Diagonal) .export_values(); - // ::pybind11::class_>(m, - // "ruiz", pybind11::module_local()) - // .def(::pybind11::init(), "Default constructor.") - // .def_readwrite("mu_eq", &RuizEquilibration::delta) - // .def_readwrite("mu_in", &RuizEquilibration::c) - // .def_readwrite("rho", &RuizEquilibration::dim) - // .def_readwrite("iter", &RuizEquilibration::epsilon) - // .def_readwrite("iter_ext", &RuizEquilibration::max_iter) - // .def_readwrite("run_time", &RuizEquilibration::sym); + // ::nanobind::class_>(m, + // "ruiz") + // .def(::nanobind::init(), "Default constructor.") + // .def_rw("mu_eq", &RuizEquilibration::delta) + // .def_rw("mu_in", &RuizEquilibration::c) + // .def_rw("rho", &RuizEquilibration::dim) + // .def_rw("iter", &RuizEquilibration::epsilon) + // .def_rw("iter_ext", &RuizEquilibration::max_iter) + // .def_rw("run_time", &RuizEquilibration::sym); - // ::pybind11::class_>(m, - // "ruiz", pybind11::module_local()) - // .def(::pybind11::init(), "Default constructor.") - // .def_readwrite("mu_eq", &RuizEquilibration::delta) - // .def_readwrite("mu_in", &RuizEquilibration::c) - // .def_readwrite("rho", &RuizEquilibration::dim) - // .def_readwrite("iter", &RuizEquilibration::epsilon) - // .def_readwrite("iter_ext", &RuizEquilibration::max_iter) - // .def_readwrite("run_time", &RuizEquilibration::sym); + // ::nanobind::class_>(m, + // "ruiz") + // .def(::nanobind::init(), "Default constructor.") + // .def_rw("mu_eq", &RuizEquilibration::delta) + // .def_rw("mu_in", &RuizEquilibration::c) + // .def_rw("rho", &RuizEquilibration::dim) + // .def_rw("iter", &RuizEquilibration::epsilon) + // .def_rw("iter_ext", &RuizEquilibration::max_iter) + // .def_rw("run_time", &RuizEquilibration::sym); - ::pybind11::class_>(m, "QP") - .def( - ::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), - pybind11::arg_v( - "box_constraints", - false, - "specify or not that the QP has box inequality constraints."), - pybind11::arg_v("hessian_type", - proxsuite::proxqp::HessianType::Dense, - "specify the problem type to be solved."), - pybind11::arg_v("dense_backend", - proxsuite::proxqp::DenseBackend::Automatic, - "specify which backend using for solving the problem."), - "Default constructor using QP model dimensions.") // constructor - .def_readwrite( - "results", - &dense::QP::results, - "class containing the solution or certificate of infeasibility, " - "and " - "information statistics in an info subclass.") - .def_readwrite( - "settings", &dense::QP::settings, "Settings of the solver.") - .def_readwrite( - "model", &dense::QP::model, "class containing the QP model") + ::nanobind::class_>(m, "QP") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, + nanobind::arg("box_constraints") = false, + nanobind::arg("hessian_type") = proxsuite::proxqp::HessianType::Dense, + nanobind::arg("dense_backend") = + proxsuite::proxqp::DenseBackend::Automatic, + "Default constructor using QP model dimensions.") // constructor + .def_rw("results", + &dense::QP::results, + "class containing the solution or certificate of infeasibility, " + "and " + "information statistics in an info subclass.") + .def_rw("settings", &dense::QP::settings, "Settings of the solver.") + .def_rw("model", &dense::QP::model, "class containing the QP model") .def("is_box_constrained", &dense::QP::is_box_constrained, "precise whether or not the QP is designed with box constraints.") @@ -111,26 +102,18 @@ exposeQpObjectDense(pybind11::module_ m) optional, optional)>(&dense::QP::init), "function for initialize the QP model.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v("compute_preconditioner", - true, - "execute the preconditioner for reducing " - "ill-conditioning and speeding up solver execution."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def("init", static_cast::*)(optional>, optional>, @@ -147,30 +130,20 @@ exposeQpObjectDense(pybind11::module_ m) optional, optional)>(&dense::QP::init), "function for initialize the QP model.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v( - "l_box", nullopt, "upper box inequality constraint vector"), - pybind11::arg_v( - "u_box", nullopt, "lower box inequality constraint vector"), - pybind11::arg_v("compute_preconditioner", - true, - "execute the preconditioner for reducing " - "ill-conditioning and speeding up solver execution."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("l_box") = nullopt, + nanobind::arg("u_box") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def("solve", static_cast::*)()>(&dense::QP::solve), "function used for solving the QP problem, using default parameters.") @@ -197,28 +170,18 @@ exposeQpObjectDense(pybind11::module_ m) optional)>(&dense::QP::update), "function used for updating matrix or vector entry of the model using " "dense matrix entries.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v( - "update_preconditioner", - false, - "update the preconditioner considering new matrices entries for " - "reducing ill-conditioning and speeding up solver execution. If set up " - "to false, use previous derived preconditioner."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("update_preconditioner") = false, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def( "update", static_cast::*)(optional>, @@ -237,48 +200,34 @@ exposeQpObjectDense(pybind11::module_ m) optional)>(&dense::QP::update), "function used for updating matrix or vector entry of the model using " "dense matrix entries.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v( - "l_box", nullopt, "upper box inequality constraint vector"), - pybind11::arg_v( - "u_box", nullopt, "lower box inequality constraint vector"), - pybind11::arg_v( - "update_preconditioner", - false, - "update the preconditioner considering new matrices entries for " - "reducing ill-conditioning and speeding up solver execution. If set up " - "to false, use previous derived preconditioner."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("l_box") = nullopt, + nanobind::arg("u_box") = nullopt, + nanobind::arg("update_preconditioner") = false, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def("cleanup", &dense::QP::cleanup, "function used for cleaning the workspace and result " "classes.") - .def(pybind11::self == pybind11::self) - .def(pybind11::self != pybind11::self) - .def(pybind11::pickle( - - [](const dense::QP& qp) { - return pybind11::bytes(proxsuite::serialization::saveToString(qp)); - }, - [](pybind11::bytes& s) { - proxsuite::proxqp::dense::QP qp(1, 1, 1); - proxsuite::serialization::loadFromString(qp, s); - return qp; - })); + .def(nanobind::self == nanobind::self) + .def(nanobind::self != nanobind::self) + .def("__getstate__", + [](const dense::QP& qp) { + return proxsuite::serialization::saveToString(qp); + }) + .def("__setstate__", [](dense::QP& qp, nanobind::bytes& s) { + new (&qp) dense::QP(1, 1, 1); + proxsuite::serialization::loadFromString(qp, s.c_str()); + }); ; } } // namespace python @@ -290,84 +239,62 @@ namespace python { template void -exposeQpObjectSparse(pybind11::module_ m) +exposeQpObjectSparse(nanobind::module_ m) { - ::pybind11::class_>(m, "QP") //,pybind11::module_local() - .def(::pybind11::init(), - pybind11::arg_v("n", 0, "primal dimension."), - pybind11::arg_v("n_eq", 0, "number of equality constraints."), - pybind11::arg_v("n_in", 0, "number of inequality constraints."), + ::nanobind::class_>(m, "QP") + .def(::nanobind::init(), + nanobind::arg("n") = 0, + nanobind::arg("n_eq") = 0, + nanobind::arg("n_in") = 0, "Constructor using QP model dimensions.") // constructor - .def( - ::pybind11::init&, - const sparse::SparseMat&, - const sparse::SparseMat&>(), - pybind11::arg_v("H_mask", nullopt, "mask of the quadratic cost."), - pybind11::arg_v( - "A_mask", nullopt, "mask of the equality constraint matrix."), - pybind11::arg_v("C_mask", 0, "mask of the inequality constraint matrix."), - "Constructor using QP model sparsity structure.") // constructor - .def_readwrite( - "model", &sparse::QP::model, "class containing the QP model") - .def_readwrite( - "results", - &sparse::QP::results, - "class containing the solution or certificate of infeasibility, " - "and " - "information statistics in an info subclass.") - .def_readwrite( - "settings", &sparse::QP::settings, "Settings of the solver.") + .def(::nanobind::init&, + const sparse::SparseMat&, + const sparse::SparseMat&>(), + nanobind::arg("H_mask") = nullopt, + nanobind::arg("A_mask") = nullopt, + nanobind::arg("C_mask") = 0, + "Constructor using QP model sparsity structure.") // constructor + .def_ro("model", &sparse::QP::model, "class containing the QP model") + .def_rw("results", + &sparse::QP::results, + "class containing the solution or certificate of infeasibility, " + "and " + "information statistics in an info subclass.") + .def_rw("settings", &sparse::QP::settings, "Settings of the solver.") .def("init", &sparse::QP::init, "function for initializing the model when passing sparse matrices in " "entry.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v("compute_preconditioner", - true, - "execute the preconditioner for reducing " - "ill-conditioning and speeding up solver execution."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("compute_preconditioner") = true, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def("update", &sparse::QP::update, "function for updating the model when passing sparse matrices in " "entry.", - pybind11::arg_v("H", nullopt, "quadratic cost"), - pybind11::arg_v("g", nullopt, "linear cost"), - pybind11::arg_v("A", nullopt, "equality constraint matrix"), - pybind11::arg_v("b", nullopt, "equality constraint vector"), - pybind11::arg_v("C", nullopt, "inequality constraint matrix"), - pybind11::arg_v("l", nullopt, "upper inequality constraint vector"), - pybind11::arg_v("u", nullopt, "lower inequality constraint vector"), - pybind11::arg_v( - "update_preconditioner", - false, - "update the preconditioner or re-use previous derived for reducing " - "ill-conditioning and speeding up solver execution."), - pybind11::arg_v("rho", nullopt, "primal proximal parameter"), - pybind11::arg_v( - "mu_eq", nullopt, "dual equality constraint proximal parameter"), - pybind11::arg_v( - "mu_in", nullopt, "dual inequality constraint proximal parameter"), - pybind11::arg_v("manual_minimal_H_eigenvalue", - nullopt, - "manual minimal H eigenvalue proposed to regularize H" - " in case it is non convex.")) + nanobind::arg("H") = nullopt, + nanobind::arg("g") = nullopt, + nanobind::arg("A") = nullopt, + nanobind::arg("b") = nullopt, + nanobind::arg("C") = nullopt, + nanobind::arg("l") = nullopt, + nanobind::arg("u") = nullopt, + nanobind::arg("update_preconditioner") = false, + nanobind::arg("rho") = nullopt, + nanobind::arg("mu_eq") = nullopt, + nanobind::arg("mu_in") = nullopt, + nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) .def("solve", static_cast::*)()>(&sparse::QP::solve), "function used for solving the QP problem, using default parameters.") From 520afabdb218f04a328b7ca0229c0840fa4b3473 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Mon, 5 Aug 2024 20:10:51 +0200 Subject: [PATCH 13/44] [bindings/python] expose-qpvector.hpp : upgrade to nanobind + change pybind11::return_value_policy to nanobind::rv_policy when relevant + include the nanobind header here (helps for IDEs) + change ctor init types from i64 to u64 -> nanobind is STRICT when it comes to integer signedness conversions --- bindings/python/src/expose-qpvector.hpp | 27 ++++++++++++++----------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/bindings/python/src/expose-qpvector.hpp b/bindings/python/src/expose-qpvector.hpp index 06abf8b35..1b077a4af 100644 --- a/bindings/python/src/expose-qpvector.hpp +++ b/bindings/python/src/expose-qpvector.hpp @@ -3,6 +3,9 @@ // #include +#include + +#include namespace proxsuite { namespace proxqp { @@ -13,17 +16,17 @@ namespace python { template void -exposeQPVectorDense(pybind11::module_ m) +exposeQPVectorDense(nanobind::module_ m) { - ::pybind11::class_>(m, "BatchQP") + ::nanobind::class_>(m, "BatchQP") .def( - ::pybind11::init(), - pybind11::arg_v("batch_size", 0, "number of QPs to be stored."), + ::nanobind::init(), + nanobind::arg("batch_size") = 0, "Default constructor using the BatchSize of qp models to store.") // constructor .def("init_qp_in_place", &dense::BatchQP::init_qp_in_place, - pybind11::return_value_policy::reference, + nanobind::rv_policy::reference, "init a dense QP in place and return a reference to it.") .def("insert", &dense::BatchQP::insert, @@ -32,7 +35,7 @@ exposeQPVectorDense(pybind11::module_ m) .def("get", (dense::QP & (dense::BatchQP::*)(isize)) & dense::BatchQP::get, - pybind11::return_value_policy::reference, + nanobind::rv_policy::reference, "get the qp."); } } // namespace python @@ -43,23 +46,23 @@ namespace python { template void -exposeQPVectorSparse(pybind11::module_ m) +exposeQPVectorSparse(nanobind::module_ m) { - ::pybind11::class_>(m, "BatchQP") + ::nanobind::class_>(m, "BatchQP") .def( - ::pybind11::init(), - pybind11::arg_v("batch_size", 0, "number of QPs to be stored."), + ::nanobind::init(), + nanobind::arg("batch_size") = 0, "Default constructor using the BatchSize of qp models to store.") // constructor .def("init_qp_in_place", &sparse::BatchQP::init_qp_in_place, - pybind11::return_value_policy::reference, + nanobind::rv_policy::reference, "init a sparse QP in place and return a reference to it.") .def("size", &sparse::BatchQP::size) .def("get", (sparse::QP & (sparse::BatchQP::*)(isize)) & sparse::BatchQP::get, - pybind11::return_value_policy::reference, + nanobind::rv_policy::reference, "get the qp."); } From 783d88f011d976331b9643851129e839ff178a0f Mon Sep 17 00:00:00 2001 From: Fabian Schramm <55981657+fabinsch@users.noreply.github.com> Date: Tue, 6 Aug 2024 10:12:17 +0200 Subject: [PATCH 14/44] [ci] checkout submodule recursive --- .github/workflows/ci-arch.yml | 2 +- .github/workflows/ci-linux-osx-win-conda.yml | 4 +++- .github/workflows/gh-pages.yml | 4 +++- .github/workflows/release-linux.yml | 4 ++-- .github/workflows/release-osx-win.yml | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci-arch.yml b/.github/workflows/ci-arch.yml index 18e21d371..af0cfd2ad 100644 --- a/.github/workflows/ci-arch.yml +++ b/.github/workflows/ci-arch.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 with: - submodules: true + submodules: recursive - run: cmake -B build -S . -DBUILD_PYTHON_INTERFACE=ON - run: cmake --build build diff --git a/.github/workflows/ci-linux-osx-win-conda.yml b/.github/workflows/ci-linux-osx-win-conda.yml index bdf3af56f..48f32ad21 100644 --- a/.github/workflows/ci-linux-osx-win-conda.yml +++ b/.github/workflows/ci-linux-osx-win-conda.yml @@ -55,7 +55,9 @@ jobs: continue_on_error: false steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + submodules: recursive - uses: conda-incubator/setup-miniconda@v2 if: matrix.os != 'macos-14' diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml index cf9ae190e..c096bd318 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -8,7 +8,9 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + submodules: recursive - uses: conda-incubator/setup-miniconda@v2 with: diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index a47c2526d..54125cbc8 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -33,9 +33,9 @@ jobs: build: "cp312-manylinux*" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: - submodules: 'true' + submodules: recursive - uses: actions/setup-python@v4 with: python-version: "3.10" diff --git a/.github/workflows/release-osx-win.yml b/.github/workflows/release-osx-win.yml index 351cee208..65f4f91de 100644 --- a/.github/workflows/release-osx-win.yml +++ b/.github/workflows/release-osx-win.yml @@ -21,7 +21,7 @@ jobs: toolset: v143 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive From 7de7adf72705f42f89b7d2259ef9babdf6675ddc Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 13:47:05 +0200 Subject: [PATCH 15/44] [bindings/python] cmake : add stub generation (basic) pyproject.toml: add dependency on typing-extensions (required for stubgen on python<3.11) Make stub generation optional --- bindings/python/CMakeLists.txt | 33 +++++++++++++++++++++++++++++++-- pyproject.toml | 1 + 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index d6b3cf216..30d49293e 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -5,6 +5,8 @@ endif() include(${JRL_CMAKE_MODULES}/python.cmake) include(${JRL_CMAKE_MODULES}/python-helpers.cmake) +option(GENERATE_PYTHON_STUBS "Generate Python stubs" OFF) + findpython(REQUIRED Development.Module) find_package(Python 3.8 COMPONENTS ${PYTHON_COMPONENTS}) @@ -54,6 +56,20 @@ if(${CMAKE_SYSTEM_PROCESSOR} MATCHES "(x86)|(X86)|(amd64)|(AMD64)") TARGETS instructionset EXPORT ${TARGETS_EXPORT_NAME} DESTINATION ${${PYWRAP}_INSTALL_DIR}) + if(GENERATE_PYTHON_STUBS) + nanobind_add_stub( + instructionset_stub + MODULE + instructionset + OUTPUT + instructionset.pyi + PYTHON_PATH + $ + DEPENDS + instructionset) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/instructionset.pyi + DESTINATION ${${PYWRAP}_INSTALL_DIR}) + endif() endif() function(list_filter list regular_expression dest_list) @@ -68,8 +84,7 @@ function(list_filter list regular_expression dest_list) endfunction(list_filter) function(CREATE_PYTHON_TARGET target_name COMPILE_OPTIONS dependencies) - nanobind_add_module(${target_name} ${PYWRAP_SOURCES} - ${PYWRAP_HEADERS}) + nanobind_add_module(${target_name} ${PYWRAP_SOURCES} ${PYWRAP_HEADERS}) add_dependencies(${PROJECT_NAME}_python ${target_name}) target_link_libraries(${target_name} PUBLIC ${dependencies}) @@ -121,6 +136,20 @@ function(CREATE_PYTHON_TARGET target_name COMPILE_OPTIONS dependencies) endif() install(TARGETS ${target_name} DESTINATION ${${PYWRAP}_INSTALL_DIR}) + if(GENERATE_PYTHON_STUBS) + nanobind_add_stub( + ${target_name}_stub + MODULE + ${target_name} + OUTPUT + ${target_name}.pyi + PYTHON_PATH + $ + DEPENDS + ${target_name}) + install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${target_name}.pyi + DESTINATION ${${PYWRAP}_INSTALL_DIR}) + endif() endfunction() if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) diff --git a/pyproject.toml b/pyproject.toml index e40bd4616..3aa236457 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -16,6 +16,7 @@ requires = [ "cmeel[build]", "cmeel-eigen", "cmeel-simde", + "typing-extensions", ] build-backend = "cmeel.build" configure-args = ["-DBUILD_TESTING:BOOL=OFF","-DBUILD_PYTHON_INTERFACE:BOOL=ON","-DBUILD_WITH_VECTORIZATION_SUPPORT:BOOL=ON","-DINSTALL_DOCUMENTATION:BOOL=OFF","-DBUILD_WITH_OPENMP_SUPPORT=OFF"] From bc0e35ab835f8b1f8e382884736f493d94cc4a0f Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 13:50:49 +0200 Subject: [PATCH 16/44] Update CHANGELOG --- CHANGELOG.md | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e7f6fad16..0953075b6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,15 +6,23 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Added + +* Stub files for Python bindings, using [nanobind's native support](https://nanobind.readthedocs.io/en/latest/typing.html#stub-generation) ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) + +### Changed + +* Change Python bindings to use nanobind instead of pybind11 ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) + ## [0.6.7] - 2024-08-27 ### Added * Fix mu update function for PrimalLDLT backend ([#349](https://github.com/Simple-Robotics/proxsuite/pull/349)) * Allow use of installed pybind11, cereal and jrl-cmakemodules via cmake * Add compatibility with jrl-cmakemodules workspace ([#339](https://github.com/Simple-Robotics/proxsuite/pull/339)) -* Specifically mention that timings are in microseconds ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) -* Fix cereal include directory in cmake ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) -* Extend doc with hint for conda installation from source ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) +* Specifically mention that timings are in microseconds ([#342](https://github.com/Simple-Robotics/proxsuite/pull/342)) +* Fix cereal include directory in cmake ([#342](https://github.com/Simple-Robotics/proxsuite/pull/342)) +* Extend doc with hint for conda installation from source ([#342](https://github.com/Simple-Robotics/proxsuite/pull/342)) ### Fixed * Fix inequality constraints return in QPLayer ([#343](https://github.com/Simple-Robotics/proxsuite/pull/343)) From 3ea4718dd5022ff34506509ef8bb2f082496586b Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 18:31:37 +0200 Subject: [PATCH 17/44] [test] dense_qp_solve.py : remove useless line --- test/src/dense_qp_solve.py | 1 - 1 file changed, 1 deletion(-) diff --git a/test/src/dense_qp_solve.py b/test/src/dense_qp_solve.py index 6475da821..b26896fd8 100644 --- a/test/src/dense_qp_solve.py +++ b/test/src/dense_qp_solve.py @@ -39,7 +39,6 @@ def generate_mixed_qp(n, seed=1): q = np.random.randn(n) A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray() v = np.random.randn(n) # Fictitious solution - delta = np.random.rand(m) # To get inequality u = A @ v l = -1.0e20 * np.ones(m) From a8256bf1e6f731a343a75f0e73a8824baf6d4446 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 21:58:15 +0200 Subject: [PATCH 18/44] [bindings/python] remove macro PROXSUITE_PYTHON_EIGEN_READWRITE + nanobind handles things well already --- bindings/python/src/expose-model.hpp | 33 +++++------------ bindings/python/src/expose-results.hpp | 51 +++++++++++++++++--------- bindings/python/src/helpers.hpp | 16 -------- 3 files changed, 43 insertions(+), 57 deletions(-) delete mode 100644 bindings/python/src/helpers.hpp diff --git a/bindings/python/src/expose-model.hpp b/bindings/python/src/expose-model.hpp index 2b4a37d4a..3739f26b3 100644 --- a/bindings/python/src/expose-model.hpp +++ b/bindings/python/src/expose-model.hpp @@ -12,7 +12,7 @@ #include #include #include -#include "helpers.hpp" + namespace proxsuite { namespace proxqp { namespace dense { @@ -22,36 +22,23 @@ void exposeDenseModel(nanobind::module_ m) { - ::nanobind::class_>(m, - "BackwardData") + ::nanobind::class_>(m, "BackwardData") .def(::nanobind::init(), "Default constructor.") .def( "initialize", - &proxsuite::proxqp::dense::BackwardData::initialize, + &BackwardData::initialize, nanobind::arg("n") = 0, nanobind::arg("n_eq") = 0, nanobind::arg("n_in") = 0, "Initialize the jacobians (allocate memory if not already done) and set" " by default their value to zero.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dH, "dL_dH.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dg, "dL_dg.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dA, "dL_dA.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_db, "dL_db.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dC, "dL_dC.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_du, "dL_du.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dl, "dL_dl.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(BackwardData, dL_dl, "dL_dl."); - // .def_ro("dL_dH", &proxsuite::proxqp::dense::BackwardData::dL_dH) - // .def_ro("dL_dg", &proxsuite::proxqp::dense::BackwardData::dL_dg) - // .def_ro("dL_dA", &proxsuite::proxqp::dense::BackwardData::dL_dA) - // .def_ro("dL_db", - // &proxsuite::proxqp::dense::BackwardData::dL_db) - // .def_ro("dL_dC", - // &proxsuite::proxqp::dense::BackwardData::dL_dC) - // .def_ro("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du) - // .def_ro("dL_dl", - // &proxsuite::proxqp::dense::BackwardData::dL_dl) - // .def_ro("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du); + .def_ro("dL_dH", &BackwardData::dL_dH) + .def_ro("dL_dg", &BackwardData::dL_dg) + .def_ro("dL_dA", &BackwardData::dL_dA) + .def_ro("dL_db", &BackwardData::dL_db) + .def_ro("dL_dC", &BackwardData::dL_dC) + .def_ro("dL_du", &BackwardData::dL_du) + .def_ro("dL_dl", &BackwardData::dL_dl); // .def_ro("dL_dse", &proxsuite::proxqp::dense::BackwardData::dL_dse) // .def_ro("dL_dsi", // &proxsuite::proxqp::dense::BackwardData::dL_dsi); diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index a882bfa91..10b2135bd 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -12,8 +12,6 @@ #include #include -#include "helpers.hpp" - namespace proxsuite { namespace proxqp { namespace python { @@ -67,23 +65,40 @@ exposeResults(nanobind::module_ m) nanobind::arg("n_eq") = 0, nanobind::arg("n_in") = 0, "Constructor from QP model dimensions.") // constructor - .PROXSUITE_PYTHON_EIGEN_READWRITE(Results, x, "The primal solution.") - .PROXSUITE_PYTHON_EIGEN_READWRITE( - Results, - y, - "The dual solution associated to the equality constraints.") - .PROXSUITE_PYTHON_EIGEN_READWRITE( - Results, - z, - "The dual solution associated to the inequality constraints.") - .PROXSUITE_PYTHON_EIGEN_READWRITE( - Results, - se, + // .PROXSUITE_PYTHON_EIGEN_READWRITE(Results, x, "The primal solution.") + // .PROXSUITE_PYTHON_EIGEN_READWRITE( + // Results, + // y, + // "The dual solution associated to the equality constraints.") + // .PROXSUITE_PYTHON_EIGEN_READWRITE( + // Results, + // z, + // "The dual solution associated to the inequality constraints.") + // .PROXSUITE_PYTHON_EIGEN_READWRITE( + // Results, + // se, + // "Optimal shift to the closest feasible problem wrt equality + // constraints.") + // .PROXSUITE_PYTHON_EIGEN_READWRITE(Results, + // si, + // "Optimal shift to the closest feasible + // " "problem wrt inequality + // constraints.") + .def_rw("x", &Results::x, "The primal solution.") + .def_rw("y", + &Results::y, + "The dual solution associated to the equality constraints.") + .def_rw("z", + &Results::z, + "The dual solution associated to the inequality constraints.") + .def_rw( + "se", + &Results::se, "Optimal shift to the closest feasible problem wrt equality constraints.") - .PROXSUITE_PYTHON_EIGEN_READWRITE(Results, - si, - "Pptimal shift to the closest feasible " - "problem wrt inequality constraints.") + .def_rw("si", + &Results::si, + "Optimal shift to the closest feasible problem wrt inequality " + "constraints.") .def_rw("info", &Results::info) .def(nanobind::self == nanobind::self) .def(nanobind::self != nanobind::self) diff --git a/bindings/python/src/helpers.hpp b/bindings/python/src/helpers.hpp deleted file mode 100644 index fc7c37de6..000000000 --- a/bindings/python/src/helpers.hpp +++ /dev/null @@ -1,16 +0,0 @@ -// -// Copyright (c) 2022 INRIA -// -#ifndef proxsuite_python_helpers_hpp -#define proxsuite_python_helpers_hpp - -#define PROXSUITE_PYTHON_EIGEN_READWRITE(class, field_name, doc) \ - def_prop_rw( \ - #field_name, \ - [](class& self) { return self.field_name; }, \ - [](class& self, const decltype(class ::field_name)& value) { \ - self.field_name = value; \ - }, \ - doc) - -#endif // ifndef proxsuite_python_helpers_hpp From aaeca09f7e19b4b0fcf4bd056b9eed00d05ae0c2 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 22:08:19 +0200 Subject: [PATCH 19/44] [test] fix bare except --- test/src/serialization.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/serialization.py b/test/src/serialization.py index 0fd4c5770..0c72298e2 100644 --- a/test/src/serialization.py +++ b/test/src/serialization.py @@ -42,7 +42,7 @@ def generic_test(object, filename): try: with open(filename, "wb") as f: pickle.dump(object, f) - except: + except pickle.PickleError: dump_success = False else: dump_success = True @@ -52,7 +52,7 @@ def generic_test(object, filename): try: with open(filename, "rb") as f: loaded_object = pickle.load(f) - except: + except pickle.PickleError: read_success = False else: read_success = True From 54f42c6d8de399c967588dc93741995e85694288 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 22:22:13 +0200 Subject: [PATCH 20/44] [test] Fix serialization + pickle expects to be loading a str instead of bytes --- bindings/python/src/expose-model.hpp | 4 ++-- bindings/python/src/expose-qpobject.hpp | 4 ++-- bindings/python/src/expose-results.hpp | 4 ++-- bindings/python/src/expose-settings.hpp | 4 ++-- test/src/serialization.py | 5 +---- 5 files changed, 9 insertions(+), 12 deletions(-) diff --git a/bindings/python/src/expose-model.hpp b/bindings/python/src/expose-model.hpp index 3739f26b3..92ea5c6ce 100644 --- a/bindings/python/src/expose-model.hpp +++ b/bindings/python/src/expose-model.hpp @@ -70,10 +70,10 @@ exposeDenseModel(nanobind::module_ m) [](const proxsuite::proxqp::dense::Model& model) { return proxsuite::serialization::saveToString(model); }) - .def("__setstate__", [](dense::Model& model, nanobind::bytes& s) { + .def("__setstate__", [](dense::Model& model, const std::string& s) { // create qp model which will be updated by loaded data new (&model) dense::Model(1, 1, 1); - proxsuite::serialization::loadFromString(model, s.c_str()); + proxsuite::serialization::loadFromString(model, s); }); } } // namespace python diff --git a/bindings/python/src/expose-qpobject.hpp b/bindings/python/src/expose-qpobject.hpp index 70e22f2b6..3f37d3b72 100644 --- a/bindings/python/src/expose-qpobject.hpp +++ b/bindings/python/src/expose-qpobject.hpp @@ -224,9 +224,9 @@ exposeQpObjectDense(nanobind::module_ m) [](const dense::QP& qp) { return proxsuite::serialization::saveToString(qp); }) - .def("__setstate__", [](dense::QP& qp, nanobind::bytes& s) { + .def("__setstate__", [](dense::QP& qp, const std::string& s) { new (&qp) dense::QP(1, 1, 1); - proxsuite::serialization::loadFromString(qp, s.c_str()); + proxsuite::serialization::loadFromString(qp, s); }); ; } diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index 10b2135bd..73440dd33 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -106,9 +106,9 @@ exposeResults(nanobind::module_ m) [](const Results& results) { return proxsuite::serialization::saveToString(results); }) - .def("__setstate__", [](Results& results, nanobind::bytes& s) { + .def("__setstate__", [](Results& results, const std::string& s) { new (&results) Results{}; - proxsuite::serialization::loadFromString(results, s.c_str()); + proxsuite::serialization::loadFromString(results, s); }); ; } diff --git a/bindings/python/src/expose-settings.hpp b/bindings/python/src/expose-settings.hpp index 4569f7d3e..c99d8767d 100644 --- a/bindings/python/src/expose-settings.hpp +++ b/bindings/python/src/expose-settings.hpp @@ -96,9 +96,9 @@ exposeSettings(nanobind::module_ m) [](const Settings& settings) { return proxsuite::serialization::saveToString(settings); }) - .def("__setstate__", [](Settings& settings, nanobind::bytes& s) { + .def("__setstate__", [](Settings& settings, const std::string& s) { new (&settings) Settings{}; - proxsuite::serialization::loadFromString(settings, s.c_str()); + proxsuite::serialization::loadFromString(settings, s); }); ; } diff --git a/test/src/serialization.py b/test/src/serialization.py index 0c72298e2..4dd935ef8 100644 --- a/test/src/serialization.py +++ b/test/src/serialization.py @@ -29,9 +29,8 @@ def generate_mixed_qp(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray() + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') v = np.random.randn(n) # Fictitious solution - delta = np.random.rand(m) # To get inequality u = A @ v l = -1.0e20 * np.ones(m) @@ -63,8 +62,6 @@ def generic_test(object, filename): class DenseqpWrapperSerialization(unittest.TestCase): def test_pickle(self): - import pickle - print("------------------------test pickle") n = 10 H, g, A, b, C, u, l = generate_mixed_qp(n) From 029767647b733e817e1ef333adbdfdf32939ba25 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 22:22:46 +0200 Subject: [PATCH 21/44] [test] Force C-order (row-major) for all matrices in the tests --- test/src/dense_qp_solve.py | 12 ++++++++---- test/src/dense_qp_wrapper.py | 9 ++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/test/src/dense_qp_solve.py b/test/src/dense_qp_solve.py index b26896fd8..3920199d6 100644 --- a/test/src/dense_qp_solve.py +++ b/test/src/dense_qp_solve.py @@ -37,7 +37,9 @@ def generate_mixed_qp(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray() + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) # row-major v = np.random.randn(n) # Fictitious solution u = A @ v l = -1.0e20 * np.ones(m) @@ -309,10 +311,12 @@ def test_sparse_problem_with_exact_solution_known(self): M[i, i - 1] = 1 H = spa.csc_matrix(M.dot(M.transpose())).toarray() + H = np.ascontiguousarray(H) g = -np.ones((n,)) A = None b = None C = spa.csc_matrix(spa.eye(n)).toarray() + C = np.ascontiguousarray(C) l = 2.0 * np.ones((n,)) u = np.full(l.shape, +np.inf) @@ -378,11 +382,11 @@ def test_solve_qpsolvers_problem(self): os.path.join(data_path, "simple_qp_with_inifinity_lower_bound.mat"), squeeze_me=True, ) - P = m["P"].astype(float) + P = np.ascontiguousarray(m["P"].astype(float)) q = m["q"].astype(float) - A = m["A"].astype(float).reshape((1, 3)) + A = np.ascontiguousarray(m["A"].astype(float).reshape((1, 3))) b = np.array([m["b"]]).reshape((1,)) - C = m["C"].astype(float) + C = np.ascontiguousarray(m["C"].astype(float)) l = m["l"].astype(float) u = m["u"].astype(float) diff --git a/test/src/dense_qp_wrapper.py b/test/src/dense_qp_wrapper.py index 0bda2f085..4f3810bae 100644 --- a/test/src/dense_qp_wrapper.py +++ b/test/src/dense_qp_wrapper.py @@ -37,7 +37,7 @@ def generate_mixed_qp(n, seed=1, reg=0.01): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray() + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') v = np.random.randn(n) # Fictitious solution delta = np.random.rand(m) # To get inequality u = A @ v @@ -67,9 +67,8 @@ def generate_mixed_qp_with_box(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray() + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') v = np.random.randn(n) # Fictitious solution - delta = np.random.rand(m) # To get inequality u = A @ v l = -1.0e20 * np.ones(m) delta_box = np.random.rand(n) @@ -3924,11 +3923,11 @@ def test_sparse_problem_with_exact_solution_known(self): M[i, i + 1] = -1 M[i, i - 1] = 1 - H = spa.csc_matrix(M.dot(M.transpose())).toarray() + H = spa.csc_matrix(M.dot(M.transpose())).toarray(order='C') g = -np.ones((n,)) A = None b = None - C = spa.csc_matrix(spa.eye(n)).toarray() + C = spa.csc_matrix(spa.eye(n)).toarray(order='C') l = 2.0 * np.ones((n,)) u = np.full(l.shape, +np.inf) From 8dccf3b1b5c0dec08d2116b0e2e37ea90ca06911 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 22:23:25 +0200 Subject: [PATCH 22/44] [bindings/python] Fix overloaded dense.solve() signatures, switch nullopt for nanobind::none() --- bindings/python/src/expose-solve.hpp | 118 +++++++++++++-------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/bindings/python/src/expose-solve.hpp b/bindings/python/src/expose-solve.hpp index 8721ba570..c89faad1b 100644 --- a/bindings/python/src/expose-solve.hpp +++ b/bindings/python/src/expose-solve.hpp @@ -51,30 +51,30 @@ solveDenseQp(nanobind::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, - nanobind::arg("x") = nullopt, - nanobind::arg("y") = nullopt, - nanobind::arg("z") = nullopt, - nanobind::arg("eps_abs") = nullopt, - nanobind::arg("eps_rel") = nullopt, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("verbose") = nullopt, + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A").none(), + nanobind::arg("b").none(), + nanobind::arg("C").none(), + nanobind::arg("l").none(), + nanobind::arg("u").none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, nanobind::arg("compute_timings") = false, - nanobind::arg("max_iter") = nullopt, + nanobind::arg("max_iter") = nanobind::none(), nanobind::arg("initial_guess") = InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, nanobind::arg("check_duality_gap") = false, - nanobind::arg("eps_duality_gap_abs") = nullopt, - nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), nanobind::arg("primal_infeasibility_solving") = false, nanobind::arg("default_H_eigenvalue_estimate") = 0.); @@ -112,32 +112,32 @@ solveDenseQp(nanobind::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, - nanobind::arg("l_box") = nullopt, - nanobind::arg("u_box") = nullopt, - nanobind::arg("x") = nullopt, - nanobind::arg("y") = nullopt, - nanobind::arg("z") = nullopt, - nanobind::arg("eps_abs") = nullopt, - nanobind::arg("eps_rel") = nullopt, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("verbose") = nullopt, + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, nanobind::arg("compute_timings") = false, - nanobind::arg("max_iter") = nullopt, + nanobind::arg("max_iter") = nanobind::none(), nanobind::arg("initial_guess") = proxsuite::proxqp::InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, nanobind::arg("check_duality_gap") = false, - nanobind::arg("eps_duality_gap_abs") = nullopt, - nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), nanobind::arg("primal_infeasibility_solving") = false, nanobind::arg("default_H_eigenvalue_estimate") = 0.); } @@ -160,31 +160,31 @@ solveSparseQp(nanobind::module_ m) "parameters (warm start, initial guess option, proximal step sizes, " "absolute and relative accuracies, maximum number of iterations, " "preconditioner execution).", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, - nanobind::arg("x") = nullopt, - nanobind::arg("y") = nullopt, - nanobind::arg("z") = nullopt, - nanobind::arg("eps_abs") = nullopt, - nanobind::arg("eps_rel") = nullopt, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("verbose") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("x") = nanobind::none(), + nanobind::arg("y") = nanobind::none(), + nanobind::arg("z") = nanobind::none(), + nanobind::arg("eps_abs") = nanobind::none(), + nanobind::arg("eps_rel") = nanobind::none(), + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("verbose") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, nanobind::arg("compute_timings") = false, - nanobind::arg("max_iter") = nullopt, + nanobind::arg("max_iter") = nanobind::none(), nanobind::arg("initial_guess") = InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, nanobind::arg("sparse_backend") = SparseBackend::Automatic, nanobind::arg("check_duality_gap") = false, - nanobind::arg("eps_duality_gap_abs") = nullopt, - nanobind::arg("eps_duality_gap_rel") = nullopt, + nanobind::arg("eps_duality_gap_abs") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), nanobind::arg("primal_infeasibility_solving") = false, nanobind::arg("default_H_eigenvalue_estimate") = 0.); } From 395933c85f82193fa88ece54184c92401f31b542 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 22:29:17 +0200 Subject: [PATCH 23/44] [test] reformat using black --- test/src/dense_qp_wrapper.py | 12 ++++++++---- test/src/serialization.py | 4 +++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/test/src/dense_qp_wrapper.py b/test/src/dense_qp_wrapper.py index 4f3810bae..871f0a612 100644 --- a/test/src/dense_qp_wrapper.py +++ b/test/src/dense_qp_wrapper.py @@ -37,7 +37,9 @@ def generate_mixed_qp(n, seed=1, reg=0.01): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) v = np.random.randn(n) # Fictitious solution delta = np.random.rand(m) # To get inequality u = A @ v @@ -67,7 +69,9 @@ def generate_mixed_qp_with_box(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) v = np.random.randn(n) # Fictitious solution u = A @ v l = -1.0e20 * np.ones(m) @@ -3923,11 +3927,11 @@ def test_sparse_problem_with_exact_solution_known(self): M[i, i + 1] = -1 M[i, i - 1] = 1 - H = spa.csc_matrix(M.dot(M.transpose())).toarray(order='C') + H = spa.csc_matrix(M.dot(M.transpose())).toarray(order="C") g = -np.ones((n,)) A = None b = None - C = spa.csc_matrix(spa.eye(n)).toarray(order='C') + C = spa.csc_matrix(spa.eye(n)).toarray(order="C") l = 2.0 * np.ones((n,)) u = np.full(l.shape, +np.inf) diff --git a/test/src/serialization.py b/test/src/serialization.py index 4dd935ef8..4c03725e9 100644 --- a/test/src/serialization.py +++ b/test/src/serialization.py @@ -29,7 +29,9 @@ def generate_mixed_qp(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray(order='C') + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) v = np.random.randn(n) # Fictitious solution u = A @ v l = -1.0e20 * np.ones(m) From 9008bf95ed5118f42f1d75dac97e483cfc9f2608 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 6 Aug 2024 23:07:15 +0200 Subject: [PATCH 24/44] [workflows] also install typing_extensions package --- .github/workflows/ci-linux-osx-win-conda.yml | 2 +- .github/workflows/release-osx-win.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci-linux-osx-win-conda.yml b/.github/workflows/ci-linux-osx-win-conda.yml index 48f32ad21..a17df614e 100644 --- a/.github/workflows/ci-linux-osx-win-conda.yml +++ b/.github/workflows/ci-linux-osx-win-conda.yml @@ -82,7 +82,7 @@ jobs: # Workaround for https://github.com/conda-incubator/setup-miniconda/issues/186 conda config --remove channels defaults # Compilation related dependencies - mamba install cmake compilers make pkg-config doxygen ninja graphviz + mamba install cmake compilers make pkg-config doxygen ninja graphviz typing_extensions # Main dependencies mamba install eigen simde # Test dependencies diff --git a/.github/workflows/release-osx-win.yml b/.github/workflows/release-osx-win.yml index 65f4f91de..8437eac91 100644 --- a/.github/workflows/release-osx-win.yml +++ b/.github/workflows/release-osx-win.yml @@ -57,7 +57,7 @@ jobs: run: | # Workaround for https://github.com/conda-incubator/setup-miniconda/issues/186 conda config --remove channels defaults - mamba install doxygen graphviz eigen simde cmake compilers + mamba install doxygen graphviz eigen simde cmake compilers typing_extensions - name: Print environment [Conda] if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') From 22fcd22483bb2ae3b050f0162bbd84f063737ea4 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Wed, 7 Aug 2024 12:03:01 +0200 Subject: [PATCH 25/44] [examples/python] fix toarray() for C order --- examples/python/solve_without_api.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/python/solve_without_api.py b/examples/python/solve_without_api.py index 3a489203a..1ba2c166d 100644 --- a/examples/python/solve_without_api.py +++ b/examples/python/solve_without_api.py @@ -16,7 +16,7 @@ # solve the problem using the dense backend results2 = proxsuite.proxqp.dense.solve( - H.toarray(), g, A.toarray(), b, C.toarray(), l, u + H.toarray(order="C"), g, A.toarray(order="C"), b, C.toarray(order="C"), l, u ) # Note finally, that the matrices are in sparse format, when using the dense backend you # should convert them in dense format From 603e208d866d92b6050d19556056d385f8d96288 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Wed, 7 Aug 2024 12:19:33 +0200 Subject: [PATCH 26/44] [test] parallel_qp_solve : .toarray() to C order --- test/src/parallel_qp_solve.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/test/src/parallel_qp_solve.py b/test/src/parallel_qp_solve.py index 4ea203569..11654c753 100644 --- a/test/src/parallel_qp_solve.py +++ b/test/src/parallel_qp_solve.py @@ -33,13 +33,23 @@ def generate_mixed_qp(n, seed=1): P = spa.coo_matrix(P) # print("sparsity of P : {}".format((P.nnz) / (n**2))) q = np.random.randn(n) - A = spa.random(m, n, density=0.95, data_rvs=np.random.randn, format="csc").toarray() + A = spa.random(m, n, density=0.95, data_rvs=np.random.randn, format="csc").toarray( + order="C" + ) v = np.random.randn(n) # Fictitious solution delta = np.random.rand(m) # To get inequality u = A @ v l = -1.0e20 * np.ones(m) - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] + return ( + P.toarray(order="C"), + q, + A[:n_eq, :], + u[:n_eq], + A[n_in:, :], + u[n_in:], + l[n_in:], + ) class ParallelWrapper(unittest.TestCase): From 856e0571a0b13e09b8229d005264a79e50a79be4 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Wed, 7 Aug 2024 17:39:50 +0200 Subject: [PATCH 27/44] Update minimum Python to 3.8 (3.7 has been EOL for a year) --- bindings/python/CMakeLists.txt | 2 +- pyproject.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 30d49293e..2e41019a5 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -8,7 +8,7 @@ include(${JRL_CMAKE_MODULES}/python-helpers.cmake) option(GENERATE_PYTHON_STUBS "Generate Python stubs" OFF) findpython(REQUIRED Development.Module) -find_package(Python 3.8 COMPONENTS ${PYTHON_COMPONENTS}) +find_package(Python 3.7 COMPONENTS ${PYTHON_COMPONENTS}) if(IS_ABSOLUTE ${PYTHON_SITELIB}) set(${PYWRAP}_INSTALL_DIR ${PYTHON_SITELIB}/${PROJECT_NAME}) diff --git a/pyproject.toml b/pyproject.toml index 3aa236457..9f65cf1c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,7 +3,7 @@ name = "proxsuite" version = "0.6.7" description = "Quadratic Programming Solver for Robotics and beyond." readme = "README.md" -requires-python = ">= 3.7" +requires-python = ">= 3.8" license = "BSD-2-Clause" dependencies = ["numpy","scipy"] From 4882f59d70edc003e157607fba3c5e1e63c1a9ef Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Thu, 8 Aug 2024 12:11:15 +0200 Subject: [PATCH 28/44] [test] cvxpy.py : ensure matrix and gradient are float64 --- test/src/cvxpy.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/src/cvxpy.py b/test/src/cvxpy.py index d4e609e16..cd45496cf 100644 --- a/test/src/cvxpy.py +++ b/test/src/cvxpy.py @@ -56,8 +56,8 @@ def test_trigger_infeasibility_with_exact_solution_known(self): def test_one_dim_with_exact_solution_known(self): print("------------------------ test_one_dim_with_exact_solution_known") n = 1 - H = np.array([[20]]) - g = np.array([-10]) + H = np.array([[20.0]]) + g = np.array([-10.0]) A = None b = None C = np.array([[1.0]]) From b810caf506d5b9da894092cc3d78d3259873465b Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Thu, 8 Aug 2024 14:37:45 +0200 Subject: [PATCH 29/44] [bindings/python] cmake : fix the bloody include dirs variable ?? --- bindings/python/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 2e41019a5..93d8d3b91 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -8,7 +8,7 @@ include(${JRL_CMAKE_MODULES}/python-helpers.cmake) option(GENERATE_PYTHON_STUBS "Generate Python stubs" OFF) findpython(REQUIRED Development.Module) -find_package(Python 3.7 COMPONENTS ${PYTHON_COMPONENTS}) +set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) if(IS_ABSOLUTE ${PYTHON_SITELIB}) set(${PYWRAP}_INSTALL_DIR ${PYTHON_SITELIB}/${PROJECT_NAME}) From e7394b3cb1002fb4314ec5d05e50115102698bb3 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Thu, 8 Aug 2024 18:03:48 +0200 Subject: [PATCH 30/44] [examples/python] fix some more C orders --- examples/python/solve_without_api.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/examples/python/solve_without_api.py b/examples/python/solve_without_api.py index 1ba2c166d..53904c718 100644 --- a/examples/python/solve_without_api.py +++ b/examples/python/solve_without_api.py @@ -34,7 +34,15 @@ # make sure to specify l_box=l_box, u_box=u_box in order to make work the # overloading results_dense_solver_box = proxsuite.proxqp.dense.solve( - H.toarray(), g, A.toarray(), b, C.toarray(), l, u, l_box=l_box, u_box=u_box + H.toarray(order="C"), + g, + A.toarray(order="C"), + b, + C.toarray(order="C"), + l, + u, + l_box=l_box, + u_box=u_box, ) # print an optimal solution print("optimal x: {}".format(results_dense_solver_box.x)) From f004f5d303893ca1a08aa77f80c114e26033871b Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Fri, 9 Aug 2024 11:42:00 +0200 Subject: [PATCH 31/44] cmake : alias some targets to Python::* --- bindings/python/CMakeLists.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 93d8d3b91..a58b39df6 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -1,5 +1,5 @@ if(UNIX) - set(PYTHON_COMPONENTS Development.Module) + set(PYTHON_COMPONENTS Interpreter Development.Module) endif() include(${JRL_CMAKE_MODULES}/python.cmake) @@ -7,8 +7,12 @@ include(${JRL_CMAKE_MODULES}/python-helpers.cmake) option(GENERATE_PYTHON_STUBS "Generate Python stubs" OFF) -findpython(REQUIRED Development.Module) -set(Python_INCLUDE_DIRS ${PYTHON_INCLUDE_DIRS}) +findpython(REQUIRED) +set(Python_INCLUDE_DIRS ${Python3_INCLUDE_DIRS}) +# Nanobind expects these targets instead of Python3::* +# https://github.com/jrl-umi3218/jrl-cmakemodules/issues/708 +add_library(Python::Module ALIAS Python3::Module) +add_executable(Python::Interpreter ALIAS Python3::Interpreter) if(IS_ABSOLUTE ${PYTHON_SITELIB}) set(${PYWRAP}_INSTALL_DIR ${PYTHON_SITELIB}/${PROJECT_NAME}) From 05632d223b8707ee79f8a106bb5a44ac27b7ab6d Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Fri, 9 Aug 2024 12:07:49 +0200 Subject: [PATCH 32/44] [workflows] remove macOS C++14 --- .github/workflows/ci-linux-osx-win-conda.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/ci-linux-osx-win-conda.yml b/.github/workflows/ci-linux-osx-win-conda.yml index a17df614e..be2bec460 100644 --- a/.github/workflows/ci-linux-osx-win-conda.yml +++ b/.github/workflows/ci-linux-osx-win-conda.yml @@ -29,10 +29,6 @@ jobs: compiler: clang-cl - name: windows-latest os: windows-latest - - name: macos-latest - os: macos-latest - build_type: Release - cxx_std: 14 - name: macos-latest os: macos-latest build_type: Debug From 27238729482744425f05c0c2ca8a7fa4d2f38dc8 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Fri, 9 Aug 2024 13:23:49 +0200 Subject: [PATCH 33/44] [bindings/python] use nanobind::none() instead of nullopt --- bindings/python/src/expose-qpobject.hpp | 144 ++++++++++++------------ 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/bindings/python/src/expose-qpobject.hpp b/bindings/python/src/expose-qpobject.hpp index 3f37d3b72..84237b640 100644 --- a/bindings/python/src/expose-qpobject.hpp +++ b/bindings/python/src/expose-qpobject.hpp @@ -102,18 +102,18 @@ exposeQpObjectDense(nanobind::module_ m) optional, optional)>(&dense::QP::init), "function for initialize the QP model.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, + nanobind::arg("H"), + nanobind::arg("g"), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def("init", static_cast::*)(optional>, optional>, @@ -130,20 +130,20 @@ exposeQpObjectDense(nanobind::module_ m) optional, optional)>(&dense::QP::init), "function for initialize the QP model.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, - nanobind::arg("l_box") = nullopt, - nanobind::arg("u_box") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def("solve", static_cast::*)()>(&dense::QP::solve), "function used for solving the QP problem, using default parameters.") @@ -170,18 +170,18 @@ exposeQpObjectDense(nanobind::module_ m) optional)>(&dense::QP::update), "function used for updating matrix or vector entry of the model using " "dense matrix entries.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), nanobind::arg("update_preconditioner") = false, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def( "update", static_cast::*)(optional>, @@ -200,20 +200,20 @@ exposeQpObjectDense(nanobind::module_ m) optional)>(&dense::QP::update), "function used for updating matrix or vector entry of the model using " "dense matrix entries.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, - nanobind::arg("l_box") = nullopt, - nanobind::arg("u_box") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), + nanobind::arg("l_box") = nanobind::none(), + nanobind::arg("u_box") = nanobind::none(), nanobind::arg("update_preconditioner") = false, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def("cleanup", &dense::QP::cleanup, "function used for cleaning the workspace and result " @@ -251,8 +251,8 @@ exposeQpObjectSparse(nanobind::module_ m) .def(::nanobind::init&, const sparse::SparseMat&, const sparse::SparseMat&>(), - nanobind::arg("H_mask") = nullopt, - nanobind::arg("A_mask") = nullopt, + nanobind::arg("H_mask") = nanobind::none(), + nanobind::arg("A_mask") = nanobind::none(), nanobind::arg("C_mask") = 0, "Constructor using QP model sparsity structure.") // constructor .def_ro("model", &sparse::QP::model, "class containing the QP model") @@ -266,35 +266,35 @@ exposeQpObjectSparse(nanobind::module_ m) &sparse::QP::init, "function for initializing the model when passing sparse matrices in " "entry.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), nanobind::arg("compute_preconditioner") = true, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def("update", &sparse::QP::update, "function for updating the model when passing sparse matrices in " "entry.", - nanobind::arg("H") = nullopt, - nanobind::arg("g") = nullopt, - nanobind::arg("A") = nullopt, - nanobind::arg("b") = nullopt, - nanobind::arg("C") = nullopt, - nanobind::arg("l") = nullopt, - nanobind::arg("u") = nullopt, + nanobind::arg("H") = nanobind::none(), + nanobind::arg("g") = nanobind::none(), + nanobind::arg("A") = nanobind::none(), + nanobind::arg("b") = nanobind::none(), + nanobind::arg("C") = nanobind::none(), + nanobind::arg("l") = nanobind::none(), + nanobind::arg("u") = nanobind::none(), nanobind::arg("update_preconditioner") = false, - nanobind::arg("rho") = nullopt, - nanobind::arg("mu_eq") = nullopt, - nanobind::arg("mu_in") = nullopt, - nanobind::arg("manual_minimal_H_eigenvalue") = nullopt) + nanobind::arg("rho") = nanobind::none(), + nanobind::arg("mu_eq") = nanobind::none(), + nanobind::arg("mu_in") = nanobind::none(), + nanobind::arg("manual_minimal_H_eigenvalue") = nanobind::none()) .def("solve", static_cast::*)()>(&sparse::QP::solve), "function used for solving the QP problem, using default parameters.") From 94f13f1898a1f66fea2ec30c7aa18b3dc2481df8 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 20 Aug 2024 17:50:00 +0200 Subject: [PATCH 34/44] [bindings] point nanobind to latest master --- .gitmodules | 2 +- bindings/python/external/nanobind | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index 2867bf33b..0556288d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "bindings/python/external/nanobind"] path = bindings/python/external/nanobind - url = https://github.com/wjakob/nanobind + url = https://github.com/ManifoldFR/nanobind [submodule "cmake-module"] path = cmake-module url = https://github.com/jrl-umi3218/jrl-cmakemodules.git diff --git a/bindings/python/external/nanobind b/bindings/python/external/nanobind index 8d7f1ee06..8a65e0e39 160000 --- a/bindings/python/external/nanobind +++ b/bindings/python/external/nanobind @@ -1 +1 @@ -Subproject commit 8d7f1ee0621c17fa370b704b2100ffa0243d5bfb +Subproject commit 8a65e0e39ca1621fdf20130e94bccb5850ca0f81 From 6d167e899256bbc69b10c9cb4f920d8dd82524d8 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 27 Aug 2024 13:14:22 +0200 Subject: [PATCH 35/44] Add fix for std::optional> --- bindings/python/src/expose-results.hpp | 2 +- bindings/python/src/expose-solve.hpp | 2 +- bindings/python/src/optional-eigen-fix.hpp | 45 ++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 bindings/python/src/optional-eigen-fix.hpp diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index 73440dd33..f4bd8c492 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "optional-eigen-fix.hpp" #include #include diff --git a/bindings/python/src/expose-solve.hpp b/bindings/python/src/expose-solve.hpp index c89faad1b..25befc0bd 100644 --- a/bindings/python/src/expose-solve.hpp +++ b/bindings/python/src/expose-solve.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include "optional-eigen-fix.hpp" namespace proxsuite { namespace proxqp { diff --git a/bindings/python/src/optional-eigen-fix.hpp b/bindings/python/src/optional-eigen-fix.hpp new file mode 100644 index 000000000..3a09564d7 --- /dev/null +++ b/bindings/python/src/optional-eigen-fix.hpp @@ -0,0 +1,45 @@ +// +// Copyright (c) 2024 INRIA +// +#pragma once + +#include +#include + +NAMESPACE_BEGIN(NB_NAMESPACE) +NAMESPACE_BEGIN(detail) + +/// Fix std::optional for Eigen::Ref +/// Credit to github.com/WKarel for this suggestion! +/// https://github.com/wjakob/nanobind/issues/682#issuecomment-2310746145 +template +struct type_caster>> + : optional_caster>> +{ + using Ref = Eigen::Ref; + using Optional = std::optional; + using Caster = typename type_caster::optional_caster::Caster; + using Map = typename Caster::Map; + using DMap = typename Caster::DMap; + NB_TYPE_CASTER(Optional, optional_name(Caster::Name)) + + bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept + { + if (src.is_none()) + return true; + Caster caster; + if (!caster.from_python(src, flags_for_local_caster(flags), cleanup) || + !caster.template can_cast()) + return false; + /// This allows us to bypass the type_caster for Eigen::Ref + /// which is broken due to lack of NRVO + move ctor in latest Eigen release. + if (caster.dcaster.caster.value.is_valid()) + value.emplace(caster.dcaster.operator DMap()); + else + value.emplace(caster.caster.operator Map()); + return true; + } +}; + +NAMESPACE_END(detail) +NAMESPACE_END(NB_NAMESPACE) From 3d637fd951876204c97698c51e064d3f699f8d90 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 27 Aug 2024 13:27:00 +0200 Subject: [PATCH 36/44] apparently i64/u64 and isize/size_t are not the same thing on the Windows --- bindings/python/src/expose-qpobject.hpp | 8 ++++---- bindings/python/src/expose-qpvector.hpp | 4 ++-- bindings/python/src/expose-results.hpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/bindings/python/src/expose-qpobject.hpp b/bindings/python/src/expose-qpobject.hpp index 84237b640..288c1a7d2 100644 --- a/bindings/python/src/expose-qpobject.hpp +++ b/bindings/python/src/expose-qpobject.hpp @@ -58,9 +58,9 @@ exposeQpObjectDense(nanobind::module_ m) // .def_rw("run_time", &RuizEquilibration::sym); ::nanobind::class_>(m, "QP") - .def(::nanobind::init(), @@ -243,7 +243,7 @@ exposeQpObjectSparse(nanobind::module_ m) { ::nanobind::class_>(m, "QP") - .def(::nanobind::init(), + .def(::nanobind::init(), nanobind::arg("n") = 0, nanobind::arg("n_eq") = 0, nanobind::arg("n_in") = 0, diff --git a/bindings/python/src/expose-qpvector.hpp b/bindings/python/src/expose-qpvector.hpp index 1b077a4af..0530ac41a 100644 --- a/bindings/python/src/expose-qpvector.hpp +++ b/bindings/python/src/expose-qpvector.hpp @@ -21,7 +21,7 @@ exposeQPVectorDense(nanobind::module_ m) ::nanobind::class_>(m, "BatchQP") .def( - ::nanobind::init(), + ::nanobind::init(), nanobind::arg("batch_size") = 0, "Default constructor using the BatchSize of qp models to store.") // constructor .def("init_qp_in_place", @@ -51,7 +51,7 @@ exposeQPVectorSparse(nanobind::module_ m) ::nanobind::class_>(m, "BatchQP") .def( - ::nanobind::init(), + ::nanobind::init(), nanobind::arg("batch_size") = 0, "Default constructor using the BatchSize of qp models to store.") // constructor .def("init_qp_in_place", diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index f4bd8c492..bab63daba 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -60,7 +60,7 @@ exposeResults(nanobind::module_ m) "find_H_minimal_eigenvalue."); ::nanobind::class_>(m, "Results") - .def(::nanobind::init(), + .def(::nanobind::init(), nanobind::arg("n") = 0, nanobind::arg("n_eq") = 0, nanobind::arg("n_in") = 0, From 3804bae16d47fd007217f8347f9f876b3b7c0185 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 27 Aug 2024 16:29:09 +0200 Subject: [PATCH 37/44] Change back URL of nanobind submodule --- .gitmodules | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitmodules b/.gitmodules index 0556288d9..2867bf33b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "bindings/python/external/nanobind"] path = bindings/python/external/nanobind - url = https://github.com/ManifoldFR/nanobind + url = https://github.com/wjakob/nanobind [submodule "cmake-module"] path = cmake-module url = https://github.com/jrl-umi3218/jrl-cmakemodules.git From bfc2bea1ac52ffa87f86bd5cca440ed69a7d00bb Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 27 Aug 2024 17:29:28 +0200 Subject: [PATCH 38/44] Allow use of installed nanobind --- bindings/python/CMakeLists.txt | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index a58b39df6..29487b9a9 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -21,9 +21,22 @@ else() ${CMAKE_INSTALL_PREFIX}/${PYTHON_SITELIB}/${PROJECT_NAME}) endif() -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) -add_subdirectory(external/nanobind - ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) +cmake_policy(PUSH) +cmake_policy(SET CMP0074 NEW) +# Detect the installed nanobind package and import it into CMake +execute_process( + COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir + OUTPUT_STRIP_TRAILING_WHITESPACE + OUTPUT_VARIABLE nanobind_ROOT) +find_package(nanobind CONFIG) +cmake_policy(POP) +if(NOT nanobind_FOUND) + file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) + add_subdirectory(external/nanobind + ${CMAKE_CURRENT_BINARY_DIR}/external/nanobind) +else() + message(STATUS "Found installed nanobind.") +endif() add_custom_target(${PROJECT_NAME}_python) From 0e4db0af171b02c0f5c11f814c65262bd567e571 Mon Sep 17 00:00:00 2001 From: Wilson Date: Tue, 27 Aug 2024 19:14:04 +0200 Subject: [PATCH 39/44] Update bindings/python/helpers/instruction-set.cpp Co-authored-by: Fabian Schramm <55981657+fabinsch@users.noreply.github.com> --- bindings/python/helpers/instruction-set.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/helpers/instruction-set.cpp b/bindings/python/helpers/instruction-set.cpp index cc92bf214..64035c447 100644 --- a/bindings/python/helpers/instruction-set.cpp +++ b/bindings/python/helpers/instruction-set.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #include From 449267f60a68f14bcea7203f5863817acde4b009 Mon Sep 17 00:00:00 2001 From: Wilson Date: Tue, 27 Aug 2024 19:16:28 +0200 Subject: [PATCH 40/44] Update bindings/python/src/expose-results.hpp Co-authored-by: Fabian Schramm <55981657+fabinsch@users.noreply.github.com> --- bindings/python/src/expose-results.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index bab63daba..fa1e63160 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022-2023 INRIA +// Copyright (c) 2022-2024 INRIA // #include #include From d509b5e373f5993ac473e06731fe886589c647f0 Mon Sep 17 00:00:00 2001 From: Wilson Date: Tue, 27 Aug 2024 19:18:51 +0200 Subject: [PATCH 41/44] Update bindings/python/src/expose-helpers.hpp Co-authored-by: Fabian Schramm <55981657+fabinsch@users.noreply.github.com> --- bindings/python/src/expose-helpers.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/src/expose-helpers.hpp b/bindings/python/src/expose-helpers.hpp index 76cba0e70..872a60fdd 100644 --- a/bindings/python/src/expose-helpers.hpp +++ b/bindings/python/src/expose-helpers.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #include From 519a6246437420b4be25ab13c5a3b16920248046 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Tue, 27 Aug 2024 19:52:51 +0200 Subject: [PATCH 42/44] [workflows] do not re-run linux, osx, win, arch CI and release CI when updating CHANGELOG.md --- .github/workflows/ci-arch.yml | 2 ++ .github/workflows/ci-linux-osx-win-conda.yml | 2 ++ .github/workflows/ci-linux-ros.yml | 6 +++++- .github/workflows/release-linux.yml | 2 ++ .github/workflows/release-osx-win.yml | 2 ++ 5 files changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ci-arch.yml b/.github/workflows/ci-arch.yml index af0cfd2ad..b9d2866d4 100644 --- a/.github/workflows/ci-arch.yml +++ b/.github/workflows/ci-arch.yml @@ -3,6 +3,8 @@ name: CI - ArchLinux on: push: pull_request: + paths-ignore: + - CHANGELOG.md jobs: build-with-arch: diff --git a/.github/workflows/ci-linux-osx-win-conda.yml b/.github/workflows/ci-linux-osx-win-conda.yml index be2bec460..ac7ed0e53 100644 --- a/.github/workflows/ci-linux-osx-win-conda.yml +++ b/.github/workflows/ci-linux-osx-win-conda.yml @@ -3,6 +3,8 @@ name: CI - Linux/OSX/Windows - Conda on: push: pull_request: + paths-ignore: + - CHANGELOG.md jobs: build-with-conda: diff --git a/.github/workflows/ci-linux-ros.yml b/.github/workflows/ci-linux-ros.yml index 49c54f1bd..c24478bc9 100644 --- a/.github/workflows/ci-linux-ros.yml +++ b/.github/workflows/ci-linux-ros.yml @@ -1,5 +1,9 @@ name: CI - Linux - ROS -on: [push, pull_request] +on: + push: + pull_request: + paths-ignore: + - CHANGELOG.md jobs: CI: diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index 54125cbc8..84bcc72b3 100644 --- a/.github/workflows/release-linux.yml +++ b/.github/workflows/release-linux.yml @@ -2,6 +2,8 @@ name: Release on PyPI [Linux] on: pull_request: + paths-ignore: + - CHANGELOG.md release: types: - published diff --git a/.github/workflows/release-osx-win.yml b/.github/workflows/release-osx-win.yml index 8437eac91..845a980e6 100644 --- a/.github/workflows/release-osx-win.yml +++ b/.github/workflows/release-osx-win.yml @@ -2,6 +2,8 @@ name: Release on PyPI [Windows, Mac] on: pull_request: + paths-ignore: + - CHANGELOG.md release: types: - published From c7c2b2a504398dae27895e76b81df2460b71a551 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Fri, 30 Aug 2024 11:55:34 +0200 Subject: [PATCH 43/44] Set version of nanobind submodule to release v2.1.0 --- bindings/python/external/nanobind | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bindings/python/external/nanobind b/bindings/python/external/nanobind index 8a65e0e39..9641bb715 160000 --- a/bindings/python/external/nanobind +++ b/bindings/python/external/nanobind @@ -1 +1 @@ -Subproject commit 8a65e0e39ca1621fdf20130e94bccb5850ca0f81 +Subproject commit 9641bb7151f04120013b812789b3ebdfa7e7324f From d71966317a12ec943ce4702135c3d90828924899 Mon Sep 17 00:00:00 2001 From: ManifoldFR Date: Fri, 30 Aug 2024 12:05:15 +0200 Subject: [PATCH 44/44] Fix for nanobind v2.1.0 release --- bindings/python/src/optional-eigen-fix.hpp | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/bindings/python/src/optional-eigen-fix.hpp b/bindings/python/src/optional-eigen-fix.hpp index 3a09564d7..cedd91103 100644 --- a/bindings/python/src/optional-eigen-fix.hpp +++ b/bindings/python/src/optional-eigen-fix.hpp @@ -14,15 +14,19 @@ NAMESPACE_BEGIN(detail) /// https://github.com/wjakob/nanobind/issues/682#issuecomment-2310746145 template struct type_caster>> - : optional_caster>> { using Ref = Eigen::Ref; using Optional = std::optional; - using Caster = typename type_caster::optional_caster::Caster; + using Caster = make_caster; using Map = typename Caster::Map; using DMap = typename Caster::DMap; NB_TYPE_CASTER(Optional, optional_name(Caster::Name)) + type_caster() + : value(std::nullopt) + { + } + bool from_python(handle src, uint8_t flags, cleanup_list* cleanup) noexcept { if (src.is_none()) @@ -39,6 +43,17 @@ struct type_caster>> value.emplace(caster.caster.operator Map()); return true; } + + template + static handle from_cpp(T_&& value, + rv_policy policy, + cleanup_list* cleanup) noexcept + { + if (!value) + return none().release(); + + return Caster::from_cpp(forward_like_(*value), policy, cleanup); + } }; NAMESPACE_END(detail)