packages feed

hanalyze-0.2.0.0: test/Hanalyze/Design/Custom/GoldenSpec.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Design.Custom.GoldenSpec (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 Data.Text   as T
import qualified Numeric.LinearAlgebra as LA
import qualified Hanalyze.Stat.ClassMetrics as CM
import qualified Hanalyze.Design.Optimal       as OPT
import qualified Hanalyze.Design.Custom.Factor     as CF
import qualified Hanalyze.Design.Custom.Model      as CM
import qualified Hanalyze.Design.Custom.Constraint as CC
import qualified Hanalyze.Design.Custom.Coordinate as CX
import qualified Hanalyze.Design.Custom.Compare    as CCMP
import SpecHelper

spec :: Spec
spec = do
  describe "Custom Design ゴールデン例題 (Phase 24-9)" $ do
    -- Example 1: 2 因子 2nd-order RSM (full quadratic、 p_terms = 6)
    it "golden ex1: 2 factor + 2nd-order RSM、 D-eff > 0、 全 grid 内" $ do
      let f1g = CF.Factor "x1" (CF.Continuous (-1) 1) CF.Controllable
          f2g = CF.Factor "x2" (CF.Continuous (-1) 1) CF.Controllable
          modelRSM = CM.Model
            [ CM.TIntercept
            , CM.TMain "x1", CM.TMain "x2"
            , CM.TInter ["x1","x2"]
            , CM.TPower "x1" 2, CM.TPower "x2" 2
            ] CM.NCoded
          spec = CX.CustomDesignSpec
            { CX.cdsFactors     = [f1g, f2g]
            , CX.cdsModel       = modelRSM
            , CX.cdsConstraints = []
            , CX.cdsNRuns       = 12
            , CX.cdsCriterion   = OPT.DOpt
            , CX.cdsBudget      = CX.defaultBudget
            , CX.cdsSeed        = Just 42
            , CX.cdsInitial     = Nothing

            , CX.cdsDJConvention = False
            }
      r <- CX.coordinateExchange spec
      case r of
        Left e -> expectationFailure (T.unpack e)
        Right cd -> do
          LA.rows (CX.cdMatrix cd) `shouldBe` 12
          LA.cols (CX.cdMatrix cd) `shouldBe` 2
          let vs = concat (LA.toLists (CX.cdMatrix cd))
          all (\v -> v >= -1.0001 && v <= 1.0001) vs `shouldBe` True
          -- D 値 > 0 (非特異)
          let dval = - CX.crCriterionValue (CX.cdReport cd)
          dval `shouldSatisfy` (> 0)
          -- D-eff via Compare も 0 より大きい
          let dc = CCMP.compareDesigns [("rsm", cd)]
          (CCMP.dcEffTable dc `LA.atIndex` (0, 0)) `shouldSatisfy` (> 0.4)
    -- Example 2: 1 連続 + 1 categorical(3) + main + interaction
    it "golden ex2: 1 cont + 1 cat(3) + main+int model、 各 cat level 出現" $ do
      let fc = CF.Factor "x"  (CF.Continuous (-1) 1)            CF.Controllable
          fk = CF.Factor "k"  (CF.Categorical ["A","B","C"])    CF.Controllable
          model = CM.Model
            [ CM.TIntercept
            , CM.TMain "x", CM.TMain "k"
            , CM.TInter ["x","k"]
            ] CM.NCoded
          spec = CX.CustomDesignSpec
            { CX.cdsFactors     = [fc, fk]
            , CX.cdsModel       = model
            , CX.cdsConstraints = []
            , CX.cdsNRuns       = 12
            , CX.cdsCriterion   = OPT.DOpt
            , CX.cdsBudget      = CX.defaultBudget
            , CX.cdsSeed        = Just 100
            , CX.cdsInitial     = Nothing

            , CX.cdsDJConvention = False
            }
      r <- CX.coordinateExchange spec
      case r of
        Left e -> expectationFailure (T.unpack e)
        Right cd -> do
          LA.rows (CX.cdMatrix cd) `shouldBe` 12
          let kCol = LA.toList (LA.flatten (LA.subMatrix (0, 1) (12, 1) (CX.cdMatrix cd)))
              ks   = map (round :: Double -> Int) kCol
          all (`elem` [0, 1, 2]) ks `shouldBe` True
          -- D-opt なら全 3 level が出現するはず (= balanced 設計の必要条件)
          length (filter (== 0) ks) `shouldSatisfy` (> 0)
          length (filter (== 1) ks) `shouldSatisfy` (> 0)
          length (filter (== 2) ks) `shouldSatisfy` (> 0)
    -- Example 3: LinearIneq 制約 + 2 factor main
    it "golden ex3: LinearIneq (x1+x2 <= 0.5) で全 row が制約満足" $ do
      let f1g = CF.Factor "x1" (CF.Continuous (-1) 1) CF.Controllable
          f2g = CF.Factor "x2" (CF.Continuous (-1) 1) CF.Controllable
          modelMain = CM.Model
            [CM.TIntercept, CM.TMain "x1", CM.TMain "x2"] CM.NCoded
          spec = CX.CustomDesignSpec
            { CX.cdsFactors     = [f1g, f2g]
            , CX.cdsModel       = modelMain
            , CX.cdsConstraints =
                [CC.LinearIneq [("x1", 1), ("x2", 1)] CC.CLeq 0.5]
            , CX.cdsNRuns       = 8
            , CX.cdsCriterion   = OPT.DOpt
            , CX.cdsBudget      = CX.defaultBudget
            , CX.cdsSeed        = Just 21
            , CX.cdsInitial     = Nothing

            , CX.cdsDJConvention = False
            }
      r <- CX.coordinateExchange spec
      case r of
        Left e -> expectationFailure (T.unpack e)
        Right cd -> do
          LA.rows (CX.cdMatrix cd) `shouldBe` 8
          let rows = LA.toLists (CX.cdMatrix cd)
              ok [a, b] = a + b <= 0.5 + 1e-6
              ok _      = False
          all ok rows `shouldBe` True
          -- D 値 > 0 (非特異)
          let dval = - CX.crCriterionValue (CX.cdReport cd)
          dval `shouldSatisfy` (> 0)