Skip to content

Commit db6b78e

Browse files
authored
Merge pull request #1 from Lucas-Haubert/topic/decomps_geometry_solvers
Bindings for Eigen's decompositions, matrix solvers, and geometry module, and add stubs
2 parents c758dcc + 0793288 commit db6b78e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+5841
-435
lines changed

.gersemirc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
definitions: [./CMakeLists.txt, ./cmake]
1+
definitions: [./CMakeLists.txt, ./cmake, ./tests]
22
line_length: 80
33
indent: 2
44
warn_about_unknown_commands: false

.git-blame-ignore-revs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# pre-commit run -a (format-all, 2025-03-19)
2+
98c85ab243b68db680a0a5f1ccbf238aeb6523ba

.github/workflows/macos-linux-windows-pixi.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ jobs:
4141
fail-fast: false
4242
matrix:
4343
os: [ubuntu-latest, macos-latest, macos-13, windows-latest]
44-
environment: [default, python-oldest]
44+
environment: [all, all-python-oldest]
4545
build_type: [Release, Debug]
4646

4747
steps:

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
build*/
22
.cache/
3+
__pycache__
34

45
# pixi environments
56
.pixi

.pre-commit-config.yaml

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ ci:
55
submodules: true
66
repos:
77
- repo: https://github.com/pre-commit/mirrors-clang-format
8-
rev: v19.1.7
8+
rev: v20.1.0
99
hooks:
1010
- id: clang-format
1111
types_or: []
@@ -27,8 +27,17 @@ repos:
2727
(?x)^(
2828
doc/doxygen-awesome.*
2929
)$
30+
- repo: https://github.com/astral-sh/ruff-pre-commit
31+
# Ruff version.
32+
rev: v0.11.2
33+
hooks:
34+
# Run the linter.
35+
- id: ruff
36+
args: [ --fix ]
37+
# Run the formatter.
38+
- id: ruff-format
3039
- repo: https://github.com/BlankSpruce/gersemi
31-
rev: 0.19.0
40+
rev: 0.19.2
3241
hooks:
3342
- id: gersemi
3443
- repo: https://github.com/Lucas-C/pre-commit-hooks

CMakeLists.txt

Lines changed: 194 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,77 @@
1+
#
2+
# Copyright 2025 INRIA
3+
#
4+
15
cmake_minimum_required(VERSION 3.18)
26

37
set(PROJECT_NAME nanoeigenpy)
4-
set(PROJECT_URL https://github.com/ManifoldFR/nanoeigenpy)
8+
set(PROJECT_URL https://github.com/Simple-Robotics/nanoeigenpy)
59
set(PROJECT_DESCRIPTION "Tools for using Eigen with nanobind")
610
set(PROJECT_CUSTOM_HEADER_EXTENSION "hpp")
711
set(PROJECT_USE_CMAKE_EXPORT True)
812

9-
include(cmake/base.cmake)
13+
# To enable jrl-cmakemodules compatibility with workspace we must define the two
14+
# following lines
15+
set(PROJECT_AUTO_RUN_FINALIZE FALSE)
16+
set(PROJECT_SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR})
17+
18+
# Check if the submodule cmake have been initialized
19+
set(JRL_CMAKE_MODULES "${CMAKE_CURRENT_LIST_DIR}/cmake")
20+
if(EXISTS "${JRL_CMAKE_MODULES}/base.cmake")
21+
message(STATUS "JRL cmakemodules found in 'cmake/' git submodule")
22+
else()
23+
find_package(jrl-cmakemodules QUIET CONFIG)
24+
if(jrl-cmakemodules_FOUND)
25+
get_property(
26+
JRL_CMAKE_MODULES
27+
TARGET jrl-cmakemodules::jrl-cmakemodules
28+
PROPERTY INTERFACE_INCLUDE_DIRECTORIES
29+
)
30+
message(STATUS "JRL cmakemodules found on system at ${JRL_CMAKE_MODULES}")
31+
elseif(${CMAKE_VERSION} VERSION_LESS "3.14.0")
32+
message(
33+
FATAL_ERROR
34+
"\nCan't find jrl-cmakemodules. Please either:\n"
35+
" - use git submodule: 'git submodule update --init'\n"
36+
" - or install https://github.com/jrl-umi3218/jrl-cmakemodules\n"
37+
" - or upgrade your CMake version to >= 3.14 to allow automatic fetching\n"
38+
)
39+
else()
40+
message(STATUS "JRL cmakemodules not found. Let's fetch it.")
41+
include(FetchContent)
42+
FetchContent_Declare(
43+
"jrl-cmakemodules"
44+
GIT_REPOSITORY "https://github.com/jrl-umi3218/jrl-cmakemodules.git"
45+
)
46+
FetchContent_MakeAvailable("jrl-cmakemodules")
47+
FetchContent_GetProperties("jrl-cmakemodules" SOURCE_DIR JRL_CMAKE_MODULES)
48+
endif()
49+
endif()
50+
51+
option(INSTALL_DOCUMENTATION "Generate and install the documentation" OFF)
1052

53+
if(POLICY CMP0167)
54+
cmake_policy(SET CMP0167 NEW)
55+
set(CMAKE_POLICY_DEFAULT_CMP0167 NEW)
56+
endif()
57+
if(POLICY CMP0177)
58+
cmake_policy(SET CMP0177 NEW)
59+
set(CMAKE_POLICY_DEFAULT_CMP0177 NEW)
60+
endif()
61+
include("${JRL_CMAKE_MODULES}/base.cmake")
1162
COMPUTE_PROJECT_ARGS(PROJECT_ARGS LANGUAGES CXX)
63+
include("${JRL_CMAKE_MODULES}/ide.cmake")
64+
include("${JRL_CMAKE_MODULES}/apple.cmake")
1265
project(${PROJECT_NAME} ${PROJECT_ARGS})
1366

67+
string(REPLACE "-pedantic" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
68+
string(REPLACE "-Wcast-qual" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
69+
string(REPLACE "-Wconversion" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
70+
1471
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/bin)
1572
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
1673
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/lib)
1774

18-
find_package(Eigen3 REQUIRED)
19-
20-
add_library(nanoeigenpy_headers INTERFACE)
21-
target_link_libraries(nanoeigenpy_headers INTERFACE Eigen3::Eigen)
22-
23-
find_package(Python REQUIRED COMPONENTS Interpreter Development)
24-
2575
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
2676
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
2777
set_property(
@@ -30,6 +80,25 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
3080
)
3181
endif()
3282

83+
option(
84+
BUILD_WITH_CHOLMOD_SUPPORT
85+
"Build NanoEigenPy with the Cholmod support"
86+
OFF
87+
)
88+
89+
if(APPLE)
90+
option(
91+
BUILD_WITH_ACCELERATE_SUPPORT
92+
"Build EigenPy with the Accelerate support"
93+
OFF
94+
)
95+
endif(APPLE)
96+
97+
# Find dependencies
98+
ADD_PROJECT_DEPENDENCY(Eigen3 REQUIRED PKG_CONFIG_REQUIRES "eigen3 >= 3.3.1")
99+
100+
find_package(Python REQUIRED COMPONENTS Interpreter Development)
101+
33102
# Detect the installed nanobind package and import it into CMake
34103
execute_process(
35104
COMMAND "${Python_EXECUTABLE}" -m nanobind --cmake_dir
@@ -38,29 +107,133 @@ execute_process(
38107
)
39108
find_package(nanobind CONFIG REQUIRED)
40109

41-
file(GLOB nanoeigenpy_SOURCES src/*.cpp)
42-
nanobind_add_module(nanoeigenpy NB_STATIC NB_SUPPRESS_WARNINGS ${nanoeigenpy_SOURCES})
43-
target_link_libraries(nanoeigenpy PUBLIC nanoeigenpy_headers)
110+
# Setup main targets
111+
file(
112+
GLOB_RECURSE ${PROJECT_NAME}_HEADERS
113+
CONFIGURE_DEPENDS
114+
include/nanoeigenpy/*.hpp
115+
)
116+
117+
add_library(nanoeigenpy_headers INTERFACE)
118+
target_include_directories(
119+
nanoeigenpy_headers
120+
INTERFACE
121+
$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>
122+
$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>
123+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
124+
)
125+
target_link_libraries(nanoeigenpy_headers INTERFACE Eigen3::Eigen)
126+
127+
set(${PROJECT_NAME}_SOURCES src/module.cpp src/solvers.cpp)
128+
nanobind_add_module(nanoeigenpy NB_STATIC NB_SUPPRESS_WARNINGS ${nanoeigenpy_SOURCES} ${nanoeigenpy_HEADERS})
129+
target_link_libraries(nanoeigenpy PRIVATE nanoeigenpy_headers)
130+
131+
# Cholmod
132+
if(BUILD_WITH_CHOLMOD_SUPPORT)
133+
set(
134+
CMAKE_MODULE_PATH
135+
${JRL_CMAKE_MODULES}/find-external/CHOLMOD
136+
${CMAKE_MODULE_PATH}
137+
)
138+
ADD_PROJECT_DEPENDENCY(CHOLMOD REQUIRED FIND_EXTERNAL "CHOLMOD")
139+
message(
140+
STATUS
141+
"Build with CHOLMOD support (LGPL). See CHOLMOD/Doc/License.txt for further details."
142+
)
143+
file(
144+
GLOB ${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_CHOLMOD_HEADERS
145+
include/nanoeigenpy/decompositions/sparse/cholmod/*.hpp
146+
)
147+
list(
148+
APPEND
149+
${PROJECT_NAME}_HEADERS
150+
${${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_CHOLMOD_HEADERS}
151+
)
152+
target_link_libraries(nanoeigenpy PRIVATE CHOLMOD::CHOLMOD)
153+
else()
154+
list(
155+
FILTER ${PROJECT_NAME}_HEADERS
156+
EXCLUDE
157+
REGEX "include/nanoeigenpy/decompositions/sparse/cholmod/.*"
158+
)
159+
endif(BUILD_WITH_CHOLMOD_SUPPORT)
160+
161+
# Apple accelerate
162+
if(BUILD_WITH_ACCELERATE_SUPPORT)
163+
if(NOT ${Eigen3_VERSION} VERSION_GREATER_EQUAL "3.4.90")
164+
message(
165+
FATAL_ERROR
166+
"Your version of Eigen is too low. Should be at least 3.4.90. Current version is ${Eigen3_VERSION}."
167+
)
168+
endif()
169+
170+
set(
171+
CMAKE_MODULE_PATH
172+
${JRL_CMAKE_MODULES}/find-external/Accelerate
173+
${CMAKE_MODULE_PATH}
174+
)
175+
find_package(Accelerate REQUIRED)
176+
message(STATUS "Build with Accelerate support framework.")
177+
target_compile_definitions(
178+
nanoeigenpy_headers
179+
INTERFACE -DNANOEIGENPY_WITH_ACCELERATE_SUPPORT
180+
)
181+
endif(BUILD_WITH_ACCELERATE_SUPPORT)
182+
183+
if(BUILD_WITH_ACCELERATE_SUPPORT)
184+
file(
185+
GLOB ${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_ACCELERATE_HEADERS
186+
include/nanoeigenpy/decompositions/sparse/accelerate/*.hpp
187+
)
188+
list(
189+
APPEND
190+
${PROJECT_NAME}_HEADERS
191+
${${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_ACCELERATE_HEADERS}
192+
)
193+
else()
194+
list(
195+
FILTER ${PROJECT_NAME}_HEADERS
196+
EXCLUDE
197+
REGEX "include/nanoeigenpy/decompositions/sparse/accelerate/.*"
198+
)
199+
endif(BUILD_WITH_ACCELERATE_SUPPORT)
200+
201+
if(BUILD_WITH_ACCELERATE_SUPPORT)
202+
target_link_libraries(nanoeigenpy PRIVATE Accelerate)
203+
endif()
44204

45205
if(BUILD_TESTING)
46206
add_subdirectory(tests)
47207
endif()
48208

49-
install(
50-
TARGETS nanoeigenpy_headers
51-
EXPORT ${TARGETS_EXPORT_NAME}
52-
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
209+
nanobind_add_stub(
210+
nanoeigenpy_stub
211+
INSTALL_TIME
212+
VERBOSE
213+
MODULE nanoeigenpy
214+
OUTPUT nanoeigenpy.pyi
215+
PYTHON_PATH $<TARGET_FILE_DIR:nanoeigenpy>
216+
DEPENDS nanoeigenpy
53217
)
54218

219+
# Install targets
55220
install(
56-
DIRECTORY include/nanoeigenpy
57-
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
58-
FILES_MATCHING
59-
PATTERN "*.hpp"
221+
TARGETS ${PROJECT_NAME}_headers
222+
EXPORT ${TARGETS_EXPORT_NAME}
223+
PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
224+
INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
225+
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
226+
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
227+
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
60228
)
61229

62230
install(
63-
TARGETS nanoeigenpy
231+
TARGETS ${PROJECT_NAME}
64232
EXPORT ${TARGETS_EXPORT_NAME}
65233
LIBRARY DESTINATION ${Python_SITELIB}
66234
)
235+
236+
ADD_HEADER_GROUP(${PROJECT_NAME}_HEADERS)
237+
ADD_SOURCE_GROUP(${PROJECT_NAME}_SOURCES)
238+
239+
SETUP_PROJECT_FINALIZE()

README.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# nanoeigenpy
22

3+
<p align="left">
4+
<a href="https://github.com/astral-sh/ruff"><img src="https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json" alt="Linter: ruff"></a>
5+
</p>
6+
37
This is a collection of tools for using Eigen together with nanobind, as a successor to the [eigenpy](https://github.com/stack-of-tasks/eigenpy) support library. Its aim is to help the transition away from Boost.Python.
48

59
It reintroduces a few features (e.g. bindings for Eigen matrix decompositions) which are not in [nanobind](https://github.com/wjakob/nanobind) at time of writing.
@@ -18,6 +22,16 @@ These features were finally added to eigenpy with a lot of developer effort. Thi
1822
- bindings for Eigen's [Geometry module](https://libeigen.gitlab.io/docs/group__Geometry__Module.html) - quaternions, angle-axis representations...
1923
- bindings for Eigen's matrix dense and sparse decompositions and solvers
2024

25+
### Optional features
26+
27+
**nanoeigenpy** also provides bindings for Eigen's [Cholmod](https://eigen.tuxfamily.org/dox/group__CholmodSupport__Module.html) and [Apple Accelerate](https://eigen.tuxfamily.org/dox/group__AccelerateSupport__Module.html) modules.
28+
29+
> [!NOTE]
30+
> The Accelerate module is only available on Eigen's master branch, and not in any official release yet (as of 3/28/2025).
31+
>
32+
> Cholmod is part of the [SuiteSparse](https://github.com/DrTimothyAldenDavis/SuiteSparse) algorithms library. It can be installed standalone from [conda](https://anaconda.org/conda-forge/libcholmod).
33+
34+
2135
## Example usage
2236

2337
The features included in **nanoeigenpy** are distributed in a Python module which can be imported, or through standalone headers which can be included in your own Python bindings code using a CMake target.
@@ -92,6 +106,7 @@ f(quat)
92106

93107
- the Eigen C++ template library - [conda-forge](https://anaconda.org/conda-forge/eigen) | [repo](https://gitlab.com/libeigen/eigen/)
94108
- nanobind - [conda-forge](https://anaconda.org/conda-forge/nanobind) | [repo](https://github.com/wjakob/nanobind)
109+
- [for testing] pytest - `conda install pytest` or `pip install pytest`
95110

96111
#### Conda
97112

development/scripts/pixi/activation.bat

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ set CMAKE_COLOR_DIAGNOSTICS=1
99

1010
:: Set default build value only if not previously set
1111
if not defined NANOEIGENPY_BUILD_TYPE (set NANOEIGENPY_BUILD_TYPE=Release)
12+
if not defined NANOEIGENPY_BUILD_WITH_CHOLMOD (set NANOEIGENPY_BUILD_WITH_CHOLMOD=OFF)

development/scripts/pixi/activation.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,4 @@ export CMAKE_COLOR_DIAGNOSTICS=1
3333

3434
# Set default build value only if not previously set
3535
export NANOEIGENPY_BUILD_TYPE=${NANOEIGENPY_BUILD_TYPE:=Release}
36+
export NANOEIGENPY_BUILD_WITH_CHOLMOD=${NANOEIGENPY_BUILD_WITH_CHOLMOD:=OFF}

0 commit comments

Comments
 (0)