experimenter-0.1.0.0: src/Experimenter/Experiment.hs
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TypeFamilies #-}
module Experimenter.Experiment where
import Control.DeepSeq
import Control.Monad.IO.Unlift
import Data.Serialize (Serialize)
import Data.Kind
import System.Random.MWC
import Experimenter.Parameter
import Experimenter.StepResult
type Period = Int
type ExperimentNumber = Int
type RepetitionNumber = Int
type ReplicationNumber = Int
-- | Phase of experiment. Each experiment runs through all phases, where however a phase may have 0 steps.
data Phase
= PreparationPhase -- ^ For preparing the evaluation. For instance to let a machine learning algorithm learn its function, before it is evaluated.
| WarmUpPhase -- ^ The warm up phase is equal to the evaluation phase, but does not count as evaluation. Used to get the system in a steady state.
| EvaluationPhase -- ^ Evaluation phase.
deriving (Eq, Ord, Show, Enum)
-- | Definition of the Experiment.
class (Monad (ExpM a), MonadUnliftIO (ExpM a), NFData a, NFData (InputState a), NFData (InputValue a), Serialize (InputValue a), Serialize (InputState a), Serialize (Serializable a)) => ExperimentDef a where
-- | Monad to run experiments in. In most cases you want this to be `IO`.
type ExpM a :: (Type -> Type)
-- | Type that is used to serialize the current state.
type Serializable a :: Type
-- Types for input values to the experiment:
-- | The input to the system for running a step. Set to `()` if unused.
type InputValue a :: Type
-- | Can be used to save a information from one call to `generateInput` to the next. Set to `()` if unused.
type InputState a :: Type
-- | Generate some input values and possibly modify state. This function can be used to change the state. It is called
-- before `runStep` and its output is used to call `runStep`.
generateInput :: GenIO -> a -> InputState a -> Period -> (ExpM a) (InputValue a, InputState a)
default generateInput :: (InputValue a ~ (), InputState a ~ ()) => GenIO -> a -> InputState a -> Period -> (ExpM a) (InputValue a, InputState a)
generateInput _ _ _ _ = return ((), ())
-- | Run a step of the environment and return new state and result.
runStep :: Phase -> a -> InputValue a -> Period -> (ExpM a) ([StepResult], a)
-- | Provides the parameter setting. Parameters are used to design the experiment instances, e.g. the variants that will be run.
parameters :: a -> [ParameterSetup a]
default parameters :: a -> [ParameterSetup a]
parameters _ = []
-- | This function defines how to find experiments that can be resumed. Note that the experiments name and experiment
-- info parameters are always comparison factors, that is, experiments with different names or info parameters are
-- unequal. The default is always True.
equalExperiments :: (a, InputState a) -> (a, InputState a) -> Bool
default equalExperiments :: (a, InputState a) -> (a, InputState a) -> Bool
equalExperiments _ _ = True
-- | Function to convert to a serializable object. Can be used to convert the state to serialisable representation.
serialisable :: a -> ExpM a (Serializable a)
default serialisable :: (a ~ Serializable a) => a -> ExpM a (Serializable a)
serialisable = return
-- | Function to convert from a serializable object. Can be used to convert the state from its serialisable representation.
deserialisable :: Serializable a -> ExpM a a
default deserialisable :: (a ~ Serializable a) => Serializable a -> ExpM a a
deserialisable = return
-- HOOKS
-- | Function to call on the state before the preparation. This function is only executed if the preparation phase
-- exists (that is >0 preparation steps) and is started from period 0!
beforePreparationHook :: ExperimentNumber -> RepetitionNumber -> GenIO -> a -> ExpM a a
default beforePreparationHook :: ExperimentNumber -> RepetitionNumber -> GenIO -> a -> ExpM a a
beforePreparationHook _ _ _ = return
-- | Function to call on the state before the warm up phase. This function is only executed if a warm up phase exists
-- (that is >0 warm-up steps) and is initialised, which happens on the first time it is started!
beforeWarmUpHook :: ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> GenIO -> a -> ExpM a a
default beforeWarmUpHook :: ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> GenIO -> a -> ExpM a a
beforeWarmUpHook _ _ _ _ = return
-- | Function to call on the state before the evaluation phase. This function is only executed if the evaluation phase
-- exists (that is >0 evaluation steps) and is initialised which happens on the first time it is started!
beforeEvaluationHook :: ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> GenIO -> a -> ExpM a a
default beforeEvaluationHook :: ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> GenIO -> a -> ExpM a a
beforeEvaluationHook _ _ _ _ = return
-- | Function to call after the preparation phase, e.g. it can be used to move files. This function is only executed if
-- the preparation phase is updated. The first parameter is the input state and is only used for type checking.
afterPreparationHook :: a -> ExperimentNumber -> RepetitionNumber -> IO ()
default afterPreparationHook :: a -> ExperimentNumber -> RepetitionNumber -> IO ()
afterPreparationHook _ _ _ = return ()
-- | Function to call after the warmUp phase, e.g. it can be used to move files. This function is only executed if
-- the warmUp phase is updated. The first parameter is the input state and is only used for type checking.
afterWarmUpHook :: a -> ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> IO ()
default afterWarmUpHook :: a -> ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> IO ()
afterWarmUpHook _ _ _ _ = return ()
-- | Function to call after the evaluation phase, e.g. it can be used to move files. This function is only executed if
-- the evaluation phase is updated. The first parameter is the input state before the evaluation and is only used for type checking.
afterEvaluationHook :: a -> ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> IO ()
default afterEvaluationHook :: a -> ExperimentNumber -> RepetitionNumber -> ReplicationNumber -> IO ()
afterEvaluationHook _ _ _ _ = return ()