MinimizeN.h 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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/GVector.h>
  9. #include <Mathematics/Minimize1.h>
  10. #include <cstring>
  11. // The Cartesian-product domain provided to GetMinimum(*) has minimum values
  12. // stored in t0[0..d-1] and maximum values stored in t1[0..d-1], where d is
  13. // 'dimensions'. The domain is searched along lines through the current
  14. // estimate of the minimum location. Each such line is searched for a minimum
  15. // using a Minimize1<Real> object. This is called "Powell's Direction Set
  16. // Method". The parameters 'maxLevel' and 'maxBracket' are used by
  17. // Minimize1<Real>, so read the documentation for that class (in its header
  18. // file) to understand what these mean. The input 'maxIterations' is the
  19. // number of iterations for the direction-set method.
  20. namespace WwiseGTE
  21. {
  22. template <typename Real>
  23. class MinimizeN
  24. {
  25. public:
  26. // Construction.
  27. MinimizeN(int dimensions, std::function<Real(Real const*)> const& F,
  28. int maxLevel, int maxBracket, int maxIterations, Real epsilon = (Real)1e-06)
  29. :
  30. mDimensions(dimensions),
  31. mFunction(F),
  32. mMaxIterations(maxIterations),
  33. mEpsilon(0),
  34. mDirections(dimensions + 1),
  35. mDConjIndex(dimensions),
  36. mDCurrIndex(0),
  37. mTCurr(dimensions),
  38. mTSave(dimensions),
  39. mMinimizer([this](Real t){ return mFunction(&(mTCurr + t * mDirections[mDCurrIndex])[0]); }, maxLevel, maxBracket)
  40. {
  41. SetEpsilon(epsilon);
  42. for (auto& direction : mDirections)
  43. {
  44. direction.SetSize(dimensions);
  45. }
  46. }
  47. // Member access.
  48. inline void SetEpsilon(Real epsilon)
  49. {
  50. mEpsilon = (epsilon > (Real)0 ? epsilon : (Real)0);
  51. }
  52. inline Real GetEpsilon() const
  53. {
  54. return mEpsilon;
  55. }
  56. // Find the minimum on the Cartesian-product domain whose minimum
  57. // values are stored in t0[0..d-1] and whose maximum values are stored
  58. // in t1[0..d-1], where d is 'dimensions'. An initial guess is
  59. // specified in tInitial[0..d-1]. The location of the minimum is
  60. // tMin[0..d-1] and the value of the minimum is 'fMin'.
  61. void GetMinimum(Real const* t0, Real const* t1, Real const* tInitial, Real* tMin, Real& fMin)
  62. {
  63. // The initial guess.
  64. size_t numBytes = mDimensions * sizeof(Real);
  65. mFCurr = mFunction(tInitial);
  66. std::memcpy(&mTSave[0], tInitial, numBytes);
  67. std::memcpy(&mTCurr[0], tInitial, numBytes);
  68. // Initialize the direction set to the standard Euclidean basis.
  69. for (int i = 0; i < mDimensions; ++i)
  70. {
  71. mDirections[i].MakeUnit(i);
  72. }
  73. Real ell0, ell1, ellMin;
  74. for (int iter = 0; iter < mMaxIterations; ++iter)
  75. {
  76. // Find minimum in each direction and update current location.
  77. for (int i = 0; i < mDimensions; ++i)
  78. {
  79. mDCurrIndex = i;
  80. ComputeDomain(t0, t1, ell0, ell1);
  81. mMinimizer.GetMinimum(ell0, ell1, (Real)0, ellMin, mFCurr);
  82. mTCurr += ellMin * mDirections[i];
  83. }
  84. // Estimate a unit-length conjugate direction.
  85. mDirections[mDConjIndex] = mTCurr - mTSave;
  86. Real length = Length(mDirections[mDConjIndex]);
  87. if (length <= mEpsilon)
  88. {
  89. // New position did not change significantly from old one.
  90. // Should there be a better convergence criterion here?
  91. break;
  92. }
  93. mDirections[mDConjIndex] /= length;
  94. // Minimize in conjugate direction.
  95. mDCurrIndex = mDConjIndex;
  96. ComputeDomain(t0, t1, ell0, ell1);
  97. mMinimizer.GetMinimum(ell0, ell1, (Real)0, ellMin, mFCurr);
  98. mTCurr += ellMin * mDirections[mDCurrIndex];
  99. // Cycle the directions and add conjugate direction to set.
  100. mDConjIndex = 0;
  101. for (int i = 0; i < mDimensions; ++i)
  102. {
  103. mDirections[i] = mDirections[i + 1];
  104. }
  105. // Set parameters for next pass.
  106. mTSave = mTCurr;
  107. }
  108. std::memcpy(tMin, &mTCurr[0], numBytes);
  109. fMin = mFCurr;
  110. }
  111. private:
  112. // The current estimate of the minimum location is mTCurr[0..d-1]. The
  113. // direction of the current line to search is mDCurr[0..d-1]. This
  114. // line must be clipped against the Cartesian-product domain, a
  115. // process implemented in this function. If the line is
  116. // mTCurr+s*mDCurr, the clip result is the s-interval [ell0,ell1].
  117. void ComputeDomain(Real const* t0, Real const* t1, Real& ell0, Real& ell1)
  118. {
  119. ell0 = -std::numeric_limits<Real>::max();
  120. ell1 = +std::numeric_limits<Real>::max();
  121. for (int i = 0; i < mDimensions; ++i)
  122. {
  123. Real value = mDirections[mDCurrIndex][i];
  124. if (value != (Real)0)
  125. {
  126. Real b0 = t0[i] - mTCurr[i];
  127. Real b1 = t1[i] - mTCurr[i];
  128. Real inv = ((Real)1) / value;
  129. if (value > (Real)0)
  130. {
  131. // The valid t-interval is [b0,b1].
  132. b0 *= inv;
  133. if (b0 > ell0)
  134. {
  135. ell0 = b0;
  136. }
  137. b1 *= inv;
  138. if (b1 < ell1)
  139. {
  140. ell1 = b1;
  141. }
  142. }
  143. else
  144. {
  145. // The valid t-interval is [b1,b0].
  146. b0 *= inv;
  147. if (b0 < ell1)
  148. {
  149. ell1 = b0;
  150. }
  151. b1 *= inv;
  152. if (b1 > ell0)
  153. {
  154. ell0 = b1;
  155. }
  156. }
  157. }
  158. }
  159. // Correction if numerical errors lead to values nearly zero.
  160. if (ell0 > (Real)0)
  161. {
  162. ell0 = (Real)0;
  163. }
  164. if (ell1 < (Real)0)
  165. {
  166. ell1 = (Real)0;
  167. }
  168. }
  169. int mDimensions;
  170. std::function<Real(Real const*)> mFunction;
  171. int mMaxIterations;
  172. Real mEpsilon;
  173. std::vector<GVector<Real>> mDirections;
  174. int mDConjIndex;
  175. int mDCurrIndex;
  176. GVector<Real> mTCurr;
  177. GVector<Real> mTSave;
  178. Real mFCurr;
  179. Minimize1<Real> mMinimizer;
  180. };
  181. }