Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.

378 righe
9.6 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 defined HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include "core.h"
  14. #include <lol/math/real.h>
  15. #include "matrix.h"
  16. #include "solver.h"
  17. using lol::real;
  18. /* Some forward declarations first. */
  19. template<> void RemezSolver<real>::Init();
  20. template<> void RemezSolver<real>::FindZeroes();
  21. template<> real RemezSolver<real>::FindExtrema();
  22. template<> void RemezSolver<real>::Step();
  23. template<> int RemezSolver<real>::Cheby(int n, int k);
  24. template<> void RemezSolver<real>::PrintPoly();
  25. template<> real RemezSolver<real>::EvalFunc(real const &x);
  26. template<> real RemezSolver<real>::Weight(real const &x);
  27. template<>
  28. void RemezSolver<real>::Run(int order, int decimals, real a, real b,
  29. RemezSolver<real>::RealFunc *func,
  30. RemezSolver<real>::RealFunc *weight)
  31. {
  32. using std::printf;
  33. m_order = order;
  34. m_func = func;
  35. m_weight = weight;
  36. m_k1 = (b + a) / 2;
  37. m_k2 = (b - a) / 2;
  38. m_invk2 = re(m_k2);
  39. m_invk1 = -m_k1 * m_invk2;
  40. m_decimals = decimals;
  41. m_epsilon = pow((real)10, (real)-(decimals + 2));
  42. Init();
  43. PrintPoly();
  44. real error = -1;
  45. for (int n = 0; ; n++)
  46. {
  47. real newerror = FindExtrema();
  48. printf("Step %i error: ", n);
  49. newerror.print(m_decimals);
  50. printf("\n");
  51. Step();
  52. if (error >= (real)0 && fabs(newerror - error) < error * m_epsilon)
  53. break;
  54. error = newerror;
  55. PrintPoly();
  56. FindZeroes();
  57. }
  58. PrintPoly();
  59. }
  60. template<>
  61. real RemezSolver<real>::EvalCheby(real const &x)
  62. {
  63. real ret = 0.0, xn = 1.0;
  64. for (int i = 0; i < m_order + 1; i++)
  65. {
  66. real mul = 0;
  67. for (int j = 0; j < m_order + 1; j++)
  68. mul += m_coeff[j] * (real)Cheby(j, i);
  69. ret += mul * xn;
  70. xn *= x;
  71. }
  72. return ret;
  73. }
  74. template<>
  75. void RemezSolver<real>::Init()
  76. {
  77. /* m_order + 1 Chebyshev coefficients, plus 1 error value */
  78. m_coeff.Resize(m_order + 2);
  79. /* m_order + 1 zeroes of the error function */
  80. m_zeroes.Resize(m_order + 1);
  81. /* m_order + 2 control points */
  82. m_control.Resize(m_order + 2);
  83. /* Pick up x_i where error will be 0 and compute f(x_i) */
  84. real fxn[m_order + 1];
  85. for (int i = 0; i < m_order + 1; i++)
  86. {
  87. m_zeroes[i] = (real)(2 * i - m_order) / (real)(m_order + 1);
  88. fxn[i] = EvalFunc(m_zeroes[i]);
  89. }
  90. /* We build a matrix of Chebishev evaluations: row i contains the
  91. * evaluations of x_i for polynomial order n = 0, 1, ... */
  92. Matrix<real> mat(m_order + 1, m_order + 1);
  93. for (int i = 0; i < m_order + 1; i++)
  94. {
  95. /* Compute the powers of x_i */
  96. real powers[m_order + 1];
  97. powers[0] = 1.0;
  98. for (int n = 1; n < m_order + 1; n++)
  99. powers[n] = powers[n - 1] * m_zeroes[i];
  100. /* Compute the Chebishev evaluations at x_i */
  101. for (int n = 0; n < m_order + 1; n++)
  102. {
  103. real sum = 0.0;
  104. for (int k = 0; k < m_order + 1; k++)
  105. sum += (real)Cheby(n, k) * powers[k];
  106. mat.m(i, n) = sum;
  107. }
  108. }
  109. /* Solve the system */
  110. mat = mat.inv();
  111. /* Compute interpolation coefficients */
  112. for (int j = 0; j < m_order + 1; j++)
  113. {
  114. m_coeff[j] = 0;
  115. for (int i = 0; i < m_order + 1; i++)
  116. m_coeff[j] += mat.m(j, i) * fxn[i];
  117. }
  118. }
  119. template<>
  120. void RemezSolver<real>::FindZeroes()
  121. {
  122. /* Find m_order + 1 zeroes of the error function. No need to
  123. * compute the relative error: its zeroes are at the same
  124. * place as the absolute error! */
  125. for (int i = 0; i < m_order + 1; i++)
  126. {
  127. struct { real value, error; } left, right, mid;
  128. left.value = m_control[i];
  129. left.error = EvalCheby(left.value) - EvalFunc(left.value);
  130. right.value = m_control[i + 1];
  131. right.error = EvalCheby(right.value) - EvalFunc(right.value);
  132. static real limit = ldexp((real)1, -500);
  133. static real zero = (real)0;
  134. while (fabs(left.value - right.value) > limit)
  135. {
  136. mid.value = (left.value + right.value) / 2;
  137. mid.error = EvalCheby(mid.value) - EvalFunc(mid.value);
  138. if ((left.error <= zero && mid.error <= zero)
  139. || (left.error >= zero && mid.error >= zero))
  140. left = mid;
  141. else
  142. right = mid;
  143. }
  144. m_zeroes[i] = mid.value;
  145. }
  146. }
  147. template<>
  148. real RemezSolver<real>::FindExtrema()
  149. {
  150. using std::printf;
  151. /* Find m_order + 2 extrema of the error function. We need to
  152. * compute the relative error, since its extrema are at slightly
  153. * different locations than the absolute error’s. */
  154. real final = 0;
  155. for (int i = 0; i < m_order + 2; i++)
  156. {
  157. real a = -1, b = 1;
  158. if (i > 0)
  159. a = m_zeroes[i - 1];
  160. if (i < m_order + 1)
  161. b = m_zeroes[i];
  162. for (int round = 0; ; round++)
  163. {
  164. real maxerror = 0, maxweight = 0;
  165. int best = -1;
  166. real c = a, delta = (b - a) / 4;
  167. for (int k = 0; k <= 4; k++)
  168. {
  169. if (round == 0 || (k & 1))
  170. {
  171. real error = fabs(EvalCheby(c) - EvalFunc(c));
  172. real weight = fabs(Weight(c));
  173. /* if error/weight >= maxerror/maxweight */
  174. if (error * maxweight >= maxerror * weight)
  175. {
  176. maxerror = error;
  177. maxweight = weight;
  178. best = k;
  179. }
  180. }
  181. c += delta;
  182. }
  183. switch (best)
  184. {
  185. case 0:
  186. b = a + delta * 2;
  187. break;
  188. case 4:
  189. a = b - delta * 2;
  190. break;
  191. default:
  192. b = a + delta * (best + 1);
  193. a = a + delta * (best - 1);
  194. break;
  195. }
  196. if (delta < m_epsilon)
  197. {
  198. real e = fabs(maxerror / maxweight);
  199. if (e > final)
  200. final = e;
  201. m_control[i] = (a + b) / 2;
  202. break;
  203. }
  204. }
  205. }
  206. return final;
  207. }
  208. template<>
  209. void RemezSolver<real>::Step()
  210. {
  211. /* Pick up x_i where error will be 0 and compute f(x_i) */
  212. real fxn[m_order + 2];
  213. for (int i = 0; i < m_order + 2; i++)
  214. fxn[i] = EvalFunc(m_control[i]);
  215. /* We build a matrix of Chebishev evaluations: row i contains the
  216. * evaluations of x_i for polynomial order n = 0, 1, ... */
  217. Matrix<real> mat(m_order + 2, m_order + 2);
  218. for (int i = 0; i < m_order + 2; i++)
  219. {
  220. /* Compute the powers of x_i */
  221. real powers[m_order + 1];
  222. powers[0] = 1.0;
  223. for (int n = 1; n < m_order + 1; n++)
  224. powers[n] = powers[n - 1] * m_control[i];
  225. /* Compute the Chebishev evaluations at x_i */
  226. for (int n = 0; n < m_order + 1; n++)
  227. {
  228. real sum = 0.0;
  229. for (int k = 0; k < m_order + 1; k++)
  230. sum += (real)Cheby(n, k) * powers[k];
  231. mat.m(i, n) = sum;
  232. }
  233. if (i & 1)
  234. mat.m(i, m_order + 1) = fabs(Weight(m_control[i]));
  235. else
  236. mat.m(i, m_order + 1) = -fabs(Weight(m_control[i]));
  237. }
  238. /* Solve the system */
  239. mat = mat.inv();
  240. /* Compute interpolation coefficients */
  241. for (int j = 0; j < m_order + 1; j++)
  242. {
  243. m_coeff[j] = 0;
  244. for (int i = 0; i < m_order + 2; i++)
  245. m_coeff[j] += mat.m(j, i) * fxn[i];
  246. }
  247. /* Compute the error */
  248. real error = 0;
  249. for (int i = 0; i < m_order + 2; i++)
  250. error += mat.m(m_order + 1, i) * fxn[i];
  251. }
  252. template<>
  253. int RemezSolver<real>::Cheby(int n, int k)
  254. {
  255. if (k > n || k < 0)
  256. return 0;
  257. if (n <= 1)
  258. return (n ^ k ^ 1) & 1;
  259. return 2 * Cheby(n - 1, k - 1) - Cheby(n - 2, k);
  260. }
  261. template<>
  262. int RemezSolver<real>::Comb(int n, int k)
  263. {
  264. if (k == 0 || k == n)
  265. return 1;
  266. return Comb(n - 1, k - 1) + Comb(n - 1, k);
  267. }
  268. template<>
  269. void RemezSolver<real>::PrintPoly()
  270. {
  271. using std::printf;
  272. /* Transform Chebyshev polynomial weights into powers of X^i
  273. * in the [-1..1] range. */
  274. real bn[m_order + 1];
  275. for (int i = 0; i < m_order + 1; i++)
  276. {
  277. bn[i] = 0;
  278. for (int j = 0; j < m_order + 1; j++)
  279. bn[i] += m_coeff[j] * (real)Cheby(j, i);
  280. }
  281. /* Transform a polynomial in the [-1..1] range into a polynomial
  282. * in the [a..b] range. */
  283. real k1p[m_order + 1], k2p[m_order + 1];
  284. real an[m_order + 1];
  285. for (int i = 0; i < m_order + 1; i++)
  286. {
  287. k1p[i] = i ? k1p[i - 1] * m_invk1 : (real)1;
  288. k2p[i] = i ? k2p[i - 1] * m_invk2 : (real)1;
  289. }
  290. for (int i = 0; i < m_order + 1; i++)
  291. {
  292. an[i] = 0;
  293. for (int j = i; j < m_order + 1; j++)
  294. an[i] += (real)Comb(j, i) * k1p[j - i] * bn[j];
  295. an[i] *= k2p[i];
  296. }
  297. printf("Polynomial estimate: ");
  298. for (int j = 0; j < m_order + 1; j++)
  299. {
  300. if (j)
  301. printf(" + x**%i * ", j);
  302. an[j].print(m_decimals);
  303. }
  304. printf("\n\n");
  305. }
  306. template<>
  307. real RemezSolver<real>::EvalFunc(real const &x)
  308. {
  309. return m_func(x * m_k2 + m_k1);
  310. }
  311. template<>
  312. real RemezSolver<real>::Weight(real const &x)
  313. {
  314. if (m_weight)
  315. return m_weight(x * m_k2 + m_k1);
  316. return 1;
  317. }