QuantLib: a free/open-source library for quantitative finance
fully annotated source code - version 1.34
Loading...
Searching...
No Matches
hybridsimulatedannealingfunctors.hpp
Go to the documentation of this file.
1/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3/*
4Copyright (C) 2015 Andres Hernandez
5
6This file is part of QuantLib, a free-software/open-source library
7for financial quantitative analysts and developers - http://quantlib.org/
8
9QuantLib is free software: you can redistribute it and/or modify it
10under the terms of the QuantLib license. You should have received a
11copy of the license along with this program; if not, please email
12<quantlib-dev@lists.sf.net>. The license is also available online at
13<http://quantlib.org/license.shtml>.
14
15This program is distributed in the hope that it will be useful, but WITHOUT
16ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17FOR A PARTICULAR PURPOSE. See the license for more details.
18*/
19
20/*! \file hybridsimulatedannealingfunctors.hpp
21\brief Functors for use on HybridSimulatedAnnealing
22*/
23
24#ifndef HYBRIDSIMULATEDANNEALINGFUNCTORS_H
25#define HYBRIDSIMULATEDANNEALINGFUNCTORS_H
26
27#include <ql/math/array.hpp>
30
31#include <algorithm>
32#include <cmath>
33#include <random>
34#include <utility>
35#include <vector>
36
37namespace QuantLib
38{
39 //! Lognormal Sampler
40 /*! Sample from lognormal distribution. This means that the parameter space
41 must have support on the positve side of the real line only.
42 */
44 {
45 public:
46 explicit SamplerLogNormal(unsigned long seed = SeedGenerator::instance().get()) :
47 generator_(seed), distribution_(0.0, 1.0) {};
48
49 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
50 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
51 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
52 for (Size i = 0; i < currentPoint.size(); i++)
53 newPoint[i] = currentPoint[i] * exp(sqrt(temp[i]) * distribution_(generator_));
54 };
55 private:
56 std::mt19937 generator_;
57 std::normal_distribution<Real> distribution_;
58 };
59
60 //! Gaussian Sampler
61 /*! Sample from normal distribution. This means that the parameter space
62 must have support on the whole real line.
63 */
65 {
66 public:
67 explicit SamplerGaussian(unsigned long seed = SeedGenerator::instance().get()) :
68 generator_(seed), distribution_(0.0, 1.0) {};
69
70 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
71 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
72 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
73 for (Size i = 0; i < currentPoint.size(); i++)
74 newPoint[i] = currentPoint[i] + std::sqrt(temp[i]) * distribution_(generator_);
75 };
76 private:
77 std::mt19937 generator_;
78 std::normal_distribution<Real> distribution_;
79 };
80
81 //! Gaussian Ring Sampler
82 /*! Sample from normal distribution, but constrained to lie within
83 * .boundaries. If the value ends up beyond the boundary, the value
84 * is circled back from the other side.
85 */
87 {
88 public:
90 Array upper,
91 unsigned long seed = SeedGenerator::instance().get())
92 : generator_(seed), distribution_(0.0, 1.0),
93 lower_(std::move(lower)), upper_(std::move(upper)){};
94
95 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
96 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
97 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
98 for (Size i = 0; i < currentPoint.size(); i++){
99 newPoint[i] = currentPoint[i] + std::sqrt(temp[i]) * distribution_(generator_);
100 while(newPoint[i] < lower_[i] || newPoint[i] > upper_[i]){
101 if(newPoint[i] < lower_[i]){
102 newPoint[i] = upper_[i] + newPoint[i] - lower_[i];
103 } else {
104 newPoint[i] = lower_[i] + newPoint[i] - upper_[i];
105 }
106 }
107 }
108 };
109 private:
110 std::mt19937 generator_;
111 std::normal_distribution<Real> distribution_;
113 };
114
115 //! Gaussian Mirror Sampler
116 /*! Sample from normal distribution, but constrained to lie within
117 * .boundaries. If the value ends up beyond the boundary, the value
118 * is reflected back.
119 */
121 {
122 public:
124 Array upper,
125 unsigned long seed = SeedGenerator::instance().get())
126 : generator_(seed), distribution_(0.0, 1.0),
127 lower_(std::move(lower)), upper_(std::move(upper)){};
128
129 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
130 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
131 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
132 for (Size i = 0; i < currentPoint.size(); i++){
133 newPoint[i] = currentPoint[i] + std::sqrt(temp[i]) * distribution_(generator_);
134 while(newPoint[i] < lower_[i] || newPoint[i] > upper_[i]){
135 if(newPoint[i] < lower_[i]){
136 newPoint[i] = lower_[i] + lower_[i] - newPoint[i];
137 } else {
138 newPoint[i] = upper_[i] + upper_[i] - newPoint[i];
139 }
140 }
141 }
142 };
143 private:
144 std::mt19937 generator_;
145 std::normal_distribution<Real> distribution_;
147 };
148
149 //! Cauchy Sampler
150 /*! Sample from cauchy distribution. This means that the parameter space
151 must have support on the positive whole real line. For lower dimensions
152 it could be faster than the Gaussian sampler, specially when combined
153 with the Cauchy temperature.
154 */
156 {
157 public:
158 explicit SamplerCauchy(unsigned long seed = SeedGenerator::instance().get()) :
159 generator_(seed), distribution_(0.0, 1.0) {};
160
161 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
162 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
163 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
164 for (Size i = 0; i < currentPoint.size(); i++)
165 newPoint[i] = currentPoint[i] + temp[i] * distribution_(generator_);
166 };
167 protected:
168 std::mt19937 generator_;
169 std::cauchy_distribution<Real> distribution_;
170 };
171
172 //! Very Fast Annealing Sampler
173 /*! For consistency should be used with TemperatureVeryFastAnnealing.
174 Requires that the parameter space be bounded above and below.
175 */
177 {
178 public:
180 Array upper,
181 unsigned long seed = SeedGenerator::instance().get())
182 : lower_(std::move(lower)), upper_(std::move(upper)), generator_(seed) {
183 QL_REQUIRE(lower_.size() == upper_.size(), "Incompatible input");
184 };
185
186 inline void operator()(Array &newPoint, const Array &currentPoint, const Array &temp) {
187 QL_REQUIRE(newPoint.size() == currentPoint.size(), "Incompatible input");
188 QL_REQUIRE(newPoint.size() == lower_.size(), "Incompatible input");
189 QL_REQUIRE(newPoint.size() == temp.size(), "Incompatible input");
190 for (Size i = 0; i < currentPoint.size(); i++) {
191 newPoint[i] = lower_[i] - 1.0;
192 while (newPoint[i] < lower_[i] || newPoint[i] > upper_[i]) {
194 Real sign = static_cast<int>(0.5 < draw) - static_cast<int>(draw < 0.5);
195 Real y = sign*temp[i] * (std::pow(1.0 + 1.0 / temp[i],
196 std::abs(2 * draw - 1.0)) - 1.0);
197 newPoint[i] = currentPoint[i] + y*(upper_[i] - lower_[i]);
198 }
199 }
200 };
201 private:
203 std::mt19937 generator_;
204 std::uniform_real_distribution<Real> distribution_;
205 };
206
207 //! Always Downhill Probability
208 /*! Only points that improve on the current solution are accepted.
209 Depending on the problem, this makes it very unlikely that the
210 optimizer will be able to escape a local optimum.
211 */
213 inline bool operator()(Real currentValue, Real newValue, const Array &temp) {
214 return currentValue > newValue; //return true if new value is lower than old value
215 }
216 };
217
218 //! Boltzmann Probability
219 /*! The probability of accepting a new point is sampled from a Boltzmann distribution.
220 A point is accepted if \f$ \frac{1}{1+exp(-(current-new)/T)} > u \f$
221 where \f$ u \f$ is drawn from a uniform distribution.
222 */
224 public:
225 explicit ProbabilityBoltzmann(unsigned long seed = SeedGenerator::instance().get()) : generator_(seed) {};
226
227 inline bool operator()(Real currentValue, Real newValue, const Array &temp) {
228 Real temperature = *std::max_element(temp.begin(), temp.end());
229 return (1.0 / (1.0 + exp((newValue - currentValue) / temperature))) > distribution_(generator_);
230 }
231 private:
232 std::mt19937 generator_;
233 std::uniform_real_distribution<Real> distribution_;
234 };
235 //! Boltzmann Downhill Probability
236 /*! Similarly to the Boltzmann Probability, but if new < current, then the point is
237 always accepted.
238 */
240 {
241 public:
242 explicit ProbabilityBoltzmannDownhill(unsigned long seed = SeedGenerator::instance().get()) : generator_(seed) {};
243
244 inline bool operator()(Real currentValue, Real newValue, const Array &temp) {
245 if (newValue < currentValue)
246 return true;
247 Real mTemperature = *std::max_element(temp.begin(), temp.end());
248 return (1.0 / (1.0 + exp((newValue - currentValue) / mTemperature))) > distribution_(generator_);
249 }
250 private:
251 std::mt19937 generator_;
252 std::uniform_real_distribution<Real> distribution_;
253 };
254 //! Temperature Boltzmann
255 /*! For use with the Gaussian sampler
256 */
258 public:
259 TemperatureBoltzmann(Real initialTemp, Size dimension)
260 : initialTemp_(dimension, initialTemp) {}
261 inline void operator()(Array &newTemp, const Array &currTemp, const Array &steps) {
262 QL_REQUIRE(currTemp.size() == initialTemp_.size(), "Incompatible input");
263 QL_REQUIRE(currTemp.size() == newTemp.size(), "Incompatible input");
264 for (Size i = 0; i < initialTemp_.size(); i++)
265 newTemp[i] = initialTemp_[i] / std::log(steps[i]);
266 }
267 private:
269 };
270 //! Temperature Cauchy
271 /*! For use with the Cauchy sampler
272 */
274 public:
275 TemperatureCauchy(Real initialTemp, Size dimension)
276 : initialTemp_(dimension, initialTemp) {}
277 inline void operator()(Array &newTemp, const Array &currTemp, const Array &steps) {
278 QL_REQUIRE(currTemp.size() == initialTemp_.size(), "Incompatible input");
279 QL_REQUIRE(currTemp.size() == newTemp.size(), "Incompatible input");
280 for (Size i = 0; i < initialTemp_.size(); i++)
281 newTemp[i] = initialTemp_[i] / steps[i];
282 }
283 private:
285 };
286
288 public:
289 TemperatureCauchy1D(Real initialTemp, Size dimension) :
290 inverseN_(1.0 / dimension),
291 initialTemp_(dimension, initialTemp) {}
292 inline void operator()(Array &newTemp, const Array &currTemp, const Array &steps) {
293 QL_REQUIRE(currTemp.size() == initialTemp_.size(), "Incompatible input");
294 QL_REQUIRE(currTemp.size() == newTemp.size(), "Incompatible input");
295 for (Size i = 0; i < initialTemp_.size(); i++)
296 newTemp[i] = initialTemp_[i] / std::pow(steps[i], inverseN_);
297 }
298 private:
301 };
302
304 public:
305 TemperatureExponential(Real initialTemp, Size dimension, Real power = 0.95)
306 : initialTemp_(dimension, initialTemp), power_(power) {}
307 inline void operator()(Array &newTemp, const Array &currTemp, const Array &steps) {
308 QL_REQUIRE(currTemp.size() == initialTemp_.size(), "Incompatible input");
309 QL_REQUIRE(currTemp.size() == newTemp.size(), "Incompatible input");
310 for (Size i = 0; i < initialTemp_.size(); i++)
311 newTemp[i] = initialTemp_[i] * std::pow(power_, steps[i]);
312 }
313 private:
316 };
317 //! Temperature Very Fast Annealing
318 /*! For use with the Very Fast Annealing sampler
319 */
321 public:
322 TemperatureVeryFastAnnealing(Real initialTemp, Real finalTemp, Real maxSteps, Size dimension)
323 :inverseN_(1.0 / dimension), initialTemp_(dimension, initialTemp),
324 finalTemp_(dimension, finalTemp), exponent_(dimension, 0.0) {
325 Real coeff = std::pow(maxSteps, -inverseN_);
326 for (Size i = 0; i < initialTemp_.size(); i++)
327 exponent_[i] = -std::log(finalTemp_[i] / initialTemp_[i])*coeff;
328 }
329 inline void operator()(Array &newTemp, const Array &currTemp, const Array &steps) {
330 QL_REQUIRE(currTemp.size() == initialTemp_.size(), "Incompatible input");
331 QL_REQUIRE(currTemp.size() == newTemp.size(), "Incompatible input");
332 for (Size i = 0; i < initialTemp_.size(); i++)
333 newTemp[i] = initialTemp_[i] * exp(-exponent_[i] * std::pow(steps[i], inverseN_));
334 }
335 private:
338 };
339 //! Reannealing Trivial
340 /*! No reannealing is performed
341 */
344 ;
345 inline void setProblem(Problem &P) {};
346 inline void operator()(Array & steps, const Array &currentPoint,
347 Real aCurrentValue, const Array & currTemp) {};
348 };
349 //! Reannealing Finite Difference
350 /*! In multidimensional problems, different dimensions might have different
351 sensitivities, and might have dimensions on which the solution is rather
352 insensitive. If possible, the search should concentrate more on the more
353 sensitive dimensions, therefore a reannealing schedule might raise the
354 temperature seen by those more fruitful dimensions so as to allow for more
355 movement along the dimensions of interest
356 */
358 public:
360 Size dimension,
361 const Array& lower = Array(),
362 const Array& upper = Array(),
363 Real stepSize = 1e-7,
364 Real minSize = 1e-10,
365 Real functionTol = 1e-10)
366 : stepSize_(stepSize), minSize_(minSize), functionTol_(functionTol), N_(dimension),
367 lower_(lower), upper_(upper), initialTemp_(dimension, initialTemp),
368 bounded_(dimension, 1.0) {
369 if (!lower.empty() && !upper.empty()) {
370 QL_REQUIRE(lower.size() == N_, "Incompatible input");
371 QL_REQUIRE(upper.size() == N_, "Incompatible input");
372 bound_ = true;
373 for (Size i = 0; i < N_; i++) {
374 bounded_[i] = upper[i] - lower[i];
375 }
376 }
377 }
378 inline void setProblem(Problem &P) { problem_ = &P; };
379 inline void operator()(Array & steps, const Array &currentPoint,
380 Real currentValue, const Array & currTemp) {
381 QL_REQUIRE(currTemp.size() == N_, "Incompatible input");
382 QL_REQUIRE(steps.size() == N_, "Incompatible input");
383
384 Array finiteDiffs(N_, 0.0);
385 Real finiteDiffMax = 0.0;
386 Array ofssetPoint(currentPoint);
387 for (Size i = 0; i < N_; i++) {
388 ofssetPoint[i] += stepSize_;
389 finiteDiffs[i] = bounded_[i] * std::abs((problem_->value(ofssetPoint) - currentValue) / stepSize_);
390 ofssetPoint[i] -= stepSize_;
391 if (finiteDiffs[i] < minSize_)
392 finiteDiffs[i] = minSize_;
393 if (finiteDiffs[i] > finiteDiffMax)
394 finiteDiffMax = finiteDiffs[i];
395 }
396 for (Size i = 0; i < N_; i++) {
397 Real tRatio = initialTemp_[i] / currTemp[i];
398 Real sRatio = finiteDiffMax / finiteDiffs[i];
399 if (sRatio*tRatio < functionTol_)
400 steps[i] = std::pow(std::fabs(std::log(functionTol_)),
401 Integer(N_));
402 else
403 steps[i] = std::pow(std::fabs(std::log(sRatio*tRatio)),
404 Integer(N_));
405 }
406 }
407 private:
411 bool bound_ = false;
413 };
414}
415#endif // HYBRIDSIMULATEDANNEALINGFUNCTORS_H
1-D array used in linear algebra.
1-D array used in linear algebra.
Definition: array.hpp:52
bool empty() const
whether the array is empty
Definition: array.hpp:499
const_iterator end() const
Definition: array.hpp:511
Size size() const
dimension of the array
Definition: array.hpp:495
const_iterator begin() const
Definition: array.hpp:503
std::uniform_real_distribution< Real > distribution_
bool operator()(Real currentValue, Real newValue, const Array &temp)
ProbabilityBoltzmannDownhill(unsigned long seed=SeedGenerator::instance().get())
std::uniform_real_distribution< Real > distribution_
bool operator()(Real currentValue, Real newValue, const Array &temp)
ProbabilityBoltzmann(unsigned long seed=SeedGenerator::instance().get())
Constrained optimization problem.
Definition: problem.hpp:42
Real value(const Array &x)
call cost function computation and increment evaluation counter
Definition: problem.hpp:116
ReannealingFiniteDifferences(Real initialTemp, Size dimension, const Array &lower=Array(), const Array &upper=Array(), Real stepSize=1e-7, Real minSize=1e-10, Real functionTol=1e-10)
void operator()(Array &steps, const Array &currentPoint, Real currentValue, const Array &currTemp)
SamplerCauchy(unsigned long seed=SeedGenerator::instance().get())
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
std::cauchy_distribution< Real > distribution_
std::normal_distribution< Real > distribution_
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
SamplerGaussian(unsigned long seed=SeedGenerator::instance().get())
SamplerLogNormal(unsigned long seed=SeedGenerator::instance().get())
std::normal_distribution< Real > distribution_
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
SamplerMirrorGaussian(Array lower, Array upper, unsigned long seed=SeedGenerator::instance().get())
std::normal_distribution< Real > distribution_
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
std::normal_distribution< Real > distribution_
SamplerRingGaussian(Array lower, Array upper, unsigned long seed=SeedGenerator::instance().get())
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
std::uniform_real_distribution< Real > distribution_
SamplerVeryFastAnnealing(Array lower, Array upper, unsigned long seed=SeedGenerator::instance().get())
void operator()(Array &newPoint, const Array &currentPoint, const Array &temp)
static SeedGenerator & instance()
access to the unique instance
Definition: singleton.hpp:104
void operator()(Array &newTemp, const Array &currTemp, const Array &steps)
TemperatureBoltzmann(Real initialTemp, Size dimension)
void operator()(Array &newTemp, const Array &currTemp, const Array &steps)
TemperatureCauchy1D(Real initialTemp, Size dimension)
void operator()(Array &newTemp, const Array &currTemp, const Array &steps)
TemperatureCauchy(Real initialTemp, Size dimension)
void operator()(Array &newTemp, const Array &currTemp, const Array &steps)
TemperatureExponential(Real initialTemp, Size dimension, Real power=0.95)
void operator()(Array &newTemp, const Array &currTemp, const Array &steps)
TemperatureVeryFastAnnealing(Real initialTemp, Real finalTemp, Real maxSteps, Size dimension)
#define QL_REQUIRE(condition, message)
throw an error if the given pre-condition is not verified
Definition: errors.hpp:117
QL_REAL Real
real number
Definition: types.hpp:50
QL_INTEGER Integer
integer number
Definition: types.hpp:35
std::size_t Size
size of a container
Definition: types.hpp:58
Definition: any.hpp:35
STL namespace.
Abstract optimization problem class.
Random seed generator.
bool operator()(Real currentValue, Real newValue, const Array &temp)
void operator()(Array &steps, const Array &currentPoint, Real aCurrentValue, const Array &currTemp)