File: minima.qbk

package info (click to toggle)
boost1.88 1.88.0-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 576,932 kB
  • sloc: cpp: 4,149,234; xml: 136,789; ansic: 35,092; python: 33,910; asm: 5,698; sh: 4,604; ada: 1,681; makefile: 1,633; pascal: 1,139; perl: 1,124; sql: 640; yacc: 478; ruby: 271; java: 77; lisp: 24; csh: 6
file content (278 lines) | stat: -rw-r--r-- 10,314 bytes parent folder | download | duplicates (8)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
[section:brent_minima Locating Function Minima using Brent's algorithm]

[import ../../example/brent_minimise_example.cpp]

[h4 Synopsis]

``
#include <boost/math/tools/minima.hpp>

``

   template <class F, class T>
   std::pair<T, T> brent_find_minima(F f, T min, T max, int bits);

   template <class F, class T>
   std::pair<T, T> brent_find_minima(F f, T min, T max, int bits, std::uintmax_t& max_iter);

[h4 Description]

These two functions locate the minima of the continuous function ['f] using
[@http://en.wikipedia.org/wiki/Brent%27s_method Brent's method]: specifically it
uses quadratic interpolation to locate the minima, or if that fails, falls back to
a [@http://en.wikipedia.org/wiki/Golden_section_search golden-section search].

[*Parameters]

[variablelist
[[f] [The function to minimise: a function object (or C++ lambda) that should be smooth over the
          range ['\[min, max\]], with no maxima occurring in that interval.]]
[[min] [The lower endpoint of the range in which to search  for the minima.]]
[[max] [The upper endpoint of the range in which to search  for the minima.]]
[[bits] [The number of bits precision to which the minima should be found.[br]
             Note that in principle, the minima can not be located to greater
             accuracy than the square root of machine epsilon (for 64-bit double, sqrt(1e-16)[cong]1e-8),
             therefore the value of ['bits] will be ignored if it's greater than half the number of bits
             in the mantissa of T.]]
[[max_iter] [The maximum number of iterations to use
             in the algorithm, if not provided the algorithm will just
             keep on going until the minima is found.]]
] [/variablelist]

[*Returns:]

A `std::pair` of type T containing the value of the abscissa (x) at the minima and the value
of ['f(x)] at the minima.

[tip Defining BOOST_MATH_INSTRUMENT will show some parameters, for example:
``
  Type T is double
  bits = 24, maximum 26
  tolerance = 1.19209289550781e-007
  seeking minimum in range min-4 to 1.33333333333333
  maximum iterations 18446744073709551615
  10 iterations.
``
]

[h4:example Brent Minimisation Example]

As a demonstration, we replicate this  [@http://en.wikipedia.org/wiki/Brent%27s_method#Example Wikipedia example]
minimising the function ['y= (x+3)(x-1)[super 2]].

It is obvious from the equation and the plot that there is a
minimum at exactly unity (x = 1) and the value of the function at one is exactly zero (y = 0).

[tip This observation shows that an analytical or
[@http://en.wikipedia.org/wiki/Closed-form_expression Closed-form expression]
solution always beats brute-force  hands-down for both speed and precision.]

[graph brent_test_function_1]

First an include is needed:

[brent_minimise_include_1]

This function is encoded in C++ as function object (functor) using `double` precision thus:

[brent_minimise_double_functor]

The Brent function is conveniently accessed through a `using` statement (noting sub-namespace `::tools`).

  using boost::math::tools::brent_find_minima;

The search minimum and maximum are chosen as -4 to 4/3 (as in the Wikipedia example).

[tip S A Stage (reference 6) reports that the Brent algorithm is ['slow to start, but fast to converge],
so choosing a tight min-max range is good.]

For simplicity, we set the precision parameter `bits` to `std::numeric_limits<double>::digits`,
which is effectively the maximum possible `std::numeric_limits<double>::digits/2`.

Nor do we provide a value for maximum iterations parameter `max_iter`,
(probably unwisely), so the function will iterate until it finds a minimum.

[brent_minimise_double_1]

The resulting [@http://en.cppreference.com/w/cpp/utility/pair std::pair]
contains the minimum close to one, and the minimum value close to zero.

  x at minimum = 1.00000000112345,
  f(1.00000000112345) = 5.04852568272458e-018

The differences from the expected ['one] and ['zero] are less than the
uncertainty, for `double` 1.5e-008, calculated from
`sqrt(std::numeric_limits<double>::epsilon())`.

We can use this uncertainty to check that the two values are close-enough to those expected,

[brent_minimise_double_1a]

that outputs

  x == 1 (compared to uncertainty 1.49011611938477e-08) is true
  f(x) == 0 (compared to uncertainty 1.49011611938477e-08) is true

[note The function `close_at_tolerance` is ineffective for testing if a value is small or zero
(because it may divide by small or zero and cause overflow).
Function `is_small` does this job.]

It is possible to make this comparison more generally with a templated function,
`is_close`, first checking `is_small` before checking `close_at_tolerance`,
returning `true` when small or close, for example:

[brent_minimise_close]

In practical applications, we might want to know how many iterations,
and maybe to limit iterations (in case the function proves ill-behaved),
and/or perhaps to trade some loss of precision for speed, for example:

[brent_minimise_double_2]

limits to a maximum of 20 iterations
(a reasonable estimate for this example function, even for much higher precision shown later).

The parameter `it` is updated to return the actual number of iterations
(so it may be useful to also keep a record of the limit set in `const maxit`).

It is neat to avoid showing insignificant digits by computing the number of decimal digits to display.

[brent_minimise_double_3]

  Showing 53 bits precision with 9 decimal digits from tolerance 1.49011611938477e-008
  x at minimum = 1, f(1) = 5.04852568e-018

We can also half the number of precision bits from 52 to 26:

[brent_minimise_double_4]

showing no change in the result and no change in the number of iterations, as expected.

It is only if we reduce the precision to a quarter, specifying only 13 precision bits

[brent_minimise_double_5]

that we reduce the number of iterations from 10 to 7 that the result slightly differs from ['one] and ['zero].

  Showing 13 bits precision with 9 decimal digits from tolerance 0.015625
  x at minimum = 0.9999776, f(0.9999776) = 2.0069572e-009 after 7 iterations.

[h5:template Templating on floating-point type]

If we want to switch the floating-point type, then the functor must be revised.
Since the functor is stateless, the easiest option is to simply make
`operator()` a template member function:

[brent_minimise_T_functor]

The `brent_find_minima` function can now be used in template form, for example using `long double`:

[brent_minimise_template_1]

The form shown uses the floating-point type `long double` by deduction,
but it is also possible to be more explicit, for example:

  std::pair<long double, long double> r = brent_find_minima<func, long double>
  (func(), bracket_min, bracket_max, bits, it);

In order to show the use of multiprecision below (or other user-defined types),
it may be convenient to write a templated function to use this:

[brent_minimise_T_show]

[note the prudent addition of `try'n'catch` blocks within the function.
This ensures that any malfunction will provide a Boost.Math error message
rather than uncommunicatively calling `std::abort`.]

We can use this with all built-in floating-point types, for example

[brent_minimise_template_fd]

[h5:quad_precision  Quad 128-bit precision]

On platforms that provide it, a
[@http://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format 128-bit quad] type can be used.
(See [@boost:libs/multiprecision/doc/html/boost_multiprecision/tut/floats/float128.html float128]).

If this type is available, the build should define the macro BOOST_HAVE_QUADMATH:

[brent_minimise_mp_include_1]

and can be (conditionally) used this:

[brent_minimise_template_quad]

[h5:multiprecision  Multiprecision]

If a higher precision than `double` (or `long double` if that is more precise) is required,
then this is easily achieved using __multiprecision with some includes, for example:

[brent_minimise_mp_include_0]

and some `typdef`s.

[brent_minimise_mp_typedefs]

Used thus

[brent_minimise_mp_1]

and with our `show_minima` function

[brent_minimise_mp_2]

[brent_minimise_mp_output_1]

[brent_minimise_mp_output_2]

[tip One can usually rely on template argument deduction
to avoid specifying the verbose multiprecision types,
but great care in needed with the ['type of the values] provided
to avoid confusing the compiler.
]

[tip Using `std::cout.precision(std::numeric_limits<T>::digits10);`
or `std::cout.precision(std::numeric_limits<T>::max_digits10);`
during debugging may be wise because it gives some warning if construction of multiprecision values
involves unintended conversion from `double` by showing trailing zero  or random  digits after
[@http://en.cppreference.com/w/cpp/types/numeric_limits/max_digits10 max_digits10],
that is 17 for `double`, digit 18... may be just noise.]

The complete example code is at [@../../example/brent_minimise_example.cpp brent_minimise_example.cpp].

[h4 Implementation]

This is a reasonably faithful implementation of Brent's algorithm.

[h4 References]

# Brent, R.P. 1973, Algorithms for Minimization without Derivatives,
(Englewood Cliffs, NJ: Prentice-Hall), Chapter 5.

# Numerical Recipes in C, The Art of Scientific Computing,
Second Edition, William H. Press, Saul A. Teukolsky,
William T. Vetterling, and Brian P. Flannery.
Cambridge University Press. 1988, 1992.

# An algorithm with guaranteed convergence for finding a zero
of a function, R. P. Brent, The Computer Journal, Vol 44, 1971.

# [@http://en.wikipedia.org/wiki/Brent%27s_method Brent's method in Wikipedia.]

# Z. Zhang, An Improvement to the Brent's Method, IJEA, vol. 2, pp. 2 to 26, May 31, 2011.
[@http://www.cscjournals.org/manuscript/Journals/IJEA/volume2/Issue1/IJEA-7.pdf ]

# Steven A. Stage, Comments on An Improvement to the Brent's Method
(and comparison of various algorithms)
[@http://www.cscjournals.org/manuscript/Journals/IJEA/volume4/Issue1/IJEA-33.pdf]
Stage concludes that Brent's algorithm is slow to start, but fast to finish convergence, and has good accuracy.

[endsect] [/section:rebt_minima Locating Function Minima]

[/
  Copyright 2006, 2015, 2018 John Maddock and Paul A. Bristow.
  Distributed under the Boost Software License, Version 1.0.
  (See accompanying file LICENSE_1_0.txt or copy at
  http://www.boost.org/LICENSE_1_0.txt).
]