You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

преди 11 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 11 години
преди 11 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. //
  2. // LolRemez - Remez algorithm implementation
  3. //
  4. // Copyright: (c) 2005-2013 Sam Hocevar <sam@hocevar.net>
  5. // This program is free software; you can redistribute it and/or
  6. // modify it under the terms of the Do What The Fuck You Want To
  7. // Public License, Version 2, as published by Sam Hocevar. See
  8. // http://www.wtfpl.net/ for more details.
  9. //
  10. #if HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include <functional>
  14. #include <lol/engine.h>
  15. #include <lol/math/real.h>
  16. #include <lol/math/polynomial.h>
  17. #include "matrix.h"
  18. #include "solver.h"
  19. using lol::real;
  20. RemezSolver::RemezSolver(int order, int decimals)
  21. : m_order(order),
  22. m_decimals(decimals)
  23. {
  24. }
  25. void RemezSolver::Run(real a, real b,
  26. RemezSolver::RealFunc *func,
  27. RemezSolver::RealFunc *weight)
  28. {
  29. using std::printf;
  30. m_func = func;
  31. m_weight = weight;
  32. m_k1 = (b + a) / 2;
  33. m_k2 = (b - a) / 2;
  34. m_epsilon = pow((real)10, (real)-(m_decimals + 2));
  35. Init();
  36. PrintPoly();
  37. real error = -1;
  38. for (int n = 0; ; n++)
  39. {
  40. real newerror = FindExtrema();
  41. printf("Step %i error: ", n);
  42. newerror.print(m_decimals);
  43. printf("\n");
  44. Step();
  45. if (error >= (real)0 && fabs(newerror - error) < error * m_epsilon)
  46. break;
  47. error = newerror;
  48. PrintPoly();
  49. FindZeroes();
  50. }
  51. PrintPoly();
  52. }
  53. /*
  54. * This is basically the first Remez step: we solve a system of
  55. * order N+1 and get a good initial polynomial estimate.
  56. */
  57. void RemezSolver::Init()
  58. {
  59. /* m_order + 1 zeroes of the error function */
  60. m_zeroes.Resize(m_order + 1);
  61. /* m_order + 2 control points */
  62. m_control.Resize(m_order + 2);
  63. /* Initial estimates for the x_i where the error will be zero and
  64. * precompute f(x_i). */
  65. array<real> fxn;
  66. for (int i = 0; i < m_order + 1; i++)
  67. {
  68. m_zeroes[i] = (real)(2 * i - m_order) / (real)(m_order + 1);
  69. fxn.Push(EvalFunc(m_zeroes[i]));
  70. }
  71. /* We build a matrix of Chebishev evaluations: row i contains the
  72. * evaluations of x_i for polynomial order n = 0, 1, ... */
  73. LinearSystem<real> system(m_order + 1);
  74. for (int n = 0; n < m_order + 1; n++)
  75. {
  76. auto p = polynomial<real>::chebyshev(n);
  77. for (int i = 0; i < m_order + 1; i++)
  78. system[i][n] = p.eval(m_zeroes[i]);
  79. }
  80. /* Solve the system */
  81. system = system.inverse();
  82. /* Compute new Chebyshev estimate */
  83. m_estimate = polynomial<real>();
  84. for (int n = 0; n < m_order + 1; n++)
  85. {
  86. real weight = 0;
  87. for (int i = 0; i < m_order + 1; i++)
  88. weight += system[n][i] * fxn[i];
  89. m_estimate += weight * polynomial<real>::chebyshev(n);
  90. }
  91. }
  92. /*
  93. * Every subsequent iteration of the Remez algorithm: we solve a system
  94. * of order N+2 to both refine the estimate and compute the error.
  95. */
  96. void RemezSolver::Step()
  97. {
  98. /* Pick up x_i where error will be 0 and compute f(x_i) */
  99. array<real> fxn;
  100. for (int i = 0; i < m_order + 2; i++)
  101. fxn.Push(EvalFunc(m_control[i]));
  102. /* We build a matrix of Chebishev evaluations: row i contains the
  103. * evaluations of x_i for polynomial order n = 0, 1, ... */
  104. LinearSystem<real> system(m_order + 2);
  105. for (int n = 0; n < m_order + 1; n++)
  106. {
  107. auto p = polynomial<real>::chebyshev(n);
  108. for (int i = 0; i < m_order + 2; i++)
  109. system[i][n] = p.eval(m_control[i]);
  110. }
  111. /* The last line of the system is the oscillating error */
  112. for (int i = 0; i < m_order + 2; i++)
  113. {
  114. real error = fabs(EvalWeight(m_control[i]));
  115. system[i][m_order + 1] = (i & 1) ? error : -error;
  116. }
  117. /* Solve the system */
  118. system = system.inverse();
  119. /* Compute new polynomial estimate */
  120. m_estimate = polynomial<real>();
  121. for (int n = 0; n < m_order + 1; n++)
  122. {
  123. real weight = 0;
  124. for (int i = 0; i < m_order + 2; i++)
  125. weight += system[n][i] * fxn[i];
  126. m_estimate += weight * polynomial<real>::chebyshev(n);
  127. }
  128. /* Compute the error (FIXME: unused?) */
  129. real error = 0;
  130. for (int i = 0; i < m_order + 2; i++)
  131. error += system[m_order + 1][i] * fxn[i];
  132. }
  133. void RemezSolver::FindZeroes()
  134. {
  135. /* Find m_order + 1 zeroes of the error function. No need to
  136. * compute the relative error: its zeroes are at the same
  137. * place as the absolute error! */
  138. for (int i = 0; i < m_order + 1; i++)
  139. {
  140. struct { real value, error; } left, right, mid;
  141. left.value = m_control[i];
  142. left.error = EvalEstimate(left.value) - EvalFunc(left.value);
  143. right.value = m_control[i + 1];
  144. right.error = EvalEstimate(right.value) - EvalFunc(right.value);
  145. static real limit = ldexp((real)1, -500);
  146. static real zero = (real)0;
  147. while (fabs(left.value - right.value) > limit)
  148. {
  149. mid.value = (left.value + right.value) / 2;
  150. mid.error = EvalEstimate(mid.value) - EvalFunc(mid.value);
  151. if ((left.error <= zero && mid.error <= zero)
  152. || (left.error >= zero && mid.error >= zero))
  153. left = mid;
  154. else
  155. right = mid;
  156. }
  157. m_zeroes[i] = mid.value;
  158. }
  159. }
  160. /* XXX: this is the real costly function */
  161. real RemezSolver::FindExtrema()
  162. {
  163. using std::printf;
  164. /* Find m_order + 2 extrema of the error function. We need to
  165. * compute the relative error, since its extrema are at slightly
  166. * different locations than the absolute error’s. */
  167. real final = 0;
  168. m_stats_cheby = m_stats_func = m_stats_weight = 0.f;
  169. int evals = 0, rounds = 0;
  170. for (int i = 0; i < m_order + 2; i++)
  171. {
  172. real a = -1, b = 1;
  173. if (i > 0)
  174. a = m_zeroes[i - 1];
  175. if (i < m_order + 1)
  176. b = m_zeroes[i];
  177. for (int r = 0; ; ++r, ++rounds)
  178. {
  179. real maxerror = 0, maxweight = 0;
  180. int best = -1;
  181. real c = a, delta = (b - a) / 4;
  182. for (int k = 0; k <= 4; k++)
  183. {
  184. if (r == 0 || (k & 1))
  185. {
  186. ++evals;
  187. real error = fabs(EvalEstimate(c) - EvalFunc(c));
  188. real weight = fabs(EvalWeight(c));
  189. /* if error/weight >= maxerror/maxweight */
  190. if (error * maxweight >= maxerror * weight)
  191. {
  192. maxerror = error;
  193. maxweight = weight;
  194. best = k;
  195. }
  196. }
  197. c += delta;
  198. }
  199. switch (best)
  200. {
  201. case 0:
  202. b = a + delta * 2;
  203. break;
  204. case 4:
  205. a = b - delta * 2;
  206. break;
  207. default:
  208. b = a + delta * (best + 1);
  209. a = a + delta * (best - 1);
  210. break;
  211. }
  212. if (delta < m_epsilon)
  213. {
  214. real e = fabs(maxerror / maxweight);
  215. if (e > final)
  216. final = e;
  217. m_control[i] = (a + b) / 2;
  218. break;
  219. }
  220. }
  221. }
  222. printf("Iterations: Rounds %d Evals %d\n", rounds, evals);
  223. printf("Times: Estimate %f Func %f EvalWeight %f\n",
  224. m_stats_cheby, m_stats_func, m_stats_weight);
  225. return final;
  226. }
  227. void RemezSolver::PrintPoly()
  228. {
  229. using std::printf;
  230. /* Transform our polynomial in the [-1..1] range into a polynomial
  231. * in the [a..b] range by composing it with q:
  232. * q(x) = 2x / (b-a) - (b+a) / (b-a) */
  233. polynomial<real> q ({ -m_k1 / m_k2, real(1) / m_k2 });
  234. polynomial<real> r = m_estimate.eval(q);
  235. printf("Polynomial estimate: ");
  236. for (int j = 0; j < m_order + 1; j++)
  237. {
  238. if (j)
  239. printf(" + x**%i * ", j);
  240. r[j].print(m_decimals);
  241. }
  242. printf("\n\n");
  243. }
  244. real RemezSolver::EvalEstimate(real const &x)
  245. {
  246. Timer t;
  247. real ret = m_estimate.eval(x);
  248. m_stats_cheby += t.Get();
  249. return ret;
  250. }
  251. real RemezSolver::EvalFunc(real const &x)
  252. {
  253. Timer t;
  254. real ret = m_func(x * m_k2 + m_k1);
  255. m_stats_func += t.Get();
  256. return ret;
  257. }
  258. real RemezSolver::EvalWeight(real const &x)
  259. {
  260. Timer t;
  261. real ret = m_weight ? m_weight(x * m_k2 + m_k1) : real(1);
  262. m_stats_weight += t.Get();
  263. return ret;
  264. }