eigs.cpp 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. #include "eigs.h"
  2. #include "../sort.h"
  3. #include "../IGL_ASSERT.h"
  4. #include <Spectra/SymGEigsShiftSolver.h>
  5. template <
  6. typename EigsScalar,
  7. typename DerivedU,
  8. typename DerivedS,
  9. typename Solver>
  10. IGL_INLINE bool igl::spectra::eigs(
  11. const Eigen::SparseMatrix<EigsScalar> & A,
  12. const Eigen::SparseMatrix<EigsScalar> & B,
  13. const int k,
  14. const igl::EigsType type,
  15. Eigen::PlainObjectBase<DerivedU> & U,
  16. Eigen::PlainObjectBase<DerivedS> & S)
  17. {
  18. IGL_ASSERT(k > 0 && "k should be positive");
  19. IGL_ASSERT(k < A.rows() && "k should be less than size of A");
  20. IGL_ASSERT(type == igl::EIGS_TYPE_SM && "Only SM supported");
  21. // This seems like a hack. For the "eigs: grid" test this is necessary to get
  22. // at least 1e-4 error for the first 5 eigen values. It's annoying that this
  23. // means that the zero modes become O(sigma) and this is now rather large.
  24. //
  25. // I wonder if this is an issue with SparseLU and if UMFPACK would be better.
  26. //
  27. // Ideally this value would be 0.
  28. const EigsScalar sigma = 1e-8;
  29. return igl::spectra::eigs(A,B,k,sigma,U,S);
  30. }
  31. template <
  32. typename EigsScalar,
  33. typename DerivedU,
  34. typename DerivedS,
  35. typename Solver>
  36. IGL_INLINE bool igl::spectra::eigs(
  37. const Eigen::SparseMatrix<EigsScalar> & A,
  38. const Eigen::SparseMatrix<EigsScalar> & B,
  39. const int k,
  40. const EigsScalar sigma,
  41. Eigen::PlainObjectBase<DerivedU> & U,
  42. Eigen::PlainObjectBase<DerivedS> & S)
  43. {
  44. IGL_ASSERT(k > 0 && "k should be positive");
  45. IGL_ASSERT(k < A.rows() && "k should be less than size of A");
  46. class SparseMatProd
  47. {
  48. public:
  49. using Scalar = EigsScalar;
  50. const Eigen::SparseMatrix<Scalar> & m_B;
  51. SparseMatProd(const Eigen::SparseMatrix<Scalar> & B) : m_B(B) {}
  52. int rows() const { return m_B.rows(); }
  53. int cols() const { return m_B.cols(); }
  54. void perform_op(const Scalar *x_in, Scalar *y_out) const
  55. {
  56. typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> VectorXS;
  57. Eigen::Map<const VectorXS> x(x_in, m_B.cols());
  58. Eigen::Map< VectorXS> y(y_out, m_B.rows());
  59. y = m_B * x;
  60. }
  61. };
  62. // Solver must expose .compute(A) and .solve(x)
  63. class ShiftInvert
  64. {
  65. public:
  66. using Scalar = EigsScalar;
  67. private:
  68. const Eigen::SparseMatrix<Scalar> & m_A;
  69. const Eigen::SparseMatrix<Scalar> & m_B;
  70. Scalar m_sigma;
  71. Solver m_solver;
  72. public:
  73. bool m_solver_is_successfully_factorized;
  74. ShiftInvert(
  75. const Eigen::SparseMatrix<Scalar>& A,
  76. const Eigen::SparseMatrix<Scalar>& B,
  77. const Scalar sigma):
  78. m_A(A), m_B(B)
  79. {
  80. IGL_ASSERT(m_A.rows() == m_A.cols() && "A must be square");
  81. IGL_ASSERT(m_B.rows() == m_B.cols() && "B must be square");
  82. IGL_ASSERT(m_A.rows() == m_B.cols() && "A and B must have the same size");
  83. set_shift(sigma, true);
  84. }
  85. void set_shift(const Scalar & sigma, const bool force = false)
  86. {
  87. if(sigma == m_sigma && !force)
  88. {
  89. return;
  90. }
  91. m_sigma = sigma;
  92. const Eigen::SparseMatrix<Scalar> C = m_A + m_sigma * m_B;
  93. m_solver.compute(C);
  94. m_solver_is_successfully_factorized = (m_solver.info() == Eigen::Success);
  95. }
  96. int rows() const { return m_A.rows(); }
  97. int cols() const { return m_A.cols(); }
  98. void perform_op(const Scalar* x_in,Scalar* y_out) const
  99. {
  100. typedef Eigen::Matrix<Scalar, Eigen::Dynamic, 1> VectorXS;
  101. Eigen::Map<const VectorXS>x(x_in, m_A.cols());
  102. Eigen::Map<VectorXS>y(y_out, m_A.rows());
  103. y = m_solver.solve(x);
  104. }
  105. };
  106. SparseMatProd Bop(B);
  107. ShiftInvert op(A, B, sigma);
  108. if(!op.m_solver_is_successfully_factorized)
  109. {
  110. return false;
  111. }
  112. Spectra::SymGEigsShiftSolver<ShiftInvert, SparseMatProd, Spectra::GEigsMode::ShiftInvert> geigs(op, Bop, k, 2*k, sigma);
  113. geigs.init();
  114. geigs.compute(Spectra::SortRule::LargestMagn);
  115. if (geigs.info() != Spectra::CompInfo::Successful)
  116. {
  117. return false;
  118. }
  119. U = geigs.eigenvectors().template cast<typename DerivedU::Scalar>();
  120. S = geigs.eigenvalues().template cast<typename DerivedS::Scalar>();
  121. Eigen::VectorXi I;
  122. igl::sort( Eigen::VectorXd(S), 1, false, S, I);
  123. U = U(Eigen::placeholders::all,I).eval();
  124. return true;
  125. }
  126. #ifdef IGL_STATIC_LIBRARY
  127. // Explicit template instantiation
  128. // generated by autoexplicit.sh
  129. template bool igl::spectra::eigs<double, Eigen::Matrix<double, -1, -1, 0, -1, -1>, Eigen::Matrix<double, -1, 1, 0, -1, 1>, Eigen::SparseLU<Eigen::SparseMatrix<double, 0, int>, Eigen::COLAMDOrdering<int> > >(Eigen::SparseMatrix<double, 0, int> const&, Eigen::SparseMatrix<double, 0, int> const&, int , igl::EigsType, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, -1, 0, -1, -1> >&, Eigen::PlainObjectBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >&);
  130. #endif