diff --git a/gadapt/factory/ga_factory.py b/gadapt/factory/ga_factory.py index ff2ed18..c1d4cee 100644 --- a/gadapt/factory/ga_factory.py +++ b/gadapt/factory/ga_factory.py @@ -5,9 +5,6 @@ from gadapt.operations.cost_finding.base_cost_finder import BaseCostFinder from gadapt.operations.cost_finding.elitism_cost_finder import ElitismCostFinder from gadapt.operations.crossover.base_crossover import BaseCrossover -from gadapt.operations.crossover.blending_parent_diversity_crossover import ( - BlendingParentDiversityCrossover, -) from gadapt.operations.exit_check.avg_cost_exit_checker import AvgCostExitChecker from gadapt.operations.exit_check.base_exit_checker import BaseExitChecker from gadapt.operations.exit_check.min_cost_exit_checker import MinCostExitChecker @@ -118,6 +115,8 @@ from gadapt.operations.population_update.common_population_updater import ( CommonPopulationUpdater, ) +from gadapt.operations.crossover.base_crossover_event_handler import BaseCrossoverEventHandler +from gadapt.operations.crossover.parent_diversity_crossover_event_handler import ParentDiversityCrossoverEventHandler class GAFactory(BaseGAFactory): @@ -515,16 +514,18 @@ def _get_crossover(self) -> BaseCrossover: for value in mutator_strings if value in definitions.POPULATION_MUTATION_SELECTION_STRINGS ] + crossover_event_handler: BaseCrossoverEventHandler = BaseCrossoverEventHandler() if ( not population_mutation_selection_strings or definitions.PARENT_DIVERSITY in mutator_strings ): - return BlendingParentDiversityCrossover() + crossover_event_handler = ParentDiversityCrossoverEventHandler() + if self._ga.crossover == definitions.BLENDING: - return BlendingCrossover() + return BlendingCrossover(crossover_event_handler) if self._ga.crossover == definitions.UNIFORM: - return UniformCrossover() - return BlendingParentDiversityCrossover() + return UniformCrossover(crossover_event_handler) + return BlendingCrossover(crossover_event_handler) def _get_variable_updater(self): """ diff --git a/gadapt/operations/crossover/base_crossover.py b/gadapt/operations/crossover/base_crossover.py index e1f305c..7979842 100644 --- a/gadapt/operations/crossover/base_crossover.py +++ b/gadapt/operations/crossover/base_crossover.py @@ -4,6 +4,7 @@ from gadapt.ga_model.gene import Gene from gadapt.ga_model.chromosome import Chromosome +from gadapt.operations.crossover.base_crossover_event_handler import BaseCrossoverEventHandler class BaseCrossover(ABC): @@ -11,8 +12,11 @@ class BaseCrossover(ABC): def __init__( self, + event_handler: BaseCrossoverEventHandler + ): self._current_gene_number = -1 + self._event_handler = event_handler def mate(self, chromosome_pairs: List[Tuple[Chromosome, Chromosome]], population): """ @@ -65,6 +69,7 @@ def _mate_pair( return self._offspring1, self._offspring2 def _cross_genetic_material(self): + self._event_handler.pre_cross_genetic_material() number_of_genes = len(self._father) for self._current_gene_number in range(number_of_genes): self._mother_gene, self._father_gene = self._get_mother_father_genes() @@ -174,7 +179,7 @@ def _increase_generation(self): self._offspring2.last_immigrant_generation = current_generation def _decision_variable_crossed(self): - pass + self._event_handler.on_decision_variable_crossed(mother_gene=self._mother_gene, father_gene=self._father_gene) def _all_decision_variable_crossed(self): - pass + self._event_handler.on_all_decision_variable_crossed(offspring1=self._offspring1, offspring2=self._offspring2) diff --git a/gadapt/operations/crossover/base_crossover_event_handler.py b/gadapt/operations/crossover/base_crossover_event_handler.py new file mode 100644 index 0000000..eec5710 --- /dev/null +++ b/gadapt/operations/crossover/base_crossover_event_handler.py @@ -0,0 +1,12 @@ +class BaseCrossoverEventHandler: + """ + Handles crossover events + """ + def on_decision_variable_crossed(self, *args, **kwargs): + pass + + def on_all_decision_variable_crossed(self, *args, **kwargs): + pass + + def pre_cross_genetic_material(self, *args, **kwargs): + pass diff --git a/gadapt/operations/crossover/blending_crossover.py b/gadapt/operations/crossover/blending_crossover.py index 4098057..3c9bc07 100644 --- a/gadapt/operations/crossover/blending_crossover.py +++ b/gadapt/operations/crossover/blending_crossover.py @@ -1,6 +1,7 @@ import random from gadapt.operations.crossover.base_crossover import BaseCrossover +from gadapt.operations.crossover.base_crossover_event_handler import BaseCrossoverEventHandler class BlendingCrossover(BaseCrossover): @@ -13,8 +14,9 @@ class BlendingCrossover(BaseCrossover): def __init__( self, + event_handler: BaseCrossoverEventHandler ): - super(BlendingCrossover, self).__init__() + super(BlendingCrossover, self).__init__(event_handler) self._current_gene_number = -1 def _combine(self): diff --git a/gadapt/operations/crossover/blending_parent_diversity_crossover.py b/gadapt/operations/crossover/blending_parent_diversity_crossover.py deleted file mode 100644 index 25f2cee..0000000 --- a/gadapt/operations/crossover/blending_parent_diversity_crossover.py +++ /dev/null @@ -1,37 +0,0 @@ -from gadapt.utils import ga_utils -from gadapt.operations.crossover.blending_crossover import BlendingCrossover -from gadapt.operations.crossover.uniform_crossover import UniformCrossover - - -class BlendingParentDiversityCrossover(BlendingCrossover): - """ - Blending Crossover. Genes from parents' chromosomes are combined in a blending way. - Calculates diversity of parents and save it to chromosomes. - """ - - def __init__(self): - super(BlendingParentDiversityCrossover, self).__init__() - self._genetic_diversity = None - - def _get_genetic_diversity(self) -> float: - return abs( - self._mother_gene.variable_value - self._father_gene.variable_value - ) / ( - self._father_gene.decision_variable.max_value - - self._father_gene.decision_variable.min_value - ) - - def _get_parent_diversity(self): - return round(ga_utils.average(self._genetic_diversity), 2) - - def _decision_variable_crossed(self): - self._genetic_diversity.append(self._get_genetic_diversity()) - - def _all_decision_variable_crossed(self): - parent_diversity = self._get_parent_diversity() - self._offspring1.parent_diversity = parent_diversity - self._offspring2.parent_diversity = parent_diversity - - def _cross_genetic_material(self): - self._genetic_diversity = [] - super()._cross_genetic_material() diff --git a/gadapt/operations/crossover/parent_diversity_crossover_event_handler.py b/gadapt/operations/crossover/parent_diversity_crossover_event_handler.py new file mode 100644 index 0000000..9b65dc3 --- /dev/null +++ b/gadapt/operations/crossover/parent_diversity_crossover_event_handler.py @@ -0,0 +1,41 @@ +from gadapt.utils import ga_utils +from gadapt.operations.crossover.base_crossover_event_handler import BaseCrossoverEventHandler + + +class ParentDiversityCrossoverEventHandler(BaseCrossoverEventHandler): + """ + Handles crossover events using parent diversity mutation. + """ + + def __init__(self): + self._genetic_diversity = None + + def _get_genetic_diversity(self, mother_gene, father_gene) -> float: + return abs( + mother_gene.variable_value - father_gene.variable_value + ) / ( + father_gene.decision_variable.max_value + - father_gene.decision_variable.min_value + ) + + def _get_parent_diversity(self): + return round(ga_utils.average(self._genetic_diversity), 2) + + def on_decision_variable_crossed(self, *args, **kwargs): + mother_gene = kwargs.get('mother_gene') + father_gene = kwargs.get('father_gene') + if mother_gene is None or father_gene is None: + return + self._genetic_diversity.append(self._get_genetic_diversity(mother_gene, father_gene)) + + def on_all_decision_variable_crossed(self, *args, **kwargs): + parent_diversity = self._get_parent_diversity() + offspring1 = kwargs.get('offspring1') + offspring2 = kwargs.get('offspring2') + if offspring1 is None or offspring2 is None: + return + offspring1.parent_diversity = parent_diversity + offspring2.parent_diversity = parent_diversity + + def pre_cross_genetic_material(self, *args, **kwargs): + self._genetic_diversity = [] diff --git a/gadapt/operations/crossover/uniform_crossover.py b/gadapt/operations/crossover/uniform_crossover.py index 0cdd31e..eefe30f 100644 --- a/gadapt/operations/crossover/uniform_crossover.py +++ b/gadapt/operations/crossover/uniform_crossover.py @@ -2,6 +2,7 @@ from typing import Tuple from gadapt.operations.crossover.base_crossover import BaseCrossover +from gadapt.operations.crossover.base_crossover_event_handler import BaseCrossoverEventHandler class UniformCrossover(BaseCrossover): @@ -11,8 +12,9 @@ class UniformCrossover(BaseCrossover): def __init__( self, + event_handler: BaseCrossoverEventHandler ): - super(UniformCrossover, self).__init__() + super(UniformCrossover, self).__init__(event_handler) def _combine(self) -> Tuple[float, float]: rnd = random.randint(0, 2) diff --git a/setup.py b/setup.py index 67459cf..e321371 100644 --- a/setup.py +++ b/setup.py @@ -2,7 +2,7 @@ setup( name="gadapt", - version="0.4.14", + version="0.4.15", author="Zoran Jankovic", author_email="bpzoran@yahoo.com", url="https://github.com/bpzoran/gadapt",