-
Notifications
You must be signed in to change notification settings - Fork 6
Bindings for Eigen's decompositions, matrix solvers, and geometry module, and add stubs #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
ManifoldFR
merged 111 commits into
Simple-Robotics:main
from
Lucas-Haubert:topic/decomps_geometry_solvers
Mar 28, 2025
Merged
Changes from 16 commits
Commits
Show all changes
111 commits
Select commit
Hold shift + click to select a range
0a9916c
begin to code minres
Lucas-Haubert 8f712d7
updated llt and ldlt
Lucas-Haubert 681bdcb
minres and qr decomps to debug and test
Lucas-Haubert f32859f
update
Lucas-Haubert 4e7092f
working on minres
Lucas-Haubert 6a0ce33
upgraded unit tests
Lucas-Haubert 43b6112
added decomps
Lucas-Haubert 65761ed
pre-commit: update
jcarpent 98c85ab
pre-commit: run on all files pre-commit run --all-files
jcarpent ea7f65f
git: ignore pre-commit previous commit
jcarpent dfabafc
cmake: fix project URL
jcarpent 32db75d
cmake: modernize the packaging
jcarpent a122ac3
[cmake] nanobind is a find_package(), because we are not supposed to …
ManifoldFR 42724f3
Add 'tests' subdir to gersemi defs
ManifoldFR 1480a16
[cmake] add all headers to the nanoeigenpy_HEADERS var
ManifoldFR ce673b1
Nanobind requires Eigen 3.3.1 minimum
ManifoldFR ad62e4f
Remove redundant include
ManifoldFR 20ea064
Add static_assert in SparseSolverBaseVisitor
ManifoldFR 0ac788e
Add static_assert in SimplicialCholeskyVisitor
ManifoldFR 807901b
Do not use EigenBaseVisitor in SimplicialCholeskyVisitor -- simplicia…
ManifoldFR a2c7fb2
Re-expose quaternion and angle-axis
ManifoldFR e62d5b5
Move 'decompositions/base.hpp' to 'nanoeigenpy/eigen-base.hpp'
ManifoldFR 2feea2c
Rework fwd.hpp - introduce macro to check for CXX20
ManifoldFR cc29d11
src/module.cpp : fix exposing Simplicial sparse solvers
ManifoldFR 1cb6e59
SimplicialCholesky.hpp : return decltype(auto) (hence, reference) fro…
ManifoldFR 1fdb180
[cmake] Add CONFIGURE_DEPENDS
ManifoldFR 4207912
Change typedef
ManifoldFR 21cba24
Fix angle-axis
ManifoldFR 4656b5f
Fix install dir for Python module
ManifoldFR ad472d9
EigenSolver.hpp : remove unused include, fixup some rv_policy and
ManifoldFR 755ecf6
Add Eigen version, expose SIMD instruction sets in use.
ManifoldFR bea40b9
decompositions/llt.hpp : do not define variable cl
ManifoldFR 908d82c
renamed hpp and py files with convention
Lucas-Haubert e7c98f2
Add spaces between copyright pragma once and includes
Lucas-Haubert 1824c51
Convention in the includes between brackets and quotation marks
Lucas-Haubert 8169153
Removed export values in computation info
Lucas-Haubert 037aff3
Removed compute proxy intermediate function in eigen solver
Lucas-Haubert 71a991b
Put references on return types of lambda functions
Lucas-Haubert ba56853
Added IdVistor and fwd header
Lucas-Haubert 7b9ab61
Convention templates MatrixType and others with underscore
Lucas-Haubert 7bdbbac
Added implicit conversion from AngleAxis to RotationBase
Lucas-Haubert f7ee9c9
Corrected argument type in compute and put MatrixType
Lucas-Haubert 1ae3eb7
Debuging minres with incorrect solve method
Lucas-Haubert a7a5142
Added code for solvers to debug and test
Lucas-Haubert 5e63491
Tests geometry
Lucas-Haubert 277f4e4
Merged the upstream/main for pixi
Lucas-Haubert d7caee9
Removed commentaries
Lucas-Haubert a504588
[tests] declare all Python tests
ManifoldFR 5a996d1
quaternion : expose "==" operator, test for it
ManifoldFR 498a72b
merge test_quaternion.py into test_geometry
ManifoldFR 0fa56bc
Add 'scipy' to dependencies
ManifoldFR de465ea
[decompositions] Remove unused "auto cl = "
ManifoldFR b004fb9
fwd.hpp : fix warning on gcc
ManifoldFR f678208
utils/is-approx.hpp : no need to use macro, use "static constexpr int"
ManifoldFR 7143c28
Changes minres file
Lucas-Haubert 29b6da2
nanoeigenpy.hpp : change to be an omnibus header
ManifoldFR a923753
Remove solvers/sparse-solver-base.hpp
ManifoldFR 5b61dbe
minres.hpp : do not use "EigenBase" as argument type, expose overload…
ManifoldFR 06312b7
fwd.hpp : move eigen typedef macro in there
ManifoldFR 77cc71d
Rename sparse/llt.hpp, sparse/ldlt.hpp
ManifoldFR 99413a2
src/module.cpp : slight refactoring of how solvers are exposed
ManifoldFR b647e1a
decompisitions/simplicial-llt.hpp : remove redundant include
ManifoldFR 6e2d7ae
solvers/iterative-solver-base.hpp : various enhancements
ManifoldFR dcc90d6
decompositions/sparse-solver-base.hpp : Use Eigen::Ref of dense vecto…
ManifoldFR 5a736b0
tests/test_simplicial_llt : add sparse RHS test
ManifoldFR 8712598
Rename computation-info.hpp to constants.hpp, expose enum Decompositi…
ManifoldFR 4ca4859
decompositions/SelfAdjointEigenSolver : fix the default argument!
ManifoldFR a7d8ce7
[decompositions] Removed duplicate iterative solver base visitor, fix…
ManifoldFR 165c2a6
Added and tested cholmod support
Lucas-Haubert 1565885
Add suitesparse dependency
ManifoldFR a465143
pixi : add 'cholmod' feature, set CI to use 'all' env
ManifoldFR f87fdf6
Update pixi lockfile
ManifoldFR 2e018ed
Corrected compilation for cholmod
Lucas-Haubert 9fe13ab
Remove unused typedefs and include
ManifoldFR 4782c8d
pixi.toml : properly add env value for cholmod
ManifoldFR 411337b
[tests] cmakelists.txt : simplify logic to add the cholmod Python tests
ManifoldFR 9907401
Cholmod : detect feature at compile-time at header inclusion time, no…
ManifoldFR 34ea4c4
tests/test_minres.py : also test for vector RHS
ManifoldFR 288ae11
Code for accelerate to be tested on mac
Lucas-Haubert 4a45d92
Remove more unused typedefs
ManifoldFR af021a8
tests/test_minres.py : also test minres.solveWithGuess()
ManifoldFR 5cb4247
New CMakeLists.txt to test for accelerate
Lucas-Haubert 6986955
[decompositions] some ctor changes
ManifoldFR 9bfac6b
decompositions.hpp : use correct define
ManifoldFR 4cac78d
format fwd.hpp
ManifoldFR b205cf3
Undo cholmod changes
ManifoldFR 2612c34
CMakeLists.txt : add stub generation and installation
ManifoldFR c92e8fe
solveWithGuess() is a const method, use templated proxy for solve()
ManifoldFR 63c54cb
Remove some is_final()
ManifoldFR 2f421b1
Split module.cpp
ManifoldFR 036d142
[tests] change test_minres.py to test_iterative_solvers.py -- test al…
ManifoldFR 6d79083
Use nb::DMap<...> for iterative solvers' constructors
ManifoldFR c706782
pre-commit-config : add ruff
ManifoldFR 5716740
Add pytest to pixi deps
ManifoldFR cb16656
[tests] Rewrite test_iterative_solvers using pytest
ManifoldFR 47a7f41
[tests] test_iterative_solvers: do not test x and x_est (unless initi…
ManifoldFR 7256fb4
[tests] test_iterative_solvers: increase max iters AGAIN
ManifoldFR aa94719
pre-commit : run ruff
ManifoldFR b1d4246
Updated CMakeLists.txt for Accelerate
Lucas-Haubert 202ed6a
Add workspace support
ManifoldFR 546496c
CMakeLists.txt : remove some warnings from the CXX flags
ManifoldFR 0fc351a
CMakeLists.txt : support FetchContent & CMake installation for jrl-cm…
ManifoldFR 18bf6ce
Set INSTALL_DOCUMENTATION to OFF by default
ManifoldFR 84aa1c2
Add setup_project_finalize()
ManifoldFR 81b4869
add pytest to readme
ManifoldFR 0157381
Update .gitignore
ManifoldFR 3a03dee
Tests Accelerate
Lucas-Haubert e457b3c
Minor fix to accelerate.hpp : undef the macro, mark function as inline
ManifoldFR 2632c64
Move MINRES to nanoeigenpy/solvers
ManifoldFR c1ff59c
Removed useless commentaries and prints
Lucas-Haubert 0793288
Add section on optional features (cholmod, accelerate)
ManifoldFR File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,4 @@ | ||
| definitions: [./CMakeLists.txt, ./cmake] | ||
| definitions: [./CMakeLists.txt, ./cmake, ./tests] | ||
| line_length: 80 | ||
| indent: 2 | ||
| warn_about_unknown_commands: false |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| # pre-commit run -a (format-all, 2025-03-19) | ||
| 98c85ab243b68db680a0a5f1ccbf238aeb6523ba |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| /// Copyright 2025 INRIA | ||
| #pragma once | ||
|
|
||
| #include <nanobind/nanobind.h> | ||
| #include <Eigen/Core> | ||
|
|
||
| namespace nanoeigenpy { | ||
| namespace nb = nanobind; | ||
| inline void exposeComputationInfo(nb::module_ m) { | ||
| nb::enum_<Eigen::ComputationInfo>(m, "ComputationInfo") | ||
| .value("Success", Eigen::Success, "Computation was successful.") | ||
| .value("NumericalIssue", Eigen::NumericalIssue, | ||
| "The provided data did not satisfy the prerequisites.") | ||
| .value("NoConvergence", Eigen::NoConvergence, | ||
| "Iterative procedure did not converge.") | ||
| .value("InvalidInput", Eigen::InvalidInput, | ||
| "The inputs are invalid, or the algorithm has been improperly " | ||
| "called. " | ||
| "When assertions are enabled, such errors trigger an assert.") | ||
| .export_values(); | ||
| } | ||
| } // namespace nanoeigenpy | ||
ManifoldFR marked this conversation as resolved.
Show resolved
Hide resolved
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,4 +1,14 @@ | ||
| /// Copyright 2025 INRIA | ||
| #pragma once | ||
|
|
||
| #include "nanoeigenpy/decompositions/ldlt.hpp" | ||
| #include "nanoeigenpy/decompositions/llt.hpp" | ||
| #include "nanoeigenpy/decompositions/ldlt.hpp" | ||
| #include "nanoeigenpy/decompositions/minres.hpp" | ||
| #include "nanoeigenpy/decompositions/HouseholderQR.hpp" | ||
| #include "nanoeigenpy/decompositions/FullPivHouseholderQR.hpp" | ||
| #include "nanoeigenpy/decompositions/ColPivHouseholderQR.hpp" | ||
| #include "nanoeigenpy/decompositions/CompleteOrthogonalDecomposition.hpp" | ||
| #include "nanoeigenpy/decompositions/EigenSolver.hpp" | ||
|
|
||
| #include "nanoeigenpy/decompositions/sparse/llt.hpp" | ||
| #include "nanoeigenpy/decompositions/sparse/ldlt.hpp" |
199 changes: 199 additions & 0 deletions
199
include/nanoeigenpy/decompositions/ColPivHouseholderQR.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,199 @@ | ||
| /// Copyright 2025 INRIA | ||
| #pragma once | ||
| #include "base.hpp" | ||
| #include <Eigen/QR> | ||
|
|
||
| namespace nanoeigenpy { | ||
| namespace nb = nanobind; | ||
|
|
||
| template <typename MatrixType, typename MatrixOrVector> | ||
| MatrixOrVector solve(const Eigen::ColPivHouseholderQR<MatrixType> &c, | ||
| const MatrixOrVector &vec) { | ||
| return c.solve(vec); | ||
| } | ||
|
|
||
| template <typename MatrixType> | ||
| MatrixType inverse(const Eigen::ColPivHouseholderQR<MatrixType> &c) { | ||
| return c.inverse(); | ||
| } | ||
|
|
||
| template <typename MatrixType> | ||
| void exposeColPivHouseholderQRSolver(nb::module_ m, const char *name) { | ||
| using Solver = Eigen::ColPivHouseholderQR<MatrixType>; | ||
| using Scalar = typename MatrixType::Scalar; | ||
| using RealScalar = typename MatrixType::RealScalar; | ||
| using VectorType = Eigen::Matrix<Scalar, -1, 1>; | ||
| auto cl = | ||
| nb::class_<Solver>( | ||
| m, name, | ||
| "This class performs a rank-revealing QR decomposition of a matrix A " | ||
| "into matrices P, Q and R such that:\n" | ||
| "AP=QR\n" | ||
| "by using Householder transformations. Here, P is a permutation " | ||
| "matrix, Q a unitary matrix and R an upper triangular matrix.\n" | ||
| "\n" | ||
| "This decomposition performs column pivoting in order to be " | ||
| "rank-revealing and improve numerical stability. It is slower than " | ||
| "HouseholderQR, and faster than FullPivHouseholderQR.") | ||
|
|
||
| .def(nb::init<>(), | ||
| "Default constructor.\n" | ||
| "The default constructor is useful in cases in which the " | ||
| "user intends to perform decompositions via " | ||
| "HouseholderQR.compute(matrix).") | ||
| .def(nb::init<Eigen::DenseIndex, Eigen::DenseIndex>(), | ||
| nb::arg("rows"), nb::arg("cols"), | ||
| "Default constructor with memory preallocation.\n" | ||
| "Like the default constructor but with preallocation of the " | ||
| "internal data according to the specified problem size. ") | ||
| .def(nb::init<const MatrixType &>(), nb::arg("matrix"), | ||
| "Constructs a QR factorization from a given matrix.\n" | ||
| "This constructor computes the QR factorization of the matrix " | ||
| "matrix by calling the method compute().") | ||
|
|
||
| .def("info", &Solver::info, | ||
| "Reports whether the QR factorization was successful.\n" | ||
| "Note: This function always returns Success. It is provided for " | ||
| "compatibility with other factorization routines.") | ||
|
|
||
| .def("absDeterminant", &Solver::absDeterminant, | ||
| "Returns the absolute value of the determinant of the matrix of " | ||
| "which *this is the QR decomposition.\n" | ||
| "It has only linear complexity (that is, O(n) where n is the " | ||
| "dimension of the square matrix) as the QR decomposition has " | ||
| "already been computed.\n" | ||
| "Note: This is only for square matrices.") | ||
| .def("logAbsDeterminant", &Solver::logAbsDeterminant, | ||
| "Returns the natural log of the absolute value of the " | ||
| "determinant " | ||
| "of the matrix of which *this is the QR decomposition.\n" | ||
| "It has only linear complexity (that is, O(n) where n is the " | ||
| "dimension of the square matrix) as the QR decomposition has " | ||
| "already been computed.\n" | ||
| "Note: This is only for square matrices. This method is useful " | ||
| "to " | ||
| "work around the risk of overflow/underflow that's inherent to " | ||
| "determinant computation.") | ||
| .def("dimensionOfKernel", &Solver::dimensionOfKernel, | ||
| "Returns the dimension of the kernel of the matrix of which " | ||
| "*this " | ||
| "is the QR decomposition.") | ||
| .def("isInjective", &Solver::isInjective, | ||
| "Returns true if the matrix associated with this QR " | ||
| "decomposition " | ||
| "represents an injective linear map, i.e. has trivial kernel; " | ||
| "false otherwise.\n" | ||
| "\n" | ||
| "Note: This method has to determine which pivots should be " | ||
| "considered nonzero. For that, it uses the threshold value that " | ||
| "you can control by calling setThreshold(threshold).") | ||
| .def( | ||
| "isInvertible", &Solver::isInvertible, | ||
| "Returns true if the matrix associated with the QR decomposition " | ||
| "is invertible.\n" | ||
| "\n" | ||
| "Note: This method has to determine which pivots should be " | ||
| "considered nonzero. For that, it uses the threshold value that " | ||
| "you can control by calling setThreshold(threshold).") | ||
| .def("isSurjective", &Solver::isSurjective, | ||
| "Returns true if the matrix associated with this QR " | ||
| "decomposition " | ||
| "represents a surjective linear map; false otherwise.\n" | ||
| "\n" | ||
| "Note: This method has to determine which pivots should be " | ||
| "considered nonzero. For that, it uses the threshold value that " | ||
| "you can control by calling setThreshold(threshold).") | ||
| .def("maxPivot", &Solver::maxPivot, | ||
| "Returns the absolute value of the biggest pivot, i.e. the " | ||
| "biggest diagonal coefficient of U.") | ||
| .def( | ||
| "nonzeroPivots", &Solver::nonzeroPivots, | ||
| "Returns the number of nonzero pivots in the QR decomposition. " | ||
| "Here nonzero is meant in the exact sense, not in a fuzzy sense. " | ||
| "So that notion isn't really intrinsically interesting, but it " | ||
| "is " | ||
| "still useful when implementing algorithms.") | ||
| .def("rank", &Solver::rank, | ||
| "Returns the rank of the matrix associated with the QR " | ||
| "decomposition.\n" | ||
| "\n" | ||
| "Note: This method has to determine which pivots should be " | ||
| "considered nonzero. For that, it uses the threshold value that " | ||
| "you can control by calling setThreshold(threshold).") | ||
|
|
||
| .def( | ||
| "setThreshold", | ||
| [](Solver &c, RealScalar const &threshold) { | ||
| return c.setThreshold(threshold); | ||
| }, | ||
| nb::arg("threshold"), | ||
| "Allows to prescribe a threshold to be used by certain methods, " | ||
| "such as rank(), who need to determine when pivots are to be " | ||
| "considered nonzero. This is not used for the QR decomposition " | ||
| "itself.\n" | ||
| "\n" | ||
| "When it needs to get the threshold value, Eigen calls " | ||
| "threshold(). By default, this uses a formula to automatically " | ||
| "determine a reasonable threshold. Once you have called the " | ||
| "present method setThreshold(const RealScalar&), your value is " | ||
| "used instead.\n" | ||
| "\n" | ||
| "Note: A pivot will be considered nonzero if its absolute value " | ||
| "is strictly greater than |pivot| ⩽ threshold×|maxpivot| where " | ||
| "maxpivot is the biggest pivot.", | ||
| nb::rv_policy::reference) | ||
| .def( | ||
| "threshold", &Solver::threshold, | ||
| "Returns the threshold that will be used by certain methods such " | ||
| "as rank().") | ||
|
|
||
| .def("matrixQR", &Solver::matrixQR, | ||
| "Returns the matrix where the Householder QR decomposition is " | ||
| "stored in a LAPACK-compatible way.", | ||
| nb::rv_policy::copy) | ||
| .def("matrixR", &Solver::matrixR, | ||
| "Returns the matrix where the result Householder QR is stored.", | ||
| nb::rv_policy::copy) | ||
|
|
||
| .def( | ||
| "compute", | ||
| [](Solver &c, VectorType const &matrix) { | ||
| return c.compute(matrix); | ||
| }, | ||
| nb::arg("matrix"), | ||
| "Computes the QR factorization of given matrix.", | ||
| nb::rv_policy::reference) | ||
|
|
||
| .def( | ||
| "inverse", | ||
| [](Solver const &c) -> MatrixType { return inverse(c); }, | ||
| "Returns the inverse of the matrix associated with the QR " | ||
| "decomposition.") | ||
|
|
||
| .def( | ||
| "solve", | ||
| [](Solver const &c, VectorType const &b) -> VectorType { | ||
| return solve(c, b); | ||
| }, | ||
| nb::arg("b"), | ||
| "Returns the solution x of A x = B using the current " | ||
| "decomposition of A where b is a right hand side vector.") | ||
| .def( | ||
| "solve", | ||
| [](Solver const &c, MatrixType const &B) -> MatrixType { | ||
| return solve(c, B); | ||
| }, | ||
| nb::arg("B"), | ||
| "Returns the solution X of A X = B using the current " | ||
| "decomposition of A where B is a right hand side matrix.") | ||
|
|
||
| .def( | ||
| "id", | ||
| [](Solver const &c) -> int64_t { | ||
| return reinterpret_cast<int64_t>(&c); | ||
| }, | ||
| "Returns the unique identity of an object.\n" | ||
| "For objects held in C++, it corresponds to its memory address."); | ||
| } | ||
|
|
||
| } // namespace nanoeigenpy |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.