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 години
преди 10 години
преди 11 години
преди 10 години
преди 11 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  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_invk2 = re(m_k2);
  35. m_invk1 = -m_k1 * m_invk2;
  36. m_epsilon = pow((real)10, (real)-(m_decimals + 2));
  37. Init();
  38. PrintPoly();
  39. real error = -1;
  40. for (int n = 0; ; n++)
  41. {
  42. real newerror = FindExtrema();
  43. printf("Step %i error: ", n);
  44. newerror.print(m_decimals);
  45. printf("\n");
  46. Step();
  47. if (error >= (real)0 && fabs(newerror - error) < error * m_epsilon)
  48. break;
  49. error = newerror;
  50. PrintPoly();
  51. FindZeroes();
  52. }
  53. PrintPoly();
  54. }
  55. void RemezSolver::Init()
  56. {
  57. /* m_order + 1 zeroes of the error function */
  58. m_zeroes.Resize(m_order + 1);
  59. /* m_order + 2 control points */
  60. m_control.Resize(m_order + 2);
  61. /* Initial estimates for the x_i where the error will be zero and
  62. * precompute f(x_i). */
  63. array<real> fxn;
  64. for (int i = 0; i < m_order + 1; i++)
  65. {
  66. m_zeroes[i] = (real)(2 * i - m_order) / (real)(m_order + 1);
  67. fxn.Push(EvalFunc(m_zeroes[i]));
  68. }
  69. /* We build a matrix of Chebishev evaluations: row i contains the
  70. * evaluations of x_i for polynomial order n = 0, 1, ... */
  71. LinearSystem<real> system(m_order + 1);
  72. for (int n = 0; n < m_order + 1; n++)
  73. {
  74. auto p = polynomial<real>::chebyshev(n);
  75. for (int i = 0; i < m_order + 1; i++)
  76. system.m(i, n) = p.eval(m_zeroes[i]);
  77. }
  78. /* Solve the system */
  79. system = system.inv();
  80. /* Compute new Chebyshev estimate */
  81. m_estimate = polynomial<real>();
  82. for (int n = 0; n < m_order + 1; n++)
  83. {
  84. real weight = 0;
  85. for (int i = 0; i < m_order + 1; i++)
  86. weight += system.m(n, i) * fxn[i];
  87. m_estimate += weight * polynomial<real>::chebyshev(n);
  88. }
  89. }
  90. void RemezSolver::FindZeroes()
  91. {
  92. /* Find m_order + 1 zeroes of the error function. No need to
  93. * compute the relative error: its zeroes are at the same
  94. * place as the absolute error! */
  95. for (int i = 0; i < m_order + 1; i++)
  96. {
  97. struct { real value, error; } left, right, mid;
  98. left.value = m_control[i];
  99. left.error = EvalCheby(left.value) - EvalFunc(left.value);
  100. right.value = m_control[i + 1];
  101. right.error = EvalCheby(right.value) - EvalFunc(right.value);
  102. static real limit = ldexp((real)1, -500);
  103. static real zero = (real)0;
  104. while (fabs(left.value - right.value) > limit)
  105. {
  106. mid.value = (left.value + right.value) / 2;
  107. mid.error = EvalCheby(mid.value) - EvalFunc(mid.value);
  108. if ((left.error <= zero && mid.error <= zero)
  109. || (left.error >= zero && mid.error >= zero))
  110. left = mid;
  111. else
  112. right = mid;
  113. }
  114. m_zeroes[i] = mid.value;
  115. }
  116. }
  117. /* XXX: this is the real costly function */
  118. real RemezSolver::FindExtrema()
  119. {
  120. using std::printf;
  121. /* Find m_order + 2 extrema of the error function. We need to
  122. * compute the relative error, since its extrema are at slightly
  123. * different locations than the absolute error’s. */
  124. real final = 0;
  125. m_stats_cheby = m_stats_func = m_stats_weight = 0.f;
  126. int evals = 0, rounds = 0;
  127. for (int i = 0; i < m_order + 2; i++)
  128. {
  129. real a = -1, b = 1;
  130. if (i > 0)
  131. a = m_zeroes[i - 1];
  132. if (i < m_order + 1)
  133. b = m_zeroes[i];
  134. for (int r = 0; ; ++r, ++rounds)
  135. {
  136. real maxerror = 0, maxweight = 0;
  137. int best = -1;
  138. real c = a, delta = (b - a) / 4;
  139. for (int k = 0; k <= 4; k++)
  140. {
  141. if (r == 0 || (k & 1))
  142. {
  143. ++evals;
  144. real error = fabs(EvalCheby(c) - EvalFunc(c));
  145. real weight = fabs(EvalWeight(c));
  146. /* if error/weight >= maxerror/maxweight */
  147. if (error * maxweight >= maxerror * weight)
  148. {
  149. maxerror = error;
  150. maxweight = weight;
  151. best = k;
  152. }
  153. }
  154. c += delta;
  155. }
  156. switch (best)
  157. {
  158. case 0:
  159. b = a + delta * 2;
  160. break;
  161. case 4:
  162. a = b - delta * 2;
  163. break;
  164. default:
  165. b = a + delta * (best + 1);
  166. a = a + delta * (best - 1);
  167. break;
  168. }
  169. if (delta < m_epsilon)
  170. {
  171. real e = fabs(maxerror / maxweight);
  172. if (e > final)
  173. final = e;
  174. m_control[i] = (a + b) / 2;
  175. break;
  176. }
  177. }
  178. }
  179. printf("Iterations: Rounds %d Evals %d\n", rounds, evals);
  180. printf("Times: Cheby %f Func %f EvalWeight %f\n",
  181. m_stats_cheby, m_stats_func, m_stats_weight);
  182. return final;
  183. }
  184. void RemezSolver::Step()
  185. {
  186. /* Pick up x_i where error will be 0 and compute f(x_i) */
  187. array<real> fxn;
  188. for (int i = 0; i < m_order + 2; i++)
  189. fxn.Push(EvalFunc(m_control[i]));
  190. /* We build a matrix of Chebishev evaluations: row i contains the
  191. * evaluations of x_i for polynomial order n = 0, 1, ... */
  192. LinearSystem<real> system(m_order + 2);
  193. for (int n = 0; n < m_order + 1; n++)
  194. {
  195. auto p = polynomial<real>::chebyshev(n);
  196. for (int i = 0; i < m_order + 2; i++)
  197. system.m(i, n) = p.eval(m_control[i]);
  198. }
  199. /* The last line of the system is the oscillating error */
  200. for (int i = 0; i < m_order + 2; i++)
  201. {
  202. real error = fabs(EvalWeight(m_control[i]));
  203. system.m(i, m_order + 1) = (i & 1) ? error : -error;
  204. }
  205. /* Solve the system */
  206. system = system.inv();
  207. /* Compute new polynomial estimate */
  208. m_estimate = polynomial<real>();
  209. for (int n = 0; n < m_order + 1; n++)
  210. {
  211. real weight = 0;
  212. for (int i = 0; i < m_order + 2; i++)
  213. weight += system.m(n, i) * fxn[i];
  214. m_estimate += weight * polynomial<real>::chebyshev(n);
  215. }
  216. /* Compute the error */
  217. real error = 0;
  218. for (int i = 0; i < m_order + 2; i++)
  219. error += system.m(m_order + 1, i) * fxn[i];
  220. }
  221. void RemezSolver::PrintPoly()
  222. {
  223. using std::printf;
  224. /* Transform our polynomial in the [-1..1] range into a polynomial
  225. * in the [a..b] range. */
  226. array<real> k1p, k2p, an;
  227. for (int i = 0; i < m_order + 1; i++)
  228. {
  229. k1p.Push(i ? k1p[i - 1] * m_invk1 : (real)1);
  230. k2p.Push(i ? k2p[i - 1] * m_invk2 : (real)1);
  231. }
  232. std::function<int(int, int)> comb = [&](int n, int k)
  233. {
  234. if (k == 0 || k == n)
  235. return 1;
  236. return comb(n - 1, k - 1) + comb(n - 1, k);
  237. };
  238. for (int i = 0; i < m_order + 1; i++)
  239. {
  240. real tmp = 0;
  241. for (int j = i; j < m_order + 1; j++)
  242. tmp += (real)comb(j, i) * k1p[j - i] * m_estimate[j];
  243. an.Push(tmp * k2p[i]);
  244. }
  245. printf("Polynomial estimate: ");
  246. for (int j = 0; j < m_order + 1; j++)
  247. {
  248. if (j)
  249. printf(" + x**%i * ", j);
  250. an[j].print(m_decimals);
  251. }
  252. printf("\n\n");
  253. }
  254. real RemezSolver::EvalCheby(real const &x)
  255. {
  256. Timer t;
  257. real ret = m_estimate.eval(x);
  258. m_stats_cheby += t.Get();
  259. return ret;
  260. }
  261. real RemezSolver::EvalFunc(real const &x)
  262. {
  263. Timer t;
  264. real ret = m_func(x * m_k2 + m_k1);
  265. m_stats_func += t.Get();
  266. return ret;
  267. }
  268. real RemezSolver::EvalWeight(real const &x)
  269. {
  270. Timer t;
  271. real ret = m_weight ? m_weight(x * m_k2 + m_k1) : real(1);
  272. m_stats_weight += t.Get();
  273. return ret;
  274. }