From 5e68d9090bba1a6a1c59e7ccb5cfc7c79e0cfe61 Mon Sep 17 00:00:00 2001 From: Zoran Jankovic Date: Sun, 16 Jun 2024 11:52:16 +0200 Subject: [PATCH] updater refactor, naming --- .../adapters/ga_logging/logging_settings.py | 18 ++++++ gadapt/execution/ga_executor.py | 2 +- gadapt/factory/ga_factory.py | 62 ++++++++++++++----- gadapt/ga_model/chromosome.py | 11 ++-- gadapt/ga_model/population.py | 4 +- .../parent_diversity_chromosome_updater.py | 4 +- .../gene_update/base_gene_updater.py | 12 ++-- .../cross_diversity_gene_updater.py | 2 +- ...versity_gene_mutation_rate_determinator.py | 28 ++++++--- ...stribution_cross_diversity_gene_mutator.py | 8 +++ ...y_chromosome_mutation_rate_determinator.py | 38 +++++++----- ..._diversity_chromosome_mutation_selector.py | 16 +++-- .../base_population_updater.py | 6 +- .../cost_diversity_population_updater.py | 12 ++-- 14 files changed, 149 insertions(+), 74 deletions(-) diff --git a/gadapt/adapters/ga_logging/logging_settings.py b/gadapt/adapters/ga_logging/logging_settings.py index 7343d96..869979c 100644 --- a/gadapt/adapters/ga_logging/logging_settings.py +++ b/gadapt/adapters/ga_logging/logging_settings.py @@ -42,3 +42,21 @@ def gadapt_log_info(msg: str): logger.info(msg) except Exception: pass + + +def gadapt_log_warning(msg: str): + logger = logging.getLogger("gadapt_logger") + if logger.disabled: + return + try: + logger.warning(msg) + except Exception: + pass + + +def gadapt_log_error(msg: str): + logger = logging.getLogger("gadapt_logger") + try: + logger.error(msg) + except Exception: + pass diff --git a/gadapt/execution/ga_executor.py b/gadapt/execution/ga_executor.py index 47f5ac8..cd94522 100644 --- a/gadapt/execution/ga_executor.py +++ b/gadapt/execution/ga_executor.py @@ -89,7 +89,7 @@ def find_costs(self): self.population.previous_avg_cost = self.population.avg_cost self.population.previous_min_cost = self.population.min_cost self.cost_finder.find_costs(self.population) - self.gene_updater.update_variables(self.population) + self.gene_updater.update_genes(self.population) self.population_updater.update_population(self.population) def immigrate(self): diff --git a/gadapt/factory/ga_factory.py b/gadapt/factory/ga_factory.py index 8e62292..622b0ec 100644 --- a/gadapt/factory/ga_factory.py +++ b/gadapt/factory/ga_factory.py @@ -121,6 +121,8 @@ from operations.chromosome_update.parent_diversity_chromosome_updater import ( ParentDiversityChromosomeUpdater, ) +from operations.gene_update.base_gene_updater import BaseGeneUpdater +from operations.population_update.base_population_updater import BasePopulationUpdater class GAFactory(BaseGAFactory): @@ -509,21 +511,7 @@ def _get_crossover(self) -> BaseCrossover: """ if self._ga is None: raise Exception("ga object must not be None!") - mutator_strings = [ - ms.strip() - for ms in self._ga.population_mutation.split(definitions.PARAM_SEPARATOR) - ] - population_mutation_selection_strings = [ - value - for value in mutator_strings - if value in definitions.POPULATION_MUTATION_SELECTION_STRINGS - ] - chromosome_updater: BaseChromosomeUpdater = BaseChromosomeUpdater() - if ( - not population_mutation_selection_strings - or definitions.PARENT_DIVERSITY in mutator_strings - ): - chromosome_updater = ParentDiversityChromosomeUpdater() + chromosome_updater = self.get_chromosome_updater() if self._ga.crossover == definitions.BLENDING: return BlendingCrossover(chromosome_updater) @@ -535,10 +523,50 @@ def _get_gene_updater(self): """ Gene Updater Instance """ - return CrossDiversityGeneUpdater() + population_mutator_strings = [ + ms.strip() + for ms in self._ga.population_mutation.split(definitions.PARAM_SEPARATOR) + ] + chromosome_mutator_strings = [ + ms.strip() + for ms in self._ga.chromosome_mutation.split(definitions.PARAM_SEPARATOR) + ] + gene_mutator_strings = [ + ms.strip() + for ms in self._ga.gene_mutation.split(definitions.PARAM_SEPARATOR) + ] + all_mutator_strings = ( + population_mutator_strings + + chromosome_mutator_strings + + gene_mutator_strings + ) + if definitions.CROSS_DIVERSITY in all_mutator_strings: + return CrossDiversityGeneUpdater() + return BaseGeneUpdater() + + def get_chromosome_updater(self): + population_mutator_strings = [ + ms.strip() + for ms in self._ga.population_mutation.split(definitions.PARAM_SEPARATOR) + ] + if ( + not population_mutator_strings + or definitions.PARENT_DIVERSITY in population_mutator_strings + ): + return ParentDiversityChromosomeUpdater() + return BaseChromosomeUpdater() def _get_population_updater(self): """ Population Updater Instance """ - return CostDiversityPopulationUpdater() + population_mutator_strings = [ + ms.strip() + for ms in self._ga.population_mutation.split(definitions.PARAM_SEPARATOR) + ] + if ( + not population_mutator_strings + or definitions.COST_DIVERSITY in population_mutator_strings + ): + return CostDiversityPopulationUpdater() + return BasePopulationUpdater() diff --git a/gadapt/ga_model/chromosome.py b/gadapt/ga_model/chromosome.py index 3beb496..69a9a9c 100644 --- a/gadapt/ga_model/chromosome.py +++ b/gadapt/ga_model/chromosome.py @@ -23,6 +23,7 @@ def __init__( population_generation: population generation """ super().__init__() + self._parent_diversity_coefficient = float("NaN") self._cost_value = definitions.FLOAT_NAN self._is_immigrant = False self._population_generation = population_generation @@ -171,15 +172,15 @@ def add_gene(self, gen_var: Gene, gen_var_value: float = definitions.FLOAT_NAN): self.append(g) @property - def parent_diversity(self) -> float: + def parent_diversity_coefficient(self) -> float: """ Diversity of parents """ - return self._parent_diversity + return self._parent_diversity_coefficient - @parent_diversity.setter - def parent_diversity(self, value: float): - self._parent_diversity = value + @parent_diversity_coefficient.setter + def parent_diversity_coefficient(self, value: float): + self._parent_diversity_coefficient = value @property def population_generation(self) -> int: diff --git a/gadapt/ga_model/population.py b/gadapt/ga_model/population.py index bda5e96..3c6cc48 100644 --- a/gadapt/ga_model/population.py +++ b/gadapt/ga_model/population.py @@ -37,8 +37,8 @@ def __init__(self, options: GAOptions): self.chromosomes: List[Chromosome] = [] self.generate_initial_population() self.start_time = datetime.now() - self.average_cost_step = float("NaN") - self.average_cost_step_in_first_population = float("NaN") + self.absolute_cost_diversity = float("NaN") + self.absolute_cost_diversity_in_first_population = float("NaN") self.timeout_expired = False self.min_cost_per_generation: List[float] = [] diff --git a/gadapt/operations/chromosome_update/parent_diversity_chromosome_updater.py b/gadapt/operations/chromosome_update/parent_diversity_chromosome_updater.py index f0263af..e790276 100644 --- a/gadapt/operations/chromosome_update/parent_diversity_chromosome_updater.py +++ b/gadapt/operations/chromosome_update/parent_diversity_chromosome_updater.py @@ -31,8 +31,8 @@ def chromosome_prepare_update(self, mother_gene: Allele, father_gene: Allele): def chromosome_update(self, offspring1: Chromosome, offspring2: Chromosome): parent_diversity = self._get_parent_diversity() - offspring1.parent_diversity = parent_diversity - offspring2.parent_diversity = parent_diversity + offspring1.parent_diversity_coefficient = parent_diversity + offspring2.parent_diversity_coefficient = parent_diversity def chromosome_start_update(self, *args, **kwargs): self._genetic_diversity = [] diff --git a/gadapt/operations/gene_update/base_gene_updater.py b/gadapt/operations/gene_update/base_gene_updater.py index ecf0e37..a64d51b 100644 --- a/gadapt/operations/gene_update/base_gene_updater.py +++ b/gadapt/operations/gene_update/base_gene_updater.py @@ -1,7 +1,4 @@ -from abc import ABC, abstractmethod - - -class BaseGeneUpdater(ABC): +class BaseGeneUpdater: """ Base class for variable update """ @@ -9,10 +6,9 @@ class BaseGeneUpdater(ABC): def __init__(self): self.population = None - def update_variables(self, population): + def update_genes(self, population): self.population = population - self._update_variables() + self._update_genes() - @abstractmethod - def _update_variables(self): + def _update_genes(self): pass diff --git a/gadapt/operations/gene_update/cross_diversity_gene_updater.py b/gadapt/operations/gene_update/cross_diversity_gene_updater.py index 31e6a3f..6ed3181 100644 --- a/gadapt/operations/gene_update/cross_diversity_gene_updater.py +++ b/gadapt/operations/gene_update/cross_diversity_gene_updater.py @@ -9,7 +9,7 @@ class CrossDiversityGeneUpdater(BaseGeneUpdater): Common variable updater """ - def _update_variables(self): + def _update_genes(self): def scale_values(g: Gene, values): scaled_values = [] if g.min_value == g.max_value: diff --git a/gadapt/operations/mutation/chromosome_mutation/cross_diversity_gene_mutation_rate_determinator.py b/gadapt/operations/mutation/chromosome_mutation/cross_diversity_gene_mutation_rate_determinator.py index cab52bf..41760de 100644 --- a/gadapt/operations/mutation/chromosome_mutation/cross_diversity_gene_mutation_rate_determinator.py +++ b/gadapt/operations/mutation/chromosome_mutation/cross_diversity_gene_mutation_rate_determinator.py @@ -1,4 +1,7 @@ +from math import isnan + import gadapt.utils.ga_utils as ga_utils +from adapters.ga_logging.logging_settings import gadapt_log_error from gadapt.operations.mutation.chromosome_mutation.random_gene_mutation_rate_determinator import ( RandomGeneMutationRateDeterminator, ) @@ -14,18 +17,25 @@ def __init__( ) -> None: super().__init__() + def _get_mutation_rate(self, genes) -> float: + avg_rsd = ga_utils.average([g.cross_diversity_coefficient for g in genes]) + if avg_rsd > 1: + avg_rsd = 1 + if avg_rsd < 0: + avg_rsd = 0 + return avg_rsd + def _get_number_of_mutation_genes(self) -> int: genes = [g.gene for g in self.chromosome] + if any( + g.cross_diversity_coefficient is None + or isnan(g.cross_diversity_coefficient) + for g in genes + ): + gadapt_log_error("cross_diversity_coefficient not set!") + return super()._get_number_of_mutation_genes() - def get_mutation_rate() -> float: - avg_rsd = ga_utils.average([g.cross_diversity_coefficient for g in genes]) - if avg_rsd > 1: - avg_rsd = 1 - if avg_rsd < 0: - avg_rsd = 0 - return avg_rsd - - mutation_rate = get_mutation_rate() + mutation_rate = self._get_mutation_rate(genes) limit_number_of_mutation_genes = mutation_rate * float( self.max_number_of_mutation_genes ) diff --git a/gadapt/operations/mutation/gene_mutation/normal_distribution_cross_diversity_gene_mutator.py b/gadapt/operations/mutation/gene_mutation/normal_distribution_cross_diversity_gene_mutator.py index 3895c3e..006c8dd 100644 --- a/gadapt/operations/mutation/gene_mutation/normal_distribution_cross_diversity_gene_mutator.py +++ b/gadapt/operations/mutation/gene_mutation/normal_distribution_cross_diversity_gene_mutator.py @@ -1,5 +1,8 @@ +import math + import numpy as np +from adapters.ga_logging.logging_settings import gadapt_log_error from gadapt.operations.mutation.gene_mutation.normal_distribution_gene_mutator import ( NormalDistributionGeneMutator, ) @@ -12,6 +15,11 @@ class NormalDistributionCrossDiversityGeneMutator(NormalDistributionGeneMutator) """ def _calculate_normal_distribution_standard_deviation(self): + if self.gene_value.gene.cross_diversity_coefficient is None or math.isnan( + self.gene_value.gene.cross_diversity_coefficient + ): + gadapt_log_error("cross_diversity_coefficient not set!") + return 0.05 min_std_dev = 0.05 max_std_dev = 0.5 std_dev_range = max_std_dev - min_std_dev diff --git a/gadapt/operations/mutation/population_mutation/cost_diversity_chromosome_mutation_rate_determinator.py b/gadapt/operations/mutation/population_mutation/cost_diversity_chromosome_mutation_rate_determinator.py index 1100660..67bf8d0 100644 --- a/gadapt/operations/mutation/population_mutation/cost_diversity_chromosome_mutation_rate_determinator.py +++ b/gadapt/operations/mutation/population_mutation/cost_diversity_chromosome_mutation_rate_determinator.py @@ -1,5 +1,6 @@ import math +from adapters.ga_logging.logging_settings import gadapt_log_error from gadapt.operations.mutation.population_mutation.base_chromosome_mutation_rate_determinator import ( BaseChromosomeMutationRateDeterminator, ) @@ -17,21 +18,28 @@ def __init__( ) -> None: super().__init__() + def _get_cost_diversity_coefficient(self): + cost_diversity_coefficient = float( + self.population.absolute_cost_diversity + / self.population.absolute_cost_diversity_in_first_population + ) + if cost_diversity_coefficient > 1.0: + cost_diversity_coefficient = 1.0 + return cost_diversity_coefficient + + def _get_mutation_rate(self) -> float: + if ( + self.population.absolute_cost_diversity_in_first_population is None + or math.isnan(self.population.absolute_cost_diversity_in_first_population) + or self.population.absolute_cost_diversity is None + or math.isnan(self.population.absolute_cost_diversity) + ): + gadapt_log_error("absolute_cost_diversity not set!") + return 1.0 + return 1.0 - self._get_cost_diversity_coefficient() + def _get_number_of_mutation_chromosomes(self) -> int: - def get_mutation_rate() -> float: - if ( - self.population.average_cost_step_in_first_population is None - or math.isnan(self.population.average_cost_step_in_first_population) - ): - return 1.0 - cost_step_ratio = float( - self.population.average_cost_step - / self.population.average_cost_step_in_first_population - ) - if cost_step_ratio > 1.0: - cost_step_ratio = 1.0 - return 1.0 - cost_step_ratio - - mutation_rate = get_mutation_rate() + + mutation_rate = self._get_mutation_rate() f_return_value = mutation_rate * float(self.max_number_of_mutation_chromosomes) return round(f_return_value) diff --git a/gadapt/operations/mutation/population_mutation/parent_diversity_chromosome_mutation_selector.py b/gadapt/operations/mutation/population_mutation/parent_diversity_chromosome_mutation_selector.py index 6f6e667..451de0a 100644 --- a/gadapt/operations/mutation/population_mutation/parent_diversity_chromosome_mutation_selector.py +++ b/gadapt/operations/mutation/population_mutation/parent_diversity_chromosome_mutation_selector.py @@ -1,5 +1,7 @@ import random +from math import isnan +from adapters.ga_logging.logging_settings import gadapt_log_error from gadapt.ga_model.chromosome import Chromosome from gadapt.operations.mutation.population_mutation.base_chromosome_mutation_rate_determinator import ( BaseChromosomeMutationRateDeterminator, @@ -28,7 +30,7 @@ def __init__( self._sampling = sampling def _sort_key_parent_diversity_random(self, c: Chromosome): - return (c.parent_diversity, random.random()) + return (c.parent_diversity_coefficient, random.random()) def _mutate_population(self): if self.population is None: @@ -36,10 +38,14 @@ def _mutate_population(self): unallocated_chromosomes: list[Chromosome] = self._get_unallocated_chromosomes( self._sort_key_parent_diversity_random ) + if any(isnan(c.parent_diversity_coefficient) for c in unallocated_chromosomes): + gadapt_log_error("parent_diversity_coefficient not set!") chromosomes_for_mutation: list[Chromosome] = [] if self.population.options.must_mutate_for_same_parents: chromosomes_for_mutation = [ - c for c in unallocated_chromosomes if c.parent_diversity == 0 + c + for c in unallocated_chromosomes + if c.parent_diversity_coefficient == 0 ] chromosomes_for_mutation_count = len(chromosomes_for_mutation) rest_number = ( @@ -48,14 +54,16 @@ def _mutate_population(self): if rest_number > 0: if self.population.options.must_mutate_for_same_parents: other_chromosomes_for_mutation = [ - c for c in unallocated_chromosomes if (not c.parent_diversity == 0) + c + for c in unallocated_chromosomes + if (not c.parent_diversity_coefficient == 0) ] else: other_chromosomes_for_mutation = [c for c in unallocated_chromosomes] other_chromosomes_for_mutation = self._sampling.get_sample( other_chromosomes_for_mutation, rest_number, - lambda c: c.parent_diversity, + lambda c: c.parent_diversity_coefficient, ) chromosomes_for_mutation.extend(other_chromosomes_for_mutation) for c in chromosomes_for_mutation: diff --git a/gadapt/operations/population_update/base_population_updater.py b/gadapt/operations/population_update/base_population_updater.py index 7c07a11..5b8a49a 100644 --- a/gadapt/operations/population_update/base_population_updater.py +++ b/gadapt/operations/population_update/base_population_updater.py @@ -1,7 +1,4 @@ -from abc import ABC, abstractmethod - - -class BasePopulationUpdater(ABC): +class BasePopulationUpdater: """ Base class for population update """ @@ -14,6 +11,5 @@ def update_population(self, population): self.population = population self._update_population() - @abstractmethod def _update_population(self): pass diff --git a/gadapt/operations/population_update/cost_diversity_population_updater.py b/gadapt/operations/population_update/cost_diversity_population_updater.py index 98f6b48..173bfc3 100644 --- a/gadapt/operations/population_update/cost_diversity_population_updater.py +++ b/gadapt/operations/population_update/cost_diversity_population_updater.py @@ -11,7 +11,7 @@ class CostDiversityPopulationUpdater(BasePopulationUpdater): Common population updater """ - def _calculate_average_cost_step(self): + def _calculate_absolute_cost_diversity(self): allocated_values = [ c.cost_value for c in self.population.chromosomes @@ -22,8 +22,10 @@ def _calculate_average_cost_step(self): return float("NaN") def _update_population(self): - self.population.average_cost_step = self._calculate_average_cost_step() - if math.isnan(self.population.average_cost_step_in_first_population): - self.population.average_cost_step_in_first_population = ( - self.population.average_cost_step + self.population.absolute_cost_diversity = ( + self._calculate_absolute_cost_diversity() + ) + if math.isnan(self.population.absolute_cost_diversity_in_first_population): + self.population.absolute_cost_diversity_in_first_population = ( + self.population.absolute_cost_diversity )