srtree-3.0.0.0: apps/Bench/Main.hs
{-# LANGUAGE BangPatterns #-}
import Criterion.Main
import Control.DeepSeq (force, NFData)
import Control.Exception (evaluate)
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector as VB
import qualified Data.Vector.Generic as G
import qualified Data.Vector.Storable as VS
import Data.SRTree
import Data.SRTree.Print
import Data.SRTree.Datasets
import Data.SRTree.Eval
import Data.SRTree.Random
import System.Random
import Control.Monad.State.Strict
import Algorithm.SRTree.NonlinearOpt
import Algorithm.SRTree.Likelihoods
import Algorithm.SRTree.AD
-- Assuming these are exported by your project modules:
-- import SRTree
-- import Compiler
-- import DatasetLoader
-- Mock signatures based on your provided functions
-- randomTree :: Int -> Int -> Int -> IO Term -> IO NonTerm -> Bool -> IO Tree
-- loadDataset :: FilePath -> Bool -> IO [V.Vector Double]
-- evalTree :: Tree -> [V.Vector Double] -> V.Vector Double
-- compile :: [V.Vector Double] -> Tree -> (Theta -> V.Vector Double)
genTerm = do coin <- tossBiased 0.4
if coin then randomFrom [Fix $ Var ix | ix <- [0..8]] else randomFrom [Fix $ Param ix | ix <- [0..9]]
genNonTerm = randomFrom [Bin Add () (), Bin Sub () (), Bin Mul () (), Uni LogAbs (), Uni SqrtAbs ()]
genMultipleTrees 0 = pure []
genMultipleTrees n = do
t <- randomTree 5 10 150 genTerm genNonTerm False
ts <- genMultipleTrees (n-1)
pure (t:ts)
getF (_, x, _) = x
{-# INLINE getF #-}
getT (t, _, _) = t
{-# INLINE getT #-}
main :: IO ()
main = do
-- 1. Initialization: Load the dataset
putStrLn "Loading dataset..."
((dataset, y, _, _), _, _, _) <- loadDataset "data.tsv" True
-- 2. Initialization: Generate the random expression tree
putStrLn "Generating random tree..."
-- Replace 'genTerm' and 'genNonTerm' with your actual generators
--g <- getStdGen
let g = mkStdGen 42
-- tree <- evalStateT (randomTree 7 10 150 genTerm genNonTerm True) g
trees' <- evalStateT (genMultipleTrees 5) g
-- let trees' = [Fix (Uni LogAbs (Fix (Bin PowerAbs (param 0) (param 1 * var 0))))] :: [Fix SRTree]
-- IMPORTANT: Force deep evaluation of the tree and dataset.
-- If we do not do this, GHC's lazy evaluation will cause the benchmark
-- to measure the time it takes to parse the CSV and build the tree in memory!
-- _ <- evaluate (force tree)
_ <- evaluate (force dataset)
-- 3. Initialization: Pre-compile the tree
-- We evaluate this strictly (!) so the one-time compilation cost
-- is not included in the runtime benchmark.
putStrLn "Compiling tree..."
let !compiledFn = [compile dataset tree | tree <- trees]
evalTree x th t = compile x t th
-- Mock theta (parameter vector) to pass into the closures
!theta = V.fromList [1.0, 0.5, 0.2, 0.3, 0.1, 0.5, 0.9, 0.3, 0.2, 0.4]
!theta1 = V.fromList [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]
trees = map relabelParamsOrder $ filter (\t -> let v = V.sum (evalTree dataset theta t) in not (isInfinite v || isNaN v)) trees'
naiveEval = evalTree dataset theta
dataset' = map G.convert dataset
y' = G.convert y
theta1' = G.convert theta1
_ <- evaluate (force theta)
_ <- evaluate (force theta1)
print $ sum $ map (\t -> V.sum $ naiveEval t) trees
print $ sum $ map (\t -> V.sum $ t theta) compiledFn
print $ sum $ map (\t -> getF $ minimizeNLL MultiThread MSE Nothing 0 dataset y t theta1) trees
--print $ sum $ map (\t -> getF $ minimizeNLLCompiled MSE Nothing 0 dataset y t theta1) trees
--print $ sum $ map (\t -> VS.sum . snd $ gradNLLGraph MSE dataset' y' Nothing t theta1') trees
--print $ sum $ map (\t -> VS.sum . snd $ gradNLLGraphO MSE dataset' y' Nothing t theta1') trees
--print $ sum $ map (\t -> VS.sum . snd $ compileGrad dataset' y' Nothing t 100 theta1') trees
--print $ sum $ map (\ct -> V.sum $ ct theta) compiledFn
--print $ sum $ map (\ct -> V.sum $ executeVM ct rowDataset theta) bytecodes
-- print $ V.sum $ evalTree dataset theta tree
-- print $ V.sum $ compiledFn theta
putStrLn "Running benchmarks..."
-- 4. The Benchmarks
defaultMain [
bgroup "Tree Evaluation (Fixed Dataset)" [
-- The slow version: dynamically traversing the AST at runtime
bench "evalTree (Naive AST Traversal)" $
nf (\ts -> sum [V.sum $ evalTree dataset theta1 t | t <- ts]) trees,
-- The fast version: executing the pre-compiled, stream-fused closure
bench "compile (Compiled Closure)" $
nf (\t -> sum [V.sum (ct t) | ct <- compiledFn]) theta1,
-- The fast version: executing the pre-compiled, stream-fused closure
bench "minimizeNLLCompiled (Compiled Closure)" $
nf (\ts -> sum [V.sum . getT $ minimizeNLL MultiThread MSE Nothing 100 dataset' y' t theta1' | t <- ts]) trees
--bench "minimizeNLLO (Naive optimized AST Traversal)" $
-- nf (\ts -> sum [V.sum . getT $ minimizeNLLO MSE Nothing 100 dataset y t theta1 | t <- ts]) trees
-- The slow version: dynamically traversing the AST at runtime
--bench "minimizeNLL (Naive AST Traversal)" $
-- nf (\ts -> sum [V.sum . getT $ minimizeNLL (NLL MSE) Nothing 100 dataset y t theta1 | t <- ts]) trees
]
]