From a15803e7efe4ebbd7378fd831aebd06bdfd4d67d Mon Sep 17 00:00:00 2001 From: SimpleArt <71458112+SimpleArt@users.noreply.github.com> Date: Sun, 20 Dec 2020 13:59:12 -0500 Subject: [PATCH] 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 --- src/structure/chromosome.py | 58 ++++++++++++++++++++++++++++++------- src/structure/gene.py | 16 +++++++--- src/structure/population.py | 57 +++++++++++++++++++++++++++++------- 3 files changed, 105 insertions(+), 26 deletions(-) diff --git a/src/structure/chromosome.py b/src/structure/chromosome.py index f38e0ce..3502542 100644 --- a/src/structure/chromosome.py +++ b/src/structure/chromosome.py @@ -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): diff --git a/src/structure/gene.py b/src/structure/gene.py index 2b797c0..6262230 100644 --- a/src/structure/gene.py +++ b/src/structure/gene.py @@ -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): diff --git a/src/structure/population.py b/src/structure/population.py index a89d5b1..ca1db32 100644 --- a/src/structure/population.py +++ b/src/structure/population.py @@ -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'