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.
 
 
 

377 lines
9.9 KiB

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