patternpythonMinor
Genetic algorithm in Python that plots its evolution
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)
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.seedis a bug: add parentheses to actually call the function.
- Use
collections.namedtupleso you can writemember.fitnessinstead of the less readablemember[0]
copy.deepcopyshould 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.