Skip to content

Commit 55fe15d

Browse files
authored
Merge pull request #571 from Lucas-Haubert/topic/linear_algebra
Linear algebra: Expose the remaining classes listed in Eigen documentation (decompositions and solvers)
2 parents b3eeec4 + e08c704 commit 55fe15d

File tree

87 files changed

+4214
-473
lines changed

Some content is hidden

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

87 files changed

+4214
-473
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88

99
### Added
1010

11+
- Add additional decompositions and solvers from Eigen ([#571](https://github.com/stack-of-tasks/eigenpy/pull/571))
12+
13+
### Added
14+
1115
- Docker images `ghcr.io/stack-of-tasks/eigenpy` ([#575](https://github.com/stack-of-tasks/eigenpy/pull/575))
1216

1317
### Changed

CMakeLists.txt

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -190,10 +190,14 @@ set(${PROJECT_NAME}_SOLVERS_HEADERS
190190
include/eigenpy/solvers/preconditioners.hpp
191191
include/eigenpy/solvers/IterativeSolverBase.hpp
192192
include/eigenpy/solvers/LeastSquaresConjugateGradient.hpp
193+
include/eigenpy/solvers/BiCGSTAB.hpp
194+
include/eigenpy/solvers/MINRES.hpp
193195
include/eigenpy/solvers/ConjugateGradient.hpp
194196
include/eigenpy/solvers/SparseSolverBase.hpp
195197
include/eigenpy/solvers/BasicPreconditioners.hpp
196-
include/eigenpy/solvers/BFGSPreconditioners.hpp)
198+
include/eigenpy/solvers/BFGSPreconditioners.hpp
199+
include/eigenpy/solvers/IncompleteCholesky.hpp
200+
include/eigenpy/solvers/IncompleteLUT.hpp)
197201

198202
set(${PROJECT_NAME}_EIGEN_HEADERS include/eigenpy/eigen/EigenBase.hpp)
199203

@@ -205,13 +209,18 @@ set(${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_CHOLMOD_HEADERS
205209
include/eigenpy/decompositions/sparse/cholmod/CholmodSupernodalLLT.hpp)
206210

207211
set(${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_ACCELERATE_HEADERS
212+
include/eigenpy/decompositions/sparse/accelerate/Accelerate.hpp
208213
include/eigenpy/decompositions/sparse/accelerate/accelerate.hpp)
209214

210215
set(${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_HEADERS
211-
include/eigenpy/decompositions/sparse/LLT.hpp
212-
include/eigenpy/decompositions/sparse/LDLT.hpp
216+
include/eigenpy/decompositions/sparse/SimplicialLLT.hpp
217+
include/eigenpy/decompositions/sparse/SimplicialLDLT.hpp
218+
include/eigenpy/decompositions/sparse/SparseLU.hpp
219+
include/eigenpy/decompositions/sparse/SparseQR.hpp
213220
include/eigenpy/decompositions/sparse/SimplicialCholesky.hpp
214-
include/eigenpy/decompositions/sparse/SparseSolverBase.hpp)
221+
include/eigenpy/decompositions/sparse/SparseSolverBase.hpp
222+
include/eigenpy/decompositions/sparse/LDLT.hpp
223+
include/eigenpy/decompositions/sparse/LLT.hpp)
215224

216225
if(BUILD_WITH_CHOLMOD_SUPPORT)
217226
list(APPEND ${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_HEADERS
@@ -227,6 +236,16 @@ set(${PROJECT_NAME}_DECOMPOSITIONS_HEADERS
227236
${${PROJECT_NAME}_DECOMPOSITIONS_SPARSE_HEADERS}
228237
include/eigenpy/decompositions/decompositions.hpp
229238
include/eigenpy/decompositions/EigenSolver.hpp
239+
include/eigenpy/decompositions/GeneralizedEigenSolver.hpp
240+
include/eigenpy/decompositions/GeneralizedSelfAdjointEigenSolver.hpp
241+
include/eigenpy/decompositions/HessenbergDecomposition.hpp
242+
include/eigenpy/decompositions/RealQZ.hpp
243+
include/eigenpy/decompositions/Tridiagonalization.hpp
244+
include/eigenpy/decompositions/RealSchur.hpp
245+
include/eigenpy/decompositions/ComplexEigenSolver.hpp
246+
include/eigenpy/decompositions/ComplexSchur.hpp
247+
include/eigenpy/decompositions/FullPivLU.hpp
248+
include/eigenpy/decompositions/PartialPivLU.hpp
230249
include/eigenpy/decompositions/PermutationMatrix.hpp
231250
include/eigenpy/decompositions/LDLT.hpp
232251
include/eigenpy/decompositions/LLT.hpp
@@ -236,6 +255,9 @@ set(${PROJECT_NAME}_DECOMPOSITIONS_HEADERS
236255
include/eigenpy/decompositions/CompleteOrthogonalDecomposition.hpp
237256
include/eigenpy/decompositions/FullPivHouseholderQR.hpp
238257
include/eigenpy/decompositions/SelfAdjointEigenSolver.hpp
258+
include/eigenpy/decompositions/SVDBase.hpp
259+
include/eigenpy/decompositions/BDCSVD.hpp
260+
include/eigenpy/decompositions/JacobiSVD.hpp
239261
include/eigenpy/decompositions/minres.hpp)
240262

241263
set(${PROJECT_NAME}_HEADERS
@@ -300,17 +322,36 @@ list(
300322
# ----------------------------------------------------
301323
# --- TARGETS ----------------------------------------
302324
# ----------------------------------------------------
303-
set(${PROJECT_NAME}_SOLVERS_SOURCES src/solvers/preconditioners.cpp
304-
src/solvers/solvers.cpp)
325+
set(${PROJECT_NAME}_SOLVERS_SOURCES
326+
src/solvers/preconditioners.cpp
327+
src/solvers/solvers.cpp
328+
src/solvers/minres.cpp
329+
src/solvers/bicgstab.cpp
330+
src/solvers/conjugate-gradient.cpp
331+
src/solvers/least-squares-conjugate-gradient.cpp
332+
src/solvers/incomplete-cholesky.cpp
333+
src/solvers/incomplete-lut.cpp)
305334

306335
set(${PROJECT_NAME}_DECOMPOSITIONS_SOURCES
307336
src/decompositions/decompositions.cpp
308337
src/decompositions/eigen-solver.cpp
338+
src/decompositions/generalized-eigen-solver.cpp
339+
src/decompositions/generalized-self-adjoint-eigen-solver.cpp
340+
src/decompositions/complex-eigen-solver.cpp
341+
src/decompositions/complex-schur.cpp
309342
src/decompositions/llt-solver.cpp
310343
src/decompositions/ldlt-solver.cpp
311-
src/decompositions/minres-solver.cpp
344+
src/decompositions/bdcsvd-solver.cpp
345+
src/decompositions/jacobisvd-solver.cpp
346+
src/decompositions/fullpivlu-solver.cpp
347+
src/decompositions/hessenberg-decomposition.cpp
348+
src/decompositions/real-qz.cpp
349+
src/decompositions/tridiagonalization.cpp
350+
src/decompositions/real-schur.cpp
351+
src/decompositions/partialpivlu-solver.cpp
352+
src/decompositions/sparse-lu-solver.cpp
353+
src/decompositions/sparse-qr-solver.cpp
312354
src/decompositions/qr-solvers.cpp
313-
src/decompositions/eigen-solver.cpp
314355
src/decompositions/self-adjoint-eigen-solver.cpp
315356
src/decompositions/permutation-matrix.cpp
316357
src/decompositions/simplicial-llt-solver.cpp

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ EigenPy — Versatile and efficient Python bindings between Numpy and Eigen
1919
- full support Eigen::Ref avoiding memory allocation
2020
- full support of the Eigen::Tensor module
2121
- exposition of the Geometry module of Eigen for easy code prototyping
22-
- standard matrix decomposion routines of Eigen such as the Cholesky decomposition (SVD and QR decompositions [can be added](#contributing))
22+
- standard matrix decomposion routines of Eigen such as the Cholesky, SVD and QR decompositions
2323
- full support of SWIG objects
2424
- full support of runtime declaration of Numpy scalar types
2525
- extended API to expose several STL types and some of their Boost equivalents: `optional` types, `std::pair`, maps, variants...
@@ -100,6 +100,7 @@ The following people have been involved in the development of **EigenPy**:
100100
- [Loïc Estève](https://github.com/lesteve) (Inria): Conda integration
101101
- [Wilson Jallet](https://manifoldfr.github.io/) (Inria): core developer
102102
- [Joris Vaillant](https://github.com/jorisv) (Inria): core developer and manager of the project
103+
- [Lucas Haubert](https://www.linkedin.com/in/lucas-haubert-b668a421a/) (Inria): core developer
103104

104105
If you have taken part in the development of **EigenPy**, feel free to add your name and contribution here.
105106

flake.nix

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,29 @@
2020
devShells.default = pkgs.mkShell { inputsFrom = [ self'.packages.default ]; };
2121
packages = {
2222
default = self'.packages.eigenpy;
23-
eigenpy = pkgs.python3Packages.eigenpy.overrideAttrs (_: {
24-
src = pkgs.lib.fileset.toSource {
25-
root = ./.;
26-
fileset = pkgs.lib.fileset.unions [
27-
./CMakeLists.txt
28-
./doc
29-
./include
30-
./package.xml
31-
./python
32-
./src
33-
./unittest
34-
];
35-
};
36-
});
23+
eigen = pkgs.eigen.overrideAttrs {
24+
# Apply https://gitlab.com/libeigen/eigen/-/merge_requests/977
25+
postPatch = ''
26+
substituteInPlace Eigen/src/SVD/BDCSVD.h \
27+
--replace-fail "if (l == 0) {" "if (i >= k && l == 0) {"
28+
'';
29+
};
30+
eigenpy =
31+
(pkgs.python3Packages.eigenpy.override { inherit (self'.packages) eigen; }).overrideAttrs
32+
(_: {
33+
src = pkgs.lib.fileset.toSource {
34+
root = ./.;
35+
fileset = pkgs.lib.fileset.unions [
36+
./CMakeLists.txt
37+
./doc
38+
./include
39+
./package.xml
40+
./python
41+
./src
42+
./unittest
43+
];
44+
};
45+
});
3746
};
3847
};
3948
};
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*
2+
* Copyright 2025 INRIA
3+
*/
4+
5+
#ifndef __eigenpy_decompositions_bdcsvd_hpp__
6+
#define __eigenpy_decompositions_bdcsvd_hpp__
7+
8+
#include <Eigen/SVD>
9+
#include <Eigen/Core>
10+
11+
#include "eigenpy/eigenpy.hpp"
12+
#include "eigenpy/utils/scalar-name.hpp"
13+
#include "eigenpy/eigen/EigenBase.hpp"
14+
#include "eigenpy/decompositions/SVDBase.hpp"
15+
16+
namespace eigenpy {
17+
18+
template <typename _MatrixType>
19+
struct BDCSVDVisitor
20+
: public boost::python::def_visitor<BDCSVDVisitor<_MatrixType>> {
21+
typedef _MatrixType MatrixType;
22+
typedef Eigen::BDCSVD<MatrixType> Solver;
23+
typedef typename MatrixType::Scalar Scalar;
24+
25+
template <class PyClass>
26+
void visit(PyClass &cl) const {
27+
cl.def(bp::init<>(bp::arg("self"), "Default constructor"))
28+
.def(bp::init<Eigen::DenseIndex, Eigen::DenseIndex,
29+
bp::optional<unsigned int>>(
30+
bp::args("self", "rows", "cols", "computationOptions "),
31+
"Default Constructor with memory preallocation. "))
32+
.def(bp::init<MatrixType, bp::optional<unsigned int>>(
33+
bp::args("self", "matrix", "computationOptions "),
34+
"Constructor performing the decomposition of given matrix."))
35+
36+
.def("cols", &Solver::cols, bp::arg("self"),
37+
"Returns the number of columns. ")
38+
.def("compute",
39+
(Solver & (Solver::*)(const MatrixType &matrix)) & Solver::compute,
40+
bp::args("self", "matrix"),
41+
"Method performing the decomposition of given matrix. Computes "
42+
"Thin/Full "
43+
"unitaries U/V if specified using the Options template parameter "
44+
"or the class constructor. ",
45+
bp::return_self<>())
46+
.def("compute",
47+
(Solver & (Solver::*)(const MatrixType &matrix,
48+
unsigned int computationOptions)) &
49+
Solver::compute,
50+
bp::args("self", "matrix", "computationOptions"),
51+
"Method performing the decomposition of given matrix, as "
52+
"specified by the computationOptions parameter. ",
53+
bp::return_self<>())
54+
.def("rows", &Solver::rows, bp::arg("self"),
55+
"Returns the number of rows. ")
56+
.def("setSwitchSize", &Solver::setSwitchSize, bp::args("self", "s"))
57+
58+
.def(SVDBaseVisitor<Solver>());
59+
}
60+
61+
static void expose() {
62+
static const std::string classname =
63+
"BDCSVD_" + scalar_name<Scalar>::shortname();
64+
expose(classname);
65+
}
66+
67+
static void expose(const std::string &name) {
68+
bp::class_<Solver, boost::noncopyable>(
69+
name.c_str(),
70+
"Class Bidiagonal Divide and Conquer SVD.\n\n"
71+
"This class first reduces the input matrix to bi-diagonal form using "
72+
"class "
73+
"UpperBidiagonalization, and then performs a divide-and-conquer "
74+
"diagonalization. "
75+
"Small blocks are diagonalized using class JacobiSVD. You can control "
76+
"the "
77+
"switching size with the setSwitchSize() method, default is 16. For "
78+
"small matrice "
79+
"(<16), it is thus preferable to directly use JacobiSVD. For larger "
80+
"ones, BDCSVD "
81+
"is highly recommended and can several order of magnitude faster.\n\n"
82+
"Warming: this algorithm is unlikely to provide accurate result when "
83+
"compiled with "
84+
"unsafe math optimizations. For instance, this concerns Intel's "
85+
"compiler (ICC), which "
86+
"performs such optimization by default unless you compile with the "
87+
"-fp-model precise "
88+
"option. Likewise, the -ffast-math option of GCC or clang will "
89+
"significantly degrade the "
90+
"accuracy.",
91+
bp::no_init)
92+
.def(BDCSVDVisitor())
93+
.def(IdVisitor<Solver>());
94+
}
95+
};
96+
97+
} // namespace eigenpy
98+
99+
#endif // ifndef __eigenpy_decompositions_bdcsvd_hpp__
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*
2+
* Copyright 2025 INRIA
3+
*/
4+
5+
#ifndef __eigenpy_decompositions_complex_eigen_solver_hpp__
6+
#define __eigenpy_decompositions_complex_eigen_solver_hpp__
7+
8+
#include <Eigen/Core>
9+
#include <Eigen/Eigenvalues>
10+
11+
#include "eigenpy/eigen-to-python.hpp"
12+
#include "eigenpy/eigenpy.hpp"
13+
#include "eigenpy/utils/scalar-name.hpp"
14+
15+
namespace eigenpy {
16+
17+
template <typename _MatrixType>
18+
struct ComplexEigenSolverVisitor : public boost::python::def_visitor<
19+
ComplexEigenSolverVisitor<_MatrixType>> {
20+
typedef _MatrixType MatrixType;
21+
typedef typename MatrixType::Scalar Scalar;
22+
typedef Eigen::ComplexEigenSolver<MatrixType> Solver;
23+
24+
template <class PyClass>
25+
void visit(PyClass& cl) const {
26+
cl.def(bp::init<>("Default constructor"))
27+
.def(bp::init<Eigen::DenseIndex>(
28+
bp::arg("size"), "Default constructor with memory preallocation"))
29+
.def(bp::init<MatrixType, bp::optional<bool>>(
30+
bp::args("matrix", "compute_eigen_vectors"),
31+
"Computes eigendecomposition of given matrix"))
32+
33+
.def("eigenvalues", &Solver::eigenvalues, bp::arg("self"),
34+
"Returns the eigenvalues of given matrix.",
35+
bp::return_internal_reference<>())
36+
.def("eigenvectors", &Solver::eigenvectors, bp::arg("self"),
37+
"Returns the eigenvectors of given matrix.",
38+
bp::return_internal_reference<>())
39+
40+
.def("compute", &ComplexEigenSolverVisitor::compute_proxy<MatrixType>,
41+
bp::args("self", "matrix"),
42+
"Computes the eigendecomposition of given matrix.",
43+
bp::return_self<>())
44+
.def("compute",
45+
(Solver &
46+
(Solver::*)(const Eigen::EigenBase<MatrixType>& matrix, bool)) &
47+
Solver::compute,
48+
bp::args("self", "matrix", "compute_eigen_vectors"),
49+
"Computes the eigendecomposition of given matrix.",
50+
bp::return_self<>())
51+
52+
.def("info", &Solver::info, bp::arg("self"),
53+
"NumericalIssue if the input contains INF or NaN values or "
54+
"overflow occured. Returns Success otherwise.")
55+
56+
.def("getMaxIterations", &Solver::getMaxIterations, bp::arg("self"),
57+
"Returns the maximum number of iterations.")
58+
.def("setMaxIterations", &Solver::setMaxIterations,
59+
bp::args("self", "max_iter"),
60+
"Sets the maximum number of iterations allowed.",
61+
bp::return_self<>());
62+
}
63+
64+
static void expose() {
65+
static const std::string classname =
66+
"ComplexEigenSolver" + scalar_name<Scalar>::shortname();
67+
expose(classname);
68+
}
69+
70+
static void expose(const std::string& name) {
71+
bp::class_<Solver>(name.c_str(), bp::no_init)
72+
.def(ComplexEigenSolverVisitor())
73+
.def(IdVisitor<Solver>());
74+
}
75+
76+
private:
77+
template <typename MatrixType>
78+
static Solver& compute_proxy(Solver& self,
79+
const Eigen::EigenBase<MatrixType>& matrix) {
80+
return self.compute(matrix);
81+
}
82+
};
83+
84+
} // namespace eigenpy
85+
86+
#endif // ifndef __eigenpy_decompositions_complex_eigen_solver_hpp__

0 commit comments

Comments
 (0)