Skip to content

Commit 6e4b860

Browse files
veds12KelvinYang0320
authored andcommitted
added rectified GA
1 parent 2e65778 commit 6e4b860

File tree

1 file changed

+192
-0
lines changed
  • deepbots/supervisor/controllers

1 file changed

+192
-0
lines changed
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import pygad
2+
3+
class GA(pygad.GA):
4+
def __init__(self, **kwargs):
5+
super(GA, self).__init__(**kwargs)
6+
7+
def cal_pop_fitness(self):
8+
9+
"""
10+
Calculating the fitness values of all solutions in the current population.
11+
It returns:
12+
-fitness: An array of the calculated fitness values.
13+
"""
14+
15+
if self.valid_parameters == False:
16+
raise ValueError("ERROR calling the cal_pop_fitness() method: \nPlease check the parameters passed while creating an instance of the GA class.\n")
17+
18+
pop_fitness = []
19+
# Calculating the fitness value of each solution in the current population.
20+
for sol_idx, sol in enumerate(self.population):
21+
22+
# Check if the parent's fitness value is already calculated. If so, use it instead of calling the fitness function.
23+
if not (self.last_generation_parents is None) and len(numpy.where(numpy.all(self.last_generation_parents == sol, axis=1))[0] > 0):
24+
# Index of the parent in the parents array (self.last_generation_parents). This is not its index within the population.
25+
parent_idx = numpy.where(numpy.all(self.last_generation_parents == sol, axis=1))[0][0]
26+
# Index of the parent in the population.
27+
parent_idx = self.last_generation_parents_indices[parent_idx]
28+
# Use the parent's index to return its pre-calculated fitness value.
29+
fitness = self.last_generation_fitness[parent_idx]
30+
else:
31+
fitness = self.fitness_func(sol, sol_idx)
32+
pop_fitness.append(fitness)
33+
34+
pop_fitness = numpy.array(pop_fitness)
35+
36+
return pop_fitness
37+
38+
def run(self):
39+
40+
"""
41+
Runs the genetic algorithm. This is the main method in which the genetic algorithm is evolved through a number of generations.
42+
"""
43+
44+
if self.valid_parameters == False:
45+
raise ValueError("Error calling the run() method: \nThe run() method cannot be executed with invalid parameters. Please check the parameters passed while creating an instance of the GA class.\n")
46+
47+
# Reset the variables that store the solutions and their fitness after each generation. If not reset, then for each call to the run() method the new solutions and their fitness values will be appended to the old variables and their length double. Some errors arise if not reset.
48+
# If, in the future, new variables are created that get appended after each generation, please consider resetting them here.
49+
self.best_solutions = [] # Holds the best solution in each generation.
50+
self.best_solutions_fitness = [] # A list holding the fitness value of the best solution for each generation.
51+
self.solutions = [] # Holds the solutions in each generation.
52+
self.solutions_fitness = [] # Holds the fitness of the solutions in each generation.
53+
54+
if not (self.on_start is None):
55+
self.on_start(self)
56+
57+
stop_run = False
58+
59+
# Measuring the fitness of each chromosome in the population. Save the fitness in the last_generation_fitness attribute.
60+
self.last_generation_fitness = self.cal_pop_fitness()
61+
62+
best_solution, best_solution_fitness, best_match_idx = self.best_solution(pop_fitness=self.last_generation_fitness)
63+
64+
# Appending the best solution in the initial population to the best_solutions list.
65+
if self.save_best_solutions:
66+
self.best_solutions.append(best_solution)
67+
68+
# Appending the solutions in the initial population to the solutions list.
69+
if self.save_solutions:
70+
self.solutions.extend(self.population.copy())
71+
72+
for generation in range(self.num_generations):
73+
if not (self.on_fitness is None):
74+
self.on_fitness(self, self.last_generation_fitness)
75+
76+
# Appending the fitness value of the best solution in the current generation to the best_solutions_fitness attribute.
77+
self.best_solutions_fitness.append(best_solution_fitness)
78+
79+
if self.save_solutions:
80+
self.solutions_fitness.extend(self.last_generation_fitness)
81+
82+
# Selecting the best parents in the population for mating.
83+
if callable(self.parent_selection_type):
84+
self.last_generation_parents, self.last_generation_parents_indices = self.select_parents(self.last_generation_fitness, self.num_parents_mating, self)
85+
else:
86+
self.last_generation_parents, self.last_generation_parents_indices = self.select_parents(self.last_generation_fitness, num_parents=self.num_parents_mating)
87+
if not (self.on_parents is None):
88+
self.on_parents(self, self.last_generation_parents)
89+
90+
# If self.crossover_type=None, then no crossover is applied and thus no offspring will be created in the next generations. The next generation will use the solutions in the current population.
91+
if self.crossover_type is None:
92+
if self.num_offspring <= self.keep_parents:
93+
self.last_generation_offspring_crossover = self.last_generation_parents[0:self.num_offspring]
94+
else:
95+
self.last_generation_offspring_crossover = numpy.concatenate((self.last_generation_parents, self.population[0:(self.num_offspring - self.last_generation_parents.shape[0])]))
96+
else:
97+
# Generating offspring using crossover.
98+
if callable(self.crossover_type):
99+
self.last_generation_offspring_crossover = self.crossover(self.last_generation_parents,
100+
(self.num_offspring, self.num_genes),
101+
self)
102+
else:
103+
self.last_generation_offspring_crossover = self.crossover(self.last_generation_parents,
104+
offspring_size=(self.num_offspring, self.num_genes))
105+
if not (self.on_crossover is None):
106+
self.on_crossover(self, self.last_generation_offspring_crossover)
107+
108+
# If self.mutation_type=None, then no mutation is applied and thus no changes are applied to the offspring created using the crossover operation. The offspring will be used unchanged in the next generation.
109+
if self.mutation_type is None:
110+
self.last_generation_offspring_mutation = self.last_generation_offspring_crossover
111+
else:
112+
# Adding some variations to the offspring using mutation.
113+
if callable(self.mutation_type):
114+
self.last_generation_offspring_mutation = self.mutation(self.last_generation_offspring_crossover, self)
115+
else:
116+
self.last_generation_offspring_mutation = self.mutation(self.last_generation_offspring_crossover)
117+
if not (self.on_mutation is None):
118+
self.on_mutation(self, self.last_generation_offspring_mutation)
119+
120+
if (self.keep_parents == 0):
121+
self.population = self.last_generation_offspring_mutation
122+
elif (self.keep_parents == -1):
123+
# Creating the new population based on the parents and offspring.
124+
self.population[0:self.last_generation_parents.shape[0], :] = self.last_generation_parents
125+
self.population[self.last_generation_parents.shape[0]:, :] = self.last_generation_offspring_mutation
126+
elif (self.keep_parents > 0):
127+
parents_to_keep, _ = self.steady_state_selection(self.last_generation_fitness, num_parents=self.keep_parents)
128+
self.population[0:parents_to_keep.shape[0], :] = parents_to_keep
129+
self.population[parents_to_keep.shape[0]:, :] = self.last_generation_offspring_mutation
130+
131+
self.generations_completed = generation + 1 # The generations_completed attribute holds the number of the last completed generation.
132+
133+
# Measuring the fitness of each chromosome in the population. Save the fitness in the last_generation_fitness attribute.
134+
self.last_generation_fitness = self.cal_pop_fitness()
135+
136+
best_solution, best_solution_fitness, best_match_idx = self.best_solution(pop_fitness=self.last_generation_fitness)
137+
138+
# Appending the best solution in the current generation to the best_solutions list.
139+
if self.save_best_solutions:
140+
self.best_solutions.append(best_solution)
141+
142+
# Appending the solutions in the current generation to the solutions list.
143+
if self.save_solutions:
144+
self.solutions.extend(self.population.copy())
145+
146+
# If the callback_generation attribute is not None, then cal the callback function after the generation.
147+
if not (self.on_generation is None):
148+
r = self.on_generation(self)
149+
if type(r) is str and r.lower() == "stop":
150+
# Before aborting the loop, save the fitness value of the best solution.
151+
_, best_solution_fitness, _ = self.best_solution(self.last_generation_fitness)
152+
self.best_solutions_fitness.append(best_solution_fitness)
153+
break
154+
155+
if not self.stop_criteria is None:
156+
for criterion in self.stop_criteria:
157+
if criterion[0] == "reach":
158+
if max(self.last_generation_fitness) >= criterion[1]:
159+
stop_run = True
160+
break
161+
elif criterion[0] == "saturate":
162+
criterion[1] = int(criterion[1])
163+
if (self.generations_completed >= criterion[1]):
164+
if (self.best_solutions_fitness[self.generations_completed - criterion[1]] - self.best_solutions_fitness[self.generations_completed - 1]) == 0:
165+
stop_run = True
166+
break
167+
168+
if stop_run:
169+
break
170+
171+
time.sleep(self.delay_after_gen)
172+
173+
# Save the fitness of the last generation.
174+
if self.save_solutions:
175+
self.solutions_fitness.extend(self.last_generation_fitness)
176+
177+
# Save the fitness value of the best solution.
178+
_, best_solution_fitness, _ = self.best_solution(pop_fitness=self.last_generation_fitness)
179+
self.best_solutions_fitness.append(best_solution_fitness)
180+
181+
self.best_solution_generation = numpy.where(numpy.array(self.best_solutions_fitness) == numpy.max(numpy.array(self.best_solutions_fitness)))[0][0]
182+
# After the run() method completes, the run_completed flag is changed from False to True.
183+
self.run_completed = True # Set to True only after the run() method completes gracefully.
184+
185+
if not (self.on_stop is None):
186+
self.on_stop(self, self.last_generation_fitness)
187+
188+
# Converting the 'best_solutions' list into a NumPy array.
189+
self.best_solutions = numpy.array(self.best_solutions)
190+
191+
# Converting the 'solutions' list into a NumPy array.
192+
self.solutions = numpy.array(self.solutions)

0 commit comments

Comments
 (0)