Another attempt at this maddess
This commit is contained in:
@ -7,7 +7,7 @@ import EasyGA.decorators
|
||||
# Import all the data structure prebuilt modules
|
||||
from EasyGA.structure import Population as make_population
|
||||
from EasyGA.structure import Chromosome as make_chromosome
|
||||
from EasyGA.structure import Gene as make_gene
|
||||
from EasyGA.structure import Gene as make_gene
|
||||
from EasyGA.structure import Population
|
||||
from EasyGA.structure import Chromosome
|
||||
from EasyGA.structure import Gene
|
||||
@ -17,23 +17,23 @@ from EasyGA.examples import Fitness
|
||||
from EasyGA.termination import Termination
|
||||
|
||||
# Parent/Survivor Selection Methods
|
||||
from EasyGA.parent import Parent
|
||||
from EasyGA.parent import Parent
|
||||
from EasyGA.survivor import Survivor
|
||||
|
||||
# Genetic Operator Methods
|
||||
from EasyGA.crossover import Crossover
|
||||
from EasyGA.mutation import Mutation
|
||||
from EasyGA.mutation import Mutation
|
||||
|
||||
# Default Attributes for the GA
|
||||
from EasyGA.attributes import Attributes
|
||||
|
||||
# Database class
|
||||
from EasyGA.database import sql_database
|
||||
from sqlite3 import Error
|
||||
# from EasyGA.database import sql_database
|
||||
# from sqlite3 import Error
|
||||
|
||||
# Graphing package
|
||||
from EasyGA.database import matplotlib_graph
|
||||
import matplotlib.pyplot as plt
|
||||
# from EasyGA.database import matplotlib_graph
|
||||
# import matplotlib.pyplot as plt
|
||||
|
||||
|
||||
class GA(Attributes):
|
||||
@ -46,7 +46,6 @@ class GA(Attributes):
|
||||
https://github.com/danielwilczak101/EasyGA/wiki
|
||||
"""
|
||||
|
||||
|
||||
def evolve(self: GA, number_of_generations: float = float('inf'), consider_termination: bool = True) -> None:
|
||||
"""
|
||||
Evolves the ga until the ga is no longer active.
|
||||
@ -63,9 +62,12 @@ class GA(Attributes):
|
||||
if self.population is None:
|
||||
self.initialize_population()
|
||||
|
||||
cond1 = lambda: number_of_generations > 0 # Evolve the specified number of generations.
|
||||
cond2 = lambda: not consider_termination # If consider_termination flag is set:
|
||||
cond3 = lambda: cond2() or self.active() # check termination conditions.
|
||||
# Evolve the specified number of generations.
|
||||
def cond1(): return number_of_generations > 0
|
||||
# If consider_termination flag is set:
|
||||
def cond2(): return not consider_termination
|
||||
# check termination conditions.
|
||||
def cond3(): return cond2() or self.active()
|
||||
|
||||
while cond1() and cond3():
|
||||
|
||||
@ -74,12 +76,13 @@ class GA(Attributes):
|
||||
|
||||
# Create the database here to allow the user to change the
|
||||
# database name and structure before running the function.
|
||||
self.database.create_all_tables(self)
|
||||
# self.database.create_all_tables(self)
|
||||
|
||||
# Add the current configuration to the config table
|
||||
self.database.insert_config(self)
|
||||
# self.database.insert_config(self)
|
||||
pass
|
||||
|
||||
# Otherwise evolve the population.
|
||||
# Otherwise evolve the population.
|
||||
else:
|
||||
self.parent_selection_impl()
|
||||
self.crossover_population_impl()
|
||||
@ -93,7 +96,7 @@ class GA(Attributes):
|
||||
self.sort_by_best_fitness()
|
||||
|
||||
# Save the population to the database
|
||||
self.save_population()
|
||||
# self.save_population()
|
||||
|
||||
# Adapt the ga if the generation times the adapt rate
|
||||
# passes through an integer value.
|
||||
@ -101,10 +104,9 @@ class GA(Attributes):
|
||||
if int(adapt_counter) < int(adapt_counter + self.adapt_rate):
|
||||
self.adapt()
|
||||
|
||||
number_of_generations -= 1
|
||||
number_of_generations -= 1
|
||||
self.current_generation += 1
|
||||
|
||||
|
||||
def update_population(self: GA) -> None:
|
||||
"""
|
||||
Updates the population to the new population
|
||||
@ -112,7 +114,6 @@ class GA(Attributes):
|
||||
"""
|
||||
self.population.update()
|
||||
|
||||
|
||||
def reset_run(self: GA) -> None:
|
||||
"""
|
||||
Resets a run by re-initializing the
|
||||
@ -122,7 +123,6 @@ class GA(Attributes):
|
||||
self.current_generation = 0
|
||||
self.run += 1
|
||||
|
||||
|
||||
def adapt(self: GA) -> None:
|
||||
"""Adapts the ga to hopefully get better results."""
|
||||
|
||||
@ -133,7 +133,6 @@ class GA(Attributes):
|
||||
self.set_all_fitness()
|
||||
self.sort_by_best_fitness()
|
||||
|
||||
|
||||
def adapt_probabilities(self: GA) -> None:
|
||||
"""
|
||||
Modifies the parent ratio and mutation rates based on the adapt
|
||||
@ -153,7 +152,7 @@ class GA(Attributes):
|
||||
|
||||
# Difference between best and i-th chromosomes
|
||||
best_chromosome = self.population[0]
|
||||
tol = lambda i: self.dist(best_chromosome, self.population[i])
|
||||
def tol(i): return self.dist(best_chromosome, self.population[i])
|
||||
|
||||
# Too few converged: cross more and mutate less
|
||||
if tol(amount_converged//2) > tol(amount_converged//4)*2:
|
||||
@ -168,13 +167,14 @@ class GA(Attributes):
|
||||
self.max_gene_mutation_rate)
|
||||
|
||||
# Weighted average of x and y
|
||||
average = lambda x, y: weight * x + (1-weight) * y
|
||||
def average(x, y): return weight * x + (1-weight) * y
|
||||
|
||||
# Adjust rates towards the bounds
|
||||
self.selection_probability = average(bounds[0], self.selection_probability)
|
||||
self.chromosome_mutation_rate = average(bounds[1], self.chromosome_mutation_rate)
|
||||
self.gene_mutation_rate = average(bounds[2], self.gene_mutation_rate)
|
||||
|
||||
self.selection_probability = average(
|
||||
bounds[0], self.selection_probability)
|
||||
self.chromosome_mutation_rate = average(
|
||||
bounds[1], self.chromosome_mutation_rate)
|
||||
self.gene_mutation_rate = average(bounds[2], self.gene_mutation_rate)
|
||||
|
||||
def adapt_population(self: GA) -> None:
|
||||
"""
|
||||
@ -201,7 +201,7 @@ class GA(Attributes):
|
||||
self.crossover_individual_impl(
|
||||
self.population[n],
|
||||
parent,
|
||||
weight = -3/4,
|
||||
weight=-3/4,
|
||||
)
|
||||
|
||||
# If negative weights can't be used or division by 0, use positive weight
|
||||
@ -209,7 +209,7 @@ class GA(Attributes):
|
||||
self.crossover_individual_impl(
|
||||
self.population[n],
|
||||
parent,
|
||||
weight = +1/4,
|
||||
weight=+1/4,
|
||||
)
|
||||
|
||||
# Stop if we've filled up an entire population
|
||||
@ -217,20 +217,19 @@ class GA(Attributes):
|
||||
break
|
||||
|
||||
# Replace worst chromosomes with new chromosomes, except for the previous best chromosome
|
||||
min_len = min(len(self.population)-1, len(self.population.next_population))
|
||||
min_len = min(len(self.population)-1,
|
||||
len(self.population.next_population))
|
||||
if min_len > 0:
|
||||
self.population[-min_len:] = self.population.next_population[:min_len]
|
||||
self.population.next_population = []
|
||||
self.population.mating_pool = []
|
||||
|
||||
|
||||
def initialize_population(self: GA) -> None:
|
||||
"""
|
||||
Sets self.population using the chromosome implementation and population size.
|
||||
"""
|
||||
self.population = self.make_population(self.population_impl())
|
||||
|
||||
|
||||
def set_all_fitness(self: GA) -> None:
|
||||
"""
|
||||
Sets the fitness of each chromosome in the population.
|
||||
@ -251,15 +250,14 @@ class GA(Attributes):
|
||||
if chromosome.fitness is None or self.update_fitness:
|
||||
chromosome.fitness = self.fitness_function_impl(chromosome)
|
||||
|
||||
|
||||
def sort_by_best_fitness(
|
||||
self: GA,
|
||||
chromosome_list: Optional[
|
||||
Union[MutableSequence[Chromosome],
|
||||
Iterable[Chromosome]]
|
||||
] = None,
|
||||
in_place: bool = True,
|
||||
) -> MutableSequence[Chromosome]:
|
||||
self: GA,
|
||||
chromosome_list: Optional[
|
||||
Union[MutableSequence[Chromosome],
|
||||
Iterable[Chromosome]]
|
||||
] = None,
|
||||
in_place: bool = True,
|
||||
) -> MutableSequence[Chromosome]:
|
||||
"""
|
||||
Sorts the chromosome list by fitness based on fitness type.
|
||||
1st element has best fitness.
|
||||
@ -314,7 +312,6 @@ class GA(Attributes):
|
||||
else:
|
||||
return sorted(chromosome_list, key=key, reverse=reverse)
|
||||
|
||||
|
||||
def get_chromosome_fitness(self: GA, index: int) -> float:
|
||||
"""
|
||||
Computes the converted fitness of a chromosome at an index.
|
||||
@ -338,7 +335,6 @@ class GA(Attributes):
|
||||
"""
|
||||
return self.convert_fitness(self.population[index].fitness)
|
||||
|
||||
|
||||
def convert_fitness(self: GA, fitness: float) -> float:
|
||||
"""
|
||||
Calculates a modified version of the fitness for various
|
||||
@ -375,23 +371,19 @@ class GA(Attributes):
|
||||
|
||||
return max_fitness - fitness + min_fitness
|
||||
|
||||
|
||||
def print_generation(self: GA) -> None:
|
||||
"""Prints the current generation."""
|
||||
print(f"Current Generation \t: {self.current_generation}")
|
||||
|
||||
|
||||
def print_population(self: GA) -> None:
|
||||
"""Prints the entire population."""
|
||||
print(self.population)
|
||||
|
||||
|
||||
def print_best_chromosome(self: GA) -> None:
|
||||
"""Prints the best chromosome and its fitness."""
|
||||
print(f"Best Chromosome \t: {self.population[0]}")
|
||||
print(f"Best Fitness \t: {self.population[0].fitness}")
|
||||
|
||||
|
||||
def print_worst_chromosome(self: GA) -> None:
|
||||
"""Prints the worst chromosome and its fitness."""
|
||||
print(f"Worst Chromosome \t: {self.population[-1]}")
|
||||
|
||||
@ -6,8 +6,8 @@ from dataclasses import dataclass, field
|
||||
from types import MethodType
|
||||
import random
|
||||
|
||||
import sqlite3
|
||||
import matplotlib.pyplot as plt
|
||||
# import sqlite3
|
||||
# import matplotlib.pyplot as plt
|
||||
|
||||
from EasyGA.structure import Population
|
||||
from EasyGA.structure import Chromosome
|
||||
@ -19,7 +19,7 @@ from EasyGA.parent import Parent
|
||||
from EasyGA.survivor import Survivor
|
||||
from EasyGA.crossover import Crossover
|
||||
from EasyGA.mutation import Mutation
|
||||
from EasyGA.database import sql_database, matplotlib_graph
|
||||
# from EasyGA.database import sql_database, matplotlib_graph
|
||||
|
||||
|
||||
@dataclass
|
||||
@ -29,7 +29,8 @@ class Attributes:
|
||||
Contains default attributes for each attribute.
|
||||
"""
|
||||
|
||||
properties: Dict[str, Any] = field(default_factory=dict, init=False, repr=False, compare=False)
|
||||
properties: Dict[str, Any] = field(
|
||||
default_factory=dict, init=False, repr=False, compare=False)
|
||||
|
||||
run: int = 0
|
||||
|
||||
@ -64,61 +65,73 @@ class Attributes:
|
||||
max_gene_mutation_rate: float = 0.15
|
||||
min_gene_mutation_rate: float = 0.01
|
||||
|
||||
fitness_function_impl: Callable[[Attributes, Chromosome], float] = Fitness.is_it_5
|
||||
make_population: Callable[[Iterable[Iterable[Any]]], Population] = Population
|
||||
fitness_function_impl: Callable[[
|
||||
Attributes, Chromosome], float] = Fitness.is_it_5
|
||||
make_population: Callable[[
|
||||
Iterable[Iterable[Any]]], Population] = Population
|
||||
make_chromosome: Callable[[Iterable[Any]], Chromosome] = Chromosome
|
||||
make_gene: Callable[[Any], Gene] = Gene
|
||||
|
||||
gene_impl: Callable[[Attributes], Any] = field(default_factory=lambda: rand_1_to_10)
|
||||
chromosome_impl: Optional[[Attributes], Iterable[Any]] = field(default_factory=lambda: use_genes)
|
||||
population_impl: Optional[[Attributes], Iterable[Iterable[Any]]] = field(default_factory=lambda: use_chromosomes)
|
||||
gene_impl: Callable[[Attributes], Any] = field(
|
||||
default_factory=lambda: rand_1_to_10)
|
||||
chromosome_impl: Optional[[Attributes], Iterable[Any]] = field(
|
||||
default_factory=lambda: use_genes)
|
||||
population_impl: Optional[[Attributes], Iterable[Iterable[Any]]] = field(
|
||||
default_factory=lambda: use_chromosomes)
|
||||
|
||||
weighted_random: Callable[[Attributes, float], float] = field(default_factory=lambda: simple_linear)
|
||||
dist: Callable[[Attributes, Chromosome, Chromosome], float] = field(default_factory=lambda: dist_fitness)
|
||||
weighted_random: Callable[[Attributes, float], float] = field(
|
||||
default_factory=lambda: simple_linear)
|
||||
dist: Callable[[Attributes, Chromosome, Chromosome],
|
||||
float] = field(default_factory=lambda: dist_fitness)
|
||||
|
||||
parent_selection_impl: Callable[[Attributes], None] = Parent.Rank.tournament
|
||||
crossover_individual_impl: Callable[[Attributes], None] = Crossover.Individual.single_point
|
||||
crossover_population_impl: Callable[[Attributes], None] = Crossover.Population.sequential
|
||||
survivor_selection_impl: Callable[[Attributes], None] = Survivor.fill_in_best
|
||||
mutation_individual_impl: Callable[[Attributes], None] = Mutation.Individual.individual_genes
|
||||
mutation_population_impl: Callable[[Attributes], None] = Mutation.Population.random_avoid_best
|
||||
termination_impl: Callable[[Attributes], None] = Termination.fitness_generation_tolerance
|
||||
parent_selection_impl: Callable[[
|
||||
Attributes], None] = Parent.Rank.tournament
|
||||
crossover_individual_impl: Callable[[
|
||||
Attributes], None] = Crossover.Individual.single_point
|
||||
crossover_population_impl: Callable[[
|
||||
Attributes], None] = Crossover.Population.sequential
|
||||
survivor_selection_impl: Callable[[
|
||||
Attributes], None] = Survivor.fill_in_best
|
||||
mutation_individual_impl: Callable[[
|
||||
Attributes], None] = Mutation.Individual.individual_genes
|
||||
mutation_population_impl: Callable[[
|
||||
Attributes], None] = Mutation.Population.random_avoid_best
|
||||
termination_impl: Callable[[Attributes],
|
||||
None] = Termination.fitness_generation_tolerance
|
||||
|
||||
database: Database = field(default_factory=sql_database.SQL_Database)
|
||||
database_name: str = 'database.db'
|
||||
sql_create_data_structure: str = """
|
||||
CREATE TABLE IF NOT EXISTS data (
|
||||
id INTEGER PRIMARY KEY,
|
||||
config_id INTEGER DEFAULT NULL,
|
||||
generation INTEGER NOT NULL,
|
||||
fitness REAL,
|
||||
chromosome TEXT
|
||||
);
|
||||
"""
|
||||
|
||||
graph: Callable[[Database], Graph] = matplotlib_graph.Matplotlib_Graph
|
||||
#database: Database = field(default_factory=sql_database.SQL_Database)
|
||||
#database_name: str = 'database.db'
|
||||
# sql_create_data_structure: str = """
|
||||
# CREATE TABLE IF NOT EXISTS data (
|
||||
# id INTEGER PRIMARY KEY,
|
||||
# config_id INTEGER DEFAULT NULL,
|
||||
# generation INTEGER NOT NULL,
|
||||
# fitness REAL,
|
||||
# chromosome TEXT
|
||||
# );
|
||||
# """
|
||||
|
||||
# graph: Callable[[Database], Graph] = matplotlib_graph.Matplotlib_Graph
|
||||
|
||||
#============================#
|
||||
# Built-in database methods: #
|
||||
#============================#
|
||||
|
||||
# def save_population(self: Attributes) -> None:
|
||||
# """Saves the current population to the database."""
|
||||
# self.database.insert_current_population(self)
|
||||
|
||||
def save_population(self: Attributes) -> None:
|
||||
"""Saves the current population to the database."""
|
||||
self.database.insert_current_population(self)
|
||||
# def save_chromosome(self: Attributes, chromosome: Chromosome) -> None:
|
||||
# """
|
||||
# Saves a chromosome to the database.#
|
||||
|
||||
|
||||
def save_chromosome(self: Attributes, chromosome: Chromosome) -> None:
|
||||
"""
|
||||
Saves a chromosome to the database.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
chromosome : Chromosome
|
||||
The chromosome to be saved.
|
||||
"""
|
||||
self.database.insert_current_chromosome(self.current_generation, chromosome)
|
||||
# Parameters
|
||||
# ----------
|
||||
# chromosome : Chromosome
|
||||
# The chromosome to be saved.
|
||||
# """
|
||||
# self.database.insert_current_chromosome(
|
||||
# was self.current_generation, chromosome)
|
||||
|
||||
|
||||
def rand_1_to_10(self: Attributes) -> int:
|
||||
@ -231,6 +244,7 @@ def get_method(name: str) -> Callable[[Attributes], Callable[..., Any]]:
|
||||
getter(ga)(...) -> Any
|
||||
The getter property, taking in an object and returning the method.
|
||||
"""
|
||||
|
||||
def getter(self: Attributes) -> Callable[..., Any]:
|
||||
return self.properties[name]
|
||||
return getter
|
||||
@ -250,6 +264,7 @@ def set_method(name: str) -> Callable[[Attributes, Optional[Callable[..., Any]]]
|
||||
setter(ga, method)
|
||||
The setter property, taking in an object and returning nothing.
|
||||
"""
|
||||
|
||||
def setter(self: Attributes, method: Optional[Callable[..., Any]]) -> None:
|
||||
if method is None:
|
||||
pass
|
||||
@ -318,6 +333,7 @@ def get_attr(name: str) -> Callable[[Attributes], Any]:
|
||||
getter(ga) -> Any
|
||||
A getter method which returns an attribute.
|
||||
"""
|
||||
|
||||
def getter(self: Attributes) -> Any:
|
||||
return self.properties[name]
|
||||
return getter
|
||||
@ -342,6 +358,7 @@ def set_attr(name: str, check: Callable[[Any], bool], error: str) -> Callable[[A
|
||||
Raises ValueError(error)
|
||||
A setter method which saves to an attribute.
|
||||
"""
|
||||
|
||||
def setter(self: Attributes, value: Any) -> Any:
|
||||
if check(value):
|
||||
self.properties[name] = value
|
||||
@ -356,7 +373,8 @@ for name in static_checks:
|
||||
name,
|
||||
property(
|
||||
get_attr(name),
|
||||
set_attr(name, static_checks[name]["check"], static_checks[name]["error"]),
|
||||
set_attr(name, static_checks[name]
|
||||
["check"], static_checks[name]["error"]),
|
||||
)
|
||||
)
|
||||
|
||||
@ -385,7 +403,8 @@ def set_max_chromosome_mutation_rate(self: Attributes, value: Optional[float]) -
|
||||
|
||||
# Raise error
|
||||
else:
|
||||
raise ValueError("Max chromosome mutation rate must be between 0 and 1")
|
||||
raise ValueError(
|
||||
"Max chromosome mutation rate must be between 0 and 1")
|
||||
|
||||
|
||||
def get_min_chromosome_mutation_rate(self: Attributes) -> float:
|
||||
@ -407,7 +426,8 @@ def set_min_chromosome_mutation_rate(self: Attributes, value: Optional[float]) -
|
||||
|
||||
# Raise error
|
||||
else:
|
||||
raise ValueError("Min chromosome mutation rate must be between 0 and 1")
|
||||
raise ValueError(
|
||||
"Min chromosome mutation rate must be between 0 and 1")
|
||||
|
||||
|
||||
def get_database_name(self: Attributes) -> str:
|
||||
@ -435,8 +455,10 @@ def get_active(self: Attributes) -> Callable[[Attributes], None]:
|
||||
return self.termination_impl
|
||||
|
||||
|
||||
Attributes.max_chromosome_mutation_rate = property(get_max_chromosome_mutation_rate, set_max_chromosome_mutation_rate)
|
||||
Attributes.min_chromosome_mutation_rate = property(get_min_chromosome_mutation_rate, set_min_chromosome_mutation_rate)
|
||||
Attributes.max_chromosome_mutation_rate = property(
|
||||
get_max_chromosome_mutation_rate, set_max_chromosome_mutation_rate)
|
||||
Attributes.min_chromosome_mutation_rate = property(
|
||||
get_min_chromosome_mutation_rate, set_min_chromosome_mutation_rate)
|
||||
Attributes.database_name = property(get_database_name, set_database_name)
|
||||
Attributes.graph = property(get_graph, set_graph)
|
||||
Attributes.active = property(get_active)
|
||||
|
||||
Reference in New Issue
Block a user