#include "TMVA/ClassifierFactory.h"
#include "TMVA/MethodHMatrix.h"
#include "TMVA/Tools.h"
#include "TMatrix.h"
#include "Riostream.h"
#include <algorithm>
REGISTER_METHOD(HMatrix)
ClassImp(TMVA::MethodHMatrix)
/*
H-Matrix method, which is implemented as a simple comparison of
chi-squared estimators for signal and background, taking into
account the linear correlations between the input variables
This MVA approach is used by the DØ collaboration (FNAL) for the
purpose of electron identification (see, eg.,
<a href="http://arxiv.org/abs/hep-ex/9507007">hep-ex/9507007</a>).
As it is implemented in TMVA, it is usually equivalent or worse than
the Fisher-Mahalanobis discriminant, and it has only been added for
the purpose of completeness.
Two χ<sup>2</sup> estimators are computed for an event, each one
for signal and background, using the estimates for the means and
covariance matrices obtained from the training sample:<br>
<center>
<img vspace=6 src="gif/tmva_chi2.gif" align="bottom" >
</center>
TMVA then uses as normalised analyser for event (<i>i</i>) the ratio:
(<i>χ<sub>S</sub>(i)<sup>2</sup> − χ<sub>B</sub><sup>2</sup>(i)</i>)
(<i>χ<sub>S</sub><sup>2</sup>(i) + χ<sub>B</sub><sup>2</sup>(i)</i>).
*/
//End_Html
TMVA::MethodHMatrix::MethodHMatrix( const TString& jobName,
const TString& methodTitle,
DataSetInfo& theData,
const TString& theOption,
TDirectory* theTargetDir )
: TMVA::MethodBase( jobName, Types::kHMatrix, methodTitle, theData, theOption, theTargetDir )
,fInvHMatrixS(0)
,fInvHMatrixB(0)
,fVecMeanS(0)
,fVecMeanB(0)
{
}
TMVA::MethodHMatrix::MethodHMatrix( DataSetInfo& theData,
const TString& theWeightFile,
TDirectory* theTargetDir )
: TMVA::MethodBase( Types::kHMatrix, theData, theWeightFile, theTargetDir )
,fInvHMatrixS(0)
,fInvHMatrixB(0)
,fVecMeanS(0)
,fVecMeanB(0)
{
}
void TMVA::MethodHMatrix::Init( void )
{
fInvHMatrixS = new TMatrixD( GetNvar(), GetNvar() );
fInvHMatrixB = new TMatrixD( GetNvar(), GetNvar() );
fVecMeanS = new TVectorD( GetNvar() );
fVecMeanB = new TVectorD( GetNvar() );
SetSignalReferenceCut( 0.0 );
}
TMVA::MethodHMatrix::~MethodHMatrix( void )
{
if (NULL != fInvHMatrixS) delete fInvHMatrixS;
if (NULL != fInvHMatrixB) delete fInvHMatrixB;
if (NULL != fVecMeanS ) delete fVecMeanS;
if (NULL != fVecMeanB ) delete fVecMeanB;
}
Bool_t TMVA::MethodHMatrix::HasAnalysisType( Types::EAnalysisType type, UInt_t numberClasses, UInt_t )
{
if( type == Types::kClassification && numberClasses == 2 ) return kTRUE;
return kFALSE;
}
void TMVA::MethodHMatrix::DeclareOptions()
{
}
void TMVA::MethodHMatrix::ProcessOptions()
{
}
void TMVA::MethodHMatrix::Train( void )
{
ComputeCovariance( kTRUE, fInvHMatrixS );
ComputeCovariance( kFALSE, fInvHMatrixB );
if (TMath::Abs(fInvHMatrixS->Determinant()) < 10E-24) {
Log() << kWARNING << "<Train> H-matrix S is almost singular with deterinant= "
<< TMath::Abs(fInvHMatrixS->Determinant())
<< " did you use the variables that are linear combinations or highly correlated ???"
<< Endl;
}
if (TMath::Abs(fInvHMatrixB->Determinant()) < 10E-24) {
Log() << kWARNING << "<Train> H-matrix B is almost singular with deterinant= "
<< TMath::Abs(fInvHMatrixB->Determinant())
<< " did you use the variables that are linear combinations or highly correlated ???"
<< Endl;
}
if (TMath::Abs(fInvHMatrixS->Determinant()) < 10E-120) {
Log() << kFATAL << "<Train> H-matrix S is singular with deterinant= "
<< TMath::Abs(fInvHMatrixS->Determinant())
<< " did you use the variables that are linear combinations ???"
<< Endl;
}
if (TMath::Abs(fInvHMatrixB->Determinant()) < 10E-120) {
Log() << kFATAL << "<Train> H-matrix B is singular with deterinant= "
<< TMath::Abs(fInvHMatrixB->Determinant())
<< " did you use the variables that are linear combinations ???"
<< Endl;
}
fInvHMatrixS->Invert();
fInvHMatrixB->Invert();
}
void TMVA::MethodHMatrix::ComputeCovariance( Bool_t isSignal, TMatrixD* mat )
{
Data()->SetCurrentType(Types::kTraining);
const UInt_t nvar = DataInfo().GetNVariables();
UInt_t ivar, jvar;
TVectorD vec(nvar); vec *= 0;
TMatrixD mat2(nvar, nvar); mat2 *= 0;
Double_t sumOfWeights = 0;
Double_t *xval = new Double_t[nvar];
for (Int_t i=0, iEnd=Data()->GetNEvents(); i<iEnd; ++i) {
const Event* origEvt = Data()->GetEvent(i);
Double_t weight = origEvt->GetWeight();
if (IgnoreEventsWithNegWeightsInTraining() && weight <= 0) continue;
if (DataInfo().IsSignal(origEvt) != isSignal) continue;
GetTransformationHandler().SetTransformationReferenceClass( origEvt->GetClass() );
const Event* ev = GetTransformationHandler().Transform( origEvt );
sumOfWeights += weight;
for (ivar=0; ivar<nvar; ivar++) xval[ivar] = ev->GetValue(ivar);
for (ivar=0; ivar<nvar; ivar++) {
vec(ivar) += xval[ivar]*weight;
mat2(ivar, ivar) += (xval[ivar]*xval[ivar])*weight;
for (jvar=ivar+1; jvar<nvar; jvar++) {
mat2(ivar, jvar) += (xval[ivar]*xval[jvar])*weight;
mat2(jvar, ivar) = mat2(ivar, jvar);
}
}
}
for (ivar=0; ivar<nvar; ivar++) {
if (isSignal) (*fVecMeanS)(ivar) = vec(ivar)/sumOfWeights;
else (*fVecMeanB)(ivar) = vec(ivar)/sumOfWeights;
for (jvar=0; jvar<nvar; jvar++) {
(*mat)(ivar, jvar) = mat2(ivar, jvar)/sumOfWeights - vec(ivar)*vec(jvar)/(sumOfWeights*sumOfWeights);
}
}
delete [] xval;
}
Double_t TMVA::MethodHMatrix::GetMvaValue( Double_t* err, Double_t* errUpper )
{
Double_t s = GetChi2( Types::kSignal );
Double_t b = GetChi2( Types::kBackground );
if (s+b < 0) Log() << kFATAL << "big trouble: s+b: " << s+b << Endl;
NoErrorCalc(err, errUpper);
return (b - s)/(s + b);
}
Double_t TMVA::MethodHMatrix::GetChi2( Types::ESBType type )
{
const Event* origEvt = fTmpEvent ? fTmpEvent:Data()->GetEvent();
UInt_t ivar(0), jvar(0), nvar(GetNvar());
std::vector<Double_t> val( nvar );
if (type==Types::kSignal)
GetTransformationHandler().SetTransformationReferenceClass( fSignalClass );
else
GetTransformationHandler().SetTransformationReferenceClass( fBackgroundClass );
const Event* ev = GetTransformationHandler().Transform( origEvt );
for (ivar=0; ivar<nvar; ivar++) val[ivar] = ev->GetValue( ivar );
Double_t chi2 = 0;
for (ivar=0; ivar<nvar; ivar++) {
for (jvar=0; jvar<nvar; jvar++) {
if (type == Types::kSignal)
chi2 += ( (val[ivar] - (*fVecMeanS)(ivar))*(val[jvar] - (*fVecMeanS)(jvar))
* (*fInvHMatrixS)(ivar,jvar) );
else
chi2 += ( (val[ivar] - (*fVecMeanB)(ivar))*(val[jvar] - (*fVecMeanB)(jvar))
* (*fInvHMatrixB)(ivar,jvar) );
}
}
if (chi2 < 0) Log() << kFATAL << "<GetChi2> negative chi2: " << chi2 << Endl;
return chi2;
}
void TMVA::MethodHMatrix::AddWeightsXMLTo( void* parent ) const
{
void* wght = gTools().AddChild(parent, "Weights");
gTools().WriteTVectorDToXML( wght, "VecMeanS", fVecMeanS );
gTools().WriteTVectorDToXML( wght, "VecMeanB", fVecMeanB );
gTools().WriteTMatrixDToXML( wght, "InvHMatS", fInvHMatrixS );
gTools().WriteTMatrixDToXML( wght, "InvHMatB", fInvHMatrixB );
}
void TMVA::MethodHMatrix::ReadWeightsFromXML( void* wghtnode )
{
void* descnode = gTools().GetChild(wghtnode);
gTools().ReadTVectorDFromXML( descnode, "VecMeanS", fVecMeanS );
descnode = gTools().GetNextChild(descnode);
gTools().ReadTVectorDFromXML( descnode, "VecMeanB", fVecMeanB );
descnode = gTools().GetNextChild(descnode);
gTools().ReadTMatrixDFromXML( descnode, "InvHMatS", fInvHMatrixS );
descnode = gTools().GetNextChild(descnode);
gTools().ReadTMatrixDFromXML( descnode, "InvHMatB", fInvHMatrixB );
}
void TMVA::MethodHMatrix::ReadWeightsFromStream( std::istream& istr )
{
UInt_t ivar,jvar;
TString var, dummy;
istr >> dummy;
for (ivar=0; ivar<GetNvar(); ivar++)
istr >> (*fVecMeanS)(ivar) >> (*fVecMeanB)(ivar);
for (ivar=0; ivar<GetNvar(); ivar++)
for (jvar=0; jvar<GetNvar(); jvar++)
istr >> (*fInvHMatrixS)(ivar,jvar);
for (ivar=0; ivar<GetNvar(); ivar++)
for (jvar=0; jvar<GetNvar(); jvar++)
istr >> (*fInvHMatrixB)(ivar,jvar);
}
void TMVA::MethodHMatrix::MakeClassSpecific( std::ostream& fout, const TString& className ) const
{
fout << " // arrays of input evt vs. variable " << std::endl;
fout << " double fInvHMatrixS[" << GetNvar() << "][" << GetNvar() << "]; // inverse H-matrix (signal)" << std::endl;
fout << " double fInvHMatrixB[" << GetNvar() << "][" << GetNvar() << "]; // inverse H-matrix (background)" << std::endl;
fout << " double fVecMeanS[" << GetNvar() << "]; // vector of mean values (signal)" << std::endl;
fout << " double fVecMeanB[" << GetNvar() << "]; // vector of mean values (background)" << std::endl;
fout << " " << std::endl;
fout << " double GetChi2( const std::vector<double>& inputValues, int type ) const;" << std::endl;
fout << "};" << std::endl;
fout << " " << std::endl;
fout << "void " << className << "::Initialize() " << std::endl;
fout << "{" << std::endl;
fout << " // init vectors with mean values" << std::endl;
for (UInt_t ivar=0; ivar<GetNvar(); ivar++) {
fout << " fVecMeanS[" << ivar << "] = " << (*fVecMeanS)(ivar) << ";" << std::endl;
fout << " fVecMeanB[" << ivar << "] = " << (*fVecMeanB)(ivar) << ";" << std::endl;
}
fout << " " << std::endl;
fout << " // init H-matrices" << std::endl;
for (UInt_t ivar=0; ivar<GetNvar(); ivar++) {
for (UInt_t jvar=0; jvar<GetNvar(); jvar++) {
fout << " fInvHMatrixS[" << ivar << "][" << jvar << "] = "
<< (*fInvHMatrixS)(ivar,jvar) << ";" << std::endl;
fout << " fInvHMatrixB[" << ivar << "][" << jvar << "] = "
<< (*fInvHMatrixB)(ivar,jvar) << ";" << std::endl;
}
}
fout << "}" << std::endl;
fout << " " << std::endl;
fout << "inline double " << className << "::GetMvaValue__( const std::vector<double>& inputValues ) const" << std::endl;
fout << "{" << std::endl;
fout << " // returns the H-matrix signal estimator" << std::endl;
fout << " std::vector<double> inputValuesSig = inputValues;" << std::endl;
fout << " std::vector<double> inputValuesBgd = inputValues;" << std::endl;
if (GetTransformationHandler().GetTransformationList().GetSize() != 0) {
UInt_t signalClass =DataInfo().GetClassInfo("Signal")->GetNumber();
UInt_t backgroundClass=DataInfo().GetClassInfo("Background")->GetNumber();
fout << " Transform(inputValuesSig," << signalClass << ");" << std::endl;
fout << " Transform(inputValuesBgd," << backgroundClass << ");" << std::endl;
}
fout << " double s = GetChi2( inputValuesSig, " << Types::kSignal << " );" << std::endl;
fout << " double b = GetChi2( inputValuesBgd, " << Types::kBackground << " );" << std::endl;
fout << " " << std::endl;
fout << " if (s+b <= 0) std::cout << \"Problem in class " << className << "::GetMvaValue__: s+b = \"" << std::endl;
fout << " << s+b << \" <= 0 \" << std::endl;" << std::endl;
fout << " " << std::endl;
fout << " return (b - s)/(s + b);" << std::endl;
fout << "}" << std::endl;
fout << " " << std::endl;
fout << "inline double " << className << "::GetChi2( const std::vector<double>& inputValues, int type ) const" << std::endl;
fout << "{" << std::endl;
fout << " // compute chi2-estimator for event according to type (signal/background)" << std::endl;
fout << " " << std::endl;
fout << " size_t ivar,jvar;" << std::endl;
fout << " double chi2 = 0;" << std::endl;
fout << " for (ivar=0; ivar<GetNvar(); ivar++) {" << std::endl;
fout << " for (jvar=0; jvar<GetNvar(); jvar++) {" << std::endl;
fout << " if (type == " << Types::kSignal << ") " << std::endl;
fout << " chi2 += ( (inputValues[ivar] - fVecMeanS[ivar])*(inputValues[jvar] - fVecMeanS[jvar])" << std::endl;
fout << " * fInvHMatrixS[ivar][jvar] );" << std::endl;
fout << " else" << std::endl;
fout << " chi2 += ( (inputValues[ivar] - fVecMeanB[ivar])*(inputValues[jvar] - fVecMeanB[jvar])" << std::endl;
fout << " * fInvHMatrixB[ivar][jvar] );" << std::endl;
fout << " }" << std::endl;
fout << " } // loop over variables " << std::endl;
fout << " " << std::endl;
fout << " // sanity check" << std::endl;
fout << " if (chi2 < 0) std::cout << \"Problem in class " << className << "::GetChi2: chi2 = \"" << std::endl;
fout << " << chi2 << \" < 0 \" << std::endl;" << std::endl;
fout << " " << std::endl;
fout << " return chi2;" << std::endl;
fout << "}" << std::endl;
fout << " " << std::endl;
fout << "// Clean up" << std::endl;
fout << "inline void " << className << "::Clear() " << std::endl;
fout << "{" << std::endl;
fout << " // nothing to clear" << std::endl;
fout << "}" << std::endl;
}
void TMVA::MethodHMatrix::GetHelpMessage() const
{
Log() << Endl;
Log() << gTools().Color("bold") << "--- Short description:" << gTools().Color("reset") << Endl;
Log() << Endl;
Log() << "The H-Matrix classifier discriminates one class (signal) of a feature" << Endl;
Log() << "vector from another (background). The correlated elements of the" << Endl;
Log() << "vector are assumed to be Gaussian distributed, and the inverse of" << Endl;
Log() << "the covariance matrix is the H-Matrix. A multivariate chi-squared" << Endl;
Log() << "estimator is built that exploits differences in the mean values of" << Endl;
Log() << "the vector elements between the two classes for the purpose of" << Endl;
Log() << "discrimination." << Endl;
Log() << Endl;
Log() << gTools().Color("bold") << "--- Performance optimisation:" << gTools().Color("reset") << Endl;
Log() << Endl;
Log() << "The TMVA implementation of the H-Matrix classifier has been shown" << Endl;
Log() << "to underperform in comparison with the corresponding Fisher discriminant," << Endl;
Log() << "when using similar assumptions and complexity. Its use is therefore" << Endl;
Log() << "depreciated. Only in cases where the background model is strongly" << Endl;
Log() << "non-Gaussian, H-Matrix may perform better than Fisher. In such" << Endl;
Log() << "occurrences the user is advised to employ non-linear classifiers. " << Endl;
Log() << Endl;
Log() << gTools().Color("bold") << "--- Performance tuning via configuration options:" << gTools().Color("reset") << Endl;
Log() << Endl;
Log() << "None" << Endl;
}