Source code for pysisyphus.optimizers.BacktrackingOptimizer

import numpy as np

from pysisyphus.optimizers.Optimizer import Optimizer

[docs] class BacktrackingOptimizer(Optimizer): def __init__(self, geometry, alpha, bt_force=5, dont_skip_after=2, bt_max_scale=4, bt_disable=False, **kwargs): # Setting some default values self.alpha = alpha assert(self.alpha > 0), "Alpha must be positive!" self.bt_force = bt_force self.dont_skip_after = dont_skip_after self.bt_max_scale = bt_max_scale self.bt_disable = bt_disable assert(self.dont_skip_after >= 1) self.cycles_since_backtrack = self.bt_force self.scale_factor = 0.5 super(BacktrackingOptimizer, self).__init__(geometry, **kwargs) self.alpha0 = self.alpha self.alpha_max = self.bt_max_scale * self.alpha0 # Keep the skipping history to avoid infinite skipping, e.g. always # return skip = False if we already skipped in the last n iterations. self.skip_log = list() def _get_opt_restart_info(self): opt_restart_info = { "alpha": self.alpha, "cycles_since_backtrack": self.cycles_since_backtrack, } return opt_restart_info def _set_opt_restart_info(self, opt_restart_info): self.alpha = opt_restart_info["alpha"] self.cycles_since_backtrack = opt_restart_info["cycles_since_backtrack"]
[docs] def reset(self): if self.alpha > self.alpha0: self.alpha = self.alpha0 self.log(f"Resetting! Current alpha is {self.alpha}. Lowering " f"it to {self.alpha0}.")
[docs] def backtrack(self, cur_forces, prev_forces, reset_hessian=None): """Accelerated backtracking line search.""" if self.bt_disable: return False epsilon = 1e-3 rms = lambda f: np.sqrt(np.mean(np.square(f))) cur_rms_force = rms(cur_forces) prev_rms_force = rms(prev_forces) rms_diff = ( (cur_rms_force - prev_rms_force) / np.abs(cur_rms_force + prev_rms_force) ) # Skip tells us if we overshot skip = False # When the optimiziation is converging cur_forces will # be smaller than prev_forces, so rms_diff will be negative # and hence smaller than epsilon, which is a positive number. # We went uphill, slow alpha self.log(f"Backtracking: rms_diff = {rms_diff:.03f}") if rms_diff > epsilon: self.log(f"Scaling alpha with {self.scale_factor:.03f}") # self.alpha = max(self.alpha0*.5, self.alpha*self.scale_factor) self.alpha *= self.scale_factor skip = True self.cycles_since_backtrack = self.bt_force # We continnue going downhill, rms_diff is smaller than epsilon else: self.cycles_since_backtrack -= 1 # Check if we didn't accelerate in the previous cycles if self.cycles_since_backtrack < 0: self.cycles_since_backtrack = self.bt_force if self.alpha < self.alpha0: # Reset alpha self.alpha = self.alpha0 skip = True self.log(f"Reset alpha to alpha0 = {self.alpha0:.4f}") else: # Accelerate alpha self.alpha /= self.scale_factor self.log(f"Scaled alpha to {self.alpha:.4f}") # Avoid huge alphas if self.alpha > self.alpha_max: self.alpha = self.alpha_max self.log("Didn't accelerate as alpha would become too large. " f"keeping it at {self.alpha}.") # Don't skip if we already skipped the previous iterations to # avoid infinite skipping. if ((len(self.skip_log) >= self.dont_skip_after) and all(self.skip_log[-self.dont_skip_after:])): self.log(f"already skipped last {self.dont_skip_after} " "iterations don't skip now.") skip = False if self.alpha > self.alpha0: self.alpha = self.alpha0 self.log("Resetted alpha to alpha0.") self.skip_log.append(skip) self.log(f"alpha = {self.alpha:.4f}, skip = {skip}") return skip