Loading...
Searching...
No Matches
ConjugateGradients.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*/
13#include "eon/SafeMath.h"
14
15#include <cmath>
16
17namespace eonc {
18
19Eigen::VectorXd ConjugateGradients::getStep() {
20 double a = std::abs(m_force.dot(m_forceOld));
21 double b = m_forceOld.squaredNorm();
22 double gamma = 0.0;
23 if (a < 0.5 * b) {
24 // Polak-Ribiere way to determine how much to mix in of old direction
25 gamma = eonc::safemath::safe_div(m_force.dot(m_force - m_forceOld), b, 0.0);
26 } else {
27 gamma = 0;
28 }
31 eonc::safemath::safe_normalize_inplace(m_directionNorm);
34
35 // Only if value for max nr of iteration before reset
36 if (m_optConfig.opts.cg.max_iter_before_reset > 0 &&
37 m_optConfig.opts.cg.max_iter_before_reset <= m_cg_i) {
38 m_cg_i = 0;
39 m_forceOld.setZero();
40 m_directionOld.setZero();
41 }
42 m_cg_i += 1;
43
44 return m_direction;
45}
46
47int ConjugateGradients::step(double a_maxMove) {
48 bool converged;
49 if (m_optConfig.opts.cg.line_search) {
50 converged = line_search(a_maxMove);
51 } else {
52 converged = single_step(a_maxMove);
53 }
54 if (converged)
55 return 1;
56 return 0;
57}
58
59int ConjugateGradients::line_search(double a_maxMove) {
60 Eigen::VectorXd pos;
61 Eigen::VectorXd posStep;
62 Eigen::VectorXd forceBeforeStep;
63 double stepSize;
64 double projectedForce;
65 double projectedForceBeforeStep;
66 double curvature;
67
68 forceBeforeStep = -m_objf->getGradient();
69 m_force = forceBeforeStep;
70 getStep();
71 pos = m_objf->getPositions();
72 projectedForceBeforeStep = m_force.dot(m_directionNorm);
73
74 // move system an infinitesimal step to determine the optimal step size along
75 // the search line
76 posStep = pos + m_directionNorm * m_optConfig.finiteDifference;
77 m_objf->setPositions(posStep);
78 m_force = -m_objf->getGradient(true);
79 projectedForce = m_force.dot(m_directionNorm);
80 stepSize = m_optConfig.finiteDifference;
81
82 int line_i = 0;
83 do {
84 // Determine curvature from last step (Secant method)
85 curvature = std::abs(eonc::safemath::safe_div(
86 projectedForceBeforeStep - projectedForce, stepSize, 0.0));
87 // A negative max move is a length (bowl breakout), not a backward step.
88 const double maxMove = std::fabs(a_maxMove);
89 stepSize = eonc::safemath::safe_div(projectedForce, curvature, maxMove);
90
91 if (maxMove < std::fabs(stepSize)) {
92 // first part get the sign of stepSize
93 stepSize = ((stepSize > 0) - (stepSize < 0)) * maxMove;
94 }
95
96 forceBeforeStep = m_force;
97 projectedForceBeforeStep = projectedForce;
98
99 pos += stepSize * m_directionNorm;
100 m_objf->setPositions(pos);
101 m_force = -m_objf->getGradient();
102 projectedForce = m_force.dot(m_directionNorm);
103
104 line_i += 1;
105
106 // Line search considered converged based in the ratio between the projected
107 // force and the norm of the true force
108 } while (m_optConfig.opts.cg.line_converged <
109 std::abs(projectedForce) /
110 (std::sqrt(m_force.dot(m_force) +
111 m_optConfig.opts.cg.line_converged)) &&
112 line_i < m_optConfig.opts.cg.line_search_max_iter);
113 if (m_objf->isConverged())
114 return 1;
115 return 0;
116}
117
118int ConjugateGradients::single_step(double a_maxMove) {
119 Eigen::VectorXd pos;
120 Eigen::VectorXd posStep;
121 Eigen::VectorXd forceAfterStep;
122
123 m_force = -m_objf->getGradient();
124 pos = m_objf->getPositions();
125 getStep();
126
127 // move system an infinitesimal step to determine the optimal step size along
128 // the search line
129 posStep = pos + m_directionNorm * m_optConfig.finiteDifference;
130 m_objf->setPositions(posStep);
131 forceAfterStep = -m_objf->getGradient(true);
132
133 // Determine curvature
134 double projectedForce1 = m_force.dot(m_directionNorm);
135 double projectedForce2 = forceAfterStep.dot(m_directionNorm);
136 double curvature = eonc::safemath::safe_div(
137 projectedForce1 - projectedForce2, m_optConfig.finiteDifference, 0.0);
138
139 double stepSize = a_maxMove;
140
141 if (curvature > 0.0) {
142 stepSize = eonc::safemath::safe_div(projectedForce1, curvature, a_maxMove);
143 }
144
145 if (m_optConfig.bowlBreakout && a_maxMove < 0.0) {
146 stepSize = -a_maxMove;
147 a_maxMove = -a_maxMove;
148 }
149
150 if (!m_optConfig.opts.cg.no_overshooting) {
151 if (m_optConfig.bowlBreakout) {
152 // max displacement is based on system not single atom
154 a_maxMove);
155 } else {
157 a_maxMove);
158 }
159 m_objf->setPositions(pos);
160 } else {
161 // negative if product of the projected forces before and after the step are
162 // in opposite directions
163 double passedMinimum = -1.;
164 double forceChange = 0.;
165 while (passedMinimum < 0.0 &&
166 0.1 * std::abs(projectedForce1) < std::abs(projectedForce2)) {
168 stepSize * m_directionNorm, a_maxMove);
169 m_objf->setPositions(posStep);
170 forceAfterStep = -m_objf->getGradient(true);
171 projectedForce2 = forceAfterStep.dot(m_directionNorm);
172
173 passedMinimum = projectedForce1 * projectedForce2;
174 if (passedMinimum < 0.0 &&
175 0.1 * std::abs(projectedForce1) < std::abs(projectedForce2)) {
176 forceChange = (projectedForce1 - projectedForce2);
177 stepSize = eonc::safemath::safe_div(projectedForce1, forceChange, 0.0) *
178 stepSize;
179 QUILL_LOG_DEBUG(m_log, "Force changed {}, step size adjusted to {}",
180 forceChange, stepSize);
181 }
182 }
183 }
184 if (m_optConfig.opts.cg.knock_out_max_move) {
185 if (stepSize >= a_maxMove) {
186 // knockout old search direction
187 m_directionOld.setZero();
188 m_forceOld.setZero();
189 QUILL_LOG_DEBUG(m_log, "Resetting the old search direction");
190 }
191 }
192
193 return m_objf->isConverged() ? 1 : 0;
194}
195
196int ConjugateGradients::run(size_t a_maxIterations, double a_maxMove) {
197 size_t iterations = 0;
198 while (!m_objf->isConverged() && iterations < a_maxIterations) {
199 step(a_maxMove);
200 iterations++;
201 }
202 return m_objf->isConverged() ? 1 : 0;
203}
204
205} // namespace eonc
Direct optimization for energy minimization.
int single_step(double a_maxMove)
Steps the conjugate gradient.
Eigen::VectorXd m_force
Current force vector.
Eigen::VectorXd getStep()
Gets the direction of the next step.
Eigen::VectorXd m_directionNorm
Normalised version of the current direction vector.
Eigen::VectorXd m_directionOld
Algorithms previous step direction.
int run(size_t a_maxIterations, double a_maxMove) override
Runs the conjugate gradient.
size_t m_cg_i
Counts the number of discrete steps until algorithm convergence.
Eigen::VectorXd m_direction
Current step direction of the conjugate gradient.
int step(double a_maxMove) override
Calls the next step in the algorithm.
Eigen::VectorXd m_forceOld
Previous force vector.
eonc::log::FileScoped m_log
int line_search(double a_maxMove)
Steps the conjugate gradient.
const OptimizerConfig m_optConfig
Definition Optimizer.h:69
std::shared_ptr< ObjectiveFunction > m_objf
Definition Optimizer.h:70
VectorXd maxMotionAppliedV(const VectorXd v1, double maxMotion)
VectorXd maxAtomMotionAppliedV(const VectorXd v1, double maxMotion)
constexpr double safe_div(double num, double denom, double fallback=0.0)
Definition SafeMath.h:21
RAII resource manager for the ARTn C library with global synchronization.