NelderMead.js 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. /* *
  2. *
  3. * !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
  4. *
  5. * */
  6. /* eslint-disable valid-jsdoc */
  7. var getCentroid = function (simplex) {
  8. var arr = simplex.slice(0, -1), length = arr.length, result = [], sum = function (data, point) {
  9. data.sum += point[data.i];
  10. return data;
  11. };
  12. for (var i = 0; i < length; i++) {
  13. result[i] = arr.reduce(sum, { sum: 0, i: i }).sum / length;
  14. }
  15. return result;
  16. };
  17. /**
  18. * Finds an optimal position for a given point.
  19. * @todo add unit tests.
  20. * @todo add constraints to optimize the algorithm.
  21. * @private
  22. * @param {Highcharts.NelderMeadTestFunction} fn
  23. * The function to test a point.
  24. * @param {Highcharts.NelderMeadPointArray} initial
  25. * The initial point to optimize.
  26. * @return {Highcharts.NelderMeadPointArray}
  27. * Returns the opimized position of a point.
  28. */
  29. var nelderMead = function nelderMead(fn, initial) {
  30. var maxIterations = 100, sortByFx = function (a, b) {
  31. return a.fx - b.fx;
  32. }, pRef = 1, // Reflection parameter
  33. pExp = 2, // Expansion parameter
  34. pCon = -0.5, // Contraction parameter
  35. pOCon = pCon * pRef, // Outwards contraction parameter
  36. pShrink = 0.5; // Shrink parameter
  37. /**
  38. * @private
  39. */
  40. var weightedSum = function weightedSum(weight1, v1, weight2, v2) {
  41. return v1.map(function (x, i) {
  42. return weight1 * x + weight2 * v2[i];
  43. });
  44. };
  45. /**
  46. * @private
  47. */
  48. var getSimplex = function getSimplex(initial) {
  49. var n = initial.length, simplex = new Array(n + 1);
  50. // Initial point to the simplex.
  51. simplex[0] = initial;
  52. simplex[0].fx = fn(initial);
  53. // Create a set of extra points based on the initial.
  54. for (var i = 0; i < n; ++i) {
  55. var point = initial.slice();
  56. point[i] = point[i] ? point[i] * 1.05 : 0.001;
  57. point.fx = fn(point);
  58. simplex[i + 1] = point;
  59. }
  60. return simplex;
  61. };
  62. var updateSimplex = function (simplex, point) {
  63. point.fx = fn(point);
  64. simplex[simplex.length - 1] = point;
  65. return simplex;
  66. };
  67. var shrinkSimplex = function (simplex) {
  68. var best = simplex[0];
  69. return simplex.map(function (point) {
  70. var p = weightedSum(1 - pShrink, best, pShrink, point);
  71. p.fx = fn(p);
  72. return p;
  73. });
  74. };
  75. var getPoint = function (centroid, worst, a, b) {
  76. var point = weightedSum(a, centroid, b, worst);
  77. point.fx = fn(point);
  78. return point;
  79. };
  80. // Create a simplex
  81. var simplex = getSimplex(initial);
  82. // Iterate from 0 to max iterations
  83. for (var i = 0; i < maxIterations; i++) {
  84. // Sort the simplex
  85. simplex.sort(sortByFx);
  86. // Create a centroid from the simplex
  87. var worst = simplex[simplex.length - 1];
  88. var centroid = getCentroid(simplex);
  89. // Calculate the reflected point.
  90. var reflected = getPoint(centroid, worst, 1 + pRef, -pRef);
  91. if (reflected.fx < simplex[0].fx) {
  92. // If reflected point is the best, then possibly expand.
  93. var expanded = getPoint(centroid, worst, 1 + pExp, -pExp);
  94. simplex = updateSimplex(simplex, (expanded.fx < reflected.fx) ? expanded : reflected);
  95. }
  96. else if (reflected.fx >= simplex[simplex.length - 2].fx) {
  97. // If the reflected point is worse than the second worse, then
  98. // contract.
  99. var contracted = void 0;
  100. if (reflected.fx > worst.fx) {
  101. // If the reflected is worse than the worst point, do a
  102. // contraction
  103. contracted = getPoint(centroid, worst, 1 + pCon, -pCon);
  104. if (contracted.fx < worst.fx) {
  105. simplex = updateSimplex(simplex, contracted);
  106. }
  107. else {
  108. simplex = shrinkSimplex(simplex);
  109. }
  110. }
  111. else {
  112. // Otherwise do an outwards contraction
  113. contracted = getPoint(centroid, worst, 1 - pOCon, pOCon);
  114. if (contracted.fx < reflected.fx) {
  115. simplex = updateSimplex(simplex, contracted);
  116. }
  117. else {
  118. simplex = shrinkSimplex(simplex);
  119. }
  120. }
  121. }
  122. else {
  123. simplex = updateSimplex(simplex, reflected);
  124. }
  125. }
  126. return simplex[0];
  127. };
  128. var nelderMeadMixin = {
  129. getCentroid: getCentroid,
  130. nelderMead: nelderMead
  131. };
  132. export default nelderMeadMixin;