HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

Genetic algorithm in Python that plots its evolution

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
evolutionplotsgeneticalgorithmitsthatpython

Problem

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) = 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)

Solution


  • random.seed is a bug: add parentheses to actually call the function.



  • Use collections.namedtuple so you can write member.fitness instead of the less readable member[0]



  • copy.deepcopy should not be necessary in a functional approach



  • Keeping the genes in a tuple instead of a list would make the member tuple fully immutable, in line with the functional approach. This eliminates the need to deep-copy objects as you can safely copy just references.



After these changes create_member becomes like this:

import collections
Member = collections.namedtuple("Member", "fitness genes")

def create_member(genes):
    genes = tuple(genes)
    return Member(sum(genes), genes)


In some places you create members without calling create_member. Be sure to change them, for example:

def mutate_member(member, rate=1):
    return create_member(mutate_genes(member.genes, rate))


  • With immutable members there is no need to recalculate fitnesses. You can delete such functions.

Code Snippets

import collections
Member = collections.namedtuple("Member", "fitness genes")

def create_member(genes):
    genes = tuple(genes)
    return Member(sum(genes), genes)
def mutate_member(member, rate=1):
    return create_member(mutate_genes(member.genes, rate))

Context

StackExchange Code Review Q#41004, answer score: 5

Revisions (0)

No revisions yet.