NestedSampling-0.1.1: lib/Statistics/MiniNest.hs
module Statistics.MiniNest where
import Control.Monad (forM)
import Data.IORef
import Data.List (sort)
import System.Random (randomRIO)
import Text.Printf
-- logarithmic addition log(exp(x)+exp(y))
plus :: Double -> Double -> Double
plus x y
| x > y = x + log (1 + exp (y-x))
| otherwise = y + log (1 + exp (x-y))
data NestedSamplingResult a = NestedSamplingResult {
nsLogZ :: Double,
nsLogZdelta :: Double, -- evidence +- deviation
nsInfoNats :: Double, -- information in nats
nsSamples :: [a] }
instance Show (NestedSamplingResult a) where
show result =
(printf "logZ: %.2f +- %.2f\n" (nsLogZ result) (nsLogZdelta result) ++
printf "information: %.2f nats\n" (nsInfoNats result) ++
printf "%i samples\n" (length $ nsSamples result))
class SamplingObject a where
setLogWt :: a -> Double -> a
getLogWt :: a -> Double
getLogL :: a -> Double
-- |nestedSampling computes the evidence Z and samples from the posterior.
-- Args:
-- priorSamples: a list of samples from the prior.
-- explore: a function that evolves an object within a likelihood constraint.
-- iterations: number of iterations to run.
nestedSampling :: (Ord a, SamplingObject a) => [a] -> (a -> Double -> IO a) -> Int -> IO (NestedSamplingResult a)
nestedSampling priorSamples explore iterations = do
let n = length priorSamples
-- Collection of n objects
objsRef <- newIORef priorSamples
samplesRef <- newIORef [] -- Posterior samples
hRef <- newIORef 0 -- Information, initially 0
logZRef <- newIORef (-10**37) -- ln(Evidence Z, initially 0)
-- Outermost interval of prior mass
-- ln(width in prior mass)
logWidthRef <- newIORef $ getLogWidth n
-- NESTED SAMPLING LOOP ______________________________________________
forM [1..iterations] (\nest -> do
-- Worst object in collection, with Weight = width * Likelihood
objs <- readIORef objsRef
let worst = head $ sort objs
logwidth <- readIORef logWidthRef
let worst' = setLogWt worst (logwidth + (getLogL worst))
-- Update Evidence Z and Information H
logZ <- readIORef logZRef
h <- readIORef hRef
let logZnew = plus logZ (getLogWt worst')
writeIORef hRef $ (exp $ getLogWt worst' - logZnew) * (getLogL worst')
+ (exp $ logZ - logZnew) * (h + logZ) - logZnew
writeIORef logZRef logZnew
-- Posterior Samples (optional)
oldSamples <- readIORef samplesRef
writeIORef samplesRef (worst' : oldSamples)
-- Kill worst object.
let objs' = drop 1 $ sort objs
writeIORef objsRef objs'
-- Copy another object at random.
objToCopy <- choice objs'
-- new likelihood constraint
let logLstar = getLogL worst'
-- Evolve copied object within constraint
mutatedCopy <- explore objToCopy logLstar
-- Save copied and mutated object.
writeIORef objsRef (mutatedCopy : objs')
-- Shrink interval
writeIORef logWidthRef (logwidth - 1.0 / fromIntegral n))
-- Exit with evidence Z, information H, and optional posterior Samples
logZ <- readIORef logZRef
h <- readIORef hRef
samples <- readIORef samplesRef
return $ NestedSamplingResult {
nsLogZ=logZ,
nsLogZdelta=sqrt (h / fromIntegral n), -- evidence +- deviation
nsInfoNats=h, -- information in nats
nsSamples=samples
}
-- |choice chooses uniformly at random from a list.
choice :: [a] -> IO a
choice [] = error "No items specified for choice."
choice [x] = return x
choice xs = do
let n = length xs
k <- randomRIO (0, n-1)
return $ xs !! k
floatRatio :: Int -> Int -> Float
floatRatio n1 n2 = fromIntegral n1 / fromIntegral n2
getLogWidth :: Int -> Double
getLogWidth n = log $ 1.0 - exp(-1.0 / fromIntegral n)