---------------------------------------------------------------------------
-- | Module    : Math.Statistics.Dirichlet.Options
-- Copyright   : (c) 2009-2012 Felipe Lessa
-- License     : BSD3
--
-- Maintainer  : felipe.lessa@gmail.com
-- Stability   : experimental
-- Portability : portable
--
--------------------------------------------------------------------------

module Math.Statistics.Dirichlet.Options
    ( TrainingVector
    , TrainingVectors
    , StepSize(..)
    , Delta
    , Predicate(..)
    , Reason(..)
    , Result(..)
    ) where

import qualified Data.Vector as V
import qualified Data.Vector.Unboxed as U
import qualified Numeric.Optimization.Algorithms.HagerZhang05 as CG
import Control.DeepSeq (NFData(..))

-- | A vector used for deriving the parameters of a Dirichlet
--   density or mixture.
type TrainingVector = U.Vector Double

-- | A vector of training vectors.  This is the only vector that
-- is not unboxed (for obvious reasons).
type TrainingVectors = V.Vector TrainingVector

-- | Usually denoted by lowercase greek letter eta (η), size of
--   each step in the gradient. Should be greater than zero and
--   much less than one.
newtype StepSize = Step Double

-- | Maximum difference between costs to consider that the
--   process converged.
type Delta = Double

-- | Predicate specifying when the training should be over.
data Predicate = Pred
    { maxIter    :: !Int    -- ^ Maximum number of iterations.
    , minDelta   :: !Delta  -- ^ Minimum delta to continue iterating.
                            -- This is invariant of @deltaSteps@, which
                            -- means that if @deltaSteps@ is @2@ then
                            -- minDelta will be considered twice bigger
                            -- to account for the different @deltaSteps@.
    , deltaSteps :: !Int    -- ^ How many estimation steps should be done
                            -- before recalculating the delta.  If
                            -- @deltaSteps@ is @1@ then it will be
                            -- recalculated on every step.
    , maxWeightIter :: !Int -- ^ Maximum number of iterations on
                            -- each weight step.
    , jumpDelta  :: !Delta  -- ^ Used only when calculating mixtures.
                            -- When the delta drops below this cutoff
                            -- the computation changes from estimating
                            -- the alphas to estimatating the weights
                            -- and vice-versa.  Should be greater than
                            -- @minDelta@.
    }
                 deriving (Eq, Read, Show)

-- | Reason why the derivation was over.
data Reason = Delta        -- ^ The difference between
                           -- applications of the cost function
                           -- dropped below the minimum delta.
                           -- In other words, it coverged.
            | MaxIter      -- ^ The maximum number of iterations
                           -- was reached while the delta was
                           -- still greater than the minimum delta.
            | CG CG.Result -- ^ CG_DESCENT returned this result,
                           -- which brought the derivation
                           -- process to a halt.
              deriving (Eq, Read, Show)

-- | Result of a deriviation.
data Result a =
    Result { reason    :: !Reason  -- ^ Reason why the derivation was over.
           , iters     :: !Int     -- ^ Number of iterations spent.
           , lastDelta :: !Delta   -- ^ Last difference between costs.
           , lastCost  :: !Double  -- ^ Last cost (i.e. the cost of the result).
           , result    :: !a       -- ^ Result obtained.
           }
    deriving (Eq, Read, Show)

instance NFData a => NFData (Result a) where
    rnf = rnf . result