137 {
139 if (
step ==
"newton" ||
step ==
"rfo") {
141 }
142 double H0 =
m_optConfig.opts.lbfgs.inverse_curvature;
143 Eigen::VectorXd r =
m_objf->getPositions();
144 Eigen::VectorXd f = a_f;
145 maybeProjectRigid(f, r,
m_optConfig.opts.lbfgs.project_rigid);
146
149 const Eigen::VectorXd y =
m_fPrev - f;
150 const double sy = dr.dot(y);
151 const double yy = y.squaredNorm();
152 const double ss = dr.squaredNorm();
154 if (C < 0) {
155 QUILL_LOG_DEBUG(
156 m_log,
"[LBFGS] Negative curvature: {:.4f} eV/A^2 take max move step",
157 C);
160 }
162 }
163
164 if (
m_optConfig.opts.lbfgs.auto_scale && sy > 0.0 && yy > 0.0) {
165 const double g_syyy = sy / yy;
166 const double g_sssy = ss / sy;
168 if (h0 == "ss_sy") {
169 H0 = g_sssy;
170 } else if (h0 == "adaptive") {
171 H0 = std::min(g_syyy, g_sssy);
172 } else {
173 H0 = g_syyy;
174 }
175 if (
m_optConfig.opts.lbfgs.max_inverse_curvature > 0.0) {
176 H0 = std::min(H0,
m_optConfig.opts.lbfgs.max_inverse_curvature);
177 }
178 QUILL_LOG_DEBUG(
m_log,
"[LBFGS] H0={:.4e} ({} scale)", H0, h0);
179 }
180 }
181
182 const std::optional<double> known =
184 ?
m_objf->knownCurvature()
185 : std::nullopt;
186 if (known && std::isfinite(*known) && *known > 0.0) {
187 H0 = 1.0 / *known;
188 if (
m_optConfig.opts.lbfgs.max_inverse_curvature > 0.0) {
189 H0 = std::min(H0,
m_optConfig.opts.lbfgs.max_inverse_curvature);
190 }
191 QUILL_LOG_DEBUG(
m_log,
"[LBFGS] H0={:.4e} (known curvature)", H0);
193 m_objf->supportsFiniteDifferenceCurvature()) {
195 eonc::safemath::safe_normalized(a_f));
196 Eigen::VectorXd dg =
m_objf->getGradient(
true) + a_f;
197 const double C = dg.dot(eonc::safemath::safe_normalized(a_f)) /
201 if (H0 < 0) {
202 QUILL_LOG_WARNING(
m_log,
203 "[LBFGS] Negative curvature calculated via FD: {:.4e} "
204 "eV/A^2, take max move step",
205 C);
208 }
209 QUILL_LOG_DEBUG(
m_log,
"[LBFGS] Curvature calculated via FD: {:.4e} eV/A^2",
210 C);
211 }
212
213 const auto idx =
214 twoLoopIndex(
static_cast<int>(
m_s.size()),
215 static_cast<int>(
m_optConfig.opts.lbfgs.extra_updates));
216 const int loopmax = static_cast<int>(idx.size());
217 std::vector<double> a(static_cast<size_t>(loopmax));
218
219 Eigen::VectorXd q = -f;
220
221 for (int k = loopmax - 1; k >= 0; k--) {
222 const int i = idx[static_cast<size_t>(k)];
223 a[static_cast<size_t>(k)] =
224 m_rho[
static_cast<size_t>(i)] *
m_s[
static_cast<size_t>(i)].dot(q);
225 q -= a[
static_cast<size_t>(k)] *
m_y[
static_cast<size_t>(i)];
226 }
227
228 Eigen::VectorXd z =
applyH0(q, H0, r);
229
230 for (int k = 0; k < loopmax; k++) {
231 const int i = idx[static_cast<size_t>(k)];
232 const double b =
233 m_rho[
static_cast<size_t>(i)] *
m_y[
static_cast<size_t>(i)].dot(z);
234 z +=
m_s[
static_cast<size_t>(i)] * (a[
static_cast<size_t>(k)] - b);
235 }
236
237 Eigen::VectorXd d = -z;
238
240 if (distance >= a_maxMove &&
m_optConfig.opts.lbfgs.distance_reset) {
241 QUILL_LOG_DEBUG(
m_log,
242 "[LBFGS] reset memory, proposed step too large: {:.4f}",
243 distance);
246 }
247
248 double vd = eonc::safemath::safe_normalized(d).dot(
249 eonc::safemath::safe_normalized(f));
250 if (vd > 1.0)
251 vd = 1.0;
252 if (vd < -1.0)
253 vd = -1.0;
255 if (angle > 90.0 &&
m_optConfig.opts.lbfgs.angle_reset) {
256 QUILL_LOG_DEBUG(
m_log,
257 "[LBFGS] reset memory, angle between LBFGS angle and "
258 "force too large: {:.4f}",
259 angle);
262 }
263
264 maybeProjectRigid(d, r,
m_optConfig.opts.lbfgs.project_rigid);
266}
std::deque< double > m_rho
std::deque< Eigen::VectorXd > m_y
std::deque< Eigen::VectorXd > m_s
int step(double a_maxMove) override
Eigen::VectorXd hessianStep(double a_maxMove, const Eigen::VectorXd &a_f)
Eigen::VectorXd applyH0(const Eigen::VectorXd &q, double H0, const Eigen::VectorXd &pos) const
VectorXd maxAtomMotionAppliedV(const VectorXd v1, double maxMotion)
double maxAtomMotionV(const VectorXd v1)
constexpr double safe_div(double num, double denom, double fallback=0.0)
double safe_acos(double x)
constexpr double safe_recip(double x, double fallback=0.0)