Skip to content

Commit 8bbc4c8

Browse files
committed
Reduce time of crossover functions
1 parent 4eba80b commit 8bbc4c8

File tree

1 file changed

+47
-24
lines changed

1 file changed

+47
-24
lines changed

pygad/utils/crossover.py

+47-24
Original file line numberDiff line numberDiff line change
@@ -26,24 +26,26 @@ def single_point_crossover(self, parents, offspring_size):
2626
offspring = numpy.empty(offspring_size, dtype=object)
2727

2828
# 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.
2930
crossover_points = numpy.random.randint(low=0,
3031
high=parents.shape[1],
3132
size=offspring_size[0])
33+
3234
for k in range(offspring_size[0]):
3335
# Check if the crossover_probability parameter is used.
3436
if not (self.crossover_probability is None):
3537
probs = numpy.random.random(size=parents.shape[0])
36-
indices = numpy.where(probs <= self.crossover_probability)[0]
38+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
3739

38-
# 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.
3941
if len(indices) == 0:
4042
offspring[k, :] = parents[k % parents.shape[0], :]
4143
continue
4244
elif len(indices) == 1:
4345
parent1_idx = indices[0]
4446
parent2_idx = parent1_idx
4547
else:
46-
indices = random.sample(list(set(indices)), 2)
48+
indices = random.sample(indices, 2)
4749
parent1_idx = indices[0]
4850
parent2_idx = indices[1]
4951
else:
@@ -88,17 +90,23 @@ def two_points_crossover(self, parents, offspring_size):
8890
else:
8991
offspring = numpy.empty(offspring_size, dtype=object)
9092

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+
91105
for k in range(offspring_size[0]):
92-
if (parents.shape[1] == 1): # If the chromosome has only a single gene. In this case, this gene is copied from the second parent.
93-
crossover_point1 = 0
94-
else:
95-
crossover_point1 = numpy.random.randint(low=0, high=numpy.ceil(parents.shape[1]/2 + 1), size=1)[0]
96-
97-
crossover_point2 = crossover_point1 + int(parents.shape[1]/2) # The second point must always be greater than the first point.
98106

99107
if not (self.crossover_probability is None):
100108
probs = numpy.random.random(size=parents.shape[0])
101-
indices = numpy.where(probs <= self.crossover_probability)[0]
109+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
102110

103111
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
104112
if len(indices) == 0:
@@ -108,7 +116,7 @@ def two_points_crossover(self, parents, offspring_size):
108116
parent1_idx = indices[0]
109117
parent2_idx = parent1_idx
110118
else:
111-
indices = random.sample(list(set(indices)), 2)
119+
indices = random.sample(indices, 2)
112120
parent1_idx = indices[0]
113121
parent2_idx = indices[1]
114122
else:
@@ -118,11 +126,11 @@ def two_points_crossover(self, parents, offspring_size):
118126
parent2_idx = (k+1) % parents.shape[0]
119127

120128
# The genes from the beginning of the chromosome up to the first point are copied from the first parent.
121-
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]]
122130
# The genes from the second point up to the end of the chromosome are copied from the first parent.
123-
offspring[k, crossover_point2:] = parents[parent1_idx, crossover_point2:]
131+
offspring[k, crossover_points_2[k]:] = parents[parent1_idx, crossover_points_2[k]:]
124132
# The genes between the 2 points are copied from the second parent.
125-
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]]
126134

127135
if self.allow_duplicate_genes == False:
128136
if self.gene_space is None:
@@ -153,10 +161,18 @@ def uniform_crossover(self, parents, offspring_size):
153161
else:
154162
offspring = numpy.empty(offspring_size, dtype=object)
155163

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+
156172
for k in range(offspring_size[0]):
157173
if not (self.crossover_probability is None):
158174
probs = numpy.random.random(size=parents.shape[0])
159-
indices = numpy.where(probs <= self.crossover_probability)[0]
175+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
160176

161177
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
162178
if len(indices) == 0:
@@ -166,7 +182,7 @@ def uniform_crossover(self, parents, offspring_size):
166182
parent1_idx = indices[0]
167183
parent2_idx = parent1_idx
168184
else:
169-
indices = random.sample(list(set(indices)), 2)
185+
indices = random.sample(indices, 2)
170186
parent1_idx = indices[0]
171187
parent2_idx = indices[1]
172188
else:
@@ -175,12 +191,11 @@ def uniform_crossover(self, parents, offspring_size):
175191
# Index of the second parent to mate.
176192
parent2_idx = (k+1) % parents.shape[0]
177193

178-
genes_source = numpy.random.randint(low=0, high=2, size=offspring_size[1])
179194
for gene_idx in range(offspring_size[1]):
180-
if (genes_source[gene_idx] == 0):
195+
if (genes_sources[k, gene_idx] == 0):
181196
# The gene will be copied from the first parent if the current gene index is 0.
182197
offspring[k, gene_idx] = parents[parent1_idx, gene_idx]
183-
elif (genes_source[gene_idx] == 1):
198+
elif (genes_sources[k, gene_idx] == 1):
184199
# The gene will be copied from the second parent if the current gene index is 1.
185200
offspring[k, gene_idx] = parents[parent2_idx, gene_idx]
186201

@@ -214,10 +229,18 @@ def scattered_crossover(self, parents, offspring_size):
214229
else:
215230
offspring = numpy.empty(offspring_size, dtype=object)
216231

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+
217240
for k in range(offspring_size[0]):
218241
if not (self.crossover_probability is None):
219242
probs = numpy.random.random(size=parents.shape[0])
220-
indices = numpy.where(probs <= self.crossover_probability)[0]
243+
indices = list(set(numpy.where(probs <= self.crossover_probability)[0]))
221244

222245
# If no parent satisfied the probability, no crossover is applied and a parent is selected.
223246
if len(indices) == 0:
@@ -227,7 +250,7 @@ def scattered_crossover(self, parents, offspring_size):
227250
parent1_idx = indices[0]
228251
parent2_idx = parent1_idx
229252
else:
230-
indices = random.sample(list(set(indices)), 2)
253+
indices = random.sample(indices, 2)
231254
parent1_idx = indices[0]
232255
parent2_idx = indices[1]
233256
else:
@@ -236,9 +259,9 @@ def scattered_crossover(self, parents, offspring_size):
236259
# Index of the second parent to mate.
237260
parent2_idx = (k+1) % parents.shape[0]
238261

239-
# 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.
240-
gene_sources = numpy.random.randint(0, 2, size=self.num_genes)
241-
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, :])
242265

243266
if self.allow_duplicate_genes == False:
244267
if self.gene_space is None:

0 commit comments

Comments
 (0)