Skip to content

Commit 0e8be9d

Browse files
authored
Merge pull request #286 from ahmedfgad/github-actions
GitHub actions
2 parents 272aaa5 + 8bbc4c8 commit 0e8be9d

File tree

5 files changed

+63
-35
lines changed

5 files changed

+63
-35
lines changed

docs/source/pygad.rst

+4-2
Original file line numberDiff line numberDiff line change
@@ -927,8 +927,10 @@ shuffles their order randomly.
927927
``adaptive_mutation()``
928928
~~~~~~~~~~~~~~~~~~~~~~~
929929

930-
Applies the adaptive mutation which selects a subset of genes and
931-
shuffles their order randomly.
930+
Applies the adaptive mutation which selects the number/percentage of
931+
genes to mutate based on the solution's fitness. If the fitness is high
932+
(i.e. solution quality is high), then small number/percentage of genes
933+
is mutated compared to a solution with a low fitness.
932934

933935
.. _bestsolution:
934936

examples/example_multi_objective.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def on_generation(ga_instance):
5353
# Running the GA to optimize the parameters of the function.
5454
ga_instance.run()
5555

56-
ga_instance.plot_fitness(labels=['Obj 1', 'Obj 2'])
56+
ga_instance.plot_fitness(label=['Obj 1', 'Obj 2'])
5757

5858
# Returning the details of the best solution.
5959
solution, solution_fitness, solution_idx = ga_instance.best_solution(ga_instance.last_generation_fitness)

pygad/pygad.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -1135,8 +1135,9 @@ def __init__(self,
11351135

11361136
# Validate delay_after_gen
11371137
if type(delay_after_gen) in GA.supported_int_float_types:
1138-
if not self.suppress_warnings:
1139-
warnings.warn("The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.")
1138+
if delay_after_gen != 0.0:
1139+
if not self.suppress_warnings:
1140+
warnings.warn("The 'delay_after_gen' parameter is deprecated starting from PyGAD 3.3.0. To delay or pause the evolution after each generation, assign a callback function/method to the 'on_generation' parameter to adds some time delay.")
11401141
if delay_after_gen >= 0.0:
11411142
self.delay_after_gen = delay_after_gen
11421143
else:

pygad/utils/__init__.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
from pygad.utils import mutation
44
from pygad.utils import nsga2
55

6-
__version__ = "1.1.1"
6+
__version__ = "1.2.1"

pygad/utils/crossover.py

+54-29
Original file line numberDiff line numberDiff line change
@@ -25,23 +25,27 @@ def single_point_crossover(self, parents, offspring_size):
2525
else:
2626
offspring = numpy.empty(offspring_size, dtype=object)
2727

28-
for k in range(offspring_size[0]):
29-
# The point at which crossover takes place between two parents. Usually, it is at the center.
30-
crossover_point = numpy.random.randint(low=0, high=parents.shape[1], size=1)[0]
28+
# Randomly generate all the K points at which crossover takes place between each two parents. The point does not have to be always at the center of the solutions.
29+
# This saves time by calling the numpy.random.randint() function only once.
30+
crossover_points = numpy.random.randint(low=0,
31+
high=parents.shape[1],
32+
size=offspring_size[0])
3133

34+
for k in range(offspring_size[0]):
35+
# Check if the crossover_probability parameter is used.
3236
if not (self.crossover_probability is None):
3337
probs = numpy.random.random(size=parents.shape[0])
34-
indices = numpy.where(probs <= self.crossover_probability)[0]
38+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
3539

36-
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
40+
# If no parent satisfied the probability, no crossover is applied and a parent is selected as is.
3741
if len(indices) == 0:
3842
offspring[k, :] = parents[k % parents.shape[0], :]
3943
continue
4044
elif len(indices) == 1:
4145
parent1_idx = indices[0]
4246
parent2_idx = parent1_idx
4347
else:
44-
indices = random.sample(list(set(indices)), 2)
48+
indices = random.sample(indices, 2)
4549
parent1_idx = indices[0]
4650
parent2_idx = indices[1]
4751
else:
@@ -51,9 +55,9 @@ def single_point_crossover(self, parents, offspring_size):
5155
parent2_idx = (k+1) % parents.shape[0]
5256

5357
# The new offspring has its first half of its genes from the first parent.
54-
offspring[k, 0:crossover_point] = parents[parent1_idx, 0:crossover_point]
58+
offspring[k, 0:crossover_points[k]] = parents[parent1_idx, 0:crossover_points[k]]
5559
# The new offspring has its second half of its genes from the second parent.
56-
offspring[k, crossover_point:] = parents[parent2_idx, crossover_point:]
60+
offspring[k, crossover_points[k]:] = parents[parent2_idx, crossover_points[k]:]
5761

5862
if self.allow_duplicate_genes == False:
5963
if self.gene_space is None:
@@ -86,17 +90,23 @@ def two_points_crossover(self, parents, offspring_size):
8690
else:
8791
offspring = numpy.empty(offspring_size, dtype=object)
8892

93+
# Randomly generate all the first K points at which crossover takes place between each two parents.
94+
# This saves time by calling the numpy.random.randint() function only once.
95+
if (parents.shape[1] == 1): # If the chromosome has only a single gene. In this case, this gene is copied from the second parent.
96+
crossover_points_1 = numpy.zeros(offspring_size[0])
97+
else:
98+
crossover_points_1 = numpy.random.randint(low=0,
99+
high=numpy.ceil(parents.shape[1]/2 + 1),
100+
size=offspring_size[0])
101+
102+
# The second point must always be greater than the first point.
103+
crossover_points_2 = crossover_points_1 + int(parents.shape[1]/2)
104+
89105
for k in range(offspring_size[0]):
90-
if (parents.shape[1] == 1): # If the chromosome has only a single gene. In this case, this gene is copied from the second parent.
91-
crossover_point1 = 0
92-
else:
93-
crossover_point1 = numpy.random.randint(low=0, high=numpy.ceil(parents.shape[1]/2 + 1), size=1)[0]
94-
95-
crossover_point2 = crossover_point1 + int(parents.shape[1]/2) # The second point must always be greater than the first point.
96106

97107
if not (self.crossover_probability is None):
98108
probs = numpy.random.random(size=parents.shape[0])
99-
indices = numpy.where(probs <= self.crossover_probability)[0]
109+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
100110

101111
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
102112
if len(indices) == 0:
@@ -106,7 +116,7 @@ def two_points_crossover(self, parents, offspring_size):
106116
parent1_idx = indices[0]
107117
parent2_idx = parent1_idx
108118
else:
109-
indices = random.sample(list(set(indices)), 2)
119+
indices = random.sample(indices, 2)
110120
parent1_idx = indices[0]
111121
parent2_idx = indices[1]
112122
else:
@@ -116,11 +126,11 @@ def two_points_crossover(self, parents, offspring_size):
116126
parent2_idx = (k+1) % parents.shape[0]
117127

118128
# The genes from the beginning of the chromosome up to the first point are copied from the first parent.
119-
offspring[k, 0:crossover_point1] = parents[parent1_idx, 0:crossover_point1]
129+
offspring[k, 0:crossover_points_1[k]] = parents[parent1_idx, 0:crossover_points_1[k]]
120130
# The genes from the second point up to the end of the chromosome are copied from the first parent.
121-
offspring[k, crossover_point2:] = parents[parent1_idx, crossover_point2:]
131+
offspring[k, crossover_points_2[k]:] = parents[parent1_idx, crossover_points_2[k]:]
122132
# The genes between the 2 points are copied from the second parent.
123-
offspring[k, crossover_point1:crossover_point2] = parents[parent2_idx, crossover_point1:crossover_point2]
133+
offspring[k, crossover_points_1[k]:crossover_points_2[k]] = parents[parent2_idx, crossover_points_1[k]:crossover_points_2[k]]
124134

125135
if self.allow_duplicate_genes == False:
126136
if self.gene_space is None:
@@ -151,10 +161,18 @@ def uniform_crossover(self, parents, offspring_size):
151161
else:
152162
offspring = numpy.empty(offspring_size, dtype=object)
153163

164+
# Randomly generate all the genes sources at which crossover takes place between each two parents.
165+
# This saves time by calling the numpy.random.randint() function only once.
166+
# There is a list of 0 and 1 for each offspring.
167+
# [0, 1, 0, 0, 1, 1]: If the value is 0, then take the gene from the first parent. If 1, take it from the second parent.
168+
genes_sources = numpy.random.randint(low=0,
169+
high=2,
170+
size=offspring_size)
171+
154172
for k in range(offspring_size[0]):
155173
if not (self.crossover_probability is None):
156174
probs = numpy.random.random(size=parents.shape[0])
157-
indices = numpy.where(probs <= self.crossover_probability)[0]
175+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
158176

159177
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
160178
if len(indices) == 0:
@@ -164,7 +182,7 @@ def uniform_crossover(self, parents, offspring_size):
164182
parent1_idx = indices[0]
165183
parent2_idx = parent1_idx
166184
else:
167-
indices = random.sample(list(set(indices)), 2)
185+
indices = random.sample(indices, 2)
168186
parent1_idx = indices[0]
169187
parent2_idx = indices[1]
170188
else:
@@ -173,12 +191,11 @@ def uniform_crossover(self, parents, offspring_size):
173191
# Index of the second parent to mate.
174192
parent2_idx = (k+1) % parents.shape[0]
175193

176-
genes_source = numpy.random.randint(low=0, high=2, size=offspring_size[1])
177194
for gene_idx in range(offspring_size[1]):
178-
if (genes_source[gene_idx] == 0):
195+
if (genes_sources[k, gene_idx] == 0):
179196
# The gene will be copied from the first parent if the current gene index is 0.
180197
offspring[k, gene_idx] = parents[parent1_idx, gene_idx]
181-
elif (genes_source[gene_idx] == 1):
198+
elif (genes_sources[k, gene_idx] == 1):
182199
# The gene will be copied from the second parent if the current gene index is 1.
183200
offspring[k, gene_idx] = parents[parent2_idx, gene_idx]
184201

@@ -212,10 +229,18 @@ def scattered_crossover(self, parents, offspring_size):
212229
else:
213230
offspring = numpy.empty(offspring_size, dtype=object)
214231

232+
# Randomly generate all the genes sources at which crossover takes place between each two parents.
233+
# This saves time by calling the numpy.random.randint() function only once.
234+
# There is a list of 0 and 1 for each offspring.
235+
# [0, 1, 0, 0, 1, 1]: If the value is 0, then take the gene from the first parent. If 1, take it from the second parent.
236+
genes_sources = numpy.random.randint(low=0,
237+
high=2,
238+
size=offspring_size)
239+
215240
for k in range(offspring_size[0]):
216241
if not (self.crossover_probability is None):
217242
probs = numpy.random.random(size=parents.shape[0])
218-
indices = numpy.where(probs <= self.crossover_probability)[0]
243+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
219244

220245
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
221246
if len(indices) == 0:
@@ -225,7 +250,7 @@ def scattered_crossover(self, parents, offspring_size):
225250
parent1_idx = indices[0]
226251
parent2_idx = parent1_idx
227252
else:
228-
indices = random.sample(list(set(indices)), 2)
253+
indices = random.sample(indices, 2)
229254
parent1_idx = indices[0]
230255
parent2_idx = indices[1]
231256
else:
@@ -234,9 +259,9 @@ def scattered_crossover(self, parents, offspring_size):
234259
# Index of the second parent to mate.
235260
parent2_idx = (k+1) % parents.shape[0]
236261

237-
# A 0/1 vector where 0 means the gene is taken from the first parent and 1 means the gene is taken from the second parent.
238-
gene_sources = numpy.random.randint(0, 2, size=self.num_genes)
239-
offspring[k, :] = numpy.where(gene_sources == 0, parents[parent1_idx, :], parents[parent2_idx, :])
262+
offspring[k, :] = numpy.where(genes_sources[k] == 0,
263+
parents[parent1_idx, :],
264+
parents[parent2_idx, :])
240265

241266
if self.allow_duplicate_genes == False:
242267
if self.gene_space is None:

0 commit comments

Comments
 (0)