Files
EasyGA/src/attributes.py
2020-12-13 10:28:16 -05:00

355 lines
13 KiB
Python

# Import square root function for ga.adapt()
from math import sqrt
import random
import sqlite3
from copy import deepcopy
# Import all the data structure prebuilt modules
from structure import Population as create_population
from structure import Chromosome as create_chromosome
from structure import Gene as create_gene
# Structure Methods
from fitness_function import Fitness_Examples
from initialization import Initialization_Methods
from termination_point import Termination_Methods
# Parent/Survivor Selection Methods
from parent_selection import Parent_Selection
from survivor_selection import Survivor_Selection
# Genetic Operator Methods
from mutation import Mutation_Methods
from crossover import Crossover_Methods
# Database class
from database import sql_database
from sqlite3 import Error
# Graphing package
from database import matplotlib_graph
import matplotlib.pyplot as plt
class Attributes:
"""Default GA attributes can be found here. If any attributes have not
been set then they will fall back onto the default attribute. All
attributes have been catigorized to explain sections in the ga process."""
def __init__(self,
chromosome_length = 10,
population_size = 10,
chromosome_impl = None,
gene_impl = lambda: random.randint(1, 10),
population = None,
target_fitness_type = 'max',
update_fitness = True,
parent_ratio = 0.10,
selection_probability = 0.50,
tournament_size_ratio = 0.10,
current_generation = 0,
current_fitness = 0,
generation_goal = 100,
fitness_goal = None,
tolerance_goal = None,
percent_converged = 0.50,
chromosome_mutation_rate = 0.15,
gene_mutation_rate = 0.05,
adapt_rate = 0.05,
adapt_probability_rate = 0.05,
adapt_population_flag = True,
max_selection_probability = 0.75,
min_selection_probability = 0.25,
max_chromosome_mutation_rate = None,
min_chromosome_mutation_rate = None,
max_gene_mutation_rate = 0.99,
min_gene_mutation_rate = 0.01,
dist = None,
initialization_impl = Initialization_Methods.random_initialization,
fitness_function_impl = Fitness_Examples.is_it_5,
make_population = create_population,
make_chromosome = create_chromosome,
make_gene = create_gene,
parent_selection_impl = Parent_Selection.Rank.tournament,
crossover_individual_impl = Crossover_Methods.Individual.single_point,
crossover_population_impl = Crossover_Methods.Population.sequential_selection,
survivor_selection_impl = Survivor_Selection.fill_in_best,
mutation_individual_impl = Mutation_Methods.Individual.individual_genes,
mutation_population_impl = Mutation_Methods.Population.random_avoid_best,
termination_impl = Termination_Methods.fitness_generation_tolerance,
Database = sql_database.SQL_Database,
database_name = 'database.db',
sql_create_data_structure = """CREATE TABLE IF NOT EXISTS data (
id INTEGER PRIMARY KEY,
config_id INTEGER DEFAULT NULL,
generation INTEGER NOT NULL,
fitness REAL,
chromosome TEXT
); """,
Graph = matplotlib_graph.Matplotlib_Graph
):
# Initilization variables
self.chromosome_length = chromosome_length
self.population_size = population_size
self.chromosome_impl = chromosome_impl
self.gene_impl = gene_impl
self.population = population
self.target_fitness_type = target_fitness_type
self.update_fitness = update_fitness
# Selection variables
self.parent_ratio = parent_ratio
self.selection_probability = selection_probability
self.tournament_size_ratio = tournament_size_ratio
# Termination variables
self.current_generation = current_generation
self.current_fitness = current_fitness
self.generation_goal = generation_goal
self.fitness_goal = fitness_goal
self.tolerance_goal = tolerance_goal
self.percent_converged = percent_converged
# Mutation variables
self.chromosome_mutation_rate = chromosome_mutation_rate
self.gene_mutation_rate = gene_mutation_rate
# Adapt variables
self.adapt_rate = adapt_rate
self.adapt_probability_rate = adapt_probability_rate
self.adapt_population_flag = adapt_population_flag
# Bounds on probabilities when adapting
self.max_selection_probability = max_selection_probability
self.min_selection_probability = min_selection_probability
self.max_chromosome_mutation_rate = max_chromosome_mutation_rate
self.min_chromosome_mutation_rate = min_chromosome_mutation_rate
self.max_gene_mutation_rate = max_gene_mutation_rate
self.min_gene_mutation_rate = min_gene_mutation_rate
# Distance between two chromosomes
self.dist = dist
# Default EasyGA implimentation structure
self.initialization_impl = initialization_impl
self.fitness_function_impl = fitness_function_impl
self.make_population = make_population
self.make_chromosome = make_chromosome
self.make_gene = make_gene
# Methods for accomplishing Parent-Selection -> Crossover -> Survivor_Selection -> Mutation
self.parent_selection_impl = parent_selection_impl
self.crossover_individual_impl = crossover_individual_impl
self.crossover_population_impl = crossover_population_impl
self.survivor_selection_impl = survivor_selection_impl
self.mutation_individual_impl = mutation_individual_impl
self.mutation_population_impl = mutation_population_impl
# The type of termination to impliment
self.termination_impl = termination_impl
# Database varibles
self.database = Database()
self.database_name = database_name
self.sql_create_data_structure = sql_create_data_structure
# Graphing variables
self.graph = Graph(self.database)
def save_population(self):
"""Saves the current population to the database."""
self.database.insert_current_population(self)
def save_chromosome(self, chromosome):
"""Saves the given chromosome to the database."""
self.database.insert_current_chromosome(self.current_generation, chromosome)
def numeric_chromosomes(self):
"""Sets default numerical based methods"""
# Adapt every 10th generation
self.adapt_rate = 0.10
# Use averaging for crossover
self.crossover_individual_impl = Crossover_Methods.Individual.Arithmetic.average
# Use averaging for mutation
self.mutation_individual_impl = Mutation_Methods.Individual.Arithmetic.average
# Euclidean norm
self.dist = lambda chromosome_1, chromosome_2:\
sqrt(sum(
(gene_1.value - gene_2.value) ** 2
for gene_1, gene_2
in zip(chromosome_1, chromosome_2)
))
def permutation_chromosomes(self):
"""Sets default permutation based methods"""
self.crossover_individual_impl = Crossover_Methods.Individual.Permutation.ox1
self.mutation_individual_impl = Mutation_Methods.Individual.Permutation.swap_genes
# Count the number of gene pairs they have in common
def dist(chromosome_1, chromosome_2):
gene_list_1 = list(chromosome_1)
gene_list_2 = list(chromosome_2)
count = 0
for i in range(len(gene_list_1)-1):
for j in range(len(gene_list_2)-1):
if gene_list_1[i] == gene_list_2[j]:
if gene_list_1[i+1] == gene_list_2[j+1]:
count += 1
break
return count
self.dist = dist
# Getter and setters for all required varibles
@property
def chromosome_length(self):
"""Getter function for chromosome length"""
return self._chromosome_length
@chromosome_length.setter
def chromosome_length(self, value_input):
"""Setter function with error checking for chromosome length"""
# If the chromosome length is less then or equal 0 throw error
if(not isinstance(value_input, int) or value_input <= 0):
raise ValueError("Chromosome length must be integer greater then 0")
self._chromosome_length = value_input
@property
def population_size(self):
"""Getter function for population size"""
return self._population_size
@population_size.setter
def population_size(self, value_input):
"""Setter function with error checking for population size"""
# If the population size is less then or equal 0 throw error
if(not isinstance(value_input, int) or value_input <= 0):
raise ValueError("Population length must be integer greater then 0")
self._population_size = value_input
@property
def target_fitness_type(self):
"""Getter function for target fitness type."""
return self._target_fitness_type
@target_fitness_type.setter
def target_fitness_type(self, value_input):
"""Setter function for target fitness type."""
self._target_fitness_type = value_input
@property
def max_chromosome_mutation_rate(self):
"""Getter function for max chromosome mutation rate"""
return self._max_chromosome_mutation_rate
@max_chromosome_mutation_rate.setter
def max_chromosome_mutation_rate(self, value_input):
"""Setter function with error checking and default value for max chromosome mutation rate"""
# Default value
if value_input is None:
self._max_chromosome_mutation_rate = min(self.chromosome_mutation_rate*2, (1+self.chromosome_mutation_rate)/2)
# Otherwise check value
elif 0 < value_input < 1:
self._max_chromosome_mutation_rate = value_input
# Throw error
else:
raise ValueError("Max chromosome mutation rate must be between 0 and 1")
@property
def min_chromosome_mutation_rate(self):
"""Getter function for min chromosome mutation rate"""
return self._min_chromosome_mutation_rate
@min_chromosome_mutation_rate.setter
def min_chromosome_mutation_rate(self, value_input):
"""Setter function with error checking and default value for min chromosome mutation rate"""
# Default value
if value_input is None:
self._min_chromosome_mutation_rate = self.chromosome_mutation_rate/2
# Otherwise check value
elif 0 < value_input < 1:
self._min_chromosome_mutation_rate = value_input
# Throw error
else:
raise ValueError("Min chromosome mutation rate must be between 0 and 1")
@property
def dist(self):
"""Getter function for the distance between chromosomes."""
return self._dist
@dist.setter
def dist(self, value_input):
"""Setter function for the distance between chromosomes."""
# Default value by comparing fitnesses of chromosomes
if value_input is None:
self._dist = lambda chromosome_1, chromosome_2:\
sqrt(abs(chromosome_1.fitness - chromosome_2.fitness))
# Given input
else:
self._dist = value_input
@property
def database_name(self):
"""Getter function for the database name"""
return self._database_name
@database_name.setter
def database_name(self, value_input):
"""Setter function with error checking for the database name"""
# Update the database class of the name change
self.database._database_name = value_input
# Set the name in the ga attribute
self._database_name = value_input