OPAL (Object Oriented Parallel Accelerator Library) 2024.2
OPAL
Variator.h
Go to the documentation of this file.
1//
2// Class Variator
3//
4// Copyright (c) 2010 - 2013, Yves Ineichen, ETH Zürich
5// All rights reserved
6//
7// Implemented as part of the PhD thesis
8// "Toward massively parallel multi-objective optimization with application to
9// particle accelerators" (https://doi.org/10.3929/ethz-a-009792359)
10//
11// This file is part of OPAL.
12//
13// OPAL is free software: you can redistribute it and/or modify
14// it under the terms of the GNU General Public License as published by
15// the Free Software Foundation, either version 3 of the License, or
16// (at your option) any later version.
17//
18// You should have received a copy of the GNU General Public License
19// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
20//
21#ifndef __VARIATOR_H__
22#define __VARIATOR_H__
23
24#include <string>
25#include <vector>
26#include <map>
27#include <utility>
28
29#include "Util/Types.h"
30#include "Util/CmdArguments.h"
32#include "Optimizer/Optimizer.h"
33
34#include "boost/property_tree/ptree.hpp"
35#include "boost/property_tree/json_parser.hpp"
36
37template<
38 class ind_t
39 , template <class> class CrossoverOperator
40 , template <class> class MutationOperator
41>
42class Variator : public CrossoverOperator<ind_t>,
43 public MutationOperator<ind_t>
44{
45
46public:
47
48 Variator(std::vector<std::string> dNames,
49 Optimizer::bounds_t dVarBounds, Expressions::Named_t constraints,
50 CmdArguments_t args)
51 : dNames_m(dNames)
52 , dVarBounds_m(dVarBounds)
53 {
54 // add constraints, if only design variables are needed for evaluation
55 for(auto constraint : constraints) {
56 bool allDesignVariables = true;
57 std::set<std::string> req_vars = constraint.second->getReqVars();
58 if (req_vars.empty()) allDesignVariables = false;
59 for (std::string req_var : req_vars) {
60 // check if it is a design variable
61 if (std::find(dNames_m.begin(),dNames_m.end(),req_var) == dNames_m.end()) {
62 allDesignVariables = false;
63 break;
64 }
65 }
66 if (allDesignVariables == true)
67 constraints_m.insert(constraint);
68 }
69
70 //FIXME: pass population as arg to variator
71 //std::shared_ptr< Population<ind_t> >
73
75 args->getArg<double>("mutation-probability", 0.5);
76
78 args->getArg<double>("recombination-probability", 0.5);
79
80 args_ = args;
81 }
82
84 }
85
86 //FIXME access population from outside
87 std::shared_ptr< Population<ind_t> > population() {
88 return population_m;
89 }
90
92 void initial_population(size_t sizeInitial, std::string fname) {
93 if ( fname.empty() ) {
94 for(size_t i = 0; i < sizeInitial; i++) {
96 }
97 return;
98 }
99 // read population from file
100 typedef boost::property_tree::ptree ptree_t;
101 ptree_t tree;
102
103 boost::property_tree::read_json(fname, tree);
104
105 if ( tree.get<std::string>("name").compare("opt-pilot") ) {
106 throw OptPilotException("Variator::initial_population",
107 "Json file not generated by opt-pilot.");
108 }
109
110 std::size_t nDVars = 0;
111 for(auto& elem : tree.get_child("dvar-bounds")) {
112 // check if it is a design variable
113 if (std::find(dNames_m.begin(), dNames_m.end(), elem.first) == dNames_m.end()) {
114 throw OptPilotException("Variator::initial_population",
115 "The design variable '" + elem.first + "' is not in the list.");
116 }
117 ++nDVars;
118 }
119
120 if ( nDVars != dVarBounds_m.size() ) {
121 throw OptPilotException("Variator::initial_population",
122 "The number of design variables do not agree.");
123 }
124
125 boost::property_tree::ptree& population = tree.get_child("population");
126
127 std::size_t size = 0;
128 Individual::genes_t dvars(nDVars);
129 for (auto& ind : population ) {
130
131 if ( size > sizeInitial - 1 )
132 break;
133
134 /* just to be sure: It might be that the order of the design variables in the
135 * Json file is not the same as it reads in, therefore, we check and take the order
136 * of the container dNames_m.
137 */
138 for (auto& dvar : population.get_child(ind.first).get_child("dvar")) {
139 auto it = std::find(dNames_m.begin(), dNames_m.end(), dvar.first);
140 std::size_t idx = std::distance(dNames_m.begin(), it);
141 //FIXME requires random access of Individual::genes_t
142 dvars[idx] = dvar.second.get_value<double>();
143 }
144
145 new_individual(dvars);
146 ++size;
147 }
148
149 // fill with random individuals
150 for (std::size_t i = size; i < sizeInitial; ++i) {
152 }
153 }
154
156 void infeasible(std::shared_ptr<ind_t> ind) {
157 population_m->remove_individual(ind);
159 }
160
163 return !individualsToEvaluate_m.empty();
164 }
165
167 std::shared_ptr<ind_t> popIndividualToEvaluate() {
168 unsigned int ind = individualsToEvaluate_m.front();
170 return population_m->get_staging(ind);
171 }
172
178 void variate(std::vector<unsigned int> parents) {
179
180 // copying all individuals from parents
181 for(unsigned int parent : parents) {
182 new_individual( population_m->get_individual(parent) );
183 }
184
185 // only variate new offspring, individuals in staging area have been
186 // variated already
187 std::queue<unsigned int> tmp(individualsToEvaluate_m);
188 while(!tmp.empty()) {
189
190 // pop first individual
191 unsigned int idx = tmp.front(); tmp.pop();
192 std::shared_ptr<ind_t> a = population_m->get_staging(idx);
193
194 // handle special case where we have an odd number of offspring
195 if(tmp.empty()) {
196 if (drand(1) <= mutationProbability_m) {
197 // temporary copy in case not successful
198 std::shared_ptr<ind_t> copyA(new ind_t(a));
199 int iter = 0;
200 while (true) {
201 // assign with shared pointer constructor
202 *a = copyA;
203 this->mutate(a, args_);
204 // check if viable offspring
205 if (a->viable()) break;
206
207 iter++;
208 // if maximum number of tries then create new individual
209 if (iter > 100) {
210 infeasible(a);
211 break;
212 }
213 }
214 }
215 break;
216 }
217
218 // and second if any
219 idx = tmp.front(); tmp.pop();
220 std::shared_ptr<ind_t> b = population_m->get_staging(idx);
221
222 // create new individuals
223
224 // temporary copy in case not successful
225 std::shared_ptr<ind_t> copyA(new ind_t(a));
226 std::shared_ptr<ind_t> copyB(new ind_t(b));
227
228 int iter = 0;
229 while (true) {
230 // assign with shared pointer constructor
231 *a = copyA;
232 *b = copyB;
233
234 // do recombination
236 this->crossover(a, b, args_);
237 }
238
239 // do mutation
240 if (drand(1) <= mutationProbability_m) {
241 this->mutate(a, args_);
242 this->mutate(b, args_);
243 }
244
245 // check if viable offspring
246 bool viableA = a->viable();
247 bool viableB = b->viable();
248 if (viableA == true && viableB == true) {
249 break;
250 }
251 std::cout << "Individuals not viable, I try again: iter= " << iter << std::endl;
252 iter++;
253 // if maximum number of tries then create new individual(s)
254 if (iter > 100) {
255 if (viableA == false) {
256 infeasible(a);
257 }
258 if (viableB == false) {
259 infeasible(b);
260 }
261 break;
262 }
263 }
264 }
265 }
266
267
268protected:
269
272 std::shared_ptr<ind_t> ind(new ind_t(dVarBounds_m, dNames_m, constraints_m));
273 std::swap(ind->genes_m, dvars);
274 individualsToEvaluate_m.push( population_m->add_individual(ind) );
275 }
276
279 std::shared_ptr<ind_t> ind(new ind_t(dVarBounds_m, dNames_m, constraints_m));
280 individualsToEvaluate_m.push( population_m->add_individual(ind) );
281 }
282
284 void new_individual(std::shared_ptr<ind_t> ind) {
285 std::shared_ptr<ind_t> return_ind(new ind_t(ind));
287 population_m->add_individual(return_ind) ) ;
288 }
289
290private:
291
293 std::shared_ptr< Population<ind_t> > population_m;
294
297
299 std::queue<unsigned int> individualsToEvaluate_m;
300
302 std::vector<std::string> dNames_m;
307
312
318 double drand(double range) {
319 return (range * (double) rand() / (RAND_MAX + 1.0));
320 }
321};
322
323#endif
std::complex< double > a
std::shared_ptr< CmdArguments > CmdArguments_t
std::map< std::string, Expressions::Expr_t * > Named_t
type of an expressions with a name
Definition Expression.h:74
std::vector< double > genes_t
representation of genes
Definition Individual.h:41
void new_individual()
create a new individual
Definition Variator.h:278
~Variator()
Definition Variator.h:83
double drand(double range)
Definition Variator.h:318
std::shared_ptr< Population< ind_t > > population()
Definition Variator.h:87
void infeasible(std::shared_ptr< ind_t > ind)
set an individual as infeasible: replace with a new individual
Definition Variator.h:156
void new_individual(Individual::genes_t &dvars)
create a new individual
Definition Variator.h:271
void variate(std::vector< unsigned int > parents)
Definition Variator.h:178
void new_individual(std::shared_ptr< ind_t > ind)
copy an individual
Definition Variator.h:284
Variator(std::vector< std::string > dNames, Optimizer::bounds_t dVarBounds, Expressions::Named_t constraints, CmdArguments_t args)
Definition Variator.h:48
bool hasMoreIndividualsToEvaluate()
returns false if all individuals have been evaluated
Definition Variator.h:162
std::shared_ptr< Population< Individual_t > > population_m
Definition Variator.h:293
std::shared_ptr< ind_t > popIndividualToEvaluate()
return next individual to evaluate
Definition Variator.h:167
void initial_population(size_t sizeInitial, std::string fname)
create an initial population
Definition Variator.h:92
std::vector< std::pair< double, double > > bounds_t
type of bounds for design variables
Definition Optimizer.h:39
T getArg(const std::string name, bool isFatal=false)