module MLLabs.Tuning
       ( evaluateParameter
       , bestParameters
       , bestParametersList
       ) where

import           Data.List         (maximumBy, sortBy)
import           Data.Ord          (comparing)

import           MLLabs.Types      (Dataset, TunableLearningMethod)
import           MLLabs.Validation (TrainAndTestSets)

-- evaluate a parameter performance on a given test set
evaluateParameter
    :: TunableLearningMethod par obj lbl
    -> [TrainAndTestSets obj lbl]
    -> ((obj -> lbl) -> Dataset obj lbl -> Double)
    -> par
    -> Double
evaluateParameter method valSets measure p =
    average $ map (\(train, test) -> measure (method p train) test) valSets
    where average xs = sum xs / fromIntegral (length xs)

-- choose parameter with the largest measure
bestParameters
    :: [par]
    -> TunableLearningMethod par obj lbl
    -> [TrainAndTestSets obj lbl]
    -> ((obj -> lbl) -> Dataset obj lbl -> Double)
    -> par
bestParameters pars method valSets measure =
    maximumOn (evaluateParameter method valSets measure) pars
  where
    maximumOn f = maximumBy (comparing f)

-- | Returns list of parameters and results, best parameters first
bestParametersList
    :: [par]
    -> TunableLearningMethod par obj lbl
    -> [TrainAndTestSets obj lbl]
    -> ((obj -> lbl) -> Dataset obj lbl -> Double)
    -> [(par,Double)]
bestParametersList pars method valSets measure =
    reverse $
    sortBy (comparing snd) $
    map (\par -> (par, evaluateParameter method valSets measure par)) pars
