Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.
 
 
 

332 Zeilen
8.4 KiB

  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. }