Skip to content

Commit 7c05780

Browse files
committed
Improve precision for incomplete gamma function
Properly evaluate constant factor before Taylor series for P. This markedly improves precision. We're still quite far from 1-ulp precision but at very least we don't lose 5(!) digits of precision in worst case Fixes #42
1 parent 9f99884 commit 7c05780

File tree

5 files changed

+96
-20
lines changed

5 files changed

+96
-20
lines changed

Numeric/SpecFunctions/Internal.hs

Lines changed: 61 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -409,24 +409,18 @@ incompleteGamma a x
409409
|| (abs mu < 0.4)
410410
-- Gautschi's algorithm.
411411
--
412-
-- Evaluate series for P(a,x). See [Temme1994] Eq. 5.5
413-
--
414-
-- Evaluation of constant multiplier is somewhat tricky. On one
415-
-- hand working in linear domain gives markedly better precision
416-
-- than log domain (in log domain we can easily lose 2-3
417-
-- digits). On other it's prone to overflow: we're working with
418-
-- exponents and gamma function here!. Solution is to try to
419-
-- compute value normally and revert to log domain it anything
420-
-- overflowed
412+
-- Evaluate series for P(a,x). See [Temme1994] Eq. 5.5 and [NOTE:
413+
-- incompleteGamma.taylorP]
421414
factorP
422-
| isInfinite num = logDom
423-
| isInfinite denom = logDom
424-
| otherwise = num / denom
425-
where
426-
num = x ** a
427-
denom = exp x * exp (logGamma (a + 1))
428-
logDom = exp (log x * a - x - logGamma (a+1))
429-
415+
| a < 10 = x ** a
416+
/ (exp x * exp (logGamma (a + 1)))
417+
| a < 1182.5 = (x * exp 1 / a) ** a
418+
/ exp x
419+
/ sqrt (2*pi*a)
420+
/ exp (logGammaCorrection a)
421+
| otherwise = (x * exp 1 / a * exp (-x/a)) ** a
422+
/ sqrt (2*pi*a)
423+
/ exp (logGammaCorrection a)
430424
taylorSeriesP
431425
= sumPowerSeries x (scanSequence (/) 1 $ enumSequenceFrom (a+1))
432426
* factorP
@@ -1337,3 +1331,53 @@ factorialTable = U.fromListN 171
13371331
, 4.269068009004705e304
13381332
, 7.257415615307998e306
13391333
]
1334+
1335+
1336+
-- [NOTE: incompleteGamma.taylorP]
1337+
--
1338+
-- Incompltete gamma uses several algorithms for different parts of
1339+
-- parameter space. Most troublesome is P(a,x) Taylor series
1340+
-- [Temme1994,Eq.5.5] which requires to evaluate rather nasty
1341+
-- expression:
1342+
--
1343+
-- x^a x^a
1344+
-- ------------- = -------------
1345+
-- exp(x)·Γ(a+1) exp(x)·a·Γ(a)
1346+
--
1347+
-- Conditions:
1348+
-- | 0.5<x<1.1 = x < 4/3*a
1349+
-- | otherwise = x < a
1350+
--
1351+
-- For small `a` computation could be performed directly. However for
1352+
-- largish values of `a` it's possible some of factor in the
1353+
-- expression overflow. Values below take into account ranges for
1354+
-- Taylor P approximation:
1355+
--
1356+
-- · a > 155 - x^a could overflow
1357+
-- · a > 1182.5 - exp(x) could overflow
1358+
--
1359+
-- Usual way to avoid overflow problem is to perform calculations in
1360+
-- the log domain. It however doesn't work very well in this case
1361+
-- since we encounter catastrophic cancellations and could easily lose
1362+
-- up to 6(!) digits for large `a`.
1363+
--
1364+
-- So we take another approach and use Stirling approximation with
1365+
-- correction (logGammaCorrection).
1366+
--
1367+
-- x^a / x·e \^a 1
1368+
-- ≈ ------------------------- = | --- | · ----------------
1369+
-- exp(x)·sqrt(2πa)·(a/e)^a) \ a / exp(x)·sqrt(2πa)
1370+
--
1371+
-- We're using this approach as soon as logGammaCorrection starts
1372+
-- working (a>10) because we don't have implementation for gamma
1373+
-- function and exp(logGamma z) results in errors for large a.
1374+
--
1375+
-- Once we get into region when exp(x) could overflow we rewrite
1376+
-- expression above once more:
1377+
--
1378+
-- / x·e \^a 1
1379+
-- | --- · e^(-x/a) | · ---------
1380+
-- \ a / sqrt(2πa)
1381+
--
1382+
-- This approach doesn't work very well but it's still big improvement
1383+
-- over calculations in the log domain.

tests/Tests/SpecFunctions.hs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,10 @@ tests = testGroup "Special functions"
8080
, testGroup "incomplete gamma"
8181
[ testCase "incompleteGamma table" $
8282
forTable "tests/tables/igamma.dat" $ \[a,x,exact] -> do
83-
checkTabularPure 16 (show (a,x)) exact (incompleteGamma a x)
83+
let err | a < 10 = 16
84+
| a == 201 = 200
85+
| otherwise = 32
86+
checkTabularPure err (show (a,x)) exact (incompleteGamma a x)
8487
, testProperty "incomplete gamma - increases" $
8588
\(abs -> s) (abs -> x) (abs -> y) -> s > 0 ==> monotonicallyIncreases (incompleteGamma s) x y
8689
, testProperty "0 <= gamma <= 1" incompleteGammaInRange

tests/tables/generate.py

100644100755
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#!/usr/bin/python
1+
#!/usr/bin/env python3
22
"""
33
"""
44
import itertools

tests/tables/igamma.dat

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
0.000100000000000000005 10 0.999999999584179852012789127241
88
0.000100000000000000005 100 1.0
99
0.000100000000000000005 1000 1.0
10+
0.000100000000000000005 3301 1.0
1011
0.00100000000000000002 9.99999999999999955e-07 0.986848133694076665339510700432
1112
0.00100000000000000002 1.00000000000000008e-05 0.989123044695782668850940465364
1213
0.00100000000000000002 0.00100000000000000002 0.993687646708860290096219637037
@@ -16,6 +17,7 @@
1617
0.00100000000000000002 10 0.999999995830692182809738396874
1718
0.00100000000000000002 100 1.0
1819
0.00100000000000000002 1000 1.0
20+
0.00100000000000000002 3301 1.0
1921
0.0100000000000000002 9.99999999999999955e-07 0.875933759832353305070970748339
2022
0.0100000000000000002 1.00000000000000008e-05 0.896336798267197200971193210546
2123
0.0100000000000000002 0.00100000000000000002 0.938570652526128985382985766694
@@ -25,6 +27,7 @@
2527
0.0100000000000000002 10 0.99999995718295022590061122615
2628
0.0100000000000000002 100 1.0
2729
0.0100000000000000002 1000 1.0
30+
0.0100000000000000002 3301 1.0
2831
0.100000000000000006 9.99999999999999955e-07 0.264033654327922324857187514752
2932
0.100000000000000006 1.00000000000000008e-05 0.332398405040503295401903974218
3033
0.100000000000000006 0.00100000000000000002 0.526768568392445111817869842601
@@ -34,6 +37,7 @@
3437
0.100000000000000006 10 0.999999445201428209809392042838
3538
0.100000000000000006 100 1.0
3639
0.100000000000000006 1000 1.0
40+
0.100000000000000006 3301 1.0
3741
0.200000000000000011 9.99999999999999955e-07 0.0687190937987684780583487585964
3842
0.200000000000000011 1.00000000000000008e-05 0.108912260585591831357806184556
3943
0.200000000000000011 0.00100000000000000002 0.273530102033034019134013607911
@@ -43,6 +47,7 @@
4347
0.200000000000000011 10 0.999998540143010797930830697127
4448
0.200000000000000011 100 1.0
4549
0.200000000000000011 1000 1.0
50+
0.200000000000000011 3301 1.0
4651
0.299999999999999989 9.99999999999999955e-07 0.0176595495901936665671778397068
4752
0.299999999999999989 1.00000000000000008e-05 0.0352353606155625769158079986437
4853
0.299999999999999989 0.00100000000000000002 0.140242458924867370902963425856
@@ -52,6 +57,7 @@
5257
0.299999999999999989 10 0.99999715515533278951644372428
5358
0.299999999999999989 100 1.0
5459
0.299999999999999989 1000 1.0
60+
0.299999999999999989 3301 1.0
5561
0.400000000000000022 9.99999999999999955e-07 0.00448690737698480048018769908146
5662
0.400000000000000022 1.00000000000000008e-05 0.0112705727782256817005130809278
5763
0.400000000000000022 0.00100000000000000002 0.0710923978953330760218610475958
@@ -61,6 +67,7 @@
6167
0.400000000000000022 10 0.999995127544578487259167603836
6268
0.400000000000000022 100 1.0
6369
0.400000000000000022 1000 1.0
70+
0.400000000000000022 3301 1.0
6471
0.5 9.99999999999999955e-07 0.00112837879096923635441785924383
6572
0.5 1.00000000000000008e-05 0.00356823633818045042058077366094
6673
0.5 0.00100000000000000002 0.0356705917296798854171108747554
@@ -70,6 +77,7 @@
7077
0.5 10 0.999992255783568955916362323619
7178
0.5 100 1.0
7279
0.5 1000 1.0
80+
0.5 3301 1.0
7381
0.599999999999999978 9.99999999999999955e-07 0.000281123932739927969642852394278
7482
0.599999999999999978 1.00000000000000008e-05 0.00111917075717695837092315556803
7583
0.599999999999999978 0.00100000000000000002 0.0177310780570833845378940622156
@@ -79,6 +87,7 @@
7987
0.599999999999999978 10 0.999988293084421631090774556099
8088
0.599999999999999978 100 1.0
8189
0.599999999999999978 1000 1.0
90+
0.599999999999999978 3301 1.0
8291
2 9.99999999999999955e-07 4.9999966666679162138149041803e-13
8392
2 1.00000000000000008e-05 4.99996666679166715135638665241e-11
8493
2 0.00100000000000000002 0.000000499666791633340297383350611252
@@ -88,6 +97,7 @@
8897
2 10 0.999500600772612666633108493329
8998
2 100 1.0
9099
2 1000 1.0
100+
2 3301 1.0
91101
3 9.99999999999999955e-07 1.6666654166671664402685631963e-19
92102
3 1.00000000000000008e-05 1.66665416671666693678925483422e-16
93103
3 0.00100000000000000002 1.66541716652780763845435502936e-10
@@ -97,6 +107,7 @@
97107
3 10 0.997230604284488424056328917551
98108
3 100 1.0
99109
3 1000 1.0
110+
3 3301 1.0
100111
6 9.99999999999999955e-07 1.38888769841321886877873406008e-39
101112
6 1.00000000000000008e-05 1.38887698417906798768211894792e-33
102113
6 0.00100000000000000002 1.38769893337745993330072800382e-21
@@ -106,6 +117,7 @@
106117
6 10 0.932914037120968217714240937173
107118
6 100 1.0
108119
6 1000 1.0
120+
6 3301 1.0
109121
12 9.99999999999999955e-07 2.08767377170244306355144979982e-81
110122
12 1.00000000000000008e-05 2.08765642802367929860815380041e-69
111123
12 0.00100000000000000002 2.08574950796625691581696824468e-45
@@ -115,6 +127,7 @@
115127
12 10 0.30322385369689331180582927404
116128
12 100 0.99999999999999999999999999999
117129
12 1000 1.0
130+
12 3301 1.0
118131
18 9.99999999999999955e-07 1.56191921714497984569652172566e-124
119132
18 1.00000000000000008e-05 1.56190589978546723049068336087e-106
120133
18 0.00100000000000000002 1.56044168515546614690893456571e-70
@@ -124,6 +137,7 @@
124137
18 10 0.0142776135970496129089830965836
125138
18 100 0.999999999999999999999998742916
126139
18 1000 1.0
140+
18 3301 1.0
127141
101 9.99999999999999955e-07 1.06090022487198225461869933883e-766
128142
101 1.00000000000000008e-05 1.06089077042094832542309156458e-665
129143
101 0.00100000000000000002 1.05985129506822713235416044578e-463
@@ -133,6 +147,7 @@
133147
101 10 5.33940546071971052337450543217e-64
134148
101 100 0.473437801470001529623393607105
135149
101 1000 1.0
150+
101 3301 1.0
136151
201 9.99999999999999955e-07 6.30833677503352627325382205745e-1584
137152
201 1.00000000000000008e-05 6.30828028132019299365575041933e-1383
138153
201 0.00100000000000000002 6.30206906057329746426353105998e-981
@@ -142,6 +157,7 @@
142157
201 10 3.01310889066541622340100430602e-181
143158
201 100 4.62617947019577288096442044516e-19
144159
201 1000 1.0
160+
201 3301 1.0
145161
1000 9.99999999999999955e-07 2.4851656605824546988202041697e-8568
146162
1000 1.00000000000000008e-05 2.48514331653642003877116895294e-7568
147163
1000 0.00100000000000000002 2.48268669750003876617472467308e-5568
@@ -151,3 +167,14 @@
151167
1000 10 1.13964958763725134069338684487e-1572
152168
1000 100 1.0270971815582277877665615585e-611
153169
1000 1000 0.504205244180215508503777843602
170+
1000 3301 1.0
171+
3003 9.99999999999999955e-07 8.90812855529069811196158732723e-27160
172+
3003 1.00000000000000008e-05 8.90804840918643793907789460332e-24157
173+
3003 0.00100000000000000002 8.89923673804629728415411068459e-18151
174+
3003 0.0100000000000000002 8.81952937102869844156580901528e-15148
175+
3003 0.100000000000000006 8.0606844309353722733298755906e-12145
176+
3003 1 3.27821191297260784144201433956e-9142
177+
3003 10 4.05779611158376897690039719814e-6143
178+
3003 100 3.42800829838332693574242322961e-3179
179+
3003 1000 6.77752709164469153719156665409e-567
180+
3003 3301 0.999999933219976432069280011406

tests/tables/inputs/igamma.dat

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ a =
1616
101
1717
201
1818
1000
19+
3003
1920

2021
x =
2122
1e-6
@@ -26,4 +27,5 @@ x =
2627
1
2728
10
2829
100
29-
1000
30+
1000
31+
3301

0 commit comments

Comments
 (0)