Skip to content
Snippets Groups Projects
Commit c79a1b0d authored by Tobias Leibner's avatar Tobias Leibner
Browse files

[eigen-solver.internal.shifted_qr] fix some errors

parent 2b3579e7
No related branches found
No related tags found
No related merge requests found
...@@ -113,9 +113,13 @@ struct RealQrEigenSolver ...@@ -113,9 +113,13 @@ struct RealQrEigenSolver
return eigenvalues; return eigenvalues;
} // .. calculate_eigenvalues_by_shifted_qr(...) } // .. calculate_eigenvalues_by_shifted_qr(...)
static void reduced_row_echelon_form(MatrixType& A, std::vector<size_t>& pivot_variables) static void reduced_row_echelon_form(MatrixType& A,
std::vector<std::pair<size_t, size_t>>& pivot_variables,
std::vector<size_t>& free_variables,
double tol)
{ {
pivot_variables.clear(); pivot_variables.clear();
free_variables.clear();
size_t col = 0; size_t col = 0;
for (size_t row = 0; row < A.rows(); ++row) { for (size_t row = 0; row < A.rows(); ++row) {
while (col < A.cols()) { while (col < A.cols()) {
...@@ -125,7 +129,8 @@ struct RealQrEigenSolver ...@@ -125,7 +129,8 @@ struct RealQrEigenSolver
if (std::abs(A[rr][col]) > std::abs(A[pivot][col])) if (std::abs(A[rr][col]) > std::abs(A[pivot][col]))
pivot = rr; pivot = rr;
// if all entries in column are zero, continue with next column // if all entries in column are zero, continue with next column
if (XT::Common::FloatCmp::eq(A[pivot][col], 0.)) { if (XT::Common::FloatCmp::eq<XT::Common::FloatCmp::Style::absolute>(A[pivot][col], 0., tol)) {
free_variables.push_back(col);
++col; ++col;
continue; continue;
} else { } else {
...@@ -139,14 +144,14 @@ struct RealQrEigenSolver ...@@ -139,14 +144,14 @@ struct RealQrEigenSolver
// add row to all other rows to zero out other entries in column // add row to all other rows to zero out other entries in column
for (size_t rr = 0; rr < A.rows(); ++rr) { for (size_t rr = 0; rr < A.rows(); ++rr) {
auto factor = A[rr][col]; auto factor = A[rr][col];
if (rr != row && XT::Common::FloatCmp::ne(factor, 0.)) { if (rr != row) {
for (size_t cc = col + 1; cc < A.cols(); ++cc) for (size_t cc = col + 1; cc < A.cols(); ++cc)
A[rr][cc] -= A[row][cc] * factor; A[rr][cc] -= A[row][cc] * factor;
A[rr][col] = 0.; A[rr][col] = 0.;
} }
} // rr } // rr
// store column as pivot variable // store (col, row) as pivot variable
pivot_variables.push_back(col); pivot_variables.push_back(std::make_pair(col, row));
// continue with next row and col // continue with next row and col
++col; ++col;
break; break;
...@@ -155,27 +160,34 @@ struct RealQrEigenSolver ...@@ -155,27 +160,34 @@ struct RealQrEigenSolver
} // row } // row
} // void reduced_row_echelon_form(...) } // void reduced_row_echelon_form(...)
static std::vector<double> get_eigenvalues(const MatrixType& A_in) static std::vector<double> get_eigenvalues(const MatrixType& A_in, MatrixType* Q = nullptr)
{ {
auto A_ptr = std::make_unique<MatrixType>(A_in); auto A_ptr = std::make_unique<MatrixType>(A_in);
auto& A = *A_ptr; auto& A = *A_ptr;
hessenberg_transformation(A); hessenberg_transformation(A, Q);
auto eigenvalues = calculate_eigenvalues_by_shifted_qr(A); auto eigenvalues = calculate_eigenvalues_by_shifted_qr(A, Q);
std::sort(eigenvalues.begin(), eigenvalues.end()); std::sort(eigenvalues.begin(), eigenvalues.end());
return eigenvalues; return eigenvalues;
} // ... get_eigenvalues(...) } // ... get_eigenvalues(...)
static std::unique_ptr<MatrixType> get_eigenvectors(const MatrixType& A_in, const std::vector<double>& eigenvalues) static std::unique_ptr<MatrixType>
get_eigenvectors(const MatrixType& A_in, const std::vector<double>& eigenvalues, const MatrixType& Q)
{ {
std::cout << "A = " << XT::Common::to_string(A_in, 17) << std::endl;
std::cout << "eigenvalues = " << XT::Common::to_string(eigenvalues, 15) << std::endl;
auto A_ptr = std::make_unique<MatrixType>(A_in); auto A_ptr = std::make_unique<MatrixType>(A_in);
auto& A = *A_ptr; auto& A = *A_ptr;
// use the same tolerance max(size(A)) * eps * norm(A,inf) as matlab for rref
double tol = std::numeric_limits<double>::epsilon() * std::max(A.rows(), A.cols()) * A.infinity_norm();
auto ret = std::make_unique<MatrixType>(A_in); auto ret = std::make_unique<MatrixType>(A_in);
*ret *= 0.;
// Calculate eigenvectors by solving (A - \lambda I) x = 0. // Calculate eigenvectors by solving (A - \lambda I) x = 0.
for (size_t ii = 0; ii < eigenvalues.size(); ++ii) { for (size_t ii = 0; ii < eigenvalues.size(); ++ii) {
// get eigenvalue, calculate multiplicity // get eigenvalue, calculate multiplicity
double lambda = eigenvalues[ii]; double lambda = eigenvalues[ii];
size_t multiplicity = 1; size_t multiplicity = 1;
while (XT::Common::FloatCmp::eq(eigenvalues[ii], eigenvalues[ii + 1])) { while (
XT::Common::FloatCmp::eq<XT::Common::FloatCmp::Style::absolute>(eigenvalues[ii], eigenvalues[ii + 1], tol)) {
++multiplicity; ++multiplicity;
++ii; ++ii;
} }
...@@ -185,25 +197,42 @@ struct RealQrEigenSolver ...@@ -185,25 +197,42 @@ struct RealQrEigenSolver
A[rr][rr] -= lambda; A[rr][rr] -= lambda;
// transform B to reduced row echelon form // transform B to reduced row echelon form
// in the process, keep track of pivot variables // in the process, keep track of variables
std::vector<size_t> pivot_variables; // pivot_variables contains pairs of (col, row), where col is the pivot column and row is the row where the 1 is
reduced_row_echelon_form(A, pivot_variables); // located
std::vector<std::pair<size_t, size_t>> pivot_variables;
// free_variables contains the columns of the free variables
std::vector<size_t> free_variables;
// This loop is a hack to avoid problems with the reduced row echelon form being wrong due to too small tolerances
// TODO: Do something more sophisticated/replace rref by a more stable algorithm to get eigenvectors?
size_t max_power = 4;
for (size_t jj = 0; jj <= max_power; ++jj) {
tol = std::numeric_limits<double>::epsilon() * std::max(A.rows(), A.cols()) * A.infinity_norm()
* std::pow(10., jj);
std::cout << "tol: " << XT::Common::to_string(tol, 15) << std::endl;
reduced_row_echelon_form(A, pivot_variables, free_variables, tol);
std::cout << "Arref = " << XT::Common::to_string(A, 15) << std::endl;
std::cout << "lambda = " << XT::Common::to_string(lambda, 15) << std::endl;
std::cout << "free_variables" << XT::Common::to_string(free_variables) << std::endl;
if (free_variables.size() == multiplicity)
break;
if (jj == max_power && free_variables.size() != multiplicity)
DUNE_THROW(Dune::MathError, "Failed to compute eigenvectors!");
A = A_in;
for (size_t rr = 0; rr < A.rows(); ++rr)
A[rr][rr] -= lambda;
} // jj
// Now get the eigenvectors from the reduced row echelon form. To get an eigenvector, set one of the free // Now get the eigenvectors from the reduced row echelon form. To get an eigenvector, set one of the free
// variables to 1 and the other free variables to 0 and calculate the pivot variables. // variables to 1 and the other free variables to 0 and calculate the pivot variables.
std::vector<size_t> free_variables;
for (size_t jj = 0; jj < A.cols(); ++jj)
if (std::find(pivot_variables.begin(), pivot_variables.end(), jj) == pivot_variables.end())
free_variables.push_back(jj);
if (free_variables.size() != multiplicity)
DUNE_THROW(Dune::MathError, "Failed to compute eigenvectors!");
size_t eigenvector_index = ii - multiplicity + 1; size_t eigenvector_index = ii - multiplicity + 1;
for (const auto& cc : free_variables) { for (const auto& free_var : free_variables) {
for (size_t rr = 0; rr < A.rows(); ++rr) for (const auto& pivot_var : pivot_variables) {
(*ret)[rr][eigenvector_index] = -A[rr][cc]; (*ret)[pivot_var.first][eigenvector_index] = -A[pivot_var.second][free_var];
(*ret)[cc][eigenvector_index] = 1.; }
(*ret)[free_var][eigenvector_index] = 1.;
++eigenvector_index; ++eigenvector_index;
} // cc } // free_var
} // ii } // ii
// normalize eigenvectors // normalize eigenvectors
for (size_t col = 0; col < ret->cols(); ++col) { for (size_t col = 0; col < ret->cols(); ++col) {
...@@ -214,6 +243,7 @@ struct RealQrEigenSolver ...@@ -214,6 +243,7 @@ struct RealQrEigenSolver
for (size_t row = 0; row < ret->rows(); ++row) for (size_t row = 0; row < ret->rows(); ++row)
(*ret)[row][col] *= inv_norm; (*ret)[row][col] *= inv_norm;
} }
std::cout << "ret = " << XT::Common::to_string(*ret, 15) << std::endl;
return ret; return ret;
} // ... get_eigenvectors(...) } // ... get_eigenvectors(...)
...@@ -339,7 +369,7 @@ struct RealQrEigenSolver ...@@ -339,7 +369,7 @@ struct RealQrEigenSolver
//! \brief Transform A to Hessenberg form by transformation P^T A P //! \brief Transform A to Hessenberg form by transformation P^T A P
//! \see https://lp.uni-goettingen.de/get/text/2137 //! \see https://lp.uni-goettingen.de/get/text/2137
static void hessenberg_transformation(MatrixType& A) static void hessenberg_transformation(MatrixType& A, MatrixType* Q)
{ {
assert(A.rows() == A.cols() && "Hessenberg transformation needs a square matrix!"); assert(A.rows() == A.cols() && "Hessenberg transformation needs a square matrix!");
VectorType u(A.rows(), 0.); VectorType u(A.rows(), 0.);
...@@ -359,6 +389,8 @@ struct RealQrEigenSolver ...@@ -359,6 +389,8 @@ struct RealQrEigenSolver
multiply_householder_from_left(A, beta, u, jj + 1, A.rows()); multiply_householder_from_left(A, beta, u, jj + 1, A.rows());
// now calculate (PA) P = PA - (PA u) (beta u^T) // now calculate (PA) P = PA - (PA u) (beta u^T)
multiply_householder_from_right(A, beta, u, jj + 1, A.cols()); multiply_householder_from_right(A, beta, u, jj + 1, A.cols());
if (Q)
multiply_householder_from_right(*Q, beta, u, jj + 1, A.cols());
} // if (gamma != 0) } // if (gamma != 0)
} // jj } // jj
} // void hessenberg_transformation(...) } // void hessenberg_transformation(...)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment