diff --git a/.github/workflows/ci-arch.yml b/.github/workflows/ci-arch.yml index 18e21d371..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: @@ -14,7 +16,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..4b1913383 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: @@ -29,10 +31,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 @@ -55,24 +53,15 @@ jobs: continue_on_error: false steps: - - uses: actions/checkout@v2 - - - uses: conda-incubator/setup-miniconda@v2 - if: matrix.os != 'macos-14' + - uses: actions/checkout@v4 with: - miniforge-variant: Mambaforge - miniforge-version: latest - channels: conda-forge - python-version: "3.10" - activate-environment: proxsuite + submodules: recursive - uses: conda-incubator/setup-miniconda@v3 - if: matrix.os == 'macos-14' with: - channels: conda-forge - python-version: "3.10" + miniforge-version: latest activate-environment: proxsuite - installer-url: https://github.com/conda-forge/miniforge/releases/download/23.11.0-0/Mambaforge-23.11.0-0-MacOSX-arm64.sh + - name: Install dependencies [Conda] shell: bash -l {0} @@ -80,17 +69,17 @@ 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 + conda install cmake compilers make pkg-config doxygen ninja graphviz typing_extensions llvm-openmp clang # Main dependencies - mamba install eigen simde + conda install eigen simde # Test dependencies - mamba install libmatio numpy scipy + conda install libmatio numpy scipy - - name: Install julia [macOS/Linux] - if: contains(matrix.os, 'macos-latest') || contains(matrix.os, 'ubuntu') + - name: Install julia [Linux] + if: contains(matrix.os, 'ubuntu') shell: bash -l {0} run: | - mamba install julia + conda install julia - name: Activate ccache [Conda] uses: hendrikmuhs/ccache-action@v1.2 @@ -102,7 +91,7 @@ jobs: shell: bash -l {0} run: | conda info - mamba list + conda list env - name: Configure [Conda/Linux&macOS] @@ -114,7 +103,7 @@ jobs: git submodule update --init mkdir build cd build - cmake .. -GNinja -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_EXECUTABLE=$(which python3) -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON -DTEST_JULIA_INTERFACE:BOOL=ON -DOpenMP_ROOT=$CONDA_PREFIX + cmake .. -GNinja -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_EXECUTABLE=$(which python3) -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON -DTEST_JULIA_INTERFACE:BOOL=OFF -DOpenMP_ROOT=$CONDA_PREFIX - name: Configure [Conda/macOS14] if: contains(matrix.os, 'macos-14') @@ -135,40 +124,43 @@ jobs: echo $(whereis ccache) echo $(which ccache) cd build - cmake .. -GNinja -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCHECK_RUNTIME_MALLOC:BOOL=ON -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_EXECUTABLE=$(which python3) -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON -DTEST_JULIA_INTERFACE:BOOL=ON -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DOpenMP_ROOT=$CONDA_PREFIX + cmake .. -GNinja -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCHECK_RUNTIME_MALLOC:BOOL=ON -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_EXECUTABLE=$(which python3) -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON -DTEST_JULIA_INTERFACE:BOOL=OFF -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DOpenMP_ROOT=$CONDA_PREFIX - name: Configure [Conda/Windows-2019] if: contains(matrix.os, 'windows-2019') shell: bash -l {0} run: | echo $(where ccache) - ls C:\\Miniconda3\\envs\\proxsuite\\Library\\lib git submodule update --init mkdir build cd build - cmake .. -G"Visual Studio 16 2019" -T "ClangCl" -DCMAKE_GENERATOR_PLATFORM=x64 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON + export CXX=clang-cl + export CC=clang-cl + cmake .. -G"Ninja" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON - name: Configure [Conda/Windows-latest] if: contains(matrix.os, 'windows-latest') && contains(matrix.cxx_std, '20') shell: bash -l {0} run: | echo $(where ccache) - ls C:\\Miniconda3\\envs\\proxsuite\\Library\\lib git submodule update --init mkdir build cd build - cmake .. -G"Visual Studio 17 2022" -T "ClangCl" -DCMAKE_GENERATOR_PLATFORM=x64 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON + export CXX=clang-cl + export CC=clang-cl + cmake .. -G"Ninja" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON - name: Configure [Conda/Windows-latest] if: contains(matrix.os, 'windows-latest') && contains(matrix.cxx_std, '17') shell: bash -l {0} run: | echo $(where ccache) - ls C:\\Miniconda3\\envs\\proxsuite\\Library\\lib git submodule update --init mkdir build cd build - cmake .. -G"Visual Studio 17 2022" -T "v143" -DCMAKE_GENERATOR_PLATFORM=x64 -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON + export CXX=clang-cl + export CC=clang-cl + cmake .. -G"Ninja" -DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DCMAKE_INSTALL_PREFIX=${CONDA_PREFIX}/Library -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_CXX_STANDARD=${{ matrix.cxx_std }} -DBUILD_PYTHON_INTERFACE:BOOL=ON -DPYTHON_SITELIB=${CONDA_PREFIX}/Lib/site-packages -DPYTHON_EXECUTABLE=${CONDA_PREFIX}/python.exe -DOpenMP_ROOT=$CONDA_PREFIX -DBUILD_WITH_OPENMP_SUPPORT:BOOL=ON -DLINK_PYTHON_INTERFACE_TO_OPENMP:BOOL=ON -DBUILD_DOCUMENTATION:BOOL=ON -DINSTALL_DOCUMENTATION:BOOL=ON - name: Build [Conda] shell: bash -l {0} 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/gh-pages.yml b/.github/workflows/gh-pages.yml index cf9ae190e..e23c64418 100644 --- a/.github/workflows/gh-pages.yml +++ b/.github/workflows/gh-pages.yml @@ -8,13 +8,13 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + submodules: recursive - - uses: conda-incubator/setup-miniconda@v2 + - uses: conda-incubator/setup-miniconda@v3 with: - miniforge-variant: Mambaforge miniforge-version: latest - channels: conda-forge python-version: "3.10" activate-environment: doc @@ -25,16 +25,16 @@ jobs: conda config --remove channels defaults # Compilation related dependencies - mamba install cmake make pkg-config doxygen graphviz + conda install cmake make pkg-config doxygen graphviz # Main dependencies - mamba install eigen + conda install eigen - name: Print environment shell: bash -l {0} run: | conda info - mamba list + conda list env - name: Configure diff --git a/.github/workflows/release-linux.yml b/.github/workflows/release-linux.yml index a47c2526d..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 @@ -33,9 +35,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..d19755881 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 @@ -21,7 +23,7 @@ jobs: toolset: v143 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: submodules: recursive @@ -33,23 +35,11 @@ jobs: git submodule update - name: Setup conda - if: contains(matrix.os, 'macos-13') || contains(matrix.os, 'windows') - uses: conda-incubator/setup-miniconda@v2 - with: - miniforge-variant: Mambaforge - miniforge-version: latest - channels: conda-forge - python-version: ${{ matrix.python-version }} - activate-environment: proxsuite - - - name: Setup conda - if: matrix.os == 'macos-14' uses: conda-incubator/setup-miniconda@v3 with: - channels: conda-forge + miniforge-version: latest python-version: ${{ matrix.python-version }} activate-environment: proxsuite - installer-url: https://github.com/conda-forge/miniforge/releases/download/23.11.0-0/Mambaforge-23.11.0-0-MacOSX-arm64.sh - name: Install dependencies [Conda] if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') @@ -57,14 +47,13 @@ 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 + conda install doxygen graphviz eigen simde cmake compilers typing_extensions - name: Print environment [Conda] - if: contains(matrix.os, 'macos') || contains(matrix.os, 'windows') shell: bash -l {0} run: | conda info - mamba list + conda list env - name: Build wheel diff --git a/.gitmodules b/.gitmodules index ce4fc4593..2867bf33b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,11 +1,9 @@ -[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 [submodule "external/cereal"] path = external/cereal url = https://github.com/USCiLab/cereal.git -[submodule "cmake"] - url = https://github.com/jrl-umi3218/jrl-cmakemodules.git diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index f1e0ff0df..63974c525 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,17 +1,19 @@ ci: autoupdate_branch: 'devel' + autofix_prs: false + autoupdate_schedule: quarterly repos: - repo: https://github.com/pre-commit/mirrors-clang-format - rev: v18.1.6 + rev: v19.1.5 hooks: - id: clang-format args: ['--style={BasedOnStyle: Mozilla, SortIncludes: false}'] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.6.0 + rev: v5.0.0 hooks: - id: trailing-whitespace - repo: https://github.com/psf/black - rev: 24.4.2 + rev: 24.10.0 hooks: - id: black - repo: https://github.com/cheshirekow/cmake-format-precommit diff --git a/CHANGELOG.md b/CHANGELOG.md index 27925e586..9281c4299 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,37 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +* CMake: Fix link to system cereal in tests ([#353](https://github.com/Simple-Robotics/proxsuite/pull/352)) +* Fix windows build error related to template usage in veg ([#357](https://github.com/Simple-Robotics/proxsuite/pull/357)) + +### 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)) +* Add `solve_no_gil` for dense backend (multithreading via python) ([#363](https://github.com/Simple-Robotics/proxsuite/pull/363)) +* Add benchmarks for `solve_no_gil` vs `solve_in_parallel` (openmp) ([#363](https://github.com/Simple-Robotics/proxsuite/pull/363)) + +### Changed +* Change Python bindings to use nanobind instead of pybind11 ([#340](https://github.com/Simple-Robotics/proxsuite/pull/340)) +* Update setup-minicondav2 to v3 ([#363](https://github.com/Simple-Robotics/proxsuite/pull/363)) + + +## [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 ([#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)) + +### Changed + +* Refactor Python examples with a new "util.py" file ([#347](https://github.com/Simple-Robotics/proxsuite/pull/347)) + ## [0.6.6] - 2024-06-15 ### Fixed @@ -414,7 +445,8 @@ More to come ([#](a forthcoming release.)) The first release of ProxSuite. -[Unreleased]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.6...HEAD +[Unreleased]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.7...HEAD +[0.6.7]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.6...v0.6.7 [0.6.6]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.5...v0.6.6 [0.6.5]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.4...v0.6.5 [0.6.4]: https://github.com/Simple-Robotics/proxsuite/compare/v0.6.3...v0.6.4 diff --git a/CITATION.cff b/CITATION.cff index 2def662cd..fd84309e6 100644 --- a/CITATION.cff +++ b/CITATION.cff @@ -14,7 +14,7 @@ authors: - family-names: Carpentier given-names: Justin orcid: "https://orcid.org/0000-0001-6585-2894" -version: 0.6.6 -date-released: "2024-06-15" +version: 0.6.7 +date-released: "2024-08-27" license: BSD-2-Clause repository-code: "https://github.com/Simple-Robotics/proxsuite" diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d22c3ed7..7f305d3a7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,7 +4,7 @@ cmake_minimum_required(VERSION 3.10) -if(DEFINED PROJECT_NAME) +if(DEFINED PROJECT_NAME AND NOT PROJECT_WORKSPACE) set(PROXSUITE_AS_SUBPROJECT ON) endif() @@ -14,6 +14,41 @@ set(PROJECT_URL "http://github.com/Simple-Robotics/proxsuite") set(PROJECT_CUSTOM_HEADER_EXTENSION "hpp") set(PROJECT_USE_CMAKE_EXPORT TRUE) set(PROJECT_USE_KEYWORD_LINK_LIBRARIES TRUE) +# To enable jrl-cmakemodules compatibility with workspace we must define the two +# following lines +set(PROJECT_AUTO_RUN_FINALIZE FALSE) +set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}) + +# Check if the submodule cmake have been initialized +set(JRL_CMAKE_MODULES "${CMAKE_CURRENT_LIST_DIR}/cmake-module") +if(EXISTS "${JRL_CMAKE_MODULES}/base.cmake") + message(STATUS "JRL cmakemodules found in 'cmake/' git submodule") +else() + find_package(jrl-cmakemodules QUIET CONFIG) + if(jrl-cmakemodules_FOUND) + get_property( + JRL_CMAKE_MODULES + TARGET jrl-cmakemodules::jrl-cmakemodules + PROPERTY INTERFACE_INCLUDE_DIRECTORIES) + message(STATUS "JRL cmakemodules found on system at ${JRL_CMAKE_MODULES}") + elseif(${CMAKE_VERSION} VERSION_LESS "3.14.0") + message( + FATAL_ERROR + "\nCan't find jrl-cmakemodules. Please either:\n" + " - use git submodule: 'git submodule update --init'\n" + " - or install https://github.com/jrl-umi3218/jrl-cmakemodules\n" + " - or upgrade your CMake version to >= 3.14 to allow automatic fetching\n" + ) + else() + message(STATUS "JRL cmakemodules not found. Let's fetch it.") + include(FetchContent) + FetchContent_Declare( + "jrl-cmakemodules" + GIT_REPOSITORY "https://github.com/jrl-umi3218/jrl-cmakemodules.git") + FetchContent_MakeAvailable("jrl-cmakemodules") + FetchContent_GetProperties("jrl-cmakemodules" SOURCE_DIR JRL_CMAKE_MODULES) + endif() +endif() # Disable -Werror on Unix for now. set(CXX_DISABLE_WERROR True) @@ -24,14 +59,6 @@ if(POLICY CMP0068) cmake_policy(SET CMP0068 NEW) endif(POLICY CMP0068) -# Check if the submodule cmake have been initialized -if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/cmake-module/base.cmake") - message( - FATAL_ERROR - "\nPlease run the following command first:\ngit submodule update --init\n" - ) -endif() - # ---------------------------------------------------- # --- OPTIONS --------------------------------------- # Need to be set before including base.cmake @@ -42,19 +69,18 @@ option(INSTALL_DOCUMENTATION "Install the documentation" OFF) set(DOXYGEN_USE_MATHJAX YES) set(DOXYGEN_USE_TEMPLATE_CSS YES) -include(${CMAKE_CURRENT_LIST_DIR}/cmake-module/base.cmake) +include(${JRL_CMAKE_MODULES}/base.cmake) compute_project_args(PROJECT_ARGS LANGUAGES CXX) project(${PROJECT_NAME} ${PROJECT_ARGS}) -include(${CMAKE_CURRENT_LIST_DIR}/cmake-module/ide.cmake) -include(${CMAKE_CURRENT_LIST_DIR}/cmake-module/apple.cmake) +include(${JRL_CMAKE_MODULES}/ide.cmake) +include(${JRL_CMAKE_MODULES}/apple.cmake) if(NOT ${CMAKE_VERSION} VERSION_GREATER "3.26.0" OR WIN32) - set(CMAKE_MODULE_PATH - ${CMAKE_CURRENT_LIST_DIR}/cmake-module/find-external/OpenMP - ${CMAKE_MODULE_PATH}) + set(CMAKE_MODULE_PATH ${JRL_CMAKE_MODULES}/find-external/OpenMP + ${CMAKE_MODULE_PATH}) endif() -include(${CMAKE_CURRENT_LIST_DIR}/cmake-module/julia.cmake) +include(${JRL_CMAKE_MODULES}/julia.cmake) include(CMakeDependentOption) # If needed, set CMake policy for APPLE systems @@ -85,9 +111,8 @@ if(BUILD_WITH_OPENMP_SUPPORT) separate_arguments(OpenMP_CXX_FLAGS UNIX_COMMAND "${OpenMP_CXX_FLAGS}") endif(BUILD_WITH_OPENMP_SUPPORT) -set(CMAKE_MODULE_PATH - "${CMAKE_CURRENT_LIST_DIR}/cmake-module/find-external/Julia" - ${CMAKE_MODULE_PATH}) +set(CMAKE_MODULE_PATH "${JRL_CMAKE_MODULES}/find-external/Julia" + ${CMAKE_MODULE_PATH}) set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake-external" ${CMAKE_MODULE_PATH}) @@ -163,13 +188,16 @@ endif() if(BUILD_TESTING OR BUILD_PYTHON_INTERFACE) # Download cereal for pything bindings and unittests - set(cereal_dir ${PROJECT_SOURCE_DIR}/external/cereal) - set(cereal ${cereal_dir}/README.md) - find_package(Git REQUIRED) - if(NOT EXISTS ${cereal}) - execute_process( - COMMAND ${GIT_EXECUTABLE} submodule update --init ${cereal_dir} - WORKING_DIRECTORY ${cereal_dir} COMMAND_ERROR_IS_FATAL ANY) + find_package(cereal QUIET CONFIG) + if(NOT cereal_FOUND) + set(cereal_dir ${PROJECT_SOURCE_DIR}/external/cereal) + set(cereal ${cereal_dir}/README.md) + find_package(Git REQUIRED) + if(NOT EXISTS ${cereal}) + execute_process( + COMMAND ${GIT_EXECUTABLE} submodule update --init ${cereal_dir} + WORKING_DIRECTORY ${cereal_dir} COMMAND_ERROR_IS_FATAL ANY) + endif() endif() endif() @@ -217,3 +245,5 @@ if(BUILD_PYTHON_INTERFACE) FILES ${CMAKE_CURRENT_BINARY_DIR}/share/${PROJECT_NAME}/hook/python_path.dsv DESTINATION share/${PROJECT_NAME}/hook) endif(BUILD_PYTHON_INTERFACE) + +setup_project_finalize() diff --git a/benchmark/CMakeLists.txt b/benchmark/CMakeLists.txt index 0c4f00a43..6f6ea47ef 100644 --- a/benchmark/CMakeLists.txt +++ b/benchmark/CMakeLists.txt @@ -1,4 +1,4 @@ -add_custom_target(bench) +add_custom_target(${PROJECT_NAME}_bench) macro(proxsuite_benchmark bench_name) if(BUILD_BENCHMARK) @@ -12,7 +12,7 @@ macro(proxsuite_benchmark bench_name) else() target_link_libraries(${bench_name} PUBLIC proxsuite) endif() - add_dependencies(bench ${bench_name}) + add_dependencies(${PROJECT_NAME}_bench ${bench_name}) endmacro(proxsuite_benchmark) proxsuite_benchmark(timings-lp) diff --git a/benchmark/timings-parallel.py b/benchmark/timings-parallel.py index 018347612..0beef2a70 100644 --- a/benchmark/timings-parallel.py +++ b/benchmark/timings-parallel.py @@ -2,6 +2,17 @@ import numpy as np import scipy.sparse as spa from time import perf_counter_ns +from concurrent.futures import ThreadPoolExecutor + +""" +There are two interfaces to solve a QP problem with the dense backend. a) create a qp object by passing the problem data (matrices, vectors) to the qp.init method (this does memory allocation and the preconditioning) and then calling qp.solve or b) use the solve function directly taking the problem data as input (this does everything in one go). + +Currently, only the qp.solve method (a) is parallelized (using openmp). Therefore the memory alloc + preconditioning is done in serial when building a batch of qps that is then passed to the `solve_in_parallel` function. The solve function (b) is not parallelized but can easily be parallelized in Python using ThreadPoolExecutor. + +Here we do some timings to compare the two approaches. We generate a batch of QP problems and solve them in parallel using the `solve_in_parallel` function and compare the timings (need to add the timings for building the batch of qps + the parallel solving) with solving each problem in parallel using ThreadPoolExecutor for the solve function. +""" + +num_threads = proxsuite.proxqp.omp_get_max_threads() def generate_mixed_qp(n, n_eq, n_in, seed=1): @@ -23,45 +34,109 @@ def generate_mixed_qp(n, n_eq, n_in, seed=1): 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(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], l[n_in:], u[n_in:] -n = 500 -n_eq = 200 -n_in = 200 +problem_specs = [ + # (n, n_eq, n_in), + (50, 20, 20), + (100, 40, 40), + (200, 80, 80), + (500, 200, 200), + (1000, 200, 200), +] num_qps = 128 -# qps = [] -timings = {} -qps = proxsuite.proxqp.dense.VectorQP() - -tic = perf_counter_ns() -for j in range(num_qps): - qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in) - H, g, A, b, C, u, l = generate_mixed_qp(n, n_eq, n_in, seed=j) - qp.init(H, g, A, b, C, l, u) - qp.settings.eps_abs = 1e-9 - qp.settings.verbose = False - qp.settings.initial_guess = proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS - qps.append(qp) -timings["problem_data"] = (perf_counter_ns() - tic) * 1e-6 - -tic = perf_counter_ns() -for qp in qps: - qp.solve() -timings["solve_serial"] = (perf_counter_ns() - tic) * 1e-6 +for n, n_eq, n_in in problem_specs: -num_threads = proxsuite.proxqp.omp_get_max_threads() -for j in range(1, num_threads): + print(f"\nProblem specs: {n=} {n_eq=} {n_in=}. Generating {num_qps} such problems.") + problems = [generate_mixed_qp(n, n_eq, n_in, seed=j) for j in range(num_qps)] + print( + f"Generated problems. Solving {num_qps} problems with proxsuite.proxqp.omp_get_max_threads()={num_threads} threads." + ) + + timings = {} + + # create a vector of QP objects. This is not efficient because memory is allocated when creating the qp object + when it is appended to the vector which creates a copy of the object. + qps_vector = proxsuite.proxqp.dense.VectorQP() tic = perf_counter_ns() - proxsuite.proxqp.dense.solve_in_parallel(j, qps) - timings[f"solve_parallel_{j}_threads"] = (perf_counter_ns() - tic) * 1e-6 + print("\nSetting up vector of qps") + for H, g, A, b, C, l, u in problems: + qp = proxsuite.proxqp.dense.QP(n, n_eq, n_in) + qp.init(H, g, A, b, C, l, u) + qp.settings.eps_abs = 1e-9 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS + qps_vector.append(qp) + timings["setup_vector_of_qps"] = (perf_counter_ns() - tic) * 1e-6 + # use BatchQP, which can initialize the qp objects in place and is more efficient + qps_batch = proxsuite.proxqp.dense.BatchQP() + tic = perf_counter_ns() + print("Setting up batch of qps") + for H, g, A, b, C, l, u in problems: + qp = qps_batch.init_qp_in_place(n, n_eq, n_in) + qp.init(H, g, A, b, C, l, u) + qp.settings.eps_abs = 1e-9 + qp.settings.verbose = False + qp.settings.initial_guess = proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS + timings["setup_batch_of_qps"] = (perf_counter_ns() - tic) * 1e-6 -tic = perf_counter_ns() -proxsuite.proxqp.dense.solve_in_parallel(qps=qps) -timings[f"solve_parallel_heuristics_threads"] = (perf_counter_ns() - tic) * 1e-6 + print("Solving batch of qps using solve_in_parallel with default thread config") + tic = perf_counter_ns() + proxsuite.proxqp.dense.solve_in_parallel(qps=qps_batch) + timings[f"solve_in_parallel_heuristics_threads"] = (perf_counter_ns() - tic) * 1e-6 + + print("Solving vector of qps serially") + tic = perf_counter_ns() + for qp in qps_vector: + qp.solve() + timings["qp_solve_serial"] = (perf_counter_ns() - tic) * 1e-6 + + print("Solving batch of qps using solve_in_parallel with various thread configs") + for j in range(1, num_threads, 2): + tic = perf_counter_ns() + proxsuite.proxqp.dense.solve_in_parallel(qps=qps_batch, num_threads=j) + timings[f"solve_in_parallel_{j}_threads"] = (perf_counter_ns() - tic) * 1e-6 + + def solve_problem_with_dense_backend( + problem, + ): + H, g, A, b, C, l, u = problem + return proxsuite.proxqp.dense.solve_no_gil( + H, + g, + A, + b, + C, + l, + u, + initial_guess=proxsuite.proxqp.InitialGuess.NO_INITIAL_GUESS, + eps_abs=1e-9, + ) + + # add final timings for the solve_in_parallel function considering setup time for batch of qps + for k, v in list(timings.items()): + if "solve_in_parallel" in k: + k_init = k + "_and_setup_batch_of_qps" + timings[k_init] = timings["setup_batch_of_qps"] + v + + print("Solving each problem serially with solve function.") + # Note: here we just pass the problem data to the solve function. This does not require running the init method separately. + tic = perf_counter_ns() + for problem in problems: + solve_problem_with_dense_backend(problem) + timings["solve_fun_serial"] = (perf_counter_ns() - tic) * 1e-6 + + print( + "Solving each problem in parallel (with a ThreadPoolExecutor) with solve function." + ) + tic = perf_counter_ns() + with ThreadPoolExecutor(max_workers=num_threads) as executor: + results = list(executor.map(solve_problem_with_dense_backend, problems)) + timings["solve_fun_parallel"] = (perf_counter_ns() - tic) * 1e-6 -for k, v in timings.items(): - print(f"{k}: {v}ms") + print("\nTimings:") + for k, v in timings.items(): + print(f"{k}: {v:.3f}ms") diff --git a/bindings/python/CMakeLists.txt b/bindings/python/CMakeLists.txt index 4e3d927fd..29487b9a9 100644 --- a/bindings/python/CMakeLists.txt +++ b/bindings/python/CMakeLists.txt @@ -1,11 +1,18 @@ if(UNIX) - set(PYTHON_COMPONENTS Development.Module) + set(PYTHON_COMPONENTS Interpreter Development.Module) endif() -include(../../cmake-module/python.cmake) -include(../../cmake-module/python-helpers.cmake) +include(${JRL_CMAKE_MODULES}/python.cmake) +include(${JRL_CMAKE_MODULES}/python-helpers.cmake) -findpython(REQUIRED Development.Module) +option(GENERATE_PYTHON_STUBS "Generate Python stubs" OFF) + +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}) @@ -14,11 +21,24 @@ else() ${CMAKE_INSTALL_PREFIX}/${PYTHON_SITELIB}/${PROJECT_NAME}) endif() -file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/external/pybind11) -add_subdirectory(external/pybind11 - ${CMAKE_CURRENT_BINARY_DIR}/external/pybind11) +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(python) +add_custom_target(${PROJECT_NAME}_python) # Collect files file(GLOB_RECURSE PYWRAP_HEADERS ${CMAKE_CURRENT_LIST_DIR}/src/*.hpp) @@ -26,27 +46,25 @@ 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) - add_dependencies(python instructionset) - target_link_libraries(instructionset PRIVATE proxsuite pybind11::module) + nanobind_add_module(instructionset helpers/instruction-set.cpp) + add_dependencies(${PROJECT_NAME}_python instructionset) + target_link_libraries(instructionset PRIVATE proxsuite) set_target_properties( instructionset PROPERTIES OUTPUT_NAME instructionset - PREFIX "" - SUFFIX ${PYTHON_EXT_SUFFIX} LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_RELEASE - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_DEBUG - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" # On Windows, shared library are treat as binary RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" RUNTIME_OUTPUT_DIRECTORY_RELEASE - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" RUNTIME_OUTPUT_DIRECTORY_DEBUG - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}") + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}") if(UNIX AND NOT APPLE) set_target_properties(instructionset PROPERTIES INSTALL_RPATH "\$ORIGIN/../../..") @@ -55,6 +73,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) @@ -69,12 +101,12 @@ 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}) - add_dependencies(python ${target_name}) + 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}) @@ -91,26 +123,29 @@ function(CREATE_PYTHON_TARGET target_name COMPILE_OPTIONS dependencies) list_filter("${PYWRAP_HEADERS}" "expose-parallel" PYWRAP_HEADERS) endif(BUILD_WITH_OPENMP_SUPPORT) - target_include_directories( - ${target_name} SYSTEM PRIVATE ${PROJECT_SOURCE_DIR}/external/cereal/include) + if(cereal_FOUND) + target_link_libraries(${target_name} PRIVATE cereal::cereal) + else() + target_include_directories( + ${target_name} SYSTEM + PRIVATE ${PROJECT_SOURCE_DIR}/external/cereal/include) + endif() set_target_properties( ${target_name} PROPERTIES OUTPUT_NAME ${target_name} - PREFIX "" - SUFFIX ${PYTHON_EXT_SUFFIX} LIBRARY_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_RELEASE - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" LIBRARY_OUTPUT_DIRECTORY_DEBUG - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" # On Windows, shared library are treat as binary RUNTIME_OUTPUT_DIRECTORY - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" RUNTIME_OUTPUT_DIRECTORY_RELEASE - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}" + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}" RUNTIME_OUTPUT_DIRECTORY_DEBUG - "${CMAKE_BINARY_DIR}/bindings/python/${PROJECT_NAME}") + "${PROJECT_BINARY_DIR}/bindings/python/${PROJECT_NAME}") if(UNIX AND NOT APPLE) set_target_properties(${target_name} PROPERTIES INSTALL_RPATH @@ -118,6 +153,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) @@ -190,7 +239,7 @@ else() endif() python_build_get_target(compile_pyc) -add_dependencies(python ${compile_pyc}) +add_dependencies(${PROJECT_NAME}_python ${compile_pyc}) set(PYTHON_FILES torch/__init__.py torch/qplayer.py torch/utils.py) diff --git a/bindings/python/external/nanobind b/bindings/python/external/nanobind new file mode 160000 index 000000000..9641bb715 --- /dev/null +++ b/bindings/python/external/nanobind @@ -0,0 +1 @@ +Subproject commit 9641bb7151f04120013b812789b3ebdfa7e7324f 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 diff --git a/bindings/python/helpers/instruction-set.cpp b/bindings/python/helpers/instruction-set.cpp index c99c43672..64035c447 100644 --- a/bindings/python/helpers/instruction-set.cpp +++ b/bindings/python/helpers/instruction-set.cpp @@ -1,8 +1,9 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // -#include +#include +#include #include @@ -10,7 +11,7 @@ namespace proxsuite { namespace helpers { namespace python { -PYBIND11_MODULE(instructionset, m) +NB_MODULE(instructionset, m) { m.doc() = R"pbdoc( CPU info library diff --git a/bindings/python/proxsuite/torch/qplayer.py b/bindings/python/proxsuite/torch/qplayer.py index 1c9955946..f3cee2b1b 100644 --- a/bindings/python/proxsuite/torch/qplayer.py +++ b/bindings/python/proxsuite/torch/qplayer.py @@ -256,6 +256,7 @@ def backward(ctx, dl_dzhat, dl_dlams, dl_dnus): class QPFunctionFn_infeas(Function): @staticmethod def forward(ctx, Q_, p_, A_, b_, G_, l_, u_): + n_in, nz = G_.size() # true double-sided inequality size nBatch = extract_nBatch(Q_, p_, A_, b_, G_, l_, u_) Q, _ = expandParam(Q_, nBatch, 3) @@ -276,6 +277,9 @@ def forward(ctx, Q_, p_, A_, b_, G_, l_, u_): zhats = torch.empty((nBatch, ctx.nz), dtype=Q.dtype) nus = torch.empty((nBatch, ctx.nineq), dtype=Q.dtype) + nus_sol = torch.empty( + (nBatch, n_in), dtype=Q.dtype + ) # double-sided inequality multiplier lams = ( torch.empty(nBatch, ctx.neq, dtype=Q.dtype) if ctx.neq > 0 @@ -287,7 +291,9 @@ def forward(ctx, Q_, p_, A_, b_, G_, l_, u_): else torch.empty() ) slacks = torch.empty((nBatch, ctx.nineq), dtype=Q.dtype) - s_i = torch.empty((nBatch, ctx.nineq), dtype=Q.dtype) + s_i = torch.empty( + (nBatch, n_in), dtype=Q.dtype + ) # this one is of size the one of the original n_in vector_of_qps = proxsuite.proxqp.dense.BatchQP() @@ -339,20 +345,29 @@ def forward(ctx, Q_, p_, A_, b_, G_, l_, u_): vector_of_qps.get(i).solve() for i in range(nBatch): - si = -h[i] + G[i] @ vector_of_qps.get(i).results.x zhats[i] = torch.tensor(vector_of_qps.get(i).results.x) - nus[i] = torch.tensor(vector_of_qps.get(i).results.z) - slacks[i] = si.clone().detach() + if nineq > 0: + # we re-convert the solution to a double sided inequality QP + slack = -h[i] + G[i] @ vector_of_qps.get(i).results.x + nus_sol[i] = torch.Tensor( + -vector_of_qps.get(i).results.z[:n_in] + + vector_of_qps.get(i).results.z[n_in:] + ) # de-projecting this one may provoke loss of information when using inexact solution + nus[i] = torch.tensor(vector_of_qps.get(i).results.z) + slacks[i] = slack.clone().detach() + s_i[i] = torch.tensor( + -vector_of_qps.get(i).results.si[:n_in] + + vector_of_qps.get(i).results.si[n_in:] + ) if neq > 0: lams[i] = torch.tensor(vector_of_qps.get(i).results.y) s_e[i] = torch.tensor(vector_of_qps.get(i).results.se) - s_i[i] = torch.tensor(vector_of_qps.get(i).results.si) ctx.lams = lams ctx.nus = nus ctx.slacks = slacks ctx.save_for_backward(zhats, s_e, Q_, p_, G_, l_, u_, A_, b_) - return zhats, lams, nus, s_e, s_i + return zhats, lams, nus_sol, s_e, s_i @staticmethod def backward(ctx, dl_dzhat, dl_dlams, dl_dnus, dl_ds_e, dl_ds_i): @@ -371,6 +386,8 @@ def backward(ctx, dl_dzhat, dl_dlams, dl_dnus, dl_ds_e, dl_ds_i): G = torch.cat((-G, G), axis=1) neq, nineq = ctx.neq, ctx.nineq + # true size + n_in_sol = int(nineq / 2) dx = torch.zeros((nBatch, Q.shape[1])) dnu = None b_5 = None @@ -457,15 +474,34 @@ def backward(ctx, dl_dzhat, dl_dlams, dl_dnus, dl_ds_e, dl_ds_i): rhs = np.zeros(kkt.shape[0]) rhs[:dim] = -dl_dzhat[i] if dl_dlams != None: - rhs[dim : dim + n_eq] = -dl_dlams[i] + if n_eq != 0: + rhs[dim : dim + n_eq] = -dl_dlams[i] + active_set = None + if n_in != 0: + active_set = -z_i[:n_in_sol] + z_i[n_in_sol:] >= 0 if dl_dnus != None: - rhs[dim + n_eq : dim + n_eq + n_in] = -dl_dnus[i] + if n_in != 0: + # we must convert dl_dnus to a uni sided version + # to do so we reconstitute the active set + rhs[dim + n_eq : dim + n_eq + n_in_sol][~active_set] = dl_dnus[ + i + ][~active_set] + rhs[dim + n_eq + n_in_sol : dim + n_eq + n_in][active_set] = ( + -dl_dnus[i][active_set] + ) if dl_ds_e != None: if dl_ds_e.shape[0] != 0: rhs[dim + n_eq + n_in : dim + 2 * n_eq + n_in] = -dl_ds_e[i] if dl_ds_i != None: if dl_ds_i.shape[0] != 0: - rhs[dim + 2 * n_eq + n_in :] = -dl_ds_i[i] + # we must convert dl_dnus to a uni sided version + # to do so we reconstitute the active set + rhs[dim + 2 * n_eq + n_in : dim + 2 * n_eq + n_in + n_in_sol][ + ~active_set + ] = dl_ds_i[i][~active_set] + rhs[dim + 2 * n_eq + n_in + n_in_sol :][active_set] = -dl_ds_i[ + i + ][active_set] l = np.zeros(0) u = np.zeros(0) @@ -562,7 +598,15 @@ def backward(ctx, dl_dzhat, dl_dlams, dl_dnus, dl_ds_e, dl_ds_i): if p_e: dps = dps.mean(0) - grads = (dQs, dps, dAs, dbs, dGs[nineq:, :], -dhs[:nineq], dhs[nineq:]) + grads = ( + dQs, + dps, + dAs, + dbs, + dGs[n_in_sol:, :], + -dhs[:n_in_sol], + dhs[n_in_sol:], + ) return grads 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."); } 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-helpers.hpp b/bindings/python/src/expose-helpers.hpp index 6dc8768e5..872a60fdd 100644 --- a/bindings/python/src/expose-helpers.hpp +++ b/bindings/python/src/expose-helpers.hpp @@ -1,10 +1,10 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 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..92ea5c6ce 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 @@ -12,85 +12,69 @@ #include #include #include -#include "helpers.hpp" + namespace proxsuite { namespace proxqp { 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."), + &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_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", - // &proxsuite::proxqp::dense::BackwardData::dL_db) - // .def_readonly("dL_dC", - // &proxsuite::proxqp::dense::BackwardData::dL_dC) - // .def_readonly("dL_du", &proxsuite::proxqp::dense::BackwardData::dL_du) - // .def_readonly("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_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); - ::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, 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); + }); } } // namespace python } // namespace dense @@ -99,24 +83,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-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..288c1a7d2 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"), + 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") = 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>, @@ -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") = 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") = 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.") @@ -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") = 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") = 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>, @@ -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") = 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") = 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 " "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, const std::string& s) { + new (&qp) dense::QP(1, 1, 1); + proxsuite::serialization::loadFromString(qp, s); + }); ; } } // 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") = 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") + .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") = 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") = 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.", - 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") = 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") = 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.") diff --git a/bindings/python/src/expose-qpvector.hpp b/bindings/python/src/expose-qpvector.hpp index 06abf8b35..0530ac41a 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."); } diff --git a/bindings/python/src/expose-results.hpp b/bindings/python/src/expose-results.hpp index 941cd32b5..fa1e63160 100644 --- a/bindings/python/src/expose-results.hpp +++ b/bindings/python/src/expose-results.hpp @@ -1,27 +1,26 @@ // -// Copyright (c) 2022-2023 INRIA +// Copyright (c) 2022-2024 INRIA // #include -#include -#include -#include +#include +#include +#include +#include +#include "optional-eigen-fix.hpp" +#include #include #include -#include "helpers.hpp" -#include "optional.hpp" - namespace proxsuite { namespace proxqp { 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,71 +30,86 @@ 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( - 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_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("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) + .def("__getstate__", + [](const Results& results) { + return proxsuite::serialization::saveToString(results); + }) + .def("__setstate__", [](Results& results, const std::string& s) { + new (&results) Results{}; + proxsuite::serialization::loadFromString(results, s); + }); ; } } // namespace python diff --git a/bindings/python/src/expose-settings.hpp b/bindings/python/src/expose-settings.hpp index c9188ca1d..c99d8767d 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, const std::string& s) { + new (&settings) Settings{}; + proxsuite::serialization::loadFromString(settings, s); + }); ; } } // namespace python diff --git a/bindings/python/src/expose-solve.hpp b/bindings/python/src/expose-solve.hpp index 8a0a2f091..89004cc8b 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 "optional-eigen-fix.hpp" 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>, @@ -45,73 +46,41 @@ solveDenseQp(pybind11::module_ m) optional, bool, optional>(&dense::solve), - "Function for solving a QP problem using PROXQP sparse backend directly " + "Function for solving a QP problem using PROXQP dense backend directly " "without defining a QP object. It is possible to set up some of the solver " "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."), - 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"), + 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") = nanobind::none(), + nanobind::arg("initial_guess") = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + 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.); m.def( "solve", - pybind11::overload_cast>, + nanobind::overload_cast>, optional>, optional>, optional>, @@ -138,71 +107,166 @@ solveDenseQp(pybind11::module_ m) optional, bool, optional>(&dense::solve), - "Function for solving a QP problem using PROXQP sparse backend directly " + "Function for solving a QP problem using PROXQP dense backend directly " "without defining a QP object. It is possible to set up some of the solver " "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."), - pybind11::arg_v("max_iter", nullopt, "maximum number of iteration."), - pybind11::arg_v( - "initial_guess", + 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") = nanobind::none(), + 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") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0.); + + m.def("solve_no_gil", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + proxsuite::proxqp::InitialGuessStatus, + bool, + optional, + optional, + bool, + optional>(&dense::solve), + "Function for solving a QP problem using PROXQP dense backend directly " + "without defining a QP object and while releasing the Global " + "Interpreter Lock (GIL). " + "It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + 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") = nanobind::none(), + nanobind::arg("initial_guess") = + InitialGuessStatus::EQUALITY_CONSTRAINED_INITIAL_GUESS, + nanobind::arg("check_duality_gap") = false, + 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., + nanobind::call_guard()); + + m.def( + "solve_no_gil", + nanobind::overload_cast>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional>, + optional, + optional, + optional, + optional, + optional, + optional, + bool, + bool, + optional, + proxsuite::proxqp::InitialGuessStatus, + bool, + optional, + optional, + bool, + optional>(&dense::solve), + "Function for solving a QP problem using PROXQP dense backend directly " + "without defining a QP object and while releasing the Global Interpreter " + "Lock (GIL). " + "It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + 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") = 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") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::call_guard()); } } // namespace python @@ -213,7 +277,7 @@ namespace python { template void -solveSparseQp(pybind11::module_ m) +solveSparseQp(nanobind::module_ m) { m.def( "solve", @@ -223,64 +287,72 @@ 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."), - 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") = 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") = 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") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0.); + + m.def( + "solve_no_gil", + &sparse::solve, + "Function for solving a QP problem using PROXQP sparse backend directly " + "without defining a QP object and while releasing the Global Interpreter " + "Lock (GIL). " + "It is possible to set up some of the solver " + "parameters (warm start, initial guess option, proximal step sizes, " + "absolute and relative accuracies, maximum number of iterations, " + "preconditioner execution).", + 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") = 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") = nanobind::none(), + nanobind::arg("eps_duality_gap_rel") = nanobind::none(), + nanobind::arg("primal_infeasibility_solving") = false, + nanobind::arg("default_H_eigenvalue_estimate") = 0., + nanobind::call_guard()); } } // namespace python 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()); + }); ; } diff --git a/bindings/python/src/helpers.hpp b/bindings/python/src/helpers.hpp deleted file mode 100644 index cd0fe716a..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_property( \ - #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 diff --git a/bindings/python/src/optional-eigen-fix.hpp b/bindings/python/src/optional-eigen-fix.hpp new file mode 100644 index 000000000..cedd91103 --- /dev/null +++ b/bindings/python/src/optional-eigen-fix.hpp @@ -0,0 +1,60 @@ +// +// 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>> +{ + using Ref = Eigen::Ref; + using Optional = std::optional; + 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()) + 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; + } + + 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) +NAMESPACE_END(NB_NAMESPACE) 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 diff --git a/cmake-module b/cmake-module index 2bea127e8..b3c2af1b6 160000 --- a/cmake-module +++ b/cmake-module @@ -1 +1 @@ -Subproject commit 2bea127e8113a32f216ae7a7201e36a6e7c56cc2 +Subproject commit b3c2af1b68686dc9d5f459fb617647e37a15a76d diff --git a/doc/2-ProxQP_api.md b/doc/2-ProxQP_api.md index 592274d2e..fedebac47 100644 --- a/doc/2-ProxQP_api.md +++ b/doc/2-ProxQP_api.md @@ -388,7 +388,7 @@ In this table, you have the three columns from left to right: the name of the se | default_rho | 1.E-6 | Default rho parameter of result class (i.e., for each initial guess, except WARM_START_WITH_PREVIOUS_RESULT, after a new solve or update, the solver initializes rho to this value). | default_mu_eq | 1.E-3 | Default mu_eq parameter of result class (i.e., for each initial guess, except WARM_START_WITH_PREVIOUS_RESULT, after a new solve or update, the solver initializes mu_eq to this value). | default_mu_in | 1.E-1 | Default mu_in parameter of result class (i.e., for each initial guess, except WARM_START_WITH_PREVIOUS_RESULT, after a new solve or update, the solver initializes mu_in to this value). -| compute_timings | False | If set to true, timings will be computed by the solver (setup time, solving time, and run time = setup time + solving time). +| compute_timings | False | If set to true, timings in microseconds will be computed by the solver (setup time, solving time, and run time = setup time + solving time). | max_iter | 10.000 | Maximal number of authorized outer iterations. | max_iter_in | 1500 | Maximal number of authorized inner iterations. | initial_guess | EQUALITY_CONSTRAINED_INITIAL_GUESS | Sets the initial guess option for initilizing x, y and z. diff --git a/doc/3-ProxQP_solve.md b/doc/3-ProxQP_solve.md index 59371c87e..79223f605 100644 --- a/doc/3-ProxQP_solve.md +++ b/doc/3-ProxQP_solve.md @@ -113,7 +113,7 @@ Different options are available for the solve function. In the table below you h | rho | 1.E-6 | Proximal step size wrt primal variable. | VERBOSE | False | If set to true, the solver prints information at each loop. | compute_preconditioner | True | If set to true, the preconditioner will be derived. -| compute_timings | False | If set to true, timings will be computed by the solver (setup time, solving time, and run time = setup time + solving time). +| compute_timings | False | If set to true, timings in microseconds will be computed by the solver (setup time, solving time, and run time = setup time + solving time). | max_iter | 10.000 | Maximal number of authorized outer iterations. | initial_guess | EQUALITY_CONSTRAINED_INITIAL_GUESS | Sets the initial guess option for initilizing x, y and z. diff --git a/doc/5-installation.md b/doc/5-installation.md index 376f679c7..67dc810af 100644 --- a/doc/5-installation.md +++ b/doc/5-installation.md @@ -27,6 +27,8 @@ make make install ``` +Note: if you are building Proxsuite within a conda environment, consider passing `-DCMAKE_INSTALL_PREFIX=$CONDA_PREFIX`. + 3. Build the Python interface You just need to ensure that Python3 is indeed present on your system and activate the cmake option `BUILD_PYTHON_INTERFACE=ON` by replacing: diff --git a/doc/Doxyfile.extra.in b/doc/Doxyfile.extra.in index 13eef9ae3..db68df82d 100644 --- a/doc/Doxyfile.extra.in +++ b/doc/Doxyfile.extra.in @@ -1,5 +1,5 @@ -INPUT = @CMAKE_SOURCE_DIR@/doc \ - @CMAKE_SOURCE_DIR@/include \ +INPUT = @PROJECT_SOURCE_DIR@/doc \ + @PROJECT_SOURCE_DIR@/include \ RECURSIVE = YES @@ -54,7 +54,7 @@ SOURCE_BROWSER = YES ALPHABETICAL_INDEX = YES -USE_MDFILE_AS_MAINPAGE = @CMAKE_SOURCE_DIR@/doc/1-Overview.md +USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/doc/1-Overview.md BUILTIN_STL_SUPPORT = YES HAVE_DOT = YES diff --git a/doc/rankupdate/rankupdate.aux b/doc/rankupdate/rankupdate.aux deleted file mode 100644 index 64a75506e..000000000 --- a/doc/rankupdate/rankupdate.aux +++ /dev/null @@ -1,9 +0,0 @@ -\relax -\newlabel{eq:rank_update_definition}{{1}{1}} -\@writefile{toc}{\contentsline {section}{\numberline {1}Rank-one update:}{1}{}\protected@file@percent } -\newlabel{sec:Rank-one_update}{{1}{1}} -\newlabel{eq:unfold}{{3}{1}} -\newlabel{eq:lincomb}{{6}{2}} -\@writefile{toc}{\contentsline {section}{\numberline {2}Rank-$m$ update}{2}{}\protected@file@percent } -\newlabel{sec:Rank-$m$_update}{{2}{2}} -\gdef \@abspage@last{3} diff --git a/doc/rankupdate/rankupdate.fdb_latexmk b/doc/rankupdate/rankupdate.fdb_latexmk deleted file mode 100644 index c653edf04..000000000 --- a/doc/rankupdate/rankupdate.fdb_latexmk +++ /dev/null @@ -1,68 +0,0 @@ -# Fdb version 3 -["pdflatex"] 1641376399 "rankupdate.tex" "rankupdate.pdf" "rankupdate" 1641376399 - "/etc/texmf/web2c/texmf.cnf" 1640843101 475 c0e671620eb5563b2130f56340a5fde8 "" - "/usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map" 1577235249 3524 cb3e574dea2d1052e39280babc910dc8 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/jknappen/ec/tcrm1000.tfm" 1136768653 1536 e07581a4bb3136ece9eeb4c3ffab8233 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm" 1246382020 1004 54797486969f23fa377b128694d548df "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm" 1246382020 988 bdf658c3bfc2d96d3c8b02cfc1c94c20 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm" 1246382020 916 f87d7c45f9c908e672703b83b72241a3 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam5.tfm" 1246382020 924 9904cf1d39e9767e7a3622f2a125a565 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm" 1246382020 928 2dc8d444221b7a635bb58038579b861a "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm" 1246382020 908 2921f8a10601f252058503cc6570e581 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm5.tfm" 1246382020 940 75ac932a52f80982a9f8ea75d03a34cf "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm" 1246382020 940 228d6584342e91276bf566bcf9716b83 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm" 1136768653 1324 c910af8c371558dc20f2d7822f66fe64 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmex10.tfm" 1136768653 992 662f679a0b3d2d53c1b94050fdaa3f50 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm" 1136768653 1524 4414a8315f39513458b80dfc63bff03a "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm" 1136768653 1512 f21f83efb36853c0b70002322c1ab3ad "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm" 1136768653 1520 eccf95517727cb11801f4f1aee3a21b4 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr12.tfm" 1136768653 1288 655e228510b4c2a1abe905c368440826 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr17.tfm" 1136768653 1292 296a67155bdbfc32aa9c636f21e91433 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm" 1136768653 1300 b62933e007d01cfd073f79b963c01526 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr8.tfm" 1136768653 1292 21c1c5bfeaebccffdb478fd231a0997d "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm" 1136768653 1124 6c73e740cf17375f03eec0ee63599741 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm" 1136768653 1116 933a60c408fc0a863a92debe84b2d294 "" - "/usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm" 1136768653 1120 8b7d695260f3cff42e636090a8002094 "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb" 1248133631 32080 340ef9bf63678554ee606688e7b5339d "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb" 1248133631 30251 6afa5cb1d0204815a708a080681d4674 "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb" 1248133631 36299 5f9df58c2139e7edcf37c8fca4bd384d "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi12.pfb" 1248133631 36741 fa121aac0049305630cf160b86157ee4 "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb" 1248133631 36281 c355509802a035cadc5f15869451dcee "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb" 1248133631 35752 024fb6c41858982481f6968b5fc26508 "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb" 1248133631 32362 179c33bbf43f19adbb3825bb4e36e57a "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr5.pfb" 1248133631 31809 8670ca339bf94e56da1fc21c80635e2a "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb" 1248133631 32762 224316ccc9ad3ca0423a14971cfa7fc1 "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb" 1248133631 32569 5e5ddc8df908dea60932f3c484a54c0d "" - "/usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb" 1248133631 32716 08e384dc442464e7285e891af9f45947 "" - "/usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty" 1591045760 12594 0d51ac3a545aaaa555021326ff22a6cc "" - "/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty" 1359763108 5949 3f3fd50a8cc94c3d4cbf4fc66cd3df1c "" - "/usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty" 1359763108 13829 94730e64147574077f8ecfea9bb69af4 "" - "/usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd" 1359763108 961 6518c6525a34feb5e8250ffa91731cff "" - "/usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd" 1359763108 961 d02606146ba5601b5645f987c92e6193 "" - "/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty" 1523134290 2211 ca7ce284ab93c8eecdc6029dc5ccbd73 "" - "/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty" 1523134290 4161 7f6eb9092061a11f87d08ed13515b48d "" - "/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty" 1601675358 87353 2c21ff5f2e32e1bf714e600924d810db "" - "/usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty" 1523134290 4116 32e6abd27229755a83a8b7f18e583890 "" - "/usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty" 1523134290 2432 8ff93b1137020e8f21930562a874ae66 "" - "/usr/share/texlive/texmf-dist/tex/latex/base/article.cls" 1601675358 20145 aad8c3dd3bc36e260347b84002182bc2 "" - "/usr/share/texlive/texmf-dist/tex/latex/base/size10.clo" 1601675358 8449 a72d5d4e612221b46000c3d71724e0ef "" - "/usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty" 1580683321 2590 e3b24ff953e5b58d924f163d25380312 "" - "/usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def" 1611959857 27097 58278863d97b10ab86e334b8da33df7a "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/microtype-pdftex.def" 1607465040 49121 4911f21dad8c1eb79a63aebba9643ece "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg" 1607465040 24929 baec9b115829e21e1ba4dfcaf6260d24 "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty" 1607465040 71807 0724aff58b422fcce3421aebcd5fc631 "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/mt-cmr.cfg" 1607465040 22870 85ef48580eb35bcb3beca9130657ae3b "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msa.cfg" 1607465040 5893 a60144fa9c61ba60cd8c1fe66baa02b3 "" - "/usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msb.cfg" 1607465040 5558 997b28f7d84cccc5fd6d2e3162e09e1e "" - "/usr/share/texlive/texmf-dist/web2c/texmf.cnf" 1613593815 38841 799d1dd9682a55ce442e10c99777ecc1 "" - "/usr/share/texmf/fonts/enc/dvips/cm-super/cm-super-ts1.enc" 1565080000 2900 1537cc8184ad1792082cd229ecc269f4 "" - "/usr/share/texmf/fonts/type1/public/cm-super/sfrm1000.pfb" 1565080000 138258 6525c253f16cededa14c7fd0da7f67b2 "" - "/usr/share/texmf/web2c/texmf.cnf" 1613593815 38841 799d1dd9682a55ce442e10c99777ecc1 "" - "/var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map" 1640843127 5160710 ecf427ae8fa19139d8691f526e47bb9b "" - "/var/lib/texmf/web2c/pdftex/pdflatex.fmt" 1640843148 2571203 eb8afda0270e9970b44064aadd8ed7f2 "" - "rankupdate.aux" 1641376399 421 e4ea47dcf05024e543bb4c3c57eb39e3 "pdflatex" - "rankupdate.tex" 1641376398 4930 d4fb108de9084263a9e28c65147a235d "" - (generated) - "rankupdate.aux" - "rankupdate.log" - "rankupdate.pdf" diff --git a/doc/rankupdate/rankupdate.fls b/doc/rankupdate/rankupdate.fls deleted file mode 100644 index 51eee1ce8..000000000 --- a/doc/rankupdate/rankupdate.fls +++ /dev/null @@ -1,239 +0,0 @@ -PWD /home/sarah/projects/inria_ldlt/rankupdate -INPUT /etc/texmf/web2c/texmf.cnf -INPUT /usr/share/texmf/web2c/texmf.cnf -INPUT /usr/share/texlive/texmf-dist/web2c/texmf.cnf -INPUT /var/lib/texmf/web2c/pdftex/pdflatex.fmt -INPUT rankupdate.tex -OUTPUT rankupdate.log -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/article.cls -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo -INPUT /usr/share/texlive/texmf-dist/tex/latex/base/size10.clo -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsmath.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amstext.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsbsy.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsopn.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsmath/amsgen.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amssymb.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/amsfonts.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/amscls/amsthm.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/graphics/keyval.sty -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/microtype.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT /usr/share/texlive/texmf-dist/tex/latex/l3backend/l3backend-pdftex.def -INPUT ./rankupdate.aux -INPUT rankupdate.aux -INPUT rankupdate.aux -OUTPUT rankupdate.aux -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-cmr.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-cmr.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-cmr.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-cmr.cfg -INPUT /usr/share/texlive/texmf-dist/fonts/map/fontname/texfonts.map -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr17.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr12.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmex10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmex10.tfm -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsa.fd -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msa.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msa.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msa.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msa.cfg -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd -INPUT /usr/share/texlive/texmf-dist/tex/latex/amsfonts/umsb.fd -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msb.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msb.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msb.cfg -INPUT /usr/share/texlive/texmf-dist/tex/latex/microtype/mt-msb.cfg -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr8.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr6.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi8.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi6.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy8.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy6.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex8.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/cmextra/cmex7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam5.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm7.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm5.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmr12.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmbx12.tfm -OUTPUT rankupdate.pdf -INPUT /var/lib/texmf/fonts/map/pdftex/updmap/pdftex.map -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmmi12.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmsy10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/cm/cmex10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msam10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/public/amsfonts/symbols/msbm10.tfm -INPUT /usr/share/texlive/texmf-dist/fonts/tfm/jknappen/ec/tcrm1000.tfm -INPUT rankupdate.aux -INPUT /usr/share/texmf/fonts/enc/dvips/cm-super/cm-super-ts1.enc -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmbx12.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmex10.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi10.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi12.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmmi7.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr10.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr17.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr5.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmr7.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy10.pfb -INPUT /usr/share/texlive/texmf-dist/fonts/type1/public/amsfonts/cm/cmsy7.pfb -INPUT /usr/share/texmf/fonts/type1/public/cm-super/sfrm1000.pfb diff --git a/examples/cpp/CMakeLists.txt b/examples/cpp/CMakeLists.txt index d26c446af..fc055fc2e 100644 --- a/examples/cpp/CMakeLists.txt +++ b/examples/cpp/CMakeLists.txt @@ -2,19 +2,19 @@ # Copyright (c) 2022 INRIA # -add_custom_target(example-cpp) +add_custom_target(${PROJECT_NAME}-example-cpp) function(ADD_PROXSUITE_CPP_EXAMPLE EXAMPLE) get_filename_component(EXAMPLE_NAME ${EXAMPLE} NAME_WE) - set(EXAMPLE_TARGET "example-cpp-${EXAMPLE_NAME}") + set(EXAMPLE_TARGET "${PROJECT_NAME}-example-cpp-${EXAMPLE_NAME}") add_unit_test(${EXAMPLE_TARGET} "${EXAMPLE}") target_link_libraries(${EXAMPLE_TARGET} PRIVATE proxsuite-test-util) - add_dependencies(example-cpp ${EXAMPLE_TARGET}) + add_dependencies(${PROJECT_NAME}-example-cpp ${EXAMPLE_TARGET}) endfunction() file(GLOB_RECURSE ${PROJECT_NAME}_CPP_EXAMPLES *.cpp) foreach(EXAMPLE ${${PROJECT_NAME}_CPP_EXAMPLES}) add_proxsuite_cpp_example(${EXAMPLE}) -endforeach(EXAMPLE ${${PROJECT_NAME}_CPP_EXAMPLES}) +endforeach() diff --git a/examples/julia/CMakeLists.txt b/examples/julia/CMakeLists.txt index dd654f27a..e7022ade0 100644 --- a/examples/julia/CMakeLists.txt +++ b/examples/julia/CMakeLists.txt @@ -3,5 +3,6 @@ file(GLOB_RECURSE ${PROJECT_NAME}_JULIA_EXAMPLES *.jl) foreach(EXAMPLE ${${PROJECT_NAME}_JULIA_EXAMPLES}) string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/examples/julia/" "" EXAMPLE ${EXAMPLE}) - add_julia_unit_test("example-jl-${EXAMPLE}" "examples/julia/${EXAMPLE}") -endforeach(EXAMPLE ${${PROJECT_NAME}_JULIA_EXAMPLES}) + add_julia_unit_test("${PROJECT_NAME}-example-jl-${EXAMPLE}" + "examples/julia/${EXAMPLE}") +endforeach() diff --git a/examples/python/CMakeLists.txt b/examples/python/CMakeLists.txt index 16e4e3bd1..fb26dcb3c 100644 --- a/examples/python/CMakeLists.txt +++ b/examples/python/CMakeLists.txt @@ -3,6 +3,6 @@ file(GLOB_RECURSE ${PROJECT_NAME}_PYTHON_EXAMPLES *.py) foreach(EXAMPLE_FILE ${${PROJECT_NAME}_PYTHON_EXAMPLES}) get_filename_component(EXAMPLE_NAME ${EXAMPLE_FILE} NAME_WE) string(REGEX REPLACE "${PROJECT_SOURCE_DIR}" "" EXAMPLE_FILE ${EXAMPLE_FILE}) - add_python_unit_test("example-py-${EXAMPLE_NAME}" "${EXAMPLE_FILE}" - "bindings/python") -endforeach(EXAMPLE_FILE ${${PROJECT_NAME}_PYTHON_EXAMPLES}) + add_python_unit_test("${PROJECT_NAME}-example-py-${EXAMPLE_NAME}" + "${EXAMPLE_FILE}" "bindings/python") +endforeach() diff --git a/examples/python/estimate_nonconvex_eigenvalue.py b/examples/python/estimate_nonconvex_eigenvalue.py index 21e2e54ef..7b6ecac59 100644 --- a/examples/python/estimate_nonconvex_eigenvalue.py +++ b/examples/python/estimate_nonconvex_eigenvalue.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1, reg=-2.0): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/init_dense_qp.py b/examples/python/init_dense_qp.py index d76736360..4408870f4 100644 --- a/examples/python/init_dense_qp.py +++ b/examples/python/init_dense_qp.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/init_dense_qp_with_box.py b/examples/python/init_dense_qp_with_box.py index 268e96e8b..15aa61c69 100644 --- a/examples/python/init_dense_qp_with_box.py +++ b/examples/python/init_dense_qp_with_box.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/init_dense_qp_with_other_options.py b/examples/python/init_dense_qp_with_other_options.py index 8b3faa97a..dbc9b4ab4 100644 --- a/examples/python/init_dense_qp_with_other_options.py +++ b/examples/python/init_dense_qp_with_other_options.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/init_dense_qp_with_timings.py b/examples/python/init_dense_qp_with_timings.py index 09cb66e2c..b156bd4c5 100644 --- a/examples/python/init_dense_qp_with_timings.py +++ b/examples/python/init_dense_qp_with_timings.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/init_with_default_options.py b/examples/python/init_with_default_options.py index caf2929c5..133cbb6cf 100644 --- a/examples/python/init_with_default_options.py +++ b/examples/python/init_with_default_options.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/loading_sparse_qp.py b/examples/python/loading_sparse_qp.py index f762fb096..7b8b3f880 100644 --- a/examples/python/loading_sparse_qp.py +++ b/examples/python/loading_sparse_qp.py @@ -8,36 +8,11 @@ import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc") - 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, q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp2 object using matrix masks -H, g, A, b, C, u, l = generate_mixed_qp(n) +H, g, A, b, C, u, l = generate_mixed_qp(n, True) H_ = H != 0.0 A_ = A != 0.0 diff --git a/examples/python/overview-simple.py b/examples/python/overview-simple.py index cc0fbb438..e220efb8b 100644 --- a/examples/python/overview-simple.py +++ b/examples/python/overview-simple.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # generate a qp problem diff --git a/examples/python/solve_dense_qp.py b/examples/python/solve_dense_qp.py index 4eb04547c..d3cd330bc 100644 --- a/examples/python/solve_dense_qp.py +++ b/examples/python/solve_dense_qp.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/solve_dense_qp_with_setting.py b/examples/python/solve_dense_qp_with_setting.py index 5999037e1..0673a0f99 100644 --- a/examples/python/solve_dense_qp_with_setting.py +++ b/examples/python/solve_dense_qp_with_setting.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/solve_without_api.py b/examples/python/solve_without_api.py index 9e84676fc..53904c718 100644 --- a/examples/python/solve_without_api.py +++ b/examples/python/solve_without_api.py @@ -1,39 +1,14 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc") - 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, q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions n = 10 n_eq = 2 n_in = 2 -H, g, A, b, C, u, l = generate_mixed_qp(n) +H, g, A, b, C, u, l = generate_mixed_qp(n, True) # solve the problem using the sparse backend results = proxsuite.proxqp.sparse.solve(H, g, A, b, C, l, u) @@ -41,7 +16,7 @@ def generate_mixed_qp(n, seed=1): # 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 @@ -59,7 +34,15 @@ def generate_mixed_qp(n, seed=1): # 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)) diff --git a/examples/python/solve_without_api_and_option.py b/examples/python/solve_without_api_and_option.py index 9e1cea272..930715dc8 100644 --- a/examples/python/solve_without_api_and_option.py +++ b/examples/python/solve_without_api_and_option.py @@ -1,32 +1,7 @@ import proxsuite import numpy as np import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/update_dense_qp.py b/examples/python/update_dense_qp.py index 106d8f544..5ceab125f 100644 --- a/examples/python/update_dense_qp.py +++ b/examples/python/update_dense_qp.py @@ -1,32 +1,6 @@ import proxsuite import numpy as np -import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/update_dense_qp_ws_previous_result.py b/examples/python/update_dense_qp_ws_previous_result.py index e8289490b..3e9b995a8 100644 --- a/examples/python/update_dense_qp_ws_previous_result.py +++ b/examples/python/update_dense_qp_ws_previous_result.py @@ -1,32 +1,6 @@ import proxsuite import numpy as np -import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating sparse random convex qps in dense format - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - 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) - - return P.toarray(), q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions diff --git a/examples/python/update_sparse_qp.py b/examples/python/update_sparse_qp.py index 12bfe7886..f52319139 100644 --- a/examples/python/update_sparse_qp.py +++ b/examples/python/update_sparse_qp.py @@ -1,32 +1,6 @@ import proxsuite import numpy as np -import scipy.sparse as spa - - -def generate_mixed_qp(n, seed=1): - # A function for generating random convex qps - - np.random.seed(seed) - n_eq = int(n / 4) - n_in = int(n / 4) - m = n_eq + n_in - - P = spa.random( - n, n, density=0.075, data_rvs=np.random.randn, format="csc" - ).toarray() - P = (P + P.T) / 2.0 - - s = max(np.absolute(np.linalg.eigvals(P))) - P += (abs(s) + 1e-02) * spa.eye(n) - P = spa.coo_matrix(P) - q = np.random.randn(n) - A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc") - 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, q, A[:n_eq, :], u[:n_eq], A[n_in:, :], u[n_in:], l[n_in:] +from util import generate_mixed_qp # load a qp object using qp problem dimensions @@ -35,7 +9,7 @@ def generate_mixed_qp(n, seed=1): n_in = 2 qp = proxsuite.proxqp.sparse.QP(n, n_eq, n_in) # generate a random QP -H, g, A, b, C, u, l = generate_mixed_qp(n) +H, g, A, b, C, u, l = generate_mixed_qp(n, True) # initialize the model of the problem to solve qp.init(H, g, A, b, C, l, u) qp.solve() @@ -44,7 +18,7 @@ def generate_mixed_qp(n, seed=1): qp.solve() # generate a QP with another sparsity structure # create a new problem and update qp -H2, g_new, A_new, b_new, C_new, u_new, l_new = generate_mixed_qp(n) +H2, g_new, A_new, b_new, C_new, u_new, l_new = generate_mixed_qp(n, True) qp.update(H=H2) # nothing will happen qp.update(g=g_new) # if only a vector changes, then the update takes effect qp.solve() # it solves the problem with the QP H,g_new,A,b,C,u,l diff --git a/examples/python/util.py b/examples/python/util.py new file mode 100644 index 000000000..75efea225 --- /dev/null +++ b/examples/python/util.py @@ -0,0 +1,31 @@ +import numpy as np +import scipy.sparse as spa + + +def generate_mixed_qp(n, sparse=False, seed=1, reg=1e-2, dens1=0.075): + # A function for generating sparse random convex qps + + np.random.seed(seed) + n_eq = int(n / 4) + n_in = int(n / 4) + m = n_eq + n_in + + P = spa.random( + n, n, density=dens1, data_rvs=np.random.randn, format="csc" + ).toarray() + P = (P + P.T) / 2.0 + + s = max(np.absolute(np.linalg.eigvals(P))) + P += (abs(s) + reg) * spa.eye(n) + P = spa.coo_matrix(P) + q = np.random.randn(n) + A = spa.random(m, n, density=0.15, data_rvs=np.random.randn, format="csc") + if not sparse: + A = A.toarray() + P = P.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) + + return P, q, A[:n_eq, :], u[:n_eq], A[n_eq:, :], u[n_eq:], l[n_eq:] diff --git a/include/proxsuite/linalg/dense/core.hpp b/include/proxsuite/linalg/dense/core.hpp index a5f1dffc2..5c43d5005 100644 --- a/include/proxsuite/linalg/dense/core.hpp +++ b/include/proxsuite/linalg/dense/core.hpp @@ -117,8 +117,8 @@ static_assert(sizeof(f64) == 8, "f64 should be 64 bits"); LDLT_FN_IMPL3(fnmadd, Prefix, Suffix); /* (-a * b + c) */ #define LDLT_LOAD_STORE(Prefix, Suffix) \ - VEG_INLINE static auto load_unaligned( \ - ScalarType const* ptr) noexcept -> Pack \ + VEG_INLINE static auto load_unaligned(ScalarType const* ptr) noexcept \ + -> Pack \ { \ return Pack{ simde_mm##Prefix##_loadu_##Suffix(ptr) }; \ } \ @@ -560,9 +560,8 @@ elem_addr(T* ptr, template auto -matrix_elem_addr(Mat&& mat, - isize row, - isize col) noexcept -> decltype(mat.data()) +matrix_elem_addr(Mat&& mat, isize row, isize col) noexcept + -> decltype(mat.data()) { return util::elem_addr::IsRowMajor)>( // @@ -575,18 +574,16 @@ matrix_elem_addr(Mat&& mat, template auto -col(T&& mat, isize col_idx) noexcept -> - typename _detail::RowColAccessImpl< - !bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor)>::template Col +col(T&& mat, isize col_idx) noexcept -> typename _detail::RowColAccessImpl< + !bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor)>::template Col { return _detail::RowColAccessImpl::IsRowMajor)>::col(mat, col_idx); } template auto -row(T&& mat, isize row_idx) noexcept -> - typename _detail::RowColAccessImpl< - !bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor)>::template Row +row(T&& mat, isize row_idx) noexcept -> typename _detail::RowColAccessImpl< + !bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor)>::template Row { return _detail::RowColAccessImpl::IsRowMajor)>::row(mat, row_idx); @@ -594,19 +591,17 @@ row(T&& mat, isize row_idx) noexcept -> template auto -trans(Mat&& mat) noexcept - -> Eigen::Map< // - _detail::const_if< - _detail::ptr_is_const::value, - Eigen::Matrix< // - typename proxsuite::linalg::veg::uncvref_t::Scalar, - proxsuite::linalg::veg::uncvref_t::ColsAtCompileTime, - proxsuite::linalg::veg::uncvref_t::RowsAtCompileTime, - bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor) - ? Eigen::ColMajor - : Eigen::RowMajor>>, - Eigen::Unaligned, - _detail::StrideOf>> +trans(Mat&& mat) noexcept -> Eigen::Map< // + _detail::const_if<_detail::ptr_is_const::value, + Eigen::Matrix< // + typename proxsuite::linalg::veg::uncvref_t::Scalar, + proxsuite::linalg::veg::uncvref_t::ColsAtCompileTime, + proxsuite::linalg::veg::uncvref_t::RowsAtCompileTime, + bool(proxsuite::linalg::veg::uncvref_t::IsRowMajor) + ? Eigen::ColMajor + : Eigen::RowMajor>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { mat.data(), @@ -621,16 +616,15 @@ trans(Mat&& mat) noexcept template auto -diagonal(Mat&& mat) noexcept - -> Eigen::Map< // - _detail::const_if<_detail::ptr_is_const::value, - Eigen::Matrix< // - typename proxsuite::linalg::veg::uncvref_t::Scalar, - Eigen::Dynamic, - 1, - Eigen::ColMajor>>, - Eigen::Unaligned, - Eigen::InnerStride> +diagonal(Mat&& mat) noexcept -> Eigen::Map< // + _detail::const_if<_detail::ptr_is_const::value, + Eigen::Matrix< // + typename proxsuite::linalg::veg::uncvref_t::Scalar, + Eigen::Dynamic, + 1, + Eigen::ColMajor>>, + Eigen::Unaligned, + Eigen::InnerStride> { VEG_DEBUG_ASSERT( // mat.rows() == mat.cols()); @@ -667,12 +661,11 @@ submatrix(Mat&& mat, template auto -to_view(Mat&& mat) noexcept - -> Eigen::Map<_detail::const_if< - _detail::ptr_is_const::value, - _detail::OwnedAll>>, - Eigen::Unaligned, - _detail::StrideOf>> +to_view(Mat&& mat) noexcept -> Eigen::Map< + _detail::const_if<_detail::ptr_is_const::value, + _detail::OwnedAll>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { mat.data(), @@ -687,12 +680,11 @@ to_view(Mat&& mat) noexcept template auto -to_view_dyn_rows(Mat&& mat) noexcept - -> Eigen::Map<_detail::const_if< - _detail::ptr_is_const::value, - _detail::OwnedRows>>, - Eigen::Unaligned, - _detail::StrideOf>> +to_view_dyn_rows(Mat&& mat) noexcept -> Eigen::Map< + _detail::const_if<_detail::ptr_is_const::value, + _detail::OwnedRows>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { mat.data(), @@ -707,12 +699,11 @@ to_view_dyn_rows(Mat&& mat) noexcept template auto -to_view_dyn_cols(Mat&& mat) noexcept - -> Eigen::Map<_detail::const_if< - _detail::ptr_is_const::value, - _detail::OwnedCols>>, - Eigen::Unaligned, - _detail::StrideOf>> +to_view_dyn_cols(Mat&& mat) noexcept -> Eigen::Map< + _detail::const_if<_detail::ptr_is_const::value, + _detail::OwnedCols>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { mat.data(), @@ -747,12 +738,11 @@ to_view_dyn(Mat&& mat) noexcept template auto -subrows(Mat&& mat, isize row_start, isize nrows) noexcept - -> Eigen::Map<_detail::const_if< - _detail::ptr_is_const::value, - _detail::OwnedRows>>, - Eigen::Unaligned, - _detail::StrideOf>> +subrows(Mat&& mat, isize row_start, isize nrows) noexcept -> Eigen::Map< + _detail::const_if<_detail::ptr_is_const::value, + _detail::OwnedRows>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { util::elem_addr::IsRowMajor)>( @@ -768,12 +758,11 @@ subrows(Mat&& mat, isize row_start, isize nrows) noexcept template auto -subcols(Mat&& mat, isize col_start, isize ncols) noexcept - -> Eigen::Map<_detail::const_if< - _detail::ptr_is_const::value, - _detail::OwnedCols>>, - Eigen::Unaligned, - _detail::StrideOf>> +subcols(Mat&& mat, isize col_start, isize ncols) noexcept -> Eigen::Map< + _detail::const_if<_detail::ptr_is_const::value, + _detail::OwnedCols>>, + Eigen::Unaligned, + _detail::StrideOf>> { return { util::elem_addr::IsRowMajor)>( @@ -859,8 +848,8 @@ temp_mat_req(proxsuite::linalg::veg::Tag /*tag*/, template auto -temp_vec_req(proxsuite::linalg::veg::Tag /*tag*/, - isize rows) noexcept -> proxsuite::linalg::veg::dynstack::StackReq +temp_vec_req(proxsuite::linalg::veg::Tag /*tag*/, isize rows) noexcept + -> proxsuite::linalg::veg::dynstack::StackReq { return { rows * isize{ sizeof(T) }, diff --git a/include/proxsuite/linalg/dense/factorize.hpp b/include/proxsuite/linalg/dense/factorize.hpp index c11f2368b..1f3c20e3f 100644 --- a/include/proxsuite/linalg/dense/factorize.hpp +++ b/include/proxsuite/linalg/dense/factorize.hpp @@ -350,8 +350,8 @@ factorize_recursive(Mat&& mat, template auto -factorize_req(proxsuite::linalg::veg::Tag tag, - isize n) noexcept -> proxsuite::linalg::veg::dynstack::StackReq +factorize_req(proxsuite::linalg::veg::Tag tag, isize n) noexcept + -> proxsuite::linalg::veg::dynstack::StackReq { return proxsuite::linalg::dense::factorize_blocked_req(tag, n, 128) | proxsuite::linalg::dense::factorize_recursive_req(tag, n); diff --git a/include/proxsuite/linalg/dense/ldlt.hpp b/include/proxsuite/linalg/dense/ldlt.hpp index 1a64dfd23..b74bde51a 100644 --- a/include/proxsuite/linalg/dense/ldlt.hpp +++ b/include/proxsuite/linalg/dense/ldlt.hpp @@ -614,23 +614,23 @@ struct Ldlt auto dim() const noexcept -> isize { return perm.len(); } auto ld_col() const noexcept -> Eigen::Map< // - ColMat const, - Eigen::Unaligned, - Eigen::OuterStride> + ColMat const, + Eigen::Unaligned, + Eigen::OuterStride> { return { ld_storage.ptr(), dim(), dim(), stride }; } auto ld_col_mut() noexcept -> Eigen::Map< // - ColMat, - Eigen::Unaligned, - Eigen::OuterStride> + ColMat, + Eigen::Unaligned, + Eigen::OuterStride> { return { ld_storage.ptr_mut(), dim(), dim(), stride }; } auto ld_row() const noexcept -> Eigen::Map< // - RowMat const, - Eigen::Unaligned, - Eigen::OuterStride> + RowMat const, + Eigen::Unaligned, + Eigen::OuterStride> { return { ld_storage.ptr(), @@ -640,9 +640,9 @@ struct Ldlt }; } auto ld_row_mut() noexcept -> Eigen::Map< // - RowMat, - Eigen::Unaligned, - Eigen::OuterStride> + RowMat, + Eigen::Unaligned, + Eigen::OuterStride> { return { ld_storage.ptr_mut(), diff --git a/include/proxsuite/linalg/sparse/factorize.hpp b/include/proxsuite/linalg/sparse/factorize.hpp index 46451dd1c..225a82f7b 100644 --- a/include/proxsuite/linalg/sparse/factorize.hpp +++ b/include/proxsuite/linalg/sparse/factorize.hpp @@ -249,8 +249,8 @@ dense_ltsolve(DenseVecMut x, MatRef l) noexcept(false) */ template auto -etree_req(proxsuite::linalg::veg::Tag /*tag*/, - isize n) noexcept -> proxsuite::linalg::veg::dynstack::StackReq +etree_req(proxsuite::linalg::veg::Tag /*tag*/, isize n) noexcept + -> proxsuite::linalg::veg::dynstack::StackReq { return { n * isize{ sizeof(I) }, alignof(I) }; } @@ -456,8 +456,8 @@ postorder_depth_first_search( // */ template auto -postorder_req(proxsuite::linalg::veg::Tag /*tag*/, - isize n) noexcept -> proxsuite::linalg::veg::dynstack::StackReq +postorder_req(proxsuite::linalg::veg::Tag /*tag*/, isize n) noexcept + -> proxsuite::linalg::veg::dynstack::StackReq { return { (3 * n) * isize(sizeof(I)), alignof(I) }; } @@ -716,9 +716,8 @@ column_counts(I* counts, template auto -amd_req(proxsuite::linalg::veg::Tag /*tag*/, - isize /*n*/, - isize nnz) noexcept -> proxsuite::linalg::veg::dynstack::StackReq +amd_req(proxsuite::linalg::veg::Tag /*tag*/, isize /*n*/, isize nnz) noexcept + -> proxsuite::linalg::veg::dynstack::StackReq { return { nnz * isize{ sizeof(char) }, alignof(char) }; } diff --git a/include/proxsuite/linalg/veg/internal/dyn_index.hpp b/include/proxsuite/linalg/veg/internal/dyn_index.hpp index ce3c4050b..5b208293b 100644 --- a/include/proxsuite/linalg/veg/internal/dyn_index.hpp +++ b/include/proxsuite/linalg/veg/internal/dyn_index.hpp @@ -223,8 +223,8 @@ struct binary_traits, Dyn> : binary_traits { using Mul = Fix<0>; VEG_NODISCARD - constexpr VEG_INLINE static auto mul_fn(Fix<0> /*a*/, - Dyn /*b*/) VEG_NOEXCEPT -> Mul + constexpr VEG_INLINE static auto mul_fn(Fix<0> /*a*/, Dyn /*b*/) VEG_NOEXCEPT + -> Mul { return {}; } @@ -234,8 +234,8 @@ template struct binary_traits> : binary_traits { using Mul = typename binary_traits, Dyn>::Mul; - VEG_INLINE static constexpr auto mul_fn(Dyn a, - Fix /*b*/) VEG_NOEXCEPT -> Mul + VEG_INLINE static constexpr auto mul_fn(Dyn a, Fix /*b*/) VEG_NOEXCEPT + -> Mul { return binary_traits, Dyn>::mul_fn({}, a); } diff --git a/include/proxsuite/linalg/veg/internal/external/hedley.ext.hpp b/include/proxsuite/linalg/veg/internal/external/hedley.ext.hpp index 91a3edf63..af3872e25 100644 --- a/include/proxsuite/linalg/veg/internal/external/hedley.ext.hpp +++ b/include/proxsuite/linalg/veg/internal/external/hedley.ext.hpp @@ -1838,7 +1838,7 @@ HEDLEY_DIAGNOSTIC_POP #else #include #define HEDLEY_IS_CONSTEXPR_(expr) \ - _Generic((1 ? (void*)((intptr_t) * 0) : (int*)0), int*: 1, void*: 0) + _Generic((1 ? (void*)((intptr_t)*0) : (int*)0), int*: 1, void*: 0) #endif #elif defined(HEDLEY_GCC_VERSION) || defined(HEDLEY_INTEL_VERSION) || \ defined(HEDLEY_TINYC_VERSION) || defined(HEDLEY_TI_ARMCL_VERSION) || \ diff --git a/include/proxsuite/linalg/veg/internal/macros.hpp b/include/proxsuite/linalg/veg/internal/macros.hpp index eed6354a3..5f277f443 100644 --- a/include/proxsuite/linalg/veg/internal/macros.hpp +++ b/include/proxsuite/linalg/veg/internal/macros.hpp @@ -324,11 +324,11 @@ Name, \ (__VA_ARGS__), \ ::proxsuite::linalg::veg::meta::bool_constant<__VA_ARGS__>); \ - VEG_TEMPLATE( \ - Tpl, \ - requires(__VA_ARGS__), \ - constexpr auto check_##Name, \ - (_ = 0, int)) noexcept -> ::proxsuite::linalg::veg::meta::true_type + VEG_TEMPLATE(Tpl, \ + requires(__VA_ARGS__), \ + constexpr auto check_##Name, \ + (_ = 0, int)) noexcept \ + -> ::proxsuite::linalg::veg::meta::true_type #define __VEG_IMPL_SFINAE(_, Param) \ , ::proxsuite::linalg::veg::meta:: \ @@ -1195,20 +1195,18 @@ struct ExtractCharsImplExpr> template auto -extract_chars(LiteralType /*unused*/) -> - typename ExtractCharsImpl< - LiteralType, - _meta::make_index_sequence>::Type +extract_chars(LiteralType /*unused*/) -> typename ExtractCharsImpl< + LiteralType, + _meta::make_index_sequence>::Type { return {}; } template auto -extract_chars_expr(LiteralType /*unused*/) -> - typename ExtractCharsImplExpr< - LiteralType, - _meta::make_index_sequence>::Type +extract_chars_expr(LiteralType /*unused*/) -> typename ExtractCharsImplExpr< + LiteralType, + _meta::make_index_sequence>::Type { return {}; } diff --git a/include/proxsuite/linalg/veg/memory/dynamic_stack.hpp b/include/proxsuite/linalg/veg/memory/dynamic_stack.hpp index b6110d1ed..528d6e900 100644 --- a/include/proxsuite/linalg/veg/memory/dynamic_stack.hpp +++ b/include/proxsuite/linalg/veg/memory/dynamic_stack.hpp @@ -279,8 +279,8 @@ struct DynStackMut template VEG_NODISCARD auto make_alloc(Tag /*unused*/, isize len, - isize align = alignof(T)) - VEG_NOEXCEPT -> DynStackAlloc + isize align = alignof(T)) VEG_NOEXCEPT + -> DynStackAlloc { assert_valid_len(len); DynStackAlloc get{ diff --git a/include/proxsuite/linalg/veg/memory/stack_alloc.hpp b/include/proxsuite/linalg/veg/memory/stack_alloc.hpp index a0affffb9..becb39cb6 100644 --- a/include/proxsuite/linalg/veg/memory/stack_alloc.hpp +++ b/include/proxsuite/linalg/veg/memory/stack_alloc.hpp @@ -76,8 +76,8 @@ struct BumpAllocLayout return blk; } - auto _grow_last_unchecked(void* ptr, - usize new_byte_size) noexcept -> mem::AllocBlock + auto _grow_last_unchecked(void* ptr, usize new_byte_size) noexcept + -> mem::AllocBlock { auto rem_bytes = usize(end_ptr - static_cast(ptr)); auto given_bytes = _align(new_byte_size); @@ -157,8 +157,8 @@ struct Alloc> using ImplMut = _detail::_mem::BumpAllocLayout&; using RefMut = proxsuite::linalg::veg::RefMut>; - VEG_INLINE static auto alloc(RefMut ref, - mem::Layout layout) noexcept -> AllocBlock + VEG_INLINE static auto alloc(RefMut ref, mem::Layout layout) noexcept + -> AllocBlock { return ImplMut(ref.get())._alloc(layout); } @@ -184,8 +184,8 @@ struct Alloc> using ImplMut = _detail::_mem::BumpAllocLayout&; using RefMut = proxsuite::linalg::veg::RefMut>; - VEG_INLINE static auto alloc(RefMut ref, - mem::Layout layout) noexcept -> AllocBlock + VEG_INLINE static auto alloc(RefMut ref, mem::Layout layout) noexcept + -> AllocBlock { return ImplMut(ref.get())._alloc(layout); } @@ -211,8 +211,8 @@ struct Alloc> using ImplMut = _detail::_mem::BumpAllocLayout&; using RefMut = proxsuite::linalg::veg::RefMut>; - VEG_INLINE static auto alloc(RefMut ref, - mem::Layout layout) noexcept -> AllocBlock + VEG_INLINE static auto alloc(RefMut ref, mem::Layout layout) noexcept + -> AllocBlock { return ImplMut(ref.get())._alloc(layout); } diff --git a/include/proxsuite/linalg/veg/slice.hpp b/include/proxsuite/linalg/veg/slice.hpp index 965875619..8abb3f0ee 100644 --- a/include/proxsuite/linalg/veg/slice.hpp +++ b/include/proxsuite/linalg/veg/slice.hpp @@ -60,8 +60,8 @@ struct Slice : _detail::_slice::adl::AdlBase } VEG_NODISCARD VEG_INLINE - constexpr auto get_unchecked(Unsafe /*tag*/, - isize idx) const VEG_NOEXCEPT -> Ref + constexpr auto get_unchecked(Unsafe /*tag*/, isize idx) const VEG_NOEXCEPT + -> Ref { return ref(*(data + idx)); } @@ -149,8 +149,8 @@ struct SliceMut : private Slice { return mut(const_cast(*(this->data + idx))); } - VEG_NODISCARD VEG_INLINE auto as_mut_bytes() - VEG_NOEXCEPT -> SliceMut + VEG_NODISCARD VEG_INLINE auto as_mut_bytes() VEG_NOEXCEPT + -> SliceMut { return { unsafe, diff --git a/include/proxsuite/linalg/veg/tuple.hpp b/include/proxsuite/linalg/veg/tuple.hpp index 8fe3ca1a9..d0104bd47 100644 --- a/include/proxsuite/linalg/veg/tuple.hpp +++ b/include/proxsuite/linalg/veg/tuple.hpp @@ -401,7 +401,7 @@ struct IndexedTuple, Ts...> (/*arg*/, Fix)) && VEG_NOEXCEPT_IF( VEG_CONCEPT(nothrow_movable(I), Ts...>>)) - -> ith(I), Ts...> + -> ith(I), Ts...> { return __VEG_IMPL_LEAF_ONCE( *this, static_cast(I), ith(I), Ts...>); @@ -535,8 +535,8 @@ get(tuple::IndexedTuple, template VEG_NODISCARD VEG_INLINE constexpr auto get(tuple::IndexedTuple, - Ts...> const&& tup) - VEG_NOEXCEPT -> ith const&& + Ts...> const&& tup) VEG_NOEXCEPT + -> ith const&& { return static_cast const&&>( __VEG_IMPL_LEAF(tup, I, ith)); @@ -659,9 +659,9 @@ struct zip VEG_INLINE static constexpr auto apply( IndexedTuple, Ts...> first, - Tuples... rest) VEG_NOEXCEPT -> Tuple< // - typename Helper:: // - template Type...> + Tuples... rest) VEG_NOEXCEPT -> Tuple< // + typename Helper:: // + template Type...> { return { ((void)first, tuplify{}), @@ -754,9 +754,15 @@ struct cat proxsuite::linalg::veg::meta::false_type /*unused*/, Tuples&&... tups) VEG_NOEXCEPT -> Concat { +#ifdef _MSC_VER + return cat::from_ref_to_result( + Tag>{}, + cat::apply(_detail::_tuple::tuple_fwd(VEG_FWD(tups))...)); +#else return cat::template from_ref_to_result( Tag>{}, cat::apply(_detail::_tuple::tuple_fwd(VEG_FWD(tups))...)); +#endif } template @@ -777,11 +783,11 @@ struct cat VEG_INLINE static constexpr auto apply( IndexedTuple, Ts...>&& first, - Tuples&&... rest) - VEG_NOEXCEPT -> proxsuite::linalg::veg::meta::type_sequence_cat< - Tuple, - Tuple, - typename _detail::meta_::IndexedToTuple::Type...> + Tuples&&... rest) VEG_NOEXCEPT + -> proxsuite::linalg::veg::meta::type_sequence_cat< + Tuple, + Tuple, + typename _detail::meta_::IndexedToTuple::Type...> { return cat::apply2(VEG_FWD(first), cat::apply(VEG_FWD(rest)...)); } diff --git a/include/proxsuite/linalg/veg/type_traits/constructible.hpp b/include/proxsuite/linalg/veg/type_traits/constructible.hpp index ce1ed6396..1872b3e59 100644 --- a/include/proxsuite/linalg/veg/type_traits/constructible.hpp +++ b/include/proxsuite/linalg/veg/type_traits/constructible.hpp @@ -189,7 +189,7 @@ struct WithArg Fn&& fn; T&& arg; VEG_INLINE constexpr auto operator()() const&& -> decltype(VEG_FWD(fn)( - VEG_FWD(arg))) + VEG_FWD(arg))) { return VEG_FWD(fn)(VEG_FWD(arg)); } diff --git a/include/proxsuite/linalg/veg/util/get.hpp b/include/proxsuite/linalg/veg/util/get.hpp index df0f3789e..453e63c9a 100644 --- a/include/proxsuite/linalg/veg/util/get.hpp +++ b/include/proxsuite/linalg/veg/util/get.hpp @@ -36,8 +36,8 @@ struct array_get using result_type = decltype(VEG_DECLVAL(T&&)[I::value]); template - VEG_INLINE static constexpr auto apply(T&& arr) - VEG_NOEXCEPT -> decltype(VEG_FWD(arr)[I]) + VEG_INLINE static constexpr auto apply(T&& arr) VEG_NOEXCEPT + -> decltype(VEG_FWD(arr)[I]) { return VEG_FWD(arr)[I]; } diff --git a/include/proxsuite/linalg/veg/vec.hpp b/include/proxsuite/linalg/veg/vec.hpp index eb409c3e6..f6166208d 100644 --- a/include/proxsuite/linalg/veg/vec.hpp +++ b/include/proxsuite/linalg/veg/vec.hpp @@ -277,9 +277,9 @@ realloc_and_append( // mem::AllocBlock out, usize out_len, T const* in, - usize in_len) - VEG_NOEXCEPT_IF(VEG_CONCEPT(alloc::nothrow_grow) && - VEG_CONCEPT(alloc::nothrow_clone)) -> mem::AllocBlock + usize in_len) VEG_NOEXCEPT_IF(VEG_CONCEPT(alloc::nothrow_grow) && + VEG_CONCEPT(alloc::nothrow_clone)) + -> mem::AllocBlock { if (in_len == 0) { diff --git a/include/proxsuite/proxqp/dense/solver.hpp b/include/proxsuite/proxqp/dense/solver.hpp index 5710fe6db..6d428ae64 100644 --- a/include/proxsuite/proxqp/dense/solver.hpp +++ b/include/proxsuite/proxqp/dense/solver.hpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // /** * @file solver.hpp @@ -195,7 +195,7 @@ mu_update(const Model& qpmodel, { LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpwork.n_c, stack); qpwork.dw_aug.head(qpmodel.dim).setOnes(); - T delta_mu(mu_in_new - qpresults.info.mu_in_inv); + T delta_mu(T(1) / mu_in_new - qpresults.info.mu_in_inv); qpwork.dw_aug.head(qpmodel.dim).array() *= delta_mu; for (isize i = 0; i < n_constraints; ++i) { isize j = qpwork.current_bijection_map[i]; @@ -212,14 +212,17 @@ mu_update(const Model& qpmodel, } } qpwork.ldl.rank_r_update( - new_cols, qpwork.dw_aug.head(qpmodel.dim), stack); + new_cols, qpwork.dw_aug.head(qpwork.n_c), stack); } // mu update for A { LDLT_TEMP_MAT_UNINIT(T, new_cols, qpmodel.dim, qpmodel.n_eq, stack); + qpwork.dw_aug.head(qpmodel.n_eq).setOnes(); + T delta_mu(1 / mu_eq_new - qpresults.info.mu_eq_inv); + qpwork.dw_aug.head(qpmodel.n_eq).array() *= delta_mu; new_cols = qpwork.A_scaled.transpose(); qpwork.ldl.rank_r_update( - new_cols, qpwork.dw_aug.head(qpmodel.dim), stack); + new_cols, qpwork.dw_aug.head(qpmodel.n_eq), stack); } } break; case DenseBackend::Automatic: @@ -1778,7 +1781,7 @@ qp_solve( // } if (qpsettings.compute_timings) { - qpresults.info.solve_time = qpwork.timer.elapsed().user; // in nanoseconds + qpresults.info.solve_time = qpwork.timer.elapsed().user; // in microseconds qpresults.info.run_time = qpresults.info.solve_time + qpresults.info.setup_time; } @@ -1786,46 +1789,46 @@ qp_solve( // if (qpsettings.verbose) { std::cout << "-------------------SOLVER STATISTICS-------------------" << std::endl; - std::cout << "outer iter: " << qpresults.info.iter_ext << std::endl; - std::cout << "total iter: " << qpresults.info.iter << std::endl; - std::cout << "mu updates: " << qpresults.info.mu_updates << std::endl; - std::cout << "rho updates: " << qpresults.info.rho_updates << std::endl; - std::cout << "objective: " << qpresults.info.objValue << std::endl; + std::cout << "outer iter: " << qpresults.info.iter_ext << std::endl; + std::cout << "total iter: " << qpresults.info.iter << std::endl; + std::cout << "mu updates: " << qpresults.info.mu_updates << std::endl; + std::cout << "rho updates: " << qpresults.info.rho_updates << std::endl; + std::cout << "objective: " << qpresults.info.objValue << std::endl; switch (qpresults.info.status) { case QPSolverOutput::PROXQP_SOLVED: { - std::cout << "status: " + std::cout << "status: " << "Solved" << std::endl; break; } case QPSolverOutput::PROXQP_MAX_ITER_REACHED: { - std::cout << "status: " + std::cout << "status: " << "Maximum number of iterations reached" << std::endl; break; } case QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Primal infeasible" << std::endl; break; } case QPSolverOutput::PROXQP_DUAL_INFEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Dual infeasible" << std::endl; break; } case QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Solved closest primal feasible" << std::endl; break; } case QPSolverOutput::PROXQP_NOT_RUN: { - std::cout << "status: " + std::cout << "status: " << "Solver not run" << std::endl; break; } } if (qpsettings.compute_timings) - std::cout << "run time: " << qpresults.info.solve_time << std::endl; + std::cout << "run time [μs]: " << qpresults.info.solve_time << std::endl; std::cout << "--------------------------------------------------------" << std::endl; } diff --git a/include/proxsuite/proxqp/dense/views.hpp b/include/proxsuite/proxqp/dense/views.hpp index c8cc22c6e..c43c8cae9 100644 --- a/include/proxsuite/proxqp/dense/views.hpp +++ b/include/proxsuite/proxqp/dense/views.hpp @@ -395,8 +395,7 @@ using DataExpr = decltype(static_cast(VEG_DECLVAL(Mat&).data())); template - class F, + template class F, typename... Ts> struct DetectedImpl : proxsuite::linalg::veg::meta::false_type { @@ -717,8 +716,8 @@ struct StridedVectorView { return *ptr(index); } - VEG_INLINE auto segment(isize i, - isize size) const noexcept -> StridedVectorView + VEG_INLINE auto segment(isize i, isize size) const noexcept + -> StridedVectorView { return { from_ptr_size_stride, @@ -784,8 +783,8 @@ struct StridedVectorViewMut { return *ptr(index); } - VEG_INLINE auto segment(isize i, - isize size) const noexcept -> StridedVectorViewMut + VEG_INLINE auto segment(isize i, isize size) const noexcept + -> StridedVectorViewMut { return { from_ptr_size_stride, @@ -884,15 +883,13 @@ struct MatrixView } public: - VEG_INLINE auto col(isize c) const noexcept - -> proxsuite::linalg::veg::meta:: - if_t<(L == colmajor), VectorView, StridedVectorView> + VEG_INLINE auto col(isize c) const noexcept -> proxsuite::linalg::veg::meta:: + if_t<(L == colmajor), VectorView, StridedVectorView> { return col_impl(proxsuite::linalg::veg::meta::constant{}, c); } - VEG_INLINE auto row(isize r) const noexcept - -> proxsuite::linalg::veg::meta:: - if_t<(L == rowmajor), VectorView, StridedVectorView> + VEG_INLINE auto row(isize r) const noexcept -> proxsuite::linalg::veg::meta:: + if_t<(L == rowmajor), VectorView, StridedVectorView> { return trans().col(r); } @@ -992,15 +989,13 @@ struct MatrixViewMut } public: - VEG_INLINE auto col(isize c) const noexcept - -> proxsuite::linalg::veg::meta:: - if_t<(L == colmajor), VectorViewMut, StridedVectorViewMut> + VEG_INLINE auto col(isize c) const noexcept -> proxsuite::linalg::veg::meta:: + if_t<(L == colmajor), VectorViewMut, StridedVectorViewMut> { return col_impl(proxsuite::linalg::veg::meta::constant{}, c); } - VEG_INLINE auto row(isize r) const noexcept - -> proxsuite::linalg::veg::meta:: - if_t<(L == rowmajor), VectorViewMut, StridedVectorViewMut> + VEG_INLINE auto row(isize r) const noexcept -> proxsuite::linalg::veg::meta:: + if_t<(L == rowmajor), VectorViewMut, StridedVectorViewMut> { return trans().col(r); } diff --git a/include/proxsuite/proxqp/settings.hpp b/include/proxsuite/proxqp/settings.hpp index b3d7ce264..f455238b4 100644 --- a/include/proxsuite/proxqp/settings.hpp +++ b/include/proxsuite/proxqp/settings.hpp @@ -182,9 +182,9 @@ struct Settings * re-computed when calling the update method. * @param compute_preconditioner If set to true, the preconditioner will be * computed with the init method. - * @param compute_timings If set to true, timings will be computed by the - * solver (setup time, solving time, and run time = setup time + solving - * time). + * @param compute_timings If set to true, timings in microseconds will be + * computed by the solver (setup time, solving time, and run time = setup time + * + solving time). * @param check_duality_gap If set to true, duality gap will be calculated and * included in the stopping criterion. * @param eps_duality_gap_abs absolute duality-gap stopping criterion. diff --git a/include/proxsuite/proxqp/sparse/solver.hpp b/include/proxsuite/proxqp/sparse/solver.hpp index 609d2b84d..c2ecd56dd 100644 --- a/include/proxsuite/proxqp/sparse/solver.hpp +++ b/include/proxsuite/proxqp/sparse/solver.hpp @@ -1623,45 +1623,45 @@ qp_solve(Results& results, if (settings.verbose) { std::cout << "-------------------SOLVER STATISTICS-------------------" << std::endl; - std::cout << "outer iter: " << results.info.iter_ext << std::endl; - std::cout << "total iter: " << results.info.iter << std::endl; - std::cout << "mu updates: " << results.info.mu_updates << std::endl; - std::cout << "rho updates: " << results.info.rho_updates << std::endl; - std::cout << "objective: " << results.info.objValue << std::endl; + std::cout << "outer iter: " << results.info.iter_ext << std::endl; + std::cout << "total iter: " << results.info.iter << std::endl; + std::cout << "mu updates: " << results.info.mu_updates << std::endl; + std::cout << "rho updates: " << results.info.rho_updates << std::endl; + std::cout << "objective: " << results.info.objValue << std::endl; switch (results.info.status) { case QPSolverOutput::PROXQP_SOLVED: { - std::cout << "status: " + std::cout << "status: " << "Solved" << std::endl; break; } case QPSolverOutput::PROXQP_MAX_ITER_REACHED: { - std::cout << "status: " + std::cout << "status: " << "Maximum number of iterations reached" << std::endl; break; } case QPSolverOutput::PROXQP_PRIMAL_INFEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Primal infeasible" << std::endl; break; } case QPSolverOutput::PROXQP_DUAL_INFEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Dual infeasible" << std::endl; break; } case QPSolverOutput::PROXQP_SOLVED_CLOSEST_PRIMAL_FEASIBLE: { - std::cout << "status: " + std::cout << "status: " << "Solved closest primal feasible" << std::endl; break; } case QPSolverOutput::PROXQP_NOT_RUN: { - std::cout << "status: " + std::cout << "status: " << "Solver not run" << std::endl; break; } } if (settings.compute_timings) - std::cout << "run time: " << results.info.solve_time << std::endl; + std::cout << "run time [μs]: " << results.info.solve_time << std::endl; std::cout << "--------------------------------------------------------" << std::endl; } diff --git a/include/proxsuite/proxqp/sparse/utils.hpp b/include/proxsuite/proxqp/sparse/utils.hpp index 62e55413b..fe1e4b275 100644 --- a/include/proxsuite/proxqp/sparse/utils.hpp +++ b/include/proxsuite/proxqp/sparse/utils.hpp @@ -613,27 +613,28 @@ global_dual_residual_infeasibility(VectorViewMut Adx, */ template auto -unscaled_primal_dual_residual(Workspace& work, - Results& results, - const Settings& settings, - VecMapMut primal_residual_eq_scaled, - VecMapMut primal_residual_in_scaled_lo, - VecMapMut primal_residual_in_scaled_up, - VecMapMut dual_residual_scaled, - T& primal_feasibility_eq_rhs_0, - T& primal_feasibility_in_rhs_0, - T& dual_feasibility_rhs_0, - T& dual_feasibility_rhs_1, - T& dual_feasibility_rhs_3, - T& rhs_duality_gap, - const P& precond, - Model const& data, - const QpView qp_scaled, - VecMapMut x_e, - VecMapMut y_e, - VecMapMut z_e, - proxsuite::linalg::veg::dynstack::DynStackMut - stack) -> proxsuite::linalg::veg::Tuple +unscaled_primal_dual_residual( + Workspace& work, + Results& results, + const Settings& settings, + VecMapMut primal_residual_eq_scaled, + VecMapMut primal_residual_in_scaled_lo, + VecMapMut primal_residual_in_scaled_up, + VecMapMut dual_residual_scaled, + T& primal_feasibility_eq_rhs_0, + T& primal_feasibility_in_rhs_0, + T& dual_feasibility_rhs_0, + T& dual_feasibility_rhs_1, + T& dual_feasibility_rhs_3, + T& rhs_duality_gap, + const P& precond, + Model const& data, + const QpView qp_scaled, + VecMapMut x_e, + VecMapMut y_e, + VecMapMut z_e, + proxsuite::linalg::veg::dynstack::DynStackMut stack) + -> proxsuite::linalg::veg::Tuple { isize n = x_e.rows(); 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 diff --git a/include/proxsuite/proxqp/utils/random_qp_problems.hpp b/include/proxsuite/proxqp/utils/random_qp_problems.hpp index 2a13423c6..e08641d47 100644 --- a/include/proxsuite/proxqp/utils/random_qp_problems.hpp +++ b/include/proxsuite/proxqp/utils/random_qp_problems.hpp @@ -226,9 +226,8 @@ positive_definite_rand(isize n, Scalar cond) -> Mat template auto -sparse_positive_definite_rand(isize n, - Scalar cond, - Scalar p) -> SparseMat +sparse_positive_definite_rand(isize n, Scalar cond, Scalar p) + -> SparseMat { auto H = SparseMat(n, n); @@ -277,9 +276,8 @@ sparse_positive_definite_rand(isize n, template auto -sparse_positive_definite_rand_compressed(isize n, - Scalar rho, - Scalar p) -> SparseMat +sparse_positive_definite_rand_compressed(isize n, Scalar rho, Scalar p) + -> SparseMat { auto H = SparseMat(n, n); @@ -308,9 +306,8 @@ sparse_positive_definite_rand_compressed(isize n, template auto -sparse_positive_definite_rand_not_compressed(isize n, - Scalar rho, - Scalar p) -> Mat +sparse_positive_definite_rand_not_compressed(isize n, Scalar rho, Scalar p) + -> Mat { auto H = Mat(n, n); H.setZero(); @@ -355,9 +352,8 @@ sparse_matrix_rand(isize nrows, isize ncols, Scalar p) -> SparseMat template auto -sparse_matrix_rand_not_compressed(isize nrows, - isize ncols, - Scalar p) -> Mat +sparse_matrix_rand_not_compressed(isize nrows, isize ncols, Scalar p) + -> Mat { auto A = Mat(nrows, ncols); A.setZero(); @@ -416,9 +412,8 @@ template auto -matmul3(MatLhs const& a, - MatMid const& b, - MatRhs const& c) -> Mat +matmul3(MatLhs const& a, MatMid const& b, MatRhs const& c) + -> Mat { return matmul(matmul(a, b), c); } diff --git a/package.xml b/package.xml index 9fe309bed..4aa4539ae 100644 --- a/package.xml +++ b/package.xml @@ -1,7 +1,7 @@ proxsuite - 0.6.6 + 0.6.7 The Advanced Proximal Optimization Toolbox Justin Carpentier diff --git a/pyproject.toml b/pyproject.toml index 186eaf6d6..9f65cf1c1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "proxsuite" -version = "0.6.6" +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"] @@ -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"] diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 052b38080..f1fcd7a95 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,8 +1,8 @@ include(../cmake-external/doctest.cmake) find_package(Matio REQUIRED) -add_library(doctest STATIC doctest/doctest.cpp) -target_include_directories(doctest PUBLIC ./doctest) +add_library(${PROJECT_NAME}-doctest STATIC doctest/doctest.cpp) +target_include_directories(${PROJECT_NAME}-doctest PUBLIC ./doctest) add_library(cnpy OBJECT src/cnpy.cpp) target_link_libraries(cnpy Eigen3::Eigen) target_include_directories(cnpy PUBLIC ./include) @@ -20,13 +20,14 @@ else() endif() macro(proxsuite_test name path) - add_executable(test-cpp-${name} ${path}) - doctest_discover_tests(test-cpp-${name}) - target_link_libraries(test-cpp-${name} PUBLIC proxsuite doctest - proxsuite-test-util) - target_compile_definitions(test-cpp-${name} + set(target_name ${PROJECT_NAME}-test-cpp-${name}) + add_executable(${target_name} ${path}) + doctest_discover_tests(${target_name}) + target_link_libraries(${target_name} PUBLIC proxsuite ${PROJECT_NAME}-doctest + proxsuite-test-util) + target_compile_definitions(${target_name} PRIVATE PROBLEM_PATH="${CMAKE_CURRENT_SOURCE_DIR}") - add_dependencies(build_tests test-cpp-${name}) + add_dependencies(build_tests ${target_name}) endmacro() proxsuite_test(dense_ruiz_equilibration src/dense_ruiz_equilibration.cpp) @@ -45,7 +46,8 @@ proxsuite_test(cvxpy src/cvxpy.cpp) if(BUILD_WITH_OPENMP_SUPPORT) proxsuite_test(parallel src/parallel_qp_solve.cpp) - target_link_libraries(test-cpp-parallel PRIVATE OpenMP::OpenMP_CXX) + target_link_libraries(${PROJECT_NAME}-test-cpp-parallel + PRIVATE OpenMP::OpenMP_CXX) endif() # Test serialization @@ -54,17 +56,22 @@ macro(ADD_TEST_CFLAGS target flag) TARGET ${target} APPEND_STRING PROPERTY COMPILE_FLAGS " ${flag}") -endmacro(ADD_TEST_CFLAGS) +endmacro() make_directory("${CMAKE_CURRENT_BINARY_DIR}/serialization-data") proxsuite_test(serialization src/serialization.cpp) add_test_cflags( - test-cpp-serialization + ${PROJECT_NAME}-test-cpp-serialization "-DTEST_SERIALIZATION_FOLDER=\\\\\"${CMAKE_CURRENT_BINARY_DIR}/serialization-data\\\\\"" ) -target_include_directories( - test-cpp-serialization SYSTEM - PRIVATE ${PROJECT_SOURCE_DIR}/external/cereal/include) +if(cereal_FOUND) + target_link_libraries(${PROJECT_NAME}-test-cpp-serialization + PRIVATE cereal::cereal) +else() + target_include_directories( + ${PROJECT_NAME}-test-cpp-serialization SYSTEM + PRIVATE ${PROJECT_SOURCE_DIR}/external/cereal/include) +endif() if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug" AND NOT MSVC) proxsuite_test(dense_maros_meszaros src/dense_maros_meszaros.cpp) @@ -83,7 +90,7 @@ if(BUILD_PYTHON_INTERFACE) foreach(TEST_FILE ${${PROJECT_NAME}_PYTHON_UNITTEST}) get_filename_component(TEST_NAME ${TEST_FILE} NAME_WE) string(REGEX REPLACE "${PROJECT_SOURCE_DIR}/" "" TEST_FILE ${TEST_FILE}) - add_python_unit_test("test-py-${TEST_NAME}" "${TEST_FILE}" + add_python_unit_test("${PROJECT_NAME}-test-py-${TEST_NAME}" "${TEST_FILE}" "bindings/python") - endforeach(TEST_FILE ${${PROJECT_NAME}_PYTHON_UNITTEST}) + endforeach() endif(BUILD_PYTHON_INTERFACE) diff --git a/test/doctest/doctest.hpp b/test/doctest/doctest.hpp index 3aa1418e0..f44173193 100644 --- a/test/doctest/doctest.hpp +++ b/test/doctest/doctest.hpp @@ -1563,8 +1563,7 @@ namespace detail { #endif // DOCTEST_CONFIG_TREAT_CHAR_STAR_AS_STRING // clang-format on -struct DOCTEST_INTERFACE TestFailureException -{}; +struct DOCTEST_INTERFACE TestFailureException{}; DOCTEST_INTERFACE bool checkIfShouldThrow(assertType::Enum at); 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]]) diff --git a/test/src/dense_qp_solve.py b/test/src/dense_qp_solve.py index d8fe30736..3920199d6 100644 --- a/test/src/dense_qp_solve.py +++ b/test/src/dense_qp_solve.py @@ -37,9 +37,10 @@ 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 - delta = np.random.rand(m) # To get inequality u = A @ v l = -1.0e20 * np.ones(m) @@ -310,12 +311,14 @@ 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.infty) + u = np.full(l.shape, +np.inf) results = proxsuite.proxqp.dense.solve(H, g, A, b, C, l, u) x_theoretically_optimal = np.array([2.0] * 149 + [3.0]) @@ -379,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.cpp b/test/src/dense_qp_wrapper.cpp index d51f8609a..353fb7dce 100644 --- a/test/src/dense_qp_wrapper.cpp +++ b/test/src/dense_qp_wrapper.cpp @@ -1,5 +1,5 @@ // -// Copyright (c) 2022 INRIA +// Copyright (c) 2022-2024 INRIA // #include #include @@ -7614,3 +7614,60 @@ TEST_CASE("ProxQP::dense: test memory allocation when estimating biggest " dense::power_iteration(H, dw, rhs, err_v, 1.E-6, 10000); PROXSUITE_EIGEN_MALLOC_ALLOWED(); } + +TEST_CASE("ProxQP::dense: sparse random strongly convex qp with" + "inequality constraints: test PrimalLDLT backend mu update") +{ + + std::cout << "---testing sparse random strongly convex qp with" + "inequality constraints: test PrimalLDLT backend mu update---" + << std::endl; + double sparsity_factor = 1; + utils::rand::set_seed(1); + isize dim = 3; + isize n_eq(0); + isize n_in(9); + T strong_convexity_factor(1.e-2); + proxqp::dense::Model qp_random = proxqp::utils::dense_strongly_convex_qp( + dim, n_eq, n_in, sparsity_factor, strong_convexity_factor); + dense::QP qp{ + dim, + n_eq, + n_in, + false, + proxsuite::proxqp::HessianType::Dense, + proxsuite::proxqp::DenseBackend::PrimalLDLT + }; // creating QP object + T eps_abs = T(1e-7); + qp.settings.eps_abs = eps_abs; + qp.settings.eps_rel = 0; + qp.settings.compute_timings = true; + qp.settings.verbose = true; + qp.init(qp_random.H, + qp_random.g, + nullopt, + nullopt, + qp_random.C, + nullopt, + qp_random.u); + qp.solve(); + + DOCTEST_CHECK(qp.results.info.mu_updates > 0); + + T pri_res = (helpers::negative_part(qp_random.C * qp.results.x - qp_random.l)) + .lpNorm(); + T dua_res = (qp_random.H * qp.results.x + qp_random.g + + qp_random.C.transpose() * qp.results.z) + .lpNorm(); + DOCTEST_CHECK(pri_res <= eps_abs); + DOCTEST_CHECK(dua_res <= eps_abs); + + std::cout << "------using API solving qp with dim: " << dim + << " neq: " << n_eq << " nin: " << n_in << std::endl; + std::cout << "primal residual: " << pri_res << std::endl; + std::cout << "dual residual: " << dua_res << std::endl; + std::cout << "total number of iteration: " << qp.results.info.iter + << std::endl; + std::cout << "setup timing " << qp.results.info.setup_time << " solve time " + << qp.results.info.solve_time << std::endl; +} diff --git a/test/src/dense_qp_wrapper.py b/test/src/dense_qp_wrapper.py index 429f5fb80..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() + 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 +69,10 @@ 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,13 +3927,13 @@ 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.infty) + u = np.full(l.shape, +np.inf) qp = proxsuite.proxqp.dense.QP(n, 0, n) qp.init(H, g, A, b, C, l, u) 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): diff --git a/test/src/serialization.py b/test/src/serialization.py index 0fd4c5770..4c03725e9 100644 --- a/test/src/serialization.py +++ b/test/src/serialization.py @@ -29,9 +29,10 @@ 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) @@ -42,7 +43,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 +53,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 @@ -63,8 +64,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) diff --git a/test/src/sparse_qp_solve.py b/test/src/sparse_qp_solve.py index a8949ec1c..f882b50e3 100644 --- a/test/src/sparse_qp_solve.py +++ b/test/src/sparse_qp_solve.py @@ -347,7 +347,7 @@ def test_sparse_problem_with_exact_solution_known(self): b = None C = spa.csc_matrix(spa.eye(n)) l = 2.0 * np.ones((n,)) - u = np.full(l.shape, +np.infty) + u = np.full(l.shape, +np.inf) results = proxsuite.proxqp.sparse.solve(H, g, A, b, C, l, u) x_theoretically_optimal = np.array([2.0] * 149 + [3.0]) diff --git a/test/src/sparse_qp_wrapper.py b/test/src/sparse_qp_wrapper.py index 5b702a36f..d9e9eae8c 100644 --- a/test/src/sparse_qp_wrapper.py +++ b/test/src/sparse_qp_wrapper.py @@ -4523,7 +4523,7 @@ def test_sparse_problem_with_exact_solution_known(self): b = None C = spa.csc_matrix(spa.eye(n)) l = 2.0 * np.ones((n,)) - u = np.full(l.shape, +np.infty) + u = np.full(l.shape, +np.inf) qp = proxsuite.proxqp.sparse.QP(n, 0, n) qp.init(H, g, A, b, C, l, u)