我想在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版中停滞了三年,但是我无法理解。它取决于类型类MonadRandom
和RandomSource
,它们没有得到很好的解释。
我也查看了软件包mwc-probability
,但我也没有意义-看来您必须已经了解PrimMonad
和PrimState
类型类。 / p>
这两个软件包都令我感到惊讶,因为它们具有过于复杂的API,并且似乎已经完全放弃了System.Random
中的Haskell的标准随机数生成框架。
任何建议将不胜感激。
答案 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-random
和statistics
使其可以用于我的目的。为了使它们更易于使用,我定义了一些包装器,如下所述。我的解决方案使用的整套软件包是transformers
,vector
,mwc-random
和statistics
。
首先,这里是所需的语言扩展和导入:
{-# 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
,它使用户无需了解PrimMonad
和PrimState
,并提供了一些所需的实用程序。 (如果您对详细信息感兴趣,Gen s a
是一个统一的PRNG,它返回类型为a
的值,ST s
是一个状态转换器monad,以及未实例化的类型变量{{1 }}是类型系统黑客的一部分,可防止状态更新的副作用逃逸到monad。)s
和mwc-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
独立抽奖,请使用f
或replicateM 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