Loading...
Searching...
No Matches
LBFGS.cpp
Go to the documentation of this file.
1/*
2** This file is part of eOn.
3**
4** SPDX-License-Identifier: BSD-3-Clause
5**
6** Copyright (c) 2010--present, eOn Development Team
7** All rights reserved.
8**
9** Repo:
10** https://github.com/TheochemUI/eOn
11*/
12// Based on the LBFGS minimizer written in ASE.
13
14#include "eon/LBFGS.h"
15#include "eon/SafeMath.h"
16
17#include <cmath>
18
19Eigen::VectorXd LBFGS::getStep(double a_maxMove, const Eigen::VectorXd &a_f) {
20 double H0 = m_optConfig.opts.lbfgs.inverse_curvature;
21 Eigen::VectorXd r = m_objf->getPositions();
22
23 if (m_iteration > 0) {
24 Eigen::VectorXd dr = m_objf->difference(r, m_rPrev);
25 // double C = dr.dot(fPrev-f)/dr.dot(dr);
26 double C = eonc::safemath::safe_div((m_fPrev - a_f).dot(m_fPrev - a_f),
27 dr.dot(m_fPrev - a_f), -1.0);
28 if (C < 0) {
29 QUILL_LOG_DEBUG(
30 m_log, "[LBFGS] Negative curvature: {:.4f} eV/A^2 take max move step",
31 C);
32 reset();
33 return eonc::helpers::maxAtomMotionAppliedV(1000 * a_f, a_maxMove);
34 }
35
36 if (m_optConfig.opts.lbfgs.auto_scale) {
37 H0 = eonc::safemath::safe_recip(C, -1.0);
38 QUILL_LOG_DEBUG(m_log, "[LBFGS] Curvature: {:.4e} eV/A^2", C);
39 }
40 }
41
42 if (m_iteration == 0 && m_optConfig.opts.lbfgs.auto_scale) {
43 m_objf->setPositions(r + m_optConfig.finiteDifference *
44 eonc::safemath::safe_normalized(a_f));
45 Eigen::VectorXd dg = m_objf->getGradient(true) + a_f;
46 double C = dg.dot(eonc::safemath::safe_normalized(a_f)) /
47 m_optConfig.finiteDifference;
48 H0 = eonc::safemath::safe_recip(C, -1.0);
49 m_objf->setPositions(r);
50 if (H0 < 0) {
51 QUILL_LOG_WARNING(m_log,
52 "[LBFGS] Negative curvature calculated via FD: {:.4e} "
53 "eV/A^2, take max move step",
54 C);
55 reset();
56 return eonc::helpers::maxAtomMotionAppliedV(1000 * a_f, a_maxMove);
57 } else {
58 QUILL_LOG_DEBUG(m_log,
59 "[LBFGS] Curvature calculated via FD: {:.4e} eV/A^2", C);
60 }
61 }
62
63 int loopmax = m_s.size();
64 std::vector<double> a(loopmax);
65
66 Eigen::VectorXd q = -a_f;
67
68 for (int i = loopmax - 1; i >= 0; i--) {
69 a[i] = m_rho[i] * m_s[i].dot(q);
70 q -= a[i] * m_y[i];
71 }
72
73 Eigen::VectorXd z = H0 * q;
74
75 for (int i = 0; i < loopmax; i++) {
76 double b = m_rho[i] * m_y[i].dot(z);
77 z += m_s[i] * (a[i] - b);
78 }
79
80 Eigen::VectorXd d = -z;
81
82 double distance = eonc::helpers::maxAtomMotionV(d);
83 if (distance >= a_maxMove && m_optConfig.opts.lbfgs.distance_reset) {
84 QUILL_LOG_DEBUG(m_log,
85 "[LBFGS] reset memory, proposed step too large: {:.4f}",
86 distance);
87 reset();
88 return eonc::helpers::maxAtomMotionAppliedV(H0 * a_f, a_maxMove);
89 }
90
91 double vd = eonc::safemath::safe_normalized(d).dot(
92 eonc::safemath::safe_normalized(a_f));
93 if (vd > 1.0)
94 vd = 1.0;
95 if (vd < -1.0)
96 vd = -1.0;
97 double angle = eonc::safemath::safe_acos(vd) * (180.0 / eonc::helpers::pi);
98 if (angle > 90.0 && m_optConfig.opts.lbfgs.angle_reset) {
99 QUILL_LOG_DEBUG(m_log,
100 "[LBFGS] reset memory, angle between LBFGS angle and "
101 "force too large: {:.4f}",
102 angle);
103 reset();
104 return eonc::helpers::maxAtomMotionAppliedV(H0 * a_f, a_maxMove);
105 }
106
107 return eonc::helpers::maxAtomMotionAppliedV(d, a_maxMove);
108}
109
111 m_s.clear();
112 m_y.clear();
113 m_rho.clear();
114}
115
116int LBFGS::update(const Eigen::VectorXd &a_r1, const Eigen::VectorXd &a_r0,
117 const Eigen::VectorXd &a_f1, const Eigen::VectorXd &a_f0) {
118 Eigen::VectorXd s0 = m_objf->difference(a_r1, a_r0);
119
120 // y0 is the change in the gradient, not the force
121 Eigen::VectorXd y0 = a_f0 - a_f1;
122
123 // Skip degenerate curvature update (reset memory instead of aborting)
124 if (std::abs(s0.dot(y0)) < LBFGS_EPS) {
125 QUILL_LOG_WARNING(m_log,
126 "[LBFGS] s0.y0 too small ({:.4e}), resetting memory",
127 s0.dot(y0));
128 reset();
129 return 0;
130 }
131
132 m_rho.push_back(eonc::safemath::safe_recip(s0.dot(y0), 0.0));
133 m_s.push_back(std::move(s0));
134 m_y.push_back(std::move(y0));
135
136 if (static_cast<int>(m_s.size()) > m_memory) {
137 m_s.pop_front();
138 m_y.pop_front();
139 m_rho.pop_front();
140 }
141 return 0;
142}
143
144int LBFGS::step(double a_maxMove) {
145 int status = 0;
146 Eigen::VectorXd r = m_objf->getPositions();
147 Eigen::VectorXd f = -m_objf->getGradient();
148
149 if (m_iteration > 0) {
150 status = update(r, m_rPrev, f, m_fPrev);
151 }
152 if (status < 0)
153 return -1;
154
155 Eigen::VectorXd dr = getStep(a_maxMove, f);
156
157 m_objf->setPositions(r + dr);
158
159 m_rPrev = r;
160 m_fPrev = f;
161
162 m_iteration++;
163
164 return m_objf->isConverged() ? 1 : 0;
165}
166
167int LBFGS::run(size_t a_maxSteps, double a_maxMove) {
168 int status;
169 while (!m_objf->isConverged() && m_iteration < a_maxSteps) {
170 status = step(a_maxMove);
171 if (status < 0)
172 return -1;
173 }
174 return m_objf->isConverged() ? 1 : 0;
175}
#define LBFGS_EPS
Definition LBFGS.h:25
Eigen::VectorXd getStep(double a_maxMove, const Eigen::VectorXd &a_f)
Definition LBFGS.cpp:19
std::deque< double > m_rho
Definition LBFGS.h:53
Eigen::VectorXd m_fPrev
Definition LBFGS.h:56
int m_iteration
Definition LBFGS.h:48
std::deque< Eigen::VectorXd > m_y
Definition LBFGS.h:52
Eigen::VectorXd getStep(double a_maxMove, const Eigen::VectorXd &a_f)
Definition LBFGS.cpp:19
std::deque< Eigen::VectorXd > m_s
Definition LBFGS.h:51
Eigen::VectorXd m_rPrev
Definition LBFGS.h:55
int run(size_t a_maxIterations, double a_maxMove) override
Definition LBFGS.cpp:167
int m_memory
Definition LBFGS.h:49
void reset(void)
Definition LBFGS.cpp:110
int step(double a_maxMove) override
Definition LBFGS.cpp:144
int update(const Eigen::VectorXd &a_r1, const Eigen::VectorXd &a_r0, const Eigen::VectorXd &a_f1, const Eigen::VectorXd &a_f0)
Definition LBFGS.cpp:116
eonc::log::FileScoped m_log
Definition LBFGS.h:57
const OptimizerConfig m_optConfig
Definition Optimizer.h:67
std::shared_ptr< ObjectiveFunction > m_objf
Definition Optimizer.h:68
constexpr double pi
VectorXd maxAtomMotionAppliedV(const VectorXd v1, double maxMotion)
double maxAtomMotionV(const VectorXd v1)
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition SafeMath.h:21
double safe_acos(double x)
Definition SafeMath.h:34
constexpr double safe_recip(double x, double fallback=0.0)
Definition SafeMath.h:29