/***************************************************************************
 *   Copyright (C) 2005 by Robot Group Leipzig                             *
 *    martius@informatik.uni-leipzig.de                                    *
 *    fhesse@informatik.uni-leipzig.de                                     *
 *    der@informatik.uni-leipzig.de                                        *
 *    guettler@mis.mpg.de                                                  *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU 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 General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU 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.             *
 *                                                                         *
 *   $Log: mutualinformationcontroller.h,v $
 *   Revision 1.6  2008/04/17 14:54:45  martius
 *   randomGen added, which is a random generator with long period and an
 *    internal state. Each Agent has an instance and passed it to the controller
 *    and the wiring. This is good for
 *   a) repeatability on agent basis,
 *   b) parallel execution as done in ode_robots
 *
 *   Revision 1.5  2007/12/12 10:26:14  der
 *   MI: calculation of H(X) and H(Y|X) added
 *
 *   Revision 1.4  2007/09/27 10:58:52  robot3
 *   MI: disabled old calculation method, additionally minor bugfixes
 *   std_adds.h: Template abs is now here (instead of mathutils.h)
 *
 *   Revision 1.3  2007/09/25 07:50:12  robot3
 *   old MI calculation disabled (but is still present)
 *   new MI update rule is used, costs O(1)
 *
 *   Revision 1.2  2007/04/19 13:48:20  robot3
 *   inserted new MI update function - needs O(1) instead of O(n^2)
 *
 *   Revision 1.1  2007/03/22 08:16:52  robot3
 *   passive controller for calculating the mutual information from sensor values.
 *   See ode_robots/simulations/MI_Simu for an example.
 *
 
 *                                                                         *
 ***************************************************************************/
#ifndef __MUTUALINFORMATIONCONTROLLER_H
#define __MUTUALINFORMATIONCONTROLLER_H

#include "abstractcontroller.h"
#include "matrix.h"

/**
 * This is a controller who is passive at the moment, that means, he will not
 * generate any motor values. This controller calculates the mutual information
 * from one to the next time step. Note that many steps are neccessary for a good
 * prediction of the mutual information.
 */
class MutualInformationController : public AbstractController
{

public:

  /**
   * Constructs the mutual information controller. At this time the
   * controller is not yet initialized (this does the Agent).
   * @param minSensorValue is the minimum value the sensors can become
   * @param maxSensorValue is the maximum value the sensors can become
   * @param sensorIntervalCount is the number of the intervals used. This
   * is important for generating the internal matrices.
   *
   */
  MutualInformationController(int sensorIntervalCount, double minSensorValue=-1, double maxSensorValue=1);

  virtual ~MutualInformationController() {};

  virtual double& getMI(int index) { assert(index<sensorNumber); return MI[index]; }
  
  virtual double& getH_x(int index) { assert(index<sensorNumber); return H_x[index]; }
  
  virtual double& getH_yx(int index) { assert(index<sensorNumber); return H_yx[index]; }
  
  virtual double& getH_Xsi(int index) { assert(index<sensorNumber); return H_Xsi[index]; }
  
  /**
  * we like to calculate the entropy of the xsi, therefore we need for the (self calculated) update rule
  * x_t+1=-a * tanh(c * x_t) the a and c, which should be set in the main.cpp.
  */
  virtual void setAandCandCalcH_xsi(double ainit, double cinit);


  /****************************************************************************/
  /*	BEGIN methods of AbstractController                                 */
  /****************************************************************************/

  /** initialisation of the controller with the given sensor/ motornumber
   * Must NORMALLY be called before use. For all ControllerAdapters
   * call first AbstractControllerAdapter::init(sensornumber,motornumber)
   * if you overwrite this method
   */
  virtual void init(int sensornumber, int motornumber, RandGen* randGen = 0);

  /** @return Number of sensors the controller
      was initialised with or 0 if not initialised */
  virtual int getSensorNumber() const { return sensorNumber; }

  /** @return Number of motors the controller
      was initialised with or 0 if not initialised */
  virtual int getMotorNumber() const { return motorNumber; }

  /** performs one step (includes learning).
      Calculates motor commands from sensor inputs.
  @param sensors sensors inputs scaled to [-1,1]
  @param sensornumber length of the sensor array
  @param motors motors outputs. MUST have enough space for motor values!
  @param motornumber length of the provided motor array
  */
  virtual void step(const sensor* sensors, int sensornumber,
                    motor* motors, int motornumber);

  /** performs one step without learning.
  @see step
  */
  virtual void stepNoLearning(const sensor* sensors , int sensornumber,
                              motor* motors, int motornumber);

  /****************************************************************************/
  /*	END methods of AbstractController                                   */
  /****************************************************************************/


  /****************************************************************************/
  /*	BEGIN methods of Inspectable                                          	*/
  /****************************************************************************/

  /** The list of the names of all internal parameters given by getInternalParams().
      The naming convention is "v[i]" for vectors
       and "A[i][j]" for matrices, where i, j start at 0.
  @return: list of keys
   */
  virtual iparamkeylist getInternalParamNames() const;

  /** @return: list of values
  */
  virtual iparamvallist getInternalParams() const;

  /****************************************************************************/
  /*	END methods of Inspectable                                       	    */
  /****************************************************************************/

  /****************************************************************************/
  /*	BEGIN methods of Storeable                                           	*/
  /****************************************************************************/

  /** stores the object to the given file stream (binary).
  */
  virtual bool store(FILE* f) const { return true; }

  /** loads the object from the given file stream (binary).
  */
  virtual bool restore(FILE* f) { return true; }

  /****************************************************************************/
  /*	END methods of Storeable                                          	    */
  /****************************************************************************/


  /****************************************************************************/
  /*	BEGIN methods of Configurable                                          	    */
  /****************************************************************************/

  Configurable::paramval getParam(const paramkey& key) const;

  bool setParam(const paramkey& key, paramval val);

  Configurable::paramlist getParamList() const;

  /****************************************************************************/
  /*	END methods of Configurable                                          	    */
  /****************************************************************************/


protected:
  paramval showF;
  paramval showP;
  bool initialized; // tells wether the controller is already initialized by the agent
  bool useXsiCalculation;

  double minSensorValue;
  double maxSensorValue;
  int sensorIntervalCount;
  int sensorNumber;
  int motorNumber;
  std::list<matrix::Matrix*> freqMatrixList; // stores the number of occurances of t-1 to t (frequency)
  std::list<matrix::Matrix*> probMatrixList; // stores the probability of state to state occurances of t-1 to t
  std::list<matrix::Matrix*> xsiFreqMatrixList; // stores the number of occurances of xsi(x) (frequency)
  
  double* oldSensorStates; // stores the sensor states for next step (t+1)
  int t; // indicates the step (time)

  double* MI; // mutual information = MI(X_{t-1},X_t)
  double* H_x; // H(x) = H(X_{t-1})
  double* H_yx; // H(X_t|X_{t-1})
  double* H_Xsi; // H(Xsi)
  
  // the next two variables are only used if Xsi_x is calculated
  double ainit;
  double cinit;
  

  /**
   * Calculates the mutual information
   * This is made by normal formula, which
   * needs O(n^2) costs.
   */
  virtual void calculateMIs(double* MI);

    /**
   * Calculates the entropy of x
   * This is made by normal formula, which
   * needs O(n) costs.
   */
  virtual void calculateH_x(double* H);
  
  
  /**
   * Calculates the conditional entropy of y|x
   * This is made by normal formula, which
   * needs O(n²) costs.
   */
  virtual void calculateH_yx(double* H_yx);
  
  
  /**
  * Updates the xsi frequency matrix list
  */
  virtual void updateXsiFreqMatrixList(const sensor* sensors);
  
  /**
   * Calculates the entropy of H(Xsi)
   * This is made by normal formula, which
   * needs O(n) costs.
   */
  virtual void calculateH_Xsi(double* H_Xsi);
  
  
  
  /**
   * Updates the mutual information
   * This is made by a difference term, which
   * is added to the old MI(t-1).
   * This function needs the OLD MI(t-1) AND
   * the OLD F matrix, so update the MI with this
   * function first before updating the F matrix!
   * calculation costs: O(1)
   */
  virtual void updateMIs(const sensor* sensors);



  /**
   * Returns the appropiate state which belongs to the given sensorValue.
   */
  virtual int getState(double sensorValue);

};

#endif
