Source code for scm.glompo.stoppers.currentfunctionvalueunmoving

import warnings

import numpy as np

from .basestopper import BaseStopper
from ..core.optimizerlogger import BaseLogger
from ...plams.core.settings import Settings

__all__ = ("CurrentFunctionValueUnmoving",)


[docs]class CurrentFunctionValueUnmoving(BaseStopper): """Considers function values the optimizers are currently exploring. Used to terminate an optimizer when its function evaluations are unchanging, usually indicating that it is approaching some convergence. Best used with a stopper which monitors step size to ensure a widely exploring optimizer is not stopped. :Parameters: calls Number of function evaluations between comparison points. tol Tolerance fraction between 0 and 1. :Returns: bool Returns ``True`` if the standard deviation of the last ``calls`` function evaluations is below ``tol * abs(latest_f_eval)``. """ def __init__(self, calls: int, tol: float = 0): super().__init__() self.calls = calls self.tol = tol def __call__(self, log: BaseLogger, best_opt_id: int, tested_opt_id: int) -> bool: vals = log.get_history(tested_opt_id, "fx") n_calls = log.len(tested_opt_id) try: assert n_calls > self.calls # If there are insufficient iterations the stopper will return False with warnings.catch_warnings(): warnings.filterwarnings( "error", "invalid value encountered in subtract", RuntimeWarning, module="numpy.core._methods" ) # Caught if there are infs in the results st_dev = np.std(vals[-self.calls :]) self.last_result = st_dev <= np.abs(vals[-1] * self.tol) except (AssertionError, RuntimeWarning): self.last_result = False return self.last_result def __amssettings__(self, s: Settings) -> Settings: s.input.ams.Stopper.Type = "CurrentFunctionValueUnmoving" s.input.ams.Stopper.CurrentFunctionValueUnmoving.NumberOfFunctionCalls = self.calls s.input.ams.Stopper.CurrentFunctionValueUnmoving.Tolerance = self.tol return s