After programming in Haskell for a while, I've gotten attached to a functional style. This is clearly evident in the code for my genetic algorithm.
Could you provide me with some hints as to how I can make this code more pythonic? By that, I mean provide some method of organisation rather than throwing a bunch of functions around. Any other recommendations are also welcome.
import copy
import matplotlib.pyplot as pyplot
import random
def create_member(genes):
return (sum(genes), genes)
def shuffle(pool):
pool_shuffled = copy.deepcopy(pool)
random.shuffle(pool_shuffled)
return pool_shuffled
def calculate_pool_fitness(pool):
return sum([member[0] for member in pool])
def calculate_member_fitness(member):
return sum(member[1])
def recalculate_fitneses(pool):
return [(calculate_member_fitness(member), member[1]) for member in pool]
def select_members_roulette(pool, count):
selection = []
while len(selection) < count:
member = select_member_roulette(pool)
selection.append(copy.deepcopy(pool[member]))
return selection
def select_member_roulette(pool):
drop = random.randint(0, calculate_pool_fitness(pool))
total_fitness = 0
for member in range(0, len(pool)):
total_fitness += pool[member][0]
if total_fitness >= drop:
return member
def mutate_gene(gene, rate=1):
return 1 - gene if random.random() <= rate else gene
def mutate_genes(genes, rate=1):
return [mutate_gene(gene, rate) for gene in genes]
def mutate_member(member, rate=1):
return member[0], mutate_genes(member[1], rate)
def mutate_pool(pool, rate=1):
return [mutate_member(member, rate) for member in pool]
def create_random_gene():
return random.choice([0, 1])
def create_random_genes(size):
return [create_random_gene() for _ in range(size)]
def crossover_genes(mother, father, rate=1):
if random.random() <= rate:
split = random.randint(1, len(mother))
daughter = mother[:split] + father[split:]
son = father[:split] + mother[split:]
else:
daughter = copy.deepcopy(mother)
son = copy.deepcopy(father)
return daughter, son
def crossover_members(mother, father, rate=1):
daughter_genes, son_genes = crossover_genes(mother[1], father[1])
return [(mother[0], daughter_genes), (father[0], son_genes)]
def crossover_pool(pool, rate=1):
children = []
# select every two elements for crossover
for mother, father in zip(pool[::2], pool[1::2]):
children.extend(crossover_members(mother, father, rate))
return children
def generate_pool(size, gene_size):
pool = []
for member in range(0, size):
genes = create_random_genes(gene_size)
pool.append(create_member(genes))
return pool
def evolve(pool, rate_crossover=0.9, rate_mutation=0.01):
successors = copy.deepcopy(pool)
# perform roulette selection whilst keeping best member
member_alpha = copy.deepcopy(max(successors, key=lambda member: member[0]))
successors = select_members_roulette(pool, len(pool) - 1)
successors.append(member_alpha)
successors = shuffle(successors)
successors = crossover_pool(successors, rate_crossover)
successors = mutate_pool(successors, rate_mutation)
successors = recalculate_fitneses(successors)
return successors
def main():
random.seed
pyplot.figure(figsize=(14, 8), dpi=400)
axgraph=pyplot.subplot(111)
pool_size = 50
gene_size = 50
generations = 100
pool = generate_pool(pool_size, gene_size)
for generation in range(0, generations):
pool = evolve(pool)
axgraph.scatter(generation, sum([member[0] for member in pool]))
pyplot.grid(True)
pyplot.axis([0, generations, 0, pool_size * gene_size])
pyplot.savefig('genetic_algorithms.png')
main()
if __name__ == '__main__':
main()