Source code for scm.glompo.generators.exploitexplore
import numpy as np
from .basegenerator import BaseGenerator
from ...plams.core.settings import Settings
__all__ = ("ExploitExploreGenerator",)
[docs]class ExploitExploreGenerator(BaseGenerator):
"""This generator blends a randomly generated point with the location of an existing optimizer.
The optimizer is chosen based on a roulette selection.
:Parameters:
max_func_calls
Maximum function calls allowed for the optimization, at and beyond this point there is a 100% chance that a
previously evaluated point will be returned by the generator. If the optimization is not limited by the number
of function calls, provide an estimate.
focus
The blend parameter between random point and incumbent points.
:Notes:
``focus`` is used as follows::
p=(f_calls / max_f_calls) ** focus
At ``p=0`` the random point is taken. At ``p=1`` the incumbent is chosen. If ``focus < 1`` points are
more like the incumbent, if ``focus > 1`` points are more like the random. Default is ``focus = 1`` which
has a linear growth from random to incumbent. The new point is calculated as::
new_pt = p*incumbent_pt + (1-p)*random_pt.
"""
def __init__(self, max_func_calls: int, focus: float = 1):
super().__init__()
self.max_func_calls = max_func_calls
if focus <= 0:
raise ValueError("Cannot parse focus, float larger than 0 required.")
self.focus = focus
def __amssettings__(self, s: Settings) -> Settings:
s.input.ams.Generator.Type = "ExploreExploit"
s.input.ams.Generator.ExploreExploit.MaxFunctionCalls = self.max_func_calls
s.input.ams.Generator.ExploreExploit.Focus = self.focus
return s
def generate(self, manager: "GloMPOManager") -> np.ndarray:
# Random Point
bounds = np.array(manager.bounds)
n_parms = manager.n_parms
random = (bounds[:, 1] - bounds[:, 0]) * np.random.random(n_parms) + bounds[:, 0]
self.logger.debug("Random = %s", random)
f_track = np.array(
[
best["fx"]
for opt_id, best in manager.opt_log.best_iters.items()
if opt_id > 0 and np.isfinite(best["fx"])
]
)
if len(f_track) == 0:
return random
# Roulette Selection
shift = f_track - np.min(f_track)
revert = np.max(shift) - shift
if np.all(revert == 0):
return random
prob = revert / np.sum(revert)
select = np.random.choice(range(len(f_track)), p=prob)
incumbent = np.array(manager.opt_log.get_best_iter(select + 1)["x"])
self.logger.debug("Selected incumbent from Optimizer %d = %s", select, incumbent)
# Blending parameter
f_calls = np.clip(manager.f_counter / self.max_func_calls, 0, 1)
alpha = f_calls**self.focus
self.logger.debug("Selected alpha = %f", alpha)
# New point
generated = alpha * incumbent + (1 - alpha) * random
self.logger.debug("Generated = %f", generated)
return generated