mirror of
https://gitlab.com/libeigen/eigen.git
synced 2026-04-10 11:34:33 +08:00
384 lines
13 KiB
C++
384 lines
13 KiB
C++
// This file is part of Eigen, a lightweight C++ template library
|
|
// for linear algebra.
|
|
//
|
|
// Copyright (C) 2025 Rasmus Munk Larsen <rmlarsen@gmail.com>
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla
|
|
// Public License v. 2.0. If a copy of the MPL was not distributed
|
|
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
#ifndef EIGEN_TEST_TRIDIAG_TEST_MATRICES_H
|
|
#define EIGEN_TEST_TRIDIAG_TEST_MATRICES_H
|
|
|
|
// Structured tridiagonal test matrices from the numerical linear algebra
|
|
// literature. Used by both the bidiagonal SVD and symmetric eigenvalue tests.
|
|
//
|
|
// Each generator writes into pre-allocated (diag, offdiag) vectors.
|
|
// For SVD, offdiag is the superdiagonal of a bidiagonal matrix.
|
|
// For eigenvalues, offdiag is the subdiagonal of a symmetric tridiagonal matrix.
|
|
//
|
|
// Usage:
|
|
// Matrix<RealScalar, Dynamic, 1> diag(n), offdiag(n-1);
|
|
// tridiag_identity(diag, offdiag); // fills diag and offdiag
|
|
// my_verify(diag, offdiag); // solver-specific verification
|
|
|
|
#include <Eigen/Core>
|
|
|
|
namespace Eigen {
|
|
namespace test {
|
|
|
|
// 1. Identity: d=[1,...,1], e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_identity(VectorType& diag, VectorType& offdiag) {
|
|
diag.setOnes();
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 2. Zero: d=[0,...,0], e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_zero(VectorType& diag, VectorType& offdiag) {
|
|
diag.setZero();
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 3. Constant: d=[c,...,c], e=[c,...,c]
|
|
template <typename VectorType>
|
|
void tridiag_constant(VectorType& diag, VectorType& offdiag,
|
|
typename VectorType::Scalar c = typename VectorType::Scalar(2.5)) {
|
|
diag.setConstant(c);
|
|
offdiag.setConstant(c);
|
|
}
|
|
|
|
// 4. 1-2-1 Toeplitz: d=[2,...,2], e=[1,...,1]
|
|
// Eigenvalues: 2 - 2*cos(k*pi/(n+1)) for k=1,...,n
|
|
template <typename VectorType>
|
|
void tridiag_1_2_1(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
diag.setConstant(Scalar(2));
|
|
offdiag.setOnes();
|
|
}
|
|
|
|
// 5. Wilkinson W_{2m+1}: d_i = |m - i|, e=[1,...,1]
|
|
// Has pairs of eigenvalues agreeing to many digits; stresses deflation.
|
|
template <typename VectorType>
|
|
void tridiag_wilkinson(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = numext::abs(Scalar(n / 2) - Scalar(i));
|
|
offdiag.setOnes();
|
|
}
|
|
|
|
// 6. Clement matrix: d=[0,...,0], e_i = sqrt(i*(n-1-i))
|
|
// Known eigenvalues: -(n-1), -(n-3), ..., (n-3), (n-1)
|
|
template <typename VectorType>
|
|
void tridiag_clement(VectorType& diag, VectorType& offdiag) {
|
|
EIGEN_USING_STD(sqrt);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
diag.setZero();
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = sqrt(Scalar(i + 1) * Scalar(n - 1 - i));
|
|
}
|
|
|
|
// 7. Kahan-style: d_i = s^i, e_i = -c*s^i with s=sin(theta), c=cos(theta).
|
|
// Geometric decay with controlled condition number.
|
|
template <typename VectorType>
|
|
void tridiag_kahan(VectorType& diag, VectorType& offdiag,
|
|
typename VectorType::Scalar theta = typename VectorType::Scalar(0.3)) {
|
|
EIGEN_USING_STD(sin);
|
|
EIGEN_USING_STD(cos);
|
|
EIGEN_USING_STD(pow);
|
|
EIGEN_USING_STD(log);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
const Scalar s = sin(theta);
|
|
const Scalar c = cos(theta);
|
|
const Scalar maxPower = -log(eps) / (-log(s));
|
|
for (Index i = 0; i < n; ++i) diag(i) = pow(s, numext::mini(Scalar(i), maxPower));
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = -c * pow(s, numext::mini(Scalar(i), maxPower));
|
|
}
|
|
|
|
// 8. Graded: d_i = base^(-i), e_i = base^(-i)
|
|
template <typename VectorType>
|
|
void tridiag_graded(VectorType& diag, VectorType& offdiag,
|
|
typename VectorType::Scalar base = typename VectorType::Scalar(10)) {
|
|
EIGEN_USING_STD(pow);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = pow(base, -Scalar(i));
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = pow(base, -Scalar(i));
|
|
}
|
|
|
|
// 9. Geometric decay diagonal: d_i = base^i, e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_geometric_diagonal(VectorType& diag, VectorType& offdiag,
|
|
typename VectorType::Scalar base = typename VectorType::Scalar(0.5)) {
|
|
EIGEN_USING_STD(pow);
|
|
EIGEN_USING_STD(log);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
const Scalar maxPower = -log(eps) / (-log(base));
|
|
for (Index i = 0; i < n; ++i) diag(i) = pow(base, numext::mini(Scalar(i), maxPower));
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 10. Geometric decay offdiagonal: d=[1,...,1], e_i = base^i
|
|
template <typename VectorType>
|
|
void tridiag_geometric_offdiag(VectorType& diag, VectorType& offdiag,
|
|
typename VectorType::Scalar base = typename VectorType::Scalar(0.5)) {
|
|
EIGEN_USING_STD(pow);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
diag.setOnes();
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = pow(base, Scalar(i));
|
|
}
|
|
|
|
// 11. Clustered eigenvalues: d_i = 1 + i*eps, e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_clustered(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
for (Index i = 0; i < n; ++i) diag(i) = Scalar(1) + Scalar(i) * eps;
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 12. Two clusters: half at 1, half at eps.
|
|
template <typename VectorType>
|
|
void tridiag_two_clusters(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
for (Index i = 0; i < n; ++i) diag(i) = (i < n / 2) ? Scalar(1) : eps;
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 13. Single tiny value: d=[1,...,1,eps], e=[eps^2,...,eps^2]
|
|
template <typename VectorType>
|
|
void tridiag_single_tiny(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
diag.setOnes();
|
|
diag(n - 1) = eps;
|
|
offdiag.setConstant(eps * eps);
|
|
}
|
|
|
|
// 14. Overflow/underflow: alternating big/tiny diagonal and offdiagonal.
|
|
template <typename VectorType>
|
|
void tridiag_overflow_underflow(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar big = (std::numeric_limits<Scalar>::max)() / Scalar(1000);
|
|
const Scalar tiny = (std::numeric_limits<Scalar>::min)() * Scalar(1000);
|
|
for (Index i = 0; i < n; ++i) diag(i) = (i % 2 == 0) ? big : tiny;
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = (i % 2 == 0) ? tiny : big;
|
|
}
|
|
|
|
// 15. Prescribed condition number: d_i = kappa^(-i/(n-1)), e_i = eps * random.
|
|
template <typename VectorType>
|
|
void tridiag_prescribed_cond(VectorType& diag, VectorType& offdiag) {
|
|
EIGEN_USING_STD(pow);
|
|
EIGEN_USING_STD(abs);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
const Scalar kappa = Scalar(1) / eps;
|
|
for (Index i = 0; i < n; ++i) diag(i) = pow(kappa, -Scalar(i) / Scalar(n - 1));
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = eps * abs(internal::random<Scalar>());
|
|
}
|
|
|
|
// 16. Rank-deficient: d=[1,..,0,..,0,..,1], e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_rank_deficient(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = (i < n / 3 || i >= 2 * n / 3) ? Scalar(1) : Scalar(0);
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 17. Arrowhead-like: d_i = linspace(1,n), e_i = 1/(i+1)
|
|
template <typename VectorType>
|
|
void tridiag_arrowhead(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = Scalar(1) + Scalar(i);
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = Scalar(1) / Scalar(i + 1);
|
|
}
|
|
|
|
// 18. Repeated values: d=[1,2,3,1,2,3,...], e=[0,...,0]
|
|
template <typename VectorType>
|
|
void tridiag_repeated(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = Scalar((i % 3) + 1);
|
|
offdiag.setZero();
|
|
}
|
|
|
|
// 19. Glued blocks: d=[1,...,1], e=0 except e[n/2-1]=eps.
|
|
// Two identity blocks coupled by a tiny off-diagonal entry.
|
|
template <typename VectorType>
|
|
void tridiag_glued(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
diag.setOnes();
|
|
offdiag.setZero();
|
|
if (n > 2) offdiag(n / 2 - 1) = NumTraits<Scalar>::epsilon();
|
|
}
|
|
|
|
// 20. Nearly diagonal: random diag, eps * random offdiag.
|
|
template <typename VectorType>
|
|
void tridiag_nearly_diagonal(VectorType& diag, VectorType& offdiag) {
|
|
EIGEN_USING_STD(abs);
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
const Scalar eps = NumTraits<Scalar>::epsilon();
|
|
diag = VectorType::Random(n).cwiseAbs() + VectorType::Constant(n, Scalar(0.1));
|
|
for (Index i = 0; i < n - 1; ++i) offdiag(i) = eps * (Scalar(0.5) + abs(internal::random<Scalar>()));
|
|
}
|
|
|
|
// 21. Negative eigenvalues: d_i = -i, e=[1,...,1]
|
|
// (Only meaningful for symmetric eigenvalue problems, not SVD.)
|
|
template <typename VectorType>
|
|
void tridiag_negative(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = -Scalar(i + 1);
|
|
offdiag.setOnes();
|
|
}
|
|
|
|
// 22. Mixed sign diagonal: d_i = (-1)^i * (i+1), e=[1,...,1]
|
|
// (Only meaningful for symmetric eigenvalue problems, not SVD.)
|
|
template <typename VectorType>
|
|
void tridiag_mixed_sign(VectorType& diag, VectorType& offdiag) {
|
|
typedef typename VectorType::Scalar Scalar;
|
|
Index n = diag.size();
|
|
for (Index i = 0; i < n; ++i) diag(i) = ((i % 2 == 0) ? Scalar(1) : Scalar(-1)) * Scalar(i + 1);
|
|
offdiag.setOnes();
|
|
}
|
|
|
|
// Helper: iterate over a set of sizes and call a functor with each (diag, offdiag) pair
|
|
// generated by a generator function.
|
|
//
|
|
// Usage:
|
|
// for_tridiag_sizes([](auto& diag, auto& offdiag) {
|
|
// tridiag_wilkinson(diag, offdiag);
|
|
// my_verify(diag, offdiag);
|
|
// });
|
|
template <typename Scalar, typename Func>
|
|
void for_tridiag_sizes(Func&& func) {
|
|
const int sizes[] = {1, 2, 3, 5, 10, 16, 20, 50, 100};
|
|
typedef Matrix<Scalar, Dynamic, 1> VectorType;
|
|
for (int si = 0; si < int(sizeof(sizes) / sizeof(sizes[0])); ++si) {
|
|
const Index n = sizes[si];
|
|
VectorType diag(n), offdiag(n > 1 ? n - 1 : 0);
|
|
func(diag, offdiag);
|
|
}
|
|
}
|
|
|
|
// Helper: run all generators (suitable for both SVD and eigenvalue problems).
|
|
// The callback receives (diag, offdiag) after each generator fills them.
|
|
template <typename Scalar, typename Func>
|
|
void for_all_tridiag_test_matrices(Func&& verify) {
|
|
const int sizes[] = {1, 2, 3, 5, 10, 16, 20, 50, 100};
|
|
typedef Matrix<Scalar, Dynamic, 1> VectorType;
|
|
|
|
for (int si = 0; si < int(sizeof(sizes) / sizeof(sizes[0])); ++si) {
|
|
const Index n = sizes[si];
|
|
VectorType diag(n), offdiag(n > 1 ? n - 1 : 0);
|
|
|
|
tridiag_identity(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_zero(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_constant(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_1_2_1(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_wilkinson(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
if (n > 1) {
|
|
tridiag_clement(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
}
|
|
|
|
tridiag_kahan(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_graded(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_geometric_diagonal(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
if (n > 1) {
|
|
tridiag_geometric_offdiag(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
}
|
|
|
|
tridiag_clustered(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_two_clusters(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_single_tiny(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_overflow_underflow(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
if (n > 1) {
|
|
tridiag_prescribed_cond(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
}
|
|
|
|
tridiag_rank_deficient(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_arrowhead(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_repeated(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_glued(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_nearly_diagonal(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
}
|
|
}
|
|
|
|
// Helper: run all generators, including those with negative values
|
|
// (suitable only for symmetric eigenvalue problems, not SVD).
|
|
template <typename Scalar, typename Func>
|
|
void for_all_symmetric_tridiag_test_matrices(Func&& verify) {
|
|
for_all_tridiag_test_matrices<Scalar>(verify);
|
|
|
|
const int sizes[] = {1, 2, 3, 5, 10, 16, 20, 50, 100};
|
|
typedef Matrix<Scalar, Dynamic, 1> VectorType;
|
|
|
|
for (int si = 0; si < int(sizeof(sizes) / sizeof(sizes[0])); ++si) {
|
|
const Index n = sizes[si];
|
|
VectorType diag(n), offdiag(n > 1 ? n - 1 : 0);
|
|
|
|
tridiag_negative(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
|
|
tridiag_mixed_sign(diag, offdiag);
|
|
verify(diag, offdiag);
|
|
}
|
|
}
|
|
|
|
} // namespace test
|
|
} // namespace Eigen
|
|
|
|
#endif // EIGEN_TEST_TRIDIAG_TEST_MATRICES_H
|