24 double eps_subsp = 1.e-3;
25 double alpha0 = 1.e-2;
26 std::unique_ptr<hlist_space::HistoryList> xlist;
27 std::unique_ptr<hlist_space::HistoryList> flist;
29 Eigen::VectorXd dir_of_descent;
31 Eigen::VectorXd prev_df_dx;
32 Eigen::VectorXd expected_positions;
33 Eigen::MatrixXd h_subsp;
34 Eigen::MatrixXd h_evec_subsp;
35 Eigen::MatrixXd h_evec;
36 Eigen::VectorXd h_eval;
38 Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> esolve;
39 Eigen::VectorXd res_temp;
41 bool estimate_step_size =
false;
56 SQNM(
int ndim_,
int nhistx_,
double alpha_) {
61 this->estimate_step_size =
true;
62 this->alpha = -alpha_;
68 xlist = std::make_unique<hlist_space::HistoryList>(ndim, nhistx);
69 flist = std::make_unique<hlist_space::HistoryList>(ndim, nhistx);
86 SQNM(
int ndim_,
int nhistx_,
double alpha_,
double alpha0_,
double eps_subsp_) {
91 this->estimate_step_size =
true;
92 this->alpha = -alpha_;
97 xlist = std::make_unique<hlist_space::HistoryList>(ndim, nhistx);
98 flist = std::make_unique<hlist_space::HistoryList>(ndim, nhistx);
100 eps_subsp = eps_subsp_;
115 Eigen::VectorXd
step(Eigen::VectorXd &x,
double &f_of_x, Eigen::VectorXd &df_dx) {
118 if (df_dx.norm() <= 10.0e-13)
120 this->dir_of_descent.setZero(ndim);
121 return this->dir_of_descent;
124 nhist = xlist->add(x);
127 this->dir_of_descent = - alpha * df_dx;
131 if ((x - expected_positions).norm() > 10e-8)
133 std::cerr <<
"SQNM was not called with positions that were expected. If this was not done on purpose, it is probably a bug.\n";
134 std::cerr <<
"Were atoms that left the simulation box put back into the cell? This is not allowed.\n";
137 if (this->estimate_step_size)
139 double prev_df_squared = std::pow(prev_df_dx.norm(), 2);
140 double l1 = (f_of_x - prev_f + alpha * prev_df_squared) / (0.5 * alpha * alpha * prev_df_squared);
141 double l2 = (df_dx - prev_df_dx).norm() / (alpha * prev_df_dx.norm());
142 alpha = 1.0 / std::max(l1, l2);
143 std::cout <<
"Automatic initial step size guess: " << alpha <<
'\n';
144 this->estimate_step_size =
false;
147 double gainratio = calc_gainratio(f_of_x);
148 adjust_stepsize(gainratio);
151 Eigen::MatrixXd S = calc_ovrlp();
153 Eigen::VectorXd S_eval = esolve.eigenvalues();
154 Eigen::MatrixXd S_evec = esolve.eigenvectors();
158 for (
int i = 0; i < S_eval.size(); i++){
159 if (S_eval(i) / S_eval(nhist-1) > eps_subsp)
165 for (
int i = 0; i < dim_subsp; i++)
167 S_evec.col(i) = S_evec.col(nhist - dim_subsp + i);
168 S_eval(i) = S_eval(nhist - dim_subsp + i);
171 Eigen::MatrixXd dr_subsp(ndim, dim_subsp);
173 for (
int i = 0; i < dim_subsp; i++) {
174 for (
int ihist = 0; ihist < nhist; ihist++){
175 dr_subsp.col(i) += S_evec(ihist, i) * xlist->normalized_difflist.col(ihist);
177 dr_subsp.col(i) /= sqrt(S_eval(i));
181 Eigen::MatrixXd df_subsp(ndim, dim_subsp);
183 for (
int i = 0; i < dim_subsp; i++) {
184 for (
int ihist = 0; ihist < nhist; ihist++){
185 df_subsp.col(i) += S_evec(ihist, i) * flist->difflist.col(ihist) / xlist->difflist.col(ihist).norm();
187 df_subsp.col(i) /= sqrt(S_eval(i));
190 h_subsp = .5 * (df_subsp.transpose() * dr_subsp + dr_subsp.transpose() * df_subsp);
191 esolve.compute(h_subsp);
192 h_eval = esolve.eigenvalues();
193 h_evec_subsp = esolve.eigenvectors();
196 h_evec.resize(ndim, dim_subsp);
198 for (
int i = 0; i < dim_subsp; i++){
199 for (
int k = 0; k < dim_subsp; k++){
200 h_evec.col(i) += h_evec_subsp(k, i) * dr_subsp.col(k);
205 res.resize(dim_subsp);
206 for (
int j = 0; j < dim_subsp; j++){
207 res_temp = - h_eval(j) * h_evec.col(j);
208 for (
int k = 0; k < dim_subsp; k++){
209 res_temp += h_evec_subsp(k, j) * df_subsp.col(k);
211 res(j) = res_temp.norm();
215 for (
int idim = 0; idim < dim_subsp; idim++){
216 h_eval(idim) = sqrt(pow(h_eval(idim), 2) + pow(res(idim), 2));
220 dir_of_descent = df_dx;
221 for (
int i = 0; i < dim_subsp; i++){
222 dir_of_descent -= h_evec.col(i).dot(df_dx) * h_evec.col(i);
224 dir_of_descent *= alpha;
227 for (
int idim = 0; idim < dim_subsp; idim++)
229 dir_of_descent += (df_dx.dot(h_evec.col(idim)) / h_eval(idim)) * h_evec.col(idim);
231 dir_of_descent *= -1.0;
234 expected_positions = x + dir_of_descent;
237 return this->dir_of_descent;
247 if (this->nhist == 0) {
248 std::cout <<
"No lower bound estimate can be given yet.";
251 return this->prev_f - 0.5 * this->prev_df_dx.dot(this->prev_df_dx) / this->h_eval(0);
255 double calc_gainratio(
double &f){
256 double gr = (f - prev_f) / ( .5 * this->dir_of_descent.dot(prev_df_dx));
260 void adjust_stepsize(
double &gainratio){
261 if ( gainratio < 0.5 ) alpha = std::max(alpha * 0.65, alpha0);
262 else if(gainratio > 1.05) alpha = alpha * 1.05;
265 Eigen::MatrixXd calc_ovrlp(){
266 Eigen::MatrixXd S = xlist->normalized_difflist.block(0,0, ndim, nhist).transpose() * xlist->normalized_difflist.block(0,0, ndim, nhist);
double lower_bound()
Estimates a lower bound of the energy of the local minimum.
Eigen::VectorXd step(Eigen::VectorXd &x, double &f_of_x, Eigen::VectorXd &df_dx)
Calculates new coordinates that are closer to local minimum that the current coordinates....
SQNM(int ndim_, int nhistx_, double alpha_, double alpha0_, double eps_subsp_)
Construct a new SQNM::SQNM object using custom parameters.
SQNM(int ndim_, int nhistx_, double alpha_)
Construct a new SQNM::SQNM object using default parameters.
Implementation of the SQNM method. More informations about the algorithm can be found here: https://a...
Variable cell stable quasi-Newton lattice optimizer.