remez.hpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. // (C) Copyright John Maddock 2006.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_TOOLS_REMEZ_HPP
  6. #define BOOST_MATH_TOOLS_REMEZ_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <boost/math/tools/solve.hpp>
  11. #include <boost/math/tools/minima.hpp>
  12. #include <boost/math/tools/roots.hpp>
  13. #include <boost/math/tools/polynomial.hpp>
  14. #include <boost/function/function1.hpp>
  15. #include <boost/scoped_array.hpp>
  16. #include <boost/math/constants/constants.hpp>
  17. #include <boost/math/policies/policy.hpp>
  18. namespace boost{ namespace math{ namespace tools{
  19. namespace detail{
  20. //
  21. // The error function: the difference between F(x) and
  22. // the current approximation. This is the function
  23. // for which we must find the extema.
  24. //
  25. template <class T>
  26. struct remez_error_function
  27. {
  28. typedef boost::function1<T, T const &> function_type;
  29. public:
  30. remez_error_function(
  31. function_type f_,
  32. const polynomial<T>& n,
  33. const polynomial<T>& d,
  34. bool rel_err)
  35. : f(f_), numerator(n), denominator(d), rel_error(rel_err) {}
  36. T operator()(const T& z)const
  37. {
  38. T y = f(z);
  39. T abs = y - (numerator.evaluate(z) / denominator.evaluate(z));
  40. T err;
  41. if(rel_error)
  42. {
  43. if(y != 0)
  44. err = abs / fabs(y);
  45. else if(0 == abs)
  46. {
  47. // we must be at a root, or it's not recoverable:
  48. BOOST_ASSERT(0 == abs);
  49. err = 0;
  50. }
  51. else
  52. {
  53. // We have a divide by zero!
  54. // Lets assume that f(x) is zero as a result of
  55. // internal cancellation error that occurs as a result
  56. // of shifting a root at point z to the origin so that
  57. // the approximation can be "pinned" to pass through
  58. // the origin: in that case it really
  59. // won't matter what our approximation calculates here
  60. // as long as it's a small number, return the absolute error:
  61. err = abs;
  62. }
  63. }
  64. else
  65. err = abs;
  66. return err;
  67. }
  68. private:
  69. function_type f;
  70. polynomial<T> numerator;
  71. polynomial<T> denominator;
  72. bool rel_error;
  73. };
  74. //
  75. // This function adapts the error function so that it's minima
  76. // are the extema of the error function. We can find the minima
  77. // with standard techniques.
  78. //
  79. template <class T>
  80. struct remez_max_error_function
  81. {
  82. remez_max_error_function(const remez_error_function<T>& f)
  83. : func(f) {}
  84. T operator()(const T& x)
  85. {
  86. BOOST_MATH_STD_USING
  87. return -fabs(func(x));
  88. }
  89. private:
  90. remez_error_function<T> func;
  91. };
  92. } // detail
  93. template <class T>
  94. class remez_minimax
  95. {
  96. public:
  97. typedef boost::function1<T, T const &> function_type;
  98. typedef boost::numeric::ublas::vector<T> vector_type;
  99. typedef boost::numeric::ublas::matrix<T> matrix_type;
  100. remez_minimax(function_type f, unsigned oN, unsigned oD, T a, T b, bool pin = true, bool rel_err = false, int sk = 0, int bits = 0);
  101. remez_minimax(function_type f, unsigned oN, unsigned oD, T a, T b, bool pin, bool rel_err, int sk, int bits, const vector_type& points);
  102. void reset(unsigned oN, unsigned oD, T a, T b, bool pin = true, bool rel_err = false, int sk = 0, int bits = 0);
  103. void reset(unsigned oN, unsigned oD, T a, T b, bool pin, bool rel_err, int sk, int bits, const vector_type& points);
  104. void set_brake(int b)
  105. {
  106. BOOST_ASSERT(b < 100);
  107. BOOST_ASSERT(b >= 0);
  108. m_brake = b;
  109. }
  110. T iterate();
  111. polynomial<T> denominator()const;
  112. polynomial<T> numerator()const;
  113. vector_type const& chebyshev_points()const
  114. {
  115. return control_points;
  116. }
  117. vector_type const& zero_points()const
  118. {
  119. return zeros;
  120. }
  121. T error_term()const
  122. {
  123. return solution[solution.size() - 1];
  124. }
  125. T max_error()const
  126. {
  127. return m_max_error;
  128. }
  129. T max_change()const
  130. {
  131. return m_max_change;
  132. }
  133. void rotate()
  134. {
  135. --orderN;
  136. ++orderD;
  137. }
  138. void rescale(T a, T b)
  139. {
  140. T scale = (b - a) / (max - min);
  141. for(unsigned i = 0; i < control_points.size(); ++i)
  142. {
  143. control_points[i] = (control_points[i] - min) * scale + a;
  144. }
  145. min = a;
  146. max = b;
  147. }
  148. private:
  149. void init_chebyshev();
  150. function_type func; // The function to approximate.
  151. vector_type control_points; // Current control points to be used for the next iteration.
  152. vector_type solution; // Solution from the last iteration contains all unknowns including the error term.
  153. vector_type zeros; // Location of points of zero error from last iteration, plus the two end points.
  154. vector_type maxima; // Location of maxima of the error function, actually contains the control points used for the last iteration.
  155. T m_max_error; // Maximum error found in last approximation.
  156. T m_max_change; // Maximum change in location of control points after last iteration.
  157. unsigned orderN; // Order of the numerator polynomial.
  158. unsigned orderD; // Order of the denominator polynomial.
  159. T min, max; // End points of the range to optimise over.
  160. bool rel_error; // If true optimise for relative not absolute error.
  161. bool pinned; // If true the approximation is "pinned" to go through the origin.
  162. unsigned unknowns; // Total number of unknowns.
  163. int m_precision; // Number of bits precision to which the zeros and maxima are found.
  164. T m_max_change_history[2]; // Past history of changes to control points.
  165. int m_brake; // amount to break by in percentage points.
  166. int m_skew; // amount to skew starting points by in percentage points: -100-100
  167. };
  168. #ifndef BRAKE
  169. #define BRAKE 0
  170. #endif
  171. #ifndef SKEW
  172. #define SKEW 0
  173. #endif
  174. template <class T>
  175. void remez_minimax<T>::init_chebyshev()
  176. {
  177. BOOST_MATH_STD_USING
  178. //
  179. // Fill in the zeros:
  180. //
  181. unsigned terms = pinned ? orderD + orderN : orderD + orderN + 1;
  182. for(unsigned i = 0; i < terms; ++i)
  183. {
  184. T cheb = cos((2 * terms - 1 - 2 * i) * constants::pi<T>() / (2 * terms));
  185. cheb += 1;
  186. cheb /= 2;
  187. if(m_skew != 0)
  188. {
  189. T p = static_cast<T>(200 + m_skew) / 200;
  190. cheb = pow(cheb, p);
  191. }
  192. cheb *= (max - min);
  193. cheb += min;
  194. zeros[i+1] = cheb;
  195. }
  196. zeros[0] = min;
  197. zeros[unknowns] = max;
  198. // perform a regular interpolation fit:
  199. matrix_type A(terms, terms);
  200. vector_type b(terms);
  201. // fill in the y values:
  202. for(unsigned i = 0; i < b.size(); ++i)
  203. {
  204. b[i] = func(zeros[i+1]);
  205. }
  206. // fill in powers of x evaluated at each of the control points:
  207. unsigned offsetN = pinned ? 0 : 1;
  208. unsigned offsetD = offsetN + orderN;
  209. unsigned maxorder = (std::max)(orderN, orderD);
  210. for(unsigned i = 0; i < b.size(); ++i)
  211. {
  212. T x0 = zeros[i+1];
  213. T x = x0;
  214. if(!pinned)
  215. A(i, 0) = 1;
  216. for(unsigned j = 0; j < maxorder; ++j)
  217. {
  218. if(j < orderN)
  219. A(i, j + offsetN) = x;
  220. if(j < orderD)
  221. {
  222. A(i, j + offsetD) = -x * b[i];
  223. }
  224. x *= x0;
  225. }
  226. }
  227. //
  228. // Now go ahead and solve the expression to get our solution:
  229. //
  230. vector_type l_solution = boost::math::tools::solve(A, b);
  231. // need to add a "fake" error term:
  232. l_solution.resize(unknowns);
  233. l_solution[unknowns-1] = 0;
  234. solution = l_solution;
  235. //
  236. // Now find all the extrema of the error function:
  237. //
  238. detail::remez_error_function<T> Err(func, this->numerator(), this->denominator(), rel_error);
  239. detail::remez_max_error_function<T> Ex(Err);
  240. m_max_error = 0;
  241. int max_err_location = 0;
  242. for(unsigned i = 0; i < unknowns; ++i)
  243. {
  244. std::pair<T, T> r = brent_find_minima(Ex, zeros[i], zeros[i+1], m_precision);
  245. maxima[i] = r.first;
  246. T rel_err = fabs(r.second);
  247. if(rel_err > m_max_error)
  248. {
  249. m_max_error = fabs(r.second);
  250. max_err_location = i;
  251. }
  252. }
  253. control_points = maxima;
  254. }
  255. template <class T>
  256. void remez_minimax<T>::reset(
  257. unsigned oN,
  258. unsigned oD,
  259. T a,
  260. T b,
  261. bool pin,
  262. bool rel_err,
  263. int sk,
  264. int bits)
  265. {
  266. control_points = vector_type(oN + oD + (pin ? 1 : 2));
  267. solution = control_points;
  268. zeros = vector_type(oN + oD + (pin ? 2 : 3));
  269. maxima = control_points;
  270. orderN = oN;
  271. orderD = oD;
  272. rel_error = rel_err;
  273. pinned = pin;
  274. m_skew = sk;
  275. min = a;
  276. max = b;
  277. m_max_error = 0;
  278. unknowns = orderN + orderD + (pinned ? 1 : 2);
  279. // guess our initial control points:
  280. control_points[0] = min;
  281. control_points[unknowns - 1] = max;
  282. T interval = (max - min) / (unknowns - 1);
  283. T spot = min + interval;
  284. for(unsigned i = 1; i < control_points.size(); ++i)
  285. {
  286. control_points[i] = spot;
  287. spot += interval;
  288. }
  289. solution[unknowns - 1] = 0;
  290. m_max_error = 0;
  291. if(bits == 0)
  292. {
  293. // don't bother about more than float precision:
  294. m_precision = (std::min)(24, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);
  295. }
  296. else
  297. {
  298. // can't be more accurate than half the bits of T:
  299. m_precision = (std::min)(bits, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);
  300. }
  301. m_max_change_history[0] = m_max_change_history[1] = 1;
  302. init_chebyshev();
  303. // do one iteration whatever:
  304. //iterate();
  305. }
  306. template <class T>
  307. inline remez_minimax<T>::remez_minimax(
  308. typename remez_minimax<T>::function_type f,
  309. unsigned oN,
  310. unsigned oD,
  311. T a,
  312. T b,
  313. bool pin,
  314. bool rel_err,
  315. int sk,
  316. int bits)
  317. : func(f)
  318. {
  319. m_brake = 0;
  320. reset(oN, oD, a, b, pin, rel_err, sk, bits);
  321. }
  322. template <class T>
  323. void remez_minimax<T>::reset(
  324. unsigned oN,
  325. unsigned oD,
  326. T a,
  327. T b,
  328. bool pin,
  329. bool rel_err,
  330. int sk,
  331. int bits,
  332. const vector_type& points)
  333. {
  334. control_points = vector_type(oN + oD + (pin ? 1 : 2));
  335. solution = control_points;
  336. zeros = vector_type(oN + oD + (pin ? 2 : 3));
  337. maxima = control_points;
  338. orderN = oN;
  339. orderD = oD;
  340. rel_error = rel_err;
  341. pinned = pin;
  342. m_skew = sk;
  343. min = a;
  344. max = b;
  345. m_max_error = 0;
  346. unknowns = orderN + orderD + (pinned ? 1 : 2);
  347. control_points = points;
  348. solution[unknowns - 1] = 0;
  349. m_max_error = 0;
  350. if(bits == 0)
  351. {
  352. // don't bother about more than float precision:
  353. m_precision = (std::min)(24, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);
  354. }
  355. else
  356. {
  357. // can't be more accurate than half the bits of T:
  358. m_precision = (std::min)(bits, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);
  359. }
  360. m_max_change_history[0] = m_max_change_history[1] = 1;
  361. // do one iteration whatever:
  362. //iterate();
  363. }
  364. template <class T>
  365. inline remez_minimax<T>::remez_minimax(
  366. typename remez_minimax<T>::function_type f,
  367. unsigned oN,
  368. unsigned oD,
  369. T a,
  370. T b,
  371. bool pin,
  372. bool rel_err,
  373. int sk,
  374. int bits,
  375. const vector_type& points)
  376. : func(f)
  377. {
  378. m_brake = 0;
  379. reset(oN, oD, a, b, pin, rel_err, sk, bits, points);
  380. }
  381. template <class T>
  382. T remez_minimax<T>::iterate()
  383. {
  384. BOOST_MATH_STD_USING
  385. matrix_type A(unknowns, unknowns);
  386. vector_type b(unknowns);
  387. // fill in evaluation of f(x) at each of the control points:
  388. for(unsigned i = 0; i < b.size(); ++i)
  389. {
  390. // take care that none of our control points are at the origin:
  391. if(pinned && (control_points[i] == 0))
  392. {
  393. if(i)
  394. control_points[i] = control_points[i-1] / 3;
  395. else
  396. control_points[i] = control_points[i+1] / 3;
  397. }
  398. b[i] = func(control_points[i]);
  399. }
  400. T err_err;
  401. unsigned convergence_count = 0;
  402. do{
  403. // fill in powers of x evaluated at each of the control points:
  404. int sign = 1;
  405. unsigned offsetN = pinned ? 0 : 1;
  406. unsigned offsetD = offsetN + orderN;
  407. unsigned maxorder = (std::max)(orderN, orderD);
  408. T Elast = solution[unknowns - 1];
  409. for(unsigned i = 0; i < b.size(); ++i)
  410. {
  411. T x0 = control_points[i];
  412. T x = x0;
  413. if(!pinned)
  414. A(i, 0) = 1;
  415. for(unsigned j = 0; j < maxorder; ++j)
  416. {
  417. if(j < orderN)
  418. A(i, j + offsetN) = x;
  419. if(j < orderD)
  420. {
  421. T mult = rel_error ? T(b[i] - sign * fabs(b[i]) * Elast): T(b[i] - sign * Elast);
  422. A(i, j + offsetD) = -x * mult;
  423. }
  424. x *= x0;
  425. }
  426. // The last variable to be solved for is the error term,
  427. // sign changes with each control point:
  428. T E = rel_error ? T(sign * fabs(b[i])) : T(sign);
  429. A(i, unknowns - 1) = E;
  430. sign = -sign;
  431. }
  432. #ifdef BOOST_MATH_INSTRUMENT
  433. for(unsigned i = 0; i < b.size(); ++i)
  434. std::cout << b[i] << " ";
  435. std::cout << "\n\n";
  436. for(unsigned i = 0; i < b.size(); ++i)
  437. {
  438. for(unsigned j = 0; j < b.size(); ++ j)
  439. std::cout << A(i, j) << " ";
  440. std::cout << "\n";
  441. }
  442. std::cout << std::endl;
  443. #endif
  444. //
  445. // Now go ahead and solve the expression to get our solution:
  446. //
  447. solution = boost::math::tools::solve(A, b);
  448. err_err = (Elast != 0) ? T(fabs((fabs(solution[unknowns-1]) - fabs(Elast)) / fabs(Elast))) : T(1);
  449. }while(orderD && (convergence_count++ < 80) && (err_err > 0.001));
  450. //
  451. // Perform a sanity check to verify that the solution to the equations
  452. // is not so much in error as to be useless. The matrix inversion can
  453. // be very close to singular, so this can be a real problem.
  454. //
  455. vector_type sanity = prod(A, solution);
  456. for(unsigned i = 0; i < b.size(); ++i)
  457. {
  458. T err = fabs((b[i] - sanity[i]) / fabs(b[i]));
  459. if(err > sqrt(epsilon<T>()))
  460. {
  461. std::cerr << "Sanity check failed: more than half the digits in the found solution are in error." << std::endl;
  462. }
  463. }
  464. //
  465. // Next comes another sanity check, we want to verify that all the control
  466. // points do actually alternate in sign, in practice we may have
  467. // additional roots in the error function that cause this to fail.
  468. // Failure here is always fatal: even though this code attempts to correct
  469. // the problem it usually only postpones the inevitable.
  470. //
  471. polynomial<T> num, denom;
  472. num = this->numerator();
  473. denom = this->denominator();
  474. T e1 = b[0] - num.evaluate(control_points[0]) / denom.evaluate(control_points[0]);
  475. #ifdef BOOST_MATH_INSTRUMENT
  476. std::cout << e1;
  477. #endif
  478. for(unsigned i = 1; i < b.size(); ++i)
  479. {
  480. T e2 = b[i] - num.evaluate(control_points[i]) / denom.evaluate(control_points[i]);
  481. #ifdef BOOST_MATH_INSTRUMENT
  482. std::cout << " " << e2;
  483. #endif
  484. if(e2 * e1 > 0)
  485. {
  486. std::cerr << std::flush << "Basic sanity check failed: Error term does not alternate in sign, non-recoverable error may follow..." << std::endl;
  487. T perturbation = 0.05;
  488. do{
  489. T point = control_points[i] * (1 - perturbation) + control_points[i-1] * perturbation;
  490. e2 = func(point) - num.evaluate(point) / denom.evaluate(point);
  491. if(e2 * e1 < 0)
  492. {
  493. control_points[i] = point;
  494. break;
  495. }
  496. perturbation += 0.05;
  497. }while(perturbation < 0.8);
  498. if((e2 * e1 > 0) && (i + 1 < b.size()))
  499. {
  500. perturbation = 0.05;
  501. do{
  502. T point = control_points[i] * (1 - perturbation) + control_points[i+1] * perturbation;
  503. e2 = func(point) - num.evaluate(point) / denom.evaluate(point);
  504. if(e2 * e1 < 0)
  505. {
  506. control_points[i] = point;
  507. break;
  508. }
  509. perturbation += 0.05;
  510. }while(perturbation < 0.8);
  511. }
  512. }
  513. e1 = e2;
  514. }
  515. #ifdef BOOST_MATH_INSTRUMENT
  516. for(unsigned i = 0; i < solution.size(); ++i)
  517. std::cout << solution[i] << " ";
  518. std::cout << std::endl << this->numerator() << std::endl;
  519. std::cout << this->denominator() << std::endl;
  520. std::cout << std::endl;
  521. #endif
  522. //
  523. // The next step is to find all the intervals in which our maxima
  524. // lie:
  525. //
  526. detail::remez_error_function<T> Err(func, this->numerator(), this->denominator(), rel_error);
  527. zeros[0] = min;
  528. zeros[unknowns] = max;
  529. for(unsigned i = 1; i < control_points.size(); ++i)
  530. {
  531. eps_tolerance<T> tol(m_precision);
  532. boost::uintmax_t max_iter = 1000;
  533. std::pair<T, T> p = toms748_solve(
  534. Err,
  535. control_points[i-1],
  536. control_points[i],
  537. tol,
  538. max_iter);
  539. zeros[i] = (p.first + p.second) / 2;
  540. //zeros[i] = bisect(Err, control_points[i-1], control_points[i], m_precision);
  541. }
  542. //
  543. // Now find all the extrema of the error function:
  544. //
  545. detail::remez_max_error_function<T> Ex(Err);
  546. m_max_error = 0;
  547. int max_err_location = 0;
  548. for(unsigned i = 0; i < unknowns; ++i)
  549. {
  550. std::pair<T, T> r = brent_find_minima(Ex, zeros[i], zeros[i+1], m_precision);
  551. maxima[i] = r.first;
  552. T rel_err = fabs(r.second);
  553. if(rel_err > m_max_error)
  554. {
  555. m_max_error = fabs(r.second);
  556. max_err_location = i;
  557. }
  558. }
  559. //
  560. // Almost done now! we just need to set our control points
  561. // to the extrema, and calculate how much each point has changed
  562. // (this will be our termination condition):
  563. //
  564. swap(control_points, maxima);
  565. m_max_change = 0;
  566. int max_change_location = 0;
  567. for(unsigned i = 0; i < unknowns; ++i)
  568. {
  569. control_points[i] = (control_points[i] * (100 - m_brake) + maxima[i] * m_brake) / 100;
  570. T change = fabs((control_points[i] - maxima[i]) / control_points[i]);
  571. #if 0
  572. if(change > m_max_change_history[1])
  573. {
  574. // divergence!!! try capping the change:
  575. std::cerr << "Possible divergent step, change will be capped!!" << std::endl;
  576. change = m_max_change_history[1];
  577. if(control_points[i] < maxima[i])
  578. control_points[i] = maxima[i] - change * maxima[i];
  579. else
  580. control_points[i] = maxima[i] + change * maxima[i];
  581. }
  582. #endif
  583. if(change > m_max_change)
  584. {
  585. m_max_change = change;
  586. max_change_location = i;
  587. }
  588. }
  589. //
  590. // store max change information:
  591. //
  592. m_max_change_history[0] = m_max_change_history[1];
  593. m_max_change_history[1] = fabs(m_max_change);
  594. return m_max_change;
  595. }
  596. template <class T>
  597. polynomial<T> remez_minimax<T>::numerator()const
  598. {
  599. boost::scoped_array<T> a(new T[orderN + 1]);
  600. if(pinned)
  601. a[0] = 0;
  602. unsigned terms = pinned ? orderN : orderN + 1;
  603. for(unsigned i = 0; i < terms; ++i)
  604. a[pinned ? i+1 : i] = solution[i];
  605. return boost::math::tools::polynomial<T>(&a[0], orderN);
  606. }
  607. template <class T>
  608. polynomial<T> remez_minimax<T>::denominator()const
  609. {
  610. unsigned terms = orderD + 1;
  611. unsigned offsetD = pinned ? orderN : (orderN + 1);
  612. boost::scoped_array<T> a(new T[terms]);
  613. a[0] = 1;
  614. for(unsigned i = 0; i < orderD; ++i)
  615. a[i+1] = solution[i + offsetD];
  616. return boost::math::tools::polynomial<T>(&a[0], orderD);
  617. }
  618. }}} // namespaces
  619. #endif // BOOST_MATH_TOOLS_REMEZ_HPP