Source code for scm.glompo.stoppers.valueannealing

import numpy as np

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

__all__ = ("ValueAnnealing",)


[docs]class ValueAnnealing(BaseStopper): """Keeps optimizers alive based on the function values they are exploring. This condition is unlikely to stop a tested optimizer which is very near the best (in terms of function value) but the probability of stopping increases with the difference between them. This condition can be applied in combination with others to prevent 'competitive' optimizers for being stopped while still terminating poorly performing ones. The decision criteria follows an exponential distribution which corresponds to the probability of survival. Control of the probability can be achieved through the ``crit_stop_chance`` initialisation criteria. :Parameters: crit_stop_chance: float The probability of stopping a tested optimizer which is twice as large as the best optimizer in absolute value. The default is 50%. :Returns: bool ``True`` if the difference between the explored function values of the best and tested optimizer fails a comparison test with a uniformly randomly generated number. """ def __init__(self, crit_stop_chance: float = 0.5): super().__init__() assert 0 < crit_stop_chance < 1, "crit_stop_chance must be between 0 and 1" self.crit_stop_chance = crit_stop_chance self.strictness = np.log(crit_stop_chance) def __call__(self, log: BaseLogger, best_opt_id: int, tested_opt_id: int) -> bool: f_best = log.get_best_iter(best_opt_id)["fx"] f_tested = log.get_best_iter(tested_opt_id)["fx"] if f_best == 0 or f_tested <= f_best: # Catch very unlikely corner cases self.last_result = False return self.last_result prob = (f_best - f_tested) / f_best prob = np.abs(prob) prob *= self.strictness prob = np.exp(prob) test_num = np.random.uniform(0, 1) self.last_result = test_num > prob return self.last_result def __amssettings__(self, s: Settings) -> Settings: s.input.ams.Stopper.Type = "ValueAnnealing" s.input.ams.Stopper.ValueAnnealing.CriticalStopChance = self.crit_stop_chance return s