Added most list functionality

This commit is contained in:
SimpleArt
2020-12-29 19:36:46 -05:00
parent 6aa4b8b82f
commit 1e2bb1f542
2 changed files with 155 additions and 45 deletions

View File

@ -1,4 +1,5 @@
from structure import Gene as make_gene from structure import Gene as make_gene
from itertools import chain
def to_gene(gene): def to_gene(gene):
"""Converts the input to a gene if it isn't already one.""" """Converts the input to a gene if it isn't already one."""
@ -9,7 +10,7 @@ def to_gene(gene):
return make_gene(gene) return make_gene(gene)
class Chromosome: class Chromosome():
def __init__(self, gene_list): def __init__(self, gene_list):
"""Initialize the chromosome with fitness value of None, and a """Initialize the chromosome with fitness value of None, and a
@ -19,18 +20,6 @@ class Chromosome:
self.fitness = None self.fitness = None
def add_gene(self, gene, index = None):
"""Add a gene to the chromosome at the specified index, defaulted to end of the chromosome"""
if index is None:
index = len(self)
self.gene_list.insert(index, to_gene(gene))
def remove_gene(self, index):
"""Removes the gene at the given index"""
return self.gene_list.pop(index)
@property @property
def gene_value_list(self): def gene_value_list(self):
"""Returns a list of gene values""" """Returns a list of gene values"""
@ -43,6 +32,11 @@ class Chromosome:
return (gene.value for gene in self) return (gene.value for gene in self)
#==================================================#
# Magic-Dunder Methods replicating list structure. #
#==================================================#
def __iter__(self): def __iter__(self):
""" """
Allows the user to use Allows the user to use
@ -109,16 +103,50 @@ class Chromosome:
return (to_gene(gene) in self.gene_list) return (to_gene(gene) in self.gene_list)
def index_of(self, gene, guess = None): def __eq__(self, chromosome):
"""Returns self == chromosome, True if all genes match."""
return self.gene_list == chromosome.gene_list
def __add__(self, chromosome):
"""Return self + chromosome, a chromosome made by concatenating the genes."""
return Chromosome(chain(self, chromosome))
def __iadd__(self, chromosome):
"""Implement self += chromosome by concatenating the new genes."""
self.gene_list += (to_gene(gene) for gene in chromosome)
def append(self, gene):
"""Append gene to the end of the chromosome."""
self.gene_list.append(to_gene(gene))
def clear(self):
"""Remove all genes from chromosome."""
self.gene_list = []
def copy(self):
"""Return a copy of the chromosome."""
return Chromosome(self)
def count(self, gene):
"""Return number of occurrences of the gene in the chromosome."""
return self.gene_list.count(to_gene(gene))
def index(self, gene, guess = None):
""" """
Allows the user to use Allows the user to use
index = chromosome.index_of(gene) index = chromosome.index(gene)
index = chromosome.index_of(gene, guess) index = chromosome.index(gene, guess)
to find the index of a gene in the chromosome. 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 IndexError exception if the gene If no guess is given, it finds the index of the first match.
is not in the chromosome. A guess may be used to find If a guess is given, it finds index of the nearest match.
the index quicker.
""" """
# Cast to gene object # Cast to gene object
@ -145,18 +173,28 @@ class Chromosome:
return (guess+i) % len(self) return (guess+i) % len(self)
# Gene not found # Gene not found
else: raise ValueError("No such gene in the chromosome found")
raise IndexError("No such gene in the chromosome found")
def __eq__(self, chromosome): def insert(self, index, gene):
"""Insert gene so that self[index] == gene."""
self.gene_list.insert(index, to_gene(gene))
def pop(self, index = -1):
"""Remove and return gene at index (default last).
Raises IndexError if chromosome is empty or index is out of range.
""" """
Allows the user to use return self.gene_list.pop(index)
chromosome_1 == chromosome_2
chromosome_1 != chromosome_2
to compare two chromosomes based on their genes. def remove(self, gene):
"""Remove first occurrence of gene.
Raises ValueError if the gene in not present.
""" """
return self.gene_list == chromosome.gene_list self.gene_list.remove(to_gene(gene))
def __repr__(self): def __repr__(self):

View File

@ -1,4 +1,5 @@
from structure import Chromosome as make_chromosome from structure import Chromosome as make_chromosome
from itertools import chain
def to_chromosome(chromosome): def to_chromosome(chromosome):
"""Converts the input to a chromosome if it isn't already one.""" """Converts the input to a chromosome if it isn't already one."""
@ -66,11 +67,6 @@ class Population:
] + self.next_population ] + self.next_population
def sort_by_best_fitness(self, ga):
"""Sorts the population by fitness"""
ga.sort_by_best_fitness(self.chromosome_list, in_place = True)
def add_chromosome(self, chromosome, index = None): def add_chromosome(self, chromosome, index = None):
"""Adds a chromosome to the population at the input index, """Adds a chromosome to the population at the input index,
defaulted to the end of the chromosome set""" defaulted to the end of the chromosome set"""
@ -95,6 +91,11 @@ class Population:
self.add_parent(self[index]) self.add_parent(self[index])
#==================================================#
# Magic-Dunder Methods replicating list structure. #
#==================================================#
def __iter__(self): def __iter__(self):
""" """
Allows the user to use Allows the user to use
@ -127,6 +128,15 @@ class Population:
self.chromosome_list[index] = to_chromosome(chromosome) self.chromosome_list[index] = to_chromosome(chromosome)
def __delitem__(self, index):
"""
Allows the user to use
del population[index]
to delete a chromosome at the specified index.
"""
del self.chromosome_list[index]
def __len__(self): def __len__(self):
""" """
Allows the user to use Allows the user to use
@ -145,30 +155,64 @@ class Population:
return (to_chromosome(chromosome) in self.chromosome_list) return (to_chromosome(chromosome) in self.chromosome_list)
def index_of(self, chromosome, guess = None): def __eq__(self, population):
"""Returns self == population, True if all chromosomes match."""
return self.chromosome_list == population.chromosome_list
def __add__(self, population):
"""Returns self + population, a population made by concatenating the chromosomes."""
return Population(chain(self, population))
def __iadd__(self, population):
"""Implement self += population by concatenating the new chromosomes."""
self.chromosome_list += (to_chromosome(chromosome) for chromosome in population)
def append(self, chromosome):
"""Append chromosome to the end of the population."""
self.chromosome_list.append(to_chromosome(chromosome))
def clear(self):
"""Remove all chromosomes from the population."""
self.chromosome_list = []
def copy(self):
"""Return a copy of the population."""
return Population(self)
def count(self, chromosome):
"""Return number of occurrences of the chromosome in the population."""
return self.chromosome_list.count(to_chromosome(chromosome))
def index(self, chromosome, guess = None):
""" """
Allows the user to use Allows the user to use
index = population.index_of(chromosome) index = population.index(chromosome)
index = population.index_of(chromosome, guess) index = population.index(chromosome, guess)
to find the index of a chromosome in the population. 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 IndexError exception if the chromosome If no guess is given, it finds the index of the first match.
is not in the population. A guess may be used to find the If a guess is given, it finds index of the nearest match.
index quicker.
""" """
chromosome = to_chromosome(chromosome) chromosome = to_chromosome(chromosome)
# Use built-in method # Use built-in method
if guess is None: if guess is None:
return self.chromosome_list.index(gene) return self.chromosome_list.index(chromosome)
# Use symmetric mod # Use symmetric mod
guess %= len(self) guess %= len(self)
if guess >= len(self)//2: if guess >= len(self)//2:
guess -= len(self) guess -= len(self)
# Search outwards for the gene # Search outwards for the chromosome
for i in range(len(self)//2): for i in range(len(self)//2):
# Search to the left # Search to the left
@ -179,9 +223,37 @@ class Population:
elif chromosome == self[guess+i]: elif chromosome == self[guess+i]:
return (guess+i) % len(self) return (guess+i) % len(self)
# Gene not found # Chromosome not found
else: raise IndexError("No such chromosome in the population found")
raise IndexError("No such chromosome in the population found")
def insert(self, index, chromosome):
"""Insert chromosome so that self[index] == chromsome."""
self.chromosome_list.insert(index, to_chromosome(chromosome))
def pop(self, index = -1):
"""Remove and return chromosome at index (default last).
Raises IndexError if population is empty or index is out of range.
"""
return self.chromosome_list.pop(index)
def remove(self, chromosome):
"""Remove first occurrence of chromosome.
Raises ValueError if the chromosome is not present.
"""
self.chromosome_list.remove(to_chromosome(chromosome))
def sort(self, *, key = lambda chromosome: chromosome.fitness, reverse):
"""Sorts the population."""
self.chromosome_list.sort(
key = key,
reverse = reverse
)
def __repr__(self): def __repr__(self):