passage-0.1: Language/Passage/SliceSample.hs
module Language.Passage.SliceSample where
import System.Random
{- GNUPLOT commands to see the distribution:
binwidth=5
bin(x,width)=width*floor(x/width)
plot 'datafile' using (bin($1,binwidth)):(1.0) smooth freq with boxes
-}
{-
Some R code for testing:
db <- read.table('data.beta', header=F)[,1]
dg <- read.table('data.gamma', header=F)[,1]
dn <- read.table('data.norm', header=F)[,1]
library(MASS)
s <- seq(0,5,length=1000)
truehist(db)
lines(s, dbeta(s,2,5),col='red')
truehist(dg)
lines(s, dgamma(s,2,5),col='red')
qqnorm(dn)
-}
genAll :: IO ()
genAll = mapM_ (genTest 100000) [0..3]
genTest :: Integer -> Int -> IO ()
genTest cnt i = do
putStr $ "Generating " ++ show nm ++ ". "
xs <- genPts cnt slicer 1 5 source
writeFile nm $ unlines (map show xs)
putStrLn "Done."
where gammaLL a b x = (a - 1) * log x - b * x
normLL = (* 0.5) . negate . (**2)
betaLL a b x = (a - 1) * log x + (b-1) * log (1 - x)
(nm, slicer, source) =
[ ("data.gamma", slicePos, gammaLL 2 5)
, ("data.norm", slice, normLL)
, ("data.beta", sliceUnit, betaLL 2 5)
, ("data.truncNormal",sliceUnit, normLL)
] !! i
genPts :: Integer -> (StdGen -> Double -> Double -> (Double -> Double) -> (Double, Double, StdGen))
-> Double -> Double -> (Double -> Double) -> IO [Double]
genPts cnt slicer w initX ll = do
g <- newStdGen
return $ go g initX cnt
where go _ _ 0 = []
go g0 x0 c = let (x1, _, g1) = slicer g0 w x0 ll
in x1 : go g1 x1 (c-1)
sliceUnit :: StdGen -- ^ Source of randomness
-> Double -- ^ Width of initial sampling interval
-> Double -- ^ variable
-> (Double -> Double) -- ^ Log likelyhood, in terms of varibale
-> (Double, Double, StdGen) -- ^ New value for variable, together with its log-likelihood
sliceUnit g w x0 ll = genericSlice g w x0 ll (\_ _ -> 0) (\_ _ -> 1)
slicePos :: StdGen -- ^ Source of randomness
-> Double -- ^ Width of initial sampling interval
-> Double -- ^ variable
-> (Double -> Double) -- ^ Log likelyhood, in terms of varibale
-> (Double, Double, StdGen) -- ^ New value for variable, together with its log-likelihood
slicePos g w x0 ll = genericSlice g w x0 ll (\_ _ -> 0) (\y lo -> search y (right_pts y lo))
where search y = head . dropWhile (\p -> ll p > y)
right_pts _ lo = [ right, right + w .. ]
where right = x0 + lo
slice :: StdGen -- ^ Source of randomness
-> Double -- ^ Width of initial sampling interval
-> Double -- ^ variable
-> (Double -> Double) -- ^ Log likelyhood, in terms of varibale
-> (Double, Double, StdGen) -- ^ New value for variable, together with its log-likelihood
slice g w x0 ll = genericSlice g w x0 ll (\y lo -> search y (left_pts y lo)) (\y lo -> search y (right_pts y lo))
where search y = head . dropWhile (\p -> ll p > y)
left_pts _ lo = [ left, left - w .. ]
where left = x0 - lo
right_pts _ lo = [ right, right + w .. ]
where right = x0 - lo + w
genericSlice :: StdGen -- ^ Source of randomness
-> Double -- ^ Width of initial sampling interval
-> Double -- ^ variable
-> (Double -> Double) -- ^ Log likelyhood, in terms of varibale
-> (Double -> Double -> Double) -- ^ left bound
-> (Double -> Double -> Double) -- ^ right bound
-> (Double, Double, StdGen) -- ^ New value for variable, together with its log-likelihood
genericSlice g w x0 ll left right =
let (r, g1) = randomR (0, 1) g
y = log r + ll x0
(lo, g2) = randomR (0, w) g1
in pickRandom g2 x0 y ll (left y lo) (right y lo)
pickRandom :: StdGen -> Double -> Double -> (Double -> Double) -> Double -> Double -> (Double, Double, StdGen)
pickRandom g0 x0 y ll = go g0
where go g l r = if ll_x1 < y then if x1 < x0 then go g1 x1 r
else go g1 l x1
else (x1, ll_x1, g1)
where (x1, g1) = randomR (l, r) g
ll_x1 = ll x1