-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgeneration.py
More file actions
168 lines (129 loc) · 5.41 KB
/
Copy pathgeneration.py
File metadata and controls
168 lines (129 loc) · 5.41 KB
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
import numpy as np
from multiprocess import Pool
# Boundary values for genes
GENE_MAX = 10000
GENE_MIN = -10000
class Generation(object):
def __init__(
self,
gen_size,
num_genes,
elitism_thresh,
crossover_thresh,
mutation_thresh,
fitness_func,
num_jobs=None):
self.gen_size = gen_size
self.num_genes = num_genes
self.elitism_thresh = elitism_thresh
self.crossover_thresh = crossover_thresh
self.mutation_thresh = mutation_thresh
self.fitness_func = fitness_func
self._num_jobs = num_jobs
self._organisms = np.random.rand(gen_size, num_genes)
self._fitnesses = np.zeros(gen_size)
def evaluate_fitness(self):
"""
Calculates fitness of all organisms in the generation and sorts by most
fit.
"""
pool = Pool(processes=self._num_jobs)
self._fitnesses = np.array(pool.map(self.fitness_func, self._organisms))
pool.close()
pool.join()
self._sort() # Make sure to sort at the end for fitness
def get_zeroth(self):
"""
Returns first organism and fitness.
"""
return self._organisms[0], self._fitnesses[0]
def evolve(self):
"""
Method to evolve the generation of organisms.
"""
# Fill a percentage of the next generation with elite organisms
num_organisms_created = int(round(self.gen_size * self.elitism_thresh))
buf = self._organisms[:num_organisms_created].tolist()
# Create rest of organisms with crossovers and mutations
while (num_organisms_created < self.gen_size):
# Randomly decide if the next organisms should be created
# from a crossover_thresh
organisms_to_create = self.gen_size - num_organisms_created
if np.random.rand() <= self.crossover_thresh and organisms_to_create >= 2:
# Create two child organisms from tournament-selected parents
(parent_1, parent_2) = self._select_parents()
children = self._crossover(parent_1, parent_2)
# Random chance to mutate either child
for child in children:
if np.random.rand() <= self.mutation_thresh:
buf.append(self._mutate(child))
else:
buf.append(self._mutate(child))
num_organisms_created += 2
# Directly move a past organism to the next generation with
# a chance at mutation
else:
curr_organism = self._organisms[num_organisms_created]
if np.random.rand() <= self.mutation_thresh:
buf.append(self._mutate(curr_organism))
else:
buf.append(curr_organism)
num_organisms_created += 1
# Sort current generation by fitness
self._organisms = np.asarray(buf)
def _sort(self):
"""
Sorts organisms by fitness.
"""
order = self._fitnesses.argsort()
self._organisms = self._organisms[order]
self._fitnesses = self._fitnesses[order]
def _tournament_selection(self):
"""
A helper method used to select a random organism from the
generation using a tournament selection algorithm.
"""
# First, get some random indexes
choices_idx = np.random.choice(self.gen_size, 5)
# From those indexes, get the fitnesses and get the choice index of the
# most fit
min_idx = np.argmin(self._fitnesses[choices_idx])
# Get the subset of organisms from the random sample. Then, use the
# min_idx to get the most fit organism in the sample
return self._organisms[choices_idx][min_idx]
def _select_parents(self):
"""
A helper method used to select two parents from the generation using a
tournament selection algorithm.
"""
return (self._tournament_selection(), self._tournament_selection())
def _crossover(self, organism_1, organism_2):
"""
Mixes the two specified organisms, returning two new organisms
that are a result of a crossover of the two original organisms.
organism_1, organism_2: organisms to crossover
return: two organisms that are crossed over
"""
org_1 = organism_1[:]
org_2 = organism_2[:]
# Define a random pivot point around which the crossover will occur
pivot = np.random.randint(0, self.num_genes-1)
# Create the new crossovered genes and organism
right_genes = org_2[:pivot].copy()
org_2[:pivot], org_1[:pivot] = org_1[:pivot], right_genes
return org_1, org_2
def _mutate(self, organism):
"""
Mutates a single random gene of the specified organism.
"""
# Don't modify existing organism
_organism = organism[:]
# Select a random gene and multiply it with a random value
index_to_mutate = np.random.randint(0, len(_organism) - 1)
_organism[index_to_mutate] *= np.random.uniform(0.5, 2)
# Clip and round all genes
_organism[index_to_mutate] = np.clip(_organism[index_to_mutate],
GENE_MIN, GENE_MAX)
_organism = [np.round(gene, 3) for gene in _organism]
# Create new organism with genes from the mutated genes
return _organism