File: Compat.hs

package info (click to toggle)
haskell-math-functions 0.3.4.4-2
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 1,132 kB
  • sloc: haskell: 2,675; python: 121; makefile: 2
file content (167 lines) | stat: -rw-r--r-- 5,461 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
{-# LANGUAGE CPP                      #-}
{-# LANGUAGE ForeignFunctionInterface #-}
-- |
-- Functions which have different implementations on different platforms
module Numeric.SpecFunctions.Compat (
    erf
  , erfc
  , log1p
  , expm1
  ) where

#if !defined(USE_SYSTEM_ERF) || !defined(USE_SYSTEM_EXPM1)
import qualified Data.Vector.Unboxed as U
#endif

#if !defined(USE_SYSTEM_ERF)
import Numeric.Polynomial.Chebyshev    (chebyshev)
import Numeric.Polynomial              (evaluateOddPolynomial)
#endif

#if !defined(USE_SYSTEM_EXPM1)
import Control.Applicative             (liftA2)
import Numeric.Polynomial.Chebyshev    (chebyshevBroucke)
import Numeric.Series                  (scanSequence,sumSeries,enumSequenceFrom)
import Numeric.MathFunctions.Constants
#endif

#if defined(USE_SYSTEM_EXPM1)
import GHC.Float (log1p,expm1)
#endif


----------------------------------------------------------------
-- erf & erfc
--
-- We provide pure haskell implementation for GHCJS and accessible on
-- GHC via flag
----------------------------------------------------------------

#if defined(USE_SYSTEM_ERF)

erf :: Double -> Double
erf = c_erf
{-# INLINE erf #-}

erfc :: Double -> Double
erfc = c_erfc
{-# INLINE erfc #-}

foreign import ccall unsafe "erf"  c_erf  :: Double -> Double
foreign import ccall unsafe "erfc" c_erfc :: Double -> Double

#else

erf :: Double -> Double
erf x
  -- Computing erf as 1-erfc loses precision near 0 so we switch to
  -- Taylor expansion here
  | abs x < 0.1 = 0.56418958354775629
                * evaluateOddPolynomial x erfTaylorSeries
  | x < 0       = (-1) + erfcCheb (-x)
  | otherwise   =   1  - erfcCheb x

erfTaylorSeries :: U.Vector Double
{-# NOINLINE erfTaylorSeries #-}
erfTaylorSeries = U.fromList
  [  2
  , -2/3
  ,  1/5
  , -1/21
  ,  1/108
  , -1/660
  ,  1/4680
  ]

erfc :: Double -> Double
erfc x | x < 0     = 2 - erfcCheb (-x)
       | otherwise = erfcCheb x

-- Adapted from Numerical Recipes §6.2.2
erfcCheb :: Double -> Double
erfcCheb z
  = t * exp( -z * z + chebyshev ty erfcCoef )
  where
    -- We're using approximation:
    --
    --   erfc(z) ≈ t·exp(-z² + P(t))
    --   t       = 2 / (2 + z)
    t  = 2 / (2 + z)
    ty = 2 * t - 1


erfcCoef :: U.Vector Double
{-# NOINLINE erfcCoef #-}
erfcCoef = U.fromList
  [ -0.6513268598908546   ,  6.4196979235649026e-1 ,  1.9476473204185836e-2
  , -9.561514786808631e-3 , -9.46595344482036e-4   ,  3.66839497852761e-4
  ,  4.2523324806907e-5   , -2.0278578112534e-5    , -1.624290004647e-6
  ,  1.303655835580e-6    ,  1.5626441722e-8       , -8.5238095915e-8
  ,  6.529054439e-9       ,  5.059343495e-9        , -9.91364156e-10
  , -2.27365122e-10       ,  9.6467911e-11         ,  2.394038e-12
  , -6.886027e-12         ,  8.94487e-13           ,  3.13092e-13
  , -1.12708e-13          ,  3.81e-16              ,  7.106e-15
  , -1.523e-15            , -9.4e-17               ,  1.21e-16
  , -2.8e-17
  ]

#endif


----------------------------------------------------------------
-- expm1 & log1p
--
-- We use one provided by base of for GHCJS use hand-coded one
----------------------------------------------------------------

#if !defined(USE_SYSTEM_EXPM1)

-- | Compute @exp x - 1@ without loss of accuracy for x near zero.
expm1 :: Double -> Double
-- NOTE: this is simplest implementation and not terribly efficient.
expm1 x
  | x < (-37.42994775023705) = -1
  | x > m_max_log            = m_pos_inf
  | abs x > 0.5              = exp x - 1
  | otherwise                = sumSeries $ liftA2 (*) (scanSequence (*) x (pure x))
                                                      (1 / scanSequence (*) 1 (enumSequenceFrom 2))
-- | Compute the natural logarithm of 1 + @x@.  This is accurate even
--   for values of @x@ near zero, where use of @log(1+x)@ would lose
--   precision.
log1p :: Double -> Double
log1p x
    | x == 0               = 0
    | x == -1              = m_neg_inf
    | x < -1               = m_NaN
    | x' < m_epsilon * 0.5 = x
    | (x >= 0 && x < 1e-8) || (x >= -1e-9 && x < 0)
                           = x * (1 - x * 0.5)
    | x' < 0.375           = x * (1 - x * chebyshevBroucke (x / 0.375) coeffs)
    | otherwise            = log (1 + x)
  where
    x' = abs x
    coeffs = U.fromList [
               0.10378693562743769800686267719098e+1,
              -0.13364301504908918098766041553133e+0,
               0.19408249135520563357926199374750e-1,
              -0.30107551127535777690376537776592e-2,
               0.48694614797154850090456366509137e-3,
              -0.81054881893175356066809943008622e-4,
               0.13778847799559524782938251496059e-4,
              -0.23802210894358970251369992914935e-5,
               0.41640416213865183476391859901989e-6,
              -0.73595828378075994984266837031998e-7,
               0.13117611876241674949152294345011e-7,
              -0.23546709317742425136696092330175e-8,
               0.42522773276034997775638052962567e-9,
              -0.77190894134840796826108107493300e-10,
               0.14075746481359069909215356472191e-10,
              -0.25769072058024680627537078627584e-11,
               0.47342406666294421849154395005938e-12,
              -0.87249012674742641745301263292675e-13,
               0.16124614902740551465739833119115e-13,
              -0.29875652015665773006710792416815e-14,
               0.55480701209082887983041321697279e-15,
              -0.10324619158271569595141333961932e-15
             ]
#endif