packages feed

srtree-3.0.0.0: src/Algorithm/SRTree/Compile.hs

{-# LANGUAGE GADTs #-}

module Algorithm.SRTree.Compile where

import Data.SRTree
import Data.SRTree.Eval (compileLoss, Target, Columns, Theta)
import qualified Data.Vector.Unboxed as U
import qualified Data.Vector.Storable as VS
import qualified Data.Vector.Generic as G
import Algorithm.SRTree.AD
import Algorithm.SRTree.Utils
import Algorithm.SRTree.Likelihoods (Distribution(..), Loss(..), buildLoss, hessianNLL)
import Algorithm.SRTree.NonlinearOpt (minimizeNLL, minimizeNLLWithFixedParam)
import Data.SRTree.Recursion (cata)

data EvalTree = EvalTree {
  ctDist            :: Distribution,
  ctLoss            :: Theta -> Double,
  ctAD              :: VS.Vector Double -> (Double, VS.Vector Double),
  ctOptimizer       :: Target -> Target,
  ctOptimizerFixed  :: Int -> Target -> Target,
  ctNLL             :: Target -> Double,
  ctGradNLL         :: Target -> (Double, Target),
  ctHessianNLL      :: Target -> Columns,
  ctTree            :: Fix SRTree,
  ctRows            :: Int,
  ctVar             :: Double
}

-- | Compile a tree and store it in a CompiledTree data structure
compileTree :: Distribution -> Columns -> Target -> Maybe Target -> Fix SRTree -> EvalTree
compileTree dist xss ys mYerr tree = EvalTree {
  ctDist            = dist,
  ctLoss            = compileLoss xss tree ys mYerr,
  ctAD              = compileFunAndGrad MultiThread xss ys mYerr tree,
  ctOptimizer       = fst3 . minimizeNLL MultiThread (NLL dist) mYerr 100 xss ys tree,
  ctOptimizerFixed  = minimizeNLLWithFixedParam MultiThread (NLL dist) mYerr 100 xss ys tree,
  ctNLL             = compileLoss xss lossTree ys mYerr,
  ctGradNLL         = \theta -> let fg = compileFunAndGrad MultiThread xss ys mYerr lossTree
                                    (obj, gradStorable) = fg (G.convert theta)
                                in (obj, G.convert gradStorable),
  ctHessianNLL      = hessianNLL dist mYerr xss ys tree,
  ctTree            = tree,
  ctRows            = n,
  ctVar             = let ym = U.sum ys / fromIntegral n
                      in U.foldr (\yi acc -> acc + (yi - ym)^2) 0 ys
}
  where
    n = U.length ys
    lossTree = buildLoss (NLL dist) (fromIntegral n) tree
    fst3 (a, _, _) = a

data EvaluatedTree = EvaluatedTree {
  valLoss             :: Double,
  valTheta            :: Theta,
  valRows             :: Double,
  valParams           :: Double,
  valTree             :: Fix SRTree,
  valLogParams        :: Double,
  valLogParamsLattice :: Double,
  valVar              :: Double
}

evaluateTree :: EvalTree -> Target -> [[Double]] -> Theta -> EvaluatedTree
evaluateTree et fisher hessian theta = EvaluatedTree {
  valLoss             = ctLoss et theta,
  valTheta            = theta,
  valRows             = fromIntegral (ctRows et),
  valParams           = fromIntegral (U.length theta),
  valTree             = ctTree et,
  valLogParams        = logParameters fisher theta,
  valLogParamsLattice = logParametersLatt hessian fisher theta,
  valVar              = ctVar et
}


-- log of the parameters complexity
logParameters :: U.Vector Double -> Target -> Double
logParameters fisher theta = -(p / 2) * log 3 + 0.5 * logFisher + logTheta
  where
    (logTheta, logFisher, p) = foldr addIfSignificant (0, 0, 0) $ zip (U.toList theta) (U.toList fisher)

-- same as above but for the Lattice
logParametersLatt :: [[Double]] -> U.Vector Double -> Target -> Double
logParametersLatt hessian fisher theta = 0.5 * p * (1 - log 3) + 0.5 * log detFisher
  where
    detFisher = det $ map U.fromList hessian

    (logTheta, logFisher, p) = foldr addIfSignificant (0, 0, 0) $ zip (U.toList theta) (U.toList fisher)

addIfSignificant (v, f) (acc_v, acc_f, acc_p)
  | isSignificant v f = (acc_v + log (abs v), acc_f + log f, acc_p + 1)
  | otherwise = (acc_v, acc_f, acc_p)
{-# INLINE addIfSignificant #-}

isSignificant v f = abs (v / sqrt(12 / f) ) >= 1
{-# INLINE isSignificant #-}

fixParam :: Int -> Double -> Fix SRTree -> Fix SRTree
fixParam ix val = cata alg
  where
    alg (Param i) | i == ix   = Fix $ Const val
                  | i > ix    = Fix $ Param (i-1)
                  | otherwise = Fix $ Param i
    alg other = Fix other
{-# INLINE fixParam #-}