hanalyze-0.2.0.0: test/Hanalyze/Design/SequentialSpec.hs
{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Design.SequentialSpec (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.Design.Sequential as Seq
import qualified Hanalyze.Design.RSM as RSMd
import SpecHelper
spec :: Spec
spec = do
describe "Hanalyze.Design.Sequential (Phase 7.2)" $ do
it "steepestAscent: ascent 方向 = +b / |b|" $ do
let sa = Seq.steepestAscent True [0, 0] [3, 4] 1.0 5
d = LA.toList (Seq.sarDirection sa)
-- (3, 4) の単位ベクトル = (0.6, 0.8)
abs (d !! 0 - 0.6) `shouldSatisfy` (< 1e-12)
abs (d !! 1 - 0.8) `shouldSatisfy` (< 1e-12)
length (Seq.sarStepPoints sa) `shouldBe` 6 -- nSteps + 1
Seq.sarStepPoints sa !! 0 `shouldBe` [0, 0]
-- step 1 = center + 1 * (0.6, 0.8) = (0.6, 0.8)
abs (Seq.sarStepPoints sa !! 1 !! 0 - 0.6) `shouldSatisfy` (< 1e-12)
it "steepestAscent: descent 方向 = -b / |b|" $ do
let sa = Seq.steepestAscent False [10, 10] [1, 0] 0.5 3
Seq.sarMaximize sa `shouldBe` False
let d = LA.toList (Seq.sarDirection sa)
-- (1, 0) の単位ベクトル = (1, 0)、 descent なので -1, 0
abs (d !! 0 - (-1.0)) `shouldSatisfy` (< 1e-12)
abs (d !! 1) `shouldSatisfy` (< 1e-12)
-- step 2 = (10, 10) + 2 * 0.5 * (-1, 0) = (9, 10)
abs (Seq.sarStepPoints sa !! 2 !! 0 - 9.0) `shouldSatisfy` (< 1e-12)
abs (Seq.sarStepPoints sa !! 2 !! 1 - 10.0) `shouldSatisfy` (< 1e-12)
it "steepestAscent: |b| = 0 なら方向 0、 全点 center" $ do
let sa = Seq.steepestAscent True [5, 5, 5] [0, 0, 0] 1.0 3
d = LA.toList (Seq.sarDirection sa)
all (== 0) d `shouldBe` True
all (== [5,5,5]) (Seq.sarStepPoints sa) `shouldBe` True
it "steepestAscentFromQuad: QuadFit から第一階係数を抽出" $ do
-- 2 因子の簡単な response surface y = 1 + 2*x1 + 3*x2 + ...
let xs = [[-1,-1], [1,-1], [-1,1], [1,1], [0,0]]
ys = [ 1 + 2*x1 + 3*x2 | [x1, x2] <- xs ]
qf = RSMd.fitQuadratic xs ys
sa = Seq.steepestAscentFromQuad True [0, 0] qf 0.1 4
-- 第一階係数 (2, 3) → 単位ベクトル (2, 3)/sqrt(13)
let expectedX = 2 / sqrt 13
expectedY = 3 / sqrt 13
d = LA.toList (Seq.sarDirection sa)
abs (d !! 0 - expectedX) `shouldSatisfy` (< 0.05) -- numerical fit tolerance
abs (d !! 1 - expectedY) `shouldSatisfy` (< 0.05)
it "sequentialCCD: 新中心 + span で coded / real 両方返す" $ do
let res = Seq.sequentialCCD [10, 20] 2.0 2 RSMd.CCF 1
Seq.sccdCenter res `shouldBe` [10, 20]
Seq.sccdSpan res `shouldBe` 2.0
-- coded は -1, 0, +1 の組合せ (FaceCentered なので axial も ±1)
length (Seq.sccdCoded res) `shouldBe` length (Seq.sccdReal res)
-- real の各行 = center + span * coded
let checkRow (coded, real) =
real == zipWith (\c x -> c + 2.0 * x) [10, 20] coded
all checkRow (zip (Seq.sccdCoded res) (Seq.sccdReal res)) `shouldBe` True
it "sequentialCCD: factorial part は ±span 周辺" $ do
let res = Seq.sequentialCCD [50, 50] 5.0 2 RSMd.CCF 1
realRows = Seq.sccdReal res
-- 角点は (45,45), (55,45), (45,55), (55,55) を含むはず
[45, 45] `elem` realRows `shouldBe` True
[55, 55] `elem` realRows `shouldBe` True