hanalyze-0.1.0.0: demo/bayesian/EnergyDemo.hs
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
-- | NUTS の Energy plot / BFMI 診断デモ。
--
-- BFMI < 0.3 → reparameterization 推奨 (典型例: Neal's funnel)。
-- 0.3 以上が望ましく、PyMC の経験則ではしばしば 0.5 を目安にする。
--
-- 例 1: 単純なガウシアンモデル → BFMI 高い (= 健全)
-- 例 2: Neal's funnel (centered) → BFMI 低い ことを期待
module Main where
import qualified Data.Map.Strict as Map
import Text.Printf (printf)
import System.Random.MWC (createSystemRandom)
import Hanalyze.MCMC.Core (chainEnergy)
import Hanalyze.MCMC.NUTS (nuts, defaultNUTSConfig, NUTSConfig (..))
import Hanalyze.Model.HBM (ModelP, sample, observe, Distribution (..))
import Hanalyze.Stat.MCMC (bfmi)
import Hanalyze.Viz.Core (PlotConfig (..), defaultConfig, OutputFormat (..))
import Hanalyze.Viz.MCMC (energyPlotFile)
cfg :: NUTSConfig
cfg = defaultNUTSConfig
{ nutsIterations = 2000
, nutsBurnIn = 500
, nutsStepSize = 0.1
}
-- ---------------------------------------------------------------------------
-- 例 1: 普通の正規回帰
-- ---------------------------------------------------------------------------
healthyModel :: ModelP ()
healthyModel = do
mu <- sample "mu" (Normal 0 5)
sig <- sample "sigma" (HalfNormal 2)
observe "y" (Normal mu sig) [1.2, 0.9, 1.4, 0.7, 1.1, 1.0, 1.3, 0.95, 1.05, 1.15]
-- ---------------------------------------------------------------------------
-- 例 2: Neal's funnel (centered) — 病的な階層構造
-- ---------------------------------------------------------------------------
-- v ~ Normal(0, 3), x | v ~ Normal(0, exp(v/2))
-- v が大きいと x の分散が爆発、小さいと潰れる → エネルギー方向の探索失敗。
funnelModel :: ModelP ()
funnelModel = do
v <- sample "v" (Normal 0 3)
_ <- sample "x" (Normal 0 (exp (v / 2)))
return ()
main :: IO ()
main = do
putStrLn "═══════════════════════════════════════════════════════════════"
putStrLn " Energy plot / BFMI 診断デモ"
putStrLn "═══════════════════════════════════════════════════════════════"
putStrLn ""
gen <- createSystemRandom
-- ── 例 1: 健全 ──
putStrLn "[1] 健全な Gaussian モデル"
ch1 <- nuts healthyModel cfg
(Map.fromList [("mu", 1), ("sigma", 1)]) gen
let es1 = chainEnergy ch1
bfmi1 = bfmi es1
printf " Energy 列: %d 件、平均 = %.3f\n"
(length es1) (sum es1 / fromIntegral (length es1))
case bfmi1 of
Just v -> printf " BFMI = %.3f (>0.3 で良好、>0.5 で理想)\n" v
Nothing -> putStrLn " BFMI: 計算不能"
energyPlotFile HTML "energy-healthy.html"
(defaultConfig "Energy plot") { plotTitle = "Energy plot — healthy model"
, plotWidth = 600, plotHeight = 250 } ch1
putStrLn " → energy-healthy.html"
putStrLn ""
-- ── 例 2: Funnel ──
putStrLn "[2] Neal's funnel (centered parameterization)"
ch2 <- nuts funnelModel cfg
(Map.fromList [("v", 0), ("x", 0)]) gen
let es2 = chainEnergy ch2
bfmi2 = bfmi es2
printf " Energy 列: %d 件、平均 = %.3f\n"
(length es2) (sum es2 / fromIntegral (length es2))
case bfmi2 of
Just v -> printf " BFMI = %.3f (低い場合は reparameterization 推奨)\n" v
Nothing -> putStrLn " BFMI: 計算不能"
energyPlotFile HTML "energy-funnel.html"
(defaultConfig "Energy plot") { plotTitle = "Energy plot — Neal's funnel"
, plotWidth = 600, plotHeight = 250 } ch2
putStrLn " → energy-funnel.html"
putStrLn ""
putStrLn "═══════════════════════════════════════════════════════════════"
putStrLn " ✓ Energy plot / BFMI が動作"
putStrLn " NUTS のサンプル列は energy も保持 (chainEnergy フィールド)"
putStrLn "═══════════════════════════════════════════════════════════════"