Skip to content

Commit b621dc3

Browse files
committed
eigenvalues: ComplexSchur
1 parent 5f02e30 commit b621dc3

File tree

7 files changed

+129
-3
lines changed

7 files changed

+129
-3
lines changed

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ set(${PROJECT_NAME}_DECOMPOSITIONS_HEADERS
237237
include/eigenpy/decompositions/decompositions.hpp
238238
include/eigenpy/decompositions/EigenSolver.hpp
239239
include/eigenpy/decompositions/ComplexEigenSolver.hpp
240+
include/eigenpy/decompositions/ComplexSchur.hpp
240241
include/eigenpy/decompositions/PermutationMatrix.hpp
241242
include/eigenpy/decompositions/LDLT.hpp
242243
include/eigenpy/decompositions/LLT.hpp
@@ -320,6 +321,7 @@ set(${PROJECT_NAME}_DECOMPOSITIONS_SOURCES
320321
src/decompositions/decompositions.cpp
321322
src/decompositions/eigen-solver.cpp
322323
src/decompositions/complex-eigen-solver.cpp
324+
src/decompositions/complex-schur.cpp
323325
src/decompositions/llt-solver.cpp
324326
src/decompositions/ldlt-solver.cpp
325327
src/decompositions/bdcsvd-solver.cpp
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
* Copyright 2020 INRIA
3+
*/
4+
5+
#ifndef __eigenpy_decompositions_complex_schur_hpp__
6+
#define __eigenpy_decompositions_complex_schur_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 ComplexSchurVisitor
19+
: public boost::python::def_visitor<ComplexSchurVisitor<_MatrixType>> {
20+
typedef _MatrixType MatrixType;
21+
typedef typename MatrixType::Scalar Scalar;
22+
typedef Eigen::ComplexSchur<MatrixType> Solver;
23+
24+
template <class PyClass>
25+
void visit(PyClass& cl) const {
26+
cl.def(bp::init<Eigen::DenseIndex>(bp::arg("size"), "Default constructor"))
27+
.def(bp::init<MatrixType, bp::optional<bool>>(
28+
bp::args("matrix", "computeU"), "Computes Schur of given matrix"))
29+
30+
.def("compute", &ComplexSchurVisitor::compute_proxy<MatrixType>,
31+
bp::args("self", "matrix"), "Computes the Schur of given matrix.",
32+
bp::return_self<>())
33+
.def("compute",
34+
(Solver &
35+
(Solver::*)(const Eigen::EigenBase<MatrixType>& matrix, bool)) &
36+
Solver::compute,
37+
bp::args("self", "matrix", "computeU"),
38+
"Computes the Schur of given matrix.", bp::return_self<>())
39+
40+
.def("computeFromHessenberg",
41+
(Solver & (Solver::*)(const Eigen::EigenBase<MatrixType>& matrixH,
42+
const Eigen::EigenBase<MatrixType>& matrixQ,
43+
bool)) &
44+
Solver::computeFromHessenberg,
45+
bp::args("self", "matrix", "computeU"),
46+
"Compute Schur decomposition from a given Hessenberg matrix. ",
47+
bp::return_self<>())
48+
49+
.def("matrixT", &Solver::matrixT, bp::arg("self"),
50+
"Returns the triangular matrix in the Schur decomposition. ",
51+
bp::return_value_policy<bp::copy_const_reference>())
52+
.def("matrixU", &Solver::matrixU, bp::arg("self"),
53+
"Returns the unitary matrix in the Schur decomposition. ",
54+
bp::return_value_policy<bp::copy_const_reference>())
55+
56+
.def("info", &Solver::info, bp::arg("self"),
57+
"NumericalIssue if the input contains INF or NaN values or "
58+
"overflow occured. Returns Success otherwise.")
59+
60+
.def("getMaxIterations", &Solver::getMaxIterations, bp::arg("self"),
61+
"Returns the maximum number of iterations.")
62+
.def("setMaxIterations", &Solver::setMaxIterations,
63+
bp::args("self", "max_iter"),
64+
"Sets the maximum number of iterations allowed.",
65+
bp::return_self<>());
66+
}
67+
68+
static void expose() {
69+
static const std::string classname =
70+
"ComplexSchur" + scalar_name<Scalar>::shortname();
71+
expose(classname);
72+
}
73+
74+
static void expose(const std::string& name) {
75+
bp::class_<Solver>(name.c_str(), bp::no_init)
76+
.def(ComplexSchurVisitor())
77+
.def(IdVisitor<Solver>());
78+
}
79+
80+
private:
81+
template <typename MatrixType>
82+
static Solver& compute_proxy(Solver& self,
83+
const Eigen::EigenBase<MatrixType>& matrix) {
84+
return self.compute(matrix);
85+
}
86+
};
87+
88+
} // namespace eigenpy
89+
90+
#endif // ifndef __eigenpy_decompositions_complex_schur_hpp__
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
2+
/*
3+
* Copyright 2024 INRIA
4+
*/
5+
6+
#include "eigenpy/decompositions/ComplexSchur.hpp"
7+
8+
namespace eigenpy {
9+
void exposeComplexSchur() {
10+
using namespace Eigen;
11+
ComplexSchurVisitor<MatrixXd>::expose("ComplexSchur");
12+
}
13+
} // namespace eigenpy

src/decompositions/decompositions.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ namespace eigenpy {
1010

1111
void exposeEigenSolver();
1212
void exposeComplexEigenSolver();
13+
void exposeComplexSchur();
1314
void exposeSelfAdjointEigenSolver();
1415
void exposeLLTSolver();
1516
void exposeLDLTSolver();
@@ -30,6 +31,7 @@ void exposeDecompositions() {
3031

3132
exposeEigenSolver();
3233
exposeComplexEigenSolver();
34+
exposeComplexSchur();
3335
exposeSelfAdjointEigenSolver();
3436
exposeLLTSolver();
3537
exposeLDLTSolver();

unittest/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,9 @@ add_python_eigenpy_lib_unit_test("py-eigen-solver"
140140
add_python_eigenpy_lib_unit_test("py-complex-eigen-solver"
141141
"unittest/python/test_complex_eigen_solver.py")
142142

143+
add_python_eigenpy_lib_unit_test("py-complex-schur"
144+
"unittest/python/test_complex_schur.py")
145+
143146
add_python_eigenpy_lib_unit_test(
144147
"py-self-adjoint-eigen-solver"
145148
"unittest/python/test_self_adjoint_eigen_solver.py")

unittest/python/test_complex_eigen_solver.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
rng = np.random.default_rng()
77
A = rng.random((dim, dim))
88

9-
es = eigenpy.ComplexEigenSolver(A)
9+
ces = eigenpy.ComplexEigenSolver(A)
1010

11-
V = es.eigenvectors()
12-
D = es.eigenvalues()
11+
V = ces.eigenvectors()
12+
D = ces.eigenvalues()
1313

1414
assert eigenpy.is_approx(A.dot(V).real, V.dot(np.diag(D)).real)
1515
assert eigenpy.is_approx(A.dot(V).imag, V.dot(np.diag(D)).imag)
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import numpy as np
2+
3+
import eigenpy
4+
5+
dim = 100
6+
rng = np.random.default_rng()
7+
A = rng.random((dim, dim))
8+
9+
cs = eigenpy.ComplexSchur(A)
10+
11+
U = cs.matrixU()
12+
T = cs.matrixT()
13+
U_star = U.conj().T
14+
15+
assert eigenpy.is_approx(A.real, (U @ T @ U_star).real)
16+
assert np.allclose(A.imag, (U @ T @ U_star).imag, atol=1e-10)

0 commit comments

Comments
 (0)