gamma-0.7: extras/LanczosConstants.hs
-- This makes use of a not-yet-released matrix library. It could be rewritten
-- to use any of the existing ones on hackage, but I don't know of any of them
-- that support matrices over arbitrary types - they are all focused on
-- efficiently packing the matrices and/or calling foreign libraries
-- (BLAS/GSL/etc.) and do not support any types other than Double, Float, and
-- Complex Double/Float.
--
-- I am keeping it around anyway and including this file in the source
-- distribution, because with a very small amount of work an end-user
-- could fill in the gaps and use this code to generate their own constants
-- for lanczos gamma function approximations, which one may wish to do if
-- they wanted to implement, say, a gamma function for a very high precision
-- floating point type.
--
-- Note that these really need to be run with significantly higher precision
-- than the target type or truncation error will make the results useless.
--
-- The algorithm implemented here is by Paul Godfrey, and is described in full
-- at http://www.numericana.com/answer/info/godfrey.htm (as of 21 June 2010).
module LanczosConstants where
import Math.Matrix
import Math.Matrix.Alias
cs g n = vectorToList (applyRat dbc f)
where
applyRat :: (Real t, Fractional t) => IMatrix Rational -> IVector t -> IVector t
applyRat m v = fromRatVec (apply m (toRatVec v))
fromRatVec :: (Vector v t, Fractional t) => IVector Rational -> v t
fromRatVec = convertByV fromRational
toRatVec :: (Vector v t, Real t) => v t -> IVector Rational
toRatVec = convertByV toRational
dbc = dbcMat n
f = fVec g n
dbcMat n = multRat d (multRat b c)
where
multRat :: (Real a, Matrix m1 a, Real b, Matrix m2 b) => m1 a -> m2 b -> IMatrix Rational
multRat = multiplyWith sum (\d b -> toRational d * toRational b)
d = dMat n
b = bMat n
c = cMat n
fVec :: (Floating b, Vector v b) => b -> Int -> v b
fVec g n = vector n f
where
f a = sqrt (2 / pi)
* product [fromIntegral i - 0.5 | i <-[1..a]]
* exp (a' + g + 0.5)
/ (a' + g + 0.5) ** (a' + 0.5)
where a' = fromIntegral a
cMat :: Int -> IMatrix Rational
cMat n = matrix n n m
where
m 0 0 = 1/2
m i j = fromInteger (c (2*i) (2*j))
c 0 0 = 1
c 1 1 = 1
c i 0 = negate (c (i-2) 0)
c i j
| i == j = 2 * c (i-1) (j-1)
| i > j = 2 * c (i-1) (j-1) - c (i-2) j
| otherwise = 0
dMat :: Int -> IAlias Mat Integer
dMat n = AsDiag (IVec (ivector n dFunc)) 0
where
dFunc 0 = 1
dFunc (i+1) = negate (factorial (2*i+2) `div` (2 * factorial i * factorial (i+1)))
factorial n = product [1..toInteger n]
bMat :: Int -> IMatrix Integer
bMat n = matrixFromList bList
where
bList = take n . map (take n) $
repeat 1 :
[ replicate i 0 ++ bicofs (negate (toInteger i*2))
| i <- [1..]
]
bFunc 0 _ = 1
bFunc i j
| i > j = 0
bFunc i j = bicofs (toInteger (2 * j - 1)) !! i
bicofs x = go x 1 1
where
go num denom x = x : go (num+signum num) (denom+signum denom) (x * num `div` denom)
--
-- p g k = sum [c (2*k+1) (2*a+1) * f a | a <- [0..k]]
-- where
-- k' = fromIntegral k
-- f a =
{-# INLINE risingPowers #-}
risingPowers x = scanl1 (*) (iterate (1+) x)
{-# INLINE fallingPowers #-}
fallingPowers x = scanl1 (*) (iterate (subtract 1) x)