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.

преди 10 години
преди 10 години
преди 11 години
преди 11 години
преди 10 години
преди 11 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
преди 10 години
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. //
  2. // LolRemez - Remez algorithm implementation
  3. //
  4. // Copyright © 2005—2015 Sam Hocevar <sam@hocevar.net>
  5. //
  6. // This program is free software. It comes without any warranty, to
  7. // the extent permitted by applicable law. You can redistribute it
  8. // and/or modify it under the terms of the Do What the Fuck You Want
  9. // to Public License, Version 2, as published by the WTFPL Task Force.
  10. // See http://www.wtfpl.net/ for more details.
  11. //
  12. #if HAVE_CONFIG_H
  13. # include "config.h"
  14. #endif
  15. #include <functional>
  16. #include <lol/engine.h>
  17. #include <lol/math/real.h>
  18. #include <lol/math/polynomial.h>
  19. #include "matrix.h"
  20. #include "solver.h"
  21. using lol::real;
  22. remez_solver::remez_solver(int order, int decimals)
  23. : m_order(order),
  24. m_decimals(decimals),
  25. m_has_weight(false)
  26. {
  27. }
  28. void remez_solver::run(real a, real b, char const *func, char const *weight)
  29. {
  30. using std::printf;
  31. m_func.parse(func);
  32. if (weight)
  33. {
  34. m_weight.parse(weight);
  35. m_has_weight = true;
  36. }
  37. m_k1 = (b + a) / 2;
  38. m_k2 = (b - a) / 2;
  39. m_epsilon = pow((real)10, (real)-(m_decimals + 2));
  40. remez_init();
  41. print_poly();
  42. for (int n = 0; ; n++)
  43. {
  44. real old_error = m_error;
  45. find_extrema();
  46. remez_step();
  47. if (m_error >= (real)0
  48. && fabs(m_error - old_error) < m_error * m_epsilon)
  49. break;
  50. print_poly();
  51. find_zeroes();
  52. }
  53. print_poly();
  54. }
  55. /*
  56. * This is basically the first Remez step: we solve a system of
  57. * order N+1 and get a good initial polynomial estimate.
  58. */
  59. void remez_solver::remez_init()
  60. {
  61. /* m_order + 1 zeroes of the error function */
  62. m_zeroes.Resize(m_order + 1);
  63. /* m_order + 2 control points */
  64. m_control.Resize(m_order + 2);
  65. /* Initial estimates for the x_i where the error will be zero and
  66. * precompute f(x_i). */
  67. array<real> fxn;
  68. for (int i = 0; i < m_order + 1; i++)
  69. {
  70. m_zeroes[i] = (real)(2 * i - m_order) / (real)(m_order + 1);
  71. fxn.Push(eval_func(m_zeroes[i]));
  72. }
  73. /* We build a matrix of Chebyshev evaluations: row i contains the
  74. * evaluations of x_i for polynomial order n = 0, 1, ... */
  75. linear_system<real> system(m_order + 1);
  76. for (int n = 0; n < m_order + 1; n++)
  77. {
  78. auto p = polynomial<real>::chebyshev(n);
  79. for (int i = 0; i < m_order + 1; i++)
  80. system[i][n] = p.eval(m_zeroes[i]);
  81. }
  82. /* Solve the system */
  83. system = system.inverse();
  84. /* Compute new Chebyshev estimate */
  85. m_estimate = polynomial<real>();
  86. for (int n = 0; n < m_order + 1; n++)
  87. {
  88. real weight = 0;
  89. for (int i = 0; i < m_order + 1; i++)
  90. weight += system[n][i] * fxn[i];
  91. m_estimate += weight * polynomial<real>::chebyshev(n);
  92. }
  93. }
  94. /*
  95. * Every subsequent iteration of the Remez algorithm: we solve a system
  96. * of order N+2 to both refine the estimate and compute the error.
  97. */
  98. void remez_solver::remez_step()
  99. {
  100. /* Pick up x_i where error will be 0 and compute f(x_i) */
  101. array<real> fxn;
  102. for (int i = 0; i < m_order + 2; i++)
  103. fxn.Push(eval_func(m_control[i]));
  104. /* We build a matrix of Chebyshev evaluations: row i contains the
  105. * evaluations of x_i for polynomial order n = 0, 1, ... */
  106. linear_system<real> system(m_order + 2);
  107. for (int n = 0; n < m_order + 1; n++)
  108. {
  109. auto p = polynomial<real>::chebyshev(n);
  110. for (int i = 0; i < m_order + 2; i++)
  111. system[i][n] = p.eval(m_control[i]);
  112. }
  113. /* The last line of the system is the oscillating error */
  114. for (int i = 0; i < m_order + 2; i++)
  115. {
  116. real error = fabs(eval_weight(m_control[i]));
  117. system[i][m_order + 1] = (i & 1) ? error : -error;
  118. }
  119. /* Solve the system */
  120. system = system.inverse();
  121. /* Compute new polynomial estimate */
  122. m_estimate = polynomial<real>();
  123. for (int n = 0; n < m_order + 1; n++)
  124. {
  125. real weight = 0;
  126. for (int i = 0; i < m_order + 2; i++)
  127. weight += system[n][i] * fxn[i];
  128. m_estimate += weight * polynomial<real>::chebyshev(n);
  129. }
  130. /* Compute the error (FIXME: unused?) */
  131. real error = 0;
  132. for (int i = 0; i < m_order + 2; i++)
  133. error += system[m_order + 1][i] * fxn[i];
  134. }
  135. /*
  136. * Find m_order + 1 zeroes of the error function. No need to compute the
  137. * relative error: its zeroes are at the same place as the absolute error!
  138. *
  139. * The algorithm used here is naïve regula falsi. It still performs a lot
  140. * better than the midpoint algorithm.
  141. */
  142. void remez_solver::find_zeroes()
  143. {
  144. Timer t;
  145. for (int i = 0; i < m_order + 1; i++)
  146. {
  147. struct { real x, err; } a, b, c;
  148. a.x = m_control[i];
  149. a.err = eval_estimate(a.x) - eval_func(a.x);
  150. b.x = m_control[i + 1];
  151. b.err = eval_estimate(b.x) - eval_func(b.x);
  152. static real limit = ldexp((real)1, -500);
  153. static real zero = (real)0;
  154. while (fabs(a.x - b.x) > limit)
  155. {
  156. real t = abs(b.err) / (abs(a.err) + abs(b.err));
  157. real newc = b.x + t * (a.x - b.x);
  158. /* If the third point didn't change since last iteration,
  159. * we may be at an inflection point. Use the midpoint to get
  160. * out of this situation. */
  161. c.x = newc == c.x ? (a.x + b.x) / 2 : newc;
  162. c.err = eval_estimate(c.x) - eval_func(c.x);
  163. if (c.err == zero)
  164. break;
  165. if ((a.err < zero && c.err < zero)
  166. || (a.err > zero && c.err > zero))
  167. a = c;
  168. else
  169. b = c;
  170. }
  171. m_zeroes[i] = c.x;
  172. }
  173. printf(" -:- timing for zeroes: %f ms\n", t.Get() * 1000.f);
  174. }
  175. /*
  176. * Find m_order extrema of the error function. We maximise the relative
  177. * error, since its extrema are at slightly different locations than the
  178. * absolute error’s.
  179. *
  180. * The algorithm used here is successive parabolic interpolation. FIXME: we
  181. * could use Brent’s method instead, which combines parabolic interpolation
  182. * and golden ratio search and has superlinear convergence.
  183. */
  184. void remez_solver::find_extrema()
  185. {
  186. Timer t;
  187. using std::printf;
  188. m_control[0] = -1;
  189. m_control[m_order + 1] = 1;
  190. m_error = 0;
  191. for (int i = 1; i < m_order + 1; i++)
  192. {
  193. struct { real x, err; } a, b, c, d;
  194. a.x = m_zeroes[i - 1];
  195. b.x = m_zeroes[i];
  196. c.x = a.x + (b.x - a.x) * real(rand(0.4f, 0.6f));
  197. a.err = eval_error(a.x);
  198. b.err = eval_error(b.x);
  199. c.err = eval_error(c.x);
  200. while (b.x - a.x > m_epsilon)
  201. {
  202. real d1 = c.x - a.x, d2 = c.x - b.x;
  203. real k1 = d1 * (c.err - b.err);
  204. real k2 = d2 * (c.err - a.err);
  205. d.x = c.x - (d1 * k1 - d2 * k2) / (k1 - k2) / 2;
  206. /* If parabolic interpolation failed, pick a number
  207. * inbetween. */
  208. if (d.x <= a.x || d.x >= b.x)
  209. d.x = (a.x + b.x) / 2;
  210. d.err = eval_error(d.x);
  211. /* Update bracketing depending on the new point. */
  212. if (d.err < c.err)
  213. {
  214. (d.x > c.x ? b : a) = d;
  215. }
  216. else
  217. {
  218. (d.x > c.x ? a : b) = c;
  219. c = d;
  220. }
  221. }
  222. if (c.err > m_error)
  223. m_error = c.err;
  224. m_control[i] = c.x;
  225. }
  226. printf(" -:- timing for extrema: %f ms\n", t.Get() * 1000.f);
  227. printf(" -:- error: ");
  228. m_error.print(m_decimals);
  229. printf("\n");
  230. }
  231. void remez_solver::print_poly()
  232. {
  233. using std::printf;
  234. /* Transform our polynomial in the [-1..1] range into a polynomial
  235. * in the [a..b] range by composing it with q:
  236. * q(x) = 2x / (b-a) - (b+a) / (b-a) */
  237. polynomial<real> q ({ -m_k1 / m_k2, real(1) / m_k2 });
  238. polynomial<real> r = m_estimate.eval(q);
  239. printf("\n");
  240. for (int j = 0; j < m_order + 1; j++)
  241. {
  242. if (j)
  243. printf(" + x**%i * ", j);
  244. r[j].print(m_decimals);
  245. }
  246. printf("\n\n");
  247. }
  248. real remez_solver::eval_estimate(real const &x)
  249. {
  250. return m_estimate.eval(x);
  251. }
  252. real remez_solver::eval_func(real const &x)
  253. {
  254. return m_func.eval(x * m_k2 + m_k1);
  255. }
  256. real remez_solver::eval_weight(real const &x)
  257. {
  258. return m_has_weight ? m_weight.eval(x * m_k2 + m_k1) : real(1);
  259. }
  260. real remez_solver::eval_error(real const &x)
  261. {
  262. return fabs((eval_estimate(x) - eval_func(x)) / eval_weight(x));
  263. }