/*--------------------------------------------------------------------*/
/*     Copyright (C) 2004-2016  Serge Iovleff, Université Lille 1, Inria

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this program; if not, write to the
    Free Software Foundation, Inc.,
    59 Temple Place,
    Suite 330,
    Boston, MA 02111-1307
    USA

    Contact : S..._Dot_I..._At_stkpp_Dot_org (see copyright for ...)
*/

/*
 * Project:  stkpp::Clustering
 * created on: 16 oct. 2012
 * Author:   iovleff, S..._Dot_I..._At_stkpp_Dot_org (see copyright for ...)
 * Originally created by Parmeet Bhatia <b..._DOT_p..._AT_gmail_Dot_com>
 **/

/** @file STK_MixtureAlgo.h
 *  @brief In this file we define the classes for mixture algorithms.
 **/

#ifndef STK_MIXTUREALGO_H
#define STK_MIXTUREALGO_H

#include "STK_Clust_Util.h"
#include <Sdk/include/STK_IRunner.h>

namespace STK
{
// forward declaration
class IMixtureComposer;
class IMixtureLearner;

/** @ingroup Clustering
 * Interface base class for the algorithms.
 * All algorithms are runners applying on a model instance given by pointer
 * and have to implement the run method.
 *
 * All algorithms start with an paramUpdateStep(), so user have to provide an instance of
 * the model with initial parameters values.
 **/
class IMixtureAlgo : public IRunnerBase
{
  protected:
    /** default constructor */
    inline IMixtureAlgo(): IRunnerBase(), p_model_(0), nbIterMax_(0), epsilon_(0.), threshold_(2.) {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline IMixtureAlgo( IMixtureAlgo const& algo) : IRunnerBase(algo)
                       , p_model_(algo.p_model_)
                       , nbIterMax_(algo.nbIterMax_)
                       , epsilon_(algo.epsilon_)
                       , threshold_(algo.threshold_)
    {}

  public:
    /** destructor */
    inline virtual ~IMixtureAlgo() {}
    /** @return the maximal number of iteration of the algorithm */
    inline int nbIterMax() const { return nbIterMax_; }
    /** @return the epsilon of the algorithm */
    inline int epsilon() const { return epsilon_; }
    /** set a new model. Default threshold_ is 2 samples. */
    void setModel(IMixtureComposer* p_model);
    /** set the maximal number of iterations */
    inline void setNbIterMax(int nbIterMax) { nbIterMax_ = nbIterMax; }
    /** set the tolerance value */
    inline void setEpsilon(Real epsilon) { epsilon_ = epsilon; }
    /** set the tolerance value */
    inline void setThreshold(Real threshold) { threshold_ = threshold; }

  protected:
    /** pointer on the mixture model */
    IMixtureComposer* p_model_;
    /** number of iterations of the algorithm */
    int nbIterMax_;
    /** tolerance of the algorithm. */
    Real epsilon_;
    /** Minimal number of individuals. If the expected number of individuals
     *  is under this number, the algorithm will stop and return false.
     **/
    Real threshold_;
};

/** @ingroup Clustering
 * Interface base class for the learning algorithms.
 * All algorithms are runners applying on a model instance given by pointer
 * and have to implement the run method.
 **/
class IMixtureLearnAlgo: public IRunnerBase
{
  protected:
    /** default constructor */
    inline IMixtureLearnAlgo(): IRunnerBase(), p_model_(0), nbIterMax_(0), epsilon_(0.) {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline IMixtureLearnAlgo( IMixtureLearnAlgo const& algo) : IRunnerBase(algo)
                       , p_model_(algo.p_model_)
                       , nbIterMax_(algo.nbIterMax_)
                       , epsilon_(algo.epsilon_)
    {}

  public:
    /** destructor */
    inline virtual ~IMixtureLearnAlgo() {}
    /** @return the maximal number of iteration of the algorithm */
    inline int nbIterMax() const { return nbIterMax_; }
    /** @return the epsilon of the algorithm */
    inline int epsilon() const { return epsilon_; }
    /** set a new model. Default threshold_ is 2 samples. */
    void setModel(IMixtureLearner* p_model);
    /** set the maximal number of iterations */
    inline void setNbIterMax(int nbIterMax) { nbIterMax_ = nbIterMax; }
    /** set the tolerance value */
    inline void setEpsilon(Real epsilon) { epsilon_ = epsilon; }

  protected:
    /** pointer on the mixture model */
    IMixtureLearner* p_model_;
    /** maximal number of iterations of the algorithm */
    int nbIterMax_;
    /** tolerance of the algorithm. */
    Real epsilon_;
};

/** @ingroup Clustering
 *  @ brief Implementation of the EM algorithm.
 *  The EM algorithm call alternatively the steps:
 *  - paramUpdateStep()
 *  - eStep()
 *  until the maximum number of iterations is reached or the variation of the
 *  ln-likelihood is less than the tolerance.
 **/
class EMAlgo: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline EMAlgo() : IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline EMAlgo( EMAlgo const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~EMAlgo(){}
    /** clone pattern */
    inline virtual EMAlgo* clone() const { return new EMAlgo(*this);}
    /** run the algorithm on the model calling the eStep and mStep of the model
     *  until the maximal number of iteration is reached or the variation
     *  of the lnLikelihood is less than epsilon.
     * @return @c true if no error occur, @c false otherwise
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SEM algorithm.
 *  The CEM algorithm calls alternatively the steps:
 *  - cStep()
 *  - paramUpdateStep()
 *  - eStep()
 *  until the maximum number of iterations is reached or the variation of the
 *  ln-likelihood is less than the tolerance.
 **/
class CEMAlgo: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline CEMAlgo() : IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline CEMAlgo( CEMAlgo const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~CEMAlgo(){}
    /** clone pattern */
    inline virtual CEMAlgo* clone() const { return new CEMAlgo(*this);}
    /** run the algorithm on the model calling cStep, mStep and eStep of the
     *  model until the maximal number of iteration is reached or the variation
     *  of the lnLikelihood is less than epsilon.
     *  @return @c true if no error occur, @c false otherwise
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SEM algorithm.
 *  The SEM algorithm calls alternatively the steps:
 *  - sStep()
 *  - samplingStep()
 *  - pStep()
 *  - paramUpdateStep()
 *  - eStep()
 *  - storeIntermediateResults(iter)
 *  until the maximum number of iterations is reached.
 **/
class SEMAlgo: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline SEMAlgo() : IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline SEMAlgo( SEMAlgo const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~SEMAlgo(){}
    /** clone pattern */
    inline virtual SEMAlgo* clone() const { return new SEMAlgo(*this);}
    /** run the algorithm on the model calling sStep, mStep and eStep of the
     *  model until the maximal number of iteration is reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SemiSEM algorithm.
 *  The SemiSEM algorithm calls alternatively the steps:
 *  - samplingStep()
 *  - pStep()
 *  - paramUpdateStep()
 *  - eStep()
 *  - storeIntermediateResults(iter)
 *  until the maximum number of iterations is reached.
 **/
class SemiSEMAlgo: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline SemiSEMAlgo() : IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline SemiSEMAlgo( SemiSEMAlgo const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~SemiSEMAlgo(){}
    /** clone pattern */
    inline virtual SemiSEMAlgo* clone() const { return new SemiSEMAlgo(*this);}
    /** run the algorithm on the model calling sStep, mStep and eStep of the
     *  model until the maximal number of iteration is reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the EMPredict algorithm.
 *  The EMPredict algorithm calls alternatively the steps:
 *  - imputationStep()
 *  - eStep()
 *  until the maximum number of iterations or the threshold is reached.
 *  This is the counterpart of EMSAlgo for prediction.
 **/
class EMPredict: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline EMPredict(): IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline EMPredict( EMPredict const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~EMPredict(){}
    /** clone pattern */
    inline virtual EMPredict* clone() const { return new EMPredict(*this);}
    /** run the algorithm on the model until the maximal number of iteration
     * or the threshold is reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SEMPredict algorithm.
 *  The SEMPredict algorithm calls alternatively the steps:
 *  - sStep()
 *  - samplingStep()
 *  - eStep()
 *  - storeIntermediateResults(iter)
 *  until the maximum number of iterations is reached.
 *  This is the counterpart of SEMSAlgo for prediction.
 **/
class SEMPredict: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline SEMPredict() : IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline SEMPredict( SEMPredict const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~SEMPredict(){}
    /** clone pattern */
    inline virtual SEMPredict* clone() const { return new SEMPredict(*this);}
    /** run the algorithm on the model until the maximal number of iteration is
     *  reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SemiSEMPredict algorithm.
 *  The SemiSEMPredict algorithm calls alternatively the steps:
 *  - samplingStep()
 *  - eStep()
 *  - storeIntermediateResults(iter)
 *  until the maximum number of iterations is reached.
 *  This is the counterpart of SemiSEMSAlgo for prediction.
 **/
class SemiSEMPredict: public IMixtureAlgo
{
  public:
    /** default constructor */
    inline SemiSEMPredict(): IMixtureAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline SemiSEMPredict( SemiSEMPredict const& algo) : IMixtureAlgo(algo) {}
    /** destructor */
    inline virtual ~SemiSEMPredict(){}
    /** clone pattern */
    inline virtual SemiSEMPredict* clone() const { return new SemiSEMPredict(*this);}
    /** run the algorithm on the model until the maximal number of iteration is
     *  reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @ brief Implementation of the ImputeAlgo learning algorithm.
 *  The Impute algorithm call alternatively the steps:
 *  - imputeStep()
 *  - paramUpdateStep()
 *  until the maximum number of iterations is reached or the variation of the
 *  log-likelihood is less than the tolerance.
 **/
class ImputeAlgo: public IMixtureLearnAlgo
{
  public:
    /** default constructor */
    inline ImputeAlgo() : IMixtureLearnAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline ImputeAlgo( ImputeAlgo const& algo) : IMixtureLearnAlgo(algo) {}
    /** destructor */
    inline virtual ~ImputeAlgo(){}
    /** clone pattern */
    inline virtual ImputeAlgo* clone() const { return new ImputeAlgo(*this);}
    /** run the algorithm on the model calling the eStep and mStep of the model
     *  until the maximal number of iteration is reached or the variation
     *  of the lnLikelihood is less than epsilon.
     * @return @c true if no error occur, @c false otherwise
     **/
    virtual bool run();
};

/** @ingroup Clustering
 *  @brief Implementation of the SimulAlgo learning algorithm.
 *  The SimulAlgo algorithm calls alternatively steps:
 *  - samplingStep()
 *  - paramUpdateStep()
 *  - storeIntermediateResults(iter)
 *  until the maximum number of iterations is reached.
 **/
class SimulAlgo: public IMixtureLearnAlgo
{
  public:
    /** default constructor */
    inline SimulAlgo() : IMixtureLearnAlgo() {}
    /** Copy constructor.
     *  @param algo the algorithm to copy */
    inline SimulAlgo( SimulAlgo const& algo) : IMixtureLearnAlgo(algo) {}
    /** destructor */
    inline virtual ~SimulAlgo(){}
    /** clone pattern */
    inline virtual SimulAlgo* clone() const { return new SimulAlgo(*this);}
    /** run the algorithm on the model calling sStep, mStep and eStep of the
     *  model until the maximal number of iteration is reached.
     *  @return @c true if no error occur, @c false otherwise.
     **/
    virtual bool run();
};
} // namespace STK

#endif /* STK_MIXTUREALGO_H */
