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