-- |
-- Module      :  Mcmc.Chain.Chain
-- Description :  Simple representation of a Markov chain
-- Copyright   :  2021 Dominik Schrempf
-- License     :  GPL-3.0-or-later
--
-- Maintainer  :  dominik.schrempf@gmail.com
-- Stability   :  unstable
-- Portability :  portable
--
-- Creation date: Tue May  5 18:01:15 2020.
module Mcmc.Chain.Chain
  ( InitialState,
    Chain (..),
  )
where

-- NOTE: Auxiliary data.
--
-- It is not necessary to add another type @b@ to store auxiliary data about the
-- chain. The information can just be stored in @a@ equally well.
--
-- I am not sure if this is the case. If @b@ is affected by a change in @a@, it
-- has to be recomputed. This is difficult to implement at the proposal step.
-- Maybe the new value is not even accepted. On the other hand, one could trust
-- the lazyness of Haskell, and recompute @b@. The computation is done only when
-- the value is accessed.
--
-- That is, I have to think about how to implement auxiliary data A. Prior and
-- likelihood functions can then act on the state space I and A, e.g.,
--
-- > type PriorFunction a b = a -> b -> Log Double
--
-- where a is the type of the state, and b is the auxiliary data type.

-- NOTE: First class parameters.
--
-- I thought a lot about implementing a type class for parameters. For example,
-- parameters should have a name, a lens, possibly proposals, monitors, and so
-- on.
--
-- However, this is really difficult. If I use a type class, I need different
-- data types for each parameter which is cumbersome (and slow?). Of course, one
-- could use a data type such as
--
-- > data ParamterSpec a = ParameterSpec { name :: ByteString, pMonitor :: (a -> ByteString) }
--
-- But even in this case we run into problems: There are proposals and monitors
-- acting on a combination of parameters. Even setting the name doesn't make
-- sense in this case.
--
-- I decided to let this idea rest.

import Mcmc.Acceptance
import Mcmc.Chain.Link
import Mcmc.Chain.Trace
import Mcmc.Cycle
import Mcmc.Likelihood
import Mcmc.Monitor
import Mcmc.Prior
import Mcmc.Proposal
import System.Random.Stateful
import Prelude hiding (cycle)

-- | Type synonym to indicate the initial state.
type InitialState a = a

-- | The chain contains all information to run an MCMC sampler.
--
-- The state of a chain has type @a@. If necessary, the type @a@ can also be
-- used to store auxiliary information.
--
-- For example, the chain stores information about the current 'Link' and
-- 'iteration', the 'Trace', the 'Acceptance' rates, and the random number
-- generator.
--
-- Further, the chain includes auxiliary variables and functions such as the
-- prior and likelihood functions, or 'Proposal's to move around the state space
-- and to 'Monitor' an MCMC run.
--
-- The 'Mcmc.Environment.Environment' of the chain is not stored externally.
data Chain a = Chain
  { -- Variables; saved.

    -- | The current 'Link' of the chain combines the current state and the
    -- current likelihood. The link is updated after a proposal has been
    -- executed.
    forall a. Chain a -> Link a
link :: Link a,
    -- | The current iteration. In contrast to the link, the iteration is
    -- updated only after all proposals in the cycle have been executed.
    forall a. Chain a -> Int
iteration :: Int,
    -- | The 'Trace' of the Markov chain. In contrast to the link, the trace is
    -- updated only after all proposals in the cycle have been executed.
    forall a. Chain a -> Trace a
trace :: Trace a,
    -- | For each 'Proposal', store actual acceptance counts and for some
    -- proposals also expected acceptance rates.
    forall a. Chain a -> Acceptances (Proposal a)
acceptances :: Acceptances (Proposal a),
    -- | The random number generator.
    forall a. Chain a -> IOGenM StdGen
generator :: IOGenM StdGen,
    --
    -- Variables and functions; not saved.

    -- | Starting iteration of the chain; used to calculate run time and ETA.
    forall a. Chain a -> Int
start :: Int,
    -- | The prior function. The un-normalized posterior is the product of the
    -- prior and the likelihood.
    forall a. Chain a -> PriorFunction a
priorFunction :: PriorFunction a,
    -- | The likelihood function. The un-normalized posterior is the product of
    -- the prior and the likelihood.
    forall a. Chain a -> PriorFunction a
likelihoodFunction :: LikelihoodFunction a,
    -- | A set of 'Proposal's form a 'Cycle'.
    forall a. Chain a -> Cycle a
cycle :: Cycle a,
    -- | A 'Monitor' observing the chain.
    forall a. Chain a -> Monitor a
monitor :: Monitor a
  }