Source code for scm.glompo.stoppers.timeannealing

import random

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

__all__ = ("TimeAnnealing",)


[docs]class TimeAnnealing(BaseStopper): """Keeps optimizers alive based on how long they have been alive. Randomly keeps optimizers alive based on how long (in function evaluations) they have been active. The newer an optimizer is, the more likely it will pass the test and be kept alive. Used to temper very strict termination conditions. :Parameters: crit_ratio Critical ratio of function calls between best and tested optimizers above which the tested optimizer is guaranteed to survive. Values lower than one are looser and allow the tested optimizer to survive even if it has been in operation longer than the best optimizer. Values higher than one are stricter and may stop the tested optimizer even if it has iterated fewer times than the best optimizer. :Returns: bool ``True`` if an optimizer has been alive long enough and fails a comparison test with a uniformly randomly generated number. :Notes: This condition calculates the quotient (``num_bet_fcalls / num_tested_fcalls``). A random number is then generated between zero and ``crit_ratio``. Only if the quotient is larger than this number does the tested optimizer remains alive. """ def __init__(self, crit_ratio: float = 1): super().__init__() if isinstance(crit_ratio, (float, int)) and crit_ratio > 0: self.crit_ratio = crit_ratio else: raise ValueError("threshold should be a positive float.") def __call__(self, log: BaseLogger, best_opt_id: int, tested_opt_id: int) -> bool: n_best = log.len(best_opt_id) n_tested = log.len(tested_opt_id) ratio = n_best / n_tested test_num = random.uniform(0, self.crit_ratio) self.last_result = test_num > ratio return self.last_result def __amssettings__(self, s: Settings) -> Settings: s.input.ams.Stopper.Type = "TimeAnnealing" s.input.ams.Stopper.TimeAnnealing.CriticalRatio = self.crit_ratio return s