{-# LANGUAGE BangPatterns #-}
module Harmonic.Traversal.Probabilistic
(
gammaIndex
, gammaIndexScaled
, gammaIndexScaledWith
, gammaSelect
, gammaSelectFromPool
, gammaSequence
, weightedSelect
, pickWeighted
, withRandomGen
) where
import System.Random (randomRIO, randomIO, StdGen, mkStdGen, Random(..))
import System.Random.MWC (GenIO, createSystemRandom, uniformRM)
import qualified System.Random.MWC.Distributions as Dist
import Statistics.Distribution (quantile)
import Statistics.Distribution.Gamma (gammaDistr)
import Data.List (sortBy)
import Data.Ord (Down(..))
import Control.Monad (replicateM)
gammaIndex :: Double -> Int -> IO Int
gammaIndex :: Double -> Int -> IO Int
gammaIndex Double
shape Int
maxIndex = do
Gen RealWorld
rng <- IO (Gen RealWorld)
IO GenIO
createSystemRandom
let safeShape :: Double
safeShape = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0.01 Double
shape
Double
x <- Double -> Double -> Gen RealWorld -> IO Double
forall g (m :: * -> *).
StatefulGen g m =>
Double -> Double -> g -> m Double
Dist.gamma Double
safeShape Double
1.0 Gen RealWorld
rng
let idx :: Int
idx = Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
x
Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
idx Int
maxIndex)
gammaIndexScaled :: Double
-> Int
-> IO Int
gammaIndexScaled :: Double -> Int -> IO Int
gammaIndexScaled Double
entropy Int
poolSize = do
Gen RealWorld
rng <- IO (Gen RealWorld)
IO GenIO
createSystemRandom
let clampedEntropy :: Double
clampedEntropy = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0.0 (Double -> Double -> Double
forall a. Ord a => a -> a -> a
min Double
1.0 Double
entropy)
shape :: Double
shape = Double
1.0 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
clampedEntropy Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
9.0
safeShape :: Double
safeShape = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0.01 Double
shape
Double
x <- Double -> Double -> Gen RealWorld -> IO Double
forall g (m :: * -> *).
StatefulGen g m =>
Double -> Double -> g -> m Double
Dist.gamma Double
safeShape Double
1.0 Gen RealWorld
rng
let idx :: Int
idx = Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
x
maxIdx :: Int
maxIdx = Int
poolSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
idx Int
maxIdx)
gammaIndexScaledWith :: GenIO
-> Double
-> Int
-> IO Int
gammaIndexScaledWith :: GenIO -> Double -> Int -> IO Int
gammaIndexScaledWith GenIO
gen Double
entropy Int
poolSize = do
let clampedEntropy :: Double
clampedEntropy = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0.0 (Double -> Double -> Double
forall a. Ord a => a -> a -> a
min Double
1.0 Double
entropy)
shape :: Double
shape = Double
1.0 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
clampedEntropy Double -> Double -> Double
forall a. Num a => a -> a -> a
* Double
9.0
safeShape :: Double
safeShape = Double -> Double -> Double
forall a. Ord a => a -> a -> a
max Double
0.01 Double
shape
Double
x <- Double -> Double -> Gen RealWorld -> IO Double
forall g (m :: * -> *).
StatefulGen g m =>
Double -> Double -> g -> m Double
Dist.gamma Double
safeShape Double
1.0 Gen RealWorld
GenIO
gen
let idx :: Int
idx = Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
x
maxIdx :: Int
maxIdx = Int
poolSize Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Int -> Int -> Int
forall a. Ord a => a -> a -> a
max Int
0 (Int -> Int -> Int
forall a. Ord a => a -> a -> a
min Int
idx Int
maxIdx)
gammaSelectFromPool :: Double -> [(a, Double)] -> IO (Maybe a)
gammaSelectFromPool :: forall a. Double -> [(a, Double)] -> IO (Maybe a)
gammaSelectFromPool Double
_ [] = Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
gammaSelectFromPool Double
entropy [(a, Double)]
pool = do
Int
idx <- Double -> Int -> IO Int
gammaIndexScaled Double
entropy ([(a, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(a, Double)]
pool)
Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a, Double) -> a
forall a b. (a, b) -> a
fst ([(a, Double)]
pool [(a, Double)] -> Int -> (a, Double)
forall a. HasCallStack => [a] -> Int -> a
!! Int
idx)
gammaSelect :: Double -> [(a, Double)] -> IO (Maybe a)
gammaSelect :: forall a. Double -> [(a, Double)] -> IO (Maybe a)
gammaSelect Double
_ [] = Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
gammaSelect Double
shape [(a, Double)]
candidates = do
let sorted :: [(a, Double)]
sorted = ((a, Double) -> (a, Double) -> Ordering)
-> [(a, Double)] -> [(a, Double)]
forall a. (a -> a -> Ordering) -> [a] -> [a]
sortBy (Down Double -> Down Double -> Ordering
forall a. Ord a => a -> a -> Ordering
compare (Down Double -> Down Double -> Ordering)
-> ((a, Double) -> Down Double)
-> (a, Double)
-> (a, Double)
-> Ordering
forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
`on` (Double -> Down Double
forall a. a -> Down a
Down (Double -> Down Double)
-> ((a, Double) -> Double) -> (a, Double) -> Down Double
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (a, Double) -> Double
forall a b. (a, b) -> b
snd)) [(a, Double)]
candidates
maxIdx :: Int
maxIdx = [(a, Double)] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [(a, Double)]
sorted Int -> Int -> Int
forall a. Num a => a -> a -> a
- Int
1
Int
idx <- Double -> Int -> IO Int
gammaIndex Double
shape Int
maxIdx
Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a, Double) -> a
forall a b. (a, b) -> a
fst ([(a, Double)]
sorted [(a, Double)] -> Int -> (a, Double)
forall a. HasCallStack => [a] -> Int -> a
!! Int
idx)
gammaSequence :: Double
-> Double
-> Int
-> IO [Int]
gammaSequence :: Double -> Double -> Int -> IO [Int]
gammaSequence Double
baseShape Double
entropy Int
count = do
Gen RealWorld
rng <- IO (Gen RealWorld)
IO GenIO
createSystemRandom
let effectiveShape :: Double
effectiveShape = Double
baseShape Double -> Double -> Double
forall a. Num a => a -> a -> a
* (Double
1 Double -> Double -> Double
forall a. Num a => a -> a -> a
+ Double
entropy)
Int -> IO Int -> IO [Int]
forall (m :: * -> *) a. Applicative m => Int -> m a -> m [a]
replicateM Int
count (IO Int -> IO [Int]) -> IO Int -> IO [Int]
forall a b. (a -> b) -> a -> b
$ do
Double
x <- Double -> Double -> Gen RealWorld -> IO Double
forall g (m :: * -> *).
StatefulGen g m =>
Double -> Double -> g -> m Double
Dist.gamma Double
effectiveShape Double
1.0 Gen RealWorld
rng
Int -> IO Int
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Int -> IO Int) -> Int -> IO Int
forall a b. (a -> b) -> a -> b
$ Double -> Int
forall b. Integral b => Double -> b
forall a b. (RealFrac a, Integral b) => a -> b
floor Double
x
weightedSelect :: [(a, Double)] -> IO (Maybe a)
weightedSelect :: forall a. [(a, Double)] -> IO (Maybe a)
weightedSelect [] = Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe a
forall a. Maybe a
Nothing
weightedSelect [(a, Double)]
candidates = do
let total :: Double
total = [Double] -> Double
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum (((a, Double) -> Double) -> [(a, Double)] -> [Double]
forall a b. (a -> b) -> [a] -> [b]
map (a, Double) -> Double
forall a b. (a, b) -> b
snd [(a, Double)]
candidates)
if Double
total Double -> Double -> Bool
forall a. Ord a => a -> a -> Bool
<= Double
0
then Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ (a, Double) -> a
forall a b. (a, b) -> a
fst ([(a, Double)] -> (a, Double)
forall a. HasCallStack => [a] -> a
head [(a, Double)]
candidates)
else do
Double
r <- (Double, Double) -> IO Double
forall a (m :: * -> *). (Random a, MonadIO m) => (a, a) -> m a
randomRIO (Double
0, Double
total)
Maybe a -> IO (Maybe a)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe a -> IO (Maybe a)) -> Maybe a -> IO (Maybe a)
forall a b. (a -> b) -> a -> b
$ a -> Maybe a
forall a. a -> Maybe a
Just (a -> Maybe a) -> a -> Maybe a
forall a b. (a -> b) -> a -> b
$ Double -> [(a, Double)] -> a
forall {t} {a}. (Ord t, Num t) => t -> [(a, t)] -> a
pick Double
r [(a, Double)]
candidates
where
pick :: t -> [(a, t)] -> a
pick t
_ [(a
x, t
_)] = a
x
pick t
r ((a
x, t
w):[(a, t)]
rest)
| t
r t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
w = a
x
| Bool
otherwise = t -> [(a, t)] -> a
pick (t
r t -> t -> t
forall a. Num a => a -> a -> a
- t
w) [(a, t)]
rest
pick t
_ [] = [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"weightedSelect: empty list"
pickWeighted :: [(a, Double)] -> Double -> a
pickWeighted :: forall a. [(a, Double)] -> Double -> a
pickWeighted [] Double
_ = [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"pickWeighted: empty list"
pickWeighted [(a, Double)]
candidates Double
r = Double -> [(a, Double)] -> a
forall {t} {a}. (Ord t, Num t) => t -> [(a, t)] -> a
go Double
r [(a, Double)]
candidates
where
go :: t -> [(a, t)] -> a
go t
_ [(a
x, t
_)] = a
x
go t
remaining ((a
x, t
w):[(a, t)]
rest)
| t
remaining t -> t -> Bool
forall a. Ord a => a -> a -> Bool
<= t
w = a
x
| Bool
otherwise = t -> [(a, t)] -> a
go (t
remaining t -> t -> t
forall a. Num a => a -> a -> a
- t
w) [(a, t)]
rest
go t
_ [] = [Char] -> a
forall a. HasCallStack => [Char] -> a
error [Char]
"pickWeighted: exhausted list"
withRandomGen :: Int -> (StdGen -> a) -> a
withRandomGen :: forall a. Int -> (StdGen -> a) -> a
withRandomGen Int
seed StdGen -> a
f = StdGen -> a
f (Int -> StdGen
mkStdGen Int
seed)
on :: (b -> b -> c) -> (a -> b) -> a -> a -> c
on :: forall b c a. (b -> b -> c) -> (a -> b) -> a -> a -> c
on b -> b -> c
cmp a -> b
f a
x a
y = b -> b -> c
cmp (a -> b
f a
x) (a -> b
f a
y)