pulsatrix
Loading...
Searching...
No Matches
crossover.hpp
Go to the documentation of this file.
1
11#pragma once
12
13#include <algorithm>
14#include <cmath>
15#include <random>
16#include <stdexcept>
17#include <utility>
18#include <vector>
19
20namespace pulsatrix {
21
22// --- One-point crossover ---
23
31template <typename T>
32std::pair<std::vector<T>, std::vector<T>> OnePointCrossoverAtPoint(
33 const std::vector<T>& parent1, const std::vector<T>& parent2, size_t point) {
34 if (parent1.size() != parent2.size()) {
35 throw std::invalid_argument("OnePointCrossoverAtPoint: parent sizes must match");
36 }
37 if (point > parent1.size()) {
38 throw std::invalid_argument("OnePointCrossoverAtPoint: point must be <= parent size");
39 }
40 std::vector<T> child1(parent1.begin(), parent1.begin() + static_cast<std::ptrdiff_t>(point));
41 child1.insert(child1.end(), parent2.begin() + static_cast<std::ptrdiff_t>(point), parent2.end());
42 std::vector<T> child2(parent2.begin(), parent2.begin() + static_cast<std::ptrdiff_t>(point));
43 child2.insert(child2.end(), parent1.begin() + static_cast<std::ptrdiff_t>(point), parent1.end());
44 return {child1, child2};
45}
46
52template <typename T, typename RNG>
53std::pair<std::vector<T>, std::vector<T>> OnePointCrossover(const std::vector<T>& parent1,
54 const std::vector<T>& parent2,
55 RNG& rng) {
56 if (parent1.size() != parent2.size()) {
57 throw std::invalid_argument("OnePointCrossover: parent sizes must match");
58 }
59 if (parent1.size() < 2) {
60 throw std::invalid_argument("OnePointCrossover: parent size must be >= 2");
61 }
62 std::uniform_int_distribution<size_t> dist(1, parent1.size() - 1);
63 return OnePointCrossoverAtPoint(parent1, parent2, dist(rng));
64}
65
66// --- Two-point crossover ---
67
73template <typename T>
74std::pair<std::vector<T>, std::vector<T>> TwoPointCrossoverAtPoints(
75 const std::vector<T>& parent1, const std::vector<T>& parent2, size_t point1, size_t point2) {
76 if (parent1.size() != parent2.size()) {
77 throw std::invalid_argument("TwoPointCrossoverAtPoints: parent sizes must match");
78 }
79 if (point1 > point2 || point2 > parent1.size()) {
80 throw std::invalid_argument(
81 "TwoPointCrossoverAtPoints: require point1 <= point2 <= parent size");
82 }
83 std::vector<T> child1 = parent1;
84 std::vector<T> child2 = parent2;
85 for (size_t i = point1; i < point2; ++i) {
86 std::swap(child1[i], child2[i]);
87 }
88 return {child1, child2};
89}
90
95template <typename T, typename RNG>
96std::pair<std::vector<T>, std::vector<T>> TwoPointCrossover(const std::vector<T>& parent1,
97 const std::vector<T>& parent2,
98 RNG& rng) {
99 if (parent1.size() != parent2.size()) {
100 throw std::invalid_argument("TwoPointCrossover: parent sizes must match");
101 }
102 if (parent1.size() < 2) {
103 throw std::invalid_argument("TwoPointCrossover: parent size must be >= 2");
104 }
105 std::uniform_int_distribution<size_t> dist(0, parent1.size());
106 size_t a = dist(rng);
107 size_t b = dist(rng);
108 if (a > b) {
109 std::swap(a, b);
110 }
111 return TwoPointCrossoverAtPoints(parent1, parent2, a, b);
112}
113
114// --- Uniform crossover ---
115
120template <typename T>
121std::pair<std::vector<T>, std::vector<T>> UniformCrossoverByMask(
122 const std::vector<T>& parent1, const std::vector<T>& parent2,
123 const std::vector<bool>& swap_mask) {
124 if (parent1.size() != parent2.size() || parent1.size() != swap_mask.size()) {
125 throw std::invalid_argument("UniformCrossoverByMask: sizes must all match");
126 }
127 std::vector<T> child1 = parent1;
128 std::vector<T> child2 = parent2;
129 for (size_t i = 0; i < swap_mask.size(); ++i) {
130 if (swap_mask[i]) {
131 std::swap(child1[i], child2[i]);
132 }
133 }
134 return {child1, child2};
135}
136
143template <typename T, typename RNG>
144std::pair<std::vector<T>, std::vector<T>> UniformCrossover(const std::vector<T>& parent1,
145 const std::vector<T>& parent2,
146 double swap_probability, RNG& rng) {
147 if (parent1.size() != parent2.size()) {
148 throw std::invalid_argument("UniformCrossover: parent sizes must match");
149 }
150 if (swap_probability < 0.0 || swap_probability > 1.0) {
151 throw std::invalid_argument("UniformCrossover: swap_probability must be in [0, 1]");
152 }
153 std::bernoulli_distribution dist(swap_probability);
154 std::vector<bool> mask(parent1.size());
155 for (size_t i = 0; i < mask.size(); ++i) {
156 mask[i] = dist(rng);
157 }
158 return UniformCrossoverByMask(parent1, parent2, mask);
159}
160
161// --- Blend crossover (BLX-alpha) ---
162
171inline std::pair<std::vector<double>, std::vector<double>> BlendCrossoverByGamma(
172 const std::vector<double>& parent1, const std::vector<double>& parent2,
173 const std::vector<double>& gamma) {
174 if (parent1.size() != parent2.size() || parent1.size() != gamma.size()) {
175 throw std::invalid_argument("BlendCrossoverByGamma: sizes must all match");
176 }
177 std::vector<double> child1(parent1.size());
178 std::vector<double> child2(parent1.size());
179 for (size_t i = 0; i < parent1.size(); ++i) {
180 child1[i] = (1.0 - gamma[i]) * parent1[i] + gamma[i] * parent2[i];
181 child2[i] = gamma[i] * parent1[i] + (1.0 - gamma[i]) * parent2[i];
182 }
183 return {child1, child2};
184}
185
191template <typename RNG>
192std::pair<std::vector<double>, std::vector<double>> BlendCrossover(
193 const std::vector<double>& parent1, const std::vector<double>& parent2, double alpha,
194 RNG& rng) {
195 if (parent1.size() != parent2.size()) {
196 throw std::invalid_argument("BlendCrossover: parent sizes must match");
197 }
198 if (alpha < 0.0) {
199 throw std::invalid_argument("BlendCrossover: alpha must be non-negative");
200 }
201 std::uniform_real_distribution<double> dist(0.0, 1.0);
202 std::vector<double> gamma(parent1.size());
203 for (size_t i = 0; i < gamma.size(); ++i) {
204 gamma[i] = (1.0 + 2.0 * alpha) * dist(rng) - alpha;
205 }
206 return BlendCrossoverByGamma(parent1, parent2, gamma);
207}
208
209// --- Simulated binary crossover (SBX) ---
210
222inline std::pair<std::vector<double>, std::vector<double>> SimulatedBinaryCrossoverByDraw(
223 const std::vector<double>& parent1, const std::vector<double>& parent2, double eta,
224 const std::vector<double>& draws) {
225 if (parent1.size() != parent2.size() || parent1.size() != draws.size()) {
226 throw std::invalid_argument("SimulatedBinaryCrossoverByDraw: sizes must all match");
227 }
228 if (eta < 0.0) {
229 throw std::invalid_argument("SimulatedBinaryCrossoverByDraw: eta must be non-negative");
230 }
231 for (double u : draws) {
232 if (u < 0.0 || u >= 1.0) {
233 throw std::invalid_argument(
234 "SimulatedBinaryCrossoverByDraw: every draw must be in [0, 1)");
235 }
236 }
237
238 std::vector<double> child1(parent1.size());
239 std::vector<double> child2(parent1.size());
240 const double exponent = 1.0 / (eta + 1.0);
241 for (size_t i = 0; i < parent1.size(); ++i) {
242 double u = draws[i];
243 double beta_q = (u <= 0.5) ? std::pow(2.0 * u, exponent)
244 : std::pow(1.0 / (2.0 * (1.0 - u)), exponent);
245 child1[i] = 0.5 * ((1.0 + beta_q) * parent1[i] + (1.0 - beta_q) * parent2[i]);
246 child2[i] = 0.5 * ((1.0 - beta_q) * parent1[i] + (1.0 + beta_q) * parent2[i]);
247 }
248 return {child1, child2};
249}
250
256template <typename RNG>
257std::pair<std::vector<double>, std::vector<double>> SimulatedBinaryCrossover(
258 const std::vector<double>& parent1, const std::vector<double>& parent2, double eta,
259 RNG& rng) {
260 if (parent1.size() != parent2.size()) {
261 throw std::invalid_argument("SimulatedBinaryCrossover: parent sizes must match");
262 }
263 std::uniform_real_distribution<double> dist(0.0, 1.0);
264 std::vector<double> draws(parent1.size());
265 for (size_t i = 0; i < draws.size(); ++i) {
266 draws[i] = dist(rng);
267 }
268 return SimulatedBinaryCrossoverByDraw(parent1, parent2, eta, draws);
269}
270
271} // namespace pulsatrix
Definition acquisition_functions.hpp:16
std::pair< std::vector< T >, std::vector< T > > TwoPointCrossover(const std::vector< T > &parent1, const std::vector< T > &parent2, RNG &rng)
RNG-driven wrapper: draws two points in [0, size], sorted ascending.
Definition crossover.hpp:96
std::pair< std::vector< T >, std::vector< T > > UniformCrossover(const std::vector< T > &parent1, const std::vector< T > &parent2, double swap_probability, RNG &rng)
RNG-driven wrapper: each gene swaps independently with probability swap_probability.
Definition crossover.hpp:144
std::pair< std::vector< double >, std::vector< double > > SimulatedBinaryCrossoverByDraw(const std::vector< double > &parent1, const std::vector< double > &parent2, double eta, const std::vector< double > &draws)
Simulated binary crossover (Deb & Agrawal 1995; DEAP's cxSimulatedBinary), given an explicit per-gene...
Definition crossover.hpp:222
std::pair< std::vector< T >, std::vector< T > > OnePointCrossoverAtPoint(const std::vector< T > &parent1, const std::vector< T > &parent2, size_t point)
Splits both parents at point and swaps tails.
Definition crossover.hpp:32
std::pair< std::vector< T >, std::vector< T > > OnePointCrossover(const std::vector< T > &parent1, const std::vector< T > &parent2, RNG &rng)
RNG-driven wrapper: draws an interior point in [1, size-1] uniformly.
Definition crossover.hpp:53
std::pair< std::vector< T >, std::vector< T > > UniformCrossoverByMask(const std::vector< T > &parent1, const std::vector< T > &parent2, const std::vector< bool > &swap_mask)
Swaps each gene independently wherever swap_mask is true.
Definition crossover.hpp:121
std::pair< std::vector< double >, std::vector< double > > BlendCrossover(const std::vector< double > &parent1, const std::vector< double > &parent2, double alpha, RNG &rng)
RNG-driven wrapper (DEAP's cxBlend): draws gamma_i = (1+2*alpha)*u_i - alpha per gene,...
Definition crossover.hpp:192
std::pair< std::vector< double >, std::vector< double > > SimulatedBinaryCrossover(const std::vector< double > &parent1, const std::vector< double > &parent2, double eta, RNG &rng)
RNG-driven wrapper: draws u_i ~ Uniform(0, 1) per gene (std::uniform_real_distribution is documented ...
Definition crossover.hpp:257
std::pair< std::vector< double >, std::vector< double > > BlendCrossoverByGamma(const std::vector< double > &parent1, const std::vector< double > &parent2, const std::vector< double > &gamma)
Blends each gene pair via an explicit per-gene gamma: child1_i = (1-gamma_i)*x1_i + gamma_i*x2_i,...
Definition crossover.hpp:171
std::pair< std::vector< T >, std::vector< T > > TwoPointCrossoverAtPoints(const std::vector< T > &parent1, const std::vector< T > &parent2, size_t point1, size_t point2)
Swaps the [point1, point2) segment between both parents.
Definition crossover.hpp:74