Coverage Report

Created: 2025-11-04 06:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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