Coverage Report

Created: 2026-02-03 07:02

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/quantlib/ql/math/optimization/simplex.cpp
Line
Count
Source
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2001, 2002, 2003 Sadruddin Rejeb
5
 Copyright (C) 2006 Ferdinando Ametrano
6
 Copyright (C) 2007 Marco Bianchetti
7
 Copyright (C) 2007 François du Vignaud
8
9
 This file is part of QuantLib, a free-software/open-source library
10
 for financial quantitative analysts and developers - http://quantlib.org/
11
12
 QuantLib is free software: you can redistribute it and/or modify it
13
 under the terms of the QuantLib license.  You should have received a
14
 copy of the license along with this program; if not, please email
15
 <quantlib-dev@lists.sf.net>. The license is also available online at
16
 <https://www.quantlib.org/license.shtml>.
17
18
 This program is distributed in the hope that it will be useful, but WITHOUT
19
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
20
 FOR A PARTICULAR PURPOSE.  See the license for more details.
21
*/
22
23
/* The implementation of the algorithm was highly inspired by
24
 * "Numerical Recipes in C", 2nd edition, Press, Teukolsky, Vetterling,
25
 * Flannery, chapter 10.
26
 * Modified may 2007: end criteria set on x instead on fx,
27
 * inspired by bad behaviour found with test function fx=x*x+x+1,
28
 * xStart = -100, lambda = 1.0, ftol = 1.e-16
29
 * (it reports x=0 as the minimum!)
30
 * and by GSL implementation, v. 1.9 (http://www.gnu.org/software/gsl/)
31
 */
32
33
#include <ql/math/optimization/simplex.hpp>
34
#include <ql/math/optimization/constraint.hpp>
35
36
namespace QuantLib {
37
38
    namespace {
39
    // Computes the size of the simplex
40
0
        Real computeSimplexSize (const std::vector<Array>& vertices) {
41
0
            Array center(vertices.front().size(),0);
42
0
            for (const auto& vertice : vertices)
43
0
                center += vertice;
44
0
            center *=1/Real(vertices.size());
45
0
            Real result = 0;
46
0
            for (const auto& vertice : vertices) {
47
0
                Array temp = vertice - center;
48
0
                result += Norm2(temp);
49
0
            }
50
0
            return result/Real(vertices.size());
51
0
        }
52
    }
53
54
    Real Simplex::extrapolate(Problem& P,
55
                              Size iHighest,
56
0
                              Real &factor) const {
57
58
0
        Array pTry;
59
0
        do {
60
0
            Size dimensions = values_.size() - 1;
61
0
            Real factor1 = (1.0 - factor)/dimensions;
62
0
            Real factor2 = factor1 - factor;
63
0
            pTry = sum_*factor1 - vertices_[iHighest]*factor2;
64
0
            factor *= 0.5;
65
0
        } while (!P.constraint().test(pTry) && std::fabs(factor) > QL_EPSILON);
66
0
        if (std::fabs(factor) <= QL_EPSILON) {
67
0
            return values_[iHighest];
68
0
        }
69
0
        factor *= 2.0;
70
0
        Real vTry = P.value(pTry);
71
0
        if (vTry < values_[iHighest]) {
72
0
            values_[iHighest] = vTry;
73
0
            sum_ += pTry - vertices_[iHighest];
74
0
            vertices_[iHighest] = pTry;
75
0
        }
76
0
        return vTry;
77
78
0
    }
79
80
81
    EndCriteria::Type Simplex::minimize(Problem& P,
82
0
                                        const EndCriteria& endCriteria) {
83
        // set up of the problem
84
        //Real ftol = endCriteria.functionEpsilon();    // end criteria on f(x) (see Numerical Recipes in C++, p.410)
85
0
        Real xtol = endCriteria.rootEpsilon();          // end criteria on x (see GSL v. 1.9, http://www.gnu.org/software/gsl/)
86
0
        Size maxStationaryStateIterations_
87
0
            = endCriteria.maxStationaryStateIterations();
88
0
        EndCriteria::Type ecType = EndCriteria::None;
89
0
        P.reset();
90
91
0
        Array x_ = P.currentValue();
92
0
        if (!P.constraint().test(x_))
93
0
            QL_FAIL("Initial guess " << x_ << " is not in the feasible region.");
94
95
0
        Integer iterationNumber_=0;
96
97
        // Initialize vertices of the simplex
98
0
        Size n = x_.size();
99
0
        vertices_ = std::vector<Array>(n+1, x_);
100
0
        for (Size i=0; i<n; ++i) {
101
0
            Array direction(n, 0.0);
102
0
            direction[i] = 1.0;
103
0
            P.constraint().update(vertices_[i+1], direction, lambda_);
104
0
        }
105
        // Initialize function values at the vertices of the simplex
106
0
        values_ = Array(n+1, 0.0);
107
0
        for (Size i=0; i<=n; ++i)
108
0
            values_[i] = P.value(vertices_[i]);
109
        // Loop looking for minimum
110
0
        do {
111
0
            sum_ = Array(n, 0.0);
112
0
            Size i;
113
0
            for (i=0; i<=n; i++)
114
0
                sum_ += vertices_[i];
115
            // Determine the best (iLowest), worst (iHighest)
116
            // and 2nd worst (iNextHighest) vertices
117
0
            Size iLowest = 0;
118
0
            Size iHighest, iNextHighest;
119
0
            if (values_[0]<values_[1]) {
120
0
                iHighest = 1;
121
0
                iNextHighest = 0;
122
0
            } else {
123
0
                iHighest = 0;
124
0
                iNextHighest = 1;
125
0
            }
126
0
            for (i=1;i<=n; i++) {
127
0
                if (values_[i]>values_[iHighest]) {
128
0
                    iNextHighest = iHighest;
129
0
                    iHighest = i;
130
0
                } else {
131
0
                    if ((values_[i]>values_[iNextHighest]) && i!=iHighest)
132
0
                        iNextHighest = i;
133
0
                }
134
0
                if (values_[i]<values_[iLowest])
135
0
                    iLowest = i;
136
0
            }
137
            // Now compute accuracy, update iteration number and check end criteria
138
            //// Numerical Recipes exit strategy on fx (see NR in C++, p.410)
139
            //Real low = values_[iLowest];
140
            //Real high = values_[iHighest];
141
            //Real rtol = 2.0*std::fabs(high - low)/
142
            //    (std::fabs(high) + std::fabs(low) + QL_EPSILON);
143
            //++iterationNumber_;
144
            //if (rtol < ftol ||
145
            //    endCriteria.checkMaxIterations(iterationNumber_, ecType)) {
146
            // GSL exit strategy on x (see GSL v. 1.9, http://www.gnu.org/software/gsl
147
0
            Real simplexSize = computeSimplexSize(vertices_);
148
0
            ++iterationNumber_;
149
0
            if (simplexSize < xtol ||
150
0
                endCriteria.checkMaxIterations(iterationNumber_, ecType)) {
151
0
                endCriteria.checkStationaryPoint(0.0, 0.0,
152
0
                    maxStationaryStateIterations_, ecType);
153
0
                endCriteria.checkMaxIterations(iterationNumber_, ecType);
154
0
                x_ = vertices_[iLowest];
155
0
                Real low = values_[iLowest];
156
0
                P.setFunctionValue(low);
157
0
                P.setCurrentValue(x_);
158
0
                return ecType;
159
0
            }
160
            // If end criteria is not met, continue
161
0
            Real factor = -1.0;
162
0
            Real vTry = extrapolate(P, iHighest, factor);
163
0
            if ((vTry <= values_[iLowest]) && (factor == -1.0)) {
164
0
                factor = 2.0;
165
0
                extrapolate(P, iHighest, factor);
166
0
            } else if (std::fabs(factor) > QL_EPSILON) {
167
0
                if (vTry >= values_[iNextHighest]) {
168
0
                    Real vSave = values_[iHighest];
169
0
                    factor = 0.5;
170
0
                    vTry = extrapolate(P, iHighest, factor);
171
0
                    if (vTry >= vSave && std::fabs(factor) > QL_EPSILON) {
172
0
                        for (Size i=0; i<=n; i++) {
173
0
                            if (i!=iLowest) {
174
0
                                vertices_[i] =
175
0
                                    0.5*(vertices_[i] + vertices_[iLowest]);
176
0
                                values_[i] = P.value(vertices_[i]);
177
0
                            }
178
0
                        }
179
0
                    }
180
0
                }
181
0
            }
182
            // If can't extrapolate given the constraints, exit
183
0
            if (std::fabs(factor) <= QL_EPSILON) {
184
0
                x_ = vertices_[iLowest];
185
0
                Real low = values_[iLowest];
186
0
                P.setFunctionValue(low);
187
0
                P.setCurrentValue(x_);
188
0
                return EndCriteria::StationaryFunctionValue;
189
0
            }
190
0
        } while (true);
191
0
        QL_FAIL("optimization failed: unexpected behaviour");
192
0
    }
193
}