Source code for hyponic.optimizers.physics_based.SA

from hyponic.optimizers.base_optimizer import BaseOptimizer

import numpy as np
import numexpr as ne


[docs]class SA(BaseOptimizer): """ Simulated Annealing Example ~~~~~~~ >>> from hyponic.optimizers.physics_based.SA import SA >>> import numpy as np >>> >>> def sphere(x): >>> return np.sum(x ** 2) >>> >>> problem_dict = { >>> 'fit_func': sphere, >>> 'lb': [-5.12, -5, -14, -6, -0.9], >>> 'ub': [5.12, 5, 14, 6, 0.9], >>> 'minmax': 'min' >>> } >>> >>> sa = SA(epoch=40, population_size=100, verbose=True, early_stopping=4) >>> sa.solve(problem_dict) >>> print(sa.get_best_score()) >>> print(sa.get_best_solution()) """ def __init__(self, epoch: int = 10, population_size: int = 10, minmax: str = None, verbose: bool = False, mode: str = 'single', n_workers: int = 4, early_stopping: int | None = None, **kwargs): """ :param epoch: number of iterations :param population_size: number of individuals in the population :param minmax: 'min' or 'max', depending on whether the problem is a minimization or maximization problem :param verbose: whether to print the progress, default is False :param mode: 'single' or 'multithread', depending on whether to use multithreading or not :param n_workers: number of workers to use in multithreading mode :param early_stopping: number of epochs to wait before stopping the optimization process. If None, then early stopping is not used """ super().__init__(**kwargs) self.epoch = epoch self.population_size = population_size self.minmax = minmax self.verbose = verbose self.mode = mode self.n_workers = n_workers self.early_stopping = early_stopping self.currents = None self.best = None self.best_score = None
[docs] def initialize(self, problem_dict): super().initialize(problem_dict) self.currents = np.random.uniform(self.lb, self.ub, (self.population_size, self.dimensions)) self.best = self.currents[0] self.best_score = self.function(self.best)
[docs] def evolve(self, current_epoch): progress = current_epoch / self.epoch t = max(0.01, min(1, 1 - progress)) amplitudes = ne.evaluate("(_max - _min) * progress * 0.1", local_dict={'_max': np.max(self.intervals), '_min': np.min(self.intervals), 'progress': progress}) deltas = np.random.uniform(-amplitudes / 2, amplitudes / 2, (self.population_size, self.dimensions)) candidates = ne.evaluate("currents + deltas", local_dict={'currents': self.currents, 'deltas': deltas}) for idx, candidate in enumerate(candidates): candidate = np.clip(candidate, self.lb, self.ub) candidate_score = self.function(candidate) if candidate_score < self.best_score: # TODO: check if the problem is minimization or maximization self.best = candidate self.best_score = candidate_score self.currents[idx] = candidate else: score_abs_diff = ne.evaluate("abs(candidate_score - best_score)", local_dict={'candidate_score': candidate_score, 'best_score': self.best_score}) if np.random.uniform() < np.exp(-score_abs_diff / t): self.currents[idx] = candidate
[docs] def get_best_score(self): return self.best_score
[docs] def get_best_solution(self): return self.best
[docs] def get_current_best_score(self): return self.function(self.currents[0])
[docs] def get_current_best_solution(self): return self.currents[0]