用于从标准概率分布中采样的Haskell软件包

时间:2019-05-10 20:59:10

标签: haskell random

我想在Haskell中做一些蒙特卡洛分析。我希望能够编写如下代码:

do n <- poisson lambda
   xs <- replicateM n $ normal mu sigma
   return $ maximum xs

对应于随机模型

n ~ Poisson(lambda)
for (i in 1:n)
  x[i] ~ Normal(mu, sigma)
y = max_{i=1}^n x[i]

我可以很容易地看到如何创建必要的随机采样单子。但是,我希望不必为所有标准概率分布实施采样器。是否已经有已实现这些的Haskell软件包?

我看过软件包random-fu,该软件包在0.2.7版中停滞了三年,但是我无法理解。它取决于类型类MonadRandomRandomSource,它们没有得到很好的解释。

我也查看了软件包mwc-probability,但我也没有意义-看来您必须已经了解PrimMonadPrimState类型类。 / p>

这两个软件包都令我感到惊讶,因为它们具有过于复杂的API,并且似乎已经完全放弃了System.Random中的Haskell的标准随机数生成框架。

任何建议将不胜感激。

2 个答案:

答案 0 :(得分:5)

好吧,如果您希望能够编写如下代码:

do n <- poisson lambda
   xs <- replicateM n $ normal mu sigma
   return $ maximum xs

那么您大概想使用random-fu

import Control.Monad
import Data.Random
import Data.Random.Distribution.Poisson
import Data.Random.Distribution.Normal

foo :: RVar Double
foo = do
  n <- poisson lambda
  xs <- replicateM (n+1) $ normal mu sigma
  return $ maximum xs

  where lambda = 10 :: Double
        mu = 0
        sigma = 6

main :: IO ()
main = print =<< replicateM 10 (sample foo)

我不确定过去三年中缺乏更新是否应该成为决定因素。在伽马分布世界中真的有 令人振奋的进步吗?

实际上,mwc-probability的工作原理大致相同:

import Control.Monad
import System.Random.MWC.Probability

foo :: Prob IO Double
foo = do
  n <- poisson lambda
  xs <- replicateM (n+1) $ normal mu sigma
  return $ maximum xs

  where lambda = 10 :: Double
        mu = 0
        sigma = 6

main :: IO ()
main = do
  gen <- createSystemRandom
  print =<< replicateM 10 (sample foo gen)

答案 1 :(得分:0)

我充分了解了软件包mwc-randomstatistics使其可以用于我的目的。为了使它们更易于使用,我定义了一些包装器,如下所述。我的解决方案使用的整套软件包是transformersvectormwc-randomstatistics

首先,这里是所需的语言扩展和导入:

{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE FlexibleContexts #-}

import Control.Monad (replicateM)
import Control.Monad.ST
import Control.Monad.Trans.Reader
import qualified Data.Vector as V
import Data.Vector.Generic hiding (replicateM, sum, product)
import Data.Word
import System.Random.MWC (Gen)
import qualified System.Random.MWC as R
import Statistics.Distribution
import Statistics.Distribution.Normal

我定义了一个随机抽样的monad Rand,它使用户无需了解PrimMonadPrimState,并提供了一些所需的实用程序。 (如果您对详细信息感兴趣,Gen s a是一个统一的PRNG,它返回类型为a的值,ST s是一个状态转换器monad,以及未实例化的类型变量{{1 }}是类型系统黑客的一部分,可防止状态更新的副作用逃逸到monad。)smwc-random软件包的不幸特征是基于Marsaglia的MWC256的原始PRNG。算法,是硬连线的;您不能替换其他PRNG。

statistics

如果您有type Rand0 s a = ReaderT (Gen s) (ST s) a type Rand a = (forall s. Rand0 s a) -- the random-sampling monad -- A draw from the Uniform(0.0, 1.0) distribution uniform01 :: Rand Double uniform01 = ask >>= R.uniform -- Provide a seed for the PRNG and return a draw from the random sampler runRandL :: Rand a -> [Word32] -> a runRandL rand seeds = runRandV rand (V.fromList seeds) -- Provide a seed for the PRNG and return a draw from the random sampler runRandV :: Vector v Word32 => Rand a -> v Word32 -> a runRandV rand seeds = runST $ R.initialize seeds >>= runReaderT rand -- Seed the PRNG with data from the system's fast source of pseudo-random numbers, -- then return a draw from the random sampler runRandIO :: Rand a -> IO a runRandIO rand = do gen <- R.createSystemRandom seeds <- R.fromSeed <$> R.save gen return $ runRandV rand seeds ,并且希望从f :: Rand a进行n独立抽奖,请使用freplicateM f :: Rand [a]。以下示例从V.replicateM f :: Rand (V.Vector a)个独立均匀随机变量的总和中得出平局:

n

sumOfUniform :: Int -> Rand Double sumOfUniform n = do xs <- replicateM n uniform01 return $ sum xs 软件包提供了与常见的统计分布相对应的各种数据类型。例如,statistics是代表正态分布的对象;它包含参数值normalDistr mu sigma :: NormalDistribution(平均值)和mu(标准偏差)。

类型为sigma的实例为分布类型为ContDistr d的对象提供密度和CDF逆函数。

类型为d的实例提供了一个ContGen d函数,用于从连续分布中进行绘制,类型为genContVar的实例提供了一个DiscrGen d函数,用于从一个离散分布中进行绘制分布。

为了使genDiscrVar monad易于使用,我定义了一些包装器:

Rand

其他分布(指数,beta,gamma等)的包装器遵循与正态分布的包装器相同的模式。

作为最后一个例子,这是两个正态分布随机变量的乘积得出的结果:

-- Draw from the continuous distribution d
genContV :: ContGen d => d -> Rand Double
genContV d = ask >>= genContVar d

-- Draw from the discrete distribution d
genDiscrV :: DiscreteGen d => d -> Rand Int
genDiscrV d = ask >>= genDiscreteVar d

-- Draw from the continuous distribution d using the inverse CDF method.
genCont :: ContDistr d => d -> Rand Double
genCont d = ask >>= genContinuous d

-- Draw from the normal distribution with mean mu and std dev sigma
normal :: Double -> Double -> Rand Double
normal mu sigma = genContV $ normalDistr mu sigma