Repository navigation
Expand file tree
/
Copy patharithmetic_crossover.py
More file actions
127 lines (96 loc) · 4.49 KB
/
Copy patharithmetic_crossover.py
File metadata and controls
127 lines (96 loc) · 4.49 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
""" Arithmetic crossover (whole/linear) operator module. """
from math import isclose
from typing import Optional
# Third party imports.
from numpy.typing import ArrayLike, NDArray
# Custom code imports.
from pygenalgo.genome.gene import Gene
from pygenalgo.genome.chromosome import Chromosome
from pygenalgo.utils.utilities import clamp, reflect_boundary
from pygenalgo.operators.crossover.crossover_operator import (CrossoverOperator, Offspring)
class ArithmeticCrossover(CrossoverOperator):
"""
Description:
The Whole ArithmeticCrossover operator with reflection is structurally quite similar
to Blend Crossover (BLX-α), but they differ fundamentally in where they look for new
solutions. They both use linear interpolation, but 'BLX-α' is explicitly designed to
explore outside the space bounded by the parents, whereas standard ArithmeticCrossover
stays inside (unless configured with an extraordinary α value).
NB: Used only for real coded genomes.
"""
def __init__(self, crossover_probability: float = 0.9,
p_alpha: Optional[float] = None,
lower_lim: ArrayLike = None,
upper_lim: ArrayLike = None) -> None:
"""
Construct a 'ArithmeticCrossover' object with a given probability value.
:param crossover_probability: (float).
:param p_alpha: (float).
:param lower_lim: (ArrayLike) lower limit values for the genes.
:param upper_lim: (ArrayLike) upper limit values for the genes.
"""
# Call the super constructor with the provided initial value.
super().__init__(crossover_probability=crossover_probability)
# Validate the bounds.
lower_lim, upper_lim = self.validate_bounds(lower_lim,
upper_lim)
# Check if p_alpha is provided.
if p_alpha is None:
# Choose a value at random.
p_alpha = self.rng.random()
else:
# Ensure that it is a float number within range (0, 1).
p_alpha = clamp(float(p_alpha), 0.0, 1.0)
# _end_if_
# Assign variables to the _items placeholder.
self._items: tuple[float, NDArray, NDArray] = (
p_alpha, lower_lim, upper_lim
)
# _end_def_
def crossover(self, parent1: Chromosome, parent2: Chromosome) -> Offspring:
"""
Perform the crossover operation on the two input parent chromosomes.
: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 self.is_operator_applicable() and (parent1 != parent2):
# Extract values from the placeholder.
p_alpha, x_lower, x_upper = self._items
# Create the 1st offspring genome list.
child_1: list[Gene] = parent1.clone_genome()
# Create the 2nd offspring genome list.
child_2: list[Gene] = parent2.clone_genome()
# Find the minimum length of the two chromosomes.
min_length: int = min(len(child_1), len(child_2))
# Set the new gene values linearly.
for i in range(min_length):
# Extract gene values from both parents.
v1: float = child_1[i].value
v2: float = child_2[i].value
# Skip if genes are (almost) identical.
if isclose(v1, v2, rel_tol=1.0e-9, abs_tol=1.0e-15):
continue
# _end_if_
# Compute the new gene values.
c1: float = p_alpha * v1 + (1.0 - p_alpha) * v2
c2: float = (1.0 - p_alpha) * v1 + p_alpha * v2
# Get the limits.
xl: float = x_lower[i]
xu: float = x_upper[i]
# Update the genomes by applying mirroring strategy for out of bounds.
child_1[i].value = reflect_boundary(c1, xl, xu)
child_2[i].value = reflect_boundary(c2, xl, xu)
# _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_