Source code for mealpy.physics_based.SA

# !/usr/bin/env python
# Created by "Thieu" at 22:08, 01/03/2021 ----------%
#       Email: nguyenthieu2102@gmail.com            %
#       Github: https://github.com/thieu1995        %
# --------------------------------------------------%

import numpy as np
from copy import deepcopy
from mealpy.optimizer import Optimizer


[docs]class BaseSA(Optimizer): """ The original version of: Simulated Annealing (SA) Hyper-parameters should fine tuned in approximate range to get faster convergen toward the global optimum: + max_sub_iter (int): [5, 10, 15], Maximum Number of Sub-Iteration (within fixed temperature), default=5 + t0 (int): Fixed parameter, Initial Temperature, default=1000 + t1 (int): Fixed parameter, Final Temperature, default=1 + move_count (int): [5, 20], Move Count per Individual Solution, default=5 + mutation_rate (float): [0.01, 0.2], Mutation Rate, default=0.1 + mutation_step_size (float): [0.05, 0.1, 0.15], Mutation Step Size, default=0.1 + mutation_step_size_damp (float): [0.8, 0.99], Mutation Step Size Damp, default=0.99 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy.physics_based.SA import BaseSA >>> >>> def fitness_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict1 = { >>> "fit_func": fitness_function, >>> "lb": [-10, -15, -4, -2, -8], >>> "ub": [10, 15, 12, 8, 20], >>> "minmax": "min", >>> "verbose": True, >>> } >>> >>> epoch = 1000 >>> pop_size = 50 >>> max_sub_iter = 5 >>> t0 = 1000 >>> t1 = 1 >>> move_count = 5 >>> mutation_rate = 0.1 >>> mutation_step_size = 0.1 >>> mutation_step_size_damp = 0.99 >>> model = BaseSA(problem_dict1, epoch, pop_size, max_sub_iter, t0, t1, move_count, mutation_rate, mutation_step_size, mutation_step_size_damp) >>> best_position, best_fitness = model.solve() >>> print(f"Solution: {best_position}, Fitness: {best_fitness}") References ~~~~~~~~~~ [1] Van Laarhoven, P.J. and Aarts, E.H., 1987. Simulated annealing. In Simulated annealing: Theory and applications (pp. 7-15). Springer, Dordrecht. """ def __init__(self, problem, epoch=10000, pop_size=100, max_sub_iter=5, t0=1000, t1=1, move_count=5, mutation_rate=0.1, mutation_step_size=0.1, mutation_step_size_damp=0.99, **kwargs): """ Args: problem (dict): The problem dictionary epoch (int): maximum number of iterations, default = 10000 pop_size (int): number of population size, default = 100 max_sub_iter (int): Maximum Number of Sub-Iteration (within fixed temperature), default=5 t0 (int): Initial Temperature, default=1000 t1 (int): Final Temperature, default=1 move_count (int): Move Count per Individual Solution, default=5 mutation_rate (float): Mutation Rate, default=0.1 mutation_step_size (float): Mutation Step Size, default=0.1 mutation_step_size_damp (float): Mutation Step Size Damp, default=0.99 """ super().__init__(problem, kwargs) self.nfe_per_epoch = pop_size * max_sub_iter * move_count self.sort_flag = True self.epoch = epoch self.pop_size = pop_size self.max_sub_iter = max_sub_iter self.t0 = t0 self.t1 = t1 self.move_count = move_count self.mutation_rate = mutation_rate self.mutation_step_size = mutation_step_size self.mutation_step_size_damp = mutation_step_size_damp self.dyn_t, self.t_damp, self.dyn_sigma = None, None, None def _mutate(self, position, sigma): # Select Mutating Variables pos_new = position + sigma * np.random.uniform(self.problem.lb, self.problem.ub) pos_new = np.where(np.random.uniform(0, 1, self.problem.n_dims) < self.mutation_rate, position, pos_new) if np.all(pos_new == position): # Select at least one variable to _mutate pos_new[np.random.randint(0, self.problem.n_dims)] = np.random.uniform() return self.amend_position(pos_new)
[docs] def initialization(self): # Initial Temperature self.dyn_t = self.t0 # Initial Temperature self.t_damp = (self.t1 / self.t0) ** (1.0 / self.epoch) # Calculate Temperature Damp Rate self.dyn_sigma = self.mutation_step_size # Initial Value of Step Size self.pop = self.create_population(self.pop_size) self.pop, self.g_best = self.get_global_best_solution(self.pop)
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ # Sub-Iterations for g in range(0, self.max_sub_iter): # Create new population pop_new = [] for i in range(0, self.pop_size): for j in range(0, self.move_count): # Perform Mutation (Move) pos_new = self._mutate(self.pop[i][self.ID_POS], self.dyn_sigma) pos_new = self.amend_position(pos_new) pop_new.append([pos_new, None]) pop_new = self.update_fitness_population(pop_new) # Columnize and Sort Newly Created Population pop_new = self.get_sorted_strim_population(pop_new, self.pop_size) # Randomized Selection for i in range(0, self.pop_size): # Check if new solution is better than current if self.compare_agent(pop_new[i], self.pop[i]): self.pop[i] = deepcopy(pop_new[i]) else: # Compute difference according to problem type delta = abs(pop_new[i][self.ID_TAR][self.ID_FIT] - self.pop[i][self.ID_TAR][self.ID_FIT]) p = np.exp(-delta / self.dyn_t) # Compute Acceptance Probability if np.random.uniform() <= p: # Accept / Reject self.pop[i] = deepcopy(pop_new[i]) # Update Temperature self.dyn_t = self.t_damp * self.dyn_t self.dyn_sigma = self.mutation_step_size_damp * self.dyn_sigma