Source code for mealpy.human_based.ILA

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

import numpy as np
from mealpy.optimizer import Optimizer
from mealpy.utils.opt_info import OptInfo, ScientificConcern


[docs]class OriginalILA(Optimizer): """ The original version of: Incomprehensible but Intelligible-in-time Logics Algorithm (ILA) Parameters ---------- epoch : int Maximum number of iterations, in range [1, 100000]. Default is 10000. pop_size : int Number of population size, in range [5, 10000]. Default is 100. n_models : int Number of models for grouping in Stage 1, in range [2, int(pop_size / 2)]. Default is 5. p_s1 : float Maximum percentage of iterations in Stage 1, in range [0.01, 0.5]. Default is 0.33. p_s2 : float Maximum percentage of iterations in Stage 2, in range [0.01, 0.5]. Default is 0.33. b_min : float The minimum boundary for the parameters of IbI, in range [-10.0, 10.0]. Default is 0.4. b_max : float The maximum boundary for the parameters of IbI, in range [-10.0, 10.0]. Default is 0.6. .. attention:: 1. This is one of the most complex and lengthiest algorithms we have ever implemented. The complexity stems from the group partitioning logic and various logical operations that do not match the real-world behaviors described in the paper. 2. This algorithm cannot be parallelized; furthermore, it evaluates a high number of function evaluations (NFEs) within a single iteration. Users should exercise caution when applying it to large-scale problems. 3. Aside from being complex, it also involves numerous parameters that heavily impact overall performance. References ~~~~~~~~~~ 1. Mirrashid, M., & Naderpour, H. (2023). Incomprehensible but Intelligible-in-time logics: Theory and optimization algorithm. Knowledge-Based Systems, 264, 110305. https://doi.org/10.1016/j.knosys.2023.110305 Examples ~~~~~~~~ >>> import numpy as np >>> from mealpy import FloatVar, ILA >>> >>> def objective_function(solution): >>> return np.sum(solution**2) >>> >>> problem_dict = { >>> "bounds": FloatVar(lb=(-10.,) * 30, ub=(10.,) * 30, name="delta"), >>> "obj_func": objective_function, >>> "minmax": "min", >>> } >>> >>> model = ILA.OriginalILA(epoch=1000, pop_size=50, n_models=5, p_s1=0.33, p_s2=0.33, b_min=0.4, b_max=0.6) >>> g_best = model.solve(problem_dict) >>> print(f"Solution: {g_best.solution}, Fitness: {g_best.target.fitness}") >>> print(f"Solution: {model.g_best.solution}, Fitness: {model.g_best.target.fitness}") """ OPT_INFO = OptInfo(name="Incomprehensible but Intelligible-in-time Logics Algorithm", year=2023, difficulty="nightmare", kind="original", scientific_status="questionable", concerns=( ScientificConcern.LACK_OF_NOVELTY, ScientificConcern.POOR_REPRODUCIBILITY )) def __init__(self, epoch: int = 10000, pop_size: int = 100, n_models: int = 5, p_s1: float=0.33, p_s2: float=0.33, b_min: float=0.4, b_max: float=0.6, **kwargs: object) -> None: super().__init__(**kwargs) self.epoch = self.validator.check_int("epoch", epoch, [1, 100000]) self.pop_size = self.validator.check_int("pop_size", pop_size, [5, 10000]) self.n_models = self.validator.check_int("n_models", n_models, [2, int(self.pop_size / 2)]) self.p_s1 = self.validator.check_float("p_s1", p_s1, [0.01, 0.5]) self.p_s2 = self.validator.check_float("p_s2", p_s2, [0.01, 0.5]) self.b_min = self.validator.check_float("b_min", b_min, [-10., 10.]) self.b_max = self.validator.check_float("b_max", b_max, [-10., 10.]) self.set_parameters(["epoch", "pop_size", "n_models", "p_s1", "p_s2", "b_min", "b_max"]) self.sort_flag = False # Iteration tracking self.n_g_max = max(1, self.pop_size // 2) self.n_t1 = int(self.p_s1 * self.epoch) self.n_t2 = int(self.p_s2 * self.epoch) self.n_t3 = self.epoch - self.n_t1 - self.n_t2 # Calculate iterations per model for Stage 1 self.t_m = max(1, self.n_t1 // self.n_models) if self.n_models > 0 else 1 # Initialize population self.experts = None self.fitness = None self.pop_prev = None self.current_best = None self.current_n_g = None
[docs] def before_main_loop(self): self.experts = np.array([agent.solution for agent in self.pop]) self.fitness = np.array([agent.target.fitness for agent in self.pop]) self.pop_prev = self.pop.copy() self.current_best = self.g_best.copy() self.current_n_g = 1
[docs] def normalize(self, values): """Normalize an array of values to [0, 1] range.""" val_min, val_max = np.min(values), np.max(values) if val_max == val_min: return np.zeros_like(values) return (values - val_min) / (val_max - val_min)
[docs] def kmeans_simple(self, data, k, max_iters=100): """Lightweight k-means clustering.""" centroids = data[self.generator.choice(data.shape[0], k, replace=False)] clusters = np.zeros(data.shape[0]) for _ in range(max_iters): distances = np.linalg.norm(data[:, np.newaxis] - centroids, axis=2) clusters = np.argmin(distances, axis=1) new_centroids = np.array( [data[clusters == i].mean(axis=0) if np.any(clusters == i) else data[self.generator.choice(data.shape[0])] for i in range(k)]) if np.allclose(centroids, new_centroids): break centroids = new_centroids return clusters
[docs] def stage_1_step(self, epoch): """Stage 1: Groupwork (Exploration).""" if (epoch-1) % self.t_m == 0: self.current_n_g = self.generator.integers(1, self.n_g_max + 1) self.current_clusters = self.kmeans_simple(self.experts, self.current_n_g) for g in range(self.current_n_g): group_indices = np.where(self.current_clusters == g)[0] if len(group_indices) == 0: continue group_experts = self.experts[group_indices] group_fitness = self.fitness[group_indices] if self.problem.minmax == "min": e_best_g = group_experts[np.argmin(group_fitness)] else: e_best_g = group_experts[np.argmax(group_fitness)] e_avg_g = np.mean(group_experts, axis=0) c_i = np.linalg.norm(group_experts - self.current_best.solution, axis=1) group_pos = np.array([self.pop_prev[idx].solution for idx in group_indices]) d_i = np.linalg.norm(group_experts - group_pos, axis=1) p_i = np.linalg.norm(group_experts - e_best_g, axis=1) r_c = self.normalize(c_i) r_d = self.normalize(d_i) r_p = self.normalize(p_i) for idx, real_idx in enumerate(group_indices): b_c = self.generator.uniform(self.b_min, self.b_max) b_p = self.generator.uniform(self.b_min, self.b_max) b_d = self.generator.uniform(self.b_min, self.b_max) e_i = self.experts[real_idx] e_r = group_experts[self.generator.integers(len(group_experts))] e_u = self.generator.uniform(self.problem.lb, self.problem.ub) # K0 if r_c[idx] <= b_c and r_p[idx] <= b_p: k_0 = r_p[idx] * (e_i + e_r) / 2 elif r_c[idx] <= b_c and r_p[idx] > b_p: k_0 = r_p[idx] * (e_i + e_avg_g) / 2 elif r_c[idx] > b_c and r_p[idx] <= b_p: k_0 = r_p[idx] * (e_best_g + e_r) / 2 else: k_0 = r_p[idx] * (e_best_g + e_avg_g) / 2 # K1 c1 = self.generator.random() k_1 = c1 * e_avg_g if r_d[idx] <= b_d else c1 * e_u k_s1 = np.abs(k_0 + k_1) / 2 c2 = self.generator.uniform(-1.5, 1.5, self.problem.n_dims) e_s1_new1 = e_i + c2 * k_s1 c3 = self.generator.uniform(-1.5, 1.5) c4 = self.generator.random() e_s1_new2 = c3 * e_s1_new1 + c4 * e_best_g # Boundary clipping & Selection e_s1_new1 = self.correct_solution(e_s1_new1) e_s1_new2 = self.correct_solution(e_s1_new2) agent1 = self.generate_agent(e_s1_new1) agent2 = self.generate_agent(e_s1_new2) if self.compare_target(agent1.target, agent2.target, self.problem.minmax): best_new = agent1 else: best_new = agent2 if self.compare_fitness(best_new.target.fitness, self.fitness[real_idx], self.problem.minmax): self.pop_prev[real_idx] = self.pop[real_idx].copy() self.pop[real_idx] = best_new
[docs] def stage_2_step(self, epoch): """Perform a single iteration of Stage 2: Integration.""" e_avg = np.mean(self.experts, axis=0) c_i = np.linalg.norm(self.experts - self.current_best.solution, axis=1) pop_prev = np.array([agent.solution for agent in self.pop_prev]) d_i = np.linalg.norm(self.experts - pop_prev, axis=1) p_i = np.linalg.norm(self.experts - self.g_best.solution, axis=1) r_c = self.normalize(c_i) r_d = self.normalize(d_i) r_p = self.normalize(p_i) for idx in range(self.pop_size): b_c = self.generator.uniform(self.b_min, self.b_max) b_p = self.generator.uniform(self.b_min, self.b_max) b_d = self.generator.uniform(self.b_min, self.b_max) e_i = self.experts[idx] e_r = self.experts[self.generator.integers(self.pop_size)] e_u = self.generator.uniform(self.problem.lb, self.problem.ub) if r_c[idx] <= b_c and r_p[idx] <= b_p: k_0 = r_p[idx] * (e_i + e_r) / 2 elif r_c[idx] <= b_c and r_p[idx] > b_p: k_0 = r_p[idx] * (e_i + e_avg) / 2 elif r_c[idx] > b_c and r_p[idx] <= b_p: k_0 = r_p[idx] * (self.g_best.solution + e_r) / 2 else: k_0 = r_p[idx] * (self.g_best.solution + e_avg) / 2 c5 = self.generator.random() k_1 = c5 * e_avg if r_d[idx] <= b_d else c5 * e_u k_s2 = np.abs(k_0 + k_1) / 2 c6 = self.generator.uniform(-0.75, 0.75, self.problem.n_dims) e_s2_new1 = e_i + c6 * k_s2 c7 = self.generator.uniform(-0.75, 0.75) c8 = self.generator.random() e_s2_new2 = c7 * e_s2_new1 + c8 * self.g_best.solution agent1 = self.generate_agent(self.correct_solution(e_s2_new1)) agent2 = self.generate_agent(self.correct_solution(e_s2_new2)) if self.compare_target(agent1.target, agent2.target, self.problem.minmax): best_new = agent1 else: best_new = agent2 if self.compare_fitness(best_new.target.fitness, self.fitness[idx], self.problem.minmax): self.pop_prev[idx] = self.pop[idx].copy() self.pop[idx] = best_new
[docs] def stage_3_step(self, epoch): """Perform a single iteration of Stage 3: IbI Logic Search.""" e_avg = np.mean(self.experts, axis=0) for idx in range(self.pop_size): e_i = self.experts[idx] e_r = self.experts[self.generator.integers(self.pop_size)] knowledge_factor = self.generator.choice([1, 2]) if knowledge_factor == 1: k_s3 = np.abs(e_avg - e_r) else: k_s3 = np.abs(e_avg - self.g_best.solution) c9 = self.generator.uniform(-0.25, 0.25, self.problem.n_dims) e_s3_new1 = e_i + c9 * k_s3 c10 = self.generator.uniform(-0.25, 0.25) c11 = self.generator.random() e_s3_new2 = c10 * e_s3_new1 + c11 * self.g_best.solution agent1 = self.generate_agent(self.correct_solution(e_s3_new1)) agent2 = self.generate_agent(self.correct_solution(e_s3_new2)) if self.compare_target(agent1.target, agent2.target, self.problem.minmax): best_new = agent1 else: best_new = agent2 if self.compare_fitness(best_new.target.fitness, self.fitness[idx], self.problem.minmax): self.pop_prev[idx] = self.pop[idx].copy() self.pop[idx] = best_new
[docs] def evolve(self, epoch): """ The main operations (equations) of algorithm. Inherit from Optimizer class Args: epoch (int): The current iteration """ if epoch < self.n_t1: self.stage_1_step(epoch) elif epoch < (self.n_t1 + self.n_t2): self.stage_2_step(epoch) else: self.stage_3_step(epoch) self.experts = np.array([agent.solution for agent in self.pop]) self.fitness = np.array([agent.target.fitness for agent in self.pop]) self.current_best, _ = self.get_best_agent(self.pop, self.problem.minmax)