Using built-in decorator wrapper

This commit is contained in:
SimpleArt
2021-02-04 20:11:38 -05:00
parent 475fd95830
commit ca286eb392

View File

@ -1,35 +1,19 @@
import random import random
from math import ceil from math import ceil
from functools import wraps
def function_info(decorator):
"""Recovers the name and doc-string for decorators throughout EasyGA for documentation purposes."""
def new_decorator(method):
# Apply old decorator
new_method = decorator(method)
# Recover name and doc-string
new_method.__name__ = method.__name__
new_method.__doc__ = method.__doc__
# Return new method with proper name and doc-string
return new_method
return new_decorator
#=======================# #=======================#
# Crossover decorators: # # Crossover decorators: #
#=======================# #=======================#
@function_info
def _check_weight(individual_method): def _check_weight(individual_method):
"""Checks if the weight is between 0 and 1 before running. """Checks if the weight is between 0 and 1 before running.
Exception may occur when using ga.adapt, which will catch Exception may occur when using ga.adapt, which will catch
the error and try again with valid weight. the error and try again with valid weight.
""" """
@wraps(individual_method)
def new_method(ga, parent_1, parent_2, *, weight = individual_method.__kwdefaults__.get('weight', None)): def new_method(ga, parent_1, parent_2, *, weight = individual_method.__kwdefaults__.get('weight', None)):
if weight is None: if weight is None:
@ -41,10 +25,10 @@ def _check_weight(individual_method):
return new_method return new_method
@function_info
def _gene_by_gene(individual_method): def _gene_by_gene(individual_method):
"""Perform crossover by making a single new chromosome by combining each gene by gene.""" """Perform crossover by making a single new chromosome by combining each gene by gene."""
@wraps(individual_method)
def new_method(ga, parent_1, parent_2, *, weight = individual_method.__kwdefaults__.get('weight', 'None')): def new_method(ga, parent_1, parent_2, *, weight = individual_method.__kwdefaults__.get('weight', 'None')):
ga.population.add_child( ga.population.add_child(
@ -62,11 +46,11 @@ def _gene_by_gene(individual_method):
# Parent decorators: # # Parent decorators: #
#====================# #====================#
@function_info
def _check_selection_probability(selection_method): def _check_selection_probability(selection_method):
"""Raises a ValueError if the selection_probability is not between 0 and 1 inclusively. """Raises a ValueError if the selection_probability is not between 0 and 1 inclusively.
Otherwise runs the selection method.""" Otherwise runs the selection method."""
@wraps(selection_method)
def new_method(ga): def new_method(ga):
if 0 <= ga.selection_probability <= 1: if 0 <= ga.selection_probability <= 1:
selection_method(ga) selection_method(ga)
@ -76,11 +60,11 @@ def _check_selection_probability(selection_method):
return new_method return new_method
@function_info
def _check_positive_fitness(selection_method): def _check_positive_fitness(selection_method):
"""Raises a ValueError if the population contains a chromosome with negative fitness. """Raises a ValueError if the population contains a chromosome with negative fitness.
Otherwise runs the selection method.""" Otherwise runs the selection method."""
@wraps(selection_method)
def new_method(ga): def new_method(ga):
if ga.get_chromosome_fitness(0) > 0 and ga.get_chromosome_fitness(-1) >= 0: if ga.get_chromosome_fitness(0) > 0 and ga.get_chromosome_fitness(-1) >= 0:
selection_method(ga) selection_method(ga)
@ -90,21 +74,21 @@ def _check_positive_fitness(selection_method):
return new_method return new_method
@function_info
def _ensure_sorted(selection_method): def _ensure_sorted(selection_method):
"""Sorts the population by fitness and then runs the selection method.""" """Sorts the population by fitness and then runs the selection method."""
@wraps(selection_method)
def new_method(ga): def new_method(ga):
ga.sort_by_best_fitness() ga.sort_by_best_fitness()
selection_method(ga) selection_method(ga)
return new_method return new_method
@function_info
def _compute_parent_amount(selection_method): def _compute_parent_amount(selection_method):
"""Computes the amount of parents needed to be selected, """Computes the amount of parents needed to be selected,
and passes it as another argument for the method.""" and passes it as another argument for the method."""
@wraps(selection_method)
def new_method(ga): def new_method(ga):
parent_amount = max(2, round(len(ga.population)*ga.parent_ratio)) parent_amount = max(2, round(len(ga.population)*ga.parent_ratio))
selection_method(ga, parent_amount) selection_method(ga, parent_amount)
@ -117,10 +101,10 @@ def _compute_parent_amount(selection_method):
#======================# #======================#
@function_info
def _check_chromosome_mutation_rate(population_method): def _check_chromosome_mutation_rate(population_method):
"""Checks if the chromosome mutation rate is a float between 0 and 1 before running.""" """Checks if the chromosome mutation rate is a float between 0 and 1 before running."""
@wraps(population_method)
def new_method(ga): def new_method(ga):
if not isinstance(ga.chromosome_mutation_rate, float): if not isinstance(ga.chromosome_mutation_rate, float):
@ -135,10 +119,10 @@ def _check_chromosome_mutation_rate(population_method):
return new_method return new_method
@function_info
def _check_gene_mutation_rate(individual_method): def _check_gene_mutation_rate(individual_method):
"""Checks if the gene mutation rate is a float between 0 and 1 before running.""" """Checks if the gene mutation rate is a float between 0 and 1 before running."""
@wraps(population_method)
def new_method(ga, index): def new_method(ga, index):
if not isinstance(ga.gene_mutation_rate, float): if not isinstance(ga.gene_mutation_rate, float):
@ -153,10 +137,10 @@ def _check_gene_mutation_rate(individual_method):
return new_method return new_method
@function_info
def _reset_fitness(individual_method): def _reset_fitness(individual_method):
"""Resets the fitness value of the chromosome.""" """Resets the fitness value of the chromosome."""
@wraps(population_method)
def new_method(ga, chromosome): def new_method(ga, chromosome):
chromosome.fitness = None chromosome.fitness = None
individual_method(ga, chromosome) individual_method(ga, chromosome)
@ -164,12 +148,12 @@ def _reset_fitness(individual_method):
return new_method return new_method
@function_info
def _loop_random_mutations(individual_method): def _loop_random_mutations(individual_method):
"""Runs the individual method until enough """Runs the individual method until enough
genes are mutated on the indexed chromosome.""" genes are mutated on the indexed chromosome."""
# Change input to include the gene index being mutated. # Change input to include the gene index being mutated.
@wraps(population_method)
def new_method(ga, chromosome): def new_method(ga, chromosome):
sample_space = range(len(chromosome)) sample_space = range(len(chromosome))
@ -191,10 +175,10 @@ def _loop_random_mutations(individual_method):
# Termination decorators: # # Termination decorators: #
#=========================# #=========================#
@function_info
def _add_by_fitness_goal(termination_impl): def _add_by_fitness_goal(termination_impl):
"""Adds termination by fitness goal to the method.""" """Adds termination by fitness goal to the method."""
@wraps(termination_method)
def new_method(ga): def new_method(ga):
# Try to check the fitness goal # Try to check the fitness goal
@ -218,10 +202,10 @@ def _add_by_fitness_goal(termination_impl):
return new_method return new_method
@function_info
def _add_by_generation_goal(termination_impl): def _add_by_generation_goal(termination_impl):
"""Adds termination by generation goal to the method.""" """Adds termination by generation goal to the method."""
@wraps(termination_method)
def new_method(ga): def new_method(ga):
# If generation goal is set, check it. # If generation goal is set, check it.
@ -234,10 +218,10 @@ def _add_by_generation_goal(termination_impl):
return new_method return new_method
@function_info
def _add_by_tolerance_goal(termination_impl): def _add_by_tolerance_goal(termination_impl):
"""Adds termination by tolerance goal to the method.""" """Adds termination by tolerance goal to the method."""
@wraps(termination_method)
def new_method(ga): def new_method(ga):
# If tolerance is set, check it, if possible. # If tolerance is set, check it, if possible.