exponential.tcl

Go to the documentation of this file.
00001 /*  exponential.tcl --*/
00002 /*     Compute exponential integrals (E1, En, Ei, li, Shi, Chi, Si, Ci)*/
00003 /* */
00004 
00005 namespace ::math::special {
00006     variable pi 3.1415926
00007     variable gamma 0.57721566490153286
00008     variable halfpi [expr {$pi/2.0}]
00009 
00010 /*  Euler's digamma function for small integer arguments*/
00011 
00012     variable psi {
00013         NaN
00014         -0.57721566490153286 0.42278433509846713 0.92278433509846713
00015         1.2561176684318005 1.5061176684318005 1.7061176684318005
00016         1.8727843350984672 2.0156414779556102 2.1406414779556102
00017         2.2517525890667214 2.3517525890667215 2.4426616799758123
00018         2.5259950133091458 2.6029180902322229 2.6743466616607945
00019         2.7410133283274614 2.8035133283274614 2.8623368577392259
00020         2.9178924132947812 2.9705239922421498 3.0205239922421496
00021         3.0681430398611971 3.1135975853157425 3.1570758461853079
00022         3.1987425128519744 3.2387425128519745 3.2772040513135128
00023         3.31424108835055 3.3499553740648356 3.3844381326855251
00024         3.4177714660188583 3.4500295305349873 3.4812795305349873
00025         3.5115825608380176 3.5409943255438998 3.5695657541153283
00026         3.597343531893106 3.6243705589201332 3.6506863483938172
00027         3.6763273740348428
00028     }
00029 }
00030 
00031 /*  ComputeExponFG --*/
00032 /*     Compute the auxiliary functions f and g*/
00033 /* */
00034 /*  Arguments:*/
00035 /*     x            Parameter of the integral (x>=0)*/
00036 /*  Result:*/
00037 /*     Approximate values for f and g*/
00038 /*  Note:*/
00039 /*     See Abramowitz and Stegun*/
00040 /* */
00041 ret  ::math::special::ComputeExponFG (type x) {
00042     set x2 [expr {$x*$x}]
00043     set fx [expr {($x2*$x2+7.241163*$x2+2.463936)/
00044                   ($x2*$x2+9.068580*$x2+7.157433)/$x}]
00045     set gx [expr {($x2*$x2+7.547478*$x2+1.564072)/
00046                   ($x2*$x2+12.723684*$x2+15.723606)/$x2}]
00047     list $fx $gx
00048 }
00049 
00050 
00051 /*  exponential_Ei --*/
00052 /*     Compute the exponential integral of the second kind, to relative*/
00053 /*     error eps*/
00054 /*  Arguments:*/
00055 /*     x       Value of the argument*/
00056 /*     eps     Relative error*/
00057 /*  Result:*/
00058 /*     Principal value of the integral exp(x)/x*/
00059 /*     from -infinity to x*/
00060 /* */
00061 ret  ::math::special::exponential_Ei ( type x , optional eps =1.0e-10  ) {
00062     variable gamma
00063 
00064     if { ![string is double -strict $x] } {
00065         return -code error "expected a floating point number but found \"$x\""
00066     }
00067     if { $x < 0.0 } {
00068         return [expr { -[exponential_En 1 [expr { - $x }] $eps] }]
00069     }
00070     if { $x == 0.0 } {
00071        set message "Argument to exponential_Ei must not be zero"
00072        return -code error -errorcode [list ARITH DOMAIN $message] $message
00073     }
00074     if { $x >= -log($eps) } {
00075         # evaluate Ei(x) as an asymptotic series; the series is formally
00076         # divergent, but the leading terms give the desired value to
00077         # enough precision.
00078         set sum 0.
00079         set term 1.
00080         set k 1
00081         while { 1 } {
00082             set p $term
00083             set term [expr { $term * ( $k / $x ) }]
00084             if { $term < $eps } {
00085                 break
00086             }
00087             if { $term < $p } {
00088                 set sum [expr { $sum + $term }]
00089             } else {
00090                 set sum [expr { $sum - $p }]
00091                 break
00092             }
00093             incr k
00094         }
00095         return [expr { exp($x) * ( 1.0 + $sum ) / $x }]
00096     } elseif { $x >= 1e-18 } {
00097         # evaluate Ei(x) as a power series
00098         set sum 0.
00099         set fact 1.
00100         set pow $x
00101         set n 1
00102         while { 1 } {
00103             set fact [expr { $fact * $n }]
00104             set term [expr { $pow / $n / $fact }]
00105             set sum [expr { $sum + $term }]
00106             if { $term < $eps * $sum } break
00107             set pow [expr { $x * $pow }]
00108             incr n
00109         }
00110         return [expr { $sum + $gamma + log($x) }]
00111     } else {
00112         # Ei(x) for small x
00113         return [expr { log($x) + $gamma }]
00114     }
00115 }
00116 
00117 
00118 /*  exponential_En --*/
00119 /*     Compute the exponential integral of n-th order, to relative error*/
00120 /*     epsilon*/
00121 /* */
00122 /*  Arguments:*/
00123 /*     n            Order of the integral (n>=1, integer)*/
00124 /*     x            Parameter of the integral (x>0)*/
00125 /*     epsilon      Relative error*/
00126 /*  Result:*/
00127 /*     Value of En(x) = integral from 0 to x of exp(-x)/x**n*/
00128 /* */
00129 ret  ::math::special::exponential_En ( type n , type x , optional epsilon =1.0e-10  ) {
00130     variable psi
00131     variable gamma
00132     if { ![string is integer -strict $n] || $n < 0 } {
00133         return -code error "expected a non-negative integer but found \"$n\""
00134     }
00135     if { ![string is double -strict $x] } {
00136         return -code error "expected a floating point number but found \"$x\""
00137     }
00138     if { $n == 0 } {
00139         if { $x == 0.0 } {
00140             return -code error "E0(0) is indeterminate"
00141         }
00142         return [expr { exp( -$x ) / $x }]
00143     }
00144     if { $n == 1 && $x < 0.0 } {
00145         return [expr {- [exponential_Ei [expr { -$x }] $eps] }]
00146     }
00147     if { $x < 0.0 } {
00148         return -code error "can't evaluate En(x) for negative x"
00149     }
00150     if { $x == 0.0 } {
00151         return [expr { 1.0 / ( $n - 1 ) }]
00152     }
00153 
00154     if { $x > 1.0 } {
00155         # evaluate En(x) as a continued fraction
00156         set b [expr { $x + $n }]
00157         set c 1.e308
00158         set d [expr { 1.0 / $b }]
00159         set h $d
00160         set i 1
00161         while { 1 } {
00162             set a [expr { -$i * ( $n - 1 + $i ) }]
00163             set b [expr { $b + 2.0 }]
00164             set d [expr { 1.0 / ( $a * $d + $b ) }]
00165             set c [expr { $b + $a / $c }]
00166             set delta [expr { $c * $d }]
00167             set h [expr { $h * $delta }]
00168             if { abs( $delta - 1. ) < $epsilon } {
00169                 return [expr { $h * exp(-$x) }]
00170             }
00171             incr i
00172         }
00173     } else {
00174         # evaluate En(x) as a series
00175         if { $n == 1 } {
00176             set a [expr { -log($x) - $gamma }]
00177         } else {
00178             set a [expr { 1.0 / ( $n - 1 ) }]
00179         }
00180         set f 1.0
00181         set i 1
00182         while { 1 } {
00183             set f [expr { - $f *  $x / $i }]
00184             if { $i == $n - 1 } {
00185                 set term [expr { $f * ([lindex $psi $n] - log($x)) }]
00186             } else {
00187                 set term [expr { $f / ( $n - 1 - $i ) }]
00188             }
00189             set a [expr { $a + $term }]
00190             if { abs($term) < $epsilon * abs($a) } {
00191                 return $a
00192             }
00193             incr i
00194         }
00195     }
00196 }
00197 
00198 /*  exponential_E1 --*/
00199 /*     Compute the exponential integral*/
00200 /* */
00201 /*  Arguments:*/
00202 /*     x            Parameter of the integral (x>0)*/
00203 /*  Result:*/
00204 /*     Value of E1(x) = integral from x to infinity of exp(-x)/x*/
00205 /*  Note:*/
00206 /*     This relies on a rational approximation (error ~ 2e-7 (x<1) or 5e-5 (x>1)*/
00207 /* */
00208 ret  ::math::special::exponential_E1 (type x) {
00209     if { $x <= 0.0 } {
00210         error "Domain error: x must be positive"
00211     }
00212 
00213    if { $x < 1.0 } {
00214       return [expr {-log($x)+((((0.00107857*$x-0.00976004)*$x+0.05519968)*$x-0.24991055)*$x+0.99999193)*$x-0.57721566}]
00215    } else {
00216       set xexpe [expr {($x*$x+2.334733*$x+0.250621)/($x*$x+3.330657*$x+1.681534)}]
00217       return [expr {$xexpe/($x*exp($x))}]
00218    }
00219 }
00220 
00221 /*  exponential_li --*/
00222 /*     Compute the logarithmic integral*/
00223 /*  Arguments:*/
00224 /*     x       Value of the argument*/
00225 /*  Result:*/
00226 /*     Value of the integral 1/ln(x) from 0 to x*/
00227 /* */
00228 ret  ::math::special::exponential_li (type x) {
00229     if { $x < 0 } {
00230         return -code error "Argument must be positive or zero"
00231     } else {
00232         if { $x == 0.0 } {
00233             return 0.0
00234         } else {
00235             return [exponential_Ei [expr {log($x)}]]
00236         }
00237     }
00238 }
00239 
00240 /*  exponential_Shi --*/
00241 /*     Compute the hyperbolic sine integral*/
00242 /*  Arguments:*/
00243 /*     x       Value of the argument*/
00244 /*  Result:*/
00245 /*     Value of the integral sinh(x)/x from 0 to x*/
00246 /* */
00247 ret  ::math::special::exponential_Shi (type x) {
00248     if { $x < 0 } {
00249         return -code error "Argument must be positive or zero"
00250     } else {
00251         if { $x == 0.0 } {
00252             return 0.0
00253         } else {
00254             proc g {x} {
00255                return [expr {sinh($x)/$x}]
00256             }
00257             return [lindex [::math::calculus::romberg g 0.0 $x] 0]
00258         }
00259     }
00260 }
00261 
00262 /*  exponential_Chi --*/
00263 /*     Compute the hyperbolic cosine integral*/
00264 /*  Arguments:*/
00265 /*     x       Value of the argument*/
00266 /*  Result:*/
00267 /*     Value of the integral (cosh(x)-1)/x from 0 to x*/
00268 /* */
00269 ret  ::math::special::exponential_Chi (type x) {
00270     variable gamma
00271     if { $x < 0 } {
00272         return -code error "Argument must be positive or zero"
00273     } else {
00274         if { $x == 0.0 } {
00275             return 0.0
00276         } else {
00277             proc g {x} {
00278                return [expr {(cosh($x)-1.0)/$x}]
00279             }
00280             set integral [lindex [::math::calculus::romberg g 0.0 $x] 0]
00281             return [expr {$gamma+log($x)+$integral}]
00282         }
00283     }
00284 }
00285 
00286 /*  exponential_Si --*/
00287 /*     Compute the sine integral*/
00288 /*  Arguments:*/
00289 /*     x       Value of the argument*/
00290 /*  Result:*/
00291 /*     Value of the integral sin(x)/x from 0 to x*/
00292 /* */
00293 ret  ::math::special::exponential_Si (type x) {
00294     variable halfpi
00295     if { $x < 0 } {
00296         return -code error "Argument must be positive or zero"
00297     } else {
00298         if { $x == 0.0 } {
00299             return 0.0
00300         } else {
00301             if { $x < 1.0 } {
00302                 proc g {x} {
00303                     return [expr {sin($x)/$x}]
00304                 }
00305                 return [lindex [::math::calculus::romberg g 0.0 $x] 0]
00306             } else {
00307                 foreach {f g} [ComputeExponFG $x] {break}
00308                 return [expr {$halfpi-$f*cos($x)-$g*sin($x)}]
00309             }
00310         }
00311     }
00312 }
00313 
00314 /*  exponential_Ci --*/
00315 /*     Compute the cosine integral*/
00316 /*  Arguments:*/
00317 /*     x       Value of the argument*/
00318 /*  Result:*/
00319 /*     Value of the integral (cosh(x)-1)/x from 0 to x*/
00320 /* */
00321 ret  ::math::special::exponential_Ci (type x) {
00322     variable gamma
00323 
00324     if { $x < 0 } {
00325         return -code error "Argument must be positive or zero"
00326     } else {
00327         if { $x == 0.0 } {
00328             return 0.0
00329         } else {
00330             if { $x < 1.0 } {
00331                 proc g {x} {
00332                     return [expr {(cos($x)-1.0)/$x}]
00333                 }
00334                 set integral [lindex [::math::calculus::romberg g 0.0 $x] 0]
00335                 return [expr {$gamma+log($x)+$integral}]
00336             } else {
00337                 foreach {f g} [ComputeExponFG $x] {break}
00338                 return [expr {$f*sin($x)-$g*cos($x)}]
00339             }
00340         }
00341     }
00342 }
00343 
00344 /*  some tests --*/
00345 /*     Reproduce tables 5.1, 5.2 from Abramowitz & Stegun,*/
00346 
00347 if { [info exists ::argv0] && ![string compare $::argv0 [info script]] } {
00348 namespace ::math::special {
00349 for {  i =  0.01 } { $i < 0.505 } {  i =  [expr { $i + 0.01 }] } {
00350      ei =  [exponential_Ei $i]
00351      e1 =  [expr { - [exponential_Ei [expr { - $i }]] }]
00352     puts [format "%9.6f\t%.10g\t%.10g" $i \
00353               [expr {($ei - log($i) - 0.57721566490153286)/$i} ] \
00354               [expr {($e1 + log($i) + 0.57721566490153286) / $i }]]
00355 }
00356 puts {}
00357 for {  i =  0.5 } { $i < 2.005 } {  i =  [expr { $i + 0.01 }] } {
00358      ei =  [exponential_Ei $i]
00359      e1 =  [expr { - [exponential_Ei [expr { - $i }]] }]
00360     puts [format "%9.6f\t%.10g\t%.10g" $i $ei $e1]
00361 }
00362 puts {}
00363 for {} { $i < 10.05 } {  i =  [expr { $i + 0.1 }] } {
00364      ei =  [exponential_Ei $i]
00365      e1 =  [expr { - [exponential_Ei [expr { - $i }]] }]
00366     puts [format "%9.6f\t%.10g\t%.10g" $i \
00367               [expr { $i * exp(-$i) * $ei }] \
00368               [expr { $i * exp($i) * $e1 }]]
00369 
00370 }
00371 puts {}
00372 for { ooi =  0.1} { $ooi > 0.0046 } {  ooi =  [expr { $ooi - 0.005 }] } {
00373      i =  [expr { 1.0 / $ooi }]
00374      ri =  [expr { round($i) }]
00375      ei =  [exponential_Ei $i]
00376      e1 =  [expr { - [exponential_Ei [expr { - $i }]] }]
00377     puts [format "%9.6f\t%.10g\t%.10g\t%d" $i \
00378               [expr { $i * exp(-$i) * $ei }] \
00379               [expr { $i * exp($i) * $e1 }] \
00380               $ri]
00381 }
00382 puts {}
00383 
00384 /*  Reproduce table 5.4 from Abramowitz and Stegun*/
00385 
00386 for {  x =  0.00 } { $x < 0.505 } {  x =  [expr { $x + 0.01 }] } {
00387      line =  [format %4.2f $x]
00388     if { $x == 0.00 } {
00389         append line { } 1.0000000
00390     } else {
00391         append line { } [format %9.7f \
00392                              [expr { [exponential_En 2 $x] - $x * log($x) }]]
00393     }
00394     foreach n { 3 4 10 20 } {
00395         append line { } [format %9.7f [exponential_En $n $x]]
00396     }
00397     puts $line
00398 }
00399 puts {}
00400 for {  x =  0.50 } { $x < 2.005 } {  x =  [expr { $x + 0.01 }] } {
00401      line =  [format %4.2f $x]
00402     foreach n { 2 3 4 10 20 } {
00403         append line { } [format %9.7f [exponential_En $n $x]]
00404     }
00405     puts $line
00406 }
00407 puts {}
00408 
00409 for {  oox =  0.5 } { $oox > 0.1025 } {  oox =  [expr { $oox - 0.05 }] } {
00410      line =  [format %4.2f $oox]
00411      x =  [expr { 1.0 / $oox }]
00412      rx =  [expr { round( $x ) }]
00413     foreach n { 2 3 4 10 20 } {
00414          en =  [exponential_En $n [expr { 1.0 / $oox }]]
00415         append line { } [format %9.7f [expr { ( $x + $n ) * exp($x) * $en }]]
00416     }
00417     append line { } [format %3d $rx]
00418     puts $line
00419 }
00420 for {  oox =  0.10 } { $oox > 0.005 } {  oox =  [expr { $oox - 0.01 }] } {
00421      line =  [format %4.2f $oox]
00422      x =  [expr { 1.0 / $oox }]
00423      rx =  [expr { round( $x ) }]
00424     foreach n { 2 3 4 10 20 } {
00425          en =  [exponential_En $n $x]
00426         append line { } [format %9.7f [expr { ( $x + $n ) * exp($x) * $en }]]
00427     }
00428     append line { } [format %3d $rx]
00429     puts $line
00430 }
00431 puts {}
00432 catch {exponential_Ei 0.0} result; puts $result
00433 }
00434 }
00435 

Generated on 21 Sep 2010 for Gui by  doxygen 1.6.1