Files
EasyGA/EasyGA/structure/chromosome.py
danielwilczak101 7e5c384872 Directory
2021-01-27 03:03:30 -06:00

225 lines
5.9 KiB
Python

from structure import Gene as make_gene
from itertools import chain
def to_gene(gene):
"""Converts the input to a gene if it isn't already one."""
if isinstance(gene, make_gene):
return gene
else:
return make_gene(gene)
class Chromosome():
def __init__(self, gene_list):
"""Initialize the chromosome with fitness value of None, and a
set of genes dependent on user-passed parameter."""
self.gene_list = [make_gene(gene) for gene in gene_list]
self.fitness = None
@property
def gene_value_list(self):
"""Returns a list of gene values"""
return [gene.value for gene in self]
@property
def gene_value_iter(self):
"""Returns an iterable of gene values"""
return (gene.value for gene in self)
#==================================================#
# Magic-Dunder Methods replicating list structure. #
#==================================================#
def __iter__(self):
"""
Allows the user to use
iter(chromosome)
list(chromosome) == chromosome.gene_list
tuple(chromosome)
for gene in chromosome
to loop through the chromosome.
Note: using list(chromosome) creates a copy of
the gene_list. Altering this will not
alter the original gene_list.
"""
return iter(self.gene_list)
def __getitem__(self, index):
"""
Allows the user to use
gene = chromosome[index]
to get the indexed gene.
"""
return self.gene_list[index]
def __setitem__(self, index, gene):
"""
Allows the user to use
chromosome[index] = gene
to set the indexed gene.
"""
# Single gene
if isinstance(index, int):
self.gene_list[index] = to_gene(gene)
# Multiple genes
else:
self.gene_list[index] = [to_gene(item) for item in 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
size = len(chromosome)
to get the length of the chromosome.
"""
return len(self.gene_list)
def __contains__(self, gene):
"""
Allows the user to use
if gene in chromosome
to check if a gene is in the chromosome.
"""
return (to_gene(gene) in self.gene_list)
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
index = chromosome.index(gene)
index = chromosome.index(gene, guess)
to find the index of a gene in the chromosome.
If no guess is given, it finds the index of the first match.
If a guess is given, it finds index of the nearest match.
"""
# Cast to gene object
gene = to_gene(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
raise ValueError("No such gene in the chromosome found")
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.
"""
return self.gene_list.pop(index)
def remove(self, gene):
"""Remove first occurrence of gene.
Raises ValueError if the gene in not present.
"""
self.gene_list.remove(to_gene(gene))
def __repr__(self):
"""
Allows the user to use
chromosome_string = repr(chromosome)
chromosome_data = eval(chromosome_string)
chromosome = ga.make_chromosome(chromosome_data)
to get a backend representation of the chromosome
which can be evaluated directly as code to create
the chromosome.
"""
return repr(self.gene_list)
def __str__(self):
"""
Allows the user to use
str(chromosome)
print(chromosome)
to get a frontend representation of the chromosome.
"""
return ''.join(str(gene) for gene in self)