Monte Carlo Integration Library 1.0
High-performance Monte Carlo methods for numerical integration and optimization
GA.cpp
Go to the documentation of this file.
1// GA.cpp
10#include "GA.hpp"
11#include "../rng/rng_factory.hpp"
12#include <algorithm>
13#include <cmath>
14#include <cstdint>
15
16namespace mc{
17namespace optim{
18
19 GA::GA(const GAConfig& config)
20 : m_config(config),
21 m_global_best(Solution::make_worst(OptimizationMode::MINIMIZE)),
22 m_rng(mc::rng::make_engine(100)) // stream_id=100 for GA
23 {}
24
26 m_func = std::move(func);
27 m_initialized = false;
28 }
29
31 m_callback = std::move(cb);
32 }
33
34 void GA::setBounds(const Coordinates& lower, const Coordinates& upper) {
35 if (lower.size() != upper.size()) {
36 throw std::invalid_argument("Lower and Upper bounds must have the same dimension.");
37 }
38 m_lower_bounds = lower;
39 m_upper_bounds = upper;
40 m_initialized = false;
41 }
42
44 m_mode = mode;
45 m_global_best = Solution::make_worst(m_mode);
46 m_initialized = false;
47 }
48
49 bool GA::isBetterFitness(Real a, Real b) const {
50 return (m_mode == OptimizationMode::MINIMIZE) ? (a < b) : (a > b);
51 }
52
53 void GA::evaluate(Individual& ind) {
54 ind.fitness = m_func(ind.genome);
55 // Global best is updated serially outside of parallel regions
56 }
57
58 void GA::enforceBounds(Coordinates& x) {
59 for (size_t i = 0; i < x.size(); ++i) {
60 if (x[i] < m_lower_bounds[i]) x[i] = m_lower_bounds[i];
61 else if (x[i] > m_upper_bounds[i]) x[i] = m_upper_bounds[i];
62 }
63 }
64
65 void GA::initialize() {
66 if (!m_func) throw std::runtime_error("Objective function not set.");
67 if (m_lower_bounds.empty()) throw std::runtime_error("Bounds not set.");
68 if (m_config.population_size == 0) throw std::runtime_error("Population size must be > 0.");
69 if (m_config.elitism_count >= m_config.population_size)
70 throw std::runtime_error("Elitism count must be < population size.");
71
72 m_population.clear();
73 m_population.resize(m_config.population_size);
74
75 const size_t dim = m_lower_bounds.size();
76 std::uniform_real_distribution<Real> u01(0.0, 1.0);
77
78 m_generation = 0;
79 m_global_best = Solution::make_worst(m_mode);
80
81 // Genome init stays serial (uses shared RNG)
82 for (auto& ind : m_population) {
83 ind.genome.resize(dim);
84 for (size_t i = 0; i < dim; ++i) {
85 Real span = m_upper_bounds[i] - m_lower_bounds[i];
86 ind.genome[i] = m_lower_bounds[i] + u01(m_rng) * span;
87 }
88 ind.fitness = 0; // will be evaluated below
89 }
90
91 // Fitness evaluation can be parallel
92 #pragma omp parallel for
93 for (int i = 0; i < static_cast<int>(m_population.size()); ++i) {
94 evaluate(m_population[static_cast<size_t>(i)]);
95 }
96
97 // Serial update of global best to ensure deterministic tie-breaking
98 for (const auto& ind : m_population) {
99 Solution sol{ind.genome, ind.fitness};
100 if (sol.isBetterThan(m_global_best, m_mode)) {
101 m_global_best = sol;
102 }
103 }
104
105 m_initialized = true;
106 }
107
108 const GA::Individual& GA::tournamentSelect() {
109 std::uniform_int_distribution<size_t> pick(0, m_population.size() - 1);
110
111 const Individual* best = nullptr;
112 for (size_t i = 0; i < m_config.tournament_k; ++i) {
113 const Individual& cand = m_population[pick(m_rng)];
114 if (!best || isBetterFitness(cand.fitness, best->fitness)) {
115 best = &cand;
116 }
117 }
118 return *best;
119 }
120
121 void GA::crossoverUniform(const Coordinates& p1, const Coordinates& p2,
122 Coordinates& c1, Coordinates& c2) {
123 std::uniform_real_distribution<Real> u01(0.0, 1.0);
124 const size_t dim = p1.size();
125
126 c1 = p1;
127 c2 = p2;
128
129 for (size_t i = 0; i < dim; ++i) {
130 if (u01(m_rng) < 0.5) {
131 std::swap(c1[i], c2[i]);
132 }
133 }
134 }
135
136 void GA::mutateGaussian(Coordinates& x) {
137 std::uniform_real_distribution<Real> u01(0.0, 1.0);
138 std::normal_distribution<Real> n01(0.0, 1.0);
139
140 for (size_t i = 0; i < x.size(); ++i) {
141 if (u01(m_rng) < m_config.mutation_rate) {
142 Real span = m_upper_bounds[i] - m_lower_bounds[i];
143 Real sigma = m_config.mutation_sigma * span;
144 x[i] += n01(m_rng) * sigma;
145 }
146 }
147 enforceBounds(x);
148 }
149
150 void GA::step() {
151 if (!m_initialized) initialize();
152
153 std::sort(m_population.begin(), m_population.end(),
154 [&](const Individual& a, const Individual& b) {
155 return isBetterFitness(a.fitness, b.fitness);
156 });
157
158 std::vector<Individual> next;
159 next.reserve(m_population.size());
160
161 for (size_t i = 0; i < m_config.elitism_count; ++i) {
162 next.push_back(m_population[i]);
163 }
164
165 std::uniform_real_distribution<Real> u01(0.0, 1.0);
166
167 // Selection + variation stays serial (uses shared RNG)
168 while (next.size() < m_population.size()) {
169 const Individual& p1 = tournamentSelect();
170 const Individual& p2 = tournamentSelect();
171
172 Individual c1, c2;
173 c1.genome = p1.genome;
174 c2.genome = p2.genome;
175
176 if (u01(m_rng) < m_config.crossover_rate) {
177 crossoverUniform(p1.genome, p2.genome, c1.genome, c2.genome);
178 }
179
180 mutateGaussian(c1.genome);
181 mutateGaussian(c2.genome);
182
183 c1.fitness = 0; // evaluated below
184 next.push_back(std::move(c1));
185
186 if (next.size() < m_population.size()) {
187 c2.fitness = 0; // evaluated below
188 next.push_back(std::move(c2));
189 }
190 }
191
192 // Fitness evaluation can be parallel (skip elites)
193 const size_t start = m_config.elitism_count;
194 #pragma omp parallel for
195 for (int i = static_cast<int>(start); i < static_cast<int>(next.size()); ++i) {
196 evaluate(next[static_cast<size_t>(i)]);
197 }
198
199 m_population = std::move(next);
200 ++m_generation;
201
202 // Serial update of global best after population evolves
203 for (const auto& ind : m_population) {
204 Solution sol{ind.genome, ind.fitness};
205 if (sol.isBetterThan(m_global_best, m_mode)) {
206 m_global_best = sol;
207 }
208 }
209 }
210
212 initialize();
213
214 for (size_t gen = 0; gen < m_config.max_generations; ++gen) {
215 step();
216
217 if (m_callback) {
218 m_callback(m_global_best, gen);
219 }
220 }
221 return m_global_best;
222 }
223
225 return m_global_best;
226 }
227
228} //namespace mc
229} //namespace optim
Genetic Algorithm (GA) interface and data structures.
GA(const GAConfig &config=GAConfig{})
Construct a GA optimizer with the given configuration.
Definition GA.cpp:19
void setMode(OptimizationMode mode) override
Set optimization mode (minimize or maximize).
Definition GA.cpp:43
void setBounds(const Coordinates &lower, const Coordinates &upper) override
Set lower/upper bounds of the search hyper-rectangle.
Definition GA.cpp:34
Solution optimize() override
Run GA for max_generations.
Definition GA.cpp:211
Solution getBestSolution() const override
Get the best solution found so far.
Definition GA.cpp:224
void setCallback(StepCallback cb) override
Register a callback invoked after each generation.
Definition GA.cpp:30
void setObjectiveFunction(ObjectiveFunction func) override
Set the objective function to optimize.
Definition GA.cpp:25
void step() override
Perform one generation: selection, crossover, mutation, evaluation, and serial update of the global b...
Definition GA.cpp:150
std::function< void(const Solution &current_best, size_t iteration)> StepCallback
Callback invoked after each step/generation.
Definition optimizer.hpp:25
constexpr int dim
Default dimensionality for integration.
Definition main.cpp:36
OptimizationMode
Optimization goal.
Definition types.hpp:42
std::function< Real(const Coordinates &)> ObjectiveFunction
Objective function signature.
Definition types.hpp:37
std::vector< Real > Coordinates
A point in the N-dimensional search space.
Definition types.hpp:31
double Real
Scalar precision used across optimizers.
Definition types.hpp:26
Configuration parameters for GA.
Definition GA.hpp:21
size_t elitism_count
Number of top individuals copied unchanged to next generation.
Definition GA.hpp:38
size_t tournament_k
Tournament size for selection (k >= 2).
Definition GA.hpp:28
Real mutation_rate
Per-gene mutation probability.
Definition GA.hpp:33
size_t population_size
Size of the population.
Definition GA.hpp:23
Real crossover_rate
Probability of performing crossover in reproduction.
Definition GA.hpp:31
Real mutation_sigma
Mutation magnitude (scaled by coordinate span).
Definition GA.hpp:35
size_t max_generations
Number of generations to evolve.
Definition GA.hpp:25
A single population member.
Definition GA.hpp:51
Coordinates genome
Encoded parameters (genome).
Definition GA.hpp:53
Real fitness
Fitness value for this genome.
Definition GA.hpp:55
Represents a candidate solution in the search space.
Definition types.hpp:51
static Solution make_worst(OptimizationMode mode)
Helper to create a worst-case solution for initialization.
Definition types.hpp:62