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