-
Notifications
You must be signed in to change notification settings - Fork 0
/
ga.py
121 lines (93 loc) · 3.5 KB
/
ga.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import random
from copy import copy as _copy
from chromosome import Chromosome
# Gene mixing (every gene) type breeding
class SwapBreeder(object):
def __init__(self, crossover_rate = 0.5):
self.crossover_rate = crossover_rate
def Breed(self, parent1, parent2):
child1 = []
child2 = []
for i in range(len(parent1)):
if random.random() < self.crossover_rate:
child1.append(parent1[i])
child2.append(parent2[i])
else:
child1.append(parent2[i])
child2.append(parent1[i])
return child1, child2
# "Crossover" type chromosome breeding
class CrossoverBreeder(object):
def __init__(self, crossover_rate = 0.7):
self.crossover_rate = crossover_rate
def Breed(self, mom, dad):
# Shall we do crossover?
if random.random() > self.crossover_rate and mom == dad:
return mom, dad
# Pick crossover point
i = random.randrange(0, len(mom))
child1 = mom[:i]
child1.extend(dad[i:])
child2 = dad[:i]
child2.extend(dad[i:])
return Chromosome(child1), Chromosome(child2)
# Mutation rate is suggested to between 0.05 and 0.2 (from ai-junki.com) for
# real number alleles
class RealGeneticAlg(SwapBreeder):
def __init__(self, crossover_rate = 0.5, mutation_rate = 0.15,
perturbation_bounds = (0.05, 0.1), elite = 4):
super(RealGeneticAlg, self).__init__(crossover_rate)
self.mutation_rate = mutation_rate
self.perturbation_bounds = perturbation_bounds
self.n_elite = elite
def NewPopulation(self, old_population):
old_population = sorted(old_population)
new_population = []
# Do some elitism
new_population.extend(old_population[-self.n_elite:])
# Mark these old best performers as such
for c in new_population:
c.is_top_performer = True
# Build up a new population
while len(new_population) < len(old_population):
# Select two parents
mom = RouletteSelect(old_population)
dad = RouletteSelect(old_population)
# Breed parents, mutate and add both children to new population
new_population.extend(map(self.Mutate,self.Breed(mom, dad)))
return new_population
def Mutate(self, chromosome):
for i in range(len(chromosome)):
chromosome[i] += random.choice((1, -1)) * \
random.random()*self.perturbation_bounds[1]
#ClampRandom(*self.perturbation_bounds)
return chromosome
# Based off code at ai-junki.com
def RouletteSelect(population):
total = sum([x.score for x in population])
pie_slice = random.random() * total
last = 0
for chromosome in population:
last += chromosome.score
if last >= pie_slice:
return chromosome
def MyRouletteSelect(population):
n = random.random()
last = 0
total = float(sum([x.score for x in population]))
if total != 0:
for chromosome in sorted(population, key = lambda x: x.score):
score = chromosome.score/total
if last < n and n <= (last + score):
return chromosome
last += score
else:
print("Huston, we have a problem!")
return random.choice(population)
def ClampRandom(low, high):
n = random.random()
if n > high:
n = high
if n < low:
n = low
return n