MUQ  0.4.3
StochasticEigenSolver.cpp
Go to the documentation of this file.
2 
4 #include <boost/property_tree/ptree.hpp>
5 
6 using namespace muq::Modeling;
7 using namespace muq::Utilities;
8 
10  double eigRelTolIn,
11  double eigAbsTolIn,
12  int expectedRankIn,
13  int samplingFactorIn,
14  int blockSizeIn,
15  int verbosityIn) : numEigs(numEigsIn),
16  eigRelTol(eigRelTolIn),
17  eigAbsTol(eigAbsTolIn),
18  expectedRank((expectedRankIn<0)?numEigsIn:expectedRankIn),
19  samplingFactor((samplingFactorIn<0)?(0.1*numEigsIn):samplingFactorIn),
20  blockSize(blockSizeIn),
21  verbosity(verbosityIn)
22 {
23  assert(eigRelTol>=0.0);
24 }
25 
26 StochasticEigenSolver::StochasticEigenSolver(boost::property_tree::ptree const& opts) : StochasticEigenSolver(opts.get("NumEigs",-1),
27  opts.get("RelativeTolerance",0.0),
28  opts.get("AbsoluteTolerance",0.0),
29  opts.get("ExpectedRank", -1),
30  opts.get("OversamplingFactor", -1),
31  opts.get("BlockSize",10),
32  opts.get("Verbosity",0))
33 {}
34 
35 
36 StochasticEigenSolver& StochasticEigenSolver::compute(std::shared_ptr<LinearOperator> const& A,
37  std::shared_ptr<LinearOperator> B,
38  std::shared_ptr<LinearOperator> Binv)
39 {
40  if(numEigs<0){
41  numEigs = A->cols();
42  if(expectedRank<0)
44  if(samplingFactor<0)
45  samplingFactor = std::ceil(0.1*expectedRank);
46  }
47 
48  assert((B!=nullptr)==(Binv!=nullptr));
49 
50  const int dim = A->cols();
51 
52  Eigen::MatrixXd randMat; // <- Will hold random draws from standard normal
53  Eigen::MatrixXd Y; // <- will hold B^{-1}*A*randMat
54  bool hasConverged = false;
55 
56  randMat = RandomGenerator::GetNormal(dim, expectedRank+samplingFactor);
57 
58  if(B!=nullptr){
59  Y = Binv->Apply(A->Apply(randMat));
60  }else{
61  Y = A->Apply(randMat);
62  }
63 
64  int it=0;
65  while(!hasConverged){
66 
67  Eigen::MatrixXd Q,R;
68  std::tie(Q,R) = CholeskyQR(Y, B);
69 
70  Eigen::MatrixXd T = Q.transpose()*A->Apply(Q);
71 
72  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> eigSolver(T);
73  eigVals = eigSolver.eigenvalues();
74 
75  auto swaps = GetSortSwaps(eigVals);
76  SortVec(swaps,eigVals);
77 
78  // Check for convergence
79  double smallestVal = eigVals(eigVals.size()-1);
80  double largestVal = eigVals(0);
81 
82  // Convergence because smallestVal is less than absolute tolerance
83  if(smallestVal<eigAbsTol){
84  if(verbosity>0){
85  std::cout << "Converged: Minimum eigenvalue (" << smallestVal << ") satisfies absolute tolerance (" << eigAbsTol << ")." << std::endl;
86  }
87  hasConverged = true;
88  }
89 
90  // Convergence because smallestVal is less than relative tolerance
91  if(smallestVal<eigRelTol*largestVal){
92  if(verbosity>0){
93  std::cout << "Converged: Minimum eigenvalue (" << smallestVal << ") satisfies relative tolerance (" << eigRelTol*largestVal << ")." << std::endl;
94  }
95  hasConverged = true;
96  }
97 
98  // Convergence because all eigenvalues of operator have been found
99  if(Q.cols()<Y.cols()){
100  if(verbosity>0){
101  std::cout << "Converged: All nonzero eigenvalues have been found (likely) or samples are degenerate (unlikely)." << std::endl;
102  }
103  hasConverged = true;
104  }
105 
106  // Converge because the maximum number of eigenvalues has been found
107  if(eigVals.size()>=numEigs){
108  if(verbosity>0){
109  std::cout << "Converged: Reached maximum number of eigenvalues." << std::endl;
110  }
111  hasConverged = true;
112  }
113 
114  if((verbosity>1)&&(!hasConverged)){
115  std::cout << "After iteration " << it << ", " << eigVals.size() << " eigenvalues in [" << smallestVal << "," << largestVal << "]" << std::endl;
116  std::cout << " Y.shape = " << Y.rows() << " x " << Y.cols() << std::endl;
117  }
118  it++;
119 
120  // If we haven't found enough eigenvalues yet, add to the random matrix
121  if(!hasConverged){
122  randMat.conservativeResize(Eigen::NoChange, randMat.cols()+blockSize);
123  Y.conservativeResize(Eigen::NoChange, Y.cols()+blockSize);
124 
125  randMat.rightCols(blockSize) = RandomGenerator::GetNormal(dim,blockSize);
126 
127  if(B!=nullptr){
128  Y.rightCols(blockSize) = Binv->Apply(A->Apply(randMat.rightCols(blockSize)));
129  }else{
130  Y.rightCols(blockSize) = A->Apply(randMat.rightCols(blockSize));
131  }
132  }
133 
134  // If we've converged, set the eigenvectors
135  if(hasConverged){
136  eigVecs = Q*eigSolver.eigenvectors();
137  SortCols(swaps, eigVecs);
138  }
139 
140  }
141 
142  // Make sure we're only keeping eigenvalues that satisfy tolerances
143  double largestVal = eigVals(0);
144  unsigned int numKeep = 0;
145  for(int i=0; i<std::min<int>(eigVals.size(), numEigs); ++i){
146  if((eigVals(i)>eigRelTol*largestVal)&&(eigVals(i)>eigAbsTol)){
147  numKeep++;
148  }else{
149  break;
150  }
151  }
152 
153  eigVals = eigVals.head(numKeep).eval();
154  eigVecs = eigVecs.leftCols(numKeep).eval();
155 
156  return *this;
157 }
158 
159 
160 std::pair<Eigen::MatrixXd, Eigen::MatrixXd> StochasticEigenSolver::CholeskyQR(Eigen::MatrixXd const& Y,
161  std::shared_ptr<LinearOperator> const& B) const
162 {
163 
164  auto qr = Y.colPivHouseholderQr();
165  unsigned int rank = qr.rank();
166 
167  Eigen::MatrixXd Z = qr.householderQ().setLength(qr.nonzeroPivots()) * Eigen::MatrixXd::Identity(Y.rows(),rank);
168  Eigen::MatrixXd Ry = qr.matrixR().topLeftCorner(rank, rank).template triangularView<Eigen::Upper>();
169 
170  if(B==nullptr){
171  return std::make_pair(Z,Ry);
172  }
173 
174  auto chol = (Z.transpose()*B->Apply(Z)).llt();
175 
176  Eigen::MatrixXd Q = chol.matrixL().solve(Z.transpose()).transpose();
177  Eigen::MatrixXd R = chol.matrixL()*Ry;
178 
179  return std::make_pair(Q,R);
180 }
static std::vector< std::pair< int, int > > GetSortSwaps(Eigen::Ref< const Eigen::VectorXd > const &residNorms, std::vector< bool > const &isActive)
std::shared_ptr< LinearOperator > A
static void SortVec(std::vector< std::pair< int, int >> const &swapInds, Eigen::Ref< Eigen::VectorXd > matrix)
static void SortCols(std::vector< std::pair< int, int >> const &swapInds, Eigen::Ref< Eigen::MatrixXd > matrix)
std::shared_ptr< LinearOperator > B
Two-pass stochastic algorithm for computing generalized eigenvalues from matrix products.
StochasticEigenSolver(int numEigsIn, double eigRelTolIn=0.0, double eigAbsTolIn=0.0, int expectedRankIn=-1, int samplingFactorIn=-1, int blockSize=10, int verbosityIn=0)
std::pair< Eigen::MatrixXd, Eigen::MatrixXd > CholeskyQR(Eigen::MatrixXd const &Y, std::shared_ptr< LinearOperator > const &B) const
virtual StochasticEigenSolver & compute(std::shared_ptr< LinearOperator > const &A, std::shared_ptr< LinearOperator > B=nullptr, std::shared_ptr< LinearOperator > Binv=nullptr)
auto get(const nlohmann::detail::iteration_proxy_value< IteratorType > &i) -> decltype(i.key())
Definition: json.h:3956