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