Logo
Fully annotated reference manual - version 1.8.12
Loading...
Searching...
No Matches
Public Types | Public Member Functions | Private Member Functions | Private Attributes | List of all members
DifferentialEvolution_MT Class Reference

#include <qle/math/differentialevolution_mt.hpp>

+ Inheritance diagram for DifferentialEvolution_MT:
+ Collaboration diagram for DifferentialEvolution_MT:

Public Types

using Strategy = DifferentialEvolution::Strategy
 
using CrossoverType = DifferentialEvolution::CrossoverType
 
using Candidate = DifferentialEvolution::Candidate
 
using Configuration = DifferentialEvolution::Configuration
 

Public Member Functions

 DifferentialEvolution_MT (Configuration configuration=Configuration(), std::string maxTime="")
 
virtual EndCriteria::Type minimize (Problem_MT &p, const EndCriteria &endCriteria) override
 minimize the optimization problem P More...
 
const Configurationconfiguration () const
 
- Public Member Functions inherited from OptimizationMethod_MT
virtual ~OptimizationMethod_MT ()
 
virtual EndCriteria::Type minimize (Problem_MT &P, const EndCriteria &endCriteria)=0
 minimize the optimization problem P More...
 
EndCriteria::Type minimize (Problem &, const EndCriteria &) override
 

Private Member Functions

void updateCost (std::vector< Candidate > &population, Problem_MT &p) const
 
bool checkMaxTime () const
 
void fillInitialPopulation (std::vector< Candidate > &population, const Problem_MT &p) const
 
void getCrossoverMask (std::vector< Array > &crossoverMask, std::vector< Array > &invCrossoverMask, const Array &mutationProbabilities) const
 
Array getMutationProbabilities (const std::vector< Candidate > &population) const
 
void adaptSizeWeights () const
 
void adaptCrossover () const
 
void calculateNextGeneration (std::vector< Candidate > &population, Problem_MT &p) const
 
Array rotateArray (Array inputArray) const
 
void crossover (const std::vector< Candidate > &oldPopulation, std::vector< Candidate > &population, const std::vector< Candidate > &mutantPopulation, const std::vector< Candidate > &mirrorPopulation, Problem_MT &p) const
 

Private Attributes

Configuration configuration_
 
std::string maxTime_
 
Array upperBound_
 
Array lowerBound_
 
Array currGenSizeWeights_
 
Array currGenCrossover_
 
Candidate bestMemberEver_
 
MersenneTwisterUniformRng rng_
 

Detailed Description

Definition at line 54 of file differentialevolution_mt.hpp.

Member Typedef Documentation

◆ Strategy

using Strategy = DifferentialEvolution::Strategy

Definition at line 56 of file differentialevolution_mt.hpp.

◆ CrossoverType

using CrossoverType = DifferentialEvolution::CrossoverType

Definition at line 57 of file differentialevolution_mt.hpp.

◆ Candidate

using Candidate = DifferentialEvolution::Candidate

Definition at line 58 of file differentialevolution_mt.hpp.

◆ Configuration

using Configuration = DifferentialEvolution::Configuration

Definition at line 59 of file differentialevolution_mt.hpp.

Constructor & Destructor Documentation

◆ DifferentialEvolution_MT()

DifferentialEvolution_MT ( Configuration  configuration = Configuration(),
std::string  maxTime = "" 
)

Member Function Documentation

◆ minimize()

EndCriteria::Type minimize ( Problem_MT P,
const EndCriteria &  endCriteria 
)
overridevirtual

minimize the optimization problem P

Implements OptimizationMethod_MT.

Definition at line 72 of file differentialevolution_mt.cpp.

72 {
73 EndCriteria::Type ecType;
74
75 if (configuration().upperBound.empty()) {
76 upperBound_ = p.constraint().upperBound(p.currentValue());
77 } else {
78 QL_REQUIRE(configuration().upperBound.size() == p.currentValue().size(),
79 "wrong upper bound size in differential evolution configuration");
80 upperBound_ = configuration().upperBound;
81 }
82 if (configuration().lowerBound.empty()) {
83 lowerBound_ = p.constraint().lowerBound(p.currentValue());
84 } else {
85 QL_REQUIRE(configuration().lowerBound.size() == p.currentValue().size(),
86 "wrong lower bound size in differential evolution configuration");
87 lowerBound_ = configuration().lowerBound;
88 }
89
90 currGenSizeWeights_ = Array(configuration().populationMembers, configuration().stepsizeWeight);
91 currGenCrossover_ = Array(configuration().populationMembers, configuration().crossoverProbability);
92
93 std::vector<Candidate> population;
94 if (!configuration().initialPopulation.empty()) {
95 population.resize(configuration().initialPopulation.size());
96 for (Size i = 0; i < population.size(); ++i) {
97 population[i].values = configuration().initialPopulation[i];
98 QL_REQUIRE(population[i].values.size() == p.currentValue().size(),
99 "wrong values size in initial population");
100 }
101 } else {
102 population = std::vector<Candidate>(configuration().populationMembers, Candidate(p.currentValue().size()));
103 fillInitialPopulation(population, p);
104 }
105
106 updateCost(population, p);
107
108 std::partial_sort(population.begin(), population.begin() + 1, population.end(), sort_by_cost());
109 bestMemberEver_ = population.front();
110 Real fxOld = population.front().cost;
111 Size iteration = 0, stationaryPointIteration = 0;
112
113 // main loop - calculate consecutive emerging populations
114 while (!endCriteria.checkMaxIterations(iteration++, ecType) && !checkMaxTime()) {
115 calculateNextGeneration(population, p);
116 std::partial_sort(population.begin(), population.begin() + 1, population.end(), sort_by_cost());
117 if (population.front().cost < bestMemberEver_.cost)
118 bestMemberEver_ = population.front();
119 Real fxNew = population.front().cost;
120 if (endCriteria.checkStationaryFunctionValue(fxOld, fxNew, stationaryPointIteration, ecType))
121 break;
122 fxOld = fxNew;
123 };
124 p.setCurrentValue(bestMemberEver_.values);
125 p.setFunctionValue(bestMemberEver_.cost);
126 if (checkMaxTime())
127 ecType = EndCriteria::Type::Unknown;
128 return ecType;
129}
void calculateNextGeneration(std::vector< Candidate > &population, Problem_MT &p) const
DifferentialEvolution::Candidate Candidate
void fillInitialPopulation(std::vector< Candidate > &population, const Problem_MT &p) const
void updateCost(std::vector< Candidate > &population, Problem_MT &p) const
+ Here is the call graph for this function:

◆ configuration()

const Configuration & configuration ( ) const

Definition at line 66 of file differentialevolution_mt.hpp.

66{ return configuration_; }
+ Here is the caller graph for this function:

◆ updateCost()

void updateCost ( std::vector< Candidate > &  population,
Problem_MT p 
) const
private

Definition at line 304 of file differentialevolution_mt.cpp.

304 {
305 struct Worker {
306 Worker(std::vector<Candidate>& p, const Size s, const Size e, const QuantLib::ext::shared_ptr<CostFunction> c)
307 : population(p), start(s), end(e), costFunction(c) {}
308 void operator()() {
309 for (Size popIter = start; popIter < end; popIter++) {
310 // evaluate objective function as soon as possible to avoid unnecessary loops
311 try {
312 population[popIter].cost = costFunction->value(population[popIter].values);
313 } catch (Error&) {
314 population[popIter].cost = QL_MAX_REAL;
315 }
316 if (!std::isfinite(population[popIter].cost))
317 population[popIter].cost = QL_MAX_REAL;
318 }
319 }
320 std::vector<Candidate>& population;
321 const Size start, end;
322 const QuantLib::ext::shared_ptr<CostFunction> costFunction;
323 };
324
325 Size threads = p.costFunctions().size();
326 QL_REQUIRE(threads > 0, "DifferentialEvolution_MT: number of available threads is zero");
327
328 std::vector<Size> chunks(threads, std::max<Size>(population.size() / threads, 1));
329 int rest = population.size() - threads * chunks[0];
330 do {
331 Size i = 0;
332 while (i < chunks.size() && rest > 0) {
333 chunks[i]++;
334 rest--;
335 ++i;
336 }
337 } while (rest > 0);
338
339 std::vector<QuantLib::ext::shared_ptr<std::thread>> workers(threads);
340
341 Size start = 0, end;
342 for (Size thread = 0; thread < threads; ++thread) {
343 end = std::min(start + chunks[thread], population.size());
344 Worker worker(population, start, end, p.costFunctions()[thread]);
345 workers[thread] = QuantLib::ext::make_shared<std::thread>(worker);
346 start = end;
347 }
348
349 for (Size thread = 0; thread < threads; ++thread) {
350 workers[thread]->join();
351 }
352}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ checkMaxTime()

bool checkMaxTime ( ) const
private

Definition at line 64 of file differentialevolution_mt.cpp.

64 {
65 if (maxTime_.empty())
66 return false;
67 QL_REQUIRE(maxTime_.length() == 15, "maxTime (" << maxTime_ << ") must have format YYYYMMDDTHHMMSS");
68 std::string ts = to_iso_string(boost::posix_time::microsec_clock::local_time()).substr(0, 15);
69 return ts > maxTime_;
70}
+ Here is the caller graph for this function:

◆ fillInitialPopulation()

void fillInitialPopulation ( std::vector< Candidate > &  population,
const Problem_MT p 
) const
private

Definition at line 417 of file differentialevolution_mt.cpp.

417 {
418
419 // use initial values provided by the user
420 population.front().values = p.currentValue();
421 // rest of the initial population is random
422 for (Size j = 1; j < population.size(); ++j) {
423 for (Size i = 0; i < p.currentValue().size(); ++i) {
424 Real l = lowerBound_[i], u = upperBound_[i];
425 population[j].values[i] = l + (u - l) * rng_.nextReal();
426 }
427 }
428}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ getCrossoverMask()

void getCrossoverMask ( std::vector< Array > &  crossoverMask,
std::vector< Array > &  invCrossoverMask,
const Array &  mutationProbabilities 
) const
private

Definition at line 354 of file differentialevolution_mt.cpp.

355 {
356 for (Size cmIter = 0; cmIter < crossoverMask.size(); cmIter++) {
357 for (Size memIter = 0; memIter < crossoverMask[cmIter].size(); memIter++) {
358 if (rng_.nextReal() < mutationProbabilities[cmIter]) {
359 invCrossoverMask[cmIter][memIter] = 0.0;
360 } else {
361 crossoverMask[cmIter][memIter] = 0.0;
362 }
363 }
364 }
365}
+ Here is the caller graph for this function:

◆ getMutationProbabilities()

Array getMutationProbabilities ( const std::vector< Candidate > &  population) const
private

Definition at line 367 of file differentialevolution_mt.cpp.

367 {
368 Array mutationProbabilities = currGenCrossover_;
369 switch (configuration().crossoverType) {
370 case DifferentialEvolution::Normal:
371 break;
372 case DifferentialEvolution::Binomial:
373 mutationProbabilities =
374 currGenCrossover_ * (1.0 - 1.0 / population.front().values.size()) + 1.0 / population.front().values.size();
375 break;
376 case DifferentialEvolution::Exponential:
377 for (Size coIter = 0; coIter < currGenCrossover_.size(); coIter++) {
378 mutationProbabilities[coIter] =
379 (1.0 - std::pow(currGenCrossover_[coIter], (int)population.front().values.size())) /
380 (population.front().values.size() * (1.0 - currGenCrossover_[coIter]));
381 }
382 break;
383 default:
384 QL_FAIL("Unknown crossover type (" << Integer(configuration().crossoverType) << ")");
385 break;
386 }
387 return mutationProbabilities;
388}
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ adaptSizeWeights()

void adaptSizeWeights ( ) const
private

Definition at line 395 of file differentialevolution_mt.cpp.

395 {
396 // [=Fl & =Fu] respectively see Brest, J. et al., 2006,
397 // "Self-Adapting Control Parameters in Differential
398 // Evolution"
399 Real sizeWeightLowerBound = 0.1, sizeWeightUpperBound = 0.9;
400 // [=tau1] A Comparative Study on Numerical Benchmark
401 // Problems." page 649 for reference
402 Real sizeWeightChangeProb = 0.1;
403 for (Size coIter = 0; coIter < currGenSizeWeights_.size(); coIter++) {
404 if (rng_.nextReal() < sizeWeightChangeProb)
405 currGenSizeWeights_[coIter] = sizeWeightLowerBound + rng_.nextReal() * sizeWeightUpperBound;
406 }
407}
+ Here is the caller graph for this function:

◆ adaptCrossover()

void adaptCrossover ( ) const
private

Definition at line 409 of file differentialevolution_mt.cpp.

409 {
410 Real crossoverChangeProb = 0.1; // [=tau2]
411 for (Size coIter = 0; coIter < currGenCrossover_.size(); coIter++) {
412 if (rng_.nextReal() < crossoverChangeProb)
413 currGenCrossover_[coIter] = rng_.nextReal();
414 }
415}
+ Here is the caller graph for this function:

◆ calculateNextGeneration()

void calculateNextGeneration ( std::vector< Candidate > &  population,
Problem_MT p 
) const
private

Definition at line 131 of file differentialevolution_mt.cpp.

131 {
132
133 std::vector<Candidate> mirrorPopulation;
134 std::vector<Candidate> oldPopulation = population;
135
136 switch (configuration().strategy) {
137
138 case QuantLib::DifferentialEvolution::Rand1Standard: {
139 randomize(population.begin(), population.end(), rng_);
140 std::vector<Candidate> shuffledPop1 = population;
141 randomize(population.begin(), population.end(), rng_);
142 std::vector<Candidate> shuffledPop2 = population;
143 randomize(population.begin(), population.end(), rng_);
144 mirrorPopulation = shuffledPop1;
145
146 for (Size popIter = 0; popIter < population.size(); popIter++) {
147 population[popIter].values =
148 population[popIter].values +
149 configuration().stepsizeWeight * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
150 }
151 } break;
152
153 case QuantLib::DifferentialEvolution::BestMemberWithJitter: {
154 randomize(population.begin(), population.end(), rng_);
155 std::vector<Candidate> shuffledPop1 = population;
156 randomize(population.begin(), population.end(), rng_);
157 Array jitter(population[0].values.size(), 0.0);
158
159 for (Size popIter = 0; popIter < population.size(); popIter++) {
160 for (double& jitterIter : jitter) {
161 jitterIter = rng_.nextReal();
162 }
163 population[popIter].values =
164 bestMemberEver_.values + (shuffledPop1[popIter].values - population[popIter].values) *
165 (0.0001 * jitter + configuration().stepsizeWeight);
166 }
167 mirrorPopulation = std::vector<Candidate>(population.size(), bestMemberEver_);
168 } break;
169
170 case QuantLib::DifferentialEvolution::CurrentToBest2Diffs: {
171 randomize(population.begin(), population.end(), rng_);
172 std::vector<Candidate> shuffledPop1 = population;
173 randomize(population.begin(), population.end(), rng_);
174
175 for (Size popIter = 0; popIter < population.size(); popIter++) {
176 population[popIter].values =
177 oldPopulation[popIter].values +
178 configuration().stepsizeWeight * (bestMemberEver_.values - oldPopulation[popIter].values) +
179 configuration().stepsizeWeight * (population[popIter].values - shuffledPop1[popIter].values);
180 }
181 mirrorPopulation = shuffledPop1;
182 } break;
183
184 case QuantLib::DifferentialEvolution::Rand1DiffWithPerVectorDither: {
185 randomize(population.begin(), population.end(), rng_);
186 std::vector<Candidate> shuffledPop1 = population;
187 randomize(population.begin(), population.end(), rng_);
188 std::vector<Candidate> shuffledPop2 = population;
189 randomize(population.begin(), population.end(), rng_);
190 mirrorPopulation = shuffledPop1;
191 Array FWeight = Array(population.front().values.size(), 0.0);
192 for (double& fwIter : FWeight)
193 fwIter = (1.0 - configuration().stepsizeWeight) * rng_.nextReal() + configuration().stepsizeWeight;
194 for (Size popIter = 0; popIter < population.size(); popIter++) {
195 population[popIter].values =
196 population[popIter].values + FWeight * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
197 }
198 } break;
199
200 case QuantLib::DifferentialEvolution::Rand1DiffWithDither: {
201 randomize(population.begin(), population.end(), rng_);
202 std::vector<Candidate> shuffledPop1 = population;
203 randomize(population.begin(), population.end(), rng_);
204 std::vector<Candidate> shuffledPop2 = population;
205 randomize(population.begin(), population.end(), rng_);
206 mirrorPopulation = shuffledPop1;
207 Real FWeight = (1.0 - configuration().stepsizeWeight) * rng_.nextReal() + configuration().stepsizeWeight;
208 for (Size popIter = 0; popIter < population.size(); popIter++) {
209 population[popIter].values =
210 population[popIter].values + FWeight * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
211 }
212 } break;
213
214 case QuantLib::DifferentialEvolution::EitherOrWithOptimalRecombination: {
215 randomize(population.begin(), population.end(), rng_);
216 std::vector<Candidate> shuffledPop1 = population;
217 randomize(population.begin(), population.end(), rng_);
218 std::vector<Candidate> shuffledPop2 = population;
219 randomize(population.begin(), population.end(), rng_);
220 mirrorPopulation = shuffledPop1;
221 Real probFWeight = 0.5;
222 if (rng_.nextReal() < probFWeight) {
223 for (Size popIter = 0; popIter < population.size(); popIter++) {
224 population[popIter].values =
225 oldPopulation[popIter].values +
226 configuration().stepsizeWeight * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
227 }
228 } else {
229 Real K = 0.5 * (configuration().stepsizeWeight + 1); // invariant with respect to probFWeight used
230 for (Size popIter = 0; popIter < population.size(); popIter++) {
231 population[popIter].values =
232 oldPopulation[popIter].values + K * (shuffledPop1[popIter].values - shuffledPop2[popIter].values -
233 2.0 * population[popIter].values);
234 }
235 }
236 } break;
237
238 case QuantLib::DifferentialEvolution::Rand1SelfadaptiveWithRotation: {
239 randomize(population.begin(), population.end(), rng_);
240 std::vector<Candidate> shuffledPop1 = population;
241 randomize(population.begin(), population.end(), rng_);
242 std::vector<Candidate> shuffledPop2 = population;
243 randomize(population.begin(), population.end(), rng_);
244 mirrorPopulation = shuffledPop1;
245
247
248 for (Size popIter = 0; popIter < population.size(); popIter++) {
249 if (rng_.nextReal() < 0.1) {
250 population[popIter].values = rotateArray(bestMemberEver_.values);
251 } else {
252 population[popIter].values =
253 bestMemberEver_.values +
254 currGenSizeWeights_[popIter] * (shuffledPop1[popIter].values - shuffledPop2[popIter].values);
255 }
256 }
257 } break;
258
259 default:
260 QL_FAIL("Unknown strategy (" << Integer(configuration().strategy) << ")");
261 }
262 // in order to avoid unnecessary copying we use the same population object for mutants
263 crossover(oldPopulation, population, population, mirrorPopulation, p);
264}
void crossover(const std::vector< Candidate > &oldPopulation, std::vector< Candidate > &population, const std::vector< Candidate > &mutantPopulation, const std::vector< Candidate > &mirrorPopulation, Problem_MT &p) const
Array rotateArray(Array inputArray) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ rotateArray()

Array rotateArray ( Array  inputArray) const
private

Definition at line 390 of file differentialevolution_mt.cpp.

390 {
391 randomize(a.begin(), a.end(), rng_);
392 return a;
393}
+ Here is the caller graph for this function:

◆ crossover()

void crossover ( const std::vector< Candidate > &  oldPopulation,
std::vector< Candidate > &  population,
const std::vector< Candidate > &  mutantPopulation,
const std::vector< Candidate > &  mirrorPopulation,
Problem_MT p 
) const
private

Definition at line 266 of file differentialevolution_mt.cpp.

269 {
270
271 if (configuration().crossoverIsAdaptive) {
273 }
274
275 Array mutationProbabilities = getMutationProbabilities(population);
276
277 std::vector<Array> crossoverMask(population.size(), Array(population.front().values.size(), 1.0));
278 std::vector<Array> invCrossoverMask = crossoverMask;
279 getCrossoverMask(crossoverMask, invCrossoverMask, mutationProbabilities);
280
281 // crossover of the old and mutant population
282
283 for (Size popIter = 0; popIter < population.size(); popIter++) {
284 population[popIter].values = oldPopulation[popIter].values * invCrossoverMask[popIter] +
285 mutantPopulation[popIter].values * crossoverMask[popIter];
286 // immediately apply bounds if specified
287 if (configuration().applyBounds) {
288 for (Size memIter = 0; memIter < population[popIter].values.size(); memIter++) {
289 if (population[popIter].values[memIter] > upperBound_[memIter])
290 population[popIter].values[memIter] =
291 upperBound_[memIter] +
292 rng_.nextReal() * (mirrorPopulation[popIter].values[memIter] - upperBound_[memIter]);
293 if (population[popIter].values[memIter] < lowerBound_[memIter])
294 population[popIter].values[memIter] =
295 lowerBound_[memIter] +
296 rng_.nextReal() * (mirrorPopulation[popIter].values[memIter] - lowerBound_[memIter]);
297 }
298 }
299 }
300
301 updateCost(population, p);
302}
void getCrossoverMask(std::vector< Array > &crossoverMask, std::vector< Array > &invCrossoverMask, const Array &mutationProbabilities) const
Array getMutationProbabilities(const std::vector< Candidate > &population) const
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Member Data Documentation

◆ configuration_

Configuration configuration_
private

Definition at line 71 of file differentialevolution_mt.hpp.

◆ maxTime_

std::string maxTime_
private

Definition at line 72 of file differentialevolution_mt.hpp.

◆ upperBound_

Array upperBound_
private

Definition at line 73 of file differentialevolution_mt.hpp.

◆ lowerBound_

Array lowerBound_
private

Definition at line 73 of file differentialevolution_mt.hpp.

◆ currGenSizeWeights_

Array currGenSizeWeights_
mutableprivate

Definition at line 74 of file differentialevolution_mt.hpp.

◆ currGenCrossover_

Array currGenCrossover_
private

Definition at line 74 of file differentialevolution_mt.hpp.

◆ bestMemberEver_

Candidate bestMemberEver_
private

Definition at line 75 of file differentialevolution_mt.hpp.

◆ rng_

MersenneTwisterUniformRng rng_
private

Definition at line 76 of file differentialevolution_mt.hpp.