packages feed

hanalyze-0.2.0.0: test/Hanalyze/Stat/QuasiRandomSpec.hs

{-# OPTIONS_GHC -Wno-unused-imports #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Hanalyze.Stat.QuasiRandomSpec (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.Stat.QuasiRandom  as QR
import qualified System.Random.MWC as MWC
import qualified System.Random.MWC as MWC
import SpecHelper

spec :: Spec
spec = do
  describe "Hanalyze.Stat.QuasiRandom (Halton)" $ do
    it "haltonSequence 10 1 lies in [0, 1) and is a permutation" $ do
      let pts = QR.haltonSequence 10 1
      length pts `shouldBe` 10
      all (\[u] -> u >= 0 && u < 1) pts `shouldBe` True

    it "haltonSequence 16 2 covers a 4x4 grid better than random" $ do
      -- For n = 16, d = 2 the Halton points should cover all 16
      -- 0.25-bins; an iid uniform usually misses some.
      let pts = QR.haltonSequence 16 2
          binIdx p = (floor (4 * head p) :: Int,
                      floor (4 * (p !! 1)) :: Int)
          unique = length (foldr (\b acc -> if b `elem` acc then acc
                                              else b : acc) [] (map binIdx pts))
      unique `shouldSatisfy` (>= 14)   -- Halton normally hits ≥ 14 / 16

    it "haltonSequenceIn rescales into the supplied bounds" $ do
      let bs  = [(-2, 2), (10, 20)] :: [(Double, Double)]
          pts = QR.haltonSequenceIn 5 bs
      all (\[a, b] -> a >= -2 && a <  2
                   && b >= 10 && b <  20) pts `shouldBe` True

    it "lhsSamples 10 3 lies in [0,1)^3 and fills every per-dim cell" $ do
      gen <- MWC.createSystemRandom
      pts <- QR.lhsSamples 10 3 gen
      length pts `shouldBe` 10
      all (\xs -> all (\u -> u >= 0 && u < 1) xs) pts `shouldBe` True
      -- For each dim, the 10 points should occupy 10 distinct cells
      -- (= floor(10 * u) is a permutation of [0..9]).
      let cellsAlong k = map (\xs -> floor (10 * (xs !! k)) :: Int) pts
          ok k = sort (cellsAlong k) == [0 .. 9]
      ok 0 `shouldBe` True
      ok 1 `shouldBe` True
      ok 2 `shouldBe` True

    it "lhsSamplesIn rescales into bounds" $ do
      gen <- MWC.createSystemRandom
      let bs = [(-1, 1), (5, 10)] :: [(Double, Double)]
      pts <- QR.lhsSamplesIn 8 bs gen
      all (\[a, b] -> a >= -1 && a <  1
                   && b >=  5 && b < 10) pts `shouldBe` True