packages feed

hanalyze-0.2.0.0: test/Hanalyze/Model/Formula/WLSSpec.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Model.Formula.WLSSpec (spec) where

import Test.Hspec
import Test.Hspec.QuickCheck (prop)
import Test.QuickCheck
import Hanalyze.Model.Formula
import Hanalyze.Model.Formula.Frame
import Hanalyze.Model.Formula.Design
import Hanalyze.Model.Formula.RFormula
import Hanalyze.Model.Formula.Nonlinear
import Hanalyze.Model.Formula.Mixed
import Hanalyze.Model.GLMM
import Hanalyze.Model.GLM (Family (..), LinkFn (..))
import Hanalyze.Stat.Distribution (Transform)
import Data.List (sort, nub)
import Control.Monad (forM, forM_)
import System.IO.Temp (withSystemTempFile)
import System.IO     (hPutStr, hClose)
import           Hanalyze.Model.HBM.Ast (Expr (..), Lit (..), DoStmt (..), Err)
import           Data.IORef         (newIORef, readIORef, modifyIORef')
import qualified Numeric.LinearAlgebra as LA
import qualified DataFrame.Internal.Column    as DX
import qualified DataFrame.Internal.DataFrame  as DX
import qualified Hanalyze.Model.Core        as Core
import qualified Hanalyze.MCMC.Core as Core
import SpecHelper

spec :: Spec
spec = do
  describe "weights / offset = WLS (Phase 47 A3)" $ do
    let frm s = case parseFormula s of Right f -> f; Left e -> error e
        xs = [1,2,3,4,5,6,7,8] :: [Double]
        ys = [2.1,3.9,6.2,7.8,10.1,12.2,13.8,16.1] :: [Double]
        ws = [1,1,2,2,3,3,4,4] :: [Double]
        df = DX.fromNamedColumns
               [ ("y", DX.fromList ys), ("x", DX.fromList xs), ("w", DX.fromList ws) ]
        f  = frm "y x = b0 + b1*x"
        coefOf = LA.toList . Core.coefficientsV
        close a b = and (zipWith (\p q -> abs (p - q) < 1e-8) a b)

    it "w≡1 (重みなし) は OLS = fitLMF と一致" $ do
      let Right (a, _) = fitWLSF defaultWLS f df
          Right (b, _) = fitLMF f df
      close (coefOf a) (coefOf b) `shouldBe` True

    it "WLS 係数 = 閉形式 (XᵀWX)⁻¹XᵀWy (hmatrix 直計算オラクル)" $ do
      let Right (r, _) = fitWLSF defaultWLS { wcWeights = Just "w" } f df
          x    = LA.fromLists [ [1, xi] | xi <- xs ]
          w    = LA.diag (LA.fromList ws)
          y    = LA.asColumn (LA.fromList ys)
          beta = LA.flatten (LA.inv (LA.tr x LA.<> w LA.<> x) LA.<> LA.tr x LA.<> w LA.<> y)
      close (coefOf r) (LA.toList beta) `shouldBe` True

    it "offset: y を offset z 付きで fit = (y−z) を素の fit と一致" $ do
      let zs  = [0.5,1.0,1.5,2.0,2.5,3.0,3.5,4.0] :: [Double]
          dfO = DX.fromNamedColumns
                  [ ("y", DX.fromList ys), ("x", DX.fromList xs), ("z", DX.fromList zs) ]
          dfM = DX.fromNamedColumns
                  [ ("y", DX.fromList (zipWith (-) ys zs)), ("x", DX.fromList xs) ]
          Right (ro, _) = fitWLSF defaultWLS { wcOffset = Just "z" } f dfO
          Right (rm, _) = fitLMF f dfM
      close (coefOf ro) (coefOf rm) `shouldBe` True

  -- ----------------------------------------------------------------------------
  -- A4 (Phase 47) 非線形フィット NLS
  -- ----------------------------------------------------------------------------