pulsatrix
Loading...
Searching...
No Matches
mutation.hpp
Go to the documentation of this file.
1
9#pragma once
10
11#include <algorithm>
12#include <cmath>
13#include <random>
14#include <stdexcept>
15#include <vector>
16
17namespace pulsatrix {
18
19// --- Bit-flip mutation ---
20
25inline std::vector<bool> BitFlipMutationByMask(const std::vector<bool>& genotype,
26 const std::vector<bool>& flip_mask) {
27 if (genotype.size() != flip_mask.size()) {
28 throw std::invalid_argument("BitFlipMutationByMask: sizes must match");
29 }
30 std::vector<bool> result = genotype;
31 for (size_t i = 0; i < flip_mask.size(); ++i) {
32 if (flip_mask[i]) {
33 result[i] = !result[i];
34 }
35 }
36 return result;
37}
38
45template <typename RNG>
46std::vector<bool> BitFlipMutation(const std::vector<bool>& genotype,
47 double mutation_probability, RNG& rng) {
48 if (mutation_probability < 0.0 || mutation_probability > 1.0) {
49 throw std::invalid_argument("BitFlipMutation: mutation_probability must be in [0, 1]");
50 }
51 std::bernoulli_distribution dist(mutation_probability);
52 std::vector<bool> mask(genotype.size());
53 for (size_t i = 0; i < mask.size(); ++i) {
54 mask[i] = dist(rng);
55 }
56 return BitFlipMutationByMask(genotype, mask);
57}
58
59// --- Gaussian mutation ---
60
66inline std::vector<double> GaussianMutationByNoise(const std::vector<double>& genotype,
67 const std::vector<double>& noise,
68 const std::vector<bool>& apply_mask) {
69 if (genotype.size() != noise.size() || genotype.size() != apply_mask.size()) {
70 throw std::invalid_argument("GaussianMutationByNoise: sizes must all match");
71 }
72 std::vector<double> result(genotype.size());
73 for (size_t i = 0; i < result.size(); ++i) {
74 result[i] = apply_mask[i] ? genotype[i] + noise[i] : genotype[i];
75 }
76 return result;
77}
78
89template <typename RNG>
90std::vector<double> GaussianMutation(const std::vector<double>& genotype, double sigma,
91 double mutation_probability, RNG& rng) {
92 if (sigma < 0.0) {
93 throw std::invalid_argument("GaussianMutation: sigma must be non-negative");
94 }
95 if (mutation_probability < 0.0 || mutation_probability > 1.0) {
96 throw std::invalid_argument("GaussianMutation: mutation_probability must be in [0, 1]");
97 }
98 std::bernoulli_distribution mask_dist(mutation_probability);
99 std::normal_distribution<double> noise_dist(0.0, sigma);
100 std::vector<bool> mask(genotype.size());
101 std::vector<double> noise(genotype.size());
102 for (size_t i = 0; i < genotype.size(); ++i) {
103 mask[i] = mask_dist(rng);
104 noise[i] = noise_dist(rng);
105 }
106 return GaussianMutationByNoise(genotype, noise, mask);
107}
108
109// --- Polynomial mutation (Deb & Goyal 1996; DEAP's mutPolynomialBounded) ---
110
127inline double PolynomialMutationByDraw(double x, double lower, double upper, double eta,
128 double u) {
129 if (!(lower < upper)) {
130 throw std::invalid_argument("PolynomialMutationByDraw: lower must be < upper");
131 }
132 if (x < lower || x > upper) {
133 throw std::invalid_argument("PolynomialMutationByDraw: x must be in [lower, upper]");
134 }
135 if (eta < 0.0) {
136 throw std::invalid_argument("PolynomialMutationByDraw: eta must be non-negative");
137 }
138 if (u < 0.0 || u > 1.0) {
139 throw std::invalid_argument("PolynomialMutationByDraw: u must be in [0, 1]");
140 }
141
142 const double range = upper - lower;
143 const double delta1 = (x - lower) / range;
144 const double delta2 = (upper - x) / range;
145 const double mut_pow = 1.0 / (eta + 1.0);
146 double delta_q;
147 if (u <= 0.5) {
148 double xy = 1.0 - delta1;
149 double val = 2.0 * u + (1.0 - 2.0 * u) * std::pow(xy, eta + 1.0);
150 delta_q = std::pow(val, mut_pow) - 1.0;
151 } else {
152 double xy = 1.0 - delta2;
153 double val = 2.0 * (1.0 - u) + 2.0 * (u - 0.5) * std::pow(xy, eta + 1.0);
154 delta_q = 1.0 - std::pow(val, mut_pow);
155 }
156 double result = x + delta_q * range;
157 return std::min(std::max(result, lower), upper);
158}
159
167template <typename RNG>
168std::vector<double> PolynomialMutation(const std::vector<double>& genotype,
169 const std::vector<double>& lower_bounds,
170 const std::vector<double>& upper_bounds, double eta,
171 double mutation_probability, RNG& rng) {
172 if (genotype.size() != lower_bounds.size() || genotype.size() != upper_bounds.size()) {
173 throw std::invalid_argument("PolynomialMutation: sizes must all match");
174 }
175 if (mutation_probability < 0.0 || mutation_probability > 1.0) {
176 throw std::invalid_argument("PolynomialMutation: mutation_probability must be in [0, 1]");
177 }
178 std::bernoulli_distribution mask_dist(mutation_probability);
179 std::uniform_real_distribution<double> draw_dist(0.0, 1.0);
180 std::vector<double> result(genotype.size());
181 for (size_t i = 0; i < genotype.size(); ++i) {
182 if (mask_dist(rng)) {
183 result[i] = PolynomialMutationByDraw(genotype[i], lower_bounds[i], upper_bounds[i],
184 eta, draw_dist(rng));
185 } else {
186 result[i] = genotype[i];
187 }
188 }
189 return result;
190}
191
192} // namespace pulsatrix
Definition acquisition_functions.hpp:16
double PolynomialMutationByDraw(double x, double lower, double upper, double eta, double u)
Polynomial-mutates a single bounded gene given an explicit draw.
Definition mutation.hpp:127
std::vector< double > GaussianMutationByNoise(const std::vector< double > &genotype, const std::vector< double > &noise, const std::vector< bool > &apply_mask)
Adds noise[i] to genotype[i] wherever apply_mask[i] is true, leaves the rest unchanged.
Definition mutation.hpp:66
std::vector< bool > BitFlipMutation(const std::vector< bool > &genotype, double mutation_probability, RNG &rng)
RNG-driven wrapper: each gene flips independently with probability mutation_probability.
Definition mutation.hpp:46
std::vector< bool > BitFlipMutationByMask(const std::vector< bool > &genotype, const std::vector< bool > &flip_mask)
Flips each gene where flip_mask is true, leaves the rest unchanged.
Definition mutation.hpp:25
std::vector< double > GaussianMutation(const std::vector< double > &genotype, double sigma, double mutation_probability, RNG &rng)
RNG-driven wrapper: each gene independently receives N(0, sigma^2) noise with probability mutation_pr...
Definition mutation.hpp:90
std::vector< double > PolynomialMutation(const std::vector< double > &genotype, const std::vector< double > &lower_bounds, const std::vector< double > &upper_bounds, double eta, double mutation_probability, RNG &rng)
RNG-driven wrapper: each gene independently mutates (via PolynomialMutationByDraw) with probability m...
Definition mutation.hpp:168