
00001 /*!@file Util/MathFunctions.H Miscellaneous math functions */ 00002 00003 // //////////////////////////////////////////////////////////////////// // 00004 // The iLab Neuromorphic Vision C++ Toolkit - Copyright (C) 2000-2003 // 00005 // by the University of Southern California (USC) and the iLab at USC. // 00006 // See http://iLab.usc.edu for information about this project. // 00007 // //////////////////////////////////////////////////////////////////// // 00008 // Major portions of the iLab Neuromorphic Vision Toolkit are protected // 00009 // under the U.S. patent ``Computation of Intrinsic Perceptual Saliency // 00010 // in Visual Environments, and Applications'' by Christof Koch and // 00011 // Laurent Itti, California Institute of Technology, 2001 (patent // 00012 // pending; application number 09/912,225 filed July 23, 2001; see // 00013 // http://pair.uspto.gov/cgi-bin/final/home.pl for current status). // 00014 // //////////////////////////////////////////////////////////////////// // 00015 // This file is part of the iLab Neuromorphic Vision C++ Toolkit. // 00016 // // 00017 // The iLab Neuromorphic Vision C++ Toolkit is free software; you can // 00018 // redistribute it and/or modify it under the terms of the GNU General // 00019 // Public License as published by the Free Software Foundation; either // 00020 // version 2 of the License, or (at your option) any later version. // 00021 // // 00022 // The iLab Neuromorphic Vision C++ Toolkit is distributed in the hope // 00023 // that it will be useful, but WITHOUT ANY WARRANTY; without even the // 00024 // implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR // 00025 // PURPOSE. See the GNU General Public License for more details. // 00026 // // 00027 // You should have received a copy of the GNU General Public License // 00028 // along with the iLab Neuromorphic Vision C++ Toolkit; if not, write // 00029 // to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, // 00030 // Boston, MA 02111-1307 USA. // 00031 // //////////////////////////////////////////////////////////////////// // 00032 // 00033 // Primary maintainer for this file: Laurent Itti <itti@usc.edu> 00034 // $HeadURL: svn://isvn.usc.edu/software/invt/trunk/saliency/src/Util/MathFunctions.H $ 00035 // $Id: MathFunctions.H 11721 2009-09-18 17:17:24Z lior $ 00036 // 00037 00038 #ifndef MATHFUNCTIONS_H_DEFINED 00039 #define MATHFUNCTIONS_H_DEFINED 00040 00041 #include "Util/Assert.H" 00042 #include "Util/FastMathFunctions.H" 00043 #include "Util/Promotions.H" 00044 #include "rutz/compat_cmath.h" // for M_PI, M_E if not present in <cmath> 00045 00046 #include <algorithm> // for std::swap() 00047 #include <cmath> 00048 00049 //! Miscellaneous math functions 00050 00051 // ###################################################################### 00052 /*! @Precomputed Constants 128 bits IEEE quad */ 00053 /*! Each constant is 128 bits IEEE quad. On a 64 bit system, 00054 a double is 128 bits. Thus, each number is a double on a modern system 00055 00056 # define M_El 2.7182818284590452353602874713526625L 00057 00058 is given for the value of 'e'. We follow this standard. 00059 00060 Note: 00061 */ 00062 //@{ 00063 #define D_PI 3.1415926535897932384626433832795029 00064 //! e 00065 #define D_E 2.7182818284590452353602874713526625 00066 //! Log Root of 2 Pi 00067 #define D_LOG_SQRT_2_PI 0.9189385332046727417803297364056176 00068 //! Root of 2*Pi 00069 #define D_SQRT_2_PI 2.5066282746310002416123552393401041 00070 //! Root of Pi 00071 #define D_SQRT_PI 1.7724538509055160272981674833411451 00072 //! Root of 2 DEFINED in cmath as M_SQRT2, but is not 128 bit 00073 #define D_SQRT_2 1.4142135623730950488016887242096981 00074 //! Natural Log of 2*Pi 00075 #define D_LOG_2_PI 1.8378770664093454835606594728112353 00076 //! Natural Log of Pi 00077 #define D_LOG_PI 1.1447298858494001741434273513530587 00078 //! Natural Log of 2 DEFINED in cmath as M_LN2, but is not 128 bit 00079 #define D_LOG_2 0.6931471805599453094172321214581766 00080 //! Natural Log of sqrt of 2 00081 #define D_LOG_SQRT_2 0.3465735902799726547086160607290883 00082 //! 2*Pi 00083 #define D_2_PI 6.2831853071795862319959269370883703 00084 //! The Euler-Mascheroni Constant (Not the same as e) 00085 #define D_EULER_GAMMA 0.5772156649015328606065120900824024 00086 //! Number of radians in one degree 00087 #define D_DEGREE 0.0174532925199432957692369076848861 00088 //! The golden ratio 00089 #define D_GOLDEN_RATIO 1.6180339887498948482045868343656381 00090 //! Catalan's Constant 00091 #define D_CATALAN 0.9159655941772190150546035149323841 00092 //! Glaisher-Kinkelin Constant 00093 #define D_GLAISHER 1.2824271291006226368753425688697917 00094 //! Khinchin's Constant 00095 #define D_KHINCHIN 2.6854520010653064453097148354817956 00096 //! Ramanujan-Soldner's Constant 00097 #define D_SOLDNER 1.4513692348833810502839684858920274 00098 //! Apery's Constant 00099 #define D_APERY 1.2020569031595942853997381615114499 00100 //! Gauss-Kuzmin-Wirsing constant 00101 #define D_GAUSS_KUZMAN 0.3036630028987326585974481219015562 00102 //! Fransén-Robinson constant 00103 #define D_FRANSEN 2.8077702420285193652215011865577729 00104 //! Sierpin'ski's constant 00105 #define D_SERPINSKY 2.5849817595792532170658935873831711 00106 //! Mills' constant 00107 #define D_MILL 1.3063778838630806904686144926026057 00108 //! Omega constant 00109 #define D_OMEGA 0.5671432904097838729999686622103555 00110 //! Golomb-Dickman constant 00111 #define D_GOLOMB_DICKMAN 0.6243299885435508709929363831008372 00112 00113 // ###################################################################### 00114 /*! @Precomputed Constants 256 Bit*/ 00115 /*! Each constant is 256 bits, This makes it a double long floating point on any modern 64 bit 00116 CPU. 00117 */ 00118 //@{ 00119 //! Pi 00120 #define L_PI 3.14159265358979323846264338327950288419716939937510582097494459230781L // ... 640628620899862803482534 00121 //! e 00122 #define L_E 2.71828182845904523536028747135266249775724709369995957496696762772407L // ... 663035354759457138 00123 //! Log Root of 2 Pi 00124 #define L_LOG_SQRT_2_PI 0.91893853320467274178032973640561763986139747363778341281715154048276L 00125 //! Root of 2*Pi 00126 #define L_SQRT_2_PI 2.50662827463100050241576528481104525300698674060993831662992357634229L 00127 //! Root of Pi 00128 #define L_SQRT_PI 1.77245385090551602729816748334114518279754945612238712821380778985291L 00129 //! Root of 2 DEFINED in cmath as M_SQRT2, but is not > 128 bit 00130 #define L_SQRT_2 1.41421356237309514547462185873882845044136047363281250000000000000000L 00131 //! Natural Log of 2*Pi 00132 #define L_LOG_2_PI 1.83787706640934548356065947281123527972279494727556682563430308096553L 00133 //! Natural Log of Pi 00134 #define L_LOG_PI 1.14472988584940017414342735135305871164729481291531157151362307147214L 00135 //! Natural Log of 2 DEFINED in cmath as M_LN2, but is not > 128 bit 00136 #define L_LOG_2 0.69314718055994528622676398299518041312694549560546875000000000000000L 00137 //! 2*Pi 00138 #define L_2_PI 6.28318530717958647692528676655900576839433879875021164194988918461560L 00139 //! Number of radians in one degree 00140 #define L_DEGREE 0.01745329251994329576923690768488612713442871888541725456097191440171L // ... 009114603449443682 00141 //! The Euler-Mascheroni Constant (Not the same as e) 00142 #define L_EULER_GAMMA 0.57721566490153286060651209008240243104215933593992359880576723488486L 00143 //! The golden ratio 00144 #define L_GOLDEN_RATIO 1.61803398874989484820458683436563811772030917980576286213544862270526L // ... 046281890244970721 00145 //! Catalan's Constant 00146 #define L_CATALAN 0.91596559417721901505460351493238411077414937428167213426649811962176L // ... 301977625476947936 00147 //! Glaisher-Kinkelin Constant 00148 #define L_GLAISHER 1.28242712910062263687534256886979172776768892732500119206374002174040L // ... 630885882646112974 00149 //! Khinchin's Constant 00150 #define L_KHINCHIN 2.68545200106530644530971483548179569382038229399446295305115234555721L // ... 885953715200280114 00151 //@} 00152 00153 // ###################################################################### 00154 /*! @name Basic numerical operations */ 00155 //@{ 00156 00157 //! Returns -1 if \a x is negative, or 1 if \a x is positve. 00158 template <class T> 00159 inline int signOf(const T& x); 00160 00161 //! Returns the square of \a x. 00162 template <class T> 00163 inline T squareOf(const T& x); 00164 00165 //! Returns the number closest to \a val and between \a bound1 and \a bound2. 00166 /*! The bounds may be specified in either order (i.e., either (lo, hi) or 00167 (hi, lo). */ 00168 template <class T, class T2> 00169 inline T clampValue(const T& val, const T2& bound1, const T2& bound2); 00170 00171 //@} 00172 00173 // ###################################################################### 00174 /*! @name Numerical range and finiteness testing */ 00175 //@{ 00176 00177 //! Function to check for finite value 00178 bool isFinite(const byte& arg); 00179 00180 //! Function to check for finite value 00181 bool isFinite(const int16& arg); 00182 00183 //! Function to check for finite value 00184 bool isFinite(const int32& arg); 00185 00186 //! Function to check for finite value 00187 bool isFinite(const float& arg); 00188 00189 //! Function to check for finite value 00190 bool isFinite(const double& arg); 00191 00192 //@} 00193 00194 // ###################################################################### 00195 /*! @name Random number generation */ 00196 //@{ 00197 00198 //! Initialize random seeds from pseudo-random sources (e.g. time(), getpid()) 00199 void initRandomNumbers(); 00200 00201 //! Initialize random seeds with zero, to produce deterministic pseudo-random sequences 00202 void initRandomNumbersZero(); 00203 00204 //! Generate a random double in [0.0,1.0) 00205 /*! This is a thin wrapper around drand48() or rand(), depending on 00206 which is available on the build platform. */ 00207 double randomDouble(); 00208 00209 //! Generate a random double from a normal distribution with mean zero and standard deviation s 00210 /*! This function call randomDouble() 12 times to sample from normal distribution */ 00211 double randomDoubleFromNormal(const double s); 00212 00213 //! Generate a random integer in [0,n] (i.e., including n) 00214 /*! This is a thin wrapper around lrand48() or rand(), depending on 00215 which is available on the build platform. */ 00216 int randomUpToIncluding(const int n); 00217 00218 //! Generate a random integer in [0,n) (i.e., not including n) 00219 /*! This is a thin wrapper around lrand48() or rand(), depending on 00220 which is available on the build platform. */ 00221 int randomUpToNotIncluding(const int n); 00222 00223 //! Randomly shuffle given array 00224 template <class T> inline 00225 void randShuffle(T* array, const uint size); 00226 00227 //! Random number generator from Numerical Recipes in C book 00228 /*! Long period >2x10^18 random number generator of L Ecuyer with 00229 Bays-Durham shuffle. Return a uniform deviate in the interval 00230 (0,1). Call with int variable as argument set to a negative 00231 value, and don't change this variable between calls unless you 00232 want to reseed the generator. You can get ain initial seed using 00233 the function getIdum() */ 00234 double ran2(int& idum); 00235 00236 //! Get a random seed for ran2 00237 /*! If useRandom is true, a random negative number will be return, 00238 otherwise a negative number that is always the same will be 00239 returned. */ 00240 int getIdum(const bool useRandom = true); 00241 00242 //! Randomly distributed Gaussian deviate with zero mean and unit variance 00243 double gasdev(int& idum); 00244 00245 //! Randomly distributed Exponential deviate 00246 double expdev(int& idum); 00247 00248 //@} 00249 00250 // ###################################################################### 00251 /*! @name Special math functions with iterative implementations */ 00252 //@{ 00253 00254 //! Compute probability of drawing a value k from a Poisson of mean mu 00255 double poisson(const unsigned int k, const double mu); 00256 00257 //! Compute log of Gamma 00258 double lngamma(double x); 00259 00260 //! Compute the psi (derivarive of log of gamma) function (requires x > 0.0) 00261 double psi(const double x); 00262 00263 00264 //@} 00265 00266 // ###################################################################### 00267 /*! @name Scalar math functions corresponding with Pixel math functions in Pixels.h */ 00268 //@{ 00269 00270 template <class T> inline T maxmerge(T A, const T B) 00271 { 00272 if (A < B) A = B; 00273 return A; 00274 } 00275 00276 // ###################################################################### 00277 template <class T> inline T minmerge(T A, const T B) 00278 { 00279 if (A > B) A = B; 00280 return A; 00281 } 00282 00283 // ###################################################################### 00284 //! Secant 00285 template <class T> inline T sec(T A) 00286 { 00287 return clamped_convert<T>(1.0/cos(A)); 00288 } 00289 00290 // ###################################################################### 00291 //! Cosecant 00292 template <class T> inline T cosec(T A) 00293 { 00294 return clamped_convert<T>(1.0/sin(A)); 00295 } 00296 00297 // ###################################################################### 00298 //! Cotangent 00299 template <class T> inline T cotan(T A) 00300 { 00301 return clamped_convert<T>(1.0/tan(A)); 00302 } 00303 00304 // ###################################################################### 00305 //! inverse secant 00306 template <class T> inline T asec(T A) 00307 { 00308 ASSERT(A >= 1.0); 00309 return clamped_convert<T>(acos(1.0/A)); 00310 } 00311 00312 // ###################################################################### 00313 //! inverse cosecant 00314 template <class T> inline T acosec(T A) 00315 { 00316 ASSERT(A >= 1.0); 00317 return clamped_convert<T>(asin(1.0/A)); 00318 } 00319 00320 // ###################################################################### 00321 //! inverse cotangent 00322 template <class T> inline T acotan(T A) 00323 { 00324 return clamped_convert<T>(atan(1.0/A)); 00325 } 00326 00327 // ###################################################################### 00328 //! Hyperbolic Secant 00329 template <class T> inline T sech(T A) 00330 { 00331 return clamped_convert<T>(1.0/cosh(A)); 00332 } 00333 00334 // ###################################################################### 00335 //! Hyperbolic Cosecant 00336 template <class T> inline T cosech(T A) 00337 { 00338 return clamped_convert<T>(1.0/sinh(A)); 00339 } 00340 00341 // ###################################################################### 00342 //! Hyperbolic Cotangent 00343 template <class T> inline T cotanh(T A) 00344 { 00345 return clamped_convert<T>(1.0/tanh(A)); 00346 } 00347 00348 // ###################################################################### 00349 //! Inverse Hyperbolic Secant 00350 template <class T> inline T asech(T A) 00351 { 00352 return clamped_convert<T>(acosh(1.0/A)); 00353 } 00354 00355 // ###################################################################### 00356 //! Inverse Hyperbolic Cosecant 00357 template <class T> inline T acosech(T A) 00358 { 00359 return clamped_convert<T>(asinh(1.0/A)); 00360 } 00361 00362 // ###################################################################### 00363 //! Inverse Hyperbolic Cotangent 00364 template <class T> inline T acotanh(T A) 00365 { 00366 return clamped_convert<T>(atanh(1.0/A)); 00367 } 00368 00369 // ###################################################################### 00370 //! return the sign of this item 00371 template <class T> inline T sign(T A) 00372 { 00373 if (A >= 0) A = 1; 00374 else A = -1; 00375 return A; 00376 } 00377 00378 // ###################################################################### 00379 //! logN for pixel 00380 template <class T> inline T logN(T A, const T n) 00381 { 00382 A = clamped_convert<T>( 00383 log(static_cast<double>(A))/ 00384 log(static_cast<double>(n))); 00385 return A; 00386 } 00387 00388 // ###################################################################### 00389 //! log sigmoid for pixel 00390 template <class T> inline T logsig(T A,const T a, const T b) 00391 { 00392 A = clamped_convert<T>(1.0/(1+exp(static_cast<double>(- (b * (A - a)))))); 00393 return A; 00394 } 00395 00396 // ###################################################################### 00397 //! log sigmoid for pixel with offset 00398 /*! Values like a = 10 and b = 5 create a nice sigmoid for x = 0 to 1 since 00399 the center of the sigmoid is at b/a. 00400 @param a The slope for the sigmoid 00401 @param b The offset for the sigmoid 00402 */ 00403 template <class T> inline T logsig2(T x, const T a, const T b) 00404 { 00405 x = clamped_convert<T>(1.0/(1+exp(static_cast<double>(-x*a + b)))); 00406 return x; 00407 } 00408 00409 // ###################################################################### 00410 //! tan sigmoid for pixel 00411 template <class T> inline T tansig(T A) 00412 { 00413 A = clamped_convert<T>(2/1+exp(-2*static_cast<double>(A))-1); 00414 return A; 00415 } 00416 00417 // ###################################################################### 00418 //! saturate pixel at some value 00419 template <class T> inline T saturate(T A, const T n) 00420 { 00421 if(A >= n) A = n; 00422 else A = A; 00423 return A; 00424 } 00425 00426 // ###################################################################### 00427 //! find p from a Gaussian distribution 00428 template <class T> inline T gauss(T A, const T Mu, const T Std) 00429 { 00430 return clamped_convert<T>( 00431 (1/(fastSqrt(D_2_PI*pow(static_cast<double>(Std),2)))) 00432 *exp(-1*(pow((static_cast<double>(A) - 00433 static_cast<double>(Mu)),2) 00434 /(2*pow(static_cast<double>(Std),2))))); 00435 } 00436 00437 //! find p from a Gaussian distribution 00438 //! (instead of the mu and A, the difference is supplied) 00439 template <class T> inline T gauss(T diff, const T Std) 00440 { 00441 return clamped_convert<T>( 00442 (1/(fastSqrt(D_2_PI*pow(static_cast<double>(Std),2)))) 00443 *exp(-1*(pow((static_cast<double>(diff)),2) 00444 /(2*pow(static_cast<double>(Std),2))))); 00445 } 00446 00447 // ###################################################################### 00448 //! find p from a gamma distrabution 00449 template <class T> inline T gamma(T X, const T A, const T B) 00450 { 00451 return clamped_convert<T>(( 00452 pow(static_cast<double>(X),static_cast<double>(A) -1) * 00453 pow(static_cast<double>(B),static_cast<double>(A)) * 00454 exp(static_cast<double>(-1.0*B*X)))/ 00455 lgamma(static_cast<double>(A))); 00456 } 00457 00458 // ###################################################################### 00459 //! this nonsense operator is for compatability 00460 template <class T> inline T max(T A) 00461 { 00462 return A; 00463 } 00464 00465 // ###################################################################### 00466 //! this nonsense operator is for compatability 00467 template <class T> inline T min(T A) 00468 { 00469 return A; 00470 } 00471 00472 // ###################################################################### 00473 //! find if this value or pixel is bounded by the other two 00474 template <class T> inline bool isBounded(T A, const T upper, const T lower) 00475 { 00476 return ((A >= lower) && (A <= upper)); 00477 } 00478 00479 // ###################################################################### 00480 //! this nonsense operator is for compatability 00481 template <class T> inline T sum(T A) 00482 { 00483 return A; 00484 } 00485 00486 // ###################################################################### 00487 //! this nonsense operator is for compatability 00488 template <class T> inline T mean(T A) 00489 { 00490 return A; 00491 } 00492 00493 //@} 00494 00495 00496 // ###################################################################### 00497 //compute the stddev and mean of each feature 00498 //This algorithm is due to Knuth (The Art of Computer Programming, volume 2: 00499 // Seminumerical Algorithms, 3rd edn., p. 232. Boston: Addison-Wesley.a 00500 // 00501 //! Compute the mean online by supplying the current mean and a new value 00502 //! mu is the previous mean, A is the current value and n is the number of values proceses so far 00503 template <class T> inline T onlineMean(T mu, T A, int n) 00504 { 00505 const T delta = A - mu; 00506 return mu + (delta/n); 00507 } 00508 00509 template <class T> inline T onlineStdev(T mu, T stdev, T A, int n) 00510 { 00511 T ret = stdev; 00512 if (n > 2) //watch for divide by 0 00513 { 00514 const T delta = A - mu; 00515 ret = (stdev * (n-2)) + delta*delta; 00516 ret /= T(n-1); 00517 } 00518 00519 return ret; 00520 } 00521 00522 // ###################################################################### 00523 // ###################################################################### 00524 // ###################################################################### 00525 // ##### Implementation of inlined functions 00526 // ###################################################################### 00527 // ###################################################################### 00528 // ###################################################################### 00529 00530 template <class T> inline int signOf(const T& x) 00531 { return (x < 0) ? -1 : 1; } 00532 00533 template <class T> inline T squareOf(const T& x) 00534 { return (x * x); } 00535 00536 template <class T, class T2> 00537 inline T clampValue(const T& val, const T2& bound1, const T2& bound2) 00538 { 00539 if (bound1 < bound2) 00540 { 00541 if (val < bound1) return bound1; 00542 if (val > bound2) return bound2; 00543 return val; 00544 } 00545 else 00546 { 00547 if (val < bound2) return bound2; 00548 if (val > bound1) return bound1; 00549 } 00550 return val; 00551 } 00552 00553 template <class T> inline void randShuffle(T* array, const uint size) 00554 { 00555 for (uint i = 0; i < size; ++i) 00556 { 00557 const uint idx = i + randomUpToNotIncluding(size-i); 00558 std::swap(array[i], array[idx]); 00559 } 00560 } 00561 00562 // ###################################################################### 00563 // ##### Digamma (psi) function 00564 // ###################################################################### 00565 // Code for computation of psi function (derivative of log gamma) 00566 // ripped from GSL, the GNU scientific library 00567 // 00568 /* Copyright (C) 1996, 1997, 1998, 1999, 2000 Gerard Jungman 00569 * 00570 * This program is free software; you can redistribute it and/or modify 00571 * it under the terms of the GNU General Public License as published by 00572 * the Free Software Foundation; either version 2 of the License, or (at 00573 * your option) any later version. 00574 * 00575 * This program is distributed in the hope that it will be useful, but 00576 * WITHOUT ANY WARRANTY; without even the implied warranty of 00577 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00578 * General Public License for more details. 00579 * 00580 * You should have received a copy of the GNU General Public License 00581 * along with this program; if not, write to the Free Software 00582 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 00583 */ 00584 00585 // ###################################################################### 00586 /* data for a Chebyshev series over a given interval */ 00587 struct cheb_series_struct { 00588 double * c; /* coefficients */ 00589 int order; /* order of expansion */ 00590 double a; /* lower interval point */ 00591 double b; /* upper interval point */ 00592 int order_sp; /* effective single precision order */ 00593 }; 00594 typedef struct cheb_series_struct cheb_series; 00595 00596 // ###################################################################### 00597 /* Chebyshev fits from SLATEC code for psi(x) */ 00598 static double psics_data[23] = { 00599 -.038057080835217922, 00600 .491415393029387130, 00601 -.056815747821244730, 00602 .008357821225914313, 00603 -.001333232857994342, 00604 .000220313287069308, 00605 -.000037040238178456, 00606 .000006283793654854, 00607 -.000001071263908506, 00608 .000000183128394654, 00609 -.000000031353509361, 00610 .000000005372808776, 00611 -.000000000921168141, 00612 .000000000157981265, 00613 -.000000000027098646, 00614 .000000000004648722, 00615 -.000000000000797527, 00616 .000000000000136827, 00617 -.000000000000023475, 00618 .000000000000004027, 00619 -.000000000000000691, 00620 .000000000000000118, 00621 -.000000000000000020 00622 }; 00623 const static cheb_series psi_cs = { psics_data, 22, -1, 1, 17 }; 00624 00625 // ###################################################################### 00626 static double apsics_data[16] = { 00627 -.0204749044678185, 00628 -.0101801271534859, 00629 .0000559718725387, 00630 -.0000012917176570, 00631 .0000000572858606, 00632 -.0000000038213539, 00633 .0000000003397434, 00634 -.0000000000374838, 00635 .0000000000048990, 00636 -.0000000000007344, 00637 .0000000000001233, 00638 -.0000000000000228, 00639 .0000000000000045, 00640 -.0000000000000009, 00641 .0000000000000002, 00642 -.0000000000000000 00643 }; 00644 const static cheb_series apsi_cs = { apsics_data, 15, -1, 1, 9 }; 00645 00646 // ###################################################################### 00647 // Ripped from GSL, the GNU scientific library, function 00648 // cheb_eval_e(); modified to eliminate the error computation and just 00649 // return the result: 00650 00651 inline double cheb_eval(const cheb_series* cs, const double x) 00652 { 00653 double d = 0.0, dd = 0.0; 00654 00655 const double y = (2.0*x - cs->a - cs->b) * (1.0/(cs->b - cs->a)); 00656 //Was: const double y = (2.0*x - cs->a - cs->b) / (cs->b - cs->a); 00657 const double y2 = 2.0 * y; 00658 00659 for (uint j = cs->order; j >= 1; j--) 00660 { 00661 const double temp = d; 00662 d = y2*d - dd + cs->c[j]; 00663 dd = temp; 00664 } 00665 00666 return y*d - dd + 0.5 * cs->c[0]; 00667 } 00668 00669 // ###################################################################### 00670 // This is the Chebyshev evaluation manually speciallized. This is *probably* 00671 // faster than the non specialized version since no structures are used and 00672 // there are no pointers. This depends on the compiler and flags used. At 00673 // worst it's just as fast as the non specialized version 00674 00675 inline double cheb_eval_psi(const double x) 00676 { 00677 double d = 0.0, dd = 0.0; 00678 00679 const double y2 = 2.0 * x; 00680 00681 for (uint j = 22; j >= 1; j--) 00682 { 00683 const double temp = d; 00684 d = y2*d - dd + psics_data[j]; 00685 dd = temp; 00686 } 00687 00688 return x*d - dd + 0.5 * -.038057080835217922; 00689 } 00690 00691 inline double cheb_eval_apsi(const double x) 00692 { 00693 double d = 0.0, dd = 0.0; 00694 00695 const double y2 = 2.0 * x; 00696 00697 for (uint j = 15; j >= 1; j--) 00698 { 00699 const double temp = d; 00700 d = y2*d - dd + apsics_data[j]; 00701 dd = temp; 00702 } 00703 00704 return x*d - dd + 0.5 * -.0204749044678185; 00705 } 00706 00707 // ###################################################################### 00708 // Ripped from GSL, the GNU scientific library, function psi_x(); 00709 // modified to eliminate the error computation and just return the 00710 // result: 00711 inline double psi(const double x) 00712 { 00713 if (x >= 2.0) 00714 { 00715 /* 00716 const double d = 1.0 / x; 00717 const double t = 8.0 * d * d - 1.0; 00718 const double cheb = cheb_eval(&apsi_cs, t); 00719 return fastLog(x) - 0.5 * d + cheb; 00720 */ 00721 const double t = 8.0 / (x*x) - 1.0; 00722 const double cheb = cheb_eval(&apsi_cs, t); 00723 return fastLog(x) - 0.5 / x + cheb; 00724 } 00725 else if (x < 1.0) 00726 { 00727 if( x <= 0.0) 00728 LFATAL("Digamma (Psi) Only defined for x > 0.0"); 00729 00730 const double t1 = 1.0 / x; 00731 const double cheb = cheb_eval(&psi_cs, 2.0*x-1.0); 00732 return cheb - t1; 00733 } 00734 else 00735 { 00736 const double v = x - 1.0; 00737 return cheb_eval(&psi_cs, 2.0*v-1.0); 00738 00739 } 00740 } 00741 00742 // ###################################################################### 00743 // Somewhat faster psi. dumps error checking and structure usage. 00744 inline double fast_psi(const double x) 00745 { 00746 if (x >= 2.0) 00747 { 00748 const double t = 8.0 / (x*x) - 1.0; 00749 const double cheb = cheb_eval_apsi(t); 00750 return fastLog(x) - 0.5 / x + cheb; 00751 } 00752 else if (x < 1.0) 00753 { 00754 const double t1 = 1.0 / x; 00755 const double cheb = cheb_eval_psi(2.0*x-1.0); 00756 return cheb - t1; 00757 } 00758 else 00759 { 00760 const double v = x - 1.0; 00761 return cheb_eval_psi(2.0*v-1.0); 00762 } 00763 } 00764 00765 // ###################################################################### 00766 // ##### Kullback-Leibler (KL) distances 00767 // ###################################################################### 00768 //! Convert a number into Wows, for instance a KL distance 00769 /*! Wow's in this computation is the same as the conversion 00770 from Nats to Bits in KL 00771 */ 00772 template <class T> inline T wow(const T& k) 00773 { 00774 // surprise should always be positive but rounding may mess that up: 00775 // This is OK since surprise is negative with equality if and only 00776 // if two distribution functions are equal. Thus, we gain no insite 00777 // with negative surprise. 00778 00779 if (k < 0.0) return 0.0; 00780 // return the surprise, in units of wows (KL Bits): 00781 return k * (1.0F/D_LOG_2); 00782 } 00783 00784 // ###################################################################### 00785 //! Compute the KL distance for a joint gamma PDF 00786 /*! 00787 @param ai New Alpha in model i 00788 @param aj New Alpha in model j 00789 @param bi New Beta in model i 00790 @param bj New Beta in model j 00791 @param Ai Current Alpha in model i 00792 @param Aj Current Alpha in model j 00793 @param Bi Current Beta in model i 00794 @param Bj Current Beta in model j 00795 @param doWow is true then convert output to Wows 00796 00797 This formula derived by T. Nathan Mundhenk 2007 00798 */ 00799 template <class T> inline T KLjointGammaGamma(const T ai,const T aj, 00800 const T bi,const T bj, 00801 const T Ai,const T Aj, 00802 const T Bi,const T Bj, 00803 const bool doWow = false, 00804 const T gammaBiasi = 1.0F, 00805 const T gammaBiasj = 1.0F) 00806 { 00807 00808 /* We will compute the joint KL for P(x), P(X), P(y) and P(Y) where 00809 they are gamma distributions. 00810 00811 P(X) P(Y) 00812 KL = Integrate[P(x) P(y) log[---------] ] 00813 P(x) P(y) 00814 00815 ai - New Alpha in model i 00816 aj - New Alpha in model j 00817 bi - New Beta in model i 00818 bj - New Beta in model j 00819 00820 Ai - Current Alpha in model i 00821 Aj - Current Alpha in model j 00822 Bi - Current Beta in model i 00823 Bj - Current Beta in model j 00824 00825 ai Bi aj Bj bi bj 00826 KL = -ai - aj + ----- + ----- - Ai Log[--] - Aj Log[--] + 00827 bi bj Bi Bj 00828 00829 Gamma[Ai] Gamma[Aj] 00830 > Log[-------------------] - (-ai + Ai) PolyGamma[0, ai] - 00831 Gamma[ai] Gamma[aj] 00832 00833 > (-aj + Aj) PolyGamma[0, aj] 00834 00835 00836 Note: Since the pure Gamma function can get large very quickly, we avoid 00837 large numbers by computing the log gamma directly as lgamma. 00838 00839 */ 00840 00841 // Simplified a little from the above: 00842 const T k = gammaBiasi * KLgamma(ai,bi,Ai,Bi) + gammaBiasj * KLgamma(aj,bj,Aj,Bj); 00843 00844 if(doWow) return wow(k); 00845 else return k; 00846 } 00847 00848 // ###################################################################### 00849 //! Compute the KL distance for a joint gamma gauss PDF 00850 /*! 00851 @param a New Alpha in model 1 00852 @param b New Beta in model 1 00853 @param u New Mean in model 2 00854 @param s New Std. in model 2 00855 @param A Current Alpha in model 1 00856 @param B Current Beta in model 1 00857 @param U Current Mean in model 2 00858 @param S Current Std. in model 2 00859 @param doWow is true then convert output to Wows 00860 00861 This formula derived by T. Nathan Mundhenk 2007 00862 */ 00863 template <class T> inline T KLjointGammaGauss(const T a,const T u, 00864 const T b,const T s, 00865 const T A,const T U, 00866 const T B,const T S, 00867 const bool doWow = false, 00868 const T gammaBias = 1.0F, 00869 const T gaussBias = 1.0F) 00870 { 00871 00872 /* We will compute the joint KL for P(x), P(X), P(y) and P(Y) 00873 where one is a gamma distribution and the other gaussian as: 00874 00875 P(X) P(Y) 00876 KL = Integrate[P(x) P(y) log[---------] ] 00877 P(x) P(y) 00878 00879 a - New Alpha in model 1 00880 b - New Beta in model 1 00881 u - New Mean in model 2 00882 s - New Std. in model 2 00883 00884 A - Current Alpha in model 1 00885 B - Current Beta in model 1 00886 U - Current Mean in model 2 00887 S - Current Std. in model 2 00888 00889 2 2 00890 1 a B s (u - U) B s 00891 = -(-) + a - --- + ---- + -------- + A Log[-] - Log[-] + 00892 2 b 2 2 b S 00893 2 S 2 S 00894 00895 Gamma[a] 00896 > Log[--------] - (a - A) PolyGamma[0, a] 00897 Gamma[A] 00898 */ 00899 00900 const T k = gammaBias * KLgamma(a,b,A,B) + gaussBias * KLgauss(u,s,U,S); 00901 00902 if(doWow) return wow(k); 00903 else return k; 00904 } 00905 00906 // ###################################################################### 00907 //! Compute the KL distance for a single gamma PDF 00908 /*! 00909 @param a New Alpha 00910 @param b New Beta 00911 @param A Current Alpha 00912 @param B Current Beta 00913 @param doWow is true then convert output to Wows 00914 */ 00915 template <class T> inline T KLgamma(const T a,const T b, 00916 const T A,const T B, 00917 const bool doWow = false) 00918 { 00919 /* We will compute the joint KL for P(x), P(X), P(y) and P(Y) 00920 where one is a gamma distribution and the other gaussian as: 00921 00922 surprise is KL(new || old): 00923 00924 P(X) 00925 KL = Integrate[P(x) log[----] ] 00926 P(x) 00927 00928 a - New Alpha 00929 b - New Beta 00930 A - Current Alpha 00931 B - Current Beta 00932 00933 a B b Gamma[A] 00934 KL = -a + --- + A Log[-] + Log[--------] + a PolyGamma[0, a] - 00935 b B Gamma[a] 00936 00937 > A * PolyGamma[0, a] 00938 00939 Note: Since the pure Gamma function can get large very quickly, we avoid 00940 large numbers by computing the log gamma directly as lgamma. 00941 */ 00942 00943 const T k = - a + a*B/b + A*fastLog(b/B) + lgamma(A) - lgamma(a) 00944 + (a - A)*fast_psi(a); 00945 00946 if(doWow) return wow(k); 00947 else return k; 00948 } 00949 00950 // ###################################################################### 00951 //! Compute the KL distance for a single gamma PDF 00952 /*! 00953 This method produces the best results for usage with RSVP data as measured 00954 by the MAG when using the example values for C1 and C2. It is also faster 00955 than the basic KL computation since we eliminate two divisions and one log 00956 computation. 00957 00958 @param a New Alpha 00959 @param A Current Alpha 00960 @param C1 the const standin for B/b - 1 00961 @param C2 the const standin for log(b/B) 00962 @param doWow is true then convert output to Wows 00963 */ 00964 template <class T> inline T KLgammaConst(const T a, const T A, 00965 const T C1, const T C2, 00966 const bool doWow = false) 00967 { 00968 /* We will compute the joint KL for P(x), P(X), P(y) and P(Y) 00969 where one is a gamma distribution and the other gaussian as: 00970 00971 surprise is KL(new || old): 00972 00973 P(X) 00974 KL = Integrate[P(x) log[----] ] 00975 P(x) 00976 00977 a - New Alpha 00978 A - Current Alpha 00979 00980 Gamma[A] 00981 KL = -a + a * C1 + A * C2 + Log[--------] + a PolyGamma[0, a] - 00982 Gamma[a] 00983 00984 > A * PolyGamma[0, a] 00985 00986 This simplifies a little to: 00987 00988 k = a*C1 + A*C2 + lgamma(A) - lgamma(a) + (a - A)*psi(a) 00989 00990 NOTES: (1) Psi is the same as the first order PolyGamma 00991 (2) lgamma(A) - lgamma(a) is a simplification of the Log gamma part 00992 00993 Note: Since the pure Gamma function can get large very quickly, we avoid 00994 large numbers by computing the log gamma directly as lgamma. 00995 00996 Examples of good values for C1 and C2 are: 00997 00998 C1 = D_SQRT_2/2.0F - 1.0F (i.e. sqrt(2)/2 - 1 ) 00999 C2 = D_LOG_SQRT_2 (i.e. log(sqrt(2)) ) 01000 01001 Also notice, if a = A then: 01002 01003 k = a*(C1 + C2) 01004 */ 01005 01006 const T k = a*C1 + A*C2 + lgamma(A) - lgamma(a) + (a - A)*fast_psi(a); 01007 01008 if(doWow) return wow(k); 01009 else return k; 01010 } 01011 01012 // ###################################################################### 01013 //! Compute the KL distance for a single gamma PDF 01014 /*! 01015 Here we fix beta to be 1. This is a special case, but if we have it 01016 we can compute KL much faster. We can also use this if beta1 = beta2 01017 The Chi Square is one such case where beta is 1/2 . 01018 01019 @param a New Alpha 01020 @param A Current Alpha 01021 @param doWow is true then convert output to Wows 01022 */ 01023 template <class T> inline T KLgamma(const T a,const T A, 01024 const bool doWow = false) 01025 { 01026 /* 01027 Note: Since the pure Gamma function can get large very quickly, we avoid 01028 large numbers by computing the log gamma directly as lgamma. 01029 */ 01030 01031 const T k = lgamma(A) - lgamma(a) + (a - A)*fast_psi(a); 01032 01033 if(doWow) return wow(k); 01034 else return k; 01035 } 01036 01037 // ###################################################################### 01038 //! Compute the KL distance for a single gauss PDF 01039 /*! 01040 @param u New Mean 01041 @param s New Std. 01042 @param U Current Mean 01043 @param S Current Std. 01044 @param doWow is true then convert output to Wows 01045 */ 01046 template <class T> inline T KLgauss(const T u,const T s, 01047 const T U,const T S, 01048 const bool doWow = false) 01049 { 01050 /* Compute the KL distance between two Gaussian PDFs as: 01051 01052 P(X) 01053 KL = Integrate[P(x) log[----] ] 01054 P(x) 01055 01056 u - New Mean 01057 s - New Std. 01058 U - Current Mean 01059 S - Current Std. 01060 01061 2 2 01062 -1 s (u - U) s 01063 KL = - (--- + ----- + -------- - Log[-]) 01064 2 2 2 S 01065 2 S 2 S 01066 */ 01067 01068 const T iS = 1.0 / 2*S*S; 01069 const T x = s*s * iS; 01070 const T mm = (u - U)*(u - U); 01071 const T k = -1.0 * (x - 0.5 - log(s/S) + mm * iS); 01072 01073 if(doWow) 01074 return wow(k); 01075 else 01076 return k; 01077 } 01078 01079 // return the AUC of two vectors 01080 double AUC(const float* model, const float* rand, 01081 size_t sm, size_t sr, 01082 const double step = 0.1); 01083 01084 #endif 01085 /* So things look consistent in everyone's emacs... */ 01086 /* Local Variables: */ 01087 /* indent-tabs-mode: nil */ 01088 /* End: */
1.4.4