From 802342aa0fa0fe2a9312043ae59fade1a878c55c Mon Sep 17 00:00:00 2001 From: "mhsatman@gmail.com" Date: Fri, 9 Aug 2024 19:08:48 +0300 Subject: [PATCH] refactor code --- src/individual.py | 114 ++++++++--------------------------- src/main.py | 8 +-- src/optimizer.py | 98 ++++++++++++------------------ src/population.py | 15 ++++- src/tests/test_individual.py | 2 +- 5 files changed, 82 insertions(+), 155 deletions(-) diff --git a/src/individual.py b/src/individual.py index 2f884e3..f9bd517 100644 --- a/src/individual.py +++ b/src/individual.py @@ -1,4 +1,5 @@ from numpy import random +import numpy as np import random as rd from problems.abstract_problem import AbstractProblem @@ -21,7 +22,7 @@ class Individual: neighbors : list or None The list of neighbors for the individual. gen_type : str - The type of genome representation ("Binary", "Permutation", "Real-valued"). + The type of genome representation ("Binary", "Permutation", "Real"). ch_size : int The size of the chromosome. """ @@ -29,16 +30,22 @@ class Individual: def __init__(self, gen_type: str = "", ch_size: int = 0, - problem: AbstractProblem = None): + problem: AbstractProblem = None, + mins : list[float] = [], + maxs : list[float] = []): """ Initialize an Individual with a specific genome type and chromosome size. Parameters ---------- gen_type : str, optional - The type of genome representation. Must be one of "Binary", "Permutation", or "Real-valued". (default is "Binary") + The type of genome representation. Must be one of "Binary", "Permutation", or "Real". (default is "Binary") ch_size : int The size of the chromosome. + mins: list[float] + The minimum values for each gene in the chromosome. + maxs: list[float] + The maximum values for each gene in the chromosome. """ self.gen_type = gen_type self.ch_size = ch_size @@ -48,6 +55,8 @@ def __init__(self, self.position = (0, 0) self.neighbors_positions = None self.neighbors = None + self.mins = mins + self.maxs = maxs def randomize(self): @@ -67,93 +76,20 @@ def randomize(self): problem_name = self.problem.__class__.__name__ if self.gen_type == "Binary": - - # CountSat, Fms, Mmdp, OneMax, Ecc, Maxcut20_01, Maxcut20_09, Maxcut100 - if problem_name in ["CountSat", "Fms", "Mmdp", "OneMax", "Ecc", "Maxcut20_01", "Maxcut20_09", "Maxcut100"]: - self.chromosome = [random.randint(2) for i in range(self.ch_size)] - # Peak - elif problem_name == "Peak": - self.chromosome = [[random.randint(2) for g in range(self.ch_size)] for h in range(self.ch_size)] - - elif self.gen_type == "Permutation": - - # Tsp - if problem_name == "Tsp": - self.chromosome = list(rd.sample(range(1, 15), self.ch_size)) - - elif self.gen_type == "Real-valued": - - # Ackley - if problem_name == "Ackley": - self.chromosome = [round(rd.uniform(-32.768, 32.768), 3) for i in range(self.ch_size)] - - # Bohachevsky - elif problem_name == "Bohachevsky": - self.chromosome = [random.randint(-15.0, 16.0)for i in range(self.ch_size)] - - # Fms - elif problem_name == "Fms": - self.chromosome = [round(rd.uniform(-6.4, 6.35), 3) for i in range(self.ch_size)] - - # Griewank - elif problem_name == "Griewank": - self.chromosome = [round(rd.uniform(-600, 600), 3) for i in range(self.ch_size)] - - # Rastrigin - elif problem_name == "Rastrigin": - self.chromosome = [round(rd.uniform(-5.12, 5.13), 2) for i in range(self.ch_size)] - - # Rosenbrock - elif problem_name == "Rosenbrock": - self.chromosome = [random.randint(-5.0, 11.0) for i in range(self.ch_size)] - - # Schaffer and Schaffer2, Bentcigar, Rothellipsoid - elif problem_name in ["Schaffer", "Schaffer2", "Bentcigar", "Rothellipsoid"]: - self.chromosome = [round(rd.uniform(-100, 100), 3) for i in range(self.ch_size)] - - # Matyas, Sumofdifferentpowers - elif problem_name in ["Matyas", "Sumofdifferentpowers", "Holzman"]: - self.chromosome = [round(rd.uniform(-10.0, 10.0), 3) for i in range(self.ch_size)] - - # Powell - elif problem_name == "Powell": - self.chromosome = [round(rd.uniform(-4.0, 5.0), 3) for i in range(self.ch_size)] - - # Chichinadze - elif problem_name == "Chichinadze": - self.chromosome = [round(rd.uniform(-30, 30), 5) for i in range(self.ch_size)] - - # Levy - elif problem_name == "Levy": - self.chromosome = [round(rd.uniform(-10.0, 10.0), 2) for i in range(self.ch_size)] - - # Zettle - elif problem_name == "Zettle": - self.chromosome = [round(rd.uniform(-5.0, 5.0), 4) for i in range(self.ch_size)] - - # Dropwave, Sphere - elif problem_name in ["Dropwave", "Sphere"]: - self.chromosome = [round(rd.uniform(-5.12, 5.12), 3) for i in range(self.ch_size)] - - # # StyblinskiTang - elif problem_name == "StyblinskiTang": - self.chromosome = [round(rd.uniform(-5.0, 5.0), 6) for i in range(self.ch_size)] + self.chromosome = [random.randint(2) for i in range(self.ch_size)] - # # Threehumps - elif problem_name == "Threehumps": - self.chromosome = [round(rd.uniform(-5.0, 5.0), 3) for i in range(self.ch_size)] - - # # Zakharov - elif problem_name == "Zakharov": - self.chromosome = [round(rd.uniform(-5.0, 10.0), 3) for i in range(self.ch_size)] - - # # Schwefel - elif problem_name == "Schwefel": - self.chromosome = [round(rd.uniform(-500.0, 500.0), 3) for i in range(self.ch_size)] - - # # Pow - elif problem_name == "Pow": - self.chromosome = [round(rd.uniform(-5.0, 15.0), 2) for i in range(self.ch_size)] + elif self.gen_type == "Permutation": + # Generate a random permutation of the numbers 1 to ch_size. + # by default random.permutation emits numbers from 0 to ch_size-1 + # so we add 1 to each number to get the desired range. + self.chromosome = list(np.random.permutation(self.ch_size) + 1) + + elif self.gen_type == "Real": + if len(self.mins) > 0: + assert len(self.mins) == len(self.maxs) == self.ch_size + self.chromosome = [rd.uniform(self.mins[i], self.maxs[i]) for i in range(self.ch_size)] + else: + self.chromosome = [rd.uniform(-1.0, 0.1) for i in range(self.ch_size)] else: raise NotImplementedError(self.gen_type + " not implemented yet.") diff --git a/src/main.py b/src/main.py index 017b3c6..13080ac 100644 --- a/src/main.py +++ b/src/main.py @@ -7,7 +7,7 @@ n_rows = 5, n_gen = 100, ch_size = 5, - gen_type = "Real-valued", + gen_type = "Real", p_crossover = 0.9, p_mutation = 0.2, problem = optimizer.Ackley(), @@ -56,7 +56,7 @@ # n_rows = 5, # n_gen = 100, # ch_size = 5, -# gen_type = "Real-valued", +# gen_type = "Real", # p_crossover = 0.9, # p_mutation = 0.2, # problem = optimizer.Ackley(), @@ -105,7 +105,7 @@ # n_rows = 5, # n_gen = 100, # ch_size = 10, -# gen_type = "Real-valued", +# gen_type = "Real", # p_crossover = 0.9, # p_mutation = 0.2, # problem = optimizer.Ackley(), @@ -190,7 +190,7 @@ # n_rows = 5, # n_gen = 100, # ch_size = 5, -# gen_type = "Real-valued", +# gen_type = "Real", # problem = optimizer.Ackley(), # selection = optimizer.TournamentSelection, # min_value = -32.768, diff --git a/src/optimizer.py b/src/optimizer.py index c8f8556..f87ed9c 100644 --- a/src/optimizer.py +++ b/src/optimizer.py @@ -1,44 +1,3 @@ - -# --------------------------------- problems -------------------------------- # -# Binary -from problems.single_objective.discrete.binary.count_sat import CountSat -# from problems.single_objective.discrete.binary.fms import Fms -from problems.single_objective.discrete.binary.mmdp import Mmdp -from problems.single_objective.discrete.binary.one_max import OneMax -from problems.single_objective.discrete.binary.peak import Peak -from problems.single_objective.discrete.binary.ecc import Ecc -from problems.single_objective.discrete.binary.maxcut20_01 import Maxcut20_01 -from problems.single_objective.discrete.binary.maxcut20_09 import Maxcut20_09 -from problems.single_objective.discrete.binary.maxcut100 import Maxcut100 -# Permutation -from problems.single_objective.discrete.permutation.tsp import Tsp -# Real-valued -from problems.single_objective.continuous.ackley import Ackley -from problems.single_objective.continuous.bohachevsky import Bohachevsky -from problems.single_objective.continuous.fms import Fms -from problems.single_objective.continuous.griewank import Griewank -from problems.single_objective.continuous.holzman import Holzman -from problems.single_objective.continuous.rastrigin import Rastrigin -from problems.single_objective.continuous.rosenbrock import Rosenbrock -from problems.single_objective.continuous.schaffer import Schaffer -from problems.single_objective.continuous.schaffer2 import Schaffer2 -from problems.single_objective.continuous.schaffer2 import Schaffer2 -from problems.single_objective.continuous.matyas import Matyas -from problems.single_objective.continuous.bentcigar import Bentcigar -from problems.single_objective.continuous.sumofdifferentpowers import Sumofdifferentpowers -from problems.single_objective.continuous.sumofdifferentpowers import Sumofdifferentpowers -from problems.single_objective.continuous.powell import Powell -from problems.single_objective.continuous.rothellipsoid import Rothellipsoid -from problems.single_objective.continuous.levy import Levy -from problems.single_objective.continuous.zettle import Zettle -from problems.single_objective.continuous.dropwave import Dropwave -from problems.single_objective.continuous.styblinskitang import StyblinskiTang -from problems.single_objective.continuous.threehumps import Threehumps -from problems.single_objective.continuous.zakharov import Zakharov -from problems.single_objective.continuous.sphere import Sphere -from problems.single_objective.continuous.pow import Pow -# -------------------------------------------------------------------------- # - # ------------------------------ selection --------------------------------- # from selection.roulette_wheel_selection import RouletteWheelSelection from selection.tournament_selection import TournamentSelection @@ -98,7 +57,9 @@ def cga( problem: AbstractProblem, selection: Callable, recombination: Callable, - mutation: Callable + mutation: Callable, + mins : list[float] = [], + maxs : list[float] = [] ) -> List: """ Optimize the given problem using a genetic algorithm. @@ -131,6 +92,10 @@ def cga( Function or class used for recombination (crossover). mutation : Callable Function or class used for mutation. + mins : list[float] + List of minimum values for each gene in the chromosome (for real value optimization). + maxs : list[float] + List of maximum values for each gene in the chromosome (for real value optimization). Returns ------- @@ -149,7 +114,7 @@ def cga( # Generate Initial Population pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem).initial_population() + gen_type, problem, mins=mins, maxs=maxs).initial_population() pop_list_ordered = sorted( pop_list, key=lambda x: x.fitness_value) @@ -236,7 +201,9 @@ def sync_cga( problem: Callable[[List[float]], float], selection: Callable, recombination: Callable, - mutation: Callable + mutation: Callable, + mins: List[float] = [], + maxs: List[float] = [] ) -> List: """ Optimize the given problem using a synchronous cellular genetic algorithm (Sync-CGA). @@ -265,7 +232,11 @@ def sync_cga( Function or class used for recombination (crossover). mutation : Callable Function or class used for mutation. - + mins : List[float] + List of minimum values for each gene in the chromosome (for real value optimization). + maxs : List[float] + List of maximum values for each gene in the chromosome (for real value optimization + Returns ------- List @@ -284,7 +255,7 @@ def sync_cga( # Generate Initial Population pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem).initial_population() + gen_type, problem, mins = mins, maxs=maxs).initial_population() # Sort population by fitness value pop_list_ordered = sorted( @@ -371,7 +342,9 @@ def alpha_cga( problem: AbstractProblem, selection: Callable, recombination: Callable, - mutation: Callable + mutation: Callable, + mins: List[float] = [], + maxs: List[float] = [] ) -> List: """ Optimize a problem using an evolutionary algorithm with an alpha-male exchange mechanism. @@ -387,7 +360,7 @@ def alpha_cga( ch_size : int Size of the chromosome. gen_type : str - Type of genome representation ("Binary", "Permutation", "Real-valued"). + Type of genome representation ("Binary", "Permutation", "Real"). p_crossover : float Probability of crossover, should be between 0 and 1. p_mutation : float @@ -404,6 +377,10 @@ def alpha_cga( Function used for recombination (crossover) in the evolutionary algorithm. mutation : Callable Function used for mutation in the evolutionary algorithm. + mins : List[float] + List of minimum values for each gene in the chromosome (for real value optimization). + maxs : List[float] + List of maximum values for each gene in the chromosome (for real value optimization). Returns ------- @@ -423,7 +400,7 @@ def alpha_cga( # Generate Initial Population pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem).initial_population() + gen_type, problem, mins=mins, maxs=maxs).initial_population() # Sort population by fitness value pop_list_ordered = sorted( @@ -528,9 +505,11 @@ def ccga( gen_type: str, problem: AbstractProblem, selection: Callable, + mins: List[float] = [], + maxs: List[float] = [] ) -> List: """ - Perform optimization using a coevolutionary genetic algorithm (CCGA). + Perform optimization using a (CCGA). Parameters ---------- @@ -543,11 +522,16 @@ def ccga( ch_size : int Size of the chromosome. gen_type : str - Type of genome representation ("Binary", "Permutation", "Real-valued"). + Type of genome representation ("Binary", "Permutation", "Real"). problem : AbstractProblem The problem instance used to evaluate fitness. selection : Callable Function used for selection in the evolutionary algorithm. + mins : List[float] + List of minimum values for each gene in the chromosome (for real value optimization). + maxs : List[float] + List of maximum values for each gene in the chromosome (for real value optimization + Returns ------- @@ -568,7 +552,7 @@ def ccga( # Generate Initial Population pop_list = Population(method_name, ch_size, n_rows, n_cols, - gen_type, problem, vector).initial_population() + gen_type, problem, vector,mins=mins, maxs=maxs).initial_population() # Sort population by fitness value pop_list_ordered = sorted( @@ -625,6 +609,7 @@ def ccga( return best_ever_solution + def mcccga( n_cols: int, n_rows: int, @@ -633,11 +618,11 @@ def mcccga( gen_type: str, problem: Callable[[List[float]], float], selection: Callable, - min_value: float, - max_value: float + mins: list[float], + maxs: list[float] ) -> List: """ - Optimize the given problem using a multi-population coevolutionary genetic algorithm (MCCGA). + Optimize the given problem using a multi-population machine-coded compact genetic algorithm (MCCGA). Parameters ---------- @@ -673,9 +658,6 @@ def mcccga( best_ever_solution = [] avg_objectives = [] method_name = "mcccga" - mins = [min_value for i in range(ch_size)] # chromosome number of min values in the list - maxs = [max_value for i in range(ch_size)] # chromosome number of max values in the list - # Generate initial probability vector vector = generate_probability_vector(mins, maxs, pop_size) diff --git a/src/population.py b/src/population.py index 257b3e6..3158678 100644 --- a/src/population.py +++ b/src/population.py @@ -26,7 +26,7 @@ class Population: n_cols : int The number of columns in the grid. gen_type : str - The type of genome representation ("Binary", "Permutation", "Real-valued"). + The type of genome representation ("Binary", "Permutation", "Real"). problem : AbstractProblem The problem instance used to evaluate fitness. vector : list @@ -39,7 +39,9 @@ def __init__(self, n_cols: int = 0, gen_type: str = "", problem: AbstractProblem = None, - vector: list = []): + vector: list = [], + mins : list[float] = [], + maxs : list[float] = []): """ Initialize the Population with the specified parameters. @@ -57,6 +59,10 @@ def __init__(self, The problem instance used to evaluate fitness (default is None). vector : list, optional A list used to generate candidates (default is an empty list). + mins: list[float] + The minimum values for each gene in the chromosome (for real value optimization). + maxs: list[float] + The maximum values for each gene in the chromosome (for real value optimization). """ self.method_name = method_name self.ch_size = ch_size @@ -65,6 +71,8 @@ def __init__(self, self.gen_type = gen_type self.problem = problem self.vector = vector + self.mins = mins + self.maxs = maxs def initial_population(self) -> List[Individual]: """ @@ -81,7 +89,8 @@ def initial_population(self) -> List[Individual]: grid = Grid(self.n_rows, self.n_cols).make_2d_grid() for i in range(pop_size): - ind = Individual(gen_type = self.gen_type, ch_size = self.ch_size, problem = self.problem) + ind = Individual(gen_type = self.gen_type, ch_size = self.ch_size, + problem = self.problem, mins = self.mins, maxs = self.maxs) # Initialize chromosome and evaluate fitness for cga, syn_cga and alpha_cga if self.method_name in ["cga", "sync_cga", "alpha_cga", "ccga"]: diff --git a/src/tests/test_individual.py b/src/tests/test_individual.py index 103aba3..6b28807 100644 --- a/src/tests/test_individual.py +++ b/src/tests/test_individual.py @@ -54,7 +54,7 @@ def test_randomize_real_valued(): Test the randomization of the chromosome for a real-valued genome type. """ chsize = 10 - ind = Individual(gen_type="Real-valued", ch_size=chsize, problem=Ackley()) + ind = Individual(gen_type="Real", ch_size=chsize, problem=Ackley()) ind.randomize() assert len(ind.chromosome) == chsize assert all(isinstance(gene, float) for gene in ind.chromosome)