packages feed

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

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Design.Custom.AugmentSpec (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 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.Coordinate as CX
import qualified Hanalyze.Design.Custom.Augment    as CAUG
import SpecHelper

spec :: Spec
spec = do
  describe "Hanalyze.Design.Custom.Augment (Phase 25-6/7/8)" $ do
    let f1a = CF.Factor "x1" (CF.Continuous (-1) 1) CF.Controllable
        f2a = CF.Factor "x2" (CF.Continuous (-1) 1) CF.Controllable
        fcA = CF.Factor "cat" (CF.Categorical ["A","B"]) CF.Controllable
        modelA = CM.Model
          [CM.TIntercept, CM.TMain "x1", CM.TMain "x2"] CM.NCoded
        rawExisting = LA.fromLists [[-1,-1],[1,-1],[-1,1],[1,1]]
        baseSpec = CX.CustomDesignSpec
          { CX.cdsFactors = [f1a, f2a]
          , CX.cdsModel   = modelA
          , CX.cdsConstraints = []
          , CX.cdsNRuns   = 4
          , CX.cdsCriterion = OPT.DOpt
          , CX.cdsBudget    = CX.defaultBudget
          , CX.cdsSeed      = Just 0
          , CX.cdsInitial   = Just rawExisting

          , CX.cdsDJConvention = False
          }
    it "cdsInitial = Nothing で Left" $ do
      let s = baseSpec { CX.cdsInitial = Nothing }
      r <- CAUG.augmentMenu s (CAUG.Replicate 1)
      case r of Left _ -> pure (); Right _ -> expectationFailure "expected Left"
    it "Replicate 2: 既存 4 行を 2 回複製、 合計 12 行" $ do
      Right r <- CAUG.augmentMenu baseSpec (CAUG.Replicate 2)
      LA.rows (CAUG.amrMatrix r) `shouldBe` 12
      CAUG.amrAdded r `shouldBe` 8
      CAUG.amrMethod r `shouldBe` "Replicate"
    it "AddCenter 3: 中心 3 行追加 (全 0)" $ do
      Right r <- CAUG.augmentMenu baseSpec (CAUG.AddCenter 3)
      LA.rows (CAUG.amrMatrix r) `shouldBe` 7
      let lastRows = drop 4 (LA.toLists (CAUG.amrMatrix r))
      all (all (== 0)) lastRows `shouldBe` True
    it "AddAxial α=1.5: 連続 2 因子 → 4 axial 点追加 (2 * 2 factors)" $ do
      Right r <- CAUG.augmentMenu baseSpec (CAUG.AddAxial 1.5 False)
      LA.rows (CAUG.amrMatrix r) `shouldBe` 8
      CAUG.amrAdded r `shouldBe` 4
      let axial = drop 4 (LA.toLists (CAUG.amrMatrix r))
      -- 各 axial 行は 1 因子だけ ±1.5、 残り 0
      all (\row -> length (filter (\v -> abs v > 1e-9) row) == 1) axial `shouldBe` True
    it "Phase 28-10 AddAxial rawUnits=True: Continuous (500, 560) で center 530 ± α·30" $ do
      let fT = CF.Factor "T" (CF.Continuous 500 560) CF.Controllable
          rawExisting' = LA.fromLists [[500], [560]]
          spec' = CX.CustomDesignSpec
            { CX.cdsFactors = [fT]
            , CX.cdsModel   = CM.Model [CM.TIntercept, CM.TMain "T"] CM.NCoded
            , CX.cdsConstraints = []
            , CX.cdsNRuns   = 2
            , CX.cdsCriterion = OPT.DOpt
            , CX.cdsBudget    = CX.defaultBudget
            , CX.cdsSeed      = Just 1
            , CX.cdsInitial   = Just rawExisting'
            , CX.cdsDJConvention = False
            }
      Right r <- CAUG.augmentMenu spec' (CAUG.AddAxial 1.4 True)
      let axial = drop 2 (LA.toLists (CAUG.amrMatrix r))
      -- center=530、 half-range=30、 axial = 530 ± 1.4·30 = 530 ± 42 = [488, 572]
      axial `shouldBe` [[572], [488]]
    it "AddRuns 2: 候補集合から 2 行追加" $ do
      Right r <- CAUG.augmentMenu baseSpec (CAUG.AddRuns 2)
      LA.rows (CAUG.amrMatrix r) `shouldBe` 6
      CAUG.amrAdded r `shouldBe` 2
    it "Phase 28-7 Foldover CategoricalSwap: cat 因子 A→B / B→A を swap した行追加" $ do
      let fc = CF.Factor "c" (CF.Categorical ["A","B","C"]) CF.Controllable
          fx = CF.Factor "x" (CF.Continuous (-1) 1) CF.Controllable
          existing = LA.fromLists [[0, -1], [1, 1], [2, 0]]  -- c=A/B/C、 x=-1/1/0
          spec' = CX.CustomDesignSpec
            { CX.cdsFactors = [fc, fx]
            , CX.cdsModel   = CM.Model [CM.TIntercept] CM.NCoded
            , CX.cdsConstraints = []
            , CX.cdsNRuns   = 3
            , CX.cdsCriterion = OPT.DOpt
            , CX.cdsBudget    = CX.defaultBudget
            , CX.cdsSeed      = Just 1
            , CX.cdsInitial   = Just existing
            , CX.cdsDJConvention = False
            }
      Right r <- CAUG.augmentMenu spec'
        (CAUG.Foldover (CAUG.CategoricalSwap [("c", [("A","B"),("B","A")])]))
      let swapped = drop 3 (LA.toLists (CAUG.amrMatrix r))
      -- 期待: A(0) → B(1)、 B(1) → A(0)、 C(2) はそのまま
      -- x 列はそのまま
      swapped `shouldBe` [[1, -1], [0, 1], [2, 0]]
    it "Foldover Full: 連続 2 因子 → 全部の符号 flip した 4 行追加" $ do
      Right r <- CAUG.augmentMenu baseSpec (CAUG.Foldover CAUG.FullFoldover)
      LA.rows (CAUG.amrMatrix r) `shouldBe` 8
      let flipped = drop 4 (LA.toLists (CAUG.amrMatrix r))
      flipped `shouldBe` [[1,1],[-1,1],[1,-1],[-1,-1]]
    it "Foldover Partial [x1]: x1 のみ flip" $ do
      Right r <- CAUG.augmentMenu baseSpec
        (CAUG.Foldover (CAUG.PartialFoldover ["x1"]))
      let flipped = drop 4 (LA.toLists (CAUG.amrMatrix r))
      flipped `shouldBe` [[1,-1],[-1,-1],[1,1],[-1,1]]
    it "Replicate 0 は Left" $ do
      r <- CAUG.augmentMenu baseSpec (CAUG.Replicate 0)
      case r of Left _ -> pure (); Right _ -> expectationFailure "expected Left"