{-# LANGUAGE TupleSections, BangPatterns, DeriveLift #-}
module Data.Buffon.Machine
(
Rand(..), empty, init
, BuffonMachine(..), runRIO
, histogram, histogramIO
, samples, samplesIO, samplesIO'
, Bern, Discrete
, toDiscrete
, flip, flip'
, dyadic, rational, real
, repeat, cond, neg
, (/\), (\/), square
, mean, even
, exp, recipLog
, geometric, geometricReal, geometricRational, vonNeumann
, poisson, generalPoisson, poissonReal, poissonRational
, logarithmic, logarithmicReal, logarithmicRational
, uniform
, DecisionTree(..), decisionTree
, unveil, maxFlips, minFlips
, avgFlips, choice
) where
import Prelude hiding (flip, init, recip,
repeat, even, exp)
import qualified Prelude as P
import Control.Monad
import Data.Bits
import Data.Word (Word32)
import qualified Data.List as L
import Numeric (floatToDigits)
import Data.MultiSet (MultiSet)
import qualified Data.MultiSet as S
import System.Random
import Language.Haskell.TH.Syntax (Lift(..))
data Rand g =
Rand { buffer :: Word32
, counter :: Int
, oracle :: g
}
empty :: Rand g -> Bool
empty rng = counter rng == 32
init :: RandomGen g => g -> Rand g
init g = let (x, g') = random g
in Rand { buffer = x
, counter = 0
, oracle = g' }
newtype BuffonMachine g a =
BuffonMachine { runR :: Rand g -> (a, Rand g) }
instance Functor (BuffonMachine g) where
fmap = liftM
instance Applicative (BuffonMachine g) where
pure = return
(<*>) = ap
instance Monad (BuffonMachine g) where
return x = BuffonMachine (x,)
(BuffonMachine f) >>= h =
BuffonMachine $ \rng ->
let (x, rng') = f rng
in runR (h x) rng'
runRIO :: BuffonMachine StdGen a -> IO a
runRIO m = fst . runR m . init <$> getStdGen
samples' :: RandomGen g
=> BuffonMachine g a -> Int -> [a]
-> BuffonMachine g [a]
samples' _ 0 xs = return xs
samples' m !n xs = do
x <- m
samples' m (pred n) (x : xs)
samples :: RandomGen g
=> BuffonMachine g a -> Int -> BuffonMachine g [a]
samples m n = samples' m n []
samplesIO :: BuffonMachine StdGen a -> Int -> IO [a]
samplesIO m n = runRIO (samples m n)
samplesIO' :: BuffonMachine StdGen a -> Int -> IO [a]
samplesIO' m n = samplesIO'' m n []
samplesIO'' :: BuffonMachine StdGen a -> Int -> [a] -> IO [a]
samplesIO'' _ 0 xs = return xs
samplesIO'' m !n xs = do
x <- runRIO m
samplesIO'' m (pred n) (x : xs)
histogram :: RandomGen g
=> Discrete g -> Int
-> BuffonMachine g [(Int, S.Occur)]
histogram m n = do
ms <- histogram' m n S.empty
return $ S.toOccurList ms
histogram' :: RandomGen g
=> Discrete g -> Int -> MultiSet Int
-> BuffonMachine g (MultiSet Int)
histogram' _ 0 s = return s
histogram' m !n s = do
x <- m
let s' = S.insert x s
histogram' m (pred n) s'
histogramIO :: BuffonMachine StdGen Int -> Int -> IO ()
histogramIO m n = runRIO (histogram m n) >>= print
mkFlip :: Rand g -> (Bool, Rand g)
mkFlip rng =
(testBit (buffer rng) (counter rng),
rng { counter = succ (counter rng) })
type Bern g = BuffonMachine g Bool
type Discrete g = BuffonMachine g Int
toDiscrete :: Bern g -> Discrete g
toDiscrete m = do
b <- m
return $ if b then 1
else 0
flip :: RandomGen g => Bern g
flip = BuffonMachine $ \rng ->
mkFlip $ if empty rng then init (oracle rng)
else rng
flip' :: RandomGen g => Bern g
flip' = do
b0 <- flip
b1 <- flip
case (b0, b1) of
(False, True) -> return False
(True, False) -> return True
_ -> flip'
genStream :: Int -> [[Bool]]
genStream 0 = [[]]
genStream !n = map (False :) (genStream $ pred n)
++ map (True :) (genStream $ pred n)
repeat :: RandomGen g
=> Int -> Bern g -> BuffonMachine g [Bool]
repeat 0 _ = return []
repeat !n m = do
b <- m
bs <- repeat (pred n) m
return (b : bs)
dyadic :: RandomGen g => Int -> Int -> Bern g
dyadic s t = do
let ps = take s (genStream t)
bs <- repeat t flip
return $ bs `elem` ps
rational :: RandomGen g => Int -> Int -> Bern g
rational a b = do
let v = 2*a
heads <- flip
if v >= b then
if heads then rational (v - b) b
else return True
else
if heads then rational v b
else return False
type Bin = [Bool]
toBool :: Int -> Bool
toBool 0 = False
toBool 1 = True
toBool _ = error "Absurd case"
binExpansion' :: [Int] -> Int -> [Bool]
binExpansion' bs 0 = map toBool bs
binExpansion' bs !n = False : binExpansion' bs (succ n)
binExpansion :: Double -> [Bool]
binExpansion x = binExpansion' bs n
where (bs, n) = floatToDigits 2 x
real' :: RandomGen g => Bin -> Bern g
real' [] = error "Absurd case"
real' (b : bs) = do
heads <- flip'
if heads then real' bs
else return b
real :: RandomGen g => Double -> Bern g
real x = real' (binExpansion x)
cond :: Bern g
-> BuffonMachine g a
-> BuffonMachine g a
-> BuffonMachine g a
cond p f g = do
pv <- p
if pv then f
else g
neg :: Bern g -> Bern g
neg p = cond p (return False) (return True)
(/\) :: Bern g -> Bern g -> Bern g
(/\) p q = cond p q (return False)
(\/) :: Bern g -> Bern g -> Bern g
(\/) p = cond p (return True)
square :: Bern g -> Bern g
square p = p /\ p
mean :: RandomGen g
=> BuffonMachine g a
-> BuffonMachine g a
-> BuffonMachine g a
mean = cond flip
even :: RandomGen g => Bern g -> Bern g
even p =
cond (neg p) (return True)
(cond (neg p) (return False)
(even p))
geometric :: Bern g -> Discrete g
geometric p = geometric' p 0
geometric' :: Bern g -> Int -> Discrete g
geometric' p n = cond (neg p)
(return n)
(geometric' p $ succ n)
geometricReal :: RandomGen g => Double -> Discrete g
geometricReal x = geometric (real x)
geometricRational :: RandomGen g => Int -> Int -> Discrete g
geometricRational p q = geometric (rational p q)
data RandTrie = Leaf Int
| Node (Maybe RandTrie)
(Maybe RandTrie)
orderType :: RandTrie -> [Int]
orderType t = leaves t []
type DiffList = [Int] -> [Int]
leaves :: RandTrie -> DiffList
leaves (Leaf n) = ([n] ++)
leaves (Node lt rt) =
case (lt, rt) of
(Just lt', Nothing) -> leaves lt'
(Nothing, Just rt') -> leaves rt'
(Just lt', Just rt') -> leaves lt' . leaves rt'
_ -> ([] ++)
root :: Maybe RandTrie
root = Just (Node Nothing Nothing)
insertR :: RandomGen g
=> Maybe RandTrie
-> Int
-> BuffonMachine g (Maybe RandTrie)
insertR (Just (Leaf k)) n = do
node' <- insertR root k
insertR node' n
insertR (Just (Node lt rt)) n = do
c <- flip
if c then do
lt' <- insertR lt n
return $ Just (Node lt' rt)
else do
rt' <- insertR rt n
return $ Just (Node lt rt')
insertR Nothing n = return (Just $ Leaf n)
nats :: [Int]
nats = [0..]
trie :: RandomGen g
=> Int
-> BuffonMachine g (Maybe RandTrie)
trie n = foldM insertR root (take n nats)
vonNeumann :: RandomGen g
=> ([Int] -> Bool)
-> Discrete g
-> Discrete g
vonNeumann test p = do
n <- p
t <- trie n
case t of
Nothing -> error "Absurd case"
Just t' -> if test (orderType t') then return n
else vonNeumann test p
sorted :: [Int] -> Bool
sorted [] = True
sorted [_] = True
sorted (n : m : ns) =
n <= m && sorted (m : ns)
cyclic :: [Int] -> Bool
cyclic [] = False
cyclic (n : ns) =
all (n >) ns
poisson :: RandomGen g => Discrete g -> Discrete g
poisson = vonNeumann sorted
generalPoisson :: RandomGen g => Double -> Discrete g
generalPoisson p =
let (n, x) = properFraction p
in do
m <- poissonN 0.5 (2 * n)
if 0 < x && x < 1 then do
x' <- poissonReal x
return (m + x')
else return m
poissonN :: RandomGen g => Double -> Int -> Discrete g
poissonN p n = do
let m = geometric (real p)
xs <- samples (poisson m) n
return $ L.foldl' (+) 0 xs
poissonReal :: RandomGen g => Double -> Discrete g
poissonReal x = poisson (geometric $ real x)
poissonRational :: RandomGen g => Int -> Int -> Discrete g
poissonRational p q = poisson (geometric $ rational p q)
logarithmic :: RandomGen g => Discrete g -> Discrete g
logarithmic = vonNeumann cyclic
logarithmicReal :: RandomGen g => Double -> Discrete g
logarithmicReal x = logarithmic (geometric $ real x)
logarithmicRational :: RandomGen g => Int -> Int -> Discrete g
logarithmicRational p q = logarithmic (geometric $ rational p q)
exp :: RandomGen g => Bern g -> Bern g
exp = poisson' 0
recipLog :: RandomGen g => Bern g -> Bern g
recipLog = poisson' 1
poisson' :: RandomGen g => Int -> Bern g -> Bern g
poisson' k m = do
n <- poisson (geometric m)
return (n == k)
uniform :: RandomGen g => Int -> Discrete g
uniform n = uniform' n 1 0
uniform' :: RandomGen g => Int -> Int -> Int -> Discrete g
uniform' n !v !c = do
b <- flip
let v' = 2 * v
let c' = 2 * c + fromEnum b
if n <= v' then
if c' < n then return c'
else uniform' n (v' - n) (c' - n)
else uniform' n v' c'
layout :: [Double] -> [Bin]
layout xs = ys ++ [n]
where xs' = L.scanl1 (+) xs
ys = map binExpansion xs'
n = L.repeat True
data DecisionTree a = Decision a
| Toss (DecisionTree a)
(DecisionTree a)
deriving (Show,Lift)
toll :: Num a => (Int -> a -> a -> a)
-> Int -> DecisionTree b -> a
toll _ _ (Decision _) = 0
toll f !d (Toss lt rt) = f d lt' rt'
where lt' = toll f (succ d) lt
rt' = toll f (succ d) rt
maxFlips :: DecisionTree a -> Int
maxFlips = toll (\_ a b -> succ $ max a b) 0
minFlips :: DecisionTree a -> Int
minFlips = toll (\_ a b -> succ $ min a b) 0
avgFlips :: DecisionTree a -> Double
avgFlips = toll (\d a b -> P.recip (2 ^ d) + a + b) 0
unveil :: Show a => Int -> DecisionTree a -> String
unveil _ (Decision a) = show a
unveil 0 (Toss _ _) = "..."
unveil n (Toss lt rt) = "(" ++ lt' ++ " | " ++ rt' ++ ")"
where lt' = unveil (pred n) lt
rt' = unveil (pred n) rt
isCut :: (a, Bin) -> Bool
isCut (_, True : _ : _) = True
isCut _ = False
decisionTree :: [Double] -> DecisionTree Int
decisionTree ps = decisionTree' (zip nats ps')
where ps' = layout ps
decisionTree' :: [(Int, Bin)] -> DecisionTree Int
decisionTree' [] = error "Absurd case"
decisionTree' [(n, _)] = Decision n
decisionTree' ps = Toss lt' rt'
where (lt, rt) = splits ps
lt' = decisionTree' (shave lt)
rt' = decisionTree' (shave rt)
splits :: [(a, Bin)] -> ([(a, Bin)], [(a, Bin)])
splits ps =
case splits' [] ps of
(p, lt @ ((_, [True]) : _), rt) -> (reverse lt, p : rt)
(p, lt, rt) -> (reverse (p : lt), p : rt)
splits' :: [(a, Bin)] -> [(a, Bin)] -> ((a, Bin), [(a, Bin)], [(a, Bin)])
splits' xs [p] = (p, xs, [])
splits' xs (p : ps)
| isCut p = (p, xs, ps)
| otherwise = splits' (p : xs) ps
splits' _ _ = error "Absurd case"
shave :: [(Int, Bin)] -> [(Int,Bin)]
shave [] = []
shave (p : ps) =
case p of
(n, [True]) -> (n, L.repeat True) : shave ps
(n, _ : bs) -> (n, bs) : shave ps
_ -> error "Absurd case"
choice :: (Num a, Enum a, RandomGen g)
=> DecisionTree a -> BuffonMachine g a
choice (Decision n) = return n
choice (Toss lt rt) = do
heads <- flip
if heads then choice rt
else choice lt