(minimize f lowx highx)
One may search for local minima of a univariate function in a number of ways.
The procedure minimize
, used as follows:
(minimize f lowx highx)
is the default minimizer. It searches for a minimum of the univariate function f
in the region of the argument delimited by the values lowx
and highx
. Our
univariate optimization programs typically return a map of the form
{:result 1.0000131781122956
:value 1.000039534857877
:iterations 26
:converged? true
:fncalls 27}
where :value
is the argument at which the extremal value fx
, keyed by
:result
, is achieved.
The procedure minimize uses Brent’s method (don’t ask how it works!).
This comment in the original refman.txt triggered a big investigation
into how Brent’s method works. It turns out there is a long history of this
algorithm being cargo-culted along from library to library. I (@sritchie) now
do understand Brent’s method, and I’ve tried to disgorge that understanding
into the
emmy.numerical.unimin.brent
namespace. Expect this to get better over time.
|
The actual procedure in the system is:
(defn minimize [f lowx highx]
(let [brent-error 1.0e-5]
(brent-min f lowx highx {:relative-threshold brent-error})))
We personally like Brent’s algorithm for univariate minimization, as found on pages 79-80 of his book "Algorithms for Minimization Without Derivatives". It is pretty reliable and pretty fast, but we cannot explain how it works. (wink, see comment above.)
Brent’s method supports the following optional parameters:
:callback
if supplied, the supplied fn will be invoked at each intermediate point with the iteration count and the values of x and f(x) at each search step.
:relative-threshold
defaults to around 1.49e8
, the sqrt
of the machine tolerance. You won’t gain
any benefit attempting to set the value less than the default.
:absolute-threshold
a smaller absolute threshold that applies when the candidate minimum point is close to 0.
:maxiter
Maximum number of iterations allowed for the minimizer. Defaults to 1000.
:maxfun
Maximum number of times the function can be evaluated before exiting. Defaults
to (inc maxiter)
.
Thus, for example, if we make a function that is a quadratic polynomial with a
minimum of 1
at 3
,
(def foo (Lagrange-interpolation-function [2 1 2] [2 3 4]))
we can find the minimum quickly (in five iterations) with Brent’s method:
(brent-min foo 0 5 {:relative-threshold 1e-2})
;;=> {:result 3.0, :value 1.0, :iterations 5, :converged? true, :fncalls 6}
Pretty good, eh?
Golden Section search is sometimes an effective method, but it must be supplied
with a convergence-test procedure, called :converged?
. We have a nice default
convergence test installed that you can customize instead with the parameters
:fn-tolerance
and :arg-tolerance
:
:fn-tolerance
check that the minimal value of any of the checked points is within the maximum
of f(a)
or f(b)
.
:arg-tolerance
check that a
and b
are within this supplied absolute distance.
(golden-section-min f lowx highx {:fn-tolerance tol})
(golden-section-max f lowx highx {:arg-tolerance tol})
If you supply a predicate to :converged?
, it must take 5 arguments:
[lowx flowx] ;; current x, f(x) of the left bound
[l fl] ;; current x, f(x) of the left interior candidate point
[r fr] ;; current x, f(x) of the right interior candidate point
[highx fhighx] ;; current x, f(x) of the left bound
current-iteration
lowx
and highx
are values of the argument that the minimum has been
localized to be between, and l
and r
are the interior arguments currently
being tendered.
The values flowx
, fl
, fr
and fhighx
are the values of the function at
the corresponding points; current-iteration
is the number of iterations of the
search.
For example, suppose we want to squeeze the minimum of the polynomial function
foo
to a difference of argument positions of 0.001
:
(let [halt? (fn [[lowx flowx] _ _ [highx fhighx] _]
(< (abs (- highx lowx)) 0.001))]
(golden-section-min foo 0 5 {:convergence-fn halt?}))
;; {:result 3.0000059608609866
;; :value 1.0000000000355318
;; :converged? true
;; :iterations 22
;; :fncalls 26}
This is not so nice. It took 22 iterations and we didn’t get anywhere near as good an answer as we got with Brent.
The following section describing local-minima and local-maxima does
not yet work. A port of this work is in progress at
this PR, if you’d like
to follow along.
|
We can find a number of local minima of a multimodal function using a search that divides the initial interval up into a number of subintervals and then does Golden Section search in each interval. For example, we may make a quartic polynomial:
(def bar
(Lagrange-interpolation-function [2 1 2 0 3] '(2 3 4 5 6)))
Now we can look for local minima of this function in the range -10
to +10
,
breaking the region up into 15 intervals as follows:
(local-minima bar -10 10 15 .0000001)
;;=> ((5.303446964995252 -.32916549541536905 18)
(2.5312725379910592 .42583263999526233 18))
The search has found two local minima, each requiring 18 iterations to localize. The local maxima are also worth chasing:
(local-maxima bar -10 10 15 .0000001)
;;=> ((3.8192274368217713 2.067961961032311 17)
(10 680 31)
(-10 19735 29))
Here we found three maxima, but two are at the endpoints of the search.
Can you improve this documentation?Edit on GitHub
cljdoc is a website building & hosting documentation for Clojure/Script libraries
× close