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.6 KiB

  1. //
  2. // Lol Engine - Sample math program: Chebyshev polynomials
  3. //
  4. // Copyright: (c) 2005-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://sam.zoy.org/projects/COPYING.WTFPL for more details.
  9. //
  10. #if defined HAVE_CONFIG_H
  11. # include "config.h"
  12. #endif
  13. #include <cstring>
  14. #include "core.h"
  15. using namespace lol;
  16. /* The order of the approximation we're looking for */
  17. static int const ORDER = 4;
  18. /* The function we want to approximate */
  19. real myfun(real const &x)
  20. {
  21. static real const one = 1.0;
  22. return cos(x) - one;
  23. //return exp(x);
  24. }
  25. /* Naive matrix inversion */
  26. template<int N> struct Matrix
  27. {
  28. inline Matrix() {}
  29. Matrix(real x)
  30. {
  31. for (int j = 0; j < N; j++)
  32. for (int i = 0; i < N; i++)
  33. if (i == j)
  34. m[i][j] = x;
  35. else
  36. m[i][j] = 0;
  37. }
  38. Matrix<N> inv() const
  39. {
  40. Matrix a = *this, b(real(1.0));
  41. /* Inversion method: iterate through all columns and make sure
  42. * all the terms are 1 on the diagonal and 0 everywhere else */
  43. for (int i = 0; i < N; i++)
  44. {
  45. /* If the expected coefficient is zero, add one of
  46. * the other lines. The first we meet will do. */
  47. if ((double)a.m[i][i] == 0.0)
  48. {
  49. for (int j = i + 1; j < N; j++)
  50. {
  51. if ((double)a.m[i][j] == 0.0)
  52. continue;
  53. /* Add row j to row i */
  54. for (int n = 0; n < N; n++)
  55. {
  56. a.m[n][i] += a.m[n][j];
  57. b.m[n][i] += b.m[n][j];
  58. }
  59. break;
  60. }
  61. }
  62. /* Now we know the diagonal term is non-zero. Get its inverse
  63. * and use that to nullify all other terms in the column */
  64. real x = fres(a.m[i][i]);
  65. for (int j = 0; j < N; j++)
  66. {
  67. if (j == i)
  68. continue;
  69. real mul = x * a.m[i][j];
  70. for (int n = 0; n < N; n++)
  71. {
  72. a.m[n][j] -= mul * a.m[n][i];
  73. b.m[n][j] -= mul * b.m[n][i];
  74. }
  75. }
  76. /* Finally, ensure the diagonal term is 1 */
  77. for (int n = 0; n < N; n++)
  78. {
  79. a.m[n][i] *= x;
  80. b.m[n][i] *= x;
  81. }
  82. }
  83. return b;
  84. }
  85. void print() const
  86. {
  87. for (int j = 0; j < N; j++)
  88. {
  89. for (int i = 0; i < N; i++)
  90. printf("%9.5f ", (double)m[j][i]);
  91. printf("\n");
  92. }
  93. }
  94. real m[N][N];
  95. };
  96. static int cheby[ORDER + 1][ORDER + 1];
  97. /* Fill the Chebyshev tables */
  98. static void cheby_init()
  99. {
  100. memset(cheby, 0, sizeof(cheby));
  101. cheby[0][0] = 1;
  102. cheby[1][1] = 1;
  103. for (int i = 2; i < ORDER + 1; i++)
  104. {
  105. cheby[i][0] = -cheby[i - 2][0];
  106. for (int j = 1; j < ORDER + 1; j++)
  107. cheby[i][j] = 2 * cheby[i - 1][j - 1] - cheby[i - 2][j];
  108. }
  109. }
  110. static void cheby_coeff(real *coeff, real *bn)
  111. {
  112. for (int i = 0; i < ORDER + 1; i++)
  113. {
  114. bn[i] = 0;
  115. for (int j = 0; j < ORDER + 1; j++)
  116. if (cheby[j][i])
  117. bn[i] += coeff[j] * (real)cheby[j][i];
  118. }
  119. }
  120. static real cheby_eval(real *coeff, real const &x)
  121. {
  122. real ret = 0.0, xn = 1.0;
  123. for (int i = 0; i < ORDER + 1; i++)
  124. {
  125. real mul = 0;
  126. for (int j = 0; j < ORDER + 1; j++)
  127. if (cheby[j][i])
  128. mul += coeff[j] * (real)cheby[j][i];
  129. ret += mul * xn;
  130. xn *= x;
  131. }
  132. return ret;
  133. }
  134. static void remez_init(real *coeff, real *zeroes)
  135. {
  136. /* Pick up x_i where error will be 0 and compute f(x_i) */
  137. real fxn[ORDER + 1];
  138. for (int i = 0; i < ORDER + 1; i++)
  139. {
  140. zeroes[i] = real(2 * i - ORDER) / real(ORDER + 1);
  141. fxn[i] = myfun(zeroes[i]);
  142. }
  143. /* We build a matrix of Chebishev evaluations: row i contains the
  144. * evaluations of x_i for polynomial order n = 0, 1, ... */
  145. Matrix<ORDER + 1> mat;
  146. for (int i = 0; i < ORDER + 1; i++)
  147. {
  148. /* Compute the powers of x_i */
  149. real powers[ORDER + 1];
  150. powers[0] = 1.0;
  151. for (int n = 1; n < ORDER + 1; n++)
  152. powers[n] = powers[n - 1] * zeroes[i];
  153. /* Compute the Chebishev evaluations at x_i */
  154. for (int n = 0; n < ORDER + 1; n++)
  155. {
  156. real sum = 0.0;
  157. for (int k = 0; k < ORDER + 1; k++)
  158. if (cheby[n][k])
  159. sum += real(cheby[n][k]) * powers[k];
  160. mat.m[i][n] = sum;
  161. }
  162. }
  163. /* Solve the system */
  164. mat = mat.inv();
  165. /* Compute interpolation coefficients */
  166. for (int j = 0; j < ORDER + 1; j++)
  167. {
  168. coeff[j] = 0;
  169. for (int i = 0; i < ORDER + 1; i++)
  170. coeff[j] += mat.m[j][i] * fxn[i];
  171. }
  172. }
  173. static void remez_findzeroes(real *coeff, real *zeroes, real *control)
  174. {
  175. /* FIXME: this is fake for now */
  176. for (int i = 0; i < ORDER + 1; i++)
  177. {
  178. real a = control[i];
  179. real ea = cheby_eval(coeff, a) - myfun(a);
  180. real b = control[i + 1];
  181. real eb = cheby_eval(coeff, b) - myfun(b);
  182. while (fabs(a - b) > (real)1e-140)
  183. {
  184. real c = (a + b) * (real)0.5;
  185. real ec = cheby_eval(coeff, c) - myfun(c);
  186. if ((ea < (real)0 && ec < (real)0)
  187. || (ea > (real)0 && ec > (real)0))
  188. {
  189. a = c;
  190. ea = ec;
  191. }
  192. else
  193. {
  194. b = c;
  195. eb = ec;
  196. }
  197. }
  198. zeroes[i] = a;
  199. }
  200. }
  201. static void remez_finderror(real *coeff, real *zeroes, real *control)
  202. {
  203. real final = 0;
  204. for (int i = 0; i < ORDER + 2; i++)
  205. {
  206. real a = -1, b = 1;
  207. if (i > 0)
  208. a = zeroes[i - 1];
  209. if (i < ORDER + 1)
  210. b = zeroes[i];
  211. printf("Error for [%g..%g]: ", (double)a, (double)b);
  212. for (;;)
  213. {
  214. real c = a, delta = (b - a) / (real)10.0;
  215. real maxerror = 0;
  216. int best = -1;
  217. for (int k = 0; k <= 10; k++)
  218. {
  219. real e = fabs(cheby_eval(coeff, c) - myfun(c));
  220. if (e > maxerror)
  221. {
  222. maxerror = e;
  223. best = k;
  224. }
  225. c += delta;
  226. }
  227. if (best == 0)
  228. best = 1;
  229. if (best == 10)
  230. best = 9;
  231. b = a + (real)(best + 1) * delta;
  232. a = a + (real)(best - 1) * delta;
  233. if (b - a < (real)1e-15)
  234. {
  235. if (maxerror > final)
  236. final = maxerror;
  237. control[i] = (a + b) * (real)0.5;
  238. printf("%g (in %g)\n", (double)maxerror, (double)control[i]);
  239. break;
  240. }
  241. }
  242. }
  243. printf("Final error: %g\n", (double)final);
  244. }
  245. static void remez_step(real *coeff, real *control)
  246. {
  247. /* Pick up x_i where error will be 0 and compute f(x_i) */
  248. real fxn[ORDER + 2];
  249. for (int i = 0; i < ORDER + 2; i++)
  250. fxn[i] = myfun(control[i]);
  251. /* We build a matrix of Chebishev evaluations: row i contains the
  252. * evaluations of x_i for polynomial order n = 0, 1, ... */
  253. Matrix<ORDER + 2> mat;
  254. for (int i = 0; i < ORDER + 2; i++)
  255. {
  256. /* Compute the powers of x_i */
  257. real powers[ORDER + 1];
  258. powers[0] = 1.0;
  259. for (int n = 1; n < ORDER + 1; n++)
  260. powers[n] = powers[n - 1] * control[i];
  261. /* Compute the Chebishev evaluations at x_i */
  262. for (int n = 0; n < ORDER + 1; n++)
  263. {
  264. real sum = 0.0;
  265. for (int k = 0; k < ORDER + 1; k++)
  266. if (cheby[n][k])
  267. sum += real(cheby[n][k]) * powers[k];
  268. mat.m[i][n] = sum;
  269. }
  270. mat.m[i][ORDER + 1] = (real)(-1 + (i & 1) * 2);
  271. }
  272. /* Solve the system */
  273. mat = mat.inv();
  274. /* Compute interpolation coefficients */
  275. for (int j = 0; j < ORDER + 1; j++)
  276. {
  277. coeff[j] = 0;
  278. for (int i = 0; i < ORDER + 2; i++)
  279. coeff[j] += mat.m[j][i] * fxn[i];
  280. }
  281. /* Compute the error */
  282. real error = 0;
  283. for (int i = 0; i < ORDER + 2; i++)
  284. error += mat.m[ORDER + 1][i] * fxn[i];
  285. }
  286. int main(int argc, char **argv)
  287. {
  288. cheby_init();
  289. /* ORDER + 1 chebyshev coefficients and 1 error value */
  290. real coeff[ORDER + 2];
  291. /* ORDER + 1 zeroes of the error function */
  292. real zeroes[ORDER + 1];
  293. /* ORDER + 2 control points */
  294. real control[ORDER + 2];
  295. real bn[ORDER + 1];
  296. remez_init(coeff, zeroes);
  297. cheby_coeff(coeff, bn);
  298. for (int j = 0; j < ORDER + 1; j++)
  299. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  300. printf("\n");
  301. for (int n = 0; n < 200; n++)
  302. {
  303. remez_finderror(coeff, zeroes, control);
  304. remez_step(coeff, control);
  305. cheby_coeff(coeff, bn);
  306. for (int j = 0; j < ORDER + 1; j++)
  307. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  308. printf("\n");
  309. remez_findzeroes(coeff, zeroes, control);
  310. }
  311. remez_finderror(coeff, zeroes, control);
  312. remez_step(coeff, control);
  313. cheby_coeff(coeff, bn);
  314. for (int j = 0; j < ORDER + 1; j++)
  315. printf("%s%12.10gx^%i", j ? "+" : "", (double)bn[j], j);
  316. printf("\n");
  317. return EXIT_SUCCESS;
  318. }