packages feed

hanalyze-0.2.0.0: test/Hanalyze/Design/ConstraintSpec.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Design.ConstraintSpec (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.Optimal       as OPT
import qualified Hanalyze.Design.Constraint    as DCons
import SpecHelper

spec :: Spec
spec = do
  describe "Hanalyze.Design.Constraint (Phase 23-b)" $ do
    let cands =
          [ [1, x1, x2] | x1 <- [-1, 0, 1], x2 <- [-1, 0, 1] ]
    it "checkRow: LinearConstraint x1 + x2 <= 1 を判定" $ do
      let c = DCons.LinearConstraint [0, 1, 1] DCons.CLeq 1
      DCons.checkRow [c] [1, -1, -1] `shouldBe` True
      DCons.checkRow [c] [1,  1,  1] `shouldBe` False
      DCons.checkRow [c] [1,  0,  1] `shouldBe` True
    it "checkRow: ForbiddenCombination で完全一致を弾く" $ do
      let c = DCons.ForbiddenCombination [1, 1, 1]
      DCons.checkRow [c] [1, 1, 1] `shouldBe` False
      DCons.checkRow [c] [1, 0, 1] `shouldBe` True
    it "checkRow: 浮動小数 tolerance (1e-9) で ForbiddenCombination 判定" $ do
      let c = DCons.ForbiddenCombination [1, 1]
      DCons.checkRow [c] [1, 1 + 1e-11] `shouldBe` False  -- tol 内 = 一致 = 違反
      DCons.checkRow [c] [1, 1 + 1e-6 ] `shouldBe` True   -- tol 外 = OK
    it "checkDesign: 違反 row index を列挙" $ do
      let c    = DCons.LinearConstraint [0, 1, 1] DCons.CLeq 1
          mat  = LA.fromLists [[1,-1,-1],[1,1,1],[1,0,1],[1,1,0.5]]
      DCons.checkDesign [c] mat `shouldBe` [1, 3]
    it "filterCandidates: x1 + x2 <= 1 で 9 候補 → 8 候補 (排除: (1,1))" $ do
      let c   = DCons.LinearConstraint [0, 1, 1] DCons.CLeq 1
          fc  = DCons.filterCandidates [c] cands
      length fc `shouldBe` 8
    it "filterCandidates 後の候補で dOptimal が動作 (5 行抽出)" $ do
      let c   = DCons.LinearConstraint [0, 1, 1] DCons.CLeq 1
          fc  = DCons.filterCandidates [c] cands
          (idxs, des) = OPT.dOptimal fc 5 42
      length idxs `shouldBe` 5
      length des `shouldBe` 5
    it "より厳しい制約 x1 + x2 <= 0 で 9 候補 → 6 候補に縮約" $ do
      -- 排除: (0,1)=1、 (1,0)=1、 (1,1)=2 の 3 件
      let c   = DCons.LinearConstraint [0, 1, 1] DCons.CLeq 0
          fc  = DCons.filterCandidates [c] cands
      length fc `shouldBe` 6
    it "CEq / CGeq 判定" $ do
      let cEq  = DCons.LinearConstraint [0, 1, 1] DCons.CEq  0
          cGeq = DCons.LinearConstraint [0, 1, 1] DCons.CGeq 1
      DCons.checkRow [cEq]  [1, -1, 1] `shouldBe` True
      DCons.checkRow [cEq]  [1,  1, 1] `shouldBe` False
      DCons.checkRow [cGeq] [1,  1, 1] `shouldBe` True
      DCons.checkRow [cGeq] [1, -1, 0] `shouldBe` False