packages feed

hanalyze-0.2.0.0: test/Hanalyze/Optim/ConstrainedSpec.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Optim.ConstrainedSpec (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 Hanalyze.Optim.Constrained as Con
import qualified Hanalyze.Optim.Common      as OC
import SpecHelper

spec :: Spec
spec = do
  describe "Hanalyze.Optim.Constrained" $ do
    it "Augmented Lagrangian: min x1²+x2² s.t. x1+x2=1 → (0.5, 0.5)" $ do
      let f xs = (head xs)^(2::Int) + (xs !! 1)^(2::Int)
          cs = Con.ConstraintSet
                 { Con.csEq   = [\xs -> head xs + xs !! 1 - 1]
                 , Con.csIneq = []
                 }
      (r, viol) <- Con.runAugmentedLagrangian
                     Con.defaultConstrainedConfig f cs [0, 0]
      let [x1, x2] = OC.orBest r
      abs (x1 - 0.5) `shouldSatisfy` (< 0.05)
      abs (x2 - 0.5) `shouldSatisfy` (< 0.05)
      viol `shouldSatisfy` (< 1e-3)

    it "Augmented Lagrangian: 不等式 x ≥ 1 (h(x)=1-x≤0) で min x²" $ do
      let f xs  = (head xs)^(2::Int)
          cs   = Con.ConstraintSet
                 { Con.csEq   = []
                 , Con.csIneq = [\xs -> 1 - head xs]    -- 1 - x ≤ 0 ⇔ x ≥ 1
                 }
      (r, viol) <- Con.runAugmentedLagrangian
                     Con.defaultConstrainedConfig f cs [0]
      let [x] = OC.orBest r
      x `shouldSatisfy` (\v -> v >= 1 - 0.01)
      viol `shouldSatisfy` (< 1e-3)

    it "Penalty method: 等式 x1+x2=1 (簡易版)" $ do
      let f xs = (head xs)^(2::Int) + (xs !! 1)^(2::Int)
          cs = Con.ConstraintSet
                 { Con.csEq   = [\xs -> head xs + xs !! 1 - 1]
                 , Con.csIneq = []
                 }
      (r, _) <- Con.penaltyMethod Con.defaultConstrainedConfig f cs [0, 0]
      let [x1, x2] = OC.orBest r
      abs (x1 + x2 - 1) `shouldSatisfy` (< 0.05)

    it "boxToIneq: bounds [(1,5)] を不等式 2 本に展開、x=3 で両方満たす" $ do
      let ineqs = Con.boxToIneq [(1.0, 5.0)]
      length ineqs `shouldBe` 2
      all (\h -> h [3.0] <= 0) ineqs `shouldBe` True
      any (\h -> h [0.0] > 0) ineqs `shouldBe` True   -- 下限違反
      any (\h -> h [6.0] > 0) ineqs `shouldBe` True   -- 上限違反

  -- ===========================================================================
  -- box 制約 (Bounds) 統一インターフェース (Hanalyze.Optim.Common)
  -- ===========================================================================