Source code for optilab.optimizers.knn_ipop_cma_es

"""
KNN-IPOP-CMA-ES optimizer. IPOP-CMA-ES is enhanced with a KNN metamodel
similar to the one from LMM-CMA-ES.
"""

from ..data_classes import Bounds, PointList
from ..functions import ObjectiveFunction
from ..functions.surrogate import KNNSurrogateObjectiveFunction
from ..metamodels import ApproximateRankingMetamodel
from .cma_es import CmaEs
from .optimizer import Optimizer


[docs] class KnnIpopCmaEs(CmaEs): """ KNN-IPOP-CMA-ES optimizer: CMA-ES with increasing population restarts and with KNN metamodel similar to LMM-CMA-ES. """ # pylint: disable=super-init-not-called, non-parent-init-called
[docs] def __init__( self, population_size: int, num_neighbors: int, buffer_size: int, ): """ Class constructor. Args: population_size: Starting size of the population. num_neighbors: Number of neighbors used by KNN metamodel. buffer_size: Number of last evaluated points provided to KNN metamodel. """ # buffer cannot be smaller than the number of neighbors buffer_size = max(buffer_size, num_neighbors) # Skipping super().__init__ and calling grandparent init instead. Optimizer.__init__( self, f"knn{num_neighbors}b{buffer_size}-ipop-cma-es", population_size, {"num_neighbors": num_neighbors, "buffer_size": buffer_size}, )
# pylint: disable=duplicate-code
[docs] def optimize( self, function: ObjectiveFunction, bounds: Bounds, call_budget: int, tolerance: float, target: float = 0.0, ) -> PointList: current_population_size = self.metadata.population_size metamodel = ApproximateRankingMetamodel( self.metadata.population_size, self.metadata.population_size // 2, function, KNNSurrogateObjectiveFunction( self.metadata.hyperparameters["num_neighbors"] ), buffer_size=self.metadata.hyperparameters["buffer_size"], ) while not self._stop_external( metamodel.get_log(), current_population_size, call_budget, target, tolerance, ): es = self._spawn_cmaes( bounds, function.metadata.dim, current_population_size, len(bounds) / 2, ) while not self._stop( es, metamodel.get_log(), current_population_size, call_budget, target, tolerance, ): solutions = PointList.from_list(es.ask()) if ( len(metamodel.train_set) < self.metadata.hyperparameters["num_neighbors"] ): xy_pairs = metamodel.evaluate(solutions) else: metamodel.adapt(solutions) xy_pairs = metamodel(solutions) x, y = xy_pairs.pairs() es.tell(x, y) current_population_size *= 2 metamodel.population_size *= 2 metamodel.mu *= 2 metamodel.init_n() return metamodel.get_log()