Main Page   Namespace List   Class Hierarchy   Alphabetical List   Compound List   File List   Namespace Members   Compound Members   File Members   Related Pages  

more/math/special_functions.h

Go to the documentation of this file.
00001 //  more/special_functions.h -- special mathematical functions
00002 //  Copyright (C) 1999--2001  Petter Urkedal (petter.urkedal@matfys.lth.se)
00003 
00004 //  This file is free software; you can redistribute it and/or modify
00005 //  it under the terms of the GNU General Public License as published by
00006 //  the Free Software Foundation; either version 2 of the License, or
00007 //  (at your option) any later version.
00008 
00009 //  This file is distributed in the hope that it will be useful,
00010 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 //  GNU General Public License for more details.
00013 
00014 //  You should have received a copy of the GNU General Public License
00015 //  along with this program; if not, write to the Free Software
00016 //  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017 
00018 //  As a special exception, you may use this file as part of a free
00019 //  software library without restriction.  Specifically, if other files
00020 //  instantiate templates or use macros or inline functions from this
00021 //  file, or you compile this file and link it with other files to
00022 //  produce an executable, this file does not by itself cause the
00023 //  resulting executable to be covered by the GNU General Public
00024 //  License.  This exception does not however invalidate any other
00025 //  reasons why the executable file might be covered by the GNU General
00026 //  Public License.
00027 
00028 //  $Id: special_functions.h,v 1.1 2002/05/30 18:01:38 petter_urkedal Exp $
00029 
00030 
00031 #ifndef MORE_SPECIAL_FUNCTIONS_H
00032 #define MORE_SPECIAL_FUNCTIONS_H
00033 
00034 #include <more/math/qtypes.h>
00035 #include <more/math/math.h>
00036 #include <more/math/complex.h>
00037 
00038 
00039 //  OBS:  The hypergeometric and legP with non-integer indices does not
00040 //  work.
00041 
00042 namespace more {
00043 namespace math {
00044 
00045   // until we have numeric_limits
00046   double mathfunc_precision(double*) { return 1e-10; }
00047   float mathfunc_precision(float*) { return 1e-6; }
00048 
00049   // Chose the most efficient of abs() and norm().
00050   template<typename T> inline T abs_or_norm(T const& x) { return abs(x); }
00051   template<typename T> inline T abs_or_norm(complex<T> const& x)
00052     { return norm(x); }
00053 
00054   extern "C" {
00055       double f1c(int);
00056       double f2c(int);
00057       double fhc(int);
00058       double f1h(int);
00059   }
00060 
00061   //  --- hypergeometric ---
00062 
00063   // T and U should be float, double, complex<float> or complex<double>
00064   // T may also be int or halfint.
00065   template<typename T, typename U> inline U
00066     nonterminating_hypergeometric(T alpha, T beta, T gamma, U z) {
00067         typedef typename norm_type_<T>::eval RT;
00068         typedef typename norm_type_<U>::eval RU;
00069         RT test = real(alpha+beta-gamma);
00070         if(abs(z) > RU(1) || test >= RT(1) || test >= RT(0) && z == U(1))
00071             throw runtime_error(
00072                 "arguments to hypergeometric is out of range");
00073 
00074         U sum = one;
00075         U term = one;
00076         T fact = one;
00077         do {
00078             term *= alpha*beta*z;
00079             term /= gamma*fact;
00080             sum += term;
00081             alpha += T(1);
00082             beta += T(1);
00083             gamma += T(1);
00084             fact += T(1);
00085         } while(abs(term) >= abs(sum)*mathfunc_precision((RU*)0)
00086                 && fact < T(500));
00087         assert(fact < T(500));
00088         return sum;
00089     }
00090 
00091   template<typename T, typename U> inline U
00092     terminating_hypergeometric(int alpha, T beta, T gamma, U z) {
00093         assert(alpha <= 0);
00094         U sum = one;
00095         U term = one;
00096         T fact = one;
00097         while(alpha) {
00098             term *= alpha*beta*z;
00099             term /= gamma*fact;
00100             sum += term;
00101             ++alpha;
00102             beta += T(1);
00103             gamma += T(1);
00104             fact += T(1);
00105         }
00106         return sum;
00107     }
00108 
00109   //  2-specialized
00110   template<typename T, typename U> inline U
00111     hypergeometric(halfint alpha, halfint beta, T gamma, U z) {
00112         if(mod1(alpha) == 0 && alpha <= 0)
00113             return terminating_hypergeometric(itrunc(alpha), beta, gamma, z);
00114         else if(mod1(beta) == 0 && beta <= 0)
00115             return terminating_hypergeometric(itrunc(beta), alpha, gamma, z);
00116         else
00117             return nontermating_hypergeometric(alpha, beta, gamma, z);
00118     }
00119   template<typename T, typename U> inline U
00120     hypergeometric(int alpha, int beta, T gamma, U z) {
00121         if(alpha <= 0)
00122             return terminating_hypergeometric(alpha, beta, gamma, z);
00123         if(beta <= 0)
00124             return terminating_hypergeometric(beta, alpha, gamma, z);
00125         else
00126             return nonterminating_hypergeometric(alpha, beta, gamma, z);
00127     }
00128   //  1-specialized
00129   template<typename T1, typename T2, typename U> inline U
00130     hypergeometric(halfint alpha, T1 beta, T2 gamma, U z) {
00131         if(mod1(alpha) == 0 && alpha <= 0)
00132             return terminating_hypergeometric(itrunc(alpha), beta, gamma, z);
00133         else
00134             return nontermating_hypergeometric(alpha, beta, gamma, z);
00135     }
00136   template<typename T1, typename T2, typename U> inline U
00137     hypergeometric(int alpha, T1 beta, T2 gamma, U z) {
00138         if(alpha <= 0)
00139             return terminating_hypergeometric(alpha, beta, gamma, z);
00140         else
00141             return nonterminating_hypergeometric(alpha, beta, gamma, z);
00142     }
00143   template<typename T1, typename T2, typename U> inline U
00144     hypergeometric(T1 alpha, int beta, T2 gamma, U z) {
00145         return hypergeometric(beta, alpha, gamma, z);
00146     }
00147   template<typename T1, typename T2, typename U> inline U
00148     hypergeometric(T1 alpha, halfint beta, T2 gamma, U z) {
00149         return hypergeometric(beta, alpha, gamma, z);
00150     }
00151   //  generic
00152   template<typename T1, typename T2, typename T3, typename U> inline U
00153     hypergeometric(T1 alpha, T2 beta, T3 gamma, U z) {
00154         return nonterminating_hypergeometric(alpha, beta, gamma, z);
00155     }
00156 
00157 
00158   //  --- gamma ---
00159 
00160   //  To extend lgamma (if present) to the complex plane,
00161   template<typename T> inline T lgamma(T z) {
00162       if(real(z) < 10.0) {
00163           T q = 1.0;
00164           do {
00165               q *= z;
00166               z += 1.0;
00167           } while(real(z) < 10.0);
00168           return lgamma(z) - log(q);
00169       }
00170 
00171       // Stirling's series for z! = Gamma(z+1)
00172 
00173       z -= 1.0;
00174       T sum = (z+.5)*log(z) + .5*numbers::log_2pi - z;
00175       const T coeff[] = {
00176           .0833333333333333333333333333333333333333, //  B_2/2
00177           -.0027777777777777777777777777777777777777,// -B_4/(4*3)
00178           .0007936507936507936507936507936507936507, //  B_6/(6*5)
00179           -.0005952380952380952380952380952380952380,// -B_8/(8*7)
00180           .0008417508417508417508417508417508417508, //  B_10/(10*9)
00181           -.0019175269175269175269175269175269175269 // -B_12/(11*10)
00182       };
00183       T zinvn = 1.0/z;
00184       T zfact = zinvn*zinvn;
00185       sum += zinvn*coeff[0];
00186       for(int i = 1; i < 6; ++i) {
00187           zinvn *= zfact;
00188           sum += zinvn*coeff[i];
00189       }
00190       return sum;
00191   }
00192   template<typename T> inline T gamma(T z) { return exp(lgamma(z)); }
00193   inline int gamma(int i) { return factorial(i-1); }
00194   inline double lgamma(int i) { return log((double)factorial(i-1)); }
00195 
00196 
00197   //  --- associated Legendre functions ---
00198 
00199   template<typename T1, typename T2, typename U>
00200     inline U legP(T1 nu, T2 mu, U z) {
00201         return pow((z+U(1))/(z-U(1)), half*mu);
00202         return hypergeometric(-nu, nu+1, 1-mu, half*(U(1)-z))/(T2(1)-mu);
00203     }
00204 
00205 //   template<typename Real>
00206 //     inline Real legP(int m, int n, Real x) {
00207 //      assert(m >= 0);
00208 //      return (m%2? -1 : 1)*factorial(n+m, n-m)/factorial(m)
00209 //          *pow(sqrt(1-x*x)/2, m);
00210 //     }
00211 
00212   template<typename T>
00213     inline T legP(int n, int m, T x) {
00214         assert(n-m >= 0); // or: if(n-m < 0) return 0;
00215         int i0 = (n-m)%2;
00216         int n_plus_m_plus_i_minus_1 = n+m+i0-1;
00217         int n_minus_m = n-m;
00218         int n_minus_m_minus_i_plus_2 = n_minus_m-i0+2;
00219         T sqr_x = x*x;
00220         T term = n_plus_m_plus_i_minus_1 < 2? 1
00221             : /*double_factorial*/f2c(n_plus_m_plus_i_minus_1);
00222         term /= n_minus_m-i0 < 2? 1 : /*double_factorial*/f2c(n_minus_m-i0);
00223         term *= pow(1-sqr_x, half*m);
00224         if(i0) term *= x;
00225         if((n+m-i0)%4) term = -term;
00226         T sum = term;
00227         for(int i = i0+2; i <= n_minus_m; i += 2) {
00228             n_plus_m_plus_i_minus_1 += 2;
00229             n_minus_m_minus_i_plus_2 -= 2;
00230             term *= sqr_x;
00231             term *= -n_plus_m_plus_i_minus_1*n_minus_m_minus_i_plus_2;
00232             term /= i*(i-1);
00233             sum += term;
00234         }
00235         return sum;
00236     }
00237   //  These were used for testing.  Maybe we don't need them.
00238 #ifdef MORE_ORTHOPOLY_SPECIAL_CASES
00239   template<typename T>
00240     inline T legP_0(T x) { return 1; }
00241   template<typename T>
00242     inline T legP_1(T x) { return x; }
00243   template<typename T>
00244     inline T legP_2(T x) { return T(1.5)*x*x-T(0.5); }
00245   template<typename T>
00246     inline T legP_3(T x) { return (T(2.5)*x*x-T(1.5))*x; }
00247   template<typename T>
00248     inline T legP_1_1(T x) { return -sqrt(T(1)-x*x); }
00249   template<typename T>
00250     inline T legP_2_1(T x) { return -T(3)*sqrt(T(1)-x*x)*x; }
00251   template<typename T>
00252     inline T legP_2_2(T x) { return T(3)*(T(1)-x*x); }
00253   template<typename T>
00254     inline T legP_3_1(T x) {
00255         T sqr_x = x*x;
00256         return -T(1.5)*sqrt(T(1)-sqr_x)*(T(5)*sqr_x-T(1));
00257     }
00258   template<typename T>
00259     inline T legP_3_2(T x) { return T(15)*(T(1)-x*x)*x; }
00260   template<typename T>
00261     inline T legP_3_3(T x) { return -T(15)*pow(T(1)-x*x, 3*half); }
00262 #endif
00263 
00264   //  This could be specialized to avoid the extra computation of
00265   //  the factorials in legP.
00266   template<typename T>
00267     inline complex<typename norm_type_<T>::eval>
00268     sphY(int l, int m, T theta, T phi) {
00269         int abs_m = abs(m);
00270         T c = (2*l+1)/(numbers::p_4_pi*/*factorial(l+abs_m, l-abs_m)*/f1c(l+abs_m)/f1c(l-abs_m));
00271         c = sqrt(c);
00272         if(m%2) c = -c;
00273         return c*exp(onei*((T)m*phi))*legP(l, abs_m, cos(theta));
00274     }
00275 
00276 
00277   //  Hermite polynomials
00278   //  fixme: more efficient implementation
00279   template<typename T>
00280     inline T herH(int n, T const& x) {
00281 #ifdef MORE_THROW_LOGIC_ERROR
00282         if(n < 0) throw std::domain_error
00283                       ("First index of herH must be non-negative.");
00284 #endif
00285         if(n == 0) return T(1);
00286         else if(n == 1) return T(2)*x;
00287         else return T(2)*(x*herH(n-1, x) - T(n-1)*herH(n-2, x));
00288     }
00289 
00290   //  Laguerre polynomials; 1st index non-negative int, 2nd arbitrary
00291   //  fixme: recursive definitions are indeed easy...
00292   template<typename T, typename U>
00293     inline T lagL(int n, U alpha, T x) {
00294 #ifdef MORE_THROW_LOGIC_ERROR
00295         if(n < 0) throw std::domain_error
00296                       ("First index of lagL must be non-negative.");
00297 #endif
00298         if(n == 0) return T(1);
00299         else if(n == 1) return T(alpha+1)-x;
00300         else return ((T(2*n-1+alpha)-x)*lagL(n-1, alpha, x)
00301                      -T(n-1+alpha)*lagL(n-2, alpha, x))/T(n);
00302     }
00303 
00304   template<typename T> T besj(int, T);
00305 }
00306   using namespace math;
00307 }
00308 
00309 #include "special_functions.tcc"
00310 #endif

Generated on Sat Sep 7 19:11:19 2002 for more with Doxygen 1.2.13.1. Doxygen 1.2.13.1 is written and copyright 1997-2002 by Dimitri van Heesch.