eOn client
Long-timescale dynamics: aKMC, NEB, parallel replica
☾
Toggle main menu visibility
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
19
Eigen::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
110
void
LBFGS::reset
() {
111
m_s
.clear();
112
m_y
.clear();
113
m_rho
.clear();
114
}
115
116
int
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
144
int
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
167
int
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
}
LBFGS.h
LBFGS_EPS
#define LBFGS_EPS
Definition
LBFGS.h:25
SafeMath.h
LBFGS::getStep
Eigen::VectorXd getStep(double a_maxMove, const Eigen::VectorXd &a_f)
Definition
LBFGS.cpp:19
eonc::LBFGS::m_rho
std::deque< double > m_rho
Definition
LBFGS.h:53
eonc::LBFGS::m_fPrev
Eigen::VectorXd m_fPrev
Definition
LBFGS.h:56
eonc::LBFGS::m_iteration
int m_iteration
Definition
LBFGS.h:48
eonc::LBFGS::m_y
std::deque< Eigen::VectorXd > m_y
Definition
LBFGS.h:52
eonc::LBFGS::getStep
Eigen::VectorXd getStep(double a_maxMove, const Eigen::VectorXd &a_f)
Definition
LBFGS.cpp:19
eonc::LBFGS::m_s
std::deque< Eigen::VectorXd > m_s
Definition
LBFGS.h:51
eonc::LBFGS::m_rPrev
Eigen::VectorXd m_rPrev
Definition
LBFGS.h:55
eonc::LBFGS::run
int run(size_t a_maxIterations, double a_maxMove) override
Definition
LBFGS.cpp:167
eonc::LBFGS::m_memory
int m_memory
Definition
LBFGS.h:49
eonc::LBFGS::reset
void reset(void)
Definition
LBFGS.cpp:110
eonc::LBFGS::step
int step(double a_maxMove) override
Definition
LBFGS.cpp:144
eonc::LBFGS::update
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::LBFGS::m_log
eonc::log::FileScoped m_log
Definition
LBFGS.h:57
eonc::Optimizer::m_optConfig
const OptimizerConfig m_optConfig
Definition
Optimizer.h:67
eonc::Optimizer::m_objf
std::shared_ptr< ObjectiveFunction > m_objf
Definition
Optimizer.h:68
eonc::helpers::pi
constexpr double pi
Definition
HelperFunctions.h:28
eonc::helpers::maxAtomMotionAppliedV
VectorXd maxAtomMotionAppliedV(const VectorXd v1, double maxMotion)
Definition
GeometryAnalysis.cpp:319
eonc::helpers::maxAtomMotionV
double maxAtomMotionV(const VectorXd v1)
Definition
GeometryAnalysis.cpp:273
eonc::safemath::safe_div
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition
SafeMath.h:21
eonc::safemath::safe_acos
double safe_acos(double x)
Definition
SafeMath.h:34
eonc::safemath::safe_recip
constexpr double safe_recip(double x, double fallback=0.0)
Definition
SafeMath.h:29
client
LBFGS.cpp
Generated by
1.17.0
Generated by
Doxygen 1.17.0
Analytics by
Antics
provided by
TurtleTech ehf