123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339 |
- // David Eberly, Geometric Tools, Redmond WA 98052
- // Copyright (c) 1998-2020
- // Distributed under the Boost Software License, Version 1.0.
- // https://www.boost.org/LICENSE_1_0.txt
- // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
- // Version: 4.0.2019.08.13
- #pragma once
- #include <Mathematics/Logger.h>
- #include <Mathematics/Math.h>
- #include <algorithm>
- #include <functional>
- // The interval [t0,t1] provided to GetMinimum(Real,Real,Real,Real&,Real&)
- // is processed by examining subintervals. On each subinteral [a,b], the
- // values f0 = F(a), f1 = F((a+b)/2), and f2 = F(b) are examined. If
- // {f0,f1,f2} is monotonic, then [a,b] is subdivided and processed. The
- // maximum depth of the recursion is limited by 'maxLevel'. If {f0,f1,f2}
- // is not monotonic, then two cases arise. First, if f1 = min{f0,f1,f2},
- // then {f0,f1,f2} is said to "bracket a minimum" and GetBracketedMinimum(*)
- // is called to locate the function minimum. The process uses a form of
- // bisection called "parabolic interpolation" and the maximum number of
- // bisection steps is 'maxBracket'. Second, if f1 = max{f0,f1,f2}, then
- // {f0,f1,f2} brackets a maximum. The minimum search continues recursively
- // as before on [a,(a+b)/2] and [(a+b)/2,b].
- namespace WwiseGTE
- {
- template <typename Real>
- class Minimize1
- {
- public:
- // Construction.
- Minimize1(std::function<Real(Real)> const& F, int maxLevel, int maxBracket,
- Real epsilon = (Real)1e-08, Real tolerance = (Real)1e-04)
- :
- mFunction(F),
- mMaxLevel(maxLevel),
- mMaxBracket(maxBracket),
- mEpsilon(0),
- mTolerance(0)
- {
- SetEpsilon(epsilon);
- SetTolerance(tolerance);
- }
- // Member access.
- inline void SetEpsilon(Real epsilon)
- {
- mEpsilon = (epsilon > (Real)0 ? epsilon : (Real)0);
- }
- inline void SetTolerance(Real tolerance)
- {
- mTolerance = (tolerance > (Real)0 ? tolerance : (Real)0);
- }
- inline Real GetEpsilon() const
- {
- return mEpsilon;
- }
- inline Real GetTolerance() const
- {
- return mTolerance;
- }
- // Search for a minimum of 'function' on the interval [t0,t1] using an
- // initial guess of 'tInitial'. The location of the minimum is 'tMin'
- // and/ the value of the minimum is 'fMin'.
- void GetMinimum(Real t0, Real t1, Real tInitial, Real& tMin, Real& fMin)
- {
- LogAssert(t0 <= tInitial && tInitial <= t1, "Invalid initial t value.");
- mTMin = std::numeric_limits<Real>::max();
- mFMin = std::numeric_limits<Real>::max();
- Real f0 = mFunction(t0);
- if (f0 < mFMin)
- {
- mTMin = t0;
- mFMin = f0;
- }
- Real fInitial = mFunction(tInitial);
- if (fInitial < mFMin)
- {
- mTMin = tInitial;
- mFMin = fInitial;
- }
- Real f1 = mFunction(t1);
- if (f1 < mFMin)
- {
- mTMin = t1;
- mFMin = f1;
- }
- GetMinimum(t0, f0, tInitial, fInitial, t1, f1, mMaxLevel);
- tMin = mTMin;
- fMin = mFMin;
- }
- private:
- // This is called to start the search on [t0,tInitial] and
- // [tInitial,t1].
- void GetMinimum(Real t0, Real f0, Real t1, Real f1, int level)
- {
- if (level-- == 0)
- {
- return;
- }
- Real tm = (Real)0.5 * (t0 + t1);
- Real fm = mFunction(tm);
- if (fm < mFMin)
- {
- mTMin = tm;
- mFMin = fm;
- }
- if (f0 - (Real)2 * fm + f1 > (Real)0)
- {
- // The quadratic fit has positive second derivative at the
- // midpoint.
- if (f1 > f0)
- {
- if (fm >= f0)
- {
- // Increasing, repeat on [t0,tm].
- GetMinimum(t0, f0, tm, fm, level);
- }
- else
- {
- // Not monotonic, have a bracket.
- GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
- }
- }
- else if (f1 < f0)
- {
- if (fm >= f1)
- {
- // Decreasing, repeat on [tm,t1].
- GetMinimum(tm, fm, t1, f1, level);
- }
- else
- {
- // Not monotonic, have a bracket.
- GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
- }
- }
- else
- {
- // Constant, repeat on [t0,tm] and [tm,t1].
- GetMinimum(t0, f0, tm, fm, level);
- GetMinimum(tm, fm, t1, f1, level);
- }
- }
- else
- {
- // The quadratic fit has nonpositive second derivative at the
- // midpoint.
- if (f1 > f0)
- {
- // Repeat on [t0,tm].
- GetMinimum(t0, f0, tm, fm, level);
- }
- else if (f1 < f0)
- {
- // Repeat on [tm,t1].
- GetMinimum(tm, fm, t1, f1, level);
- }
- else
- {
- // Repeat on [t0,tm] and [tm,t1].
- GetMinimum(t0, f0, tm, fm, level);
- GetMinimum(tm, fm, t1, f1, level);
- }
- }
- }
- // This is called recursively to search [a,(a+b)/2] and [(a+b)/2,b].
- void GetMinimum(Real t0, Real f0, Real tm, Real fm, Real t1, Real f1, int level)
- {
- if (level-- == 0)
- {
- return;
- }
- if ((t1 - tm) * (f0 - fm) > (tm - t0) * (fm - f1))
- {
- // The quadratic fit has positive second derivative at the
- // midpoint.
- if (f1 > f0)
- {
- if (fm >= f0)
- {
- // Increasing, repeat on [t0,tm].
- GetMinimum(t0, f0, tm, fm, level);
- }
- else
- {
- // Not monotonic, have a bracket.
- GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
- }
- }
- else if (f1 < f0)
- {
- if (fm >= f1)
- {
- // Decreasing, repeat on [tm,t1].
- GetMinimum(tm, fm, t1, f1, level);
- }
- else
- {
- // Not monotonic, have a bracket.
- GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
- }
- }
- else
- {
- // Constant, repeat on [t0,tm] and [tm,t1].
- GetMinimum(t0, f0, tm, fm, level);
- GetMinimum(tm, fm, t1, f1, level);
- }
- }
- else
- {
- // The quadratic fit has a nonpositive second derivative at
- // the midpoint.
- if (f1 > f0)
- {
- // Repeat on [t0,tm].
- GetMinimum(t0, f0, tm, fm, level);
- }
- else if (f1 < f0)
- {
- // Repeat on [tm,t1].
- GetMinimum(tm, fm, t1, f1, level);
- }
- else
- {
- // Repeat on [t0,tm] and [tm,t1].
- GetMinimum(t0, f0, tm, fm, level);
- GetMinimum(tm, fm, t1, f1, level);
- }
- }
- }
- // This is called when {f0,f1,f2} brackets a minimum.
- void GetBracketedMinimum(Real t0, Real f0, Real tm, Real fm, Real t1, Real f1, int level)
- {
- for (int i = 0; i < mMaxBracket; ++i)
- {
- // Update minimum value.
- if (fm < mFMin)
- {
- mTMin = tm;
- mFMin = fm;
- }
- // Test for convergence.
- if (std::fabs(t1 - t0) <= (Real)2 * mTolerance * std::fabs(tm) + mEpsilon)
- {
- break;
- }
- // Compute vertex of interpolating parabola.
- Real dt0 = t0 - tm;
- Real dt1 = t1 - tm;
- Real df0 = f0 - fm;
- Real df1 = f1 - fm;
- Real tmp0 = dt0 * df1;
- Real tmp1 = dt1 * df0;
- Real denom = tmp1 - tmp0;
- if (std::fabs(denom) <= mEpsilon)
- {
- return;
- }
- // Compute tv and clamp to [t0,t1] to offset floating-point
- // rounding errors.
- Real tv = tm + (Real)0.5 * (dt1 * tmp1 - dt0 * tmp0) / denom;
- tv = std::max(t0, std::min(tv, t1));
- Real fv = mFunction(tv);
- if (fv < mFMin)
- {
- mTMin = tv;
- mFMin = fv;
- }
- if (tv < tm)
- {
- if (fv < fm)
- {
- t1 = tm;
- f1 = fm;
- tm = tv;
- fm = fv;
- }
- else
- {
- t0 = tv;
- f0 = fv;
- }
- }
- else if (tv > tm)
- {
- if (fv < fm)
- {
- t0 = tm;
- f0 = fm;
- tm = tv;
- fm = fv;
- }
- else
- {
- t1 = tv;
- f1 = fv;
- }
- }
- else
- {
- // The vertex of parabola is already at middle sample point.
- GetMinimum(t0, f0, tm, fm, level);
- GetMinimum(tm, fm, t1, f1, level);
- }
- }
- }
- std::function<Real(Real)> mFunction;
- int mMaxLevel;
- int mMaxBracket;
- Real mTMin, mFMin;
- Real mEpsilon, mTolerance;
- };
- }
|