remez.hpp

来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 668 行 · 第 1/2 页

HPP
668
字号
//  (C) Copyright John Maddock 2006.//  Use, modification and distribution are subject to the//  Boost Software License, Version 1.0. (See accompanying file//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)#ifndef BOOST_MATH_TOOLS_REMEZ_HPP#define BOOST_MATH_TOOLS_REMEZ_HPP#ifdef _MSC_VER#pragma once#endif#include <boost/math/tools/solve.hpp>#include <boost/math/tools/minima.hpp>#include <boost/math/tools/roots.hpp>#include <boost/math/tools/polynomial.hpp>#include <boost/function/function1.hpp>#include <boost/scoped_array.hpp>#include <boost/math/constants/constants.hpp>#include <boost/math/policies/policy.hpp>namespace boost{ namespace math{ namespace tools{namespace detail{//// The error function: the difference between F(x) and// the current approximation.  This is the function// for which we must find the extema.//template <class T>struct remez_error_function{   typedef boost::function1<T, T const &> function_type;public:   remez_error_function(      function_type f_,       const polynomial<T>& n,       const polynomial<T>& d,       bool rel_err)         : f(f_), numerator(n), denominator(d), rel_error(rel_err) {}   T operator()(const T& z)const   {      T y = f(z);      T abs = y - (numerator.evaluate(z) / denominator.evaluate(z));      T err;      if(rel_error)      {         if(y != 0)            err = abs / fabs(y);         else if(0 == abs)         {            // we must be at a root, or it's not recoverable:            BOOST_ASSERT(0 == abs);            err = 0;         }         else         {            // We have a divide by zero!            // Lets assume that f(x) is zero as a result of            // internal cancellation error that occurs as a result            // of shifting a root at point z to the origin so that            // the approximation can be "pinned" to pass through            // the origin: in that case it really            // won't matter what our approximation calculates here            // as long as it's a small number, return the absolute error:            err = abs;         }      }      else         err = abs;      return err;   }private:   function_type f;   polynomial<T> numerator;   polynomial<T> denominator;   bool rel_error;};//// This function adapts the error function so that it's minima// are the extema of the error function.  We can find the minima// with standard techniques.//template <class T>struct remez_max_error_function{   remez_max_error_function(const remez_error_function<T>& f)      : func(f) {}   T operator()(const T& x)   {      BOOST_MATH_STD_USING      return -fabs(func(x));   }private:   remez_error_function<T> func;};} // detailtemplate <class T>class remez_minimax{public:   typedef boost::function1<T, T const &> function_type;   typedef boost::numeric::ublas::vector<T> vector_type;   typedef boost::numeric::ublas::matrix<T> matrix_type;   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);   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);   void reset(unsigned oN, unsigned oD, T a, T b, bool pin = true, bool rel_err = false, int sk = 0, int bits = 0);   void reset(unsigned oN, unsigned oD, T a, T b, bool pin, bool rel_err, int sk, int bits, const vector_type& points);   void set_brake(int b)   {      BOOST_ASSERT(b < 100);      BOOST_ASSERT(b >= 0);      m_brake = b;   }   T iterate();   polynomial<T> denominator()const;   polynomial<T> numerator()const;   vector_type const& chebyshev_points()const   {      return control_points;   }   vector_type const& zero_points()const   {      return zeros;   }   T error_term()const   {      return solution[solution.size() - 1];   }   T max_error()const   {      return m_max_error;   }   T max_change()const   {      return m_max_change;   }   void rotate()   {      --orderN;      ++orderD;   }   void rescale(T a, T b)   {      T scale = (b - a) / (max - min);      for(unsigned i = 0; i < control_points.size(); ++i)      {         control_points[i] = (control_points[i] - min) * scale + a;      }      min = a;      max = b;   }private:   void init_chebyshev();   function_type func;            // The function to approximate.   vector_type control_points;    // Current control points to be used for the next iteration.   vector_type solution;          // Solution from the last iteration contains all unknowns including the error term.   vector_type zeros;             // Location of points of zero error from last iteration, plus the two end points.   vector_type maxima;            // Location of maxima of the error function, actually contains the control points used for the last iteration.   T m_max_error;                 // Maximum error found in last approximation.   T m_max_change;                // Maximum change in location of control points after last iteration.   unsigned orderN;               // Order of the numerator polynomial.   unsigned orderD;               // Order of the denominator polynomial.   T min, max;                    // End points of the range to optimise over.   bool rel_error;                // If true optimise for relative not absolute error.   bool pinned;                   // If true the approximation is "pinned" to go through the origin.   unsigned unknowns;             // Total number of unknowns.   int m_precision;               // Number of bits precision to which the zeros and maxima are found.   T m_max_change_history[2];     // Past history of changes to control points.   int m_brake;                     // amount to break by in percentage points.   int m_skew;                      // amount to skew starting points by in percentage points: -100-100};#ifndef BRAKE#define BRAKE 0#endif#ifndef SKEW#define SKEW 0#endiftemplate <class T>void remez_minimax<T>::init_chebyshev(){   BOOST_MATH_STD_USING   //   // Fill in the zeros:   //   unsigned terms = pinned ? orderD + orderN : orderD + orderN + 1;   for(unsigned i = 0; i < terms; ++i)   {      T cheb = cos((2 * terms - 1 - 2 * i) * constants::pi<T>() / (2 * terms));      cheb += 1;      cheb /= 2;      if(m_skew != 0)      {         T p = static_cast<T>(200 + m_skew) / 200;         cheb = pow(cheb, p);      }      cheb *= (max - min);      cheb += min;      zeros[i+1] = cheb;   }   zeros[0] = min;   zeros[unknowns] = max;   // perform a regular interpolation fit:   matrix_type A(terms, terms);   vector_type b(terms);   // fill in the y values:   for(unsigned i = 0; i < b.size(); ++i)   {      b[i] = func(zeros[i+1]);   }   // fill in powers of x evaluated at each of the control points:   unsigned offsetN = pinned ? 0 : 1;   unsigned offsetD = offsetN + orderN;   unsigned maxorder = (std::max)(orderN, orderD);   for(unsigned i = 0; i < b.size(); ++i)   {      T x0 = zeros[i+1];      T x = x0;      if(!pinned)         A(i, 0) = 1;      for(unsigned j = 0; j < maxorder; ++j)      {         if(j < orderN)            A(i, j + offsetN) = x;         if(j < orderD)         {            A(i, j + offsetD) = -x * b[i];         }         x *= x0;      }   }   //   // Now go ahead and solve the expression to get our solution:   //   vector_type l_solution = boost::math::tools::solve(A, b);   // need to add a "fake" error term:   l_solution.resize(unknowns);   l_solution[unknowns-1] = 0;   solution = l_solution;   //   // Now find all the extrema of the error function:   //   detail::remez_error_function<T> Err(func, this->numerator(), this->denominator(), rel_error);   detail::remez_max_error_function<T> Ex(Err);   m_max_error = 0;   int max_err_location = 0;   for(unsigned i = 0; i < unknowns; ++i)   {      std::pair<T, T> r = brent_find_minima(Ex, zeros[i], zeros[i+1], m_precision);      maxima[i] = r.first;      T rel_err = fabs(r.second);      if(rel_err > m_max_error)      {         m_max_error = fabs(r.second);         max_err_location = i;      }   }   control_points = maxima;}template <class T>void remez_minimax<T>::reset(         unsigned oN,          unsigned oD,          T a,          T b,          bool pin,          bool rel_err,          int sk,         int bits){   control_points = vector_type(oN + oD + (pin ? 1 : 2));   solution = control_points;   zeros = vector_type(oN + oD + (pin ? 2 : 3));   maxima = control_points;   orderN = oN;   orderD = oD;   rel_error = rel_err;   pinned = pin;   m_skew = sk;   min = a;   max = b;   m_max_error = 0;   unknowns = orderN + orderD + (pinned ? 1 : 2);   // guess our initial control points:   control_points[0] = min;   control_points[unknowns - 1] = max;   T interval = (max - min) / (unknowns - 1);   T spot = min + interval;   for(unsigned i = 1; i < control_points.size(); ++i)   {      control_points[i] = spot;      spot += interval;   }   solution[unknowns - 1] = 0;   m_max_error = 0;   if(bits == 0)   {      // don't bother about more than float precision:      m_precision = (std::min)(24, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);   }   else   {      // can't be more accurate than half the bits of T:      m_precision = (std::min)(bits, (boost::math::policies::digits<T, boost::math::policies::policy<> >() / 2) - 2);   }   m_max_change_history[0] = m_max_change_history[1] = 1;   init_chebyshev();   // do one iteration whatever:   //iterate();}template <class T>inline remez_minimax<T>::remez_minimax(         typename remez_minimax<T>::function_type f,          unsigned oN, 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?