packages feed

srtree-2.0.0.0: apps/eqsatrepr/Main.hs

{-# LANGUAGE OverloadedStrings #-}
module Main where

import Data.SRTree 
import Algorithm.EqSat.Egraph
import Data.SRTree.Print 
import qualified Data.Map as Map
import qualified Data.IntMap as IM
import Control.Monad.State.Strict
import System.Random
import Data.SRTree.Recursion ( cata )
import Control.Monad
import Control.Monad.Reader
import qualified Data.SRTree.Random as RT
import Data.List ( nub )
import Algorithm.EqSat.DB
import Algorithm.EqSat.Info
import Algorithm.EqSat.Build
import Algorithm.EqSat.Queries
import Algorithm.EqSat

isConstPt :: Pattern -> Map.Map ClassOrVar ClassOrVar -> EGraph -> Bool
isConstPt (VarPat c) subst eg =
    let cid = getInt $ subst Map.! (Right $ fromEnum c)
    in case (_consts . _info) (_eClass eg IM.! cid) of
         ConstVal x -> True
         _ -> False
isConstPt _ _ _ = False

notZero (VarPat c) subst eg =
  let cid = getInt $ subst Map.! (Right $ fromEnum c)
   in case (_consts . _info) (_eClass eg IM.! cid) of
         ConstVal x -> x /= 0
         _ -> True
notZero _ _ _ = True

rewriteBasic =
    [
      "x" * "x" :=> "x" ** 2
    , "x" * "y" :=> "y" * "x"
    , "x" + "y" :=> "y" + "x"
    , ("x" ** "y") * "x" :=> "x" ** ("y" + 1) :| isConstPt "y"
    -- , ("x" * "y") / "x" :=> "y"
    , ("x" ** "y") * ("x" ** "z") :=> "x" ** ("y" + "z")
    , ("x" + "y") + "z" :=> "x" + ("y" + "z")
    , ("x" + "y") - "z" :=> "x" + ("y" - "z")
    , ("x" * "y") * "z" :=> "x" * ("y" * "z")
    , ("x" * "y") + ("x" * "z") :=> "x" * ("y" + "z")
    , "x" - ("y" + "z") :=> ("x" - "y") - "z"
    , "x" - ("y" - "z") :=> ("x" - "y") + "z"
    , ("x" * "y") / "z" :=> ("x" / "z") * "y"
    , (("w" * "x") / ("z" * "y") :=> ("w" / "z") * ("x" / "y") :| isConstPt "w") :| isConstPt "z"
    , ((("x" * "y") + ("z" * "w")) :=> "x" * ("y" + ("z" / "x") * "w") :| isConstPt "x") :| isConstPt "z"
    , ((("x" * "y") - ("z" * "w")) :=> "x" * ("y" - ("z" / "x") * "w") :| isConstPt "x") :| isConstPt "z"
    , ((("x" * "y") * ("z" * "w")) :=> ("x" * "z") * ("y" * "w") :| isConstPt "x") :| isConstPt "z"
    ]

rewritesFun =
    [
      log (sqrt "x") :=> 0.5 * log "x"
    , log (exp "x")  :=> "x"
    , exp (log "x")  :=> "x"
    , "x" ** (1/2)   :=> sqrt "x"
    ,  log ("x" * "y") :=> log "x" + log "y"
    , log ("x" / "y") :=> log "x" - log "y"
    , log ("x" ** "y") :=> "y" * log "x"
    , sqrt ("y" * "x") :=> sqrt "y" * sqrt "x"
    , sqrt ("y" / "x") :=> sqrt "y" / sqrt "x"
    , abs ("x" * "y") :=> abs "x" * abs "y"
    ,  sqrt ("z" * ("x" - "y")) :=> sqrt (negate "z") * sqrt ("y" - "x")
    , sqrt ("z" * ("x" + "y")) :=> sqrt "z" * sqrt ("x" + "y")
    ]

-- Rules that reduces redundant parameters
constReduction =
    [
      0 + "x" :=> "x"
    , "x" - 0 :=> "x"
    , 1 * "x" :=> "x"
    , 0 * "x" :=> 0
    , 0 / "x" :=> 0 :| notZero "x"
    , "x" - "x" :=> 0
    , "x" / "x" :=> 1 :| notZero "x"
    , "x" ** 1 :=> "x"
    , 0 ** "x" :=> 0
    , 1 ** "x" :=> 1
    , "x" * (1 / "x") :=> 1
    , 0 - "x" :=> negate "x"
    , "x" + negate "y" :=> "x" - "y"
    , negate ("x" * "y") :=> (negate "x") * "y" :| isConstPt "x"
    ]


x0 = var 0
x1 = var 1
x2 = var 2
x3 = var 3
x4 = var 4
x5 = var 5
x6 = var 6
x7 = var 7
x8 = var 8

trees :: [Fix SRTree]
trees = [  (4.059e-3 + (0.988153 * (((1.923901 * x1) * ((-1.228652 * x0) * (-0.278891 * x2))) * ((((((-0.35119 * x5) + (-0.354523 * x3)) - (-0.369148 * x6)) + ((0.342012 * x4) + (2.054e-2 * x2))) - ((0.349297 * x7) - (0.336081 * x8)))))))
        , ((14.316036 * (((((0.975231 * x4)) * (1.259663 * x3)) * (0.314221 * x0)) * (0.178249 * x2))))
        , (1.2 * (3.4 * x1 * 4.2 * x0) / ((3.2 * x2) * ((1.1 * x3) * (3.5 * x4))))
        , ((1.002563 * (((0.428416 * x1) * (2.554566 * x0)) / (((2.53743 * x2) * (2.327917 * x3)) * (2.320736 * x3)))))
        , (2.82238 + (3.092415 * (sin(log(abs(0.0))) * ((-0.162842 * x2) - (0.116404 * x1)))))
        , log(0.0) * ((1.2 * x2) - (0.116404 * x1))
        , ((x0 - x0) * x0)
        , (1 + 1) - 1
        , (x0 + x0) - x0
        , (x0/x0 + 1) - 1
        , (x0 * x0) / x0
        , sin(log(0.0))
        , -1 * exp(log(abs(-1.3 * (x1 - 1.2 * x2))))
        , -1 * exp(log(abs((1.3 * x1 + 1.56 * x2))))
        , -1 * exp(log(abs((-1.3 * x1 + 1.56 * x2))))
        , -1 * exp(log(abs(((0.256 * x3) + (-0.2561 * x2)))))
        , log(abs(-1.199026) * abs((x2 + (1.191617 * x3))))
        , log(abs((1.199026 * x2) + (-1.191617 * x3)))
        , 1 * x0
        , x0 * 1
        , x0 + x1
        , x1 + x0
        , x0 + sin(x1)
        , x0 * (1 + x1)
        , (1 + x1) * x0
        , x0 + x0 * x1
        , (x0 + x1) + 2
        , x0 + (x1 + 2)
        , x0 + (2 + x1)
        , log(abs(0)) + x0
        , abs(((1.3 * x1) + (-1.56 * x2))) * (-1.0)
        ]


myCost :: SRTree Int -> Int
myCost (Var _) = 1
myCost (Const _) = 1
myCost (Param _) = 1
myCost (Bin op l r) = 2 + l + r
myCost (Uni _ t) = 3 + t

rewrites = rewriteBasic <> constReduction <> rewritesFun

testEqSat :: Fix SRTree -> IO ()
testEqSat t = do
    let e = eqSat t rewrites myCost 30 `evalState` emptyGraph
    putStr $ (showExpr t) <> " == " <> (showExpr e) <> "\n"

testEqSats :: IO ()
testEqSats = mapM_ testEqSat trees



initialPop :: HyperParams -> Rng [Fix SRTree]
initialPop hyperparams = do
   let depths = [3 .. _maxDepth hyperparams]
   pop <- forM depths $ \md ->
           do let m = _popSize hyperparams `div` (_maxDepth hyperparams - 3 + 1)
                  g = take m $ cycle [True, False]
              mapM (randomTree hyperparams{ _maxDepth = md}) g
   pure (concat pop)
{-# INLINE initialPop #-}

data Method = Grow | Full

type Rng a = StateT StdGen IO a
type GenUni = Fix SRTree -> Fix SRTree
type GenBin = Fix SRTree -> Fix SRTree -> Fix SRTree

toss :: Rng Bool
toss = state random
{-# INLINE toss #-}

randomRange :: (Ord val, Random val) => (val, val) -> Rng val
randomRange rng = state (randomR rng)
{-# INLINE randomRange #-}

randomFrom :: [a] -> Rng a
randomFrom funs = do n <- randomRange (0, length funs - 1)
                     pure $ funs !! n
{-# INLINE randomFrom #-}

countNodes' :: Fix SRTree -> Int
countNodes' = cata alg
  where
    alg (Var _)     = 1
    alg (Param _)   = 1
    alg (Const _)   = 0
    alg (Bin _ l r) = 1 + l + r
    alg (Uni Abs t) = t
    alg (Uni _ t)   = 1 + t
{-# INLINE countNodes' #-}


randomTree :: HyperParams -> Bool -> Rng (Fix SRTree)
randomTree hp grow
  | depth <= 1 || size <= 2 = randomFrom term
  | (min_depth >= 0 || (depth > 2 && not grow)) && size > 2 = genNonTerm
  | otherwise = genTermOrNon
  where
    min_depth = _minDepth hp
    depth     = _maxDepth hp
    size      = _maxSize hp
    term      = _term hp
    nonterm   = _nonterm hp

    genNonTerm =
       do et <- randomFrom nonterm
          case et of
            Left uniT -> uniT <$> randomTree hp{_minDepth = min_depth-1, _maxDepth = depth - 1, _maxSize = size - 1} grow
            Right binT -> do l <- randomTree hp{_minDepth = min_depth-1, _maxDepth = depth - 1, _maxSize = size - 1} grow
                             r <- randomTree hp{_minDepth = min_depth-1, _maxDepth = depth - 1, _maxSize = size - 1 - countNodes' l} grow
                             pure (binT l r)
    genTermOrNon = do r <- toss
                      if r
                        then randomFrom term
                        else genNonTerm
{-# INLINE randomTree #-}

data HyperParams =
    HP { _minDepth  :: Int
       , _maxDepth  :: Int
       , _maxSize   :: Int
       , _popSize   :: Int
       , _tournSize :: Int
       , _pc        :: Double
       , _pm        :: Double
       , _term      :: [Fix SRTree]
       , _nonterm   :: [Either GenUni GenBin]
       }


countSubTrees = do ecs <- gets (IM.keys . _eClass) 
                   subs <- mapM (\ec -> getAllExpressionsFrom ec >>= pure . length) ecs 
                   pure $ sum subs 
countRootTrees rs = do subs <- mapM (\ec -> getAllExpressionsFrom ec >>= pure . length) rs
                       pure $ sum subs

terms = [var 0, var 1, var 2, param 0, param 1, param 2, param 3]
nonterms = [Right (+), Right (-), Right (*), Right (/), Right (\l r -> abs l ** r), Left (1/)]

calcRedundancy :: Int -> IO ()
calcRedundancy nPop = do
    let hp = HP 2 4 10 nPop 2 1.0 0.25 terms nonterms
        p  = RT.P [0, 1, 2, 3, 4, 5] (0, 3) (1, 3) [Log]
    g <- getStdGen
    pop <- (`evalStateT` g)  <$> replicateM nPop $ runReaderT (RT.randomTree 10) p
    let nSubsSingle = sum $ map (\p -> (fromTrees myCost [p] >> countSubTrees) `evalState` emptyGraph) pop 
        myEqPop = do rs <- fromTrees myCost pop
                     let rsN = nub rs 
                     cnt <- countSubTrees
                     pure (cnt, rsN)
        (nSubs, rsN) = myEqPop `evalState` emptyGraph 
    putStr "Ratio of subtrees: "
    putStrLn $ show nSubsSingle <> "/" <> show nSubs <> " = " <> show (fromIntegral nSubsSingle / fromIntegral nSubs)
    let nSubsR = sum $ map (\p -> (fromTree myCost p >>= \r -> countRootTrees [r]) `evalState` emptyGraph) pop
        nSubsSingleR = (fromTrees myCost pop >>= countRootTrees) `evalState` emptyGraph
    putStr "Ratio of rooted trees: "
    putStrLn $ show nSubsSingleR <> "/" <> show nSubsR <> " = " <> show (fromIntegral nSubsSingleR / fromIntegral nSubsR)

main :: IO ()
main = do 
    let t1 = var 0 + 12.0
        t2 = 3.2 * var 0
        t3 = 3.2 * var 0 / (var 0 + 12.0)
        t4 = var 0 + sin (var 0)
        t5 = 1.5 + exp 5.2
        egraphRun :: EGraphST IO ()
        egraphRun = do v <- fromTrees myCost [t3,t1,t2,t4]
                       roots <- findRootClasses
                       ecId  <- gets ((Map.! (Var 0)) . _eNodeToEClass)
                       calculateHeights 
                       h <- gets (map _height . IM.elems . _eClass)
                       v <- gets (map (_consts . _info) . IM.elems . _eClass)
                       c <- gets (map (_cost . _info) . IM.elems . _eClass)
                       parents <- gets (_parents . (IM.! ecId) . _eClass)
                       exprs <- mapM getExpressionFrom roots
                       exprs' <- gets (IM.keys . _eClass) >>= mapM getExpressionFrom 

                       lift $ do putStr "Parents of x0: "
                                 print parents 
                                 putStrLn "\nexpressions from root: "
                                 mapM_ (putStrLn . showExpr) exprs
                                 putStrLn "\nexpressions from each e-class: "
                                 mapM_ (putStrLn . showExpr) exprs'
                                 putStrLn "heights: "
                                 mapM_ print h -- (print . _height) (IM.elems $ _eClass eg')
                                 putStrLn "values: "
                                 mapM_ print v -- (print . _consts . _info) (IM.elems $ _eClass eg')
                                 putStrLn "costs: "
                                 mapM_ print c -- (print . _cost . _info) (IM.elems $ _eClass eg')
        nPop = 10000
        hp = HP 3 7 100 nPop 2 1.0 0.25 terms nonterms
        p  = RT.P [0] (-3, 3) (-3, 3) []
    egraphRun `evalStateT` emptyGraph
    g <- getStdGen
    pop <- evalStateT (initialPop hp) g
    mapM_ (\nP -> putStr "pop " >> print nP >> calcRedundancy nP >> putStrLn "") [100, 200, 500, 1000, 5000, 10000, 20000, 100000]