Proper repr implementations, improved index_of method, and improved initialization

- Repr now supports reversal using eval i.e. obj == eval(repr(obj)) for any data object. This assumes a GA object called ga.
- Index of now supports local searching if an estimate of the index is given.
- Initialization of data objects is now easier and works for any iterable input e.g. ga.make_chromosome
This commit is contained in:
SimpleArt
2020-12-20 13:59:12 -05:00
parent 6861688400
commit a15803e7ef
3 changed files with 105 additions and 26 deletions

View File

@ -1,4 +1,4 @@
from copy import deepcopy
from structure import Gene as make_gene
class Chromosome:
@ -6,7 +6,7 @@ class Chromosome:
"""Initialize the chromosome with fitness value of None, and a
set of genes dependent on user-passed parameter."""
self.gene_list = [deepcopy(gene) for gene in gene_list]
self.gene_list = [make_gene(gene) for gene in gene_list]
self.fitness = None
@ -70,6 +70,15 @@ class Chromosome:
self.gene_list[index] = gene
def __delitem__(self, index):
"""
Allows the user to use
del chromosome[index]
to delete a gene at the specified index.
"""
del self.gene_list[index]
def __len__(self):
"""
Allows the user to use
@ -88,16 +97,41 @@ class Chromosome:
return (searched_gene in self.gene_list)
def index_of(self, searched_gene):
def index_of(self, gene, guess = None):
"""
Allows the user to use
index = chromosome.index_of(gene)
index = chromosome.index_of(gene, guess)
to find the index of a gene in the chromosome.
Be sure to check if the chromosome contains the gene
first, or to catch an exception if the gene is not
in the chromosome.
first, or to catch an IndexError exception if the gene
is not in the chromosome. A guess may be used to find
the index quicker.
"""
return self.gene_list.index(searched_gene)
# Use built-in method
if guess is None:
return self.gene_list.index(gene)
# Use symmetric mod
guess %= len(self)
if guess >= len(self)//2:
guess -= len(self)
# Search outwards for the gene
for i in range(1+len(self)//2):
# Search to the left
if gene == self[guess-i]:
return (guess-i) % len(self)
# Search to the right
elif gene == self[guess+i]:
return (guess+i) % len(self)
# Gene not found
else:
raise IndexError("No such gene in the chromosome found")
def __eq__(self, chromosome):
@ -107,17 +141,19 @@ class Chromosome:
chromosome_1 != chromosome_2
to compare two chromosomes based on their genes.
"""
return all(gene_1 == gene_2 for gene_1, gene_2 in zip(self, chromosome))
return self.gene_list == chromosome.gene_list
def __repr__(self):
"""
Allows the user to use
repr(chromosome)
to get a backend representation of the chromosome.
chromosome_string = repr(chromosome)
chromosome = eval(chromosome_string)
to get a backend representation of the chromosome
which can be evaluated directly as code to create
the chromosome.
"""
return ', '.join(repr(gene) for gene in self)
return f"EasyGA.make_chromosome({repr(self.gene_list)})"
def __str__(self):

View File

@ -3,8 +3,15 @@ from copy import deepcopy
class Gene:
def __init__(self, value):
"""Initialize a gene with fitness of value None and the input value."""
self.value = deepcopy(value)
"""Initialize a gene with the input value."""
# Copy another gene
try:
self.value = deepcopy(value.value)
# Otherwise copy the given value
except:
self.value = deepcopy(value)
def __eq__(self, other_gene):
@ -20,10 +27,11 @@ class Gene:
def __repr__(self):
"""
Allows the user to use
repr(gene)
gene_string = repr(gene)
gene = eval(gene_string)
to get a backend representation of the gene.
"""
return str(self.value)
return f"EasyGA.make_gene({repr(self.value)})"
def __str__(self):

View File

@ -1,13 +1,12 @@
from copy import deepcopy
from structure import Chromosome as make_chromosome
class Population:
def __init__(self, chromosome_list):
"""Initialize the population with fitness of value None, and a
set of chromosomes dependant on user-passed parameter."""
"""Initialize the population with a collection
of chromosomes dependant on user-passed parameter."""
self.chromosome_list = [deepcopy(chromosome) for chromosome in chromosome_list]
self.fitness = None
self.chromosome_list = [make_chromosome(chromosome) for chromosome in chromosome_list]
self.mating_pool = []
self.next_population = []
@ -135,25 +134,61 @@ class Population:
return (searched_chromosome in self.chromosome_list)
def index_of(self, searched_chromosome):
def index_of(self, chromosome, guess = None):
"""
Allows the user to use
index = population.index_of(chromosome)
index = population.index_of(chromosome, guess)
to find the index of a chromosome in the population.
Be sure to check if the population contains the chromosome
first, or to catch an exception if the chromosome is not
in the population.
first, or to catch an IndexError exception if the chromosome
is not in the population. A guess may be used to find the
index quicker.
"""
return self.chromosome_list.index(searched_chromosome)
# Use built-in method
if guess is None:
return self.chromosome_list.index(gene)
# Use symmetric mod
guess %= len(self)
if guess >= len(self)//2:
guess -= len(self)
# Search outwards for the gene
for i in range(len(self)//2):
# Search to the left
if chromosome == self[guess-i]:
return (guess-i) % len(self)
# Search to the right
elif chromosome == self[guess+i]:
return (guess+i) % len(self)
# Gene not found
else:
raise IndexError("No such chromosome in the population found")
def __repr__(self):
"""
Allows the user to use
repr(population)
population_string = repr(population)
population = eval(population_string)
to get a backend representation of the population
which can be evaluated directly as code to create
the population.
"""
return "EasyGA.make_population({repr(self.chromosome_list)})"
def __str__(self):
"""
Allows the user to use
str(population)
print(population)
to get a backend representation of the population.
to get a frontend representation of the population.
"""
return ''.join(
f'Chromosome - {index} {chromosome} / Fitness = {chromosome.fitness}\n'