Skip to content

Commit fb331f2

Browse files
committed
decompositions: ComplexSchur: Completed tests
1 parent d27aa9f commit fb331f2

File tree

2 files changed

+100
-17
lines changed

2 files changed

+100
-17
lines changed

include/nanoeigenpy/decompositions/complex-schur.hpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ void exposeComplexSchur(nb::module_ m, const char *name) {
2323
"Default constructor with memory preallocation.")
2424
.def(nb::init<const MatrixType &>(), "matrix"_a,
2525
"Constructor; computes Schur decomposition of given matrix.")
26-
2726
.def(nb::init<const MatrixType &, bool>(), "matrix"_a,
2827
"computeU"_a = true,
2928
"Constructor; computes Schur decomposition of given matrix.")

tests/test_complex_schur.py

Lines changed: 100 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,117 @@
11
import nanoeigenpy
22
import numpy as np
33

4-
dim = 100
4+
dim = 5
55
rng = np.random.default_rng()
6-
76
A = rng.random((dim, dim))
87

9-
# Tests init
10-
cs = nanoeigenpy.ComplexSchur(dim)
118
cs = nanoeigenpy.ComplexSchur(A)
129
assert cs.info() == nanoeigenpy.ComputationInfo.Success
1310

1411
U = cs.matrixU()
1512
T = cs.matrixT()
16-
U_star = U.conj().T
1713

18-
assert nanoeigenpy.is_approx(A.real, (U @ T @ U_star).real)
19-
assert np.allclose(A.imag, (U @ T @ U_star).imag)
14+
A_complex = A.astype(complex)
15+
assert nanoeigenpy.is_approx(A_complex, U @ T @ U.conj().T, 1e-10)
16+
assert nanoeigenpy.is_approx(U @ U.conj().T, np.eye(dim), 1e-10)
17+
18+
for row in range(1, dim):
19+
for col in range(row):
20+
assert abs(T[row, col]) < 1e-12
21+
22+
A_test = rng.random((dim, dim))
23+
cs1 = nanoeigenpy.ComplexSchur(dim)
24+
cs1.compute(A_test)
25+
cs2 = nanoeigenpy.ComplexSchur(A_test)
26+
27+
assert cs1.info() == nanoeigenpy.ComputationInfo.Success
28+
assert cs2.info() == nanoeigenpy.ComputationInfo.Success
29+
30+
T1 = cs1.matrixT()
31+
U1 = cs1.matrixU()
32+
T2 = cs2.matrixT()
33+
U2 = cs2.matrixU()
34+
35+
assert nanoeigenpy.is_approx(T1, T2, 1e-12)
36+
assert nanoeigenpy.is_approx(U1, U2, 1e-12)
37+
38+
cs_no_u = nanoeigenpy.ComplexSchur(A, False)
39+
assert cs_no_u.info() == nanoeigenpy.ComputationInfo.Success
40+
T_no_u = cs_no_u.matrixT()
41+
42+
assert nanoeigenpy.is_approx(T, T_no_u, 1e-12)
43+
44+
cs_compute_no_u = nanoeigenpy.ComplexSchur(dim)
45+
result_no_u = cs_compute_no_u.compute(A, False)
46+
assert result_no_u.info() == nanoeigenpy.ComputationInfo.Success
47+
T_compute_no_u = cs_compute_no_u.matrixT()
48+
assert nanoeigenpy.is_approx(T, T_compute_no_u, 1e-12)
2049

21-
# Test nb::init<Eigen::DenseIndex>()
22-
# Test id
23-
dim_constructor = 3
50+
cs_iter = nanoeigenpy.ComplexSchur(dim)
51+
cs_iter.setMaxIterations(30 * dim) # m_maxIterationsPerRow * size
52+
result_iter = cs_iter.compute(A)
53+
assert result_iter.info() == nanoeigenpy.ComputationInfo.Success
54+
assert cs_iter.getMaxIterations() == 30 * dim
2455

25-
cs3 = nanoeigenpy.ComplexSchur(dim_constructor)
26-
cs4 = nanoeigenpy.ComplexSchur(dim_constructor)
56+
T_iter = cs_iter.matrixT()
57+
U_iter = cs_iter.matrixU()
58+
assert nanoeigenpy.is_approx(T, T_iter, 1e-12)
59+
assert nanoeigenpy.is_approx(U, U_iter, 1e-12)
2760

28-
id3 = cs3.id()
29-
id4 = cs4.id()
61+
cs_few_iter = nanoeigenpy.ComplexSchur(dim)
62+
cs_few_iter.setMaxIterations(1)
63+
result_few = cs_few_iter.compute(A)
64+
assert cs_few_iter.getMaxIterations() == 1
3065

66+
A_triangular = np.triu(A)
67+
cs_triangular = nanoeigenpy.ComplexSchur(dim)
68+
cs_triangular.setMaxIterations(1)
69+
result_triangular = cs_triangular.compute(A_triangular)
70+
assert result_triangular.info() == nanoeigenpy.ComputationInfo.Success
71+
72+
T_triangular = cs_triangular.matrixT()
73+
U_triangular = cs_triangular.matrixU()
74+
75+
A_triangular_complex = A_triangular.astype(complex)
76+
assert nanoeigenpy.is_approx(T_triangular, A_triangular_complex, 1e-10)
77+
assert nanoeigenpy.is_approx(U_triangular, np.eye(dim, dtype=complex), 1e-10)
78+
79+
hess = nanoeigenpy.HessenbergDecomposition(A)
80+
H = hess.matrixH()
81+
Q_hess = hess.matrixQ()
82+
83+
cs_from_hess = nanoeigenpy.ComplexSchur(dim)
84+
result_from_hess = cs_from_hess.computeFromHessenberg(H, Q_hess, True)
85+
assert result_from_hess.info() == nanoeigenpy.ComputationInfo.Success
86+
87+
T_from_hess = cs_from_hess.matrixT()
88+
U_from_hess = cs_from_hess.matrixU()
89+
90+
A_complex = A.astype(complex)
91+
assert nanoeigenpy.is_approx(
92+
A_complex, U_from_hess @ T_from_hess @ U_from_hess.conj().T, 1e-10
93+
)
94+
95+
cs1_id = nanoeigenpy.ComplexSchur(dim)
96+
cs2_id = nanoeigenpy.ComplexSchur(dim)
97+
id1 = cs1_id.id()
98+
id2 = cs2_id.id()
99+
assert id1 != id2
100+
assert id1 == cs1_id.id()
101+
assert id2 == cs2_id.id()
102+
103+
cs3_id = nanoeigenpy.ComplexSchur(A)
104+
cs4_id = nanoeigenpy.ComplexSchur(A)
105+
id3 = cs3_id.id()
106+
id4 = cs4_id.id()
31107
assert id3 != id4
32-
assert id3 == cs3.id()
33-
assert id4 == cs4.id()
108+
assert id3 == cs3_id.id()
109+
assert id4 == cs4_id.id()
110+
111+
cs5_id = nanoeigenpy.ComplexSchur(A, True)
112+
cs6_id = nanoeigenpy.ComplexSchur(A, False)
113+
id5 = cs5_id.id()
114+
id6 = cs6_id.id()
115+
assert id5 != id6
116+
assert id5 == cs5_id.id()
117+
assert id6 == cs6_id.id()

0 commit comments

Comments
 (0)