packages feed

hanalyze-0.1.0.0: demo/regression/MultivariateDemo.hs

{-# LANGUAGE OverloadedStrings #-}
-- | Phase T3-T5: RRR / PLS / CCA のデモ。
module Main where

import qualified Numeric.LinearAlgebra as LA
import Text.Printf (printf)
import System.Random.MWC (createSystemRandom)
import qualified System.Random.MWC.Distributions as MWC

import Hanalyze.Model.Multivariate

main :: IO ()
main = do
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  Phase T3-T5: RRR / PLS / CCA"
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn ""

  -- データ: 真の B が rank 1 (= 1 つの latent factor で説明可能)
  -- u (p×1): "directions" of X が response に効く
  -- v (q×1): "loadings" on Y
  let n = 100 :: Int
      p = 5  :: Int
      q = 3  :: Int
  let uTrue = LA.asColumn (LA.fromList [1.0, -0.5, 0.3, 0.2, -0.1])  -- p × 1
      vTrue = LA.asColumn (LA.fromList [2.0, 1.0, -0.5])             -- q × 1
      bTrue = uTrue LA.<> LA.tr vTrue                                -- p × q (rank 1)

  printf "真の B (rank 1, %dx%d):\n" p q
  printM bTrue
  putStrLn ""

  gen <- createSystemRandom
  -- X ~ N(0, I)
  xRows <- mapM (const (mapM (const (MWC.standard gen)) [1 .. p])) [1 .. n]
  let xMat = LA.fromLists xRows
  -- Y = X B + noise
  noiseRows <- mapM (const (mapM (const (MWC.normal 0 0.3 gen)) [1 .. q])) [1 .. n]
  let nMat = LA.fromLists noiseRows
      yMat = (xMat LA.<> bTrue) + nMat

  -- ── RRR ──
  putStrLn "[1] Reduced Rank Regression (rank=1)"
  let rrr = reducedRankRegression 1 xMat yMat
  printf "  推定 B̂ (rank %d):\n" (rrrRank rrr)
  printM (rrrBeta rrr)
  let bDiff = rrrBeta rrr - bTrue
      maxErr = LA.maxElement (LA.cmap abs bDiff)
  printf "  B̂ - B 最大誤差: %.4f\n" maxErr
  putStrLn ""

  -- 比較: 通常 OLS (rank 制約なし)
  let bOLS = xMat LA.<\> yMat
  printf "  比較: OLS B̂ (rank %d):\n" (LA.rank bOLS)
  printM bOLS
  putStrLn ""

  -- ── PLS ──
  putStrLn "[2] PLS Regression (k=2 成分)"
  let plsFit = pls 2 xMat yMat
  printf "  推定 B̂ (PLS k=2):\n"
  printM (plsBeta plsFit)
  let bDiffPLS = plsBeta plsFit - bTrue
      maxErrPLS = LA.maxElement (LA.cmap abs bDiffPLS)
  printf "  B̂ - B 最大誤差 (PLS): %.4f\n" maxErrPLS
  putStrLn ""

  -- ── CCA ──
  putStrLn "[3] CCA"
  let ccaFit = cca xMat yMat
      corrs = LA.toList (ccaCorr ccaFit)
  printf "  Canonical correlations: %s\n"
         (show (map (\v -> fromIntegral (round (v * 1000) :: Int) / 1000 :: Double) corrs))
  printf "  最大相関 (= rank 1 構造を反映): %.4f\n" (head corrs)
  putStrLn ""

  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  ✓ RRR / PLS / CCA すべて動作"
  putStrLn "═══════════════════════════════════════════════════════════════"

  where
    printM :: LA.Matrix Double -> IO ()
    printM m = mapM_ (\row -> do
                        putStr "    "
                        mapM_ (printf "%+8.3f  ") (LA.toList row)
                        putStrLn "")
                     (LA.toRows m)