// @(#)root/mlp:$Name:  $:$Id: TMultiLayerPerceptron.h,v 1.11 2006/05/26 15:13:02 rdm Exp $
// Author: Christophe.Delaere@cern.ch   20/07/03

/*************************************************************************
 * Copyright (C) 1995-2003, Rene Brun and Fons Rademakers.               *
 * All rights reserved.                                                  *
 *                                                                       *
 * For the licensing terms see $ROOTSYS/LICENSE.                         *
 * For the list of contributors see $ROOTSYS/README/CREDITS.             *
 *************************************************************************/

#ifndef ROOT_TMultiLayerPerceptron
#define ROOT_TMultiLayerPerceptron

#ifndef ROOT_TObject
#include "TObject.h"
#endif
#ifndef ROOT_TString
#include "TString.h"
#endif
#ifndef ROOT_TObjArray
#include "TObjArray.h"
#endif
#ifndef ROOT_TMatrixD
#include "TMatrixD.h"
#endif
#ifndef ROOT_TNeuron
#include "TNeuron.h"
#endif

class TTree;
class TEventList;
class TTreeFormula;
class TTreeFormulaManager;

//____________________________________________________________________
//
// TMultiLayerPerceptron
//
// This class decribes a Neural network.
// There are facilities to train the network and use the output.
//
// The input layer is made of inactive neurons (returning the
// normalized input), hidden layers are made of sigmoids and output
// neurons are linear.
//
// The basic input is a TTree and two (training and test) TEventLists.
// For classification jobs, a branch (maybe in a TFriend) must contain
// the expected output.
// 6 learning methods are available: kStochastic, kBatch,
// kSteepestDescent, kRibierePolak, kFletcherReeves and kBFGS.
//
// This implementation is *inspired* from the mlpfit package from
// J.Schwindling et al.
//
//____________________________________________________________________

class TMultiLayerPerceptron : public TObject {
 friend class TMLPAnalyzer;

 public:
   enum LearningMethod { kStochastic, kBatch, kSteepestDescent,
                         kRibierePolak, kFletcherReeves, kBFGS };
   enum DataSet { kTraining, kTest };
   TMultiLayerPerceptron();
   TMultiLayerPerceptron(const char* layout, TTree* data = 0,
                         const char* training = "Entry$%2==0",
                         const char* test = "",
                         TNeuron::NeuronType type = TNeuron::kSigmoid,
                         const char* extF = "", const char* extD  = "");
   TMultiLayerPerceptron(const char* layout,
                         const char* weight, TTree* data = 0,
                         const char* training = "Entry$%2==0",
                         const char* test = "",
                         TNeuron::NeuronType type = TNeuron::kSigmoid,
                         const char* extF = "", const char* extD  = "");
   TMultiLayerPerceptron(const char* layout, TTree* data,
                         TEventList* training,
                         TEventList* test,
                         TNeuron::NeuronType type = TNeuron::kSigmoid,
                         const char* extF = "", const char* extD  = "");
   TMultiLayerPerceptron(const char* layout,
                         const char* weight, TTree* data,
                         TEventList* training,
                         TEventList* test,
                         TNeuron::NeuronType type = TNeuron::kSigmoid,
                         const char* extF = "", const char* extD  = "");
   virtual ~TMultiLayerPerceptron();
   void SetData(TTree*);
   void SetTrainingDataSet(TEventList* train);
   void SetTestDataSet(TEventList* test);
   void SetTrainingDataSet(const char* train);
   void SetTestDataSet(const char* test);
   void SetLearningMethod(TMultiLayerPerceptron::LearningMethod method);
   void SetEventWeight(const char*);
   void Train(Int_t nEpoch, Option_t* option = "text");
   Double_t Result(Int_t event, Int_t index = 0) const;
   Double_t GetError(Int_t event) const;
   Double_t GetError(TMultiLayerPerceptron::DataSet set) const;
   void ComputeDEDw() const;
   void Randomize() const;
   void SetEta(Double_t eta);
   void SetEpsilon(Double_t eps);
   void SetDelta(Double_t delta);
   void SetEtaDecay(Double_t ed);
   void SetTau(Double_t tau);
   void SetReset(Int_t reset);
   inline Double_t GetEta()      const { return fEta; }
   inline Double_t GetEpsilon()  const { return fEpsilon; }
   inline Double_t GetDelta()    const { return fDelta; }
   inline Double_t GetEtaDecay() const { return fEtaDecay; }
   inline Double_t GetTau()      const { return fTau; }
   inline Int_t GetReset()       const { return fReset; }
   inline TString GetStructure() const { return fStructure; }
   inline TNeuron::NeuronType GetType() const { return fType; }
   void DrawResult(Int_t index = 0, Option_t* option = "test") const;
   void DumpWeights(Option_t* filename = "-") const;
   void LoadWeights(Option_t* filename = "");
   Double_t Evaluate(Int_t index, Double_t* params) const;
   void Export(Option_t* filename = "NNfunction", Option_t* language = "C++") const;
   virtual void Draw(Option_t *option="");

 protected:
   void AttachData();
   void BuildNetwork();
   void GetEntry(Int_t) const;
   // it's a choice not to force learning function being const, even if possible
   void MLP_Stochastic(Double_t*);
   void MLP_Batch(Double_t*);
   Bool_t LineSearch(Double_t*, Double_t*);
   void SteepestDir(Double_t*);
   void ConjugateGradientsDir(Double_t*, Double_t);
   void SetGammaDelta(TMatrixD&, TMatrixD&, Double_t*);
   bool GetBFGSH(TMatrixD&, TMatrixD &, TMatrixD&);
   void BFGSDir(TMatrixD&, Double_t*);
   Double_t DerivDir(Double_t*);
   Double_t GetCrossEntropyBinary() const;
   Double_t GetCrossEntropy() const;
   Double_t GetSumSquareError() const;

 private:
   void ExpandStructure();
   void BuildFirstLayer(TString&);
   void BuildHiddenLayers(TString&);
   void BuildLastLayer(TString&, Int_t);
   void Shuffle(Int_t*, Int_t) const;
   void MLP_Line(Double_t*, Double_t*, Double_t);

   TTree* fData;                   //! pointer to the tree used as datasource
   Int_t fCurrentTree;             //! index of the current tree in a chain
   Double_t fCurrentTreeWeight;    //! weight of the current tree in a chain
   TObjArray fNetwork;             // Collection of all the neurons in the network
   TObjArray fFirstLayer;          // Collection of the input neurons; subset of fNetwork
   TObjArray fLastLayer;           // Collection of the output neurons; subset of fNetwork
   TObjArray fSynapses;            // Collection of all the synapses in the network
   TString fStructure;             // String containing the network structure
   TString fWeight;                // String containing the event weight
   TNeuron::NeuronType fType;      // Type of hidden neurons
   TNeuron::NeuronType fOutType;   // Type of output neurons
   TString fextF;                  // String containing the function name
   TString fextD;                  // String containing the derivative name
   TEventList *fTraining;          //! EventList defining the events in the training dataset
   TEventList *fTest;              //! EventList defining the events in the test dataset
   LearningMethod fLearningMethod; //! The Learning Method
   TTreeFormula* fEventWeight;     //! formula representing the event weight
   TTreeFormulaManager* fManager;  //! TTreeFormulaManager for the weight and neurons
   Double_t fEta;                  //! Eta - used in stochastic minimisation - Default=0.1
   Double_t fEpsilon;              //! Epsilon - used in stochastic minimisation - Default=0.
   Double_t fDelta;                //! Delta - used in stochastic minimisation - Default=0.
   Double_t fEtaDecay;             //! EtaDecay - Eta *= EtaDecay at each epoch - Default=1.
   Double_t fTau;                  //! Tau - used in line search - Default=3.
   Double_t fLastAlpha;            //! internal parameter used in line search
   Int_t fReset;                   //! number of epochs between two resets of the search direction to the steepest descent - Default=50
   Bool_t fTrainingOwner;          //! internal flag whether one has to delete fTraining or not
   Bool_t fTestOwner;              //! internal flag whether one has to delete fTest or not
   ClassDef(TMultiLayerPerceptron, 4)	// a Neural Network
};

#endif
