packages feed

experimenter-0.1.0.0: src/Experimenter/Eval/Type.hs

{-# LANGUAGE BangPatterns              #-}
{-# LANGUAGE DeriveAnyClass            #-}
{-# LANGUAGE DeriveGeneric             #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE OverloadedStrings         #-}
{-# LANGUAGE TemplateHaskell           #-}

module Experimenter.Eval.Type where

import           Control.DeepSeq
import           Control.Lens              hiding (Over)
import           Data.ByteString           (ByteString)
import           Data.Serialize
import qualified Data.Text                 as T
import qualified Data.Text.Encoding        as E
import           GHC.Generics
import           Prelude                   hiding (sum)


import           Experimenter.Availability
import           Experimenter.Result.Type  hiding (Experiments)
import qualified Experimenter.Result.Type  as R

-- | Over datatype to reduce data vectors.
data Over a
  = OverPeriods
  | OverReplications
  | OverExperimentRepetitions
  -- implement this by sorting and taking/dropping results
  --  | OverBestXExperimentRepetitions Int (ExperimentResult a -> ExperimentResult a -> Ordering)
  deriving (Generic, Serialize, NFData)

instance Eq (Over a) where
  OverReplications == OverReplications = True
  OverPeriods == OverPeriods = True
  OverExperimentRepetitions == OverExperimentRepetitions = True
  -- OverBestXExperimentRepetitions _ _ == OverBestXExperimentRepetitions _ _ = True
  _ == _ = False

instance Show (Over a) where
  show OverReplications          = "Replications"
  show OverPeriods               = "Periods"
  show OverExperimentRepetitions = "Experiments"
  -- show (OverBestXExperimentRepetitions nr _) = "(BestXExperimentEvaluations " <> show nr <> ")"

instance Ord (Over a) where
  compare OverExperimentRepetitions OverExperimentRepetitions = EQ
  compare OverExperimentRepetitions _                         = GT
  compare OverReplications OverReplications                   = EQ
  compare OverReplications _                                  = GT
  compare OverPeriods OverPeriods                             = EQ
  compare OverPeriods _                                       = LT

  -- compare OverExperimentRepetitions OverReplications          = GT
  -- compare OverExperimentRepetitions OverPeriods               = GT

  -- compare OverReplications _                                  = LT
  -- compare OverReplications OverPeriods                        = GT
  -- compare OverPeriods OverPeriods                             = EQ
  -- compare OverPeriods _                                       = LT
  -- compare OverBestXExperimentRepetitions{} OverBestXExperimentRepetitions{} = EQ
  -- compare OverBestXExperimentRepetitions{} _                                = GT


-- | Definition of statisics. Is used to define the desired output.


data StatsDef a
  = Mean !(Over a) !(Of a)
  | StdDev !(Over a) !(Of a)
  | Sum !(Over a) !(Of a)
  --  | TakeBest Int (StatsDef a) (Of a)
  | Id !(Of a)
  | Named !(StatsDef a) !ByteString
  | Name !ByteString !(StatsDef a)
  deriving (Generic, Serialize, Show, Eq, Ord, NFData)

-- type Name =

getOver :: StatsDef a -> Maybe (Over a)
getOver (Mean o _)   = Just o
getOver (StdDev o _) = Just o
getOver (Sum o _)    = Just o
getOver (Id _)       = Nothing
getOver (Named _ _)  = Nothing
getOver (Name _ _)   = Nothing


data Of a
  = Of !ByteString
  | Stats !(StatsDef a)
  | Div !(Of a) !(Of a)
  | Add !(Of a) !(Of a)
  | Sub !(Of a) !(Of a)
  | Mult !(Of a) !(Of a)
  | Last !(Of a)
  | First !(Of a)
  | EveryXthElem !Int !(Of a)
  | Length !(Of a)
  deriving (Generic, Serialize, Show, Eq, Ord, NFData)

prettyStatsDef :: StatsDef a -> T.Text
prettyStatsDef statsDef =
  case statsDef of
    Named _ txt     -> E.decodeUtf8 txt
    Name txt _      -> E.decodeUtf8 txt
    Mean over of'   -> "Mean " <> prettyOver over <> " " <> prettyOf of'
    StdDev over of' -> "StdDev " <> prettyOver over <> " " <> prettyOf of'
    Sum over of'    -> "Sum " <> prettyOver over <> " " <> prettyOf of'
    Id (Of name)    -> E.decodeUtf8 name <> "s"
    Id of'          -> prettyOf of'

prettyOf :: Of a -> T.Text
prettyOf = dropDoublePars . prettyOf'
  where
    prettyOf' of' =
      case of' of
        Of name -> "of " <> E.decodeUtf8 name
        Stats statsDef -> "(" <> prettyStatsDef statsDef <> ")"
        Div x y -> "( " <> prettyOf' x <> ") / (" <> prettyOf' y <> ")"
        Add x y -> "( " <> prettyOf' x <> ") + (" <> prettyOf' y <> ")"
        Sub x y -> "( " <> prettyOf' x <> ") - (" <> prettyOf' y <> ")"
        Mult x y -> "( " <> prettyOf' x <> ") * (" <> prettyOf' y <> ")"
        Last x -> "Last(" <> prettyOf' x <> ")"
        First x -> "First(" <> prettyOf' x <> ")"
        Length x -> "Length(" <> prettyOf' x <> ")"
        EveryXthElem nr x -> "EveryXthElem(" <> T.pack (show nr) <> ", " <> prettyOf' x <> ")"
    dropDoublePars = T.replace "((" "(" . T.replace "))" ")"


prettyOver :: Over a -> T.Text
prettyOver ov = "over " <> case ov of
  OverPeriods               -> "periods"
  OverReplications          -> "replications"
  OverExperimentRepetitions -> "experiment repetitions"
  -- OverBestXExperimentRepetitions nr _ -> "best " <> T.pack (show nr) <> " experiment repetitions"


-- Helper functions for demoting StatsDefs to Ofs.

sum :: Over a -> Of a -> Of a
sum over of' = Stats (Sum over of')

stdDev :: Over a -> Of a -> Of a
stdDev over of' = Stats (StdDev over of')

mean :: Over a -> Of a -> Of a
mean over of' = Stats (Mean over of')


-- | Simple examples on how to use the types

example :: StatsDef a
example = Mean OverReplications (sum OverPeriods (Of "NrEarly") `Div` sum OverPeriods (Of "NrOrders"))

example2 :: StatsDef a
example2 = Mean OverReplications (Of "NrEarly" `Div` Of "NrOrders")


example3 :: StatsDef a
example3 = Mean OverReplications (Of "X")


-- | Datatypes for the evaluation result.
data Unit
  = UnitPeriods
  | UnitReplications
  | UnitExperimentRepetition
  | UnitScalar
  deriving (Generic, Serialize, Read, Eq, Ord, Show, NFData)
  --  UnitBestExperimentRepetitions Int

data EvalResults a
  = EvalVector { _evalType   :: !(StatsDef a)
               , _evalUnit   :: !Unit -- ^ Over which the vector runs.
               , _evalValues :: ![EvalResults a]
               }
  | EvalValue { _evalType         :: !(StatsDef a)
              , _evalUnit         :: !Unit -- ^ Is always periods.
              , _evalVariableName :: !ByteString
              , _evalX            :: !(Either Int Double) -- ^ Either period or xValue.
              , _evalY            :: !Double }
  | EvalReducedValue { _evalType  :: !(StatsDef a)
                     , _evalUnit  :: !Unit -- ^ Over which was reduced.
                     , _evalValue :: !Double }
  deriving (Generic, Serialize, Show, NFData)
makeLenses ''EvalResults

data ExperimentEval a = ExperimentEval
  { _evalExperimentNumber  :: !Int
  , _evalExperimentResults :: ![Availability IO (EvalResults a)]
  , _evalExperiment        :: !(Experiment a)
  } deriving (Generic, NFData)
makeLenses ''ExperimentEval

instance Show (ExperimentEval a) where
  show x = show (x ^. evalExperimentResults)

data Evals a = Evals
  { _evalsExperiments :: !(R.Experiments a)
  , _evalsResults     :: ![ExperimentEval a]
  } deriving (Generic, NFData)
makeLenses ''Evals


-- Helper Functions

getEvalValue :: EvalResults a -> [Double]
getEvalValue (EvalVector _ _ xs)      = concatMap getEvalValue xs
getEvalValue (EvalValue _ _ _ _ y)    = [y]
getEvalValue (EvalReducedValue _ _ y) = [y]

getEvalType :: (Over a -> Of a -> StatsDef a) -> EvalResults a -> StatsDef a
getEvalType f (EvalVector tp unit _)     = f (fromUnit unit) (Stats tp)
  where fromUnit UnitPeriods              = OverPeriods
        fromUnit UnitReplications         = OverReplications
        fromUnit UnitExperimentRepetition = OverExperimentRepetitions
        -- fromUnit (UnitBestExperimentRepetitions nr) = OverBestXExperimentRepetitions nr (error "compare function in BestXExperimentEvaluations may not be used")
        fromUnit UnitScalar               = OverExperimentRepetitions -- TODO really?
getEvalType _ (EvalValue t _ _ _ _)      = t
getEvalType _ (EvalReducedValue t _ _) = t

fromOver :: Over a -> Unit
fromOver OverPeriods               = UnitPeriods
fromOver OverReplications          = UnitReplications
fromOver OverExperimentRepetitions = UnitExperimentRepetition
-- fromOver (OverBestXExperimentRepetitions nr _) = UnitBestExperimentRepetitions nr


-- | Demotes the unit by 1 degree. Thus this calculates the unit of a vector over which it was reduced.
demoteUnit :: Unit -> Maybe Unit
demoteUnit UnitPeriods              = Nothing
demoteUnit UnitReplications         = Just UnitPeriods
demoteUnit UnitExperimentRepetition = Just UnitReplications
-- demoteUnit UnitBestExperimentRepetitions{} = Just UnitReplications
demoteUnit UnitScalar               = Just UnitExperimentRepetition