/src/quantlib/ql/math/optimization/differentialevolution.hpp
Line | Count | Source |
1 | | /* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ |
2 | | |
3 | | /* |
4 | | Copyright (C) 2012 Ralph Schreyer |
5 | | Copyright (C) 2012 Mateusz Kapturski |
6 | | |
7 | | This file is part of QuantLib, a free-software/open-source library |
8 | | for financial quantitative analysts and developers - http://quantlib.org/ |
9 | | |
10 | | QuantLib is free software: you can redistribute it and/or modify it |
11 | | under the terms of the QuantLib license. You should have received a |
12 | | copy of the license along with this program; if not, please email |
13 | | <quantlib-dev@lists.sf.net>. The license is also available online at |
14 | | <https://www.quantlib.org/license.shtml>. |
15 | | |
16 | | This program is distributed in the hope that it will be useful, but WITHOUT |
17 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
18 | | FOR A PARTICULAR PURPOSE. See the license for more details. |
19 | | */ |
20 | | |
21 | | /*! \file differentialevolution.hpp |
22 | | \brief Differential Evolution optimization method |
23 | | */ |
24 | | |
25 | | #ifndef quantlib_optimization_differential_evolution_hpp |
26 | | #define quantlib_optimization_differential_evolution_hpp |
27 | | |
28 | | #include <ql/math/optimization/constraint.hpp> |
29 | | #include <ql/math/optimization/problem.hpp> |
30 | | #include <ql/math/randomnumbers/mt19937uniformrng.hpp> |
31 | | |
32 | | namespace QuantLib { |
33 | | |
34 | | //! Differential Evolution configuration object |
35 | | /*! The algorithm and strategy names are taken from here: |
36 | | |
37 | | Price, K., Storn, R., 1997. Differential Evolution - |
38 | | A Simple and Efficient Heuristic for Global Optimization |
39 | | over Continuous Spaces. |
40 | | Journal of Global Optimization, Kluwer Academic Publishers, |
41 | | 1997, Vol. 11, pp. 341 - 359. |
42 | | |
43 | | There are seven basic strategies for creating mutant population |
44 | | currently implemented. Three basic crossover types are also |
45 | | available. |
46 | | |
47 | | Future development: |
48 | | 1) base element type to be extracted |
49 | | 2) L differences to be used instead of fixed number |
50 | | 3) various weights distributions for the differences (dither etc.) |
51 | | 4) printFullInfo parameter usage to track the algorithm |
52 | | |
53 | | \warning This was reported to fail tests on Mac OS X 10.8.4. |
54 | | */ |
55 | | |
56 | | |
57 | | //! %OptimizationMethod using Differential Evolution algorithm |
58 | | /*! \ingroup optimizers */ |
59 | | class DifferentialEvolution: public OptimizationMethod { |
60 | | public: |
61 | | enum Strategy { |
62 | | Rand1Standard, |
63 | | BestMemberWithJitter, |
64 | | CurrentToBest2Diffs, |
65 | | Rand1DiffWithPerVectorDither, |
66 | | Rand1DiffWithDither, |
67 | | EitherOrWithOptimalRecombination, |
68 | | Rand1SelfadaptiveWithRotation |
69 | | }; |
70 | | enum CrossoverType { |
71 | | Normal, |
72 | | Binomial, |
73 | | Exponential |
74 | | }; |
75 | | |
76 | | struct Candidate { |
77 | | Array values; |
78 | | Real cost = 0.0; |
79 | 0 | Candidate(Size size = 0) : values(size, 0.0) {} |
80 | | }; |
81 | | |
82 | | class Configuration { |
83 | | public: |
84 | | Strategy strategy = BestMemberWithJitter; |
85 | | CrossoverType crossoverType = Normal; |
86 | | Size populationMembers = 100; |
87 | | Real stepsizeWeight = 0.2, crossoverProbability = 0.9; |
88 | | unsigned long seed = 0; |
89 | | bool applyBounds = true, crossoverIsAdaptive = false; |
90 | | std::vector<Array> initialPopulation; |
91 | | Array upperBound, lowerBound; |
92 | | |
93 | | // Clang seems to have problems if we use '= default' here. |
94 | | // NOLINTNEXTLINE(modernize-use-equals-default) |
95 | 0 | Configuration() {} |
96 | | |
97 | 0 | Configuration& withBounds(bool b = true) { |
98 | 0 | applyBounds = b; |
99 | 0 | return *this; |
100 | 0 | } |
101 | | |
102 | 0 | Configuration& withCrossoverProbability(Real p) { |
103 | 0 | QL_REQUIRE(p>=0.0 && p<=1.0, |
104 | 0 | "Crossover probability (" << p |
105 | 0 | << ") must be in [0,1] range"); |
106 | 0 | crossoverProbability = p; |
107 | 0 | return *this; |
108 | 0 | } |
109 | | |
110 | 0 | Configuration& withPopulationMembers(Size n) { |
111 | 0 | QL_REQUIRE(n>0, "Positive number of population members required"); |
112 | 0 | populationMembers = n; |
113 | 0 | initialPopulation.clear(); |
114 | 0 | return *this; |
115 | 0 | } |
116 | | |
117 | 0 | Configuration& withInitialPopulation(const std::vector<Array>& c) { |
118 | 0 | initialPopulation = c; |
119 | 0 | populationMembers = c.size(); |
120 | 0 | return *this; |
121 | 0 | } |
122 | | |
123 | 0 | Configuration& withUpperBound(const Array& u) { |
124 | 0 | upperBound = u; |
125 | 0 | return *this; |
126 | 0 | } |
127 | | |
128 | 0 | Configuration& withLowerBound(const Array& l) { |
129 | 0 | lowerBound = l; |
130 | 0 | return *this; |
131 | 0 | } |
132 | | |
133 | 0 | Configuration& withSeed(unsigned long s) { |
134 | 0 | seed = s; |
135 | 0 | return *this; |
136 | 0 | } |
137 | | |
138 | 0 | Configuration& withAdaptiveCrossover(bool b = true) { |
139 | 0 | crossoverIsAdaptive = b; |
140 | 0 | return *this; |
141 | 0 | } |
142 | | |
143 | 0 | Configuration& withStepsizeWeight(Real w) { |
144 | 0 | QL_ENSURE(w>=0 && w<=2.0, |
145 | 0 | "Step size weight ("<< w |
146 | 0 | << ") must be in [0,2] range"); |
147 | 0 | stepsizeWeight = w; |
148 | 0 | return *this; |
149 | 0 | } |
150 | | |
151 | 0 | Configuration& withCrossoverType(CrossoverType t) { |
152 | 0 | crossoverType = t; |
153 | 0 | return *this; |
154 | 0 | } |
155 | | |
156 | 0 | Configuration& withStrategy(Strategy s) { |
157 | 0 | strategy = s; |
158 | 0 | return *this; |
159 | 0 | } |
160 | | }; |
161 | | |
162 | | |
163 | | DifferentialEvolution(const Configuration& configuration = Configuration()) |
164 | 0 | : configuration_(configuration), rng_(configuration.seed) {} |
165 | | |
166 | | EndCriteria::Type minimize(Problem& p, const EndCriteria& endCriteria) override; |
167 | | |
168 | 0 | const Configuration& configuration() const { |
169 | 0 | return configuration_; |
170 | 0 | } |
171 | | |
172 | | private: |
173 | | Configuration configuration_; |
174 | | Array upperBound_, lowerBound_; |
175 | | mutable Array currGenSizeWeights_, currGenCrossover_; |
176 | | Candidate bestMemberEver_; |
177 | | MersenneTwisterUniformRng rng_; |
178 | | |
179 | | void fillInitialPopulation(std::vector<Candidate>& population, |
180 | | const Problem& p) const; |
181 | | |
182 | | void getCrossoverMask(std::vector<Array>& crossoverMask, |
183 | | std::vector<Array>& invCrossoverMask, |
184 | | const Array& mutationProbabilities) const; |
185 | | |
186 | | Array getMutationProbabilities( |
187 | | const std::vector<Candidate>& population) const; |
188 | | |
189 | | void adaptSizeWeights() const; |
190 | | |
191 | | void adaptCrossover() const; |
192 | | |
193 | | void calculateNextGeneration(std::vector<Candidate>& population, |
194 | | Problem& costFunction) const; |
195 | | |
196 | | Array rotateArray(Array inputArray) const; |
197 | | |
198 | | void crossover(const std::vector<Candidate>& oldPopulation, |
199 | | std::vector<Candidate> & population, |
200 | | const std::vector<Candidate>& mutantPopulation, |
201 | | const std::vector<Candidate>& mirrorPopulation, |
202 | | Problem& costFunction) const; |
203 | | }; |
204 | | |
205 | | } |
206 | | |
207 | | #endif |