Minimize1.h 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  1. // David Eberly, Geometric Tools, Redmond WA 98052
  2. // Copyright (c) 1998-2020
  3. // Distributed under the Boost Software License, Version 1.0.
  4. // https://www.boost.org/LICENSE_1_0.txt
  5. // https://www.geometrictools.com/License/Boost/LICENSE_1_0.txt
  6. // Version: 4.0.2019.08.13
  7. #pragma once
  8. #include <Mathematics/Logger.h>
  9. #include <Mathematics/Math.h>
  10. #include <algorithm>
  11. #include <functional>
  12. // The interval [t0,t1] provided to GetMinimum(Real,Real,Real,Real&,Real&)
  13. // is processed by examining subintervals. On each subinteral [a,b], the
  14. // values f0 = F(a), f1 = F((a+b)/2), and f2 = F(b) are examined. If
  15. // {f0,f1,f2} is monotonic, then [a,b] is subdivided and processed. The
  16. // maximum depth of the recursion is limited by 'maxLevel'. If {f0,f1,f2}
  17. // is not monotonic, then two cases arise. First, if f1 = min{f0,f1,f2},
  18. // then {f0,f1,f2} is said to "bracket a minimum" and GetBracketedMinimum(*)
  19. // is called to locate the function minimum. The process uses a form of
  20. // bisection called "parabolic interpolation" and the maximum number of
  21. // bisection steps is 'maxBracket'. Second, if f1 = max{f0,f1,f2}, then
  22. // {f0,f1,f2} brackets a maximum. The minimum search continues recursively
  23. // as before on [a,(a+b)/2] and [(a+b)/2,b].
  24. namespace WwiseGTE
  25. {
  26. template <typename Real>
  27. class Minimize1
  28. {
  29. public:
  30. // Construction.
  31. Minimize1(std::function<Real(Real)> const& F, int maxLevel, int maxBracket,
  32. Real epsilon = (Real)1e-08, Real tolerance = (Real)1e-04)
  33. :
  34. mFunction(F),
  35. mMaxLevel(maxLevel),
  36. mMaxBracket(maxBracket),
  37. mEpsilon(0),
  38. mTolerance(0)
  39. {
  40. SetEpsilon(epsilon);
  41. SetTolerance(tolerance);
  42. }
  43. // Member access.
  44. inline void SetEpsilon(Real epsilon)
  45. {
  46. mEpsilon = (epsilon > (Real)0 ? epsilon : (Real)0);
  47. }
  48. inline void SetTolerance(Real tolerance)
  49. {
  50. mTolerance = (tolerance > (Real)0 ? tolerance : (Real)0);
  51. }
  52. inline Real GetEpsilon() const
  53. {
  54. return mEpsilon;
  55. }
  56. inline Real GetTolerance() const
  57. {
  58. return mTolerance;
  59. }
  60. // Search for a minimum of 'function' on the interval [t0,t1] using an
  61. // initial guess of 'tInitial'. The location of the minimum is 'tMin'
  62. // and/ the value of the minimum is 'fMin'.
  63. void GetMinimum(Real t0, Real t1, Real tInitial, Real& tMin, Real& fMin)
  64. {
  65. LogAssert(t0 <= tInitial && tInitial <= t1, "Invalid initial t value.");
  66. mTMin = std::numeric_limits<Real>::max();
  67. mFMin = std::numeric_limits<Real>::max();
  68. Real f0 = mFunction(t0);
  69. if (f0 < mFMin)
  70. {
  71. mTMin = t0;
  72. mFMin = f0;
  73. }
  74. Real fInitial = mFunction(tInitial);
  75. if (fInitial < mFMin)
  76. {
  77. mTMin = tInitial;
  78. mFMin = fInitial;
  79. }
  80. Real f1 = mFunction(t1);
  81. if (f1 < mFMin)
  82. {
  83. mTMin = t1;
  84. mFMin = f1;
  85. }
  86. GetMinimum(t0, f0, tInitial, fInitial, t1, f1, mMaxLevel);
  87. tMin = mTMin;
  88. fMin = mFMin;
  89. }
  90. private:
  91. // This is called to start the search on [t0,tInitial] and
  92. // [tInitial,t1].
  93. void GetMinimum(Real t0, Real f0, Real t1, Real f1, int level)
  94. {
  95. if (level-- == 0)
  96. {
  97. return;
  98. }
  99. Real tm = (Real)0.5 * (t0 + t1);
  100. Real fm = mFunction(tm);
  101. if (fm < mFMin)
  102. {
  103. mTMin = tm;
  104. mFMin = fm;
  105. }
  106. if (f0 - (Real)2 * fm + f1 > (Real)0)
  107. {
  108. // The quadratic fit has positive second derivative at the
  109. // midpoint.
  110. if (f1 > f0)
  111. {
  112. if (fm >= f0)
  113. {
  114. // Increasing, repeat on [t0,tm].
  115. GetMinimum(t0, f0, tm, fm, level);
  116. }
  117. else
  118. {
  119. // Not monotonic, have a bracket.
  120. GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
  121. }
  122. }
  123. else if (f1 < f0)
  124. {
  125. if (fm >= f1)
  126. {
  127. // Decreasing, repeat on [tm,t1].
  128. GetMinimum(tm, fm, t1, f1, level);
  129. }
  130. else
  131. {
  132. // Not monotonic, have a bracket.
  133. GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
  134. }
  135. }
  136. else
  137. {
  138. // Constant, repeat on [t0,tm] and [tm,t1].
  139. GetMinimum(t0, f0, tm, fm, level);
  140. GetMinimum(tm, fm, t1, f1, level);
  141. }
  142. }
  143. else
  144. {
  145. // The quadratic fit has nonpositive second derivative at the
  146. // midpoint.
  147. if (f1 > f0)
  148. {
  149. // Repeat on [t0,tm].
  150. GetMinimum(t0, f0, tm, fm, level);
  151. }
  152. else if (f1 < f0)
  153. {
  154. // Repeat on [tm,t1].
  155. GetMinimum(tm, fm, t1, f1, level);
  156. }
  157. else
  158. {
  159. // Repeat on [t0,tm] and [tm,t1].
  160. GetMinimum(t0, f0, tm, fm, level);
  161. GetMinimum(tm, fm, t1, f1, level);
  162. }
  163. }
  164. }
  165. // This is called recursively to search [a,(a+b)/2] and [(a+b)/2,b].
  166. void GetMinimum(Real t0, Real f0, Real tm, Real fm, Real t1, Real f1, int level)
  167. {
  168. if (level-- == 0)
  169. {
  170. return;
  171. }
  172. if ((t1 - tm) * (f0 - fm) > (tm - t0) * (fm - f1))
  173. {
  174. // The quadratic fit has positive second derivative at the
  175. // midpoint.
  176. if (f1 > f0)
  177. {
  178. if (fm >= f0)
  179. {
  180. // Increasing, repeat on [t0,tm].
  181. GetMinimum(t0, f0, tm, fm, level);
  182. }
  183. else
  184. {
  185. // Not monotonic, have a bracket.
  186. GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
  187. }
  188. }
  189. else if (f1 < f0)
  190. {
  191. if (fm >= f1)
  192. {
  193. // Decreasing, repeat on [tm,t1].
  194. GetMinimum(tm, fm, t1, f1, level);
  195. }
  196. else
  197. {
  198. // Not monotonic, have a bracket.
  199. GetBracketedMinimum(t0, f0, tm, fm, t1, f1, level);
  200. }
  201. }
  202. else
  203. {
  204. // Constant, repeat on [t0,tm] and [tm,t1].
  205. GetMinimum(t0, f0, tm, fm, level);
  206. GetMinimum(tm, fm, t1, f1, level);
  207. }
  208. }
  209. else
  210. {
  211. // The quadratic fit has a nonpositive second derivative at
  212. // the midpoint.
  213. if (f1 > f0)
  214. {
  215. // Repeat on [t0,tm].
  216. GetMinimum(t0, f0, tm, fm, level);
  217. }
  218. else if (f1 < f0)
  219. {
  220. // Repeat on [tm,t1].
  221. GetMinimum(tm, fm, t1, f1, level);
  222. }
  223. else
  224. {
  225. // Repeat on [t0,tm] and [tm,t1].
  226. GetMinimum(t0, f0, tm, fm, level);
  227. GetMinimum(tm, fm, t1, f1, level);
  228. }
  229. }
  230. }
  231. // This is called when {f0,f1,f2} brackets a minimum.
  232. void GetBracketedMinimum(Real t0, Real f0, Real tm, Real fm, Real t1, Real f1, int level)
  233. {
  234. for (int i = 0; i < mMaxBracket; ++i)
  235. {
  236. // Update minimum value.
  237. if (fm < mFMin)
  238. {
  239. mTMin = tm;
  240. mFMin = fm;
  241. }
  242. // Test for convergence.
  243. if (std::fabs(t1 - t0) <= (Real)2 * mTolerance * std::fabs(tm) + mEpsilon)
  244. {
  245. break;
  246. }
  247. // Compute vertex of interpolating parabola.
  248. Real dt0 = t0 - tm;
  249. Real dt1 = t1 - tm;
  250. Real df0 = f0 - fm;
  251. Real df1 = f1 - fm;
  252. Real tmp0 = dt0 * df1;
  253. Real tmp1 = dt1 * df0;
  254. Real denom = tmp1 - tmp0;
  255. if (std::fabs(denom) <= mEpsilon)
  256. {
  257. return;
  258. }
  259. // Compute tv and clamp to [t0,t1] to offset floating-point
  260. // rounding errors.
  261. Real tv = tm + (Real)0.5 * (dt1 * tmp1 - dt0 * tmp0) / denom;
  262. tv = std::max(t0, std::min(tv, t1));
  263. Real fv = mFunction(tv);
  264. if (fv < mFMin)
  265. {
  266. mTMin = tv;
  267. mFMin = fv;
  268. }
  269. if (tv < tm)
  270. {
  271. if (fv < fm)
  272. {
  273. t1 = tm;
  274. f1 = fm;
  275. tm = tv;
  276. fm = fv;
  277. }
  278. else
  279. {
  280. t0 = tv;
  281. f0 = fv;
  282. }
  283. }
  284. else if (tv > tm)
  285. {
  286. if (fv < fm)
  287. {
  288. t0 = tm;
  289. f0 = fm;
  290. tm = tv;
  291. fm = fv;
  292. }
  293. else
  294. {
  295. t1 = tv;
  296. f1 = fv;
  297. }
  298. }
  299. else
  300. {
  301. // The vertex of parabola is already at middle sample point.
  302. GetMinimum(t0, f0, tm, fm, level);
  303. GetMinimum(tm, fm, t1, f1, level);
  304. }
  305. }
  306. }
  307. std::function<Real(Real)> mFunction;
  308. int mMaxLevel;
  309. int mMaxBracket;
  310. Real mTMin, mFMin;
  311. Real mEpsilon, mTolerance;
  312. };
  313. }