packages feed

hanalyze-0.1.0.0: demo/bayesian/AR1Demo.hs

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
-- | AR(1) 状態空間モデルのデモ (Phase J2)。
--
-- 真値: ϕ=0.7, σ_state=0.5, σ_obs=0.3
-- 真の x_t を AR(1) で生成、ノイズを加えて y_t を観測。
-- ϕ, σ_state, σ_obs を NUTS で同時推定 (x_t は latent ベクトル)。
module Main where

import qualified Data.Map.Strict as Map
import Data.Text (Text)
import qualified Data.Text as T
import Text.Printf (printf)
import System.Random.MWC (createSystemRandom)
import qualified System.Random.MWC.Distributions as MWC

import Hanalyze.MCMC.NUTS (nuts, defaultNUTSConfig, NUTSConfig (..))
import Hanalyze.Model.HBM (ModelP, sample, observe, ar1Latent,
                  Distribution (..), augmentChainWithDeterministic)
import Hanalyze.Viz.MCMC (printPosteriorSummary, posteriorSummaryFile)

cfg :: NUTSConfig
cfg = defaultNUTSConfig
        { nutsIterations = 600
        , nutsBurnIn     = 400
        , nutsStepSize   = 0.05
        , nutsMaxDepth   = 7
        }

genData :: Int -> Double -> Double -> Double -> IO ([Double], [Double])
genData nT phi sigSt sigOb = do
  gen <- createSystemRandom
  let stat0 = sigSt / sqrt (1 - phi * phi)
  z0 <- MWC.normal 0 stat0 gen
  let go t prev acc
        | t == nT = return (reverse acc)
        | otherwise = do
            eps <- MWC.normal 0 sigSt gen
            let x = phi * prev + eps
            go (t+1) x (x : acc)
  xs <- go 1 z0 [z0]
  ys <- mapM (\x -> do
                 e <- MWC.normal 0 sigOb gen
                 return (x + e)) xs
  return (xs, ys)

ar1Model :: Int -> [Double] -> ModelP ()
ar1Model nT ys = do
  phi   <- sample "phi"       (Uniform (-0.99) 0.99)
  sigSt <- sample "sig_state" (HalfNormal 1)
  sigOb <- sample "sig_obs"   (HalfNormal 1)
  xs <- ar1Latent "x" nT phi sigSt
  mapM_ (\(t, y) -> observe ("y_" <> tShow t) (Normal (xs !! t) sigOb) [y])
        (zip [0 .. nT - 1] ys)
  where
    tShow :: Int -> Text
    tShow = T.pack . show

main :: IO ()
main = do
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  AR(1) 状態空間モデル (Phase J2)"
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn ""

  let nT = 30
  printf "真値: ϕ=0.7, σ_state=0.5, σ_obs=0.3, 系列長 N=%d\n" nT
  (_, ysObs) <- genData nT 0.7 0.5 0.3
  printf "観測 y の最初 5 点: %s\n"
         (show (take 5 ysObs))
  putStrLn ""

  gen <- createSystemRandom
  let init0 = Map.fromList $
        [(("x_raw" <> T.pack (show t)) :: Text, 0.0 :: Double)
         | t <- [0 .. nT - 1]]
        ++ [("phi", 0.5), ("sig_state", 0.5), ("sig_obs", 0.3)]
  ch0 <- nuts (ar1Model nT ysObs) cfg init0 gen
  let ch = augmentChainWithDeterministic (ar1Model nT ysObs) ch0

  putStrLn "[1] Posterior summary (主要パラメタのみ)"
  printPosteriorSummary ["phi", "sig_state", "sig_obs"] [ch]
  putStrLn ""

  putStrLn "[2] 一部の latent state x_t (派生量)"
  printPosteriorSummary
    [ "x_" <> T.pack (show t) | t <- [0, 5, 10, 15, 20, 25, 29] ]
    [ch]
  putStrLn ""

  posteriorSummaryFile "ar1-summary.html" "AR(1) posterior"
    ["phi", "sig_state", "sig_obs"] [ch]
  putStrLn "  → ar1-summary.html"
  putStrLn ""

  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  ✓ AR(1) latent + 観測モデルで状態空間が動作"
  putStrLn "═══════════════════════════════════════════════════════════════"