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 for member in pool]) def calculate_member_fitness(member): return sum(member) def recalculate_fitneses(pool): return [(calculate_member_fitness(member), member) 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] 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, mutate_genes(member, 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, father) return [(mother, daughter_genes), (father, 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)) 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 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()