packages feed

hanalyze-0.1.0.0: demo/doe-optim/OptimalDOEDemo.hs

{-# LANGUAGE OverloadedStrings #-}
-- | 最適計画 (D-optimal / A-optimal) のデモ (Phase P2)。
--
-- 候補集合 (3 水準グリッド) から指定試行数の部分集合を Fedorov 交換で
-- 最適化する。線形 vs 二次モデルの両方で確認。
module Main where

import Text.Printf (printf)

import qualified Hanalyze.Design.Optimal as DO
import qualified Hanalyze.Design.Quality as DQ

main :: IO ()
main = do
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  最適計画 (D-optimal / A-optimal) — Phase P2"
  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn ""

  -- ── 1. 線形モデル: k=3 因子、3 水準グリッド (27 候補) から 8 行選ぶ ──
  putStrLn "[1] 線形モデル (k=3 因子)"
  putStrLn "    候補: 3 水準グリッド = 27 候補。8 試行を選ぶ。"
  putStrLn ""
  let cands1 = DO.candidateGrid 3 3
      n1     = 8
  printf "  候補集合サイズ: %d\n" (length cands1)
  let (idxD, designD) = DO.dOptimal cands1 n1 42
      (idxA, designA) = DO.aOptimal cands1 n1 42
  printf "  D-optimal 選定: %s\n" (show idxD)
  printf "    D-eff       = %.4f\n" (DQ.dEfficiency designD)
  printf "    A-eff       = %.4f\n" (DQ.aEfficiency designD)
  printf "    条件数      = %.4f\n" (DQ.conditionNumber designD)
  putStrLn ""
  printf "  A-optimal 選定: %s\n" (show idxA)
  printf "    D-eff       = %.4f\n" (DQ.dEfficiency designA)
  printf "    A-eff       = %.4f\n" (DQ.aEfficiency designA)
  printf "    条件数      = %.4f\n" (DQ.conditionNumber designA)
  putStrLn ""
  putStrLn "  選ばれた D-optimal 設計:"
  mapM_ printRow designD
  putStrLn ""

  -- ── 2. 二次モデル: 候補は [1, x_i, x_i², x_i x_j] 拡張済 ──
  putStrLn "[2] 二次モデル (k=2 因子, 1 + 2 + 2 + 1 = 6 列)"
  putStrLn "    候補: 5 水準グリッド = 25 候補。10 試行を選ぶ。"
  putStrLn ""
  let cands2 = DO.quadraticCandidates 2 5
      n2     = 10
  printf "  候補集合サイズ: %d, 列数 (二次拡張後): %d\n"
         (length cands2) (length (head cands2))
  let (_, qDesign) = DO.dOptimal cands2 n2 7
  printf "  D-eff       = %.4f\n" (DQ.dEfficiency qDesign)
  printf "  条件数      = %.4f\n" (DQ.conditionNumber qDesign)
  putStrLn "  最初 5 行 (拡張済):"
  mapM_ printRow (take 5 qDesign)
  putStrLn ""

  -- ── 3. ランダム選択との比較 ──
  putStrLn "[3] 改善度比較 (D-optimal vs ランダム選択, k=3 線形, n=8)"
  let randDesigns = [ map (cands1 !!) (take n1 (DO.pseudoShuffle seed [0 .. length cands1 - 1]))
                    | seed <- [1..5] ]
      randDEffs = map DQ.dEfficiency randDesigns
      avgRand   = sum randDEffs / fromIntegral (length randDEffs)
  printf "  ランダム選択 5 種の平均 D-eff: %.4f\n" avgRand
  printf "  D-optimal:                     %.4f\n" (DQ.dEfficiency designD)
  printf "  改善率: %.1fx\n" (DQ.dEfficiency designD / avgRand)
  putStrLn ""

  putStrLn "═══════════════════════════════════════════════════════════════"
  putStrLn "  ✓ Fedorov 交換で D-/A-optimal 設計を構築"
  putStrLn "═══════════════════════════════════════════════════════════════"

  where
    printRow row = putStrLn ("    " ++ unwords (map (printf "%+6.3f") row))

-- (Optimal モジュールに pseudoShuffleI を export してないので、
--  ここでは pseudoShuffle を直接使う代わりに)