-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathmulti_point_crossover.py
More file actions
119 lines (89 loc) · 4.19 KB
/
Copy pathmulti_point_crossover.py
File metadata and controls
119 lines (89 loc) · 4.19 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
""" Multipoint crossover module. """
# Custom code imports.
from pygenalgo.genome.gene import Gene
from pygenalgo.genome.chromosome import Chromosome
from pygenalgo.operators.crossover.crossover_operator import (CrossoverOperator, Offsprings)
class MultiPointCrossover(CrossoverOperator):
"""
Description:
Multipoint crossover creates two children chromosomes (offsprings),
by taking two parent chromosomes and cutting them at randomly chosen,
sites (loci).
It produces faster mixing, compared with single-point crossover.
"""
def __init__(self, crossover_probability: float = 0.9, n_points: int = 2) -> None:
"""
Construct a 'MultiPointCrossover' object with a given probability value.
:param crossover_probability: (float).
:param n_points: (int) the number of points to cut the genome.
"""
# Call the super constructor with the provided initial value.
super().__init__(crossover_probability=crossover_probability)
# Make sure number of points are at least 2.
self._items: int = max(int(n_points), 2)
# _end_def_
def crossover(self, parent1: Chromosome, parent2: Chromosome) -> Offsprings:
"""
Perform the crossover operation on the two input parent
chromosomes, using multiple cutting points (num_loci).
NOTE: the number of loci is held in the '_items' variable.
:param parent1: (Chromosome).
:param parent2: (Chromosome).
:return: child1 and child2 (as Chromosomes).
"""
# If the crossover probability is higher than a uniformly
# random value and the parents aren't identical apply the
# changes.
if (parent1 != parent2) and self.is_operator_applicable():
# Find the minimum length of the two chromosomes.
min_length: int = min(len(parent1), len(parent2))
# Extract the number of cut points.
num_points: int = self._items
# Ensure the number of requested cutting points
# does not exceed the length of the chromosomes.
if num_points >= min_length:
raise ValueError(f"{self.__class__.__name__}:"
" Number of requested crossover points"
" exceeds the length of the chromosome.")
# _end_def_
# Select randomly the crossover points and sort them.
loci = sorted(self.rng.choice(min_length, size=num_points,
replace=False, shuffle=False))
# Create the 1st offspring genome list.
child_1: list[Gene] = [
gene.clone() for gene in parent1.genome
]
# Create the 2nd offspring genome list.
child_2: list[Gene] = [
gene.clone() for gene in parent2.genome
]
# Initialize a set of hyperparameters.
reset_flag, upper_lim, j = True, loci[0], 0
# Scan the genomes up to min_length.
for i in range(min_length):
# Once we surpass the upper limit (in loci)
# we reset the flag value to allow changes
# to take place within that range.
if i >= upper_lim:
# Swap the reset flag.
reset_flag = not reset_flag
# Increase the index of the loci.
j += 1
# We make sure the upper limit value does not exceed
# the number of genes. Also, this avoids the out of
# bound IndexError.
upper_lim = loci[j] if j < num_points else min_length
# _end_if_
# Check the flag value.
if not reset_flag:
child_1[i], child_2[i] = child_2[i], child_1[i]
# _end_for_
# Increase the crossover counter.
self.inc_counter()
# Return two new offsprings.
return Chromosome(child_1), Chromosome(child_2)
# _end_if_
# Return two cloned offsprings.
return parent1.clone(), parent2.clone()
# _end_def_
# _end_class_