Changed __setattr__ for clean inheritance approach
No longer necessary to prepend built-in method names with _ when defining bound class methods
This commit is contained in:
@ -31,24 +31,18 @@ from database import matplotlib_graph
|
|||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
|
||||||
def function_info(setter):
|
|
||||||
"""Decorator for setter functions involving functions so that the name and doc-string are kept."""
|
|
||||||
|
|
||||||
def new_setter(obj, method):
|
|
||||||
if (new_method := setter(obj, method)) is not None:
|
|
||||||
new_method.__name__ = method.__name__
|
|
||||||
new_method.__doc__ = method.__doc__
|
|
||||||
|
|
||||||
return new_setter
|
|
||||||
|
|
||||||
|
|
||||||
class Attributes:
|
class Attributes:
|
||||||
"""Default GA attributes can be found here. If any attributes have not
|
"""Default GA attributes can be found here. If any attributes have not
|
||||||
been set then they will fall back onto the default attribute. All
|
been set then they will fall back onto the default attribute. All
|
||||||
attributes have been catigorized to explain sections in the ga process."""
|
attributes have been catigorized to explain sections in the ga process."""
|
||||||
|
|
||||||
|
#=====================================#
|
||||||
|
# Special built-in class __methods__: #
|
||||||
|
#=====================================#
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
*,
|
||||||
chromosome_length = 10,
|
chromosome_length = 10,
|
||||||
population_size = 10,
|
population_size = 10,
|
||||||
chromosome_impl = None,
|
chromosome_impl = None,
|
||||||
@ -168,6 +162,96 @@ class Attributes:
|
|||||||
self.graph = Graph(self.database)
|
self.graph = Graph(self.database)
|
||||||
|
|
||||||
|
|
||||||
|
def __setattr__(self, name, value):
|
||||||
|
"""Custom setter for using
|
||||||
|
|
||||||
|
self.name = value
|
||||||
|
|
||||||
|
which follows the following guidelines:
|
||||||
|
- if self.name is a property, the specific property setter is used
|
||||||
|
- else if value is callable, self is passed in as the first parameter
|
||||||
|
- else if value is not None or self.name is not set, assign it like normal
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check for property
|
||||||
|
if hasattr(type(self), name) \
|
||||||
|
and isinstance((prop := getattr(type(self), name)), property):
|
||||||
|
if name == 'dist': print("property")
|
||||||
|
prop.fset(self, value)
|
||||||
|
|
||||||
|
# Check for function
|
||||||
|
elif callable(value):
|
||||||
|
foo = lambda *args, **kwargs: value(self, *args, **kwargs)
|
||||||
|
# Reassign name and doc-string for documentation
|
||||||
|
foo.__name__ = value.__name__
|
||||||
|
foo.__doc__ = value.__doc__
|
||||||
|
self.__dict__[name] = foo
|
||||||
|
|
||||||
|
# Assign like normal unless None or undefined self.name
|
||||||
|
elif value is not None or not hasattr(self, name):
|
||||||
|
self.__dict__[name] = value
|
||||||
|
|
||||||
|
|
||||||
|
#===========================#
|
||||||
|
# Default built-in methods: #
|
||||||
|
#===========================#
|
||||||
|
|
||||||
|
|
||||||
|
def dist(self, chromosome_1, chromosome_2):
|
||||||
|
"""Default distance lambda. Returns the square root of the difference in fitnesses."""
|
||||||
|
return sqrt(abs(chromosome_1.fitness - chromosome_2.fitness))
|
||||||
|
|
||||||
|
|
||||||
|
def gene_impl(self, *args, **kwargs):
|
||||||
|
"""Default gene implementation. Returns a random integer from 1 to 10."""
|
||||||
|
return random.randint(1, 10)
|
||||||
|
|
||||||
|
|
||||||
|
def _fitness_function_impl(self, *args, **kwargs):
|
||||||
|
"""Default fitness function. Returns the number of genes that are 5."""
|
||||||
|
return Fitness_Examples.is_it_5(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def parent_selection_impl(self, *args, **kwargs):
|
||||||
|
"""Default parent selection method using tournament selection."""
|
||||||
|
return Parent_Selection.Rank.tournament(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def crossover_individual_impl(self, *args, **kwargs):
|
||||||
|
"""Default individual crossover method using single point crossover."""
|
||||||
|
return Crossover_Methods.Individual.single_point(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def crossover_population_impl(self, *args, **kwargs):
|
||||||
|
"""Default population crossover method using sequential selection."""
|
||||||
|
return Crossover_Methods.Population.sequential_selection(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def survivor_selection_impl(self, *args, **kwargs):
|
||||||
|
"""Default survivor selection method using the fill in best method."""
|
||||||
|
return Survivor_Selection.fill_in_best(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def mutation_individual_impl(self, *args, **kwargs):
|
||||||
|
"""Default individual mutation method by randomizing individual genes."""
|
||||||
|
return Mutation_Methods.Individual.individual_genes(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def mutation_population_impl(self, *args, **kwargs):
|
||||||
|
"""Default population mutation method selects chromosomes randomly while avoiding the best."""
|
||||||
|
return Mutation_Methods.Population.random_avoid_best(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def termination_impl(self, *args, **kwargs):
|
||||||
|
"""Default termination method by testing the fitness, generation, and tolerance goals."""
|
||||||
|
return Termination_Methods.fitness_generation_tolerance(self, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
#============================#
|
||||||
|
# Built-in database methods: #
|
||||||
|
#============================#
|
||||||
|
|
||||||
|
|
||||||
def save_population(self):
|
def save_population(self):
|
||||||
"""Saves the current population to the database."""
|
"""Saves the current population to the database."""
|
||||||
self.database.insert_current_population(self)
|
self.database.insert_current_population(self)
|
||||||
@ -178,6 +262,11 @@ class Attributes:
|
|||||||
self.database.insert_current_chromosome(self.current_generation, chromosome)
|
self.database.insert_current_chromosome(self.current_generation, chromosome)
|
||||||
|
|
||||||
|
|
||||||
|
#===================#
|
||||||
|
# Built-in options: #
|
||||||
|
#===================#
|
||||||
|
|
||||||
|
|
||||||
def numeric_chromosomes(self):
|
def numeric_chromosomes(self):
|
||||||
"""Sets default numerical based methods"""
|
"""Sets default numerical based methods"""
|
||||||
|
|
||||||
@ -191,7 +280,7 @@ class Attributes:
|
|||||||
self.mutation_individual_impl = Mutation_Methods.Individual.Arithmetic.average
|
self.mutation_individual_impl = Mutation_Methods.Individual.Arithmetic.average
|
||||||
|
|
||||||
# Euclidean norm
|
# Euclidean norm
|
||||||
self.dist = lambda chromosome_1, chromosome_2:\
|
self.dist = lambda self, chromosome_1, chromosome_2:\
|
||||||
sqrt(sum(
|
sqrt(sum(
|
||||||
(gene_1.value - gene_2.value) ** 2
|
(gene_1.value - gene_2.value) ** 2
|
||||||
for gene_1, gene_2
|
for gene_1, gene_2
|
||||||
@ -208,7 +297,7 @@ class Attributes:
|
|||||||
self.mutation_individual_impl = Mutation_Methods.Individual.Permutation.swap_genes
|
self.mutation_individual_impl = Mutation_Methods.Individual.Permutation.swap_genes
|
||||||
|
|
||||||
# Count the number of gene pairs they don't have in common
|
# Count the number of gene pairs they don't have in common
|
||||||
def dist(chromosome_1, chromosome_2):
|
def dist(self, chromosome_1, chromosome_2):
|
||||||
|
|
||||||
# Used to set values during comprehension
|
# Used to set values during comprehension
|
||||||
set_value = lambda arg: True
|
set_value = lambda arg: True
|
||||||
@ -234,224 +323,69 @@ class Attributes:
|
|||||||
self.dist = dist
|
self.dist = dist
|
||||||
|
|
||||||
|
|
||||||
# Getter and setters for all required varibles
|
#===========================#
|
||||||
|
# Getter/setter properties: #
|
||||||
|
#===========================#
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def dist(self):
|
def make_population(self):
|
||||||
"""Getter for distance lambda which passes a reference of self in."""
|
"""Getter function for making populations."""
|
||||||
return self._dist
|
return self._make_population
|
||||||
|
|
||||||
|
|
||||||
@dist.setter
|
@make_population.setter
|
||||||
@function_info
|
def make_population(self, method):
|
||||||
def dist(self, method):
|
"""Setter function for making populations."""
|
||||||
"""Setter for distance lambda."""
|
self.__dict__['_make_population'] = method
|
||||||
if method is not None:
|
|
||||||
self._dist = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._dist
|
|
||||||
|
|
||||||
|
|
||||||
def _dist(self, chromosome_1, chromosome_2):
|
|
||||||
"""Default distance lambda. Returns the square root of the difference in fitnesses."""
|
|
||||||
return sqrt(abs(chromosome_1.fitness - chromosome_2.fitness))
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def chromosome_impl(self):
|
def make_chromosome(self):
|
||||||
"""Getter for chromosome implementation lambda which passes a reference of self in."""
|
"""Getter function for making chromosomes."""
|
||||||
return self._chromosome_impl
|
return self._make_chromosome
|
||||||
|
|
||||||
|
|
||||||
@chromosome_impl.setter
|
@make_chromosome.setter
|
||||||
@function_info
|
def make_chromosome(self, method):
|
||||||
def chromosome_impl(self, method):
|
"""Setter function for making chromosomes."""
|
||||||
"""Setter for chromosome implementation lambda."""
|
self.__dict__['_make_chromosome'] = method
|
||||||
if method is not None:
|
|
||||||
self._chromosome_impl = lambda *args, **kwargs: method(*args, **kwargs)
|
|
||||||
return self._chromosome_impl
|
@property
|
||||||
|
def make_gene(self):
|
||||||
|
"""Getter function for making genes."""
|
||||||
|
return self._make_gene
|
||||||
|
|
||||||
|
|
||||||
|
@make_gene.setter
|
||||||
|
def make_gene(self, method):
|
||||||
|
"""Setter function for making genes."""
|
||||||
|
self.__dict__['_make_gene'] = method
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gene_impl(self):
|
def gene_impl(self):
|
||||||
"""Getter for gene implementation lambda which passes a reference of self in."""
|
"""Getter function for making randomized genes."""
|
||||||
return self._gene_impl
|
return self._gene_impl
|
||||||
|
|
||||||
|
|
||||||
@gene_impl.setter
|
@gene_impl.setter
|
||||||
@function_info
|
|
||||||
def gene_impl(self, method):
|
def gene_impl(self, method):
|
||||||
"""Setter for gene implementation lambda."""
|
"""Setter function for making randomized genes."""
|
||||||
if method is not None:
|
self.__dict__['_gene_impl'] = method
|
||||||
self._gene_impl = lambda *args, **kwargs: method(*args, **kwargs)
|
|
||||||
return self._gene_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _gene_impl(self, *args, **kwargs):
|
|
||||||
"""Default gene implementation. Returns a random integer from 1 to 10."""
|
|
||||||
return random.randint(1, 10)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def fitness_function_impl(self):
|
def chromosome_impl(self):
|
||||||
"""Getter for fitness function lambda which passes a reference of self in."""
|
"""Getter function for making randomized chromosomes."""
|
||||||
return self._fitness_function_impl
|
return self._chromosome_impl
|
||||||
|
|
||||||
|
|
||||||
@fitness_function_impl.setter
|
@chromosome_impl.setter
|
||||||
@function_info
|
def chromosome_impl(self, method):
|
||||||
def fitness_function_impl(self, method):
|
"""Setter function for making randomized chromosomes."""
|
||||||
"""Setter for fitness function lambda."""
|
self.__dict__['_chromosome_impl'] = method
|
||||||
if method is not None:
|
|
||||||
self._fitness_function_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._fitness_function_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _fitness_function_impl(self, *args, **kwargs):
|
|
||||||
"""Default fitness function. Returns the number of genes that are 5."""
|
|
||||||
return Fitness_Examples.is_it_5(*args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def parent_selection_impl(self):
|
|
||||||
"""Getter for parent selection lambda which passes a reference of self in."""
|
|
||||||
|
|
||||||
return self._parent_selection_impl
|
|
||||||
|
|
||||||
|
|
||||||
@parent_selection_impl.setter
|
|
||||||
@function_info
|
|
||||||
def parent_selection_impl(self, method):
|
|
||||||
"""Setter for parent selection lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._parent_selection_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._parent_selection_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _parent_selection_impl(self, *args, **kwargs):
|
|
||||||
"""Default parent selection method using tournament selection."""
|
|
||||||
return Parent_Selection.Rank.tournament(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def crossover_individual_impl(self):
|
|
||||||
"""Getter for individual crossover lambda which passes a reference of self in."""
|
|
||||||
|
|
||||||
return self._crossover_individual_impl
|
|
||||||
|
|
||||||
|
|
||||||
@crossover_individual_impl.setter
|
|
||||||
@function_info
|
|
||||||
def crossover_individual_impl(self, method):
|
|
||||||
"""Setter for individual crossover lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._crossover_individual_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._crossover_individual_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _crossover_individual_impl(self, *args, **kwargs):
|
|
||||||
"""Default individual crossover method using single point crossover."""
|
|
||||||
return Crossover_Methods.Individual.single_point(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def crossover_population_impl(self):
|
|
||||||
"""Getter for population crossover lambda which passes a reference of self in."""
|
|
||||||
return self._crossover_population_impl
|
|
||||||
|
|
||||||
|
|
||||||
@crossover_population_impl.setter
|
|
||||||
@function_info
|
|
||||||
def crossover_population_impl(self, method):
|
|
||||||
"""Setter for population crossover lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._crossover_population_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._crossover_population_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _crossover_population_impl(self, *args, **kwargs):
|
|
||||||
"""Default population crossover method using sequential selection."""
|
|
||||||
return Crossover_Methods.Population.sequential_selection(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def survivor_selection_impl(self):
|
|
||||||
"""Getter for survivor selection lambda which passes a reference of self in."""
|
|
||||||
return self._survivor_selection_impl
|
|
||||||
|
|
||||||
|
|
||||||
@survivor_selection_impl.setter
|
|
||||||
@function_info
|
|
||||||
def survivor_selection_impl(self, method):
|
|
||||||
"""Setter for survivor selection lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._survivor_selection_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._survivor_selection_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _survivor_selection_impl(self, *args, **kwargs):
|
|
||||||
"""Default survivor selection method using the fill in best method."""
|
|
||||||
return Survivor_Selection.fill_in_best(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mutation_individual_impl(self):
|
|
||||||
"""Getter for individual mutation lambda which passes a reference of self in."""
|
|
||||||
return self._mutation_individual_impl
|
|
||||||
|
|
||||||
|
|
||||||
@mutation_individual_impl.setter
|
|
||||||
@function_info
|
|
||||||
def mutation_individual_impl(self, method):
|
|
||||||
"""Setter for individual mutation lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._mutation_individual_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._mutation_individual_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _mutation_individual_impl(self, *args, **kwargs):
|
|
||||||
"""Default individual mutation method by randomizing individual genes."""
|
|
||||||
return Mutation_Methods.Individual.individual_genes(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def mutation_population_impl(self):
|
|
||||||
"""Getter for population mutation lambda which passes a reference of self in."""
|
|
||||||
return self._mutation_population_impl
|
|
||||||
|
|
||||||
|
|
||||||
@mutation_population_impl.setter
|
|
||||||
@function_info
|
|
||||||
def mutation_population_impl(self, method):
|
|
||||||
"""Setter for population mutation lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._mutation_population_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._mutation_population_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _mutation_population_impl(self, *args, **kwargs):
|
|
||||||
"""Default population mutation method selects chromosomes randomly while avoiding the best."""
|
|
||||||
return Mutation_Methods.Population.random_avoid_best(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
|
||||||
def termination_impl(self):
|
|
||||||
"""Getter for termination lambda which passes a reference of self in."""
|
|
||||||
return self._termination_impl
|
|
||||||
|
|
||||||
|
|
||||||
@termination_impl.setter
|
|
||||||
@function_info
|
|
||||||
def termination_impl(self, method):
|
|
||||||
"""Setter for termination lambda."""
|
|
||||||
if method is not None:
|
|
||||||
self._termination_impl = lambda *args, **kwargs: method(self, *args, **kwargs)
|
|
||||||
return self._termination_impl
|
|
||||||
|
|
||||||
|
|
||||||
def _termination_impl(self, *args, **kwargs):
|
|
||||||
"""Default termination method by testing the fitness, generation, and tolerance goals."""
|
|
||||||
return Termination_Methods.fitness_generation_tolerance(self, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
Reference in New Issue
Block a user