Numeric.Probability を使う〜Haskellの確率的プログラミング

  • Haskellで確率密度分布を扱ってランダムサンプリングするときにモナドを使う、という話がある
  • こちらの記事によると、それは遡ること2006年のこちらのペイパーとそれが作成したProbabilistic Fucntional Programming用パッケージが始まりだという。
  • その後、これをとり込もうよ、ということになり、今では、Preludeでとってくれば、後はインポートすればよい(らしい)
  • 以下、stackを使って、ここの処理がなぞれるようにするための処置
  • stackは入っているものとして、適当な場所でpfptestという名前で環境を作ることにする
stack new pfptest
cd probHoge
stack build
  • これで、GHCも入るし、準備はほぼOK
  • 次に、probability関係を使うために、pfptestフォルダのcabalファイルに、probability パッケージを使うことを記載して、再度 buildする
    • Main.hsで使うことにしましたよ、と
executable pfptest-exe
  hs-source-dirs:      app
  main-is:             Main.hs
  ghc-options:         -threaded -rtsopts -with-rtsopts=-N
  build-depends:       base
                     , pfptest
                     , probability
  default-language:    Haskell2010
  • こうしてから、再度build
stack build
  • これで、probabilityパッケージを使う環境として、pfptestが出来上がったから
stack ghci
  • としてghciを開始
    • cabalの記載状況から、Main Libという「環境」にでスタート
    • そこでNumeric.Probability.Distributionをimportすれば、choose関数が使えるようになりました、と。
*Main Lib> import Numeric.Probability.Distribution
*Main Lib Numeric.Probability.Distribution> choose 0.3 0 1
fromFreqs [(1,0.7),(0,0.3)]
*Main Lib Numeric.Probability.Distribution> 
  • chooseという関数について確認する
*Main Lib Numeric.Probability.Distribution> choose 0.3 0 1
fromFreqs [(1,0.7),(0,0.3)]
    • 値0を取る確率が0.3、値1を取る確率が0.7という、確率質量分布ができて
    • それをfromFreqsなる関数が受け取った状態、が返ってきている
    • 次のようなバリエーションができる。事象は0 1と指定したり、1 0と指定しても、'a' 'b'でも。
*Main Lib Numeric.Probability.Distribution> choose 0.3 1 0
fromFreqs [(0,0.7),(1,0.3)]
*Main Lib Numeric.Probability.Distribution> choose 0.3 'a' 'b'
fromFreqs [('b',0.7),('a',0.3)]
    • 現れる関数choose,fromFreqについて確認する、さらに現れるTも示してみる
*Main Lib Numeric.Probability.Distribution> :type choose
choose :: Num prob => prob -> a -> a -> T prob a
*Main Lib Numeric.Probability.Distribution> :type fromFreqs
fromFreqs :: Fractional prob => [(a, prob)] -> T prob a
*Main Lib Numeric.Probability.Distribution> :kind T
T :: * -> * -> *
*Main Lib Numeric.Probability.Distribution> :info T
newtype T prob a = Cons {decons :: [(a, prob)]}
  	-- Defined in ‘Numeric.Probability.Distribution’
*Main Lib Numeric.Probability.Distribution> :type Cons
Cons :: [(a, prob)] -> T prob a
*Main Lib Numeric.Probability.Distribution> :type decons
decons :: T prob a -> [(a, prob)]
*Main Lib Numeric.Probability.Distribution> decons $ choose 0.3 'a' 'b'
[('a',0.3),('b',0.7)]
*Main Lib Numeric.Probability.Distribution> :type decons $ choose 0.3 'a' 'b'
decons $ choose 0.3 'a' 'b' :: Fractional prob => [(Char, prob)]
  • 次の関数 uniform
*Main Lib Numeric.Probability.Distribution> uniform [0,1,2,3]
fromFreqs [(0,0.25),(1,0.25),(2,0.25),(3,0.25)]
*Main Lib Numeric.Probability.Distribution> decons $ uniform [0,1,2,3]
[(0,0.25),(1,0.25),(2,0.25),(3,0.25)]
(decons $ uniform [0,1,2,3]) !! 2
(2,0.25)
*Main Lib Numeric.Probability.Distribution> fst $ (decons $ uniform [0,1,2,3]) !! 2
2
*Main Lib Numeric.Probability.Distribution> snd $ (decons $ uniform [0,1,2,3]) !! 2
0.25
    • これを使うと、0以上のすべての整数の一様確率質量分布もできる。長さが無限なので全部表示させようとすると大変
      • また、確率値は1/無限大だが、表示させようとすると計算が終わらないので表示されない
*Main Lib Numeric.Probability.Distribution> let uni = uniform [0..]
*Main Lib Numeric.Probability.Distribution> fst (( decons uni) !! 1)
1
    • 起きうる事象が1つしか無いとき。事象は"ab"とする。ふたとおりの指定の仕方がある(ようだ)
*Main Lib Numeric.Probability.Distribution> certainly "ab"
fromFreqs [("ab",1)]
*Main Lib Numeric.Probability.Distribution> uniform ["ab"]
fromFreqs [("ab",1.0)]