// file: $isip/class/stat/SupportVectorModel/SupportVectorModel.h
// version: $Id: SupportVectorModel.h 9378 2003-12-23 20:56:11Z alphonso $
//

// make sure definitions are only made once
//
#ifndef ISIP_SUPPORT_VECTOR_MODEL
#define ISIP_SUPPORT_VECTOR_MODEL

// isip include files
//
#ifndef ISIP_SIGMOID
#include <Sigmoid.h>
#endif

#ifndef ISIP_VECTOR
#include <Vector.h>
#endif

#ifndef ISIP_KERNEL
#include <Kernel.h>
#endif

#ifndef ISIP_VECTOR_FLOAT
#include <VectorFloat.h>
#endif

#ifndef ISIP_MEMORY_MANAGER
#include <MemoryManager.h>
#endif

#ifndef ISIP_STATISTICAL_MODEL_BASE
#include <StatisticalModelBase.h>
#endif

// SupportVectorModel: a class to compute distance of the test vector
// from a hyperplane in transformed space
//
//                     N
//                   ----                      
// distance = bias + \    [ alpha(i) * kernel (x, sv[i]) ]
//                   /___                    i
//                     i
//
//  'sv' is the support vector
//  'N' is the number of support vectors
//  'alpha(i)' is the weigth for i-th support vector
//  'bias' is the distance of hyperplane from the origin
//
// resulting distance (positive or negative) can be transformed to
// posterior probability using e.g. sigmoid transformation
//
// probability = sigmoid (distance)
//
class SupportVectorModel : public StatisticalModelBase {

  //---------------------------------------------------------------------------
  //
  // public constants
  //
  //---------------------------------------------------------------------------
public:

  // define the class name
  //
  static const String CLASS_NAME;
  
  //----------------------------------------
  //
  // other important constants
  //
  //----------------------------------------
  
  enum ALGORITHM { SINGLE_KERNEL = 0, MULTIPLE_KERNEL,
                   DEF_ALGORITHM = SINGLE_KERNEL };

  enum IMPLEMENTATION { SIGMOID = 0, LOGISTIC, LOGSYM, TANH, SINUS, GAUSS,
			RANDOM, DEF_IMPLEMENTATION = SIGMOID };

  static const double MAX_LOG_DURATION = 3.912;

  // define static NameMap objects for the enumerated values
  //
  static const NameMap ALGO_MAP;
  static const NameMap IMPL_MAP;

  //----------------------------------------
  //
  // i/o related constants
  //
  //----------------------------------------  
  
  static const String DEF_PARAM;
  static const String PARAM_ALGORITHM;
  static const String PARAM_IMPLEMENTATION;
  static const String PARAM_SUPPORT_VECTORS;
  static const String PARAM_KERNELS;
  static const String PARAM_ALPHAS;
  static const String PARAM_BIAS;
  static const String PARAM_SIGMOID;
  static const String DEF_COMMENT_TAG;

  //----------------------------------------
  //
  // error codes
  //
  //----------------------------------------  

  static const long ERR = 60350;
    
  //---------------------------------------------------------------------------
  //
  // protected data
  //
  //---------------------------------------------------------------------------
protected:

  // define algorithm and implementation
  //
  ALGORITHM algorithm_d;
  IMPLEMENTATION implementation_d;

  // support vectors, their kernels and alphas
  //
  Vector<VectorFloat> support_vectors_d;
  Vector<Kernel> kernels_d;
  Vector<Double> alphas_d;
  Double bias_d;

  // define distance->probability transformation function
  // currently sigmoid
  //
  Sigmoid sigmoid_d;

  // memory manager
  //
  static MemoryManager mgr_d;
  
  //---------------------------------------------------------------------------
  //
  // required public methods
  //
  //---------------------------------------------------------------------------
public:

  // method: name
  //
  static const String& name() {
    return CLASS_NAME;
  }

  // other static methods
  //
  static boolean diagnose(Integral::DEBUG debug_level);

  // debug methods
  //  setDebug is inherited from base class
  //
  boolean debug(const unichar* msg) const;
  
  // method: destructor
  //
  ~SupportVectorModel() {}

  // method: default constructor
  //
  SupportVectorModel() {
    algorithm_d = DEF_ALGORITHM;
    implementation_d = DEF_IMPLEMENTATION;
    bias_d = 0;
  }

  // method: copy constructor
  //
  SupportVectorModel(const SupportVectorModel& arg) {
    assign(arg);
  }

  // assign methods
  //
  boolean assign(const SupportVectorModel& arg);

  // method: operator=
  //
  SupportVectorModel& operator=(const SupportVectorModel& arg) {
    assign(arg);
    return *this;
  }
  
  // sofSize method
  //
  long sofSize() const;
  
  // method: sofAccumulatorSize
  //
  long sofAccumulatorSize() const {
    return Error::handle(name(), L"sofAccumulatorSize", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: sofOccupanciesSize
  //
  long sofOccupanciesSize() const {
    return Error::handle(name(), L"sofOccupanciesSize", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }    

  // other i/o methods
  //
  boolean read(Sof& sof, long tag, const String& name = CLASS_NAME);
  boolean write(Sof& sof, long tag, const String& name = CLASS_NAME) const;

  boolean readData(Sof& sof, const String& pname = DEF_PARAM,
                   long size = SofParser::FULL_OBJECT,
                   boolean param = true,
                   boolean nested = false);
  boolean writeData(Sof& sof, const String& pname = DEF_PARAM) const;

  // method: readAccumulator
  //
  boolean readAccumulator(Sof& sof, long tag,
		    const String& cname = CLASS_NAME) {
    return Error::handle(name(), L"readAccumulator", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: writeAccumulator
  //
  boolean writeAccumulator(Sof& sof, long tag,
		     const String& cname = CLASS_NAME) const {
    return Error::handle(name(), L"writeAccumulator", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: readAccumulatorData
  //
  boolean readAccumulatorData(Sof& sof, const String& pname = DEF_PARAM,
			long size = SofParser::FULL_OBJECT,
			boolean param = true,
			boolean nested = false) {
    return Error::handle(name(), L"readAccumulatorData", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: writeAccumulatorData
  //
  boolean writeAccumulatorData(Sof& sof,
			       const String& pname = DEF_PARAM) const {
    return Error::handle(name(), L"writeAccumulatorData", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // method: readOccupancies
  //
  boolean readOccupancies(Sof& sof, long tag,
			  const String& cname = CLASS_NAME) {
    return Error::handle(name(), L"readOccupancies", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: writeOccupancies
  //
  boolean writeOccupancies(Sof& sof, long tag,
			   const String& cname = CLASS_NAME) const {
    return Error::handle(name(), L"writeOccupancies", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // method: readOccupanciesData
  //
  boolean readOccupanciesData(Sof& sof, const String& pname = DEF_PARAM,
			      long size = SofParser::FULL_OBJECT,
			      boolean param = true,
			      boolean nested = false) {
    return Error::handle(name(), L"readOccupanciesData", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: writeOccupanciesData
  //
  boolean writeOccupanciesData(Sof& sof,
			       const String& pname = DEF_PARAM) const {
    return Error::handle(name(), L"writeOccupanciesData", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }
  
  // equality methods
  //
  boolean eq(const SupportVectorModel& arg) const;

  // method: operator new
  //
  static void* operator new(size_t size) {
    return mgr_d.get();
  }

  // method: operator new[]
  //
  static void* operator new[](size_t size) {
    return mgr_d.getBlock(size);
  }

  // method: operator delete
  //
  static void operator delete(void* ptr) {
    mgr_d.release(ptr);
  }

  // method: operator delete[]
  //
  static void operator delete[](void* ptr) {
    mgr_d.releaseBlock(ptr);
  }

  // method: setGrowSize
  //
  static boolean setGrowSize(long grow_size) {
    return mgr_d.setGrow(grow_size);
  }

  // method: clear
  //
  boolean clear(Integral::CMODE cmode = Integral::DEF_CMODE) {
    support_vectors_d.clear(cmode);
    kernels_d.clear(cmode);
    alphas_d.clear(cmode);
    sigmoid_d.clear(cmode);
    return true;
  }
  
  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  additional methods unique to this class
  //
  //--------------------------------------------------------------------------

  // method: setAlgorithm
  // to set single or muti-kernel mode
  //
  boolean setAlgorithm(ALGORITHM algo) {
    algorithm_d = algo;
    return true;
  }

  // method: getAlgorithm
  // to get what mode we are using (single/multi-kernel)
  //
  ALGORITHM getAlgorithm() {
    return algorithm_d;
  }

  // method: setImplementation
  // methods to set the distance->probability transformation
  //
  boolean setImplementation(IMPLEMENTATION impl) {
    implementation_d = impl;
    return true;
  }

  // method: getImplementation
  // to get what distance->probability transformation we use
  //
  IMPLEMENTATION getImplemenation() {
    return implementation_d;
  }

  // methods to manipulate the support vectors, kernels and weights
  //
  boolean setNumSupportVectors(long number,
			       ALGORITHM algorithm = DEF_ALGORITHM);

  // method: getSupportVector
  //
  VectorFloat& getSupportVector(long index) {
    return support_vectors_d(index);
  }

  // method: getSupportVectors
  //
  Vector<VectorFloat>& getSupportVectors() {
    return support_vectors_d;
  }  

  // method: getKernel
  //
  Kernel& getKernel(long index) {
    if (algorithm_d == SINGLE_KERNEL) {
      return kernels_d(index);
    }
    else {
      return kernels_d(0);
    }
  }

  // method: getKernels
  //
  Vector<Kernel>& getKernels() {
    return kernels_d;
  }  

  // method: getAlpha
  //
  Double& getAlpha(long index) {
    return alphas_d(index);
  }

  // method: getAlphas
  //
  Vector<Double>& getAlphas() {
    return alphas_d;
  }
  
  // method: getBias
  //
  Double& getBias() {
    return bias_d;
  }

  // method: getSigmoid
  //
  Sigmoid& getSigmoid() {
    if (!implementation_d == SIGMOID) {
      Error::handle(name(), L"getSigmoid - no sigmoid",
			   Error::ARG, __FILE__, __LINE__);
    }
    return sigmoid_d;
  }

  // get the SVM distance (this method will check dimensions first)
  //
  double getDistance(const VectorFloat& data);

  static boolean generateSvmFeatures(Vector<Vector<VectorFloat> >& segm_feat,
				     const Vector<VectorFloat>& frame_feat);

  static boolean generateSvmSegment(VectorFloat& segm_feat,
				    long feat_dim,
				    long start_frame, long end_frame,
				    boolean duration_flag,
				    const Vector<VectorFloat>& frame_feat);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  required for the base class interface contract
  //
  //--------------------------------------------------------------------------

  // StatisticalModelBase required methods
  //
  boolean assign(const StatisticalModelBase& arg);
  boolean eq(const StatisticalModelBase& arg) const;

  // set methods
  //
  boolean setMode(MODE arg);

  // method: className
  //
  const String& className() const {
    return CLASS_NAME;
  }

  // initialization methods
  //
  boolean init();

  // method: getLikelihood
  //
  float getLikelihood(const VectorFloat& input) {
    return Integral::exp(getLogLikelihood(input));
  }

  // computational methods
  //
  float getLogLikelihood(const VectorFloat& input);

  //---------------------------------------------------------------------------
  //
  // class-specific public methods:
  //  accumulate and update methods needed for training models
  //
  //---------------------------------------------------------------------------

  // method: getMean
  //
  boolean getMean(VectorFloat& mean) {
    return Error::handle(name(), L"getMean", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: getCovariance
  //
  boolean getCovariance(MatrixFloat& cov) {
    return Error::handle(name(), L"getCovariance", Error::NOT_IMPLEM,
			 __FILE__, __LINE__);
  }

  // method: resetAccumulators
  //
  boolean resetAccumulators() {
    return Error::handle(name(), L"resetAccumulators",
			 Error::NOT_IMPLEM, __FILE__, __LINE__);
  }

  // method: getOccupancy
  //
  double getOccupancy() {
    return Error::handle(name(), L"getOccupancy",
			 Error::NOT_IMPLEM, __FILE__, __LINE__);
  }

  // method: setOccupancy
  //
  boolean setOccupancy(double arg) {
    return Error::handle(name(), L"setOccupancy",
			 Error::NOT_IMPLEM, __FILE__, __LINE__);
  }  

  // method: getAccessCount
  //
  long getAccessCount() {
    return Error::handle(name(), L"getAccessCount",
			 Error::NOT_IMPLEM, __FILE__, __LINE__);
  }

  // method: setAccessCount
  //
  boolean setAccessCount(long arg) {
    return Error::handle(name(), L"setAccessCount",
			 Error::NOT_IMPLEM, __FILE__, __LINE__);
  }  
  
  // method that accumulates the statistics for the model which are
  // needed to update the model parameters during training
  //
  boolean accumulate(VectorDouble& param,
		     VectorFloat& data, boolean precomp);

  // method that updates the statistical model parameters using the
  // accumulated statistics during training
  //  
  boolean update(VectorFloat& varfloor, long min_count);

  // methods that initializes the statistical model parameters using
  // accumulated feature vectors
  //
  boolean accumulate(VectorFloat& data);    
  boolean initialize(VectorFloat& param);

  boolean loadSvmLight(const String& input_filename);
  
  //---------------------------------------------------------------------------
  //
  // private methods
  //
  //---------------------------------------------------------------------------
private:
};

// end of include file
// 
#endif
