packages feed

hanalyze-0.2.0.0: test/Hanalyze/Stat/SummarySpec.hs

{-# LANGUAGE OverloadedStrings #-}
-- | Hanalyze.Stat.Summary の spec (Phase 100: 表示系 ESS の essBulk 統一)。
--
-- posteriorSummary の srEssV が旧 pooled 'ess' ではなく chain 構造を渡した
-- 'essBulk' (arviz ess_bulk 互換・MCMCSpec の golden で検証済) と一致する
-- 配線を固定する。データは MCMCSpec と同じ決定的 LCG+AR(1)。
module Hanalyze.Stat.SummarySpec (spec) where

import qualified Data.Map.Strict as Map
import Test.Hspec

import Hanalyze.MCMC.Core    (Chain (..))
import Hanalyze.Stat.MCMC    (essBulk)
import Hanalyze.Stat.Summary (SummaryRow (..), posteriorSummary)

-- | glibc 系数の LCG (mod 2^31)・[-0.5, 0.5) 一様。Integer 演算なので厳密。
lcg :: Int -> Int -> [Double]
lcg seed n = take n (map toU (drop 1 (iterate step (fromIntegral seed))))
  where
    step x = (1103515245 * x + 12345) `mod` (2 ^ (31 :: Int)) :: Integer
    toU x  = fromIntegral x / 2 ^ (31 :: Int) - 0.5

-- | AR(1): y_i = phi*y_{i-1} + u_i (y_0 起点 0)。
ar1 :: Int -> Int -> Double -> [Double]
ar1 seed n phi = drop 1 (scanl (\prev u -> phi * prev + u) 0 (lcg seed n))

mkChain :: [Double] -> Chain
mkChain vs = Chain
  { chainSamples     = [Map.singleton "x" v | v <- vs]
  , chainAccepted    = 0
  , chainTotal       = 0
  , chainEnergy      = []
  , chainDivergences = []
  , chainTreeDepths  = []
  }

relClose :: Double -> Double -> Double -> Bool
relClose tol expected actual = abs (actual - expected) <= tol * abs expected

spec :: Spec
spec = do
  describe "posteriorSummary の ESS (Phase 100: essBulk 統一)" $ do
    it "多 chain: srEssV = essBulk perChain (arviz golden 84.428・旧 pooled ess 86.804 ではない)" $ do
      let perChain = [ar1 (c + 1) 300 0.9 | c <- [0 .. 3 :: Int]]
          [row]    = posteriorSummary ["x"] (map mkChain perChain)
      srEssV row `shouldBe` essBulk perChain
      srEssV row `shouldSatisfy` relClose 1e-6 84.42798772749184

    it "単一 chain でも essBulk (split 2 sub-chain) を返す" $ do
      let vs    = ar1 42 300 0.5
          [row] = posteriorSummary ["x"] [mkChain vs]
      srEssV row `shouldBe` essBulk [vs]
      srRhat row `shouldBe` Nothing