diff --git a/hist/CMakeLists.txt b/hist/CMakeLists.txt index 455ed789fe17d7c2f42f916e3c373a6e4707c866..d9547d3a58c8792b1ed41a8723342a246dfd7d67 100644 --- a/hist/CMakeLists.txt +++ b/hist/CMakeLists.txt @@ -2,6 +2,7 @@ Add_Subdirectory(hist) # special CMakeLists.txt Add_Subdirectory(histpainter) # special CMakeLists.txt Add_Subdirectory(spectrum) Add_Subdirectory(spectrumpainter) # special CMakeLists.txt +Add_Subdirectory(unfold) If(CMAKE_Fortran_COMPILER) Add_Subdirectory(hbook) EndIf(CMAKE_Fortran_COMPILER) diff --git a/hist/hist/inc/LinkDef.h b/hist/hist/inc/LinkDef.h index dfef00e18774cb7fd13c583bd509c855fd75a6e2..5a0d4ee81e8f92a261d0ce40660298824a4b19e5 100644 --- a/hist/hist/inc/LinkDef.h +++ b/hist/hist/inc/LinkDef.h @@ -160,10 +160,6 @@ #pragma link C++ class TVirtualGraphPainter+; #pragma link C++ class TVirtualFitter+; #pragma link C++ class TBackCompFitter+; -#pragma link C++ class TUnfold+; -#pragma link C++ class TUnfoldSys+; -#pragma link C++ class TUnfoldBinning+; -#pragma link C++ class TUnfoldDensity+; #pragma link C++ class TSVDUnfold+; #pragma link C++ class TEfficiency+; #pragma link C++ class TKDE+; diff --git a/hist/hist/src/TUnfoldBinning.cxx b/hist/hist/src/TUnfoldBinning.cxx deleted file mode 100644 index 120337f0446e9552519b66ff6e573320758d8d75..0000000000000000000000000000000000000000 --- a/hist/hist/src/TUnfoldBinning.cxx +++ /dev/null @@ -1,1748 +0,0 @@ -// Author: Stefan Schmitt -// DESY, 10/08/11 - -// Version 17.1, in parallel to changes in TUnfold -// -// History: -// Version 17.0, initial version, numbered in parallel to TUnfold - -/** \class TUnfoldBinning - \ingroup Hist - This class serves as a container of analysis bins - analysis bins are specified by defining the axes of a distribution. - It is also possible to have unconnected analysis bins without axis. - Multiple TUnfoldBinning objects may be arranged in a tree, - such that a full tree structure of histograms and bins is supported - - If you use this software, please consider the following citation - S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201] - - More documentation and updates are available on - http://www.desy.de/~sschmitt - - Functionality - - The class gives access to all analysis bins numbered in sequence. - Such a sequence of bins may be stored in a 1-dimension histogram. - Correlations between two TUnfoldBinning objects may be stored in - a 2-dimensional histogram. This type of ordering is required for - the TUnfold class. - - In addition, it is possible to have root histograms, using the - axes as defined with the distributions. Underflow/overflow bins - can be included or excluded when mapping bins on root histograms. - In addition, it is possible to collapse one of more axes when going - from a N-dimensional distribution to a root histogram. -*/ - -/* - This file is part of TUnfold. - - TUnfold 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 3 of the License, or - (at your option) any later version. - - TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. - */ - - -#include "TUnfoldBinning.h" -#include <TVectorD.h> -#include <TAxis.h> -#include <TString.h> -#include <iostream> -#include <fstream> -#include <TMath.h> -#include <TF1.h> -#include <TH1D.h> -#include <TH2D.h> -#include <TH3D.h> -#include <TList.h> -#include <TIterator.h> - -// #define DEBUG - -using namespace std; - -ClassImp(TUnfoldBinning) - -/********************* setup **************************/ - -void TUnfoldBinning::Initialize(Int_t nBins) -{ - // initialize variables - parentNode=0; - childNode=0; - nextNode=0; - prevNode=0; - fAxisList=new TObjArray(); - fAxisLabelList=new TObjArray(); - fAxisList->SetOwner(); - fAxisLabelList->SetOwner(); - fHasUnderflow=0; - fHasOverflow=0; - fDistributionSize=nBins; - fBinFactorFunction=0; - fBinFactorConstant=1.0; -} - -Int_t TUnfoldBinning::UpdateFirstLastBin(Bool_t startWithRootNode) -{ - // update fFirstBin and fLastBin members of this node and its children - // startWithRootNode: if true, start the update with the root node - if(startWithRootNode) { - return GetRootNode()->UpdateFirstLastBin(kFALSE); - } - if(GetPrevNode()) { - // if this is not the first node in a sequence, - // start with the end bin of the previous node - fFirstBin=GetPrevNode()->GetEndBin(); - } else if(GetParentNode()) { - // if this is the first node in a sequence but has a parent, - // start with the end bin of the parent's distribution - fFirstBin=GetParentNode()->GetStartBin()+ - GetParentNode()->GetDistributionNumberOfBins(); - } else { - // if this is the top level node, the first bin number is 1 - fFirstBin=1; - // ... unless the top level node is the only node - // ... with dimension=1 - // ... and there are no child nodes - // ... and there is an underflow bin - if((!GetChildNode())&&(GetDistributionDimension()==1)&& - (fHasUnderflow==1)) { - fFirstBin=0; - } - } - fLastBin=fFirstBin+fDistributionSize; - // now update count for all children - for(TUnfoldBinning *node=childNode;node;node=node->nextNode) { - fLastBin=node->UpdateFirstLastBin(kFALSE); - } - return fLastBin; -} - -TUnfoldBinning::TUnfoldBinning -(const char *name,Int_t nBins,const char *binNames) -: TNamed(name ? name : "",name ? name : "") -{ - // initialize a node with bins but without axis - // name: name of the node - // nBin: number of extra bins (could be zero) - // binNames: (optionally) names of the bins sepatared by ';' - Initialize(nBins); - if(binNames) { - TString nameString(binNames); - delete fAxisLabelList; - fAxisLabelList=nameString.Tokenize(";"); - } - UpdateFirstLastBin(); -} - -TUnfoldBinning::TUnfoldBinning -(const TAxis &axis,Int_t includeUnderflow,Int_t includeOverflow) -: TNamed(axis.GetName(),axis.GetTitle()) -{ - // create binning containing a distribution with one axis - // axis: the axis to represent - // includeUnderflow: include underflow bin - // includeOverflow: include overflow bin - Initialize(0); - AddAxis(axis,includeUnderflow,includeOverflow); - UpdateFirstLastBin(); -} - -TUnfoldBinning::~TUnfoldBinning(void) -{ - // delete all children - if(childNode) delete childNode; - // remove this node from the tree - if(GetParentNode() && (GetParentNode()->GetChildNode()==this)) { - parentNode->childNode=nextNode; - } - if(GetPrevNode()) prevNode->nextNode=nextNode; - if(GetNextNode()) nextNode->prevNode=prevNode; - delete fAxisList; - delete fAxisLabelList; -} - -TUnfoldBinning *TUnfoldBinning::AddBinning -(const char *name,Int_t nBins,const char *binNames) -{ - // add a binning as last daughter to this tree - // name: name of the node - // nBin: number of bins not belonging to a distribution (usually zero) - // binNames: (optionally) names of these bins sepatared by ';' - return AddBinning(new TUnfoldBinning(name,nBins,binNames)); -} - -TUnfoldBinning *TUnfoldBinning::AddBinning(TUnfoldBinning *binning) -{ - // add a binning as last daughter to this tree - // binning: pointer to the new binning - // return value: if succeeded, return "binning" - // otherwise return 0 - TUnfoldBinning *r=0; - if(binning->GetParentNode()) { - Error("binning \"%s\" already has parent \"%s\", can not be added to %s", - (char *)binning->GetName(), - (char *)binning->GetParentNode()->GetName(), - (char *)GetName()); - } else if(binning->GetPrevNode()) { - Error("binning \"%s\" has previous node \"%s\", can not be added to %s", - (char *)binning->GetName(), - (char *)binning->GetPrevNode()->GetName(), - (char *)GetName()); - } else if(binning->GetNextNode()) { - Error("binning \"%s\" has next node \"%s\", can not be added to %s", - (char *)binning->GetName(), - (char *)binning->GetNextNode()->GetName(), - (char *)GetName()); - } else { - r=binning; - binning->parentNode=this; - if(childNode) { - TUnfoldBinning *child=childNode; - // find last child - while(child->nextNode) { - child=child->nextNode; - } - // add as last child - child->nextNode=r; - r->prevNode=child; - } else { - childNode=r; - } - UpdateFirstLastBin(); - r=binning; - } - return r; -} - -Bool_t TUnfoldBinning::AddAxis -(const char *name,Int_t nBin,Double_t xMin,Double_t xMax, - Bool_t hasUnderflow,Bool_t hasOverflow) -{ - // add an axis with equidistant bins to the distribution - // name: name of the axis - // nBin: number of bins - // xMin: lower edge of the first bin - // xMax: upper edge of the last bin - // hasUnderflow: decide whether the axis has an underflow bin - // hasOverflow: decide whether the axis has an overflow bin - // return: true if the axis has been added - Bool_t r=kFALSE; - if(nBin<=0) { - Fatal("AddAxis","number of bins %d is not positive", - nBin); - } else if((!TMath::Finite(xMin))||(!TMath::Finite(xMax))|| - (xMin>=xMax)) { - Fatal("AddAxis","xmin=%f required to be smaller than xmax=%f", - xMin,xMax); - } else { - Double_t *binBorders=new Double_t[nBin+1]; - Double_t x=xMin; - Double_t dx=(xMax-xMin)/nBin; - for(Int_t i=0;i<=nBin;i++) { - binBorders[i]=x+i*dx; - } - r=AddAxis(name,nBin,binBorders,hasUnderflow,hasOverflow); - delete [] binBorders; - } - return r; -} - -Bool_t TUnfoldBinning::AddAxis -(const TAxis &axis,Bool_t hasUnderflow,Bool_t hasOverflow) -{ - // add an axis to the distribution - // axis: the axis - // hasUnderflow: decide whether the underflow bin should be included - // hasOverflow: decide whether the overflow bin should be included - // return: true if the axis has been added - // - // Note: axis labels are not imported - Int_t nBin=axis.GetNbins(); - Double_t *binBorders=new Double_t[nBin+1]; - for(Int_t i=0;i<nBin;i++) { - binBorders[i]=axis.GetBinLowEdge(i+1); - } - binBorders[nBin]=axis.GetBinUpEdge(nBin); - Bool_t r=AddAxis(axis.GetTitle(),nBin,binBorders,hasUnderflow,hasOverflow); - delete [] binBorders; - return r; -} - -Bool_t TUnfoldBinning::AddAxis -(const char *name,Int_t nBin,const Double_t *binBorders, - Bool_t hasUnderflow,Bool_t hasOverflow) -{ - // add an axis with the specified bin borders to the distribution - // name: name of the axis - // nBin: number of bins - // binBorders: array of bin borders, with nBin+1 elements - // hasUnderflow: decide whether the axis has an underflow bin - // hasOverflow: decide whether the axis has an overflow bin - Bool_t r=kFALSE; - if(HasUnconnectedBins()) { - Fatal("AddAxis","node already has %d bins without axis", - GetDistributionNumberOfBins()); - } else if(nBin<=0) { - Fatal("AddAxis","number of bins %d is not positive", - nBin); - } else { - TVectorD *bins=new TVectorD(nBin+1); - r=kTRUE; - for(Int_t i=0;i<=nBin;i++) { - (*bins)(i)=binBorders[i]; - if(!TMath::Finite((*bins)(i))) { - Fatal("AddAxis","bin border %d is not finite",i); - r=kFALSE; - } else if((i>0)&&((*bins)(i)<=(*bins)(i-1))) { - Fatal("AddAxis","bins not in order x[%d]=%f <= %f=x[%d]", - i,(*bins)(i),(*bins)(i-1),i-1); - r=kFALSE; - } - } - if(r) { - Int_t axis=fAxisList->GetEntriesFast(); - Int_t bitMask=1<<axis; - Int_t nBinUO=nBin; - if(hasUnderflow) { - fHasUnderflow |= bitMask; - nBinUO++; - } else { - fHasUnderflow &= ~bitMask; - } - if(hasOverflow) { - fHasOverflow |= bitMask; - nBinUO++; - } else { - fHasOverflow &= ~bitMask; - } - fAxisList->AddLast(bins); - fAxisLabelList->AddLast(new TObjString(name)); - if(!fDistributionSize) fDistributionSize=1; - fDistributionSize *= nBinUO; - UpdateFirstLastBin(); - } - } - return r; -} - -void TUnfoldBinning::PrintStream(ostream &out,Int_t indent) const -{ - // print some information about this binning tree - // out: stream to write to - // indent: initial indentation (sub-trees have indent+1) - for(Int_t i=0;i<indent;i++) out<<" "; - out<<"TUnfoldBinning \""<<GetName()<<"\" has "; - Int_t nBin=GetEndBin()-GetStartBin(); - if(nBin==1) { - out<<"1 bin"; - } else { - out<<nBin<<" bins"; - } - out<<" [" - <<GetStartBin()<<","<<GetEndBin()<<"] nTH1x=" - <<GetTH1xNumberOfBins() - <<"\n"; - if(GetDistributionNumberOfBins()) { - for(Int_t i=0;i<indent;i++) out<<" "; - out<<" distribution: "<<GetDistributionNumberOfBins()<<" bins\n"; - if(fAxisList->GetEntriesFast()) { - /* for(Int_t i=0;i<indent;i++) out<<" "; - out<<" axes:\n"; */ - for(Int_t axis=0;axis<GetDistributionDimension();axis++) { - for(Int_t i=0;i<indent;i++) out<<" "; - out<<" \"" - <<GetDistributionAxisLabel(axis) - <<"\" nbin="<<GetDistributionBinning(axis)->GetNrows()-1; - if(fHasUnderflow & (1<<axis)) out<<" plus underflow"; - if(fHasOverflow & (1<<axis)) out<<" plus overflow"; - out<<"\n"; - } - } else { - for(Int_t i=0;i<indent;i++) out<<" "; - out<<" no axis\n"; - for(Int_t i=0;i<indent;i++) out<<" "; - out<<" names: "; - for(Int_t ibin=0;(ibin<GetDistributionNumberOfBins())&& - (ibin<fAxisLabelList->GetEntriesFast());ibin++) { - if(ibin) out<<";"; - if(GetDistributionAxisLabel(ibin)) { - out<<GetDistributionAxisLabel(ibin); - } - } - out<<"\n"; - } - } - TUnfoldBinning const *child=GetChildNode(); - if(child) { - while(child) { - child->PrintStream(out,indent+1); - child=child->GetNextNode(); - } - } -} - -/********************* Navigation **********************/ - -TUnfoldBinning const *TUnfoldBinning::FindNode(char const *name) const -{ - // parse the tree and return a node with the given name - // name: the name of the node to find - TUnfoldBinning const *r=0; - if((!name)||(!TString(GetName()).CompareTo(name))) { - r=this; - } - for(TUnfoldBinning const *child=GetChildNode(); - (!r) && child;child=child->GetNextNode()) { - r=child->FindNode(name); - } - return r; -} - -TUnfoldBinning *TUnfoldBinning::GetRootNode(void) -{ - // return root node - TUnfoldBinning *node=this; - while(node->GetParentNode()) node=node->parentNode; - return node; -} - -TUnfoldBinning const *TUnfoldBinning::GetRootNode(void) const -{ - // return root node - TUnfoldBinning const *node=this; - while(node->GetParentNode()) node=node->GetParentNode(); - return node; -} - -/********************* Create THxx histograms **********/ - -TString TUnfoldBinning::BuildHistogramTitle -(const char *histogramName,const char *histogramTitle,Int_t const *axisList) -const -{ - // build a title - // input: - // histogramTitle : if this is non-zero, use that title - // otherwise: - // title=histogramName[;x[;y[;z]]] - // ?Axis : -2 stop adding text to the title - // -1 add name of this node - // >=0 use name of the corresponding axis - TString r; - if(histogramTitle) { - r=histogramTitle; - } else { - r=histogramName; - Int_t iEnd; - for(iEnd=2;iEnd>0;iEnd--) { - if(axisList[iEnd]>=0) break; - } - for(Int_t i=0;i<=iEnd;i++) { - r += ";"; - if(axisList[i]<0) { - r += GetName(); - } else { - r += GetNonemptyNode()->GetDistributionAxisLabel(axisList[i]); - } - } - } - return r; -} - -TString TUnfoldBinning::BuildHistogramTitle2D -(const char *histogramName,const char *histogramTitle, - Int_t xAxis,const TUnfoldBinning *yAxisBinning,Int_t yAxis) const -{ - // build a title - // input: - // histogramTitle : if this is non-zero, use that title - // otherwise: - // title=histogramName;x;y - // xAxis : -1 no title for this axis - // >=0 use name of the corresponding axis - TString r; - if(histogramTitle) { - r=histogramTitle; - } else { - r=histogramName; - r += ";"; - if(xAxis==-1) { - r += GetName(); - } else if(xAxis>=0) { - r += GetNonemptyNode()->GetDistributionAxisLabel(xAxis); - } - r+= ";"; - if(yAxis==-1) { - r += yAxisBinning->GetName(); - } else if(yAxis>=0) { - r += yAxisBinning->GetNonemptyNode()->GetDistributionAxisLabel(yAxis); - } - - } - return r; -} - -Int_t TUnfoldBinning::GetTH1xNumberOfBins -(Bool_t originalAxisBinning,const char *axisSteering) const -{ - // return the number of histogram bins required when storing - // this binning in a one-dimensional histogram - // originalAxisBinning : try to preserve the axis binning, - // if this binning is 1-dimensional then the - // underflow/overflow are stored in the - // corresponding bins of the TH1 and - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // return: number of bins of the TH1 (underflow/overflow are not counted) - Int_t axisBins[3],axisList[3]; - GetTHxxBinning(originalAxisBinning ? 1 : 0,axisBins,axisList, - axisSteering); - return axisBins[0]; -} - -TH1 *TUnfoldBinning::CreateHistogram -(const char *histogramName,Bool_t originalAxisBinning,Int_t **binMap, - const char *histogramTitle,const char *axisSteering) const -{ - // create a THxx histogram capable to hold the bins of this binning - // scheme and its children - // input: - // histogramName: name of the histogram which is created - // originalAxisBinning : try to preserve the axis binning - // if this parameter is true, the resulting - // histogram has bin widths and histogram - // dimension (TH1D, TH2D, TH3D) - // in parallel to the binning scheme - // (if possible) - // binMap : mapping of global bins to histogram bins - // see method CreateBinMap() - // if(binMap==0), no binMap is created - // histogramTitle: if this is non-zero, it is taken as histogram title - // otherwise, the title is created automatically - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // returns: a new histogram (TH1D, TH2D or TH3D) - Int_t nBin[3],axisList[3]; - Int_t nDim=GetTHxxBinning(originalAxisBinning ? 3 : 0,nBin,axisList, - axisSteering); - const TUnfoldBinning *neNode=GetNonemptyNode(); - TString title=BuildHistogramTitle(histogramName,histogramTitle,axisList); - TH1 *r=0; - if(nDim>0) { - const TVectorD *axisBinsX= - neNode->GetDistributionBinning(axisList[0]); - if(nDim>1) { - const TVectorD *axisBinsY= - neNode->GetDistributionBinning(axisList[1]); - if(nDim>2) { - const TVectorD *axisBinsZ= - neNode->GetDistributionBinning(axisList[2]); - r=new TH3D(histogramName,title, - nBin[0],axisBinsX->GetMatrixArray(), - nBin[1],axisBinsY->GetMatrixArray(), - nBin[2],axisBinsZ->GetMatrixArray()); - } else { - r=new TH2D(histogramName,title, - nBin[0],axisBinsX->GetMatrixArray(), - nBin[1],axisBinsY->GetMatrixArray()); - } - } else { - r=new TH1D(histogramName,title,nBin[0],axisBinsX->GetMatrixArray()); - } - } else { - if(originalAxisBinning) { - Warning("CreateHistogram", - "Original binning can not be represented as THxx"); - } - r=new TH1D(histogramName,title,nBin[0],0.5,nBin[0]+0.5); - nDim=0; - } - if(binMap) { - *binMap=CreateBinMap(r,nDim,axisList,axisSteering); - } - return r; -} - -TH2D *TUnfoldBinning::CreateErrorMatrixHistogram -(const char *histogramName,Bool_t originalAxisBinning,Int_t **binMap, - const char *histogramTitle,const char *axisSteering) const -{ - // create a TH2D histogram capable to hold an error matrix - // coresponding to the bins of this binning scheme and its children - // input: - // histogramName: name of the histogram which is created - // originalAxisBinning : try to preserve the axis binning - // if this parameter is true, the resulting - // histogram has bin widths - // in parallel to this binning scheme - // (if possible) - // binMap : mapping of global bins to histogram bins - // see method CreateBinMap() - // if(binMap==0), no binMap is created - // histogramTitle: if this is non-zero, it is taken as histogram title - // otherwise, the title is created automatically - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // returns: a new TH2D - - Int_t nBin[3],axisList[3]; - Int_t nDim=GetTHxxBinning(originalAxisBinning ? 1 : 0,nBin,axisList, - axisSteering); - TString title=BuildHistogramTitle(histogramName,histogramTitle,axisList); - TH2D *r=0; - if(nDim==1) { - const TVectorD *axisBinsX=(TVectorD const *) - GetNonemptyNode()->fAxisList->At(axisList[0]); - r=new TH2D(histogramName,title,nBin[0],axisBinsX->GetMatrixArray(), - nBin[0],axisBinsX->GetMatrixArray()); - } else { - if(originalAxisBinning) { - Info("CreateErrorMatrixHistogram", - "Original binning can not be represented on one axis"); - } - r=new TH2D(histogramName,title,nBin[0],0.5,nBin[0]+0.5, - nBin[0],0.5,nBin[0]+0.5); - nDim=0; - } - if(binMap) { - *binMap=CreateBinMap(r,nDim,axisList,axisSteering); - } - return r; -} - -TH2D *TUnfoldBinning::CreateHistogramOfMigrations -(TUnfoldBinning const *xAxis,TUnfoldBinning const *yAxis, - char const *histogramName,Bool_t originalXAxisBinning, - Bool_t originalYAxisBinning,char const *histogramTitle) -{ - // create a TH2D histogram capable to hold the bins of the two - // input binning schemes on the x and y axes, respectively - // input: - // histogramName: name of the histogram which is created - // xAxis: binning scheme for the x axis - // yAxis: binning scheme for the y axis - // originalXAxisBinning: preserve x-axis bin widths if possible - // originalXAxisBinning: preserve y-axis bin widths if possible - // histogramTitle: if this is non-zero, it is taken as histogram title - // otherwise, the title is created automatically - // returns: a new TH2D - - Int_t nBinX[3],axisListX[3]; - Int_t nDimX= - xAxis->GetTHxxBinning(originalXAxisBinning ? 1 : 0,nBinX,axisListX,0); - Int_t nBinY[3],axisListY[3]; - Int_t nDimY= - yAxis->GetTHxxBinning(originalYAxisBinning ? 1 : 0,nBinY,axisListY,0); - TString title=xAxis->BuildHistogramTitle2D - (histogramName,histogramTitle,axisListX[0],yAxis,axisListY[0]); - if(nDimX==1) { - const TVectorD *axisBinsX=(TVectorD const *) - xAxis->fAxisList->At(axisListX[0]); - if(nDimY==1) { - const TVectorD *axisBinsY=(TVectorD const *) - yAxis->fAxisList->At(axisListY[0]); - return new TH2D(histogramName,title, - nBinX[0],axisBinsX->GetMatrixArray(), - nBinY[0],axisBinsY->GetMatrixArray()); - } else { - return new TH2D(histogramName,title, - nBinX[0],axisBinsX->GetMatrixArray(), - nBinY[0],0.5,0.5+nBinY[0]); - } - } else { - if(nDimY==1) { - const TVectorD *axisBinsY=(TVectorD const *) - yAxis->fAxisList->At(axisListY[0]); - return new TH2D(histogramName,title, - nBinX[0],0.5,0.5+nBinX[0], - nBinY[0],axisBinsY->GetMatrixArray()); - } else { - return new TH2D(histogramName,title, - nBinX[0],0.5,0.5+nBinX[0], - nBinY[0],0.5,0.5+nBinY[0]); - } - } -} - -Int_t TUnfoldBinning::GetTHxxBinning -(Int_t maxDim,Int_t *axisBins,Int_t *axisList, - const char *axisSteering) const -{ - // calculate properties of a THxx histogram to store this binning - // input: - // maxDim : maximum dimension of the THxx (0 or 1..3) - // maxDim==0 is used to indicate that the histogram should be - // 1-dimensional with all bins mapped on one axis, - // bin centers equal to bin numbers - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // output; - // axisBins[0..2] : number of bins on the THxx axes [0]:x [1]:y [2]:z - // axisList[0..2] : TUnfoldBinning axis number corresponding to TH1 axis - // [0]:x [1]:y [2]:z - // return value : 1-3: dimension of THxx - // 0 : use 1-dim THxx, binning structure is not preserved - for(Int_t i=0;i<3;i++) { - axisBins[i]=0; - axisList[i]=-1; - } - const TUnfoldBinning *theNode=GetNonemptyNode(); - if(theNode) { - return theNode->GetTHxxBinningSingleNode - (maxDim,axisBins,axisList,axisSteering); - } else { - axisBins[0]=GetTHxxBinsRecursive(axisSteering); - return 0; - } -} - -const TUnfoldBinning *TUnfoldBinning::GetNonemptyNode(void) const -{ - // get the node which has non-empty distributions - // if there is none or if there are many, return zero - const TUnfoldBinning *r=GetDistributionNumberOfBins()>0 ? this : 0; - for(TUnfoldBinning const *child=GetChildNode();child; - child=child->GetNextNode()) { - const TUnfoldBinning *c=child->GetNonemptyNode(); - if(!r) { - // new candidate found - r=c; - } else { - if(c) { - // multiple nodes found - r=0; - break; - } - } - } - return r; -} - -Int_t TUnfoldBinning::GetTHxxBinningSingleNode -(Int_t maxDim,Int_t *axisBins,Int_t *axisList,const char *axisSteering) const -{ - // get the properties of a histogram capable to hold the distribution - // of this node - // input: - // maxDim : maximum dimension of the THxx (0 or 1..3) - // maxDim==0 is used to indicate that the histogram should be - // 1-dimensional with all bins mapped on one axis - // axisSteering : - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // input/output: - // axisBins[0..2] : cumulated number of bins on the THxx axes - // [0]:x [1]:y [2]:z - // axisList[0..2] : TUnfoldBinning axis number corresponding to TH1 axis - // [0]:x [1]:y [2]:z - // return value : 1-3: dimension of THxx - // 0 : use 1-dim THxx, binning structure is not preserved - - - // decode axisSteering - // isOptionGiven[0] ('C'): bit vector which axes to collapse - // isOptionGiven[1] ('U'): bit vector to discarde underflow bins - // isOptionGiven[2] ('U'): bit vector to discarde overflow bins - Int_t isOptionGiven[3] = { 0 }; - DecodeAxisSteering(axisSteering,"CUO",isOptionGiven); - // count number of axes after projecting - Int_t numDimension=GetDistributionDimension(); - Int_t r=0; - for(Int_t i=0;i<numDimension;i++) { - if(isOptionGiven[0] & (1<<i)) continue; - r++; - } - if((r>0)&&(r<=maxDim)) { - // 0<r<=maxDim - // - // -> preserve the original binning - // axisList[] and axisBins[] are overwritten - r=0; - for(Int_t i=0;i<numDimension;i++) { - if(isOptionGiven[0] & (1<<i)) continue; - axisList[r]=i; - axisBins[r]=GetDistributionBinning(i)->GetNrows()-1; - r++; - } - } else { - // map everything on one axis - // axisBins[0] is the number of bins - if(HasUnconnectedBins() || (GetDistributionNumberOfBins()<=0)) { - axisBins[0] = GetDistributionNumberOfBins(); - } else { - Int_t nBin=1; - for(Int_t i=0;i<numDimension;i++) { - Int_t mask=(1<<i); - if(isOptionGiven[0] & mask) continue; - Int_t nBinI=GetDistributionBinning(i)->GetNrows()-1; - if((fHasUnderflow & mask)&& !(isOptionGiven[1] & mask)) nBinI++; - if((fHasOverflow & mask)&& !(isOptionGiven[2] & mask)) nBinI++; - nBin *= nBinI; - } - axisBins[0] = nBin; - } - r=0; - } - return r; -} - -Int_t TUnfoldBinning::GetTHxxBinsRecursive(const char *axisSteering) const -{ - // input: - // axisSteering : - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // output: - // binMap[] : map global bin numbers to histogram bins - // return value : number of bins - - Int_t r=0; - for(TUnfoldBinning const *child=GetChildNode();child; - child=child->GetNextNode()) { - r +=child->GetTHxxBinsRecursive(axisSteering); - } - // here: process distribution of this node - Int_t axisBins[3] = {0}, axisList[3] = {0}; - GetTHxxBinningSingleNode(0,axisBins,axisList,axisSteering); - r += axisBins[0]; - return r; -} - -Int_t *TUnfoldBinning::CreateBinMap -(const TH1 *hist,Int_t nDim,const Int_t *axisList,const char *axisSteering) -const -{ - // create mapping from global bin number to a histogram for this node - // global bins are the bins of the root node binning scheme - // when projecting them on a TH1 histogram "hRootNode" without special - // axis steering and without attempting to preserve the axis binnings - // - // The bin map is an array of size hRootNode->GetNbinsX()+2 - // For each bin of the "hRootNode" histogram it holds the target bin in - // "hist" or the number -1 if the corresponding "hRootNode" bin is not - // represented in "hist" - // - // input - // hist : the histogram (to calculate root bin numbers) - // nDim : target dimension of the TUnfoldBinning - // if(nDim==0) all bins are mapped linearly - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // - // input used only if nDim>0: - // axisList : for each THxx axis give the TUnfoldBinning axis number - // - // return value: - // an new array which holds the bin mapping - // r[0] : to which THxx bin to map global bin number 0 - // r[1] : to which THxx bin to map global bin number 1 - // ... - // r[nmax] - // where nmax=GetRootNode()->GetEndBin()+1 - Int_t nMax=GetRootNode()->GetEndBin()+1; - Int_t *r=new Int_t[nMax]; - for(Int_t i=0;i<nMax;i++) { - r[i]=-1; - } - Int_t startBin=GetRootNode()->GetStartBin(); - if(nDim>0) { - const TUnfoldBinning *nonemptyNode=GetNonemptyNode(); - if(nonemptyNode) { - FillBinMapSingleNode(hist,startBin,nDim,axisList,axisSteering,r); - } else { - Fatal("CreateBinMap","called with nDim=%d but GetNonemptyNode()=0", - nDim); - } - } else { - FillBinMapRecursive(startBin,axisSteering,r); - } - return r; -} - -Int_t TUnfoldBinning::FillBinMapRecursive -(Int_t startBin,const char *axisSteering,Int_t *binMap) const -{ - // fill bin map recursively - // input - // startBin : first histogram bin - // axisSteering : specify how to project the axes - // binMap : the bin mapping which is to be filled - // - // the positions - // binMap[GetStartBin()] - // .. - // binMap[GetEndBin()-1] - // are filled - // - Int_t nbin=0; - nbin = FillBinMapSingleNode(0,startBin,0,0,axisSteering,binMap); - for(TUnfoldBinning const *child=GetChildNode();child; - child=child->GetNextNode()) { - nbin += child->FillBinMapRecursive(startBin+nbin,axisSteering,binMap); - } - return nbin; -} - -Int_t TUnfoldBinning::FillBinMapSingleNode -(const TH1 *hist,Int_t startBin,Int_t nDim,const Int_t *axisList, - const char *axisSteering,Int_t *binMap) const -{ - // fill bin map for a single node - // input: - // hist: the histogram represeinthing this node (used if nDim>0) - // startBin: start bin in the bin map - // nDim: - // 0: bins are mapped in linear order, ignore hist and axisList - // nDim=hist->GetDimension(): - // bins are mapped to "hist" bin numbers - // the corresponding TUnfoldBinning axes are taken from axisList[] - // nDim=1 and hist->GetDimension()>1: - // bins are mapped to th x-axis of "hist" - // the corresponding TUnfoldBinning axis is taken from axisList[0] - // axisList[]: - // TUnfoldBinning axis numbers corresponding to the - // x[0], y[1], z[2] axes of "hist" - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // binMap: the bin map to fill - // return value: - // the number of bins mapped - // (only relevant if nDim==0) - // first, decode axisSteering - // isOptionGiven[0] ('C'): bit vector which axes to collapse - // isOptionGiven[1] ('U'): bit vector to discarde underflow bins - // isOptionGiven[2] ('U'): bit vector to discarde overflow bins - Int_t isOptionGiven[3] = {0}; - DecodeAxisSteering(axisSteering,"CUO",isOptionGiven); - Int_t axisBins[MAXDIM] = {0}; - Int_t dimension=GetDistributionDimension(); - Int_t axisNbin[MAXDIM] = {0}; - for(Int_t i=0;i<dimension;i++) { - const TVectorD *binning=GetDistributionBinning(i); - axisNbin[i]=binning->GetNrows()-1; - }; - for(Int_t i=0;i<GetDistributionNumberOfBins();i++) { - Int_t globalBin=GetStartBin()+i; - const TUnfoldBinning *dest=ToAxisBins(globalBin,axisBins); - if(dest!=this) { - if(!dest) { - Fatal("FillBinMapSingleNode", - "bin %d outside binning scheme", - globalBin); - } else { - Fatal("FillBinMapSingleNode", - "bin %d located in %s %d-%d rather than %s %d=%d", - i,(const char *)dest->GetName(), - dest->GetStartBin(),dest->GetEndBin(), - (const char *)GetName(),GetStartBin(),GetEndBin()); - } - } - // check whether this bin has to be skipped (underflow/overflow excluded) - Bool_t skip=kFALSE; - for(Int_t axis=0;axis<dimension;axis++) { - Int_t mask=(1<<axis); - if(((axisBins[axis]<0)&&(isOptionGiven[1] & mask))|| - ((axisBins[axis]>=axisNbin[axis])&&(isOptionGiven[2] & mask))) - skip=kTRUE; - } - if(skip) continue; - - if(nDim>0) { - // get bin number from THxx function(s) - if(nDim==hist->GetDimension()) { - Int_t ibin[3]; - ibin[0]=ibin[1]=ibin[2]=0; - for(Int_t hdim=0;hdim<nDim;hdim++) { - Int_t axis=axisList[hdim]; - ibin[hdim]=axisBins[axis]+1; - } - binMap[globalBin]=hist->GetBin(ibin[0],ibin[1],ibin[2]); - } else if(nDim==1) { - // histogram has more dimensions than the binning scheme - // and the binning scheme has one axis only - // -> use the first valid axis only - for(Int_t ii=0;ii<hist->GetDimension();ii++) { - if(axisList[ii]>=0) { - binMap[globalBin]=axisBins[axisList[ii]]+1; - break; - } - } - } else { - Fatal("FillBinMapSingleNode","unexpected bin mapping %d %d",nDim, - hist->GetDimension()); - } - } else { - // order all bins in sequence - // calculation in parallel to ToGlobalBin() - // but take care of - // startBin,collapseAxis,discardeUnderflow,discardeOverflow - if(dimension>0) { - Int_t r=0; - for(Int_t axis=dimension-1;axis>=0;axis--) { - Int_t mask=(1<<axis); - if(isOptionGiven[0] & mask) { - // bins on this axis are integrated over - continue; - } - Int_t iBin=axisBins[axis]; - Int_t nMax=axisNbin[axis]; - if((fHasUnderflow & ~isOptionGiven[1]) & mask) { - nMax +=1; - iBin +=1; - } - if((fHasOverflow & ~isOptionGiven[2]) & mask) { - nMax += 1; - } - r = r*nMax +iBin; - } - binMap[globalBin] = startBin + r; - } else { - binMap[globalBin] = startBin + axisBins[0]; - } - } - } - Int_t nbin; - if(dimension>0) { - nbin=1; - for(Int_t axis=dimension-1;axis>=0;axis--) { - Int_t mask=(1<<axis); - if(isOptionGiven[0] & mask) { - // bins on this axis are integrated over - continue; - } - Int_t nMax=axisNbin[axis]; - if((fHasUnderflow & ~isOptionGiven[1]) & mask) { - nMax +=1; - } - if((fHasOverflow & ~isOptionGiven[2]) & mask) { - nMax += 1; - } - nbin = nbin*nMax; - } - } else { - nbin=GetDistributionNumberOfBins(); - } - return nbin; -} - -TH1 *TUnfoldBinning::ExtractHistogram -(const char *histogramName,const TH1 *globalBins, - const TH2 *globalBinsEmatrix,Bool_t originalAxisBinning, - const char *axisSteering) const -{ - // extract a distribution from the given set of global bins - // input: - // histogramName : name of the histogram which ic created - // globalBins : histogram with all bins - // globalBinsEmatrix : corresponding error matrix - // if this pointer is zero, only diagonal errors - // are considered - // originalAxisBinning : extract histogram with proper binning - // (if possible) - // axisSteering - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - Int_t *binMap=0; - TH1 *r=CreateHistogram(histogramName,originalAxisBinning,&binMap,0, - axisSteering); - TUnfoldBinning const *root=GetRootNode(); - Int_t nMax=0; - for(Int_t iSrc=root->GetStartBin();iSrc<root->GetEndBin();iSrc++) { - if(binMap[iSrc]>nMax) nMax=binMap[iSrc]; - } - TVectorD eSquared(nMax+1); - for(Int_t iSrc=root->GetStartBin();iSrc<root->GetEndBin();iSrc++) { - Int_t iDest=binMap[iSrc]; - if(iDest>=0) { - Double_t c=r->GetBinContent(iDest); - r->SetBinContent(iDest,c+globalBins->GetBinContent(iSrc)); - if(!globalBinsEmatrix) { - eSquared(iDest) += TMath::Power(globalBins->GetBinError(iSrc),2.); - } else { - for(Int_t jSrc=root->GetStartBin();jSrc<root->GetEndBin();jSrc++) { - if(binMap[jSrc]==iDest) { - eSquared(iDest) += - TMath::Power(globalBins->GetBinError(jSrc),2.); - } - } - } - } - } - for(Int_t i=0;i<nMax;i++) { - Double_t e2=eSquared(i); - if(e2>0.0) { - r->SetBinError(i,TMath::Sqrt(e2)); - } - } - delete [] binMap; - - return r; -} - -/********************* Calculate global bin number ******/ - -Int_t TUnfoldBinning::GetGlobalBinNumber(Double_t x) const -{ - // locate bin on a one-dimensional distribution - // input - // x: coordinate to locate - if(GetDistributionDimension()!=1) { - Fatal("GetBinNumber", - "called with 1 argument for %d dimensional distribution", - GetDistributionDimension()); - } - return GetGlobalBinNumber(&x); -} - -Int_t TUnfoldBinning::GetGlobalBinNumber(Double_t x,Double_t y) const -{ - // locate bin on a two-dimensional distribution - // input - // x,y: coordinates to locate - if(GetDistributionDimension()!=2) { - Fatal("GetBinNumber", - "called with 2 arguments for %d dimensional distribution", - GetDistributionDimension()); - } - Double_t xx[2]; - xx[0]=x; - xx[1]=y; - return GetGlobalBinNumber(xx); -} - -Int_t TUnfoldBinning::GetGlobalBinNumber -(Double_t x,Double_t y,Double_t z) const -{ - // locate bin on a three-dimensional distribution - // input - // x,y,z: coordinates to locate - if(GetDistributionDimension()!=3) { - Fatal("GetBinNumber", - "called with 3 arguments for %d dimensional distribution", - GetDistributionDimension()); - } - Double_t xx[3]; - xx[0]=x; - xx[1]=y; - xx[2]=z; - return GetGlobalBinNumber(xx); -} - -Int_t TUnfoldBinning::GetGlobalBinNumber -(Double_t x0,Double_t x1,Double_t x2,Double_t x3) const -{ - // locate bin on a four-dimensional distribution - // input - // x0,x1,x2,x3: coordinates to locate - if(GetDistributionDimension()!=4) { - Fatal("GetBinNumber", - "called with 4 arguments for %d dimensional distribution", - GetDistributionDimension()); - } - Double_t xx[4]; - xx[0]=x0; - xx[1]=x1; - xx[2]=x2; - xx[3]=x3; - return GetGlobalBinNumber(xx); -} - -Int_t TUnfoldBinning::GetGlobalBinNumber(const Double_t *x) const -{ - // locate bin on a n-dimensional distribution - // input - // x[]: coordinates to locate - if(!GetDistributionDimension()) { - Fatal("GetBinNumber", - "no axes are defined for node %s", - (char const *)GetName()); - } - Int_t iAxisBins[MAXDIM] = {0}; - for(Int_t dim=0;dim<GetDistributionDimension();dim++) { - TVectorD const *bins=(TVectorD const *) fAxisList->At(dim); - Int_t i0=0; - Int_t i1=bins->GetNrows()-1; - Int_t iBin= 0; - if(x[dim]<(*bins)[i0]) { - iBin += i0-1; - // underflow - } else if(x[dim]>=(*bins)[i1]) { - // overflow - iBin += i1; - } else { - while(i1-i0>1) { - Int_t i2=(i0+i1)/2; - if(x[dim]<(*bins)[i2]) { - i1=i2; - } else { - i0=i2; - } - } - iBin += i0; - } - iAxisBins[dim]=iBin; - } - Int_t r=ToGlobalBin(iAxisBins); - if(r<0) r=0; - return r; -} - -/********************* access by global bin number ******/ - -TString TUnfoldBinning::GetBinName(Int_t iBin) const -{ - // get the name of a bin in the given tree - // iBin: bin number - Int_t axisBins[MAXDIM] = {0}; - TString r=TString::Format("#%d",iBin); - TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); - if(distribution) { - r +=" ("; - r += distribution->GetName(); - Int_t dimension=distribution->GetDistributionDimension(); - if(dimension>0) { - TString axisString; - for(Int_t axis=0;axis<dimension;axis++) { - TString thisAxisString= - distribution->GetDistributionAxisLabel(axis); - TVectorD const *bins=distribution->GetDistributionBinning(axis); - Int_t i=axisBins[axis]; - if(i<0) thisAxisString += "[ufl]"; - else if(i>=bins->GetNrows()-1) thisAxisString += "[ofl]"; - else { - thisAxisString += - TString::Format("[%.3g,%.3g]",(*bins)[i],(*bins)[i+1]); - } - axisString = ":"+thisAxisString+axisString; - } - r += axisString; - } else { - // extra bins - Int_t i=axisBins[0]; - if((i>=0)&&(i<distribution->fAxisLabelList->GetEntriesFast())) { - r += distribution->GetDistributionAxisLabel(i); - } else { - r += TString::Format(" %d",i); - } - } - r +=")"; - } - return r; -} - -Double_t TUnfoldBinning::GetBinSize(Int_t iBin) const -{ - // get N-dimensional bin size - // input: - // iBin : global bin number - // includeUO : include underflow/overflow bins or not - Int_t axisBins[MAXDIM] = {0}; - TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); - Double_t r=0.0; - if(distribution) { - if(distribution->GetDistributionDimension()>0) r=1.0; - for(Int_t axis=0;axis<distribution->GetDistributionDimension();axis++) { - TVectorD const *bins=distribution->GetDistributionBinning(axis); - Int_t pos=axisBins[axis]; - if(pos<0) { - r *= distribution->GetDistributionUnderflowBinWidth(axis); - } else if(pos>=bins->GetNrows()-1) { - r *= distribution->GetDistributionOverflowBinWidth(axis); - } else { - r *= (*bins)(pos+1)-(*bins)(pos); - } - if(r<=0.) break; - } - } - return r; -} - -Double_t TUnfoldBinning::GetBinFactor(Int_t iBin) const -{ - // return user factor for a bin - // iBin : global bin number - Int_t axisBins[MAXDIM] = {0}; - TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); - Double_t r=distribution->fBinFactorConstant; - if((r!=0.0) && distribution->fBinFactorFunction) { - Double_t x[MAXDIM]; - Int_t dimension=distribution->GetDistributionDimension(); - if(dimension>0) { - for(Int_t axis=0;axis<dimension;axis++) { - x[axis]=distribution->GetDistributionBinCenter - (axis,axisBins[axis]); - } - r *= distribution->fBinFactorFunction->EvalPar - (x,distribution->fBinFactorFunction->GetParameters()); - } else { - x[0]=axisBins[0]; - r *= distribution->fBinFactorFunction->Eval(x[0]); - } - - } - return r; -} - -void TUnfoldBinning::GetBinNeighbours -(Int_t bin,Int_t axis,Int_t *prev,Double_t *distPrev, - Int_t *next,Double_t *distNext) const -{ - // get neighbour bins along the specified axis - // input: - // bin,axis : bin number and axis number - // output: - // prev: bin number of previous bin (or -1 if not existing) - // distPrev: distance to previous bin - // next: bin number of next bin (or -1 if not existing) - // distNext: distance to next bin - Int_t axisBins[MAXDIM] = {0}; - TUnfoldBinning const *distribution=ToAxisBins(bin,axisBins); - Int_t dimension=distribution->GetDistributionDimension(); - *prev=-1; - *next=-1; - *distPrev=0.; - *distNext=0.; - if((axis>=0)&&(axis<dimension)) { - //TVectorD const *bins=distribution->GetDistributionBinning(axis); - //Int_t nBin=bins->GetNrows()-1; - Int_t centerBin= axisBins[axis]; - axisBins[axis] =centerBin-1; - *prev=ToGlobalBin(axisBins); - if(*prev>=0) { - *distPrev=distribution->GetDistributionBinCenter(axis,axisBins[axis])- - distribution->GetDistributionBinCenter(axis,centerBin); - } - axisBins[axis] =centerBin+1; - *next=ToGlobalBin(axisBins); - if(*next>=0) { - *distNext=distribution->GetDistributionBinCenter(axis,axisBins[axis])- - distribution->GetDistributionBinCenter(axis,centerBin); - } - } -} - -void TUnfoldBinning::GetBinUnderflowOverflowStatus -(Int_t iBin,Int_t *uStatus,Int_t *oStatus) const -{ - // return bit map indicating underflow and overflow status - // iBin: global bin number - // output - // uStatus: bin map indicating whether the bin is in underflow - // oStatus: bin map indicating whether the bin is in overflow - Int_t axisBins[MAXDIM] = {0}; - TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); - Int_t dimension=distribution->GetDistributionDimension(); - *uStatus=0; - *oStatus=0; - for(Int_t axis=0;axis<dimension;axis++) { - TVectorD const *bins=distribution->GetDistributionBinning(axis); - Int_t nBin=bins->GetNrows()-1; - if(axisBins[axis]<0) *uStatus |= (1<<axis); - if(axisBins[axis]>=nBin) *oStatus |= (1<<axis); - } -} - -/********************* access by bin number, given a projection mode ******/ -const TUnfoldBinning *TUnfoldBinning::GetBinLocation -(Int_t binTHxx,const char *axisSteering,Int_t axisBins[MAXDIM]) const -{ - // locate the node corresponding to - // a given THxx bin for a given projection mode - // input: - // binTHxx : bin in histogram - // axisSteering : - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // output: - // axisBins[] - // for each axis of the identified binning node give the bin number - // -1 underflow bin - // >=0 inside distribution or overflow bin - // special bin numbers - // -2 : axis collapsed - // -3 : axis collapsed and underflow bin excluded - // -4 : axis collapsed and overflow bin excluded - // -5 : axis collapsed and underflow+overflow bin excluded - // return value: - // the binning node or 0 if not found - Int_t offset=binTHxx-GetStartBin(); - return GetBinLocationRecursive(offset,axisSteering,axisBins); -} - -const TUnfoldBinning *TUnfoldBinning::GetBinLocationRecursive -(Int_t &offset,const char *axisSteering,Int_t axisBins[MAXDIM]) const -{ - // recursively locate the node corresponding to - // a given THxx bin for a given projection mode - // input: - // offset : start bin of this nodes in the histogram - // axisSteering : - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // output: - // axisBins[] - // for each axis of the identified binning node give the bin number - // -1 underflow bin - // >=0 inside distribution or overflow bin - // special bin numbers - // -2 : axis collapsed - // -3 : axis collapsed and underflow bin excluded - // -4 : axis collapsed and overflow bin excluded - // -5 : axis collapsed and underflow+overflow bin excluded - // return value: - // the binning node or 0 if not found - - // decode axisSteering - // isOptionGiven[0] ('C'): bit vector which axes to collapse - // isOptionGiven[1] ('U'): bit vector to discarde underflow bins - // isOptionGiven[2] ('U'): bit vector to discarde overflow bins - Int_t isOptionGiven[3] = { 0 }; - DecodeAxisSteering(axisSteering,"CUO",isOptionGiven); - const TUnfoldBinning *r=0; - if(offset>=0) { - if(GetDistributionDimension()>0) { - Int_t nBinsTotal=1; - Int_t i=offset; - for(Int_t axis=0;axis<GetDistributionDimension();axis++) { - Int_t mask=1<<axis; - if(isOptionGiven[0] & mask) { - axisBins[axis]=-2; - if((isOptionGiven[1] & mask)&& - (fHasUnderflow & mask)) axisBins[axis] -= 1; - if((isOptionGiven[2] & mask)&& - (fHasOverflow & mask)) axisBins[axis] -= 2; - } else { - Int_t nBin=GetDistributionBinning(axis)->GetNrows()-1; - axisBins[axis]=0; - if((fHasUnderflow & mask) && !(isOptionGiven[1] & mask)) { - nBin++; - axisBins[axis]=-1; - } - if((fHasOverflow & mask) && !(isOptionGiven[2] & mask)) { - nBin++; - } - axisBins[axis] += i % nBin; - i /= nBin; - nBinsTotal *= nBin; - } - } - offset -= nBinsTotal; - if(offset<0) { - r=this; - } - } else { - axisBins[0]=offset; - offset -= GetDistributionNumberOfBins(); - if(offset<0) r=this; - } - } - if(!r) { - for(TUnfoldBinning const *child=GetChildNode();child; - child=child->GetNextNode()) { - r=child->GetBinLocationRecursive(offset,axisSteering,axisBins); - if(r) break; - } - } - return r; -} - -Bool_t TUnfoldBinning::HasUnconnectedBins(void) const -{ - // check whether there are bins but no axis - return (!GetDistributionDimension())&&(GetDistributionNumberOfBins()>0); -} - -Double_t TUnfoldBinning::GetDistributionAverageBinSize -(Int_t axis,Bool_t includeUnderflow,Bool_t includeOverflow) const -{ - // get average bin size of the specified axis - // axis : axis number - // includeUnderflow : include underflow bin - // includeOverflow : include overflow bin - Double_t r=0.0; - if((axis>=0)&&(axis<GetDistributionDimension())) { - TVectorD const *bins=GetDistributionBinning(axis); - Double_t d=(*bins)[bins->GetNrows()-1]-(*bins)[0]; - Double_t nBins=bins->GetNrows()-1; - if(includeUnderflow && (fHasUnderflow & (1<<axis))) { - Double_t w=GetDistributionUnderflowBinWidth(axis); - if(w>0) { - nBins++; - d += w; - } - } - if(includeOverflow && (fHasOverflow & (1<<axis))) { - Double_t w=GetDistributionOverflowBinWidth(axis); - if(w>0.0) { - nBins++; - d += w; - } - } - if(nBins>0) { - r=d/nBins; - } - } else { - Error("GetDistributionAverageBinSize","axis %d does not exist",axis); - } - return r; -} - -Double_t TUnfoldBinning::GetDistributionUnderflowBinWidth(Int_t axis) const -{ - // return width of the underflow bin - // axis: axis number - TVectorD const *bins=GetDistributionBinning(axis); - return (*bins)[1]-(*bins)[0]; -} - -Double_t TUnfoldBinning::GetDistributionOverflowBinWidth(Int_t axis) const -{ - // return width of the underflow bin - // axis: axis number - TVectorD const *bins=GetDistributionBinning(axis); - return (*bins)[bins->GetNrows()-1]-(*bins)[bins->GetNrows()-2]; -} - -Double_t TUnfoldBinning::GetDistributionBinCenter -(Int_t axis,Int_t bin) const -{ - // position of the bin center - // input - // axis : axis number - // bin : bin number on the axis - TVectorD const *bins=GetDistributionBinning(axis); - Double_t r=0.0; - if(bin<0) { - // underflow bin - r=(*bins)[0]-0.5*GetDistributionUnderflowBinWidth(axis); - } else if(bin>=bins->GetNrows()-1) { - // overflow bin - r=(*bins)[bins->GetNrows()-1]+0.5*GetDistributionOverflowBinWidth(axis); - } else { - r=0.5*((*bins)[bin+1]+(*bins)[bin]); - } - return r; -} - -Int_t TUnfoldBinning::ToGlobalBin(Int_t const *axisBins) const -{ - // get global bin number, given axis bin numbers - // axisBins[]: bin numbers on each axis - // return: global bin nmber or -1 if not inside distribution - Int_t dimension=GetDistributionDimension(); - Int_t r=0; - if(dimension>0) { - for(Int_t axis=dimension-1;axis>=0;axis--) { - Int_t nMax=GetDistributionBinning(axis)->GetNrows()-1; - Int_t i=axisBins[axis]; - if(fHasUnderflow & (1<<axis)) { - nMax +=1; - i +=1; - } - if(fHasOverflow & (1<<axis)) nMax +=1; - if((i>=0)&&(i<nMax)) { - r = r*nMax +i; - } else { - r=-1; - break; - } - } - if(r>=0) { - r += GetStartBin(); - } - } else { - if((axisBins[0]>=0)&&(axisBins[0]<GetDistributionNumberOfBins())) - r=GetStartBin()+axisBins[0]; - } - return r; -} - -TUnfoldBinning const *TUnfoldBinning::ToAxisBins -(Int_t globalBin,Int_t *axisBins) const -{ - // return distribution in which the bin is located - // and bin numbers on the corresponding axes - // input - // globalBin : global bin number - // output - // if(GetDistributionDimension()>0) - // axisBin[] : bin number on the individual axes - // bin numbers are starting with -1 (underflow) - // else - // axisBin[0] : bin number, counted from zero - // return value - // the distribution in which the globalBin is located - // or 0 if the globalBin is outside the tree - TUnfoldBinning const *r=0; - if((globalBin>=GetStartBin())&&(globalBin<GetEndBin())) { - TUnfoldBinning const *node; - for(node=GetChildNode();node && !r; node=node->GetNextNode()) { - r=node->ToAxisBins(globalBin,axisBins); - } - if(!r) { - r=this; - Int_t i=globalBin-GetStartBin(); - Int_t dimension=GetDistributionDimension(); - if(dimension>0) { - for(int axis=0;axis<dimension;axis++) { - Int_t nMax=GetDistributionBinning(axis)->GetNrows()-1; - axisBins[axis]=0; - if(fHasUnderflow & (1<<axis)) { - axisBins[axis] =-1; - nMax += 1; - } - if(fHasOverflow & (1<<axis)) nMax +=1; - axisBins[axis] += i % nMax; - i /= nMax; - } - } else { - axisBins[0]=i; - } - } - } - return r; -} - -void TUnfoldBinning::DecodeAxisSteering -(const char *axisSteering,const char *options,Int_t *isOptionGiven) const -{ - // decode axis steering - // input - // axisSteering: the steering to decode - // options: the allowed options to extract - // output - // isOptionGiven[] : array of decoded steering options - // the dimension of isOptionGiven[] has to match the number of - // characters in "option" - // - // the axis steering is given in the form - // steering1;steering2;...;steeringN - // all parts of the steering, sepatared by ';' are parsed - // each part must have the form - // axisName[optionlist] - // where: - // axisName : the name of the axis for which the optionlist is relevant - // if the name is the character '*', all axes are matched - // optionlist : for each option the corresonding character - // - // example: - // imagine this node has two axes, named "xgen" and "ygen" - // then - // DecodeAxisSteering("*[u];xgen[c]","cuo",options); - // will set the "U" option for all axes and the "C" option for the "xgen" - // axis, so: - // options[0]=0x1; // option 'c' is true for axis 0 (bit #0 is set) - // options[1]=0x3; // option 'u' is true for both axes (bit #0,#1 are set) - // options[2]=0x0; // option 'o' is not given (no bits are set) - Int_t nOpt=TString(options).Length(); - for(Int_t i=0;i<nOpt;i++) isOptionGiven[i]=0; - if(axisSteering) { - TObjArray *patterns=TString(axisSteering).Tokenize(";"); - Int_t nPattern=patterns->GetEntries(); - Int_t nAxis=fAxisLabelList->GetEntries(); - for(Int_t i=0;i<nPattern;i++) { - TString const &pattern=((TObjString * const)patterns->At(i)) - ->GetString(); - Int_t bracketBegin=pattern.Last('['); - Int_t len=pattern.Length(); - if((bracketBegin>0)&&(pattern[len-1]==']')) { - TString axisId=pattern(0,bracketBegin); - Int_t mask=0; - if((axisId[0]=='*')&&(axisId.Length()==1)) { - // turn all bins on - mask=(1<<nAxis)-1; - } else { - // if axis is there, turn its bit on - for(Int_t j=0;j<nAxis;j++) { - if(!axisId.CompareTo(GetDistributionAxisLabel(j))) { - mask|= (1<<j); - } - } - } - for(Int_t o=0;o<nOpt;o++) { - if(pattern.Last(options[o])>bracketBegin) { - isOptionGiven[o] |= mask; - } - } - } else { - Error("DecodeAxisSteering", - "steering \"%s\" does not end with [options]", - (const char *)pattern); - } - } - } -} - diff --git a/hist/unfold/CMakeLists.txt b/hist/unfold/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..fc719703cbb8a6b9680fcfb483c6ecccaa4ecce9 --- /dev/null +++ b/hist/unfold/CMakeLists.txt @@ -0,0 +1,7 @@ +############################################################################ +# CMakeLists.txt file for building ROOT hist/unfold package +############################################################################ + +set(libname Unfold) + +ROOT_STANDARD_LIBRARY_PACKAGE(Unfold DEPENDENCIES Hist XMLParser Matrix DICTIONARY_OPTIONS "-writeEmptyRootPCM") \ No newline at end of file diff --git a/hist/unfold/Module.mk b/hist/unfold/Module.mk new file mode 100644 index 0000000000000000000000000000000000000000..173fbe1053ebc5a2460e3980aec000d49f7ff20f --- /dev/null +++ b/hist/unfold/Module.mk @@ -0,0 +1,75 @@ +# Module.mk for unfold module +# Copyright (c) 2000 Rene Brun and Fons Rademakers +# +# Author: Olivier Couet, 23/11/2016 + +MODNAME := unfold +MODDIR := $(ROOT_SRCDIR)/hist/$(MODNAME) +MODDIRS := $(MODDIR)/src +MODDIRI := $(MODDIR)/inc + +UNFOLDDIR := $(MODDIR) +UNFOLDDIRS := $(UNFOLDDIR)/src +UNFOLDDIRI := $(UNFOLDDIR)/inc + +##### libSpectrum ##### +UNFOLDL := $(MODDIRI)/LinkDef.h +UNFOLDDS := $(call stripsrc,$(MODDIRS)/G__Spectrum.cxx) +UNFOLDDO := $(UNFOLDDS:.cxx=.o) +UNFOLDDH := $(UNFOLDDS:.cxx=.h) + +UNFOLDH := $(filter-out $(MODDIRI)/LinkDef%,$(wildcard $(MODDIRI)/*.h)) +UNFOLDS := $(filter-out $(MODDIRS)/G__%,$(wildcard $(MODDIRS)/*.cxx)) +UNFOLDO := $(call stripsrc,$(UNFOLDS:.cxx=.o)) + +UNFOLDDEP := $(UNFOLDO:.o=.d) $(UNFOLDDO:.o=.d) + +UNFOLDLIB := $(LPATH)/libSpectrum.$(SOEXT) +UNFOLDMAP := $(UNFOLDLIB:.$(SOEXT)=.rootmap) + +# used in the main Makefile +ALLHDRS += $(patsubst $(MODDIRI)/%.h,include/%.h,$(UNFOLDH)) +ALLLIBS += $(UNFOLDLIB) +ALLMAPS += $(UNFOLDMAP) + +# include all dependency files +INCLUDEFILES += $(UNFOLDDEP) + +##### local rules ##### +.PHONY: all-$(MODNAME) clean-$(MODNAME) distclean-$(MODNAME) + +include/%.h: $(UNFOLDDIRI)/%.h + cp $< $@ + +$(UNFOLDLIB): $(UNFOLDO) $(UNFOLDDO) $(ORDER_) $(MAINLIBS) \ + $(UNFOLDLIBDEP) + @$(MAKELIB) $(PLATFORM) $(LD) "$(LDFLAGS)" \ + "$(SOFLAGS)" libSpectrum.$(SOEXT) $@ \ + "$(UNFOLDO) $(UNFOLDDO)" \ + "$(UNFOLDLIBEXTRA)" + +$(call pcmrule,UNFOLD) + $(noop) + +$(UNFOLDDS): $(UNFOLDH) $(UNFOLDL) $(ROOTCLINGEXE) $(call pcmdep,UNFOLD) + $(MAKEDIR) + @echo "Generating dictionary $@..." + $(ROOTCLINGSTAGE2) -f $@ $(call dictModule,UNFOLD) -c -writeEmptyRootPCM $(UNFOLDH) $(UNFOLDL) + +$(UNFOLDMAP): $(UNFOLDH) $(UNFOLDL) $(ROOTCLINGEXE) $(call pcmdep,UNFOLD) + $(MAKEDIR) + @echo "Generating rootmap $@..." + $(ROOTCLINGSTAGE2) -r $(UNFOLDDS) $(call dictModule,UNFOLD) -c $(UNFOLDH) $(UNFOLDL) + +all-$(MODNAME): $(UNFOLDLIB) + +clean-$(MODNAME): + @rm -f $(UNFOLDO) $(UNFOLDDO) + +clean:: clean-$(MODNAME) + +distclean-$(MODNAME): clean-$(MODNAME) + @rm -f $(UNFOLDDEP) $(UNFOLDLIB) $(UNFOLDMAP) \ + $(UNFOLDDS) $(UNFOLDDH) + +distclean:: distclean-$(MODNAME) diff --git a/hist/unfold/doc/index.md b/hist/unfold/doc/index.md new file mode 100644 index 0000000000000000000000000000000000000000..c119ced298f0e7958a6fd728be683d2d96527aeb --- /dev/null +++ b/hist/unfold/doc/index.md @@ -0,0 +1,7 @@ +\defgroup Unfold TUnfold classes +\ingroup Hist + +An algorithm to unfold distributions from detector to truth level. + +\author Stefan Schmitt DESY + diff --git a/hist/unfold/inc/LinkDef.h b/hist/unfold/inc/LinkDef.h new file mode 100644 index 0000000000000000000000000000000000000000..ca6e63144f457d87afa20abcb3cb6dbc77561aeb --- /dev/null +++ b/hist/unfold/inc/LinkDef.h @@ -0,0 +1,23 @@ +/* @(#)root/unfold:$Id$ */ + +/************************************************************************* + * Copyright (C) 1995-2000, Rene Brun and Fons Rademakers. * + * All rights reserved. * + * * + * For the licensing terms see $ROOTSYS/LICENSE. * + * For the list of contributors see $ROOTSYS/README/CREDITS. * + *************************************************************************/ + +#ifdef __CINT__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class TUnfold+; +#pragma link C++ class TUnfoldSys+; +#pragma link C++ class TUnfoldBinning+; +#pragma link C++ class TUnfoldDensity+; +#pragma link C++ class TUnfoldBinningXML+; + +#endif diff --git a/hist/hist/inc/TUnfold.h b/hist/unfold/inc/TUnfold.h similarity index 60% rename from hist/hist/inc/TUnfold.h rename to hist/unfold/inc/TUnfold.h index 24af3a3faad373aa0e4ec5546072569da4a9d019..8c8a91c4755999fc81f46a6e1f3662551dba1ab0 100644 --- a/hist/hist/inc/TUnfold.h +++ b/hist/unfold/inc/TUnfold.h @@ -1,9 +1,14 @@ // Author: Stefan Schmitt // DESY, 13/10/08 -// Version 17.1, bug fixes in GetFoldedOutput, GetOutput +// Version 17.6, updated doxygen-style comments, add one argument for scanLCurve // // History: +// Version 17.5, fix memory leak and other bugs +// Version 17.4, in parallel to changes in TUnfoldBinning +// Version 17.3, in parallel to changes in TUnfoldBinning +// Version 17.2, in parallel to changes in TUnfoldBinning +// Version 17.1, bug fixes in GetFoldedOutput, GetOutput // Version 17.0, error matrix with SetInput, store fL not fLSquared // Version 16.2, in parallel to bug-fix in TUnfoldSys // Version 16.1, in parallel to bug-fix in TUnfold.C @@ -60,7 +65,7 @@ // matrices with large dimensions. // // // // Thus the algorithm should not used for large dimensions of x and y // -// dim(x) should not exceed O(100) // +// dim(x) should not exceed O(100) // // dim(y) should not exceed O(500) // // // ////////////////////////////////////////////////////////////////////////// @@ -92,7 +97,7 @@ #include <TObjArray.h> #include <TString.h> -#define TUnfold_VERSION "V17.1" +#define TUnfold_VERSION "V17.6" #define TUnfold_CLASS_VERSION 17 @@ -100,51 +105,110 @@ class TUnfold : public TObject { private: void InitTUnfold(void); // initialize all data members public: + + /// type of extra constraint enum EConstraint { - kEConstraintNone =0, // use no extra constraint - kEConstraintArea =1 // enforce preservation of the area + + /// use no extra constraint + kEConstraintNone =0, + + /// enforce preservation of the area + kEConstraintArea =1 + }; + + /// choice of regularisation scheme + enum ERegMode { + + /// no regularisation, or defined later by RegularizeXXX() methods + kRegModeNone = 0, + + /// regularise the amplitude of the output distribution + kRegModeSize = 1, + + /// regularize the 1st derivative of the output distribution + kRegModeDerivative = 2, + + /// regularize the 2nd derivative of the output distribution + kRegModeCurvature = 3, + + + /// mixed regularisation pattern + kRegModeMixed = 4 }; - enum ERegMode { // regularisation scheme - kRegModeNone = 0, // no regularisation - kRegModeSize = 1, // regularise the size of the output - kRegModeDerivative = 2, // regularize the 1st derivative of the output - kRegModeCurvature = 3, // regularize the 2nd derivative of the output - kRegModeMixed = 4 // mixed regularisation pattern + + /// arrangement of axes for the response matrix (TH2 histogram) + enum EHistMap { + + /// truth level on x-axis of the response matrix + kHistMapOutputHoriz = 0, + + /// truth level on y-axis of the response matrix + kHistMapOutputVert = 1 }; + protected: - TMatrixDSparse * fA; // Input: matrix - TMatrixDSparse *fL; // Input: regularisation conditions - TMatrixDSparse *fVyy; // Input: covariance matrix for y - TMatrixD *fY; // Input: y - TMatrixD *fX0; // Input: x0 - Double_t fTauSquared; // Input: regularisation parameter - Double_t fBiasScale; // Input: scale factor for the bias - TArrayI fXToHist; // Input: matrix indices -> histogram bins - TArrayI fHistToX; // Input: histogram bins -> matrix indices - TArrayD fSumOverY; // Input: sum of all columns - EConstraint fConstraint; // Input: type of constraint to use - ERegMode fRegMode; // Input: type of regularisation + /// response matrix A + TMatrixDSparse * fA; + /// regularisation conditions L + TMatrixDSparse *fL; + /// input (measured) data y + TMatrixD *fY; + /// covariance matrix Vyy corresponding to y + TMatrixDSparse *fVyy; + /// scale factor for the bias + Double_t fBiasScale; + /// bias vector x0 + TMatrixD *fX0; + /// regularisation parameter tau squared + Double_t fTauSquared; + /// mapping of matrix indices to histogram bins + TArrayI fXToHist; + /// mapping of histogram bins to matrix indices + TArrayI fHistToX; + /// truth vector calculated from the non-normalized response matrix + TArrayD fSumOverY; + /// type of constraint to use for the unfolding + EConstraint fConstraint; + /// type of regularisation + ERegMode fRegMode; private: - Int_t fIgnoredBins; // number of input bins which are dropped because they have error=0 - Double_t fEpsMatrix; // machine accuracy for eingenvalue analysis - TMatrixD *fX; // Result: x - TMatrixDSparse *fVyyInv; // Result: inverse of covariance matrix on y - TMatrixDSparse *fVxx; // Result: covariance matrix on x - TMatrixDSparse *fVxxInv; // Result: inverse of covariance matrix on x - TMatrixDSparse *fAx; // Result: Ax - Double_t fChi2A; // Result: chi**2 contribution from (y-Ax)V(y-Ax) - Double_t fLXsquared; // Result: chi**2 contribution from (x-s*x0)Lsquared(x-s*x0) - Double_t fRhoMax; // Result: maximum global correlation - Double_t fRhoAvg; // Result: average global correlation - Int_t fNdf; // Result: number of degrees of freedom - TMatrixDSparse *fDXDAM[2]; // Result: part of derivative dx_k/dA_ij - TMatrixDSparse *fDXDAZ[2]; // Result: part of derivative dx_k/dA_ij - TMatrixDSparse *fDXDtauSquared; // Result: derivative dx/dtau - TMatrixDSparse *fDXDY; // Result: derivative dx/dy - TMatrixDSparse *fEinv; // Result: matrix E^(-1) - TMatrixDSparse *fE; // Result: matrix E + /// number of input bins which are dropped because they have error=0 + Int_t fIgnoredBins; + /// machine accuracy used to determine matrix rank after eigenvalue analysis + Double_t fEpsMatrix; + /// unfolding result x + TMatrixD *fX; + /// covariance matrix Vxx + TMatrixDSparse *fVxx; + /// inverse of covariance matrix Vxx<sup>-1</sub> + TMatrixDSparse *fVxxInv; + /// inverse of the input covariance matrix Vyy<sup>-1</sub> + TMatrixDSparse *fVyyInv; + /// result x folded back A*x + TMatrixDSparse *fAx; + /// chi**2 contribution from (y-Ax)Vyy<sup>-1</sub>(y-Ax) + Double_t fChi2A; + /// chi**2 contribution from (x-s*x0)<sup>T</sub>L<sup>T</sub>L(x-s*x0) + Double_t fLXsquared; + /// maximum global correlation coefficient + Double_t fRhoMax; + /// average global correlation coefficient + Double_t fRhoAvg; + /// number of degrees of freedom + Int_t fNdf; + /// matrix contribution to the of derivative dx_k/dA_ij + TMatrixDSparse *fDXDAM[2]; + /// vector contribution to the of derivative dx_k/dA_ij + TMatrixDSparse *fDXDAZ[2]; + /// derivative of the result wrt tau squared + TMatrixDSparse *fDXDtauSquared; + /// derivative of the result wrt dx/dy + TMatrixDSparse *fDXDY; + /// matrix E^(-1) + TMatrixDSparse *fEinv; + /// matrix E + TMatrixDSparse *fE; protected: - TUnfold(void); // for derived classes // Int_t IsNotSymmetric(TMatrixDSparse const &m) const; virtual Double_t DoUnfold(void); // the unfolding algorithm virtual void ClearResults(void); // clear all results @@ -159,91 +223,120 @@ class TUnfold : public TObject { TMatrixDSparse *InvertMSparseSymmPos(const TMatrixDSparse *A,Int_t *rank) const; // invert symmetric (semi-)positive sparse matrix void AddMSparse(TMatrixDSparse *dest,Double_t f,const TMatrixDSparse *src) const; // replacement for dest += f*src TMatrixDSparse *CreateSparseMatrix(Int_t nrow,Int_t ncol,Int_t nele,Int_t *row,Int_t *col,Double_t *data) const; // create a TMatrixDSparse from an array + /// returns internal number of output (truth) matrix rows inline Int_t GetNx(void) const { return fA->GetNcols(); - } // number of non-zero output bins + } + /// converts truth histogram bin number to matrix row + inline Int_t GetRowFromBin(int ix) const { return fHistToX[ix]; } + /// converts matrix row to truth histogram bin number + inline Int_t GetBinFromRow(int ix) const { return fXToHist[ix]; } + /// returns the number of measurement bins inline Int_t GetNy(void) const { return fA->GetNrows(); - } // number of input bins + } + /// vector of the unfolding result + inline const TMatrixD *GetX(void) const { return fX; } + /// covariance matrix of the result + inline const TMatrixDSparse *GetVxx(void) const { return fVxx; } + /// inverse of covariance matrix of the result + inline const TMatrixDSparse *GetVxxInv(void) const { return fVxxInv; } + /// vector of folded-back result + inline const TMatrixDSparse *GetAx(void) const { return fAx; } + /// matrix of derivatives dx/dy + inline const TMatrixDSparse *GetDXDY(void) const { return fDXDY; } + /// matrix contributions of the derivative dx/dA + inline const TMatrixDSparse *GetDXDAM(int i) const { return fDXDAM[i]; } + /// vector contributions of the derivative dx/dA + inline const TMatrixDSparse *GetDXDAZ(int i) const { return fDXDAZ[i]; } + /// matrix E<sup>-1</sup>, using internal bin counting + inline const TMatrixDSparse *GetEinv(void) const { return fEinv; } + /// matrix E, using internal bin counting + inline const TMatrixDSparse *GetE(void) const { return fE; } + /// inverse of covariance matrix of the data y + inline const TMatrixDSparse *GetVyyInv(void) const { return fVyyInv; } + void ErrorMatrixToHist(TH2 *ematrix,const TMatrixDSparse *emat,const Int_t *binMap,Bool_t doClear) const; // return an error matrix as histogram Double_t GetRhoIFromMatrix(TH1 *rhoi,const TMatrixDSparse *eOrig,const Int_t *binMap,TH2 *invEmat) const; // return global correlation coefficients - inline const TMatrixDSparse *GetDXDY(void) const { return fDXDY; } // access derivative dx/dy - inline const TMatrixDSparse *GetDXDAM(int i) const { return fDXDAM[i]; } // access matrix parts of the derivative dx/dA - inline const TMatrixDSparse *GetDXDAZ(int i) const { return fDXDAZ[i]; } // access vector parts of the derivative dx/dA - inline const TMatrixDSparse *GetDXDtauSquared(void) const { return fDXDtauSquared; } // get derivative dx/dtauSquared - inline const TMatrixDSparse *GetAx(void) const { return fAx; } // get vector Ax - inline const TMatrixDSparse *GetEinv(void) const { return fEinv; } // get matrix E^-1 - inline const TMatrixDSparse *GetE(void) const { return fE; } // get matrix E - inline const TMatrixDSparse *GetVxx(void) const { return fVxx; } // get covariance matrix of x - inline const TMatrixDSparse *GetVxxInv(void) const { return fVxxInv; } // get inverse of covariance matrix of x - inline const TMatrixDSparse *GetVyyInv(void) const { return fVyyInv; } // get inverse of covariance matrix of y - inline const TMatrixD *GetX(void) const { return fX; } // get result vector x - inline Int_t GetRowFromBin(int ix) const { return fHistToX[ix]; } // convert histogram bin number to matrix row - inline Int_t GetBinFromRow(int ix) const { return fXToHist[ix]; } // convert matrix row to histogram bin number - static void DeleteMatrix(TMatrixD **m); // delete and invalidate pointer - static void DeleteMatrix(TMatrixDSparse **m); // delete and invalidate pointer + /// vector of derivative dx/dtauSquared, using internal bin counting + inline const TMatrixDSparse *GetDXDtauSquared(void) const { return fDXDtauSquared; } + /// delete matrix and invalidate pointer + static void DeleteMatrix(TMatrixD **m); + /// delete sparse matrix and invalidate pointer + static void DeleteMatrix(TMatrixDSparse **m); + Bool_t AddRegularisationCondition(Int_t i0,Double_t f0,Int_t i1=-1,Double_t f1=0.,Int_t i2=-1,Double_t f2=0.); // add regularisation condition for a triplet of output bins Bool_t AddRegularisationCondition(Int_t nEle,const Int_t *indices,const Double_t *rowData); // add a regularisation condition public: - enum EHistMap { // mapping between unfolding matrix and TH2 axes - kHistMapOutputHoriz = 0, // map unfolding output to x-axis of TH2 matrix - kHistMapOutputVert = 1 // map unfolding output to y-axis of TH2 matrix - }; - + static const char*GetTUnfoldVersion(void); + // Set up response matrix and regularisation scheme TUnfold(const TH2 *hist_A, EHistMap histmap, ERegMode regmode = kRegModeSize, - EConstraint constraint=kEConstraintArea); // constructor - virtual ~TUnfold(void); // delete data members - static const char*GetTUnfoldVersion(void); - void SetBias(const TH1 *bias); // set alternative bias - void SetConstraint(EConstraint constraint); // set type of constraint for the next unfolding - Int_t RegularizeSize(int bin, Double_t scale = 1.0); // regularise the size of one output bin - Int_t RegularizeDerivative(int left_bin, int right_bin, Double_t scale = 1.0); // regularize difference of two output bins (1st derivative) - Int_t RegularizeCurvature(int left_bin, int center_bin, int right_bin, Double_t scale_left = 1.0, Double_t scale_right = 1.0); // regularize curvature of three output bins (2nd derivative) - Int_t RegularizeBins(int start, int step, int nbin, ERegMode regmode); // regularize a 1-dimensional curve - Int_t RegularizeBins2D(int start_bin, int step1, int nbin1, int step2, int nbin2, ERegMode regmode); // regularize a 2-dimensional grid - Double_t DoUnfold(Double_t tau, - const TH1 *hist_y, Double_t scaleBias=0.0); // do the unfolding - virtual Int_t SetInput(const TH1 *hist_y, Double_t scaleBias=0.0,Double_t oneOverZeroError=0.0,const TH2 *hist_vyy=0,const TH2 *hist_vyy_inv=0); // define input distribution - virtual Double_t DoUnfold(Double_t tau); // Unfold with given choice of tau + EConstraint constraint=kEConstraintArea); + // for root streamer and derived classes + TUnfold(void); + virtual ~TUnfold(void); + // define input distribution + virtual Int_t SetInput(const TH1 *hist_y, Double_t scaleBias=0.0,Double_t oneOverZeroError=0.0,const TH2 *hist_vyy=0,const TH2 *hist_vyy_inv=0); + // Unfold with given choice of tau and input + virtual Double_t DoUnfold(Double_t tau); + Double_t DoUnfold(Double_t tau,const TH1 *hist_y, Double_t scaleBias=0.0); + // scan the L curve using successive calls to DoUnfold(Double_t) at various tau virtual Int_t ScanLcurve(Int_t nPoint,Double_t tauMin, Double_t tauMax,TGraph **lCurve, - TSpline **logTauX=0,TSpline **logTauY=0); // scan the L curve using successive calls to DoUnfold(Double_t) at various tau - void GetProbabilityMatrix(TH2 *A,EHistMap histmap) const; // get the matrix A of probabilities - void GetNormalisationVector(TH1 *s,const Int_t *binMap=0) const; // get the vector of normalisation factors, equivalent to the initial bias vector - - void GetOutput(TH1 *output,const Int_t *binMap=0) const; // get output distribution, arbitrary bin mapping + TSpline **logTauX=0,TSpline **logTauY=0, + TSpline **logTauCurvature=0); - void GetBias(TH1 *bias,const Int_t *binMap=0) const; // get bias (includind biasScale) - - void GetFoldedOutput(TH1 *folded,const Int_t *binMap=0) const; // get unfolding result folded back through the matrix + // access unfolding results + Double_t GetTau(void) const; + void GetOutput(TH1 *output,const Int_t *binMap=0) const; + void GetEmatrix(TH2 *ematrix,const Int_t *binMap=0) const; + void GetRhoIJ(TH2 *rhoij,const Int_t *binMap=0) const; + Double_t GetRhoI(TH1 *rhoi,const Int_t *binMap=0,TH2 *invEmat=0) const; + void GetFoldedOutput(TH1 *folded,const Int_t *binMap=0) const; + // access input parameters + void GetProbabilityMatrix(TH2 *A,EHistMap histmap) const; + void GetNormalisationVector(TH1 *s,const Int_t *binMap=0) const; // get the vector of normalisation factors, equivalent to the initial bias vector void GetInput(TH1 *inputData,const Int_t *binMap=0) const; // get input data - - void GetRhoIJ(TH2 *rhoij,const Int_t *binMap=0) const; // get correlation coefficients, arbitrary bin mapping - - void GetEmatrix(TH2 *ematrix,const Int_t *binMap=0) const; // get error matrix, arbitrary bin mapping - - Double_t GetRhoI(TH1 *rhoi,const Int_t *binMap=0,TH2 *invEmat=0) const; // get global correlation coefficients, arbitrary bin mapping - - void GetLsquared(TH2 *lsquared) const; // get regularisation conditions squared - - inline Int_t GetNr(void) const { return fL->GetNrows(); } // number of regularisation conditions + void GetInputInverseEmatrix(TH2 *ematrix); // get input data inverse of error matrix + void GetBias(TH1 *bias,const Int_t *binMap=0) const; // get bias (includind biasScale) + Int_t GetNr(void) const; // number of regularisation conditions void GetL(TH2 *l) const; // get matrix of regularisation conditions + void GetLsquared(TH2 *lsquared) const; - void GetInputInverseEmatrix(TH2 *ematrix); // get input data inverse of error matrix + // access various properties of the result + /// get maximum global correlation determined in recent unfolding + inline Double_t GetRhoMax(void) const { return fRhoMax; } + /// get average global correlation determined in recent unfolding + inline Double_t GetRhoAvg(void) const { return fRhoAvg; } + /// get χ<sup>2</sup><sub>A</sub> contribution determined in recent unfolding + inline Double_t GetChi2A(void) const { return fChi2A; } - Double_t GetTau(void) const; // get regularisation parameter - inline Double_t GetRhoMax(void) const { return fRhoMax; } // get maximum global correlation - inline Double_t GetRhoAvg(void) const { return fRhoAvg; } // get average global correlation - inline Double_t GetChi2A(void) const { return fChi2A; } // get chi**2 contribution from A - Double_t GetChi2L(void) const; // get chi**2 contribution from L + Double_t GetChi2L(void) const; // get χ<sup>2</sup><sub>L</sub> contribution determined in recent unfolding virtual Double_t GetLcurveX(void) const; // get value on x axis of L curve virtual Double_t GetLcurveY(void) const; // get value on y axis of L curve - inline Int_t GetNdf(void) const { return fNdf; } // get number of degrees of freedom + /// get number of degrees of freedom determined in recent unfolding + /// + /// This returns the number of valid measurements minus the number + /// of unfolded truth bins. If the area constraint is active, one + /// further degree of freedom is subtracted + inline Int_t GetNdf(void) const { return fNdf; } Int_t GetNpar(void) const; // get number of parameters - inline Double_t GetEpsMatrix(void) const { return fEpsMatrix; } // get accuracy for eingenvalue analysis + // advanced features + void SetBias(const TH1 *bias); // set alternative bias + void SetConstraint(EConstraint constraint); // set type of constraint for the next unfolding + Int_t RegularizeSize(int bin, Double_t scale = 1.0); // regularise the size of one output bin + Int_t RegularizeDerivative(int left_bin, int right_bin, Double_t scale = 1.0); // regularize difference of two output bins (1st derivative) + Int_t RegularizeCurvature(int left_bin, int center_bin, int right_bin, Double_t scale_left = 1.0, Double_t scale_right = 1.0); // regularize curvature of three output bins (2nd derivative) + Int_t RegularizeBins(int start, int step, int nbin, ERegMode regmode); // regularize a 1-dimensional curve + Int_t RegularizeBins2D(int start_bin, int step1, int nbin1, int step2, int nbin2, ERegMode regmode); // regularize a 2-dimensional grid + /// get numerical accuracy for Eigenvalue analysis when inverting + /// matrices with rank problems + inline Double_t GetEpsMatrix(void) const { return fEpsMatrix; } + /// set numerical accuracy for Eigenvalue analysis when inverting + /// matrices with rank problems void SetEpsMatrix(Double_t eps); // set accuracy for eigenvalue analysis ClassDef(TUnfold, TUnfold_CLASS_VERSION) //Unfolding with support for L-curve analysis diff --git a/hist/hist/inc/TUnfoldBinning.h b/hist/unfold/inc/TUnfoldBinning.h similarity index 64% rename from hist/hist/inc/TUnfoldBinning.h rename to hist/unfold/inc/TUnfoldBinning.h index 2ec68b718f51275f840e731af35f69ded3dbb03a..2384ab90762aa3f9087c83147547239783fc7d8c 100644 --- a/hist/hist/inc/TUnfoldBinning.h +++ b/hist/unfold/inc/TUnfoldBinning.h @@ -1,9 +1,13 @@ // Author: Stefan Schmitt // DESY, 10/08/11 -// Version 17.1, in parallel to TUnfold +// Version 17.5, in parallel to changes in TUnfold // // History: +// Version 17.4, bug fix with error handling +// Version 17.3, bug fix with underflow/overflow bins +// Version 17.2, new option isPeriodic +// Version 17.1, in parallel to TUnfold // Version 17.0, initial version, numbered in parallel to TUnfold #ifndef ROOT_TUnfoldBinning @@ -42,31 +46,48 @@ #include <TNamed.h> #include <TObjArray.h> #include <TObjString.h> - -class TAxis; -class TF1; +#include <TAxis.h> +#include <TF1.h> class TUnfoldBinning : public TNamed { protected: - TUnfoldBinning *parentNode; // mother node - TUnfoldBinning *childNode; // first daughter node - TUnfoldBinning *nextNode; // next sister - TUnfoldBinning *prevNode; // previous sister - TObjArray *fAxisList; // for each axis the bin borders (TVectorD) - TObjArray *fAxisLabelList; // for each axis its name (TObjString) - Int_t fHasUnderflow,fHasOverflow; // bit fields indicating whether there are underflow/overflow bins on the axes - Int_t fDistributionSize; // number of bins in this node's distribution - Int_t fFirstBin; // global bin number of the first bin - Int_t fLastBin; // global bin number of the last(+1) bin - TF1 *fBinFactorFunction; // function to calculate user factor from bin centres (default function is a constant) - Double_t fBinFactorConstant; // scale factor on user factor + /// mother node + TUnfoldBinning *parentNode; + /// first daughter node + TUnfoldBinning *childNode; + /// next sister + TUnfoldBinning *nextNode; + /// previous sister + TUnfoldBinning *prevNode; + /// for each axis the bin borders (TVectorD) + TObjArray *fAxisList; + /// for each axis its name (TObjString), or names of unconnected bins + TObjArray *fAxisLabelList; + /// bit fields indicating whether there are underflow bins on the axes + Int_t fHasUnderflow; + /// bit fields indicating whether there are overflow bins on the axes + Int_t fHasOverflow; + /// number of bins in this node's distribution + Int_t fDistributionSize; + /// global bin number of the first bin + Int_t fFirstBin; + /// global bin number of the last(+1) bin, including daughters + Int_t fLastBin; + /// function to calculate a scale factor from bin centres (may be a TF1 or a TVectorD + TObject *fBinFactorFunction; + /// common scale factor for all bins of this node + Double_t fBinFactorConstant; public: /********************* setup **************************/ - + enum { + /// maximum numner of axes per distribution + MAXDIM=32 + }; TUnfoldBinning(const char *name=0,Int_t nBins=0,const char *binNames=0); // create a new root node with a given number of unconnected bins TUnfoldBinning(const TAxis &axis,Int_t includeUnderflow,Int_t includeOverflow); // create a binning scheme with one axis - TUnfoldBinning *AddBinning(TUnfoldBinning *binning); // add a new node to the TUnfoldBinning tree + TUnfoldBinning *AddBinning + (TUnfoldBinning *binning); // add a new node to the TUnfoldBinning tree TUnfoldBinning *AddBinning(const char *name,Int_t nBins=0,const char *binNames=0); // add a new node to the TUnfoldBinning tree Bool_t AddAxis(const char *name,Int_t nBins,const Double_t *binBorders, Bool_t hasUnderflow,Bool_t hasOverflow); // add an axis (variable bins) to the distribution associated with this node @@ -74,16 +95,21 @@ class TUnfoldBinning : public TNamed { Bool_t hasUnderflow,Bool_t hasOverflow); // add an axis (equidistant bins) to the distribution associated with this node Bool_t AddAxis(const TAxis &axis,Bool_t includeUnderflow,Bool_t includeOverflow); // add an axis (from TAxis instance) to the distribution associated with this node virtual ~TUnfoldBinning(void); - void PrintStream(std::ostream &out,Int_t indent=0) const; - inline void SetBinFactorFunction(Double_t normalisation,TF1 *userFunc=0) { - fBinFactorConstant=normalisation; fBinFactorFunction=userFunc; }// define function to calculate bin factor + void PrintStream(std::ostream &out,Int_t indent=0,int debug=0) const; + void SetBinFactorFunction(Double_t normalisation,TF1 *userFunc=0); // define function to calculate bin factor. Note: the function is not owned by this class /********************* Navigation **********************/ - inline TUnfoldBinning const *GetChildNode(void) const { return childNode; } // first daughter - inline TUnfoldBinning const *GetPrevNode(void) const { return prevNode; } // previoous sister - inline TUnfoldBinning const *GetNextNode(void) const { return nextNode; } // next sister - inline TUnfoldBinning const *GetParentNode(void) const { return parentNode; } // mother + /// first daughter node + inline TUnfoldBinning const *GetChildNode(void) const { return childNode; } + /// previous sister node + inline TUnfoldBinning const *GetPrevNode(void) const { return prevNode; } + /// next sister node + inline TUnfoldBinning const *GetNextNode(void) const { return nextNode; } + /// mother node + inline TUnfoldBinning const *GetParentNode(void) const { return parentNode; } TUnfoldBinning const *FindNode(char const *name) const; // find node by name + /// return root node of the binnig scheme + TUnfoldBinning const *GetRootNode(void) const; /********************* Create THxx histograms **********/ Int_t GetTH1xNumberOfBins(Bool_t originalAxisBinning=kTRUE,const char *axisSteering=0) const; // get number of bins of a one-dimensional histogram TH1 @@ -96,49 +122,66 @@ class TUnfoldBinning : public TNamed { Bool_t originalYAxisBinning=kFALSE, char const *histogramTitle=0); // create 2D histogram with one binning on the x axis and the other binning on the y axis TH1 *ExtractHistogram(const char *histogramName,const TH1 *globalBins,const TH2 *globalBinsEmatrix=0,Bool_t originalAxisBinning=kTRUE,const char *axisSteering=0) const; // extract a distribution from the given set of global bins + /********************* Create and manipulate bin map ****/ + Int_t *CreateEmptyBinMap(void) const; // create empty bin map + void SetBinMapEntry(Int_t *binMap,Int_t globalBin,Int_t destBin) const; // change mapping for a given global bin + Int_t FillBinMap1D(Int_t *binMap,const char *axisSteering, + Int_t firstBinX) const; // map bins from this distribution to destHist /********************* Calculate global bin number ******/ Int_t GetGlobalBinNumber(Double_t x) const; // get bin number 1-dim distribution Int_t GetGlobalBinNumber(Double_t x,Double_t y) const; // get bin number 2-dim distribution Int_t GetGlobalBinNumber(Double_t x,Double_t y,Double_t z) const; // get bin number 3-dim distribution - Int_t GetGlobalBinNumber(Double_t x0,Double_t x1,Double_t x2,Double_t x3) const; // get bin number for given variables, up to four-dimensional binning - Int_t GetGlobalBinNumber(const Double_t *x) const; // get bin number, up to 32 dimenstional binning - inline Int_t GetStartBin(void) const { return fFirstBin; } // first bin of this node - inline Int_t GetEndBin(void) const { return fLastBin; } // last+1 bin of this node (includes children) + Int_t GetGlobalBinNumber(Double_t x0,Double_t x1,Double_t x2,Double_t x3) const; // get bin number four-dimensional binning + Int_t GetGlobalBinNumber(Double_t x0,Double_t x1,Double_t x2,Double_t x3,Double_t x4) const; // get bin number five-dimensional binning + Int_t GetGlobalBinNumber(Double_t x0,Double_t x1,Double_t x2,Double_t x3,Double_t x4,Double_t x5) const; // get bin number six-dimensional binning + Int_t GetGlobalBinNumber(const Double_t *x,Int_t *isBelow=0,Int_t *isAbove=0) const; // get bin number, up to 32 dimensional binning + /// first bin of this node + inline Int_t GetStartBin(void) const { return fFirstBin; } + /// last+1 bin of this node (includes children) + inline Int_t GetEndBin(void) const { return fLastBin; } + virtual Bool_t IsBinFactorGlobal(void) const; // check whether global or local bin factor must be used + Double_t GetGlobalFactor(void) const; // return global factor /********************* access by global bin number ******/ TString GetBinName(Int_t iBin) const; // return bin name Double_t GetBinSize(Int_t iBin) const; // return bin size (in N dimensions) virtual Double_t GetBinFactor(Int_t iBin) const; // return user factor void GetBinUnderflowOverflowStatus(Int_t iBin,Int_t *uStatus,Int_t *oStatus) const; // return bit map indicating underflow and overflow status - void GetBinNeighbours(Int_t globalBin,Int_t axis, - Int_t *prev,Double_t *distPrev, - Int_t *next,Double_t *distNext) const; // get neighbour bins along an axis - /********************* access by bin number, given an axis steering ******/ - enum { MAXDIM=32 }; - const TUnfoldBinning *GetBinLocation(Int_t binTHxx,const char *axisSteering, - Int_t axisBins[MAXDIM]) const; // locate a given THxx bin for a given axis steering - void DecodeAxisSteering(const char *axisSteering,const char *options, - Int_t *isOptionGiven) const; // decode axis steering options + Int_t GetBinNeighbours(Int_t globalBin,Int_t axis, + Int_t *prev,Double_t *distPrev, + Int_t *next,Double_t *distNext, + Bool_t isPeriodic=kFALSE) const; // get neighbour bins along an axis /********************** access distribution properties *************/ - inline Int_t GetDistributionNumberOfBins(void) const { return fDistributionSize; } // number of bins in the distribution possibly including under/overflow - inline Int_t GetDistributionDimension(void) const { return fAxisList->GetEntriesFast(); } // query dimension of this node's distribution + /// number of bins in the distribution possibly including under/overflow + inline Int_t GetDistributionNumberOfBins(void) const { return fDistributionSize; } + /// query dimension of this node's distribution + inline Int_t GetDistributionDimension(void) const { return fAxisList->GetEntriesFast(); } virtual Double_t GetDistributionAverageBinSize(Int_t axis,Bool_t includeUnderflow, Bool_t includeOverflow) const; // get average bin size + /// get vector of bin borders for one axis inline TVectorD const *GetDistributionBinning(Int_t axis) const { - return (TVectorD const *)fAxisList->At(axis); } // get bin borders for some axis + return (TVectorD const *)fAxisList->At(axis); } + /// get name of an axis inline TString GetDistributionAxisLabel(Int_t axis) const { - return ((TObjString * const)fAxisLabelList->At(axis))->GetString(); }// get name of this axis + return ((TObjString * const)fAxisLabelList->At(axis))->GetString(); } virtual Double_t GetDistributionUnderflowBinWidth(Int_t axis) const; // width of underflow bin on the given axis virtual Double_t GetDistributionOverflowBinWidth(Int_t axis) const; // width of overflow bin on the given axis virtual Double_t GetDistributionBinCenter(Int_t axis,Int_t bin) const; // position of bin center on the given axis + Bool_t HasUnconnectedBins(void) const; // check whether this node has bins without axis + const TObjString *GetUnconnectedBinName(Int_t bin) const; // return bin name (if any) + /// check whether an axis has an underflow bin + Bool_t HasUnderflow(int axis) const { return fHasUnderflow & (1<<axis); } + /// check whether the axis has an overflow bin + Bool_t HasOverflow(int axis) const { return fHasOverflow & (1<<axis); } + void DecodeAxisSteering(const char *axisSteering,const char *options, + Int_t *isOptionGiven) const; // decode axis steering options protected: - TUnfoldBinning *GetRootNode(void); // return root node - TUnfoldBinning const *GetRootNode(void) const; // return root node + /// return root node + TUnfoldBinning *GetRootNode(void); void Initialize(Int_t nBins); Int_t UpdateFirstLastBin(Bool_t startWithRootNode=kTRUE); // update fFirstBin and fLastBin - Bool_t HasUnconnectedBins(void) const; // check whether this node has bins without axis TUnfoldBinning const *ToAxisBins(Int_t globalBin,Int_t *axisBins) const; // return distribution in which the bin is located - Int_t ToGlobalBin(Int_t const *axisBins) const; // return -1 if not inside distribution + Int_t ToGlobalBin(Int_t const *axisBins,Int_t *isBelow=0,Int_t *isAbove=0) const; // return -1 if not inside distribution TString BuildHistogramTitle(const char *histogramName,const char *histogramTitle, Int_t const *axisList) const; // construct histogram title TString BuildHistogramTitle2D(const char *histogramName,const char *histogramTitle, @@ -146,13 +189,12 @@ class TUnfoldBinning : public TNamed { Int_t GetTHxxBinning(Int_t maxDim,Int_t *axisBins,Int_t *axisList,const char *axisSteering) const; // get binning information for creating a THxx Int_t GetTHxxBinningSingleNode(Int_t maxDim,Int_t *axisBins,Int_t *axisList,const char *axisSteering) const; // get binning information for creating a THxx Int_t GetTHxxBinsRecursive(const char *axisSteering) const; // get binning information for creating a THxx - const TUnfoldBinning *GetBinLocationRecursive(Int_t &offset,const char *axisSteering, - Int_t axisBins[MAXDIM]) const; // locate a THxx bin offset for a given axis steering const TUnfoldBinning *GetNonemptyNode(void) const; // get the only nodes with non-empty distributions if there are multiple nodes, return 0 Int_t *CreateBinMap(const TH1 *hist,Int_t nDim,const Int_t *axisList,const char *axisSteering) const; // create mapping from global bins to a histogram Int_t FillBinMapRecursive(Int_t startBin,const char *axisSteering, Int_t *binMap) const; // fill bin map recursively Int_t FillBinMapSingleNode(const TH1 *hist,Int_t startBin,Int_t nDim,const Int_t *axisList,const char *axisSteering,Int_t *binMap) const; // fill bin map for a single node + void SetBinFactor(Double_t normalisation,TObject *factors); // define function to calculate bin factor. Note: the object is owned by this class, unless it is a function ClassDef(TUnfoldBinning, TUnfold_CLASS_VERSION) //Complex binning schemes for TUnfoldDensity }; diff --git a/hist/unfold/inc/TUnfoldBinningXML.h b/hist/unfold/inc/TUnfoldBinningXML.h new file mode 100644 index 0000000000000000000000000000000000000000..e843258dee9f5dd684a683822a896eac21aa1468 --- /dev/null +++ b/hist/unfold/inc/TUnfoldBinningXML.h @@ -0,0 +1,75 @@ +// Author: Stefan Schmitt +// DESY, 10/08/11 + +// Version 17.5, in parallel to changes in TUnfold +// +// History: +// Version 17.4, in parallel to changes in TUnfoldBinning +// Version 17.3, support for repeated bins with the same width +// Version 17.2, XML interface for class TUnfoldBinning + +#ifndef ROOT_TUnfoldBinningXML +#define ROOT_TUnfoldBinningXML + + +////////////////////////////////////////////////////////////////////////// +// // +// // +// TUnfoldBinningXML, an auxillary class to read and write // +// complex binning schemes in XML // +// // +// Citation: S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201] // +// // +////////////////////////////////////////////////////////////////////////// + +/* + This file is part of TUnfold. + + TUnfold 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 3 of the License, or + (at your option) any later version. + + TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "TUnfoldBinning.h" +#include <iostream> +#include <TNamed.h> +#include <TObjArray.h> +#include <TObjString.h> +#include <TXMLNode.h> +#include <TXMLDocument.h> +#include <iostream> + +class TXMLNode; +class TXMLDocument; + + +class TUnfoldBinningXML : public TUnfoldBinning { + public: + /********************** XML interface to read binning schemes *************/ +static TUnfoldBinningXML *ImportXML(const TXMLDocument *document,const char *name); // import binning scheme + static Int_t ExportXML(const TUnfoldBinning &binning,std::ostream &out,Bool_t writeHeader,Bool_t writeFooter,Int_t indent=0); // append binning scheme to file + Int_t ExportXML(const char *fileName) const; // export this binning scheme + static void WriteDTD(const char *fileName="tunfoldbinning.dtd"); // write DTD file + static void WriteDTD(std::ostream &out); // write DTD to stream + + /// construct a new binning scheme, for use with the root streamer + TUnfoldBinningXML (const char *name=0,Int_t nBins=0,const char *binNames=0) + : TUnfoldBinning (name,nBins,binNames) { } + protected: + static TUnfoldBinningXML *ImportXMLNode(TXMLNode *node); // import the given node as binning scheme + void AddAxisXML(TXMLNode *node); // import axis information +protected: + + ClassDef(TUnfoldBinningXML, TUnfold_CLASS_VERSION) //Complex binning schemes for TUnfoldDensity +}; + +#endif diff --git a/hist/hist/inc/TUnfoldDensity.h b/hist/unfold/inc/TUnfoldDensity.h similarity index 57% rename from hist/hist/inc/TUnfoldDensity.h rename to hist/unfold/inc/TUnfoldDensity.h index 7ffcb5b3a6967eed6b1e3e572cb6579eec3fbed8..d93981377b1a62405434c4c2c930c84506c36339 100644 --- a/hist/hist/inc/TUnfoldDensity.h +++ b/hist/unfold/inc/TUnfoldDensity.h @@ -1,10 +1,14 @@ // Author: Stefan Schmitt // DESY, 11/08/11 -// Version 17.1, add scan type RhoSquare +// Version 17.5, bug fix in TUnfold also corrects GetEmatrixSysUncorr() // // History: -// Version 17.0, support for density regularisation and complex binning schemes +// Version 17.4, in parallel to changes in TUnfoldBinning +// Version 17.3, in parallel to changes in TUnfoldBinning +// Version 17.2, in parallel to changes in TUnfoldBinning +// Version 17.1, add scan type RhoSquare +// Version 17.0, support for density regularisation and complex binning schemes #ifndef ROOT_TUnfoldDensity #define ROOT_TUnfoldDensity @@ -43,25 +47,33 @@ class TUnfoldDensity : public TUnfoldSys { protected: - const TUnfoldBinning * fConstOutputBins; // binning scheme for the output - const TUnfoldBinning * fConstInputBins; // binning scheme for the input - TUnfoldBinning *fOwnedOutputBins; // output binning scheme if owner - TUnfoldBinning *fOwnedInputBins; // input binning scheme if owner - TUnfoldBinning *fRegularisationConditions; // binning scheme for the regularisation conditions + /// binning scheme for the output (truth level) + const TUnfoldBinning * fConstOutputBins; + /// binning scheme for the input (detector level) + const TUnfoldBinning * fConstInputBins; + /// pointer to output binning scheme if owned by this class + TUnfoldBinning *fOwnedOutputBins; + /// pointer to input binning scheme if owned by this class + TUnfoldBinning *fOwnedInputBins; + /// binning scheme for the regularisation conditions + TUnfoldBinning *fRegularisationConditions; public: + /// choice of regularisation scale factors to cinstruct the matrix L enum EDensityMode { - kDensityModeeNone=0, - kDensityModeBinWidth=1, - kDensityModeUser=2, - kDensityModeBinWidthAndUser=3 + /// no scale factors, matrix L is similar to unity matrix + kDensityModeNone=0, + /// scale factors from multidimensional bin width + kDensityModeBinWidth=1, + /// scale factors from user function in TUnfoldBinning + kDensityModeUser=2, + /// scale factors from multidimensional bin width and user function + kDensityModeBinWidthAndUser=3 }; protected: virtual TString GetOutputBinName(Int_t iBinX) const; // name a bin - TUnfoldDensity(void); // constructor for derived classes, do nothing - Double_t GetDensityFactor(EDensityMode densityMode,Int_t iBin) const; // density correction factor for this bin void RegularizeDistributionRecursive (const TUnfoldBinning *binning,ERegMode regmode, @@ -72,29 +84,37 @@ class TUnfoldDensity : public TUnfoldSys { EDensityMode densityMode,const char *axisSteering); // regularize the distribution of one binning node public: + TUnfoldDensity(void); // constructor for derived classes, do nothing + TUnfoldDensity(const TH2 *hist_A, EHistMap histmap, - ERegMode regmode = kRegModeCurvature, - EConstraint constraint=kEConstraintArea, - EDensityMode densityMode=kDensityModeBinWidthAndUser, - const TUnfoldBinning *outputBins=0, - const TUnfoldBinning *inputBins=0, - const char *regularisationDistribution=0, - const char *regularisationAxisSteering="*[UOB]"); // constructor for using the histogram classes. Default regularisation is on the curvature of the bin-width normalized density, excluding underflow and overflow bins + ERegMode regmode = kRegModeCurvature, + EConstraint constraint=kEConstraintArea, + EDensityMode densityMode=kDensityModeBinWidthAndUser, + const TUnfoldBinning *outputBins=0, + const TUnfoldBinning *inputBins=0, + const char *regularisationDistribution=0, + const char *regularisationAxisSteering="*[UOB]"); // constructor for using the histogram classes. Default regularisation is on the curvature of the bin-width normalized density, excluding underflow and overflow bins virtual ~ TUnfoldDensity(void); // delete data members void RegularizeDistribution(ERegMode regmode,EDensityMode densityMode, - const char *distribution, - const char *axisSteering); // regularize distribution(s) of the output binning scheme - - - enum EScanTauMode { // scan mode of correlation scan - kEScanTauRhoAvg =0, // average global correlation coefficient (from TUnfold::GetRhoI()) - kEScanTauRhoMax =1, // maximum global correlation coefficient (from TUnfold::GetRhoI()) - kEScanTauRhoAvgSys =2, // average global correlation coefficient (from TUnfoldSys::GetRhoItotal()) - kEScanTauRhoMaxSys =3, // maximum global correlation coefficient (from TUnfoldSys::GetRhoItotal()) - kEScanTauRhoSquareAvg =4, // average global correlation coefficient squared (from TUnfold::GetRhoI()) - kEScanTauRhoSquareAvgSys =5 // average global correlation coefficient squared (from TUnfoldSys::GetRhoItotal()) + const char *distribution, + const char *axisSteering); // regularize distribution(s) of the output binning scheme + + /// scan mode for correlation scan + enum EScanTauMode { + /// average global correlation coefficient (from TUnfold::GetRhoI()) + kEScanTauRhoAvg =0, + /// maximum global correlation coefficient (from TUnfold::GetRhoI()) + kEScanTauRhoMax =1, + /// average global correlation coefficient (from TUnfoldSys::GetRhoItotal()) + kEScanTauRhoAvgSys =2, + /// maximum global correlation coefficient (from TUnfoldSys::GetRhoItotal()) + kEScanTauRhoMaxSys =3, + /// average global correlation coefficient squared (from TUnfold::GetRhoI()) + kEScanTauRhoSquareAvg =4, + /// average global correlation coefficient squared (from TUnfoldSys::GetRhoItotal()) + kEScanTauRhoSquareAvgSys =5 }; virtual Int_t ScanTau(Int_t nPoint,Double_t tauMin,Double_t tauMax, @@ -104,10 +124,10 @@ class TUnfoldDensity : public TUnfoldSys { TH1 *GetOutput(const char *histogramName, const char *histogramTitle=0,const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE) const; // get unfolding result + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE) const; // get unfolding result TH1 *GetBias(const char *histogramName, const char *histogramTitle=0,const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE) const; // get bias + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE) const; // get bias TH1 *GetFoldedOutput(const char *histogramName, const char *histogramTitle=0, const char *distributionName=0, @@ -116,8 +136,7 @@ class TUnfoldDensity : public TUnfoldSys { TH1 *GetBackground(const char *histogramName,const char *bgrSource=0, const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE,Int_t includeError=3, - Bool_t clearHist=kTRUE) const; // get background source + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE,Int_t includeError=3) const; // get background source TH1 *GetInput(const char *histogramName,const char *histogramTitle=0, const char *distributionName=0, const char *projectionMode=0,Bool_t useAxisBinning=kTRUE) const; // get unfolding input @@ -125,57 +144,58 @@ class TUnfoldDensity : public TUnfoldSys { const char *histogramName, const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get systematic shifts from one systematic source + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get systematic shifts from one systematic source TH1 *GetDeltaSysBackgroundScale(const char *bgrSource, const char *histogramName, const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlated uncertainty induced by the scale uncertainty of a background source + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlated uncertainty induced by the scale uncertainty of a background source TH1 *GetDeltaSysTau(const char *histogramName, const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlated uncertainty from varying tau + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlated uncertainty from varying tau TH2 *GetEmatrixSysUncorr(const char *histogramName, - const char *histogramTitle=0, - const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error matrix contribution from uncorrelated errors on the matrix A + const char *histogramTitle=0, + const char *distributionName=0, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error matrix contribution from uncorrelated errors on the matrix A TH2 *GetEmatrixSysBackgroundUncorr(const char *bgrSource, - const char *histogramName, - const char *histogramTitle=0, - const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error matrix from uncorrelated error of one background source + const char *histogramName, + const char *histogramTitle=0, + const char *distributionName=0, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error matrix from uncorrelated error of one background source TH2 *GetEmatrixInput(const char *histogramName, const char *histogramTitle=0, - const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error contribution from input vector + const char *distributionName=0, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get error contribution from input vector TH2 *GetEmatrixTotal(const char *histogramName, - const char *histogramTitle=0, - const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get total error including systematic,statistical,background,tau errors + const char *histogramTitle=0, + const char *distributionName=0, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get total error including systematic,statistical,background,tau errors TH1 *GetRhoIstatbgr(const char *histogramName,const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE, TH2 **ematInv=0); // get global correlation coefficients, stat+bgr errors only (from TUnfold) TH1 *GetRhoItotal(const char *histogramName,const char *histogramTitle=0, const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE, TH2 **ematInv=0); // get global correlation coefficients, including systematic errors (from TUnfoldSys) TH2 *GetRhoIJtotal(const char *histogramName, - const char *histogramTitle=0, - const char *distributionName=0, - const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlation coefficients + const char *histogramTitle=0, + const char *distributionName=0, + const char *projectionMode=0,Bool_t useAxisBinning=kTRUE); // get correlation coefficients TH2 *GetL(const char *histogramName, const char *histogramTitle=0, Bool_t useAxisBinning=kTRUE); // get regularisation matrix - TH1 *GetLxMinusBias(const char *histogramName,const char *histogramTitle=0); // get vector L(x-bias) of regularisation conditions + TH1 *GetLxMinusBias(const char *histogramName,const char *histogramTitle=0); // get vector L(x-bias) of regularisation conditions TH2 *GetProbabilityMatrix(const char *histogramName, - const char *histogramTitle=0,Bool_t useAxisBinning=kTRUE) const; // get matrix of probabilities + const char *histogramTitle=0,Bool_t useAxisBinning=kTRUE) const; // get matrix of probabilities const TUnfoldBinning *GetInputBinning(const char *distributionName=0) const; // find binning scheme for input bins const TUnfoldBinning *GetOutputBinning(const char *distributionName=0) const; // find binning scheme for output bins -TUnfoldBinning *GetLBinning(void) const { return fRegularisationConditions; } // binning scheme for regularisation conditions (matrix L) - ClassDef(TUnfoldDensity, TUnfold_CLASS_VERSION) //Unfolding with densisty regularisation + /// return binning scheme for regularisation conditions (matrix L) +TUnfoldBinning *GetLBinning(void) const { return fRegularisationConditions; } + ClassDef(TUnfoldDensity, TUnfold_CLASS_VERSION) //Unfolding with density regularisation }; #endif diff --git a/hist/hist/inc/TUnfoldSys.h b/hist/unfold/inc/TUnfoldSys.h similarity index 71% rename from hist/hist/inc/TUnfoldSys.h rename to hist/unfold/inc/TUnfoldSys.h index 75e0344d5d513ef8eb191a8f8d88e2ed5b807220..9943efb94b74ab95804fa5a45344abd5c4a1a007 100644 --- a/hist/hist/inc/TUnfoldSys.h +++ b/hist/unfold/inc/TUnfoldSys.h @@ -1,16 +1,20 @@ // Author: Stefan Schmitt // DESY, 23/01/09 -// Version 17.1, bug fix with background uncertainty +// Version 17.5, bug fixes in TUnfold fix problem with GetEmatrixSysUncorr // // History: -// Version 17.0, possibility to specify an error matrix with SetInput -// Version 16.2, bug-fix with the calculation of background errors -// Version 16.1, parallel to changes in TUnfold -// Version 16.0, parallel to changes in TUnfold -// Version 15, fix bugs with uncorr. uncertainties, add backgnd subtraction -// Version 14, with changes in TUnfoldSys.cxx -// Version 13, support for systematic errors +// Version 17.4, in parallel to changes in TUnfoldBinning +// Version 17.3, in parallel to changes in TUnfoldBinning +// Version 17.2, add methods to find back systematic and background sources +// Version 17.1, bug fix with background uncertainty +// Version 17.0, possibility to specify an error matrix with SetInput +// Version 16.2, bug-fix with the calculation of background errors +// Version 16.1, parallel to changes in TUnfold +// Version 16.0, parallel to changes in TUnfold +// Version 15, fix bugs with uncorr. uncertainties, add backgnd subtraction +// Version 14, with changes in TUnfoldSys.cxx +// Version 13, support for systematic errors #ifndef ROOT_TUnfoldSys #define ROOT_TUnfoldSys @@ -43,32 +47,46 @@ along with TUnfold. If not, see <http://www.gnu.org/licenses/>. */ +#include <TMap.h> +#include <TSortedList.h> #include "TUnfold.h" -class TMap; - class TUnfoldSys : public TUnfold { private: void InitTUnfoldSys(void); // initialize all data members protected: - TMatrixDSparse *fDAinRelSq; // Input: normalized errors from input matrix - TMatrixD* fDAinColRelSq; // Input: normalized column err.sq. (inp.matr.) - TMatrixD* fAoutside; // Input: underflow/overflow bins - TMap *fSysIn; // Input: correlated errors - TMap *fBgrIn; // Input: size of background sources - TMap *fBgrErrUncorrInSq; // Input: uncorr error squared from bgr sources - TMap *fBgrErrScaleIn; // Input: background sources correlated error - Double_t fDtau; // Input: error on tau - TMatrixD *fYData; // Input: fY prior to bgr subtraction - TMatrixDSparse *fVyyData; // Input: error on fY prior to bgr subtraction - TMatrixDSparse *fEmatUncorrX; // Result: syst.error from fDA2 on fX - TMatrixDSparse *fEmatUncorrAx; // Result: syst.error from fDA2 on fAx - TMap *fDeltaCorrX; // Result: syst.shift from fSysIn on fX - TMap *fDeltaCorrAx; // Result: syst.shift from fSysIn on fAx - TMatrixDSparse *fDeltaSysTau; // Result: systematic shift from tau + /// Input: normalized errors from input matrix + TMatrixDSparse *fDAinRelSq; + /// Input: normalized column err.sq. (inp.matr.) + TMatrixD* fDAinColRelSq; + /// Input: underflow/overflow bins + TMatrixD* fAoutside; + /// Input: correlated errors + TMap *fSysIn; + /// Input: size of background sources + TMap *fBgrIn; + /// Input: uncorr error squared from bgr sources + TMap *fBgrErrUncorrInSq; + /// Input: background sources correlated error + TMap *fBgrErrScaleIn; + /// Input: error on tau + Double_t fDtau; + /// Input: fY prior to bgr subtraction + TMatrixD *fYData; + /// Input: error on fY prior to bgr subtraction + TMatrixDSparse *fVyyData; + /// Result: syst.error from fDA2 on fX + TMatrixDSparse *fEmatUncorrX; + /// Result: syst.error from fDA2 on fAx + TMatrixDSparse *fEmatUncorrAx; + /// Result: syst.shift from fSysIn on fX + TMap *fDeltaCorrX; + /// Result: syst.shift from fSysIn on fAx + TMap *fDeltaCorrAx; + /// Result: systematic shift from tau + TMatrixDSparse *fDeltaSysTau; protected: - TUnfoldSys(void); // for derived classes virtual void ClearResults(void); // clear all results virtual void PrepareSysError(void); // common calculations for syst.errors virtual TMatrixDSparse *PrepareUncorrEmat(const TMatrixDSparse *m1,const TMatrixDSparse *m2); // calculate uncorrelated error matrix @@ -80,36 +98,43 @@ class TUnfoldSys : public TUnfold { TMatrixDSparse *GetSummedErrorMatrixYY(void); TMatrixDSparse *GetSummedErrorMatrixXX(void); public: - enum ESysErrMode { // meaning of the argument to AddSysError() - kSysErrModeMatrix=0, // matrix is an alternative to the default matrix, the errors are the difference to the original matrix - kSysErrModeShift=1, // matrix gives the absolute shifts - kSysErrModeRelative=2 // matrix gives the relative shifts + /// type of matrix specified with AddSysError() + enum ESysErrMode { + /// matrix is an alternative to the default matrix, the errors are the difference to the original matrix + kSysErrModeMatrix=0, + /// matrix gives the absolute shifts + kSysErrModeShift=1, + /// matrix gives the relative shifts + kSysErrModeRelative=2 }; TUnfoldSys(const TH2 *hist_A, EHistMap histmap, ERegMode regmode = kRegModeSize, EConstraint constraint=kEConstraintArea); // constructor + TUnfoldSys(void); // for derived classes virtual ~ TUnfoldSys(void); // delete data members void AddSysError(const TH2 *sysError,const char *name, EHistMap histmap, ESysErrMode mode); // add a systematic error source - Bool_t GetDeltaSysSource(TH1 *hist_delta,const char *source, - const Int_t *binMap=0); // get systematic shifts from one systematic source void SubtractBackground(const TH1 *hist_bgr,const char *name, Double_t scale=1.0, Double_t scale_error=0.0); // subtract background prior to unfolding - void GetBackground(TH1 *bgr,const char *bgrSource=0,const Int_t *binMap=0,Int_t includeError=3,Bool_t clearHist=kTRUE) const; // get background as histogram virtual Int_t SetInput(const TH1 *hist_y,Double_t scaleBias=0.0,Double_t oneOverZeroError=0.0,const TH2 *hist_vyy=0,const TH2 *hist_vyy_inv=0); // define input consistently in case of background subtraction - Bool_t GetDeltaSysBackgroundScale(TH1 *delta,const char *source, - const Int_t *binMap=0); // get correlated uncertainty induced by the scale uncertainty of a background source void SetTauError(Double_t delta_tau); // set uncertainty on tau - Bool_t GetDeltaSysTau(TH1 *delta,const Int_t *binMap=0); // get correlated uncertainty from varying tau - void GetEmatrixSysUncorr(TH2 *ematrix,const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix contribution from uncorrelated errors on the matrix A - void GetEmatrixSysSource(TH2 *ematrix,const char *source, - const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix from one systematic source + TSortedList *GetBgrSources(void) const; // get names of background sources + TSortedList *GetSysSources(void) const; // get names of systematic sources + void GetBackground(TH1 *bgr,const char *bgrSource=0,const Int_t *binMap=0,Int_t includeError=3,Bool_t clearHist=kTRUE) const; // get background as histogram void GetEmatrixSysBackgroundUncorr(TH2 *ematrix,const char *source, const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix from uncorrelated error of one background source void GetEmatrixSysBackgroundScale(TH2 *ematrix,const char *source, const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix from the scale error of one background source + Bool_t GetDeltaSysBackgroundScale(TH1 *delta,const char *source, + const Int_t *binMap=0); // get correlated uncertainty induced by the scale uncertainty of a background source + void GetEmatrixSysUncorr(TH2 *ematrix,const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix contribution from uncorrelated errors on the matrix A + void GetEmatrixSysSource(TH2 *ematrix,const char *source, + const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix from one systematic source + Bool_t GetDeltaSysSource(TH1 *hist_delta,const char *source, + const Int_t *binMap=0); // get systematic shifts from one systematic source void GetEmatrixSysTau(TH2 *ematrix, const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error matrix from tau variation + Bool_t GetDeltaSysTau(TH1 *delta,const Int_t *binMap=0); // get correlated uncertainty from varying tau void GetEmatrixInput(TH2 *ematrix,const Int_t *binMap=0,Bool_t clearEmat=kTRUE); // get error contribution from input vector void GetEmatrixTotal(TH2 *ematrix,const Int_t *binMap=0); // get total error including systematic,statistical,background,tau errors void GetRhoItotal(TH1 *rhoi,const Int_t *binMap=0,TH2 *invEmat=0); // get global correlation coefficients including systematic,statistical,background,tau errors diff --git a/hist/hist/src/TUnfold.cxx b/hist/unfold/src/TUnfold.cxx similarity index 57% rename from hist/hist/src/TUnfold.cxx rename to hist/unfold/src/TUnfold.cxx index e497e73adb5324d3953c24f66a34962c25ef98d7..75dce1ed0a1821d3f0a3c005d8390fe09b6733e7 100644 --- a/hist/hist/src/TUnfold.cxx +++ b/hist/unfold/src/TUnfold.cxx @@ -1,265 +1,117 @@ -// Author: Stefan Schmitt -// DESY, 13/10/08 - -// Version 17.1, bug fixes in GetFoldedOutput, GetOutput -// -// History: -// Version 17.0, option to specify an error matrix with SetInput(), new ScanRho() method -// Version 16.2, in parallel to bug-fix in TUnfoldSys -// Version 16.1, fix bug with error matrix in case kEConstraintArea is used -// Version 16.0, fix calculation of global correlations, improved error messages -// Version 15, simplified L-curve scan, new tau definition, new error calc., area preservation -// Version 14, with changes in TUnfoldSys.cxx -// Version 13, new methods for derived classes and small bug fix -// Version 12, report singular matrices -// Version 11, reduce the amount of printout -// Version 10, more correct definition of the L curve, update references -// Version 9, faster matrix inversion and skip edge points for L-curve scan -// Version 8, replace all TMatrixSparse matrix operations by private code -// Version 7, fix problem with TMatrixDSparse,TMatrixD multiplication -// Version 6, replace class XY by std::pair -// Version 5, replace run-time dynamic arrays by new and delete[] -// Version 4, fix new bug from V3 with initial regularisation condition -// Version 3, fix bug with initial regularisation condition -// Version 2, with improved ScanLcurve() algorithm -// Version 1, added ScanLcurve() method -// Version 0, stable version of basic unfolding algorithm +// @(#)root/unfold:$Id$ +// Author: Stefan Schmitt DESY, 13/10/08 /** \class TUnfold - \ingroup Hist - TUnfold is used to decompose a measurement y into several sources x - given the measurement uncertainties and a matrix of migrations A - - **For most applications, it is better to use TUnfoldDensity - instead of using TUnfoldSys or TUnfold** - - If you use this software, please consider the following citation - S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201] - - More documentation and updates are available on - http://www.desy.de/~sschmitt - - A short summary of the algorithm is given in the following: - the "best" x matching the measurement y within errors - is determined by minimizing the function - L1+L2+L3 - - where - L1 = (y-Ax)# Vyy^-1 (y-Ax) - L2 = tau^2 (L(x-x0))# L(x-x0) - L3 = lambda sum_i(y_i -(Ax)_i) - - [the notation # means that the matrix is transposed ] - - The term L1 is familiar from a least-square minimisation - The term L2 defines the regularisation (smootheness condition on x), - where the parameter tau^2 gives the strength of the regularisation - The term L3 is an optional area constraint with Lagrangian parameter - lambda, ensuring that that the normalisation of x is consistent with the - normalisation of y - - The method can be applied to a very large number of problems, - where the measured distribution y is a linear superposition - of several Monte Carlo shapes - - ## Input from measurement: - y: vector of measured quantities (dimension ny) - Vyy: covariance matrix for y (dimension ny x ny) - in many cases V is diagonal and calculated from the errors of y - - ## From simulation: - A: migration matrix (dimension ny x nx) - - ## Result - x: unknown underlying distribution (dimension nx) - The error matrix of x, V_xx, is also determined - - ## Regularisation - tau: parameter, defining the regularisation strength - L: matrix of regularisation conditions (dimension nl x nx) - depends on the structure of the input data - x0: bias distribution, from simulation - - ## Preservation of the area - lambda: Lagrangian multiplier - y_i: one component of the vector y - (Ax)_i: one component of the vector Ax - - - Determination of the unfolding result x: - - (a) not constrained: minimisation is performed as a function of x - for fixed lambda=0 - - (b) constrained: stationary point is found as a function of x and lambda - - The constraint can be useful to reduce biases on the result x - in cases where the vector y follows non-Gaussian probability densities - (example: Poisson statistics at counting experiments in particle physics) - - Some random examples: - 1. measure a cross-section as a function of, say, E_T(detector) - and unfold it to obtain the underlying distribution E_T(generator) - 2. measure a lifetime distribution and unfold the contributions from - different flavours - 3. measure the transverse mass and decay angle - and unfold for the true mass distribution plus background - - ## Documentation - Some technical documentation is available here: - http://www.desy.de/~sschmitt - - Note: - - For most applications it is better to use the derived class - TUnfoldSys or even better TUnfoldDensity - - TUnfoldSys extends the functionality of TUnfold - such that systematic errors are propagated to the result - and that the unfolding can be done with proper background - subtraction - - TUnfoldDensity extends further the functionality of TUnfoldSys - complex binning schemes are supported - The binning of input histograms is handeld consistently: - (1) the regularisation may be done by density, - i.e respecting the bin widths - (2) methods are provided which preserve the proper binning - of the result histograms - ## Implementation - The result of the unfolding is calculated as follows: - - Lsquared = L#L regularisation conditions squared - - epsilon_j = sum_i A_ij vector of efficiencies - - E^-1 = ((A# Vyy^-1 A)+tau^2 Lsquared) - - x = E (A# Vyy^-1 y + tau^2 Lsquared x0 +lambda/2 * epsilon) is the result - - The derivatives - dx_k/dy_i - dx_k/dA_ij - dx_k/d(tau^2) - are calculated for further usage. - - The covariance matrix V_xx is calculated as: - Vxx_ij = sum_kl dx_i/dy_k Vyy_kl dx_j/dy_l - - ## Warning: - The algorithm is based on "standard" matrix inversion, with the - known limitations in numerical accuracy and computing cost for - matrices with large dimensions. - - Thus the algorithm should not used for large dimensions of x and y - nx should not be much larger than 200 - ny should not be much larger than 1000 - -## Proper choice of tau - One of the difficult questions is about the choice of tau. - The method implemented in TUnfold is the L-curve method: - a two-dimensional curve is plotted - x-axis: log10(chisquare) - y-axis: log10(regularisation condition) - In many cases this curve has an L-shape. The best choice of tau is in the - kink of the L - - Within TUnfold a simple version of the L-curve analysis is available. - It tests a given number of points in a predefined tau-range and searches - for the maximum of the curvature in the L-curve (kink position). - if no tau range is given, the range of the scan is determined automatically - - A nice overview of the L-curve method is given in: - The L-curve and Its Use in the Numerical Treatment of Inverse Problems - (2000) by P. C. Hansen, in Computational Inverse Problems in - Electrocardiology, ed. P. Johnston, - Advances in Computational Bioengineering - http://www.imm.dtu.dk/~pch/TR/Lcurve.ps - - ## Alternative Regularisation conditions - Regularisation is needed for most unfolding problems, in order to avoid - large oscillations and large correlations on the output bins. - It means that some extra conditions are applied on the output bins - - Within TUnfold these conditions are posed on the difference (x-x0), where - x: unfolding output - x0: the bias distribution, by default calculated from - the input matrix A. There is a method SetBias() to change the - bias distribution. - The 3rd argument to DoUnfold() is a scale factor applied to the bias - bias_default[j] = sum_i A[i][j] - x0[j] = scaleBias*bias[j] - The scale factor can be used to - (a) completely suppress the bias by setting it to zero - (b) compensate differences in the normalisation between data - and Monte Carlo - - If the regularisation is strong, i.e. large parameter tau, - then the distribution x or its derivatives will look like the bias - distribution. If the parameter tau is small, the distribution x is - independent of the bias. - - Three basic types of regularisation are implemented in TUnfold - - condition | regularisation - -----------------|------------------------------------ - kRegModeNone | none - kRegModeSize | minimize the size of (x-x0) - kRegModeDerivative | minimize the 1st derivative of (x-x0) - kRegModeCurvature | minimize the 2nd derivative of (x-x0) - - kRegModeSize is the regularisation scheme which often is found in - literature. The second derivative is often named curvature. - Sometimes the bias is not discussed, equivalent to a bias scale factor - of zero. - - The regularisation schemes kRegModeDerivative and - kRegModeCurvature have the nice feature that they create correlations - between x-bins, whereas the non-regularized unfolding tends to create - negative correlations between bins. For these regularisation schemes the - parameter tau could be tuned such that the correlations are smallest, - as an alternative to the L-curve method. - - If kRegModeSize is chosen or if x is a smooth function through all bins, - the regularisation condition can be set on all bins together by giving - the appropriate argument in the constructor (see examples above). - - If x is composed of independent groups of bins (for example, - signal and background binning in two variables), it may be necessary to - set regularisation conditions for the individual groups of bins. - In this case, give kRegModeNone in the constructor and specify - the bin grouping with calls to - RegularizeBins() specify a 1-dimensional group of bins - RegularizeBins2D() specify a 2-dimensional group of bins - - Note, the class TUnfoldDensity provides an automatic setup of complex - regularisation schemes - - For ultimate flexibility, the regularisation condition can be set on each - bin individually - -> give kRegModeNone in the constructor and use - RegularizeSize() regularize one bin - RegularizeDerivative() regularize the slope given by two bins - RegularizeCurvature() regularize the curvature given by three bins - AddRegularisationCondition() - define an arbitrary regularization condition +\ingroup Unfold + +An algorithm to unfold distributions from detector to truth level + +TUnfold is used to decompose a measurement y into several sources x, +given the measurement uncertainties and a matrix of migrations A. +The method can be applied to a large number of problems, +where the measured distribution y is a linear superposition +of several Monte Carlo shapes. Beyond such a simple template fit, +TUnfold has an adjustable regularisation term and also supports an +optional constraint on the total number of events. + +<b>For most applications, it is better to use the derived class +TUnfoldDensity instead of TUnfold</b>. TUnfoldDensity adds various +features to TUnfold, such as: +background subtraction, propagation of systematic uncertainties, +complex multidimensional arrangements of the bins. For innocent +users, the most notable improvement of TUnfoldDensity over TUnfold are +the getter functions. For TUnfold, histograms have to be booked by the +user and the getter functions fill the histogram bins. TUnfoldDensity +simply returns a new, already filled histogram. + +If you use this software, please consider the following citation + +<b>S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201]</b> + +Detailed documentation and updates are available on +http://www.desy.de/~sschmitt + +Brief recipe to use TUnfold: + + - a matrix (truth,reconstructed) is given as a two-dimensional histogram + as argument to the constructor of TUnfold + - a vector of measurements is given as one-dimensional histogram using + the SetInput() method + - The unfolding is performed + + - either once with a fixed parameter tau, method DoUnfold(tau) + - or multiple times in a scan to determine the best choice of tau, + method ScanLCurve() + + - Unfolding results are retrieved using various GetXXX() methods + +Basic formulae: +\f[ +\chi^{2}_{A}=(Ax-y)^{T}V_{yy}^{-1}(Ax-y) \\ +\chi^{2}_{L}=(x-f*x_{0})^{T}L^{T}L(x-f*x_{0}) \\ +\chi^{2}_{unf}=\chi^{2}_{A}+\tau^{2}\chi^{2}_{L}+\lambda\Sigma_{i}(Ax-y)_{i} +\f] + + - \f$ x \f$:result, + - \f$ A \f$:probabilities, + - \f$ y \f$:data, + - \f$ V_{yy} \f$:data covariance, + - \f$ f \f$:bias scale, + - \f$ x_{0} \f$:bias, + - \f$ L \f$:regularisation conditions, + - \f$ \tau \f$:regularisation strength, + - \f$ \lambda \f$:Lagrangian multiplier. + + Without area constraint, \f$ \lambda \f$ is set to zero, and +\f$ \chi^{2}_{unf} \f$ is minimized to determine \f$ x \f$. +With area constraint, both \f$ x \f$ and \f$ \lambda \f$ are determined. + +-------------------------------------------------------------------------------- +This file is part of TUnfold. + +TUnfold 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 3 of the License, or +(at your option) any later version. + +TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. + +<b>Version 17.6, updated doxygen-style comments, add one argument for scanLCurve </b> + +#### History: + - Version 17.5, fix memory leak with fVyyInv, bugs in GetInputInverseEmatrix(), GetInput(), bug in MultiplyMSparseMSparseTranspVector + - Version 17.4, in parallel to changes in TUnfoldBinning + - Version 17.3, in parallel to changes in TUnfoldBinning + - Version 17.2, bug fix with GetProbabilityMatrix + - Version 17.1, bug fixes in GetFoldedOutput, GetOutput + - Version 17.0, option to specify an error matrix with SetInput(), new ScanRho() method + - Version 16.2, in parallel to bug-fix in TUnfoldSys + - Version 16.1, fix bug with error matrix in case kEConstraintArea is used + - Version 16.0, fix calculation of global correlations, improved error messages + - Version 15, simplified L-curve scan, new tau definition, new error calc., area preservation + - Version 14, with changes in TUnfoldSys.cxx + - Version 13, new methods for derived classes and small bug fix + - Version 12, report singular matrices + - Version 11, reduce the amount of printout + - Version 10, more correct definition of the L curve, update references + - Version 9, faster matrix inversion and skip edge points for L-curve scan + - Version 8, replace all TMatrixSparse matrix operations by private code + - Version 7, fix problem with TMatrixDSparse,TMatrixD multiplication + - Version 6, replace class XY by std::pair + - Version 5, replace run-time dynamic arrays by new and delete[] + - Version 4, fix new bug from V3 with initial regularisation condition + - Version 3, fix bug with initial regularisation condition + - Version 2, with improved ScanLcurve() algorithm + - Version 1, added ScanLcurve() method + - Version 0, stable version of basic unfolding algorithm */ -/* - This file is part of TUnfold. - - TUnfold 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 3 of the License, or - (at your option) any later version. - - TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. - */ - #include <iostream> #include <TMatrixD.h> #include <TMatrixDSparse.h> @@ -271,26 +123,28 @@ #include <map> #include <vector> -// this option saves the spline of the L curve curvature to a file -// named splinec.ps for debugging - //#define DEBUG //#define DEBUG_DETAIL -//#define DEBUG_LCURVE //#define FORCE_EIGENVALUE_DECOMPOSITION -#ifdef DEBUG_LCURVE -#include <TCanvas.h> -#endif - ClassImp(TUnfold) -//______________________________________________________________________________ -const char *TUnfold::GetTUnfoldVersion(void) +TUnfold::~TUnfold(void) { - return TUnfold_VERSION; + // delete all data members + + DeleteMatrix(&fA); + DeleteMatrix(&fL); + DeleteMatrix(&fVyy); + DeleteMatrix(&fY); + DeleteMatrix(&fX0); + DeleteMatrix(&fVyyInv); + + ClearResults(); } +//////////////////////////////////////////////////////////////////////////////// +/// Initialize data members, for use in constructors. void TUnfold::InitTUnfold(void) { @@ -305,18 +159,19 @@ void TUnfold::InitTUnfold(void) fX0 = 0; fTauSquared = 0.0; fBiasScale = 0.0; - fNdf = 0; fConstraint = kEConstraintNone; fRegMode = kRegModeNone; // output + fX = 0; fVyyInv = 0; fVxx = 0; - fX = 0; + fVxxInv = 0; fAx = 0; fChi2A = 0.0; fLXsquared = 0.0; fRhoMax = 999.0; fRhoAvg = -1.0; + fNdf = 0; fDXDAM[0] = 0; fDXDAZ[0] = 0; fDXDAM[1] = 0; @@ -325,23 +180,41 @@ void TUnfold::InitTUnfold(void) fDXDY = 0; fEinv = 0; fE = 0; - fVxxInv = 0; fEpsMatrix=1.E-13; fIgnoredBins=0; } +//////////////////////////////////////////////////////////////////////////////// +/// Delete matrix and invalidate pointer. +/// +/// \param[inout] m pointer to a matrix-pointer +/// +/// If the matrix pointer os non-zero, the matrix id deleted. The matrix pointer +/// is set to zero. + void TUnfold::DeleteMatrix(TMatrixD **m) { if(*m) delete *m; *m=0; } +//////////////////////////////////////////////////////////////////////////////// +/// Delete sparse matrix and invalidate pointer +/// +/// \param[inout] m pointer to a matrix-pointer +/// +/// if the matrix pointer os non-zero, the matrix id deleted. The matrix pointer +/// is set to zero. + void TUnfold::DeleteMatrix(TMatrixDSparse **m) { if(*m) delete *m; *m=0; } +//////////////////////////////////////////////////////////////////////////////// +/// Reset all results. + void TUnfold::ClearResults(void) { // delete old results (if any) @@ -369,53 +242,60 @@ void TUnfold::ClearResults(void) fRhoAvg = -1.0; } +//////////////////////////////////////////////////////////////////////////////// +/// Only for use by root streamer or derived classes. + TUnfold::TUnfold(void) { // set all matrix pointers to zero InitTUnfold(); } +//////////////////////////////////////////////////////////////////////////////// +/// Core unfolding algorithm. +/// +/// Main unfolding algorithm. Declared virtual, because other algorithms +/// could be implemented +/// +/// Purpose: unfold y -> x +/// +/// - Data members required: +/// - fA: matrix to relate x and y +/// - fY: measured data points +/// - fX0: bias on x +/// - fBiasScale: scale factor for fX0 +/// - fVyy: covariance matrix for y +/// - fL: regularisation conditions +/// - fTauSquared: regularisation strength +/// - fConstraint: whether the constraint is applied +/// - Data members modified: +/// - fVyyInv: inverse of input data covariance matrix +/// - fNdf: number of degrees of freedom +/// - fEinv: inverse of the matrix needed for unfolding calculations +/// - fE: the matrix needed for unfolding calculations +/// - fX: unfolded data points +/// - fDXDY: derivative of x wrt y (for error propagation) +/// - fVxx: error matrix (covariance matrix) on x +/// - fAx: estimate of distribution y from unfolded data +/// - fChi2A: contribution to chi**2 from y-Ax +/// - fChi2L: contribution to chi**2 from L*(x-x0) +/// - fDXDtauSquared: derivative of x wrt tau +/// - fDXDAM[0,1]: matrix parts of derivative x wrt A +/// - fDXDAZ[0,1]: vector parts of derivative x wrt A +/// - fRhoMax: maximum global correlation coefficient +/// - fRhoAvg: average global correlation coefficient +/// - Return code: +/// - fRhoMax if(fRhoMax>=1.0) then the unfolding has failed! + Double_t TUnfold::DoUnfold(void) { - // main unfolding algorithm. Declared virtual, because other algorithms - // could be implemented - // - // Purpose: unfold y -> x - // Data members required: - // fA: matrix to relate x and y - // fY: measured data points - // fX0: bias on x - // fBiasScale: scale factor for fX0 - // fVyy: covariance matrix for y - // fL: regularisation conditions - // fTauSquared: regularisation strength - // fConstraint: whether the constraint is applied - // Data members modified: - // fVyyInv: inverse of input data covariance matrix - // fNdf: number of degrees of freedom - // fEinv: inverse of the matrix needed for unfolding calculations - // fE: the matrix needed for unfolding calculations - // fX: unfolded data points - // fDXDY: derivative of x wrt y (for error propagation) - // fVxx: error matrix (covariance matrix) on x - // fAx: estimate of distribution y from unfolded data - // fChi2A: contribution to chi**2 from y-Ax - // fChi2L: contribution to chi**2 from L*(x-x0) - // fDXDtauSquared: derivative of x wrt tau - // fDXDAM[0,1]: matrix parts of derivative x wrt A - // fDXDAZ[0,1]: vector parts of derivative x wrt A - // fRhoMax: maximum global correlation coefficient - // fRhoAvg: average global correlation coefficient - // return code: - // fRhoMax if(fRhoMax>=1.0) then the unfolding has failed! - ClearResults(); // get pseudo-inverse matrix Vyyinv and NDF if(!fVyyInv) { GetInputInverseEmatrix(0); if(fConstraint != kEConstraintNone) { - fNdf--; + fNdf--; } } // @@ -432,7 +312,7 @@ Double_t TUnfold::DoUnfold(void) TMatrixDSparse *rhs=MultiplyMSparseM(AtVyyinv,fY); TMatrixDSparse *lSquared=MultiplyMSparseTranspMSparse(fL,fL); if (fBiasScale != 0.0) { - TMatrixDSparse *rhs2=MultiplyMSparseM(lSquared,fX0); + TMatrixDSparse *rhs2=MultiplyMSparseM(lSquared,fX0); AddMSparse(rhs, fTauSquared * fBiasScale ,rhs2); DeleteMatrix(&rhs2); } @@ -530,7 +410,7 @@ Double_t TUnfold::DoUnfold(void) data[i]=one_over_epsEeps; } TMatrixDSparse *temp=CreateSparseMatrix - (1,GetNy(),GetNy(),rows,cols,data); + (1,GetNy(),GetNy(),rows,cols,data); delete[] data; delete[] rows; delete[] cols; @@ -623,7 +503,7 @@ Double_t TUnfold::DoUnfold(void) if(fConstraint != kEConstraintNone) { // add correction to fDXDAM[0] TMatrixDSparse *temp1=MultiplyMSparseMSparseTranspVector - (Eepsilon,Eepsilon,0); + (Eepsilon,Eepsilon,0); AddMSparse(fDXDAM[0], -one_over_epsEeps,temp1); DeleteMatrix(&temp1); // add correction to fDXDAZ[0] @@ -636,7 +516,7 @@ Double_t TUnfold::DoUnfold(void) data[i]=lambda_half; } TMatrixDSparse *temp2=CreateSparseMatrix - (GetNy(),1,GetNy(),rows,cols,data); + (GetNy(),1,GetNy(),rows,cols,data); delete[] data; delete[] rows; delete[] cols; @@ -677,7 +557,7 @@ Double_t TUnfold::DoUnfold(void) for(int ik=Vxx_rows[ix];ik<Vxx_rows[ix+1];ik++) { if(ix==Vxx_cols[ik]) { Double_t rho_squared = - 1. - 1. / (VxxInvDiag(ix) * Vxx_data[ik]); + 1. - 1. / (VxxInvDiag(ix) * Vxx_data[ik]); if (rho_squared > rho_squared_max) rho_squared_max = rho_squared; if(rho_squared>0.0) { @@ -694,10 +574,24 @@ Double_t TUnfold::DoUnfold(void) return fRhoMax; } +//////////////////////////////////////////////////////////////////////////////// +/// Create a sparse matrix, given the nonzero elements. +/// +/// \param[in] nrow number of rows +/// \param[in] ncol number of columns +/// \param[in] nel number of non-zero elements +/// \param[in] row row indexes of non-zero elements +/// \param[in] col column indexes of non-zero elements +/// \param[in] data non-zero elements data +/// +/// return pointer to a new sparse matrix +/// +/// shortcut to new TMatrixDSparse() followed by SetMatrixArray(). + TMatrixDSparse *TUnfold::CreateSparseMatrix (Int_t nrow,Int_t ncol,Int_t nel,Int_t *row,Int_t *col,Double_t *data) const { - // create a sparse matrix + // create a sparse matri // nrow,ncol : dimension of the matrix // nel: number of non-zero elements // row[nel],col[nel],data[nel] : indices and data of the non-zero elements @@ -708,13 +602,21 @@ TMatrixDSparse *TUnfold::CreateSparseMatrix return A; } +//////////////////////////////////////////////////////////////////////////////// +/// Multiply two sparse matrices. +/// +/// \param[in] a sparse matrix +/// \param[in] b sparse matrix +/// +/// returns a new sparse matrix a*b. +/// +/// A replacement for: +/// new TMatrixDSparse(a,TMatrixDSparse::kMult,b) +/// the root implementation had problems in older versions of root. + TMatrixDSparse *TUnfold::MultiplyMSparseMSparse(const TMatrixDSparse *a, const TMatrixDSparse *b) const { - // calculate the product of two sparse matrices - // a,b: pointers to sparse matrices, where a->GetNcols()==b->GetNrows() - // this is a replacement for the call - // new TMatrixDSparse(*a,TMatrixDSparse::kMult,*b); if(a->GetNcols()!=b->GetNrows()) { Fatal("MultiplyMSparseMSparse", "inconsistent matrix col/ matrix row %d !=%d", @@ -775,17 +677,22 @@ TMatrixDSparse *TUnfold::MultiplyMSparseMSparse(const TMatrixDSparse *a, return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Multiply a transposed Sparse matrix with another sparse matrix, +/// +/// \param[in] a sparse matrix (to be transposed) +/// \param[in] b sparse matrix +/// +/// returns a new sparse matrix a^{T}*b +/// +/// this is a replacement for the root constructors +/// new TMatrixDSparse(TMatrixDSparse(TMatrixDSparse::kTransposed,*a), +/// TMatrixDSparse::kMult,*b) TMatrixDSparse *TUnfold::MultiplyMSparseTranspMSparse (const TMatrixDSparse *a,const TMatrixDSparse *b) const { - // multiply a transposed Sparse matrix with another Sparse matrix - // a: pointer to sparse matrix (to be transposed) - // b: pointer to sparse matrix - // this is a replacement for the call - // new TMatrixDSparse(TMatrixDSparse(TMatrixDSparse::kTransposed,*a), - // TMatrixDSparse::kMult,*b) - if(a->GetNrows() != b->GetNrows()) { + if(a->GetNrows() != b->GetNrows()) { Fatal("MultiplyMSparseTranspMSparse", "inconsistent matrix row numbers %d!=%d", a->GetNrows(),b->GetNrows()); @@ -855,14 +762,20 @@ TMatrixDSparse *TUnfold::MultiplyMSparseTranspMSparse return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Multiply sparse matrix and a non-sparse matrix. +/// +/// \param[in] a sparse matrix +/// \param[in] b matrix +/// +/// returns a new sparse matrix a*b. +/// A replacement for: +/// new TMatrixDSparse(a,TMatrixDSparse::kMult,b) +/// the root implementation had problems in older versions of root. + TMatrixDSparse *TUnfold::MultiplyMSparseM(const TMatrixDSparse *a, const TMatrixD *b) const { - // multiply a Sparse matrix with a non-sparse matrix - // a: pointer to sparse matrix - // b: pointer to non-sparse matrix - // this is a replacement for the call - // new TMatrixDSparse(*a,TMatrixDSparse::kMult,*b); if(a->GetNcols()!=b->GetNrows()) { Fatal("MultiplyMSparseM","inconsistent matrix col /matrix row %d!=%d", a->GetNcols(),b->GetNrows()); @@ -907,14 +820,22 @@ TMatrixDSparse *TUnfold::MultiplyMSparseM(const TMatrixDSparse *a, return r; } + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate a sparse matrix product\f$ M1*V*M2^{T} \f$ where the diagonal matrix V is +/// given by a vector. +/// +/// \param[in] m1 pointer to sparse matrix with dimension I*K +/// \param[in] m2 pointer to sparse matrix with dimension J*K +/// \param[in] v pointer to vector (matrix) with dimension K*1 +/// +/// returns a sparse matrix R with elements +/// \f$ r_{ij}=\Sigma_{k}M1_{ik}V_{k}M2_{jk} \f$ + TMatrixDSparse *TUnfold::MultiplyMSparseMSparseTranspVector (const TMatrixDSparse *m1,const TMatrixDSparse *m2, const TMatrixTBase<Double_t> *v) const { - // calculate M_ij = sum_k [m1_ik*m2_jk*v[k] ]. - // m1: pointer to sparse matrix with dimension I*K - // m2: pointer to sparse matrix with dimension J*K - // v: pointer to vector (matrix) with dimension K*1 if((m1->GetNcols() != m2->GetNcols())|| (v && ((m1->GetNcols()!=v->GetNrows())||(v->GetNcols()!=1)))) { if(v) { @@ -969,13 +890,11 @@ TMatrixDSparse *TUnfold::MultiplyMSparseMSparseTranspVector Int_t v_index=v_rows[k1]; if(v_index<v_rows[k1+1]) { data_r[num_r] += data_m1[index_m1] * data_m2[index_m2] - * v_data[v_index]; - } else { - data_r[num_r] =0.0; + * v_data[v_index]; } } else if(v) { data_r[num_r] += data_m1[index_m1] * data_m2[index_m2] - * (*v)(k1,0); + * (*v)(k1,0); } else { data_r[num_r] += data_m1[index_m1] * data_m2[index_m2]; } @@ -998,11 +917,22 @@ TMatrixDSparse *TUnfold::MultiplyMSparseMSparseTranspVector return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Add a sparse matrix, scaled by a factor, to another scaled matrix. +/// +/// \param[inout] dest destination matrix +/// \param[in] f scaling factor +/// \param[in] src matrix to be added to dest +/// +/// a replacement for +/// ~~~ +/// (*dest) += f * (*src) +/// ~~~ +/// which suffered from a bug in old root versions. + void TUnfold::AddMSparse(TMatrixDSparse *dest,Double_t f, const TMatrixDSparse *src) const { - // a replacement for - // (*dest) += f*(*src) const Int_t *dest_rows=dest->GetRowIndexArray(); const Int_t *dest_cols=dest->GetColIndexArray(); const Double_t *dest_data=dest->GetMatrixArray(); @@ -1025,9 +955,9 @@ void TUnfold::AddMSparse(TMatrixDSparse *dest,Double_t f, Int_t i_src=src_rows[row]; while((i_dest<dest_rows[row+1])||(i_src<src_rows[row+1])) { Int_t col_dest=(i_dest<dest_rows[row+1]) ? - dest_cols[i_dest] : dest->GetNcols(); + dest_cols[i_dest] : dest->GetNcols(); Int_t col_src =(i_src <src_rows[row+1] ) ? - src_cols [i_src] : src->GetNcols(); + src_cols [i_src] : src->GetNcols(); result_rows[n]=row; if(col_dest<col_src) { result_cols[n]=col_dest; @@ -1060,19 +990,26 @@ void TUnfold::AddMSparse(TMatrixDSparse *dest,Double_t f, delete[] result_cols; } +//////////////////////////////////////////////////////////////////////////////// +/// Get the inverse or pseudo-inverse of a positive, sparse matrix. +/// +/// \param[in] A the sparse matrix to be inverted, has to be positive +/// \param[inout] rankPtr if zero, suppress calculation of pseudo-inverse +/// otherwise the rank of the matrix is returned in *rankPtr +/// +/// return value: 0 or a new sparse matrix +/// +/// - if(rankPtr==0) return the inverse if it exists, or return 0 +/// - else return a (pseudo-)inverse and store the rank of the matrix in +/// *rankPtr +/// +/// +/// the matrix inversion is optimized in performance for the case +/// where a large submatrix of A is diagonal + TMatrixDSparse *TUnfold::InvertMSparseSymmPos (const TMatrixDSparse *A,Int_t *rankPtr) const { - // get the inverse or pseudo-inverse of a sparse matrix - // A: the original matrix - // rank: - // if(rankPtr==0) - // return the inverse if it exists, or return NULL - // else - // return the pseudo-invers - // and return the rank of the matrix in *rankPtr - // the matrix inversion is optimized for the case - // where a large submatrix of A is diagonal if(A->GetNcols()!=A->GetNrows()) { Fatal("InvertMSparseSymmPos","inconsistent matrix row/col %d!=%d", @@ -1101,7 +1038,6 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos } } if(nError>0) { - delete [] isZero; Fatal("InvertMSparseSymmPos", "Matrix has %d negative elements on the diagonal", nError); return 0; @@ -1388,7 +1324,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos minusBD2inv=MultiplyMSparseMSparse(B,D2inv); if(minusBD2inv) { Int_t mbd2_nMax=minusBD2inv->GetRowIndexArray() - [minusBD2inv->GetNrows()]; + [minusBD2inv->GetNrows()]; Double_t *mbd2_data=minusBD2inv->GetMatrixArray(); for(Int_t i=0;i<mbd2_nMax;i++) { mbd2_data[i] = - mbd2_data[i]; @@ -1397,7 +1333,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos } if(minusBD2inv && F) { TMatrixDSparse *minusBD2invBt= - MultiplyMSparseMSparseTranspVector(minusBD2inv,B,0); + MultiplyMSparseMSparseTranspVector(minusBD2inv,B,0); AddMSparse(F,1.,minusBD2invBt); DeleteMatrix(&minusBD2invBt); } @@ -1415,7 +1351,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos if(f_cols[indexF]>=i) c(f_cols[indexF],i)=f_data[indexF]; } // calculate diagonal element - Double_t c_ii=c(i,i); + Double_t c_ii=c(i,i); for(Int_t j=0;j<i;j++) { Double_t c_ij=c(i,j); c_ii -= c_ij*c_ij; @@ -1424,8 +1360,8 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos nErrorF++; break; } - c_ii=TMath::Sqrt(c_ii); - c(i,i)=c_ii; + c_ii=TMath::Sqrt(c_ii); + c(i,i)=c_ii; // off-diagonal elements for(Int_t j=i+1;j<nF;j++) { Double_t c_ji=c(j,i); @@ -1466,7 +1402,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos } TMatrixDSparse cInvSparse(cinv); Finv=MultiplyMSparseTranspMSparse - (&cInvSparse,&cInvSparse); + (&cInvSparse,&cInvSparse); } DeleteMatrix(&F); } @@ -1496,7 +1432,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos if(D2inv) E=new TMatrixDSparse(*D2inv); if(G && minusBD2inv) { TMatrixDSparse *minusBD2invTransG= - MultiplyMSparseTranspMSparse(minusBD2inv,G); + MultiplyMSparseTranspMSparse(minusBD2inv,G); if(E) { AddMSparse(E,1.,minusBD2invTransG); DeleteMatrix(&minusBD2invTransG); @@ -1643,7 +1579,7 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos } TMatrixDSparse V(Eigen.GetEigenVectors()); TMatrixDSparse *VDVt=MultiplyMSparseMSparseTranspVector - (&V,&V,&inverseEV); + (&V,&V,&inverseEV); // pack matrix VDVt to r const Int_t *vdvt_rows=VDVt->GetRowIndexArray(); @@ -1679,8 +1615,8 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos delete [] isZero; TMatrixDSparse *r=(rNumEl>=0) ? - CreateSparseMatrix(A->GetNrows(),A->GetNrows(),rNumEl, - rEl_row,rEl_col,rEl_data) : 0; + CreateSparseMatrix(A->GetNrows(),A->GetNrows(),rNumEl, + rEl_row,rEl_col,rEl_data) : 0; delete [] rEl_data; delete [] rEl_col; delete [] rEl_row; @@ -1709,19 +1645,19 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos if(TMath::Abs(ar(i,j)-ar(j,i))> epsilonA2*(TMath::Abs(ar(i,j))+TMath::Abs(ar(j,i)))) { std::cout<<"Ar is not symmetric Ar("<<i<<","<<j<<")="<<ar(i,j) - <<" Ar("<<j<<","<<i<<")="<<ar(j,i)<<"\n"; + <<" Ar("<<j<<","<<i<<")="<<ar(j,i)<<"\n"; } // ara should be equal a if(TMath::Abs(ara(i,j)-a(i,j))> epsilonA2*(TMath::Abs(ara(i,j))+TMath::Abs(a(i,j)))) { std::cout<<"ArA is not equal A ArA("<<i<<","<<j<<")="<<ara(i,j) - <<" A("<<i<<","<<j<<")="<<a(i,j)<<"\n"; + <<" A("<<i<<","<<j<<")="<<a(i,j)<<"\n"; } // ara should be equal a if(TMath::Abs(rar(i,j)-R(i,j))> epsilonA2*(TMath::Abs(rar(i,j))+TMath::Abs(R(i,j)))) { std::cout<<"rAr is not equal r rAr("<<i<<","<<j<<")="<<rar(i,j) - <<" r("<<i<<","<<j<<")="<<R(i,j)<<"\n"; + <<" r("<<i<<","<<j<<")="<<R(i,j)<<"\n"; } } } @@ -1734,36 +1670,58 @@ TMatrixDSparse *TUnfold::InvertMSparseSymmPos } +//////////////////////////////////////////////////////////////////////////////// +/// Get bin name of an output bin. +/// +/// \param[in] iBinX bin number +/// +/// Return value: name of the bin +/// +/// For TUnfold and TUnfoldSys, this function simply returns the bin +/// number as a string. This function really only makes sense in the +/// context of TUnfoldDensity, where binning schemes are implemented +/// using the class TUnfoldBinning, and non-trivial bin names are +/// returned. + TString TUnfold::GetOutputBinName(Int_t iBinX) const { - // given a bin number, return the name of the output bin - // this method makes more sense for the class TUnfoldDnesity - // where it gets overwritten return TString::Format("#%d",iBinX); } +//////////////////////////////////////////////////////////////////////////////// +/// Set up response matrix and regularisation scheme. +/// +/// \param[in] hist_A matrix of MC events that describes the migrations +/// \param[in] histmap mapping of the histogram axes +/// \param[in] regmode (default=kRegModeSize) global regularisation mode +/// \param[in] constraint (default=kEConstraintArea) type of constraint +/// +/// Treatment of overflow bins in the matrix hist_A +/// +/// - Events reconstructed in underflow or overflow bins are counted +/// as inefficiency. They have to be filled properly. +/// - Events where the truth level is in underflow or overflow bins are +/// treated as a part of the generator level distribution. +/// The full truth level distribution (including underflow and +/// overflow) is unfolded. +/// +/// If unsure, do the following: +/// +/// - store evens where the truth is in underflow or overflow +/// (sometimes called "fakes") in a separate TH1. Ensure that the +/// truth-level underflow and overflow bins of hist_A are all zero. +/// - the fakes are background to the +/// measurement. Use the classes TUnfoldSys and TUnfoldDensity instead +/// of the plain TUnfold for subtracting background. + TUnfold::TUnfold(const TH2 *hist_A, EHistMap histmap, ERegMode regmode, EConstraint constraint) { - // set up unfolding matrix and initial regularisation scheme - // hist_A: matrix that describes the migrations - // histmap: mapping of the histogram axes to the unfolding output - // regmode: global regularisation mode - // constraint: type of constraint to use // data members initialized to something different from zero: // fA: filled from hist_A // fDA: filled from hist_A // fX0: filled from hist_A // fL: filled depending on the regularisation scheme - // Treatment of overflow bins - // Bins where the unfolding input (Detector level) is in overflow - // are used for the efficiency correction. They have to be filled - // properly! - // Bins where the unfolding output (Generator level) is in overflow - // are treated as a part of the generator level distribution. - // I.e. the unfolding output could have non-zero overflow bins if the - // input matrix does have such bins. - InitTUnfold(); SetConstraint(constraint); Int_t nx0, nx, ny; @@ -1824,12 +1782,12 @@ TUnfold::TUnfold(const TH2 *hist_A, EHistMap histmap, ERegMode regmode, fSumOverY[nx] = sum; if (histmap == kHistMapOutputHoriz) { fSumOverY[nx] += - hist_A->GetBinContent(ix, 0) + - hist_A->GetBinContent(ix, ny + 1); + hist_A->GetBinContent(ix, 0) + + hist_A->GetBinContent(ix, ny + 1); } else { fSumOverY[nx] += - hist_A->GetBinContent(0, ix) + - hist_A->GetBinContent(ny + 1, ix); + hist_A->GetBinContent(0, ix) + + hist_A->GetBinContent(ny + 1, ix); } nx++; } else { @@ -1879,7 +1837,7 @@ TUnfold::TUnfold(const TH2 *hist_A, EHistMap histmap, ERegMode regmode, } if(nskipped==(2-underflowBin-overflowBin)) { Info("TUnfold","underflow and overflow bin " - "do not depend on the input data"); + "do not depend on the input data"); } else { Warning("TUnfold","%d output bins " "do not depend on the input data %s",nDisconnected, @@ -1945,23 +1903,16 @@ TUnfold::TUnfold(const TH2 *hist_A, EHistMap histmap, ERegMode regmode, } } -TUnfold::~TUnfold(void) -{ - // delete all data members - - DeleteMatrix(&fA); - DeleteMatrix(&fL); - DeleteMatrix(&fVyy); - DeleteMatrix(&fY); - DeleteMatrix(&fX0); - - ClearResults(); -} +//////////////////////////////////////////////////////////////////////////////// +/// Set bias vector. +/// +/// \param[in] bias histogram with new bias vector +/// +/// the initial bias vector is determined from the response matrix +/// but may be changed by using this method void TUnfold::SetBias(const TH1 *bias) { - // initialize alternative bias from histogram - // modifies data member fX0 DeleteMatrix(&fX0); fX0 = new TMatrixD(GetNx(), 1); for (Int_t i = 0; i < GetNx(); i++) { @@ -1969,18 +1920,23 @@ void TUnfold::SetBias(const TH1 *bias) } } +//////////////////////////////////////////////////////////////////////////////// +/// Add a row of regularisation conditions to the matrix L. +/// +/// \param[in] i0 truth histogram bin number +/// \param[in] f0 entry in the matrix L, column i0 +/// \param[in] i1 truth histogram bin number +/// \param[in] f1 entry in the matrix L, column i1 +/// \param[in] i2 truth histogram bin number +/// \param[in] f2 entry in the matrix L, column i2 +/// +/// the arguments are used to form one row (k) of the matrix L, where +/// \f$ L_{k,i0}=f0 \f$ and \f$ L_{k,i1}=f1 \f$ and \f$ L_{k,i2}=f2 \f$ +/// negative indexes i0,i1,i2 are ignored. + Bool_t TUnfold::AddRegularisationCondition (Int_t i0,Double_t f0,Int_t i1,Double_t f1,Int_t i2,Double_t f2) { - // add regularisation condition for a triplet of bins and scale factors - // the arguments are used to form one row (k) of the matrix L - // L(k,i0)=f0 - // L(k,i1)=f1 - // L(k,i2)=f2 - // the indices i0,i1,i2 are transformed of bin numbers - // if i2<0, do not fill L(k,i2) - // if i1<0, do not fill L(k,i1) - Int_t indices[3]; Double_t data[3]; Int_t nEle=0; @@ -2003,19 +1959,22 @@ Bool_t TUnfold::AddRegularisationCondition return AddRegularisationCondition(nEle,indices,data); } +//////////////////////////////////////////////////////////////////////////////// +/// Add a row of regularisation conditions to the matrix L. +/// +/// \param[in] nEle number of valid entries in indices and rowData +/// \param[in] indices column numbers of L to fill +/// \param[in] rowData data to fill into the new row of L +/// +/// returns true if a row was added, false otherwise +/// +/// A new row k is added to the matrix L, its dimension is expanded. +/// The new elements \f$ L_{ki} \f$ are filled from the array rowData[] +/// where the indices i which are taken from the array indices[]. + Bool_t TUnfold::AddRegularisationCondition (Int_t nEle,const Int_t *indices,const Double_t *rowData) { - // add a regularisation condition - // the arguments are used to form one row (k) of the matrix L - // L(k,indices[0])=rowData[0] - // ... - // L(k,indices[nEle-1])=rowData[nEle-1] - // - // the indices are taken as bin numbers, - // transformed to internal bin numbers using the array fHistToX - - Bool_t r=kTRUE; const Int_t *l0_rows=fL->GetRowIndexArray(); const Int_t *l0_cols=fL->GetColIndexArray(); @@ -2066,6 +2025,24 @@ Bool_t TUnfold::AddRegularisationCondition return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Add a regularisation condition on the magnitude of a truth bin. +/// +/// \param[in] bin bin number +/// \param[in] scale (default=1) scale factor +/// +/// this adds one row to L, where the element <b>bin</b> takes the +/// value <b>scale</b> +/// +/// return value: 0 if ok, 1 if the condition has not been +/// added. Conditions which are not added typically correspond to bin +/// numbers where the truth can not be unfolded (either response +/// matrix is empty or the data do not constrain). +/// +/// The RegularizeXXX() methods can be used to set up a custom matrix +/// of regularisation conditions. In this case, start with an empty +/// matrix L (argument regmode=kRegModeNone in the constructor) + Int_t TUnfold::RegularizeSize(int bin, Double_t scale) { // add regularisation on the size of bin i @@ -2080,8 +2057,28 @@ Int_t TUnfold::RegularizeSize(int bin, Double_t scale) return AddRegularisationCondition(bin,scale) ? 0 : 1; } +//////////////////////////////////////////////////////////////////////////////// +/// Add a regularisation condition on the difference of two truth bin. +/// +/// \param[in] left_bin bin number +/// \param[in] right_bin bin number +/// \param[in] scale (default=1) scale factor +/// +/// this adds one row to L, where the element <b>left_bin</b> takes the +/// value <b>-scale</b> and the element <b>right_bin</b> takes the +/// value <b>+scale</b> +/// +/// return value: 0 if ok, 1 if the condition has not been +/// added. Conditions which are not added typically correspond to bin +/// numbers where the truth can not be unfolded (either response +/// matrix is empty or the data do not constrain). +/// +/// The RegularizeXXX() methods can be used to set up a custom matrix +/// of regularisation conditions. In this case, start with an empty +/// matrix L (argument regmode=kRegModeNone in the constructor) + Int_t TUnfold::RegularizeDerivative(int left_bin, int right_bin, - Double_t scale) + Double_t scale) { // add regularisation on the difference of two bins // left_bin: 1st bin @@ -2096,10 +2093,33 @@ Int_t TUnfold::RegularizeDerivative(int left_bin, int right_bin, return AddRegularisationCondition(left_bin,-scale,right_bin,scale) ? 0 : 1; } +//////////////////////////////////////////////////////////////////////////////// +/// Add a regularisation condition on the curvature of three truth bin. +/// +/// \param[in] left_bin bin number +/// \param[in] center_bin bin number +/// \param[in] right_bin bin number +/// \param[in] scale_left (default=1) scale factor +/// \param[in] scale_right (default=1) scale factor +/// +/// this adds one row to L, where the element <b>left_bin</b> takes the +/// value <b>-scale_left</b>, the element <b>right_bin</b> takes the +/// value <b>-scale_right</b> and the element <b>center_bin</b> takes +/// the value <b>scale_left+scale_right</b> +/// +/// return value: 0 if ok, 1 if the condition has not been +/// added. Conditions which are not added typically correspond to bin +/// numbers where the truth can not be unfolded (either response +/// matrix is empty or the data do not constrain). +/// +/// The RegularizeXXX() methods can be used to set up a custom matrix +/// of regularisation conditions. In this case, start with an empty +/// matrix L (argument regmode=kRegModeNone in the constructor) + Int_t TUnfold::RegularizeCurvature(int left_bin, int center_bin, - int right_bin, - Double_t scale_left, - Double_t scale_right) + int right_bin, + Double_t scale_left, + Double_t scale_right) { // add regularisation on the curvature through 3 bins (2nd derivative) // left_bin: 1st bin @@ -2114,16 +2134,36 @@ Int_t TUnfold::RegularizeCurvature(int left_bin, int center_bin, if(fRegMode!=kRegModeCurvature) fRegMode=kRegModeMixed; return AddRegularisationCondition - (left_bin,-scale_left, - center_bin,scale_left+scale_right, - right_bin,-scale_right) - ? 0 : 1; + (left_bin,-scale_left, + center_bin,scale_left+scale_right, + right_bin,-scale_right) + ? 0 : 1; } +//////////////////////////////////////////////////////////////////////////////// +/// Add regularisation conditions for a group of bins. +/// +/// \param[in] start first bin number +/// \param[in] step step size +/// \param[in] nbin number of bins +/// \param[in] regmode regularisation mode (one of: kRegModeSize, +/// kRegModeDerivative, kRegModeCurvature) +/// +/// add regularisation conditions for a group of equidistant +/// bins. There are <b>nbin</b> bins, starting with bin <b>start</b> +/// and with a distance of <b>step</b> between bins. +/// +/// Return value: number of regularisation conditions which could not +/// be added. +/// +/// Conditions which are not added typically correspond to bin +/// numbers where the truth can not be unfolded (either response +/// matrix is empty or the data do not constrain). + Int_t TUnfold::RegularizeBins(int start, int step, int nbin, - ERegMode regmode) + ERegMode regmode) { - // set regularization on a 1-dimensional curve + // set regulatisation on a 1-dimensional curve // start: first bin // step: distance between neighbouring bins // nbin: total number of bins @@ -2160,11 +2200,32 @@ Int_t TUnfold::RegularizeBins(int start, int step, int nbin, return nError; } +//////////////////////////////////////////////////////////////////////////////// +/// Add regularisation conditions for 2d unfolding. +/// +/// \param[in] start_bin first bin number +/// \param[in] step1 step size, 1st dimension +/// \param[in] nbin1 number of bins, 1st dimension +/// \param[in] step2 step size, 2nd dimension +/// \param[in] nbin2 number of bins, 2nd dimension +/// \param[in] regmode regularisation mode (one of: kRegModeSize, +/// kRegModeDerivative, kRegModeCurvature) +/// +/// add regularisation conditions for a grid of bins. The start bin is +/// <b>start_bin</b>. Along the first (second) dimension, there are +/// <b>nbin1</b> (<b>nbin2</b>) bins and adjacent bins are spaced by +/// <b>step1</b> (<b>step2</b>) units. +/// +/// Return value: number of regularisation conditions which could not +/// be added. Conditions which are not added typically correspond to bin +/// numbers where the truth can not be unfolded (either response +/// matrix is empty or the data do not constrain). + Int_t TUnfold::RegularizeBins2D(int start_bin, int step1, int nbin1, - int step2, int nbin2, ERegMode regmode) + int step2, int nbin2, ERegMode regmode) { // set regularisation on a 2-dimensional grid of bins - // start: first bin + // start_bin: first bin // step1: distance between bins in 1st direction // nbin1: number of bins in 1st direction // step2: distance between bins in 2nd direction @@ -2183,604 +2244,661 @@ Int_t TUnfold::RegularizeBins2D(int start_bin, int step1, int nbin1, return nError; } +//////////////////////////////////////////////////////////////////////////////// +/// Perform the unfolding for a given input and regularisation. +/// +/// \param[in] tau_reg regularisation parameter +/// \param[in] input input distribution with uncertainties +/// \param[in] scaleBias (default=0.0) scale factor applied to the bias +/// +/// This is a shortcut for `{ SetInput(input,scaleBias); DoUnfold(tau); }` +/// +/// Data members required: +/// - fA, fX0, fL +/// Data members modified: +/// - those documented in SetInput() +/// and those documented in DoUnfold(Double_t) +/// Return value: +/// - maximum global correlation coefficient +/// NOTE!!! return value >=1.0 means error, and the result is junk +/// +/// Overflow bins of the input distribution are ignored! + Double_t TUnfold::DoUnfold(Double_t tau_reg,const TH1 *input, Double_t scaleBias) { - // Do unfolding of an input histogram - // tau_reg: regularisation parameter - // input: input distribution with errors - // scaleBias: scale factor applied to the bias - // Data members required: - // fA, fX0, fL - // Data members modified: - // those documented in SetInput() - // and those documented in DoUnfold(Double_t) - // Return value: - // maximum global correlation coefficient - // NOTE!!! return value >=1.0 means error, and the result is junk - // - // Overflow bins of the input distribution are ignored! SetInput(input,scaleBias); return DoUnfold(tau_reg); } +//////////////////////////////////////////////////////////////////////////////// +/// Define input data for subsequent calls to DoUnfold(tau). +/// +/// \param[in] input input distribution with uncertainties +/// \param[in] scaleBias (default=0) scale factor applied to the bias +/// \param[in] oneOverZeroError (default=0) for bins with zero error, this number defines 1/error. +/// \param[in] hist_vyy (default=0) if non-zero, this defines the data covariance matrix +/// \param[in] hist_vyy_inv (default=0) if non-zero and hist_vyy is +/// set, defines the inverse of the data covariance matrix. This +/// feature can be useful for repeated unfoldings in cases where the +/// inversion of the input covariance matrix is lengthy +/// +/// Return value: nError1+10000*nError2 +/// +/// - nError1: number of bins where the uncertainty is zero. +/// these bins either are not used for the unfolding (if +/// oneOverZeroError==0) or 1/uncertainty is set to oneOverZeroError. +/// - nError2: return values>10000 are fatal errors, because the +/// unfolding can not be done. The number nError2 corresponds to the +/// number of truth bins which are not constrained by data points. +/// +/// Data members modified: +/// - fY, fVyy, , fBiasScale +/// Data members cleared +/// - fVyyInv, fNdf +/// - + see ClearResults + Int_t TUnfold::SetInput(const TH1 *input, Double_t scaleBias, Double_t oneOverZeroError,const TH2 *hist_vyy, const TH2 *hist_vyy_inv) { - // Define the input data for subsequent calls to DoUnfold(Double_t) - // input: input distribution with errors - // scaleBias: scale factor applied to the bias - // oneOverZeroError: for bins with zero error, this number defines 1/error. - // hist_vyy: if non-zero, defines the data covariance matrix - // otherwise it is calculated from the data errors - // hist_vyy_inv: if non-zero and if hist_vyy is set, defines the inverse of the data covariance matrix - // Return value: number of bins with bad error - // +10000*number of unconstrained output bins - // Note: return values>=10000 are fatal errors, - // for the given input, the unfolding can not be done! - // Data members modified: - // fY, fVyy, , fBiasScale - // Data members cleared - // fVyyInv, fNdf - // + see ClearResults + DeleteMatrix(&fVyyInv); + fNdf=0; - DeleteMatrix(&fVyyInv); - fNdf=0; - - fBiasScale = scaleBias; + fBiasScale = scaleBias; // delete old results (if any) - ClearResults(); - - // construct error matrix and inverted error matrix of measured quantities - // from errors of input histogram or use error matrix - - Int_t *rowVyyN=new Int_t[GetNy()*GetNy()+1]; - Int_t *colVyyN=new Int_t[GetNy()*GetNy()+1]; - Double_t *dataVyyN=new Double_t[GetNy()*GetNy()+1]; - - Int_t *rowVyy1=new Int_t[GetNy()]; - Int_t *colVyy1=new Int_t[GetNy()]; - Double_t *dataVyy1=new Double_t[GetNy()]; - Double_t *dataVyyDiag=new Double_t[GetNy()]; - - Int_t nError=0; - Int_t nVyyN=0; - Int_t nVyy1=0; - for (Int_t iy = 0; iy < GetNy(); iy++) { - // diagonals - Double_t dy2; - if(!hist_vyy) { - Double_t dy = input->GetBinError(iy + 1); - dy2=dy*dy; - if (dy2 <= 0.0) { - nError++; - if(oneOverZeroError>0.0) { - dy2 = 1./ ( oneOverZeroError*oneOverZeroError); - } - } - } else { - dy2 = hist_vyy->GetBinContent(iy+1,iy+1); - } - rowVyyN[nVyyN] = iy; - colVyyN[nVyyN] = iy; - rowVyy1[nVyy1] = iy; - colVyy1[nVyy1] = 0; - dataVyyDiag[iy] = dy2; - if(dy2>0.0) { - dataVyyN[nVyyN++] = dy2; - dataVyy1[nVyy1++] = dy2; - } - } - if(hist_vyy) { - // non-diagonal elements - for (Int_t iy = 0; iy < GetNy(); iy++) { - // ignore rows where the diagonal is zero - if(dataVyyDiag[iy]<=0.0) continue; - for (Int_t jy = 0; jy < GetNy(); jy++) { - // skip diagonal elements - if(iy==jy) continue; - // ignore columns where the diagonal is zero - if(dataVyyDiag[jy]<=0.0) continue; - - rowVyyN[nVyyN] = iy; - colVyyN[nVyyN] = jy; - dataVyyN[nVyyN]= hist_vyy->GetBinContent(iy+1,jy+1); - if(dataVyyN[nVyyN] == 0.0) continue; - nVyyN ++; - } - } - if(hist_vyy_inv) { - Warning("SetInput", - "inverse of input covariance is taken from user input"); - Int_t *rowVyyInv=new Int_t[GetNy()*GetNy()+1]; - Int_t *colVyyInv=new Int_t[GetNy()*GetNy()+1]; - Double_t *dataVyyInv=new Double_t[GetNy()*GetNy()+1]; - Int_t nVyyInv=0; - for (Int_t iy = 0; iy < GetNy(); iy++) { - for (Int_t jy = 0; jy < GetNy(); jy++) { - rowVyyInv[nVyyInv] = iy; - colVyyInv[nVyyInv] = jy; - dataVyyInv[nVyyInv]= hist_vyy_inv->GetBinContent(iy+1,jy+1); - if(dataVyyInv[nVyyInv] == 0.0) continue; - nVyyInv ++; - } - } - fVyyInv=CreateSparseMatrix - (GetNy(),GetNy(),nVyyInv,rowVyyInv,colVyyInv,dataVyyInv); - delete [] rowVyyInv; - delete [] colVyyInv; - delete [] dataVyyInv; - } - } - DeleteMatrix(&fVyy); - fVyy = CreateSparseMatrix - (GetNy(),GetNy(),nVyyN,rowVyyN,colVyyN,dataVyyN); - - delete[] rowVyyN; - delete[] colVyyN; - delete[] dataVyyN; - - TMatrixDSparse *vecV=CreateSparseMatrix - (GetNy(),1,nVyy1,rowVyy1,colVyy1, dataVyy1); - - delete[] rowVyy1; - delete[] colVyy1; - delete[] dataVyy1; - - // - // get input vector - DeleteMatrix(&fY); - fY = new TMatrixD(GetNy(), 1); - for (Int_t i = 0; i < GetNy(); i++) { - (*fY) (i, 0) = input->GetBinContent(i + 1); - } - // simple check whether unfolding is possible, given the matrices fA and fV - TMatrixDSparse *mAtV=MultiplyMSparseTranspMSparse(fA,vecV); - DeleteMatrix(&vecV); - Int_t nError2=0; - for (Int_t i = 0; i <mAtV->GetNrows();i++) { - if(mAtV->GetRowIndexArray()[i]== - mAtV->GetRowIndexArray()[i+1]) { - nError2 ++; - } - } - if(nError>0) { - if(oneOverZeroError !=0.0) { - if(nError>1) { - Warning("SetInput","%d/%d input bins have zero error," - " 1/error set to %lf.",nError,GetNy(),oneOverZeroError); - } else { - Warning("SetInput","One input bin has zero error," - " 1/error set to %lf.",oneOverZeroError); - } - } else { - if(nError>1) { - Warning("SetInput","%d/%d input bins have zero error," - " and are ignored.",nError,GetNy()); - } else { - Warning("SetInput","One input bin has zero error," - " and is ignored."); - } - } - fIgnoredBins=nError; - } - if(nError2>0) { - // check whether data points with zero error are responsible - if(oneOverZeroError<=0.0) { - //const Int_t *a_rows=fA->GetRowIndexArray(); - //const Int_t *a_cols=fA->GetColIndexArray(); - for (Int_t col = 0; col <mAtV->GetNrows();col++) { - if(mAtV->GetRowIndexArray()[col]== - mAtV->GetRowIndexArray()[col+1]) { - TString binlist("no data to constrain output bin "); - binlist += GetOutputBinName(fXToHist[col]); - /* binlist +=" depends on ignored input bins "; - for(Int_t row=0;row<fA->GetNrows();row++) { - if(dataVyyDiag[row]>0.0) continue; - for(Int_t i=a_rows[row];i<a_rows[row+1];i++) { - if(a_cols[i]!=col) continue; - binlist +=" "; - binlist +=row; - } - } */ - Warning("SetInput","%s",binlist.Data()); - } - } - } - if(nError2>1) { - Error("SetInput","%d/%d output bins are not constrained by any data.", - nError2,mAtV->GetNrows()); - } else { - Error("SetInput","One output bins is not constrained by any data."); - } - } - DeleteMatrix(&mAtV); - - delete[] dataVyyDiag; - - return nError+10000*nError2; + ClearResults(); + + // construct error matrix and inverted error matrix of measured quantities + // from errors of input histogram or use error matrix + + Int_t *rowVyyN=new Int_t[GetNy()*GetNy()+1]; + Int_t *colVyyN=new Int_t[GetNy()*GetNy()+1]; + Double_t *dataVyyN=new Double_t[GetNy()*GetNy()+1]; + + Int_t *rowVyy1=new Int_t[GetNy()]; + Int_t *colVyy1=new Int_t[GetNy()]; + Double_t *dataVyy1=new Double_t[GetNy()]; + Double_t *dataVyyDiag=new Double_t[GetNy()]; + + Int_t nVarianceZero=0; + Int_t nVarianceForced=0; + Int_t nVyyN=0; + Int_t nVyy1=0; + for (Int_t iy = 0; iy < GetNy(); iy++) { + // diagonals + Double_t dy2; + if(!hist_vyy) { + Double_t dy = input->GetBinError(iy + 1); + dy2=dy*dy; + if (dy2 <= 0.0) { + if(oneOverZeroError>0.0) { + dy2 = 1./ ( oneOverZeroError*oneOverZeroError); + nVarianceForced++; + } else { + nVarianceZero++; + } + } + } else { + dy2 = hist_vyy->GetBinContent(iy+1,iy+1); + if (dy2 <= 0.0) { + nVarianceZero++; + } + } + rowVyyN[nVyyN] = iy; + colVyyN[nVyyN] = iy; + rowVyy1[nVyy1] = iy; + colVyy1[nVyy1] = 0; + dataVyyDiag[iy] = dy2; + if(dy2>0.0) { + dataVyyN[nVyyN++] = dy2; + dataVyy1[nVyy1++] = dy2; + } + } + if(hist_vyy) { + // non-diagonal elements + Int_t nOffDiagNonzero=0; + for (Int_t iy = 0; iy < GetNy(); iy++) { + // ignore rows where the diagonal is zero + if(dataVyyDiag[iy]<=0.0) { + for (Int_t jy = 0; jy < GetNy(); jy++) { + if(hist_vyy->GetBinContent(iy+1,jy+1)!=0.0) { + nOffDiagNonzero++; + } + } + continue; + } + for (Int_t jy = 0; jy < GetNy(); jy++) { + // skip diagonal elements + if(iy==jy) continue; + // ignore columns where the diagonal is zero + if(dataVyyDiag[jy]<=0.0) continue; + + rowVyyN[nVyyN] = iy; + colVyyN[nVyyN] = jy; + dataVyyN[nVyyN]= hist_vyy->GetBinContent(iy+1,jy+1); + if(dataVyyN[nVyyN] == 0.0) continue; + nVyyN ++; + } + } + if(hist_vyy_inv) { + Warning("SetInput", + "inverse of input covariance is taken from user input"); + Int_t *rowVyyInv=new Int_t[GetNy()*GetNy()+1]; + Int_t *colVyyInv=new Int_t[GetNy()*GetNy()+1]; + Double_t *dataVyyInv=new Double_t[GetNy()*GetNy()+1]; + Int_t nVyyInv=0; + for (Int_t iy = 0; iy < GetNy(); iy++) { + for (Int_t jy = 0; jy < GetNy(); jy++) { + rowVyyInv[nVyyInv] = iy; + colVyyInv[nVyyInv] = jy; + dataVyyInv[nVyyInv]= hist_vyy_inv->GetBinContent(iy+1,jy+1); + if(dataVyyInv[nVyyInv] == 0.0) continue; + nVyyInv ++; + } + } + fVyyInv=CreateSparseMatrix + (GetNy(),GetNy(),nVyyInv,rowVyyInv,colVyyInv,dataVyyInv); + delete [] rowVyyInv; + delete [] colVyyInv; + delete [] dataVyyInv; + } else { + if(nOffDiagNonzero) { + Error("SetInput", + "input covariance has elements C(X,Y)!=0 where V(X)==0"); + } + } + } + DeleteMatrix(&fVyy); + fVyy = CreateSparseMatrix + (GetNy(),GetNy(),nVyyN,rowVyyN,colVyyN,dataVyyN); + + delete[] rowVyyN; + delete[] colVyyN; + delete[] dataVyyN; + + TMatrixDSparse *vecV=CreateSparseMatrix + (GetNy(),1,nVyy1,rowVyy1,colVyy1, dataVyy1); + + delete[] rowVyy1; + delete[] colVyy1; + delete[] dataVyy1; + + // + // get input vector + DeleteMatrix(&fY); + fY = new TMatrixD(GetNy(), 1); + for (Int_t i = 0; i < GetNy(); i++) { + (*fY) (i, 0) = input->GetBinContent(i + 1); + } + // simple check whether unfolding is possible, given the matrices fA and fV + TMatrixDSparse *mAtV=MultiplyMSparseTranspMSparse(fA,vecV); + DeleteMatrix(&vecV); + Int_t nError2=0; + for (Int_t i = 0; i <mAtV->GetNrows();i++) { + if(mAtV->GetRowIndexArray()[i]== + mAtV->GetRowIndexArray()[i+1]) { + nError2 ++; + } + } + if(nVarianceForced) { + if(nVarianceForced>1) { + Warning("SetInput","%d/%d input bins have zero error," + " 1/error set to %lf.", + nVarianceForced,GetNy(),oneOverZeroError); + } else { + Warning("SetInput","One input bin has zero error," + " 1/error set to %lf.",oneOverZeroError); + } + } + if(nVarianceZero) { + if(nVarianceZero>1) { + Warning("SetInput","%d/%d input bins have zero error," + " and are ignored.",nVarianceZero,GetNy()); + } else { + Warning("SetInput","One input bin has zero error," + " and is ignored."); + } + fIgnoredBins=nVarianceZero; + } + if(nError2>0) { + // check whether data points with zero error are responsible + if(oneOverZeroError<=0.0) { + //const Int_t *a_rows=fA->GetRowIndexArray(); + //const Int_t *a_cols=fA->GetColIndexArray(); + for (Int_t col = 0; col <mAtV->GetNrows();col++) { + if(mAtV->GetRowIndexArray()[col]== + mAtV->GetRowIndexArray()[col+1]) { + TString binlist("no data to constrain output bin "); + binlist += GetOutputBinName(fXToHist[col]); + /* binlist +=" depends on ignored input bins "; + for(Int_t row=0;row<fA->GetNrows();row++) { + if(dataVyyDiag[row]>0.0) continue; + for(Int_t i=a_rows[row];i<a_rows[row+1];i++) { + if(a_cols[i]!=col) continue; + binlist +=" "; + binlist +=row; + } + } */ + Warning("SetInput","%s",(char const *)binlist); + } + } + } + if(nError2>1) { + Error("SetInput","%d/%d output bins are not constrained by any data.", + nError2,mAtV->GetNrows()); + } else { + Error("SetInput","One output bins is not constrained by any data."); + } + } + DeleteMatrix(&mAtV); + + delete[] dataVyyDiag; + + return nVarianceForced+nVarianceZero+10000*nError2; } +//////////////////////////////////////////////////////////////////////////////// +/// Perform the unfolding for a given regularisation parameter tau. +/// +/// \param[in] tau regularisation parameter +/// +/// This method sets tau and then calls the core unfolding algorithm +/// required data members: +/// - fA: matrix to relate x and y +/// - fY: measured data points +/// - fX0: bias on x +/// - fBiasScale: scale factor for fX0 +/// - fV: inverse of covariance matrix for y +/// - fL: regularisation conditions +/// modified data members: +/// - fTauSquared and those documented in DoUnfold(void) + Double_t TUnfold::DoUnfold(Double_t tau) { - // Unfold with given value of regularisation parameter tau - // tau: new tau parameter - // required data members: - // fA: matrix to relate x and y - // fY: measured data points - // fX0: bias on x - // fBiasScale: scale factor for fX0 - // fV: inverse of covariance matrix for y - // fL: regularisation conditions - // modified data members: - // fTauSquared and those documented in DoUnfold(void) fTauSquared=tau*tau; return DoUnfold(); } +//////////////////////////////////////////////////////////////////////////////// +/// Scan the L curve, determine tau and unfold at the final value of tau. +/// +/// \param[in] nPoint number of points used for the scan +/// \param[in] tauMin smallest tau value to study +/// \param[in] tauMax largest tau value to study. If tauMin=tauMax=0, +/// a scan interval is determined automatically. +/// \param[out] lCurve if nonzero, a new TGraph is returned, +/// containing the L-curve +/// \param[out] logTauX if nonzero, a new TSpline is returned, to +/// parameterize the L-curve's x-coordinates as a function of log10(tau) +/// \param[out] logTauY if nonzero, a new TSpline is returned, to +/// parameterize the L-curve's y-coordinates as a function of log10(tau) +/// \param[out] logTauCurvature if nonzero, a new TSpline is returned +/// of the L-curve curvature as a function of log10(tau) +/// +/// return value: the coordinate number in the logTauX,logTauY graphs +/// corresponding to the "final" choice of tau +/// +/// Recommendation: always check <b>logTauCurvature</b>, it +/// should be a peaked function (similar to a Gaussian), the maximum +/// corresponding to the final choice of tau. Also, check the <b>lCurve</b> +/// it should be approximately L-shaped. If in doubt, adjust tauMin +/// and tauMax until the results are satisfactory. + Int_t TUnfold::ScanLcurve(Int_t nPoint, Double_t tauMin,Double_t tauMax, - TGraph **lCurve,TSpline **logTauX, - TSpline **logTauY) + TGraph **lCurve,TSpline **logTauX, + TSpline **logTauY,TSpline **logTauCurvature) { - // scan the L curve - // nPoint: number of points on the resulting curve - // tauMin: smallest tau value to study - // tauMax: largest tau value to study - // lCurve: the L curve as graph - // logTauX: output spline of x-coordinates vs tau for the L curve - // logTauY: output spline of y-coordinates vs tau for the L curve - // return value: the coordinate number (0..nPoint-1) with the "best" choice - // of tau - typedef std::map<Double_t,std::pair<Double_t,Double_t> > XYtau_t; - XYtau_t curve; - - //========================================================== - // algorithm: - // (1) do the unfolding for nPoint-1 points - // and store the results in the map - // curve - // (1a) store minimum and maximum tau to curve - // (1b) insert additional points, until nPoint-1 values - // have been calculated - // - // (2) determine the best choice of tau - // do the unfolding for this point - // and store the result in - // curve - // (3) return the result in - // lCurve logTauX logTauY - - //========================================================== - // (1) do the unfolding for nPoint-1 points - // and store the results in - // curve - // (1a) store minimum and maximum tau to curve - - if((tauMin<=0)||(tauMax<=0.0)||(tauMin>=tauMax)) { - // here no range is given, has to be determined automatically - // the maximum tau is determined from the chi**2 values - // observed from unfolding without regularization - - // first unfolding, without regularisation - DoUnfold(0.0); - - // if the number of degrees of freedom is too small, create an error - if(GetNdf()<=0) { - Error("ScanLcurve","too few input bins, NDF<=0 %d",GetNdf()); - } - - Double_t x0=GetLcurveX(); - Double_t y0=GetLcurveY(); - Info("ScanLcurve","logtau=-Infinity X=%lf Y=%lf",x0,y0); - if(!TMath::Finite(x0)) { - Fatal("ScanLcurve","problem (too few input bins?) X=%f",x0); - } - if(!TMath::Finite(y0)) { - Fatal("ScanLcurve","problem (missing regularisation?) Y=%f",y0); - } - { - // unfolding guess maximum tau and store it - Double_t logTau= - 0.5*(TMath::Log10(fChi2A+3.*TMath::Sqrt(GetNdf()+1.0)) - -GetLcurveY()); - DoUnfold(TMath::Power(10.,logTau)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", - logTau,GetLcurveX(),GetLcurveY()); - } - if((*curve.begin()).second.first<x0) { - // if the point at tau==0 seems numerically unstable, - // try to find the minimum chi**2 as start value - // - // "unstable" means that there is a finite tau where the - // unfolding chi**2 is smaller than for the case of no - // regularisation. Ideally this should never happen - do { - x0=GetLcurveX(); - Double_t logTau=(*curve.begin()).first-0.5; - DoUnfold(TMath::Power(10.,logTau)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", - logTau,GetLcurveX(),GetLcurveY()); - } - while(((int)curve.size()<(nPoint-1)/2)&& - ((*curve.begin()).second.first<x0)); - } else { - // minimum tau is chosen such that is less than - // 1% different from the case of no regularization - // log10(1.01) = 0.00432 - - // here, more than one point are inserted if necessary - while(((int)curve.size()<nPoint-1)&& - (((*curve.begin()).second.first-x0>0.00432)|| - ((*curve.begin()).second.second-y0>0.00432)|| - (curve.size()<2))) { - Double_t logTau=(*curve.begin()).first-0.5; - DoUnfold(TMath::Power(10.,logTau)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", - logTau,GetLcurveX(),GetLcurveY()); - } - } - } else { - Double_t logTauMin=TMath::Log10(tauMin); - Double_t logTauMax=TMath::Log10(tauMax); - if(nPoint>1) { - // insert maximum tau - DoUnfold(TMath::Power(10.,logTauMax)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", - logTauMax,GetLcurveX(),GetLcurveY()); - curve[logTauMax]=std::make_pair(GetLcurveX(),GetLcurveY()); - } - // insert minimum tau - DoUnfold(TMath::Power(10.,logTauMin)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", - logTauMin,GetLcurveX(),GetLcurveY()); - curve[logTauMin]=std::make_pair(GetLcurveX(),GetLcurveY()); - } - - - //========================================================== - // (1b) insert additional points, until nPoint-1 values - // have been calculated - - while(int(curve.size())<nPoint-1) { - // insert additional points, such that the sizes of the delta(XY) vectors - // are getting smaller and smaller - XYtau_t::const_iterator i0,i1; - i0=curve.begin(); - i1=i0; - Double_t logTau=(*i0).first; - Double_t distMax=0.0; - for(i1++;i1!=curve.end();i1++) { - const std::pair<Double_t,Double_t> &xy0=(*i0).second; - const std::pair<Double_t,Double_t> &xy1=(*i1).second; - Double_t dx=xy1.first-xy0.first; - Double_t dy=xy1.second-xy0.second; - Double_t d=TMath::Sqrt(dx*dx+dy*dy); - if(d>=distMax) { - distMax=d; - logTau=0.5*((*i0).first+(*i1).first); - } - i0=i1; - } - DoUnfold(TMath::Power(10.,logTau)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - Info("ScanLcurve","logtau=%lf X=%lf Y=%lf",logTau,GetLcurveX(),GetLcurveY()); - curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); - } - - //========================================================== - // (2) determine the best choice of tau - // do the unfolding for this point - // and store the result in - // curve - XYtau_t::const_iterator i0,i1; - i0=curve.begin(); - i1=i0; - i1++; - Double_t logTauFin=(*i0).first; - if( ((int)curve.size())<nPoint) { - // set up splines and determine (x,y) curvature in each point - Double_t *cTi=new Double_t[curve.size()-1]; - Double_t *cCi=new Double_t[curve.size()-1]; - Int_t n=0; - { - Double_t *lXi=new Double_t[curve.size()]; - Double_t *lYi=new Double_t[curve.size()]; - Double_t *lTi=new Double_t[curve.size()]; - for( XYtau_t::const_iterator i=curve.begin();i!=curve.end();i++) { - lXi[n]=(*i).second.first; - lYi[n]=(*i).second.second; - lTi[n]=(*i).first; - n++; - } - TSpline3 *splineX=new TSpline3("x vs tau",lTi,lXi,n); - TSpline3 *splineY=new TSpline3("y vs tau",lTi,lYi,n); - // calculate (x,y) curvature for all points - // the curvature is stored in the array cCi[] as a function of cTi[] - for(Int_t i=0;i<n-1;i++) { - Double_t ltau,xy,bi,ci,di; - splineX->GetCoeff(i,ltau,xy,bi,ci,di); - Double_t tauBar=0.5*(lTi[i]+lTi[i+1]); - Double_t dTau=0.5*(lTi[i+1]-lTi[i]); - Double_t dx1=bi+dTau*(2.*ci+3.*di*dTau); - Double_t dx2=2.*ci+6.*di*dTau; - splineY->GetCoeff(i,ltau,xy,bi,ci,di); - Double_t dy1=bi+dTau*(2.*ci+3.*di*dTau); - Double_t dy2=2.*ci+6.*di*dTau; - cTi[i]=tauBar; - cCi[i]=(dy2*dx1-dy1*dx2)/TMath::Power(dx1*dx1+dy1*dy1,1.5); - } - delete splineX; - delete splineY; - delete[] lXi; - delete[] lYi; - delete[] lTi; - } - // create curvature Spline - TSpline3 *splineC=new TSpline3("L curve curvature",cTi,cCi,n-1); - // find the maximum of the curvature - // if the parameter iskip is non-zero, then iskip points are - // ignored when looking for the largest curvature - // (there are problems with the curvature determined from the first - // few points of splineX,splineY in the algorithm above) - Int_t iskip=0; - if(n>4) iskip=1; - if(n>7) iskip=2; - Double_t cCmax=cCi[iskip]; - Double_t cTmax=cTi[iskip]; - for(Int_t i=iskip;i<n-2-iskip;i++) { - // find maximum on this spline section - // check boundary conditions for x[i+1] - Double_t xMax=cTi[i+1]; - Double_t yMax=cCi[i+1]; - if(cCi[i]>yMax) { - yMax=cCi[i]; - xMax=cTi[i]; - } - // find maximum for x[i]<x<x[i+1] - // get spline coefficients and solve equation - // derivative(x)==0 - Double_t x,y,b,c,d; - splineC->GetCoeff(i,x,y,b,c,d); - // coefficients of quadratic equation - Double_t m_p_half=-c/(3.*d); - Double_t q=b/(3.*d); - Double_t discr=m_p_half*m_p_half-q; - if(discr>=0.0) { - // solution found - discr=TMath::Sqrt(discr); - Double_t xx; - if(m_p_half>0.0) { - xx = m_p_half + discr; - } else { - xx = m_p_half - discr; - } - Double_t dx=cTi[i+1]-x; - // check first solution - if((xx>0.0)&&(xx<dx)) { - y=splineC->Eval(x+xx); - if(y>yMax) { - yMax=y; - xMax=x+xx; - } - } - // second solution - if(xx !=0.0) { - xx= q/xx; - } else { - xx=0.0; - } - // check second solution - if((xx>0.0)&&(xx<dx)) { - y=splineC->Eval(x+xx); - if(y>yMax) { - yMax=y; - xMax=x+xx; - } - } - } - // check whether this local minimum is a global minimum - if(yMax>cCmax) { - cCmax=yMax; - cTmax=xMax; - } - } -#ifdef DEBUG_LCURVE - { - TCanvas lcc; - lcc.Divide(1,1); - lcc.cd(1); - splineC->Draw(); - lcc.SaveAs("splinec.ps"); - } -#endif - delete splineC; - delete[] cTi; - delete[] cCi; - logTauFin=cTmax; - DoUnfold(TMath::Power(10.,logTauFin)); - if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { - Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", - GetLcurveX(),GetLcurveY()); - } - Info("ScanLcurve","Result logtau=%lf X=%lf Y=%lf", - logTauFin,GetLcurveX(),GetLcurveY()); - curve[logTauFin]=std::make_pair(GetLcurveX(),GetLcurveY()); - } - - - //========================================================== - // (3) return the result in - // lCurve logTauX logTauY - - Int_t bestChoice=-1; - if(curve.size()>0) { - Double_t *x=new Double_t[curve.size()]; - Double_t *y=new Double_t[curve.size()]; - Double_t *logT=new Double_t[curve.size()]; - int n=0; + typedef std::map<Double_t,std::pair<Double_t,Double_t> > XYtau_t; + XYtau_t curve; + + //========================================================== + // algorithm: + // (1) do the unfolding for nPoint-1 points + // and store the results in the map + // curve + // (1a) store minimum and maximum tau to curve + // (1b) insert additional points, until nPoint-1 values + // have been calculated + // + // (2) determine the best choice of tau + // do the unfolding for this point + // and store the result in + // curve + // (3) return the result in + // lCurve logTauX logTauY + + //========================================================== + // (1) do the unfolding for nPoint-1 points + // and store the results in + // curve + // (1a) store minimum and maximum tau to curve + + if((tauMin<=0)||(tauMax<=0.0)||(tauMin>=tauMax)) { + // here no range is given, has to be determined automatically + // the maximum tau is determined from the chi**2 values + // observed from unfolding without regulatisation + + // first unfolding, without regularisation + DoUnfold(0.0); + + // if the number of degrees of freedom is too small, create an error + if(GetNdf()<=0) { + Error("ScanLcurve","too few input bins, NDF<=0 %d",GetNdf()); + } + + Double_t x0=GetLcurveX(); + Double_t y0=GetLcurveY(); + Info("ScanLcurve","logtau=-Infinity X=%lf Y=%lf",x0,y0); + if(!TMath::Finite(x0)) { + Fatal("ScanLcurve","problem (too few input bins?) X=%f",x0); + } + if(!TMath::Finite(y0)) { + Fatal("ScanLcurve","problem (missing regularisation?) Y=%f",y0); + } + { + // unfolding guess maximum tau and store it + Double_t logTau= + 0.5*(TMath::Log10(fChi2A+3.*TMath::Sqrt(GetNdf()+1.0)) + -GetLcurveY()); + DoUnfold(TMath::Power(10.,logTau)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", + logTau,GetLcurveX(),GetLcurveY()); + } + if((*curve.begin()).second.first<x0) { + // if the point at tau==0 seems numerically unstable, + // try to find the minimum chi**2 as start value + // + // "unstable" means that there is a finite tau where the + // unfolding chi**2 is smaller than for the case of no + // regularisation. Ideally this should never happen + do { + x0=GetLcurveX(); + Double_t logTau=(*curve.begin()).first-0.5; + DoUnfold(TMath::Power(10.,logTau)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", + logTau,GetLcurveX(),GetLcurveY()); + } + while(((int)curve.size()<(nPoint-1)/2)&& + ((*curve.begin()).second.first<x0)); + } else { + // minimum tau is chosen such that is less than + // 1% different from the case of no regularization + // log10(1.01) = 0.00432 + + // here, more than one point are inserted if necessary + while(((int)curve.size()<nPoint-1)&& + (((*curve.begin()).second.first-x0>0.00432)|| + ((*curve.begin()).second.second-y0>0.00432)|| + (curve.size()<2))) { + Double_t logTau=(*curve.begin()).first-0.5; + DoUnfold(TMath::Power(10.,logTau)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", + logTau,GetLcurveX(),GetLcurveY()); + } + } + } else { + Double_t logTauMin=TMath::Log10(tauMin); + Double_t logTauMax=TMath::Log10(tauMax); + if(nPoint>1) { + // insert maximum tau + DoUnfold(TMath::Power(10.,logTauMax)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", + logTauMax,GetLcurveX(),GetLcurveY()); + curve[logTauMax]=std::make_pair(GetLcurveX(),GetLcurveY()); + } + // insert minimum tau + DoUnfold(TMath::Power(10.,logTauMin)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf", + logTauMin,GetLcurveX(),GetLcurveY()); + curve[logTauMin]=std::make_pair(GetLcurveX(),GetLcurveY()); + } + + + //========================================================== + // (1b) insert additional points, until nPoint-1 values + // have been calculated + + while(int(curve.size())<nPoint-1) { + // insert additional points, such that the sizes of the delta(XY) vectors + // are getting smaller and smaller + XYtau_t::const_iterator i0,i1; + i0=curve.begin(); + i1=i0; + Double_t logTau=(*i0).first; + Double_t distMax=0.0; + for(i1++;i1!=curve.end();i1++) { + const std::pair<Double_t,Double_t> &xy0=(*i0).second; + const std::pair<Double_t,Double_t> &xy1=(*i1).second; + Double_t dx=xy1.first-xy0.first; + Double_t dy=xy1.second-xy0.second; + Double_t d=TMath::Sqrt(dx*dx+dy*dy); + if(d>=distMax) { + distMax=d; + logTau=0.5*((*i0).first+(*i1).first); + } + i0=i1; + } + DoUnfold(TMath::Power(10.,logTau)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + Info("ScanLcurve","logtau=%lf X=%lf Y=%lf",logTau,GetLcurveX(),GetLcurveY()); + curve[logTau]=std::make_pair(GetLcurveX(),GetLcurveY()); + } + + //========================================================== + // (2) determine the best choice of tau + // do the unfolding for this point + // and store the result in + // curve + XYtau_t::const_iterator i0,i1; + i0=curve.begin(); + i1=i0; + i1++; + Double_t logTauFin=(*i0).first; + if( ((int)curve.size())<nPoint) { + // set up splines and determine (x,y) curvature in each point + Double_t *cTi=new Double_t[curve.size()-1]; + Double_t *cCi=new Double_t[curve.size()-1]; + Int_t n=0; + { + Double_t *lXi=new Double_t[curve.size()]; + Double_t *lYi=new Double_t[curve.size()]; + Double_t *lTi=new Double_t[curve.size()]; for( XYtau_t::const_iterator i=curve.begin();i!=curve.end();i++) { - if(logTauFin==(*i).first) { - bestChoice=n; - } - x[n]=(*i).second.first; - y[n]=(*i).second.second; - logT[n]=(*i).first; - n++; - } - if(lCurve) { - (*lCurve)=new TGraph(n,x,y); - (*lCurve)->SetTitle("L curve"); - } - if(logTauX) (*logTauX)=new TSpline3("log(chi**2)%log(tau)",logT,x,n); - if(logTauY) (*logTauY)=new TSpline3("log(reg.cond)%log(tau)",logT,y,n); - delete[] x; - delete[] y; - delete[] logT; - } - - return bestChoice; + lXi[n]=(*i).second.first; + lYi[n]=(*i).second.second; + lTi[n]=(*i).first; + n++; + } + TSpline3 *splineX=new TSpline3("x vs tau",lTi,lXi,n); + TSpline3 *splineY=new TSpline3("y vs tau",lTi,lYi,n); + // calculate (x,y) curvature for all points + // the curvature is stored in the array cCi[] as a function of cTi[] + for(Int_t i=0;i<n-1;i++) { + Double_t ltau,xy,bi,ci,di; + splineX->GetCoeff(i,ltau,xy,bi,ci,di); + Double_t tauBar=0.5*(lTi[i]+lTi[i+1]); + Double_t dTau=0.5*(lTi[i+1]-lTi[i]); + Double_t dx1=bi+dTau*(2.*ci+3.*di*dTau); + Double_t dx2=2.*ci+6.*di*dTau; + splineY->GetCoeff(i,ltau,xy,bi,ci,di); + Double_t dy1=bi+dTau*(2.*ci+3.*di*dTau); + Double_t dy2=2.*ci+6.*di*dTau; + cTi[i]=tauBar; + cCi[i]=(dy2*dx1-dy1*dx2)/TMath::Power(dx1*dx1+dy1*dy1,1.5); + } + delete splineX; + delete splineY; + delete[] lXi; + delete[] lYi; + delete[] lTi; + } + // create curvature Spline + TSpline3 *splineC=new TSpline3("L curve curvature",cTi,cCi,n-1); + // find the maximum of the curvature + // if the parameter iskip is non-zero, then iskip points are + // ignored when looking for the largest curvature + // (there are problems with the curvature determined from the first + // few points of splineX,splineY in the algorithm above) + Int_t iskip=0; + if(n>4) iskip=1; + if(n>7) iskip=2; + Double_t cCmax=cCi[iskip]; + Double_t cTmax=cTi[iskip]; + for(Int_t i=iskip;i<n-2-iskip;i++) { + // find maximum on this spline section + // check boundary conditions for x[i+1] + Double_t xMax=cTi[i+1]; + Double_t yMax=cCi[i+1]; + if(cCi[i]>yMax) { + yMax=cCi[i]; + xMax=cTi[i]; + } + // find maximum for x[i]<x<x[i+1] + // get spline coefficients and solve equation + // derivative(x)==0 + Double_t x,y,b,c,d; + splineC->GetCoeff(i,x,y,b,c,d); + // coefficients of quadratic equation + Double_t m_p_half=-c/(3.*d); + Double_t q=b/(3.*d); + Double_t discr=m_p_half*m_p_half-q; + if(discr>=0.0) { + // solution found + discr=TMath::Sqrt(discr); + Double_t xx; + if(m_p_half>0.0) { + xx = m_p_half + discr; + } else { + xx = m_p_half - discr; + } + Double_t dx=cTi[i+1]-x; + // check first solution + if((xx>0.0)&&(xx<dx)) { + y=splineC->Eval(x+xx); + if(y>yMax) { + yMax=y; + xMax=x+xx; + } + } + // second solution + if(xx !=0.0) { + xx= q/xx; + } else { + xx=0.0; + } + // check second solution + if((xx>0.0)&&(xx<dx)) { + y=splineC->Eval(x+xx); + if(y>yMax) { + yMax=y; + xMax=x+xx; + } + } + } + // check whether this local minimum is a global minimum + if(yMax>cCmax) { + cCmax=yMax; + cTmax=xMax; + } + } + if(logTauCurvature) { + *logTauCurvature=splineC; + } else { + delete splineC; + } + delete[] cTi; + delete[] cCi; + logTauFin=cTmax; + DoUnfold(TMath::Power(10.,logTauFin)); + if((!TMath::Finite(GetLcurveX())) ||(!TMath::Finite(GetLcurveY()))) { + Fatal("ScanLcurve","problem (missing regularisation?) X=%f Y=%f", + GetLcurveX(),GetLcurveY()); + } + Info("ScanLcurve","Result logtau=%lf X=%lf Y=%lf", + logTauFin,GetLcurveX(),GetLcurveY()); + curve[logTauFin]=std::make_pair(GetLcurveX(),GetLcurveY()); + } + + + //========================================================== + // (3) return the result in + // lCurve logTauX logTauY + + Int_t bestChoice=-1; + if(curve.size()>0) { + Double_t *x=new Double_t[curve.size()]; + Double_t *y=new Double_t[curve.size()]; + Double_t *logT=new Double_t[curve.size()]; + int n=0; + for( XYtau_t::const_iterator i=curve.begin();i!=curve.end();i++) { + if(logTauFin==(*i).first) { + bestChoice=n; + } + x[n]=(*i).second.first; + y[n]=(*i).second.second; + logT[n]=(*i).first; + n++; + } + if(lCurve) { + (*lCurve)=new TGraph(n,x,y); + (*lCurve)->SetTitle("L curve"); + } + if(logTauX) (*logTauX)=new TSpline3("log(chi**2)%log(tau)",logT,x,n); + if(logTauY) (*logTauY)=new TSpline3("log(reg.cond)%log(tau)",logT,y,n); + delete[] x; + delete[] y; + delete[] logT; + } + + return bestChoice; } +//////////////////////////////////////////////////////////////////////////////// +/// Histogram of truth bins, determined from summing over the response matrix. +/// +/// \param[out] out histogram to store the truth bins. The bin contents +/// are overwritten +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// This vector is also used to initialize the bias +/// x_{0}. However, the bias vector may be changed using the +/// SetBias() method. +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method. + void TUnfold::GetNormalisationVector(TH1 *out,const Int_t *binMap) const { - // get vector of normalisation factors - // out: output histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... ClearHistogram(out); for (Int_t i = 0; i < GetNx(); i++) { @@ -2791,16 +2909,20 @@ void TUnfold::GetNormalisationVector(TH1 *out,const Int_t *binMap) const } } +//////////////////////////////////////////////////////////////////////////////// +/// Get bias vector including bias scale. +/// +/// \param[out] out histogram to store the scaled bias vector. The bin +/// contents are overwritten +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// This method returns the bias vector times scaling factor, f*x_{0} +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + void TUnfold::GetBias(TH1 *out,const Int_t *binMap) const { - // get bias distribution, possibly with bin remapping - // out: output histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... ClearHistogram(out); for (Int_t i = 0; i < GetNx(); i++) { @@ -2812,17 +2934,21 @@ void TUnfold::GetBias(TH1 *out,const Int_t *binMap) const } } +//////////////////////////////////////////////////////////////////////////////// +/// Get unfolding result on detector level. +/// +/// \param[out] out histogram to store the correlation coefficients. The bin +/// contents and errors are overwritten. +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// This method returns the unfolding output folded by the response +/// matrix, i.e. the vector Ax. +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + void TUnfold::GetFoldedOutput(TH1 *out,const Int_t *binMap) const { - // get unfolding result, folded back trough the matrix - // out: output histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... - ClearHistogram(out); TMatrixDSparse *AVxx=MultiplyMSparseMSparse(fA,fVxx); @@ -2842,7 +2968,7 @@ void TUnfold::GetFoldedOutput(TH1 *out,const Int_t *binMap) const Double_t e2=0.0; Int_t index_a=rows_A[i]; Int_t index_av=rows_AVxx[i]; - while((index_a<rows_A[i+1])&&(index_av<rows_AVxx[i])) { + while((index_a<rows_A[i+1])&&(index_av<rows_AVxx[i+1])) { Int_t j_a=cols_A[index_a]; Int_t j_av=cols_AVxx[index_av]; if(j_a<j_av) { @@ -2860,6 +2986,15 @@ void TUnfold::GetFoldedOutput(TH1 *out,const Int_t *binMap) const DeleteMatrix(&AVxx); } +//////////////////////////////////////////////////////////////////////////////// +/// Get matrix of probabilities. +/// +/// \param[out] A two-dimensional histogram to store the +/// probabilities (normalized response matrix). The bin contents are +/// overwritten +/// \param[in] histmap specify axis along which the truth bins are +/// oriented + void TUnfold::GetProbabilityMatrix(TH2 *A,EHistMap histmap) const { // retrieve matrix of probabilities @@ -2873,25 +3008,30 @@ void TUnfold::GetProbabilityMatrix(TH2 *A,EHistMap histmap) const Int_t ix = cols_A[indexA]; Int_t ih=fXToHist[ix]; if (histmap == kHistMapOutputHoriz) { - A->SetBinContent(ih, iy,data_A[indexA]); + A->SetBinContent(ih, iy+1,data_A[indexA]); } else { - A->SetBinContent(iy, ih,data_A[indexA]); + A->SetBinContent(iy+1, ih,data_A[indexA]); } } } } +//////////////////////////////////////////////////////////////////////////////// +/// Input vector of measurements +/// +/// \param[out] out histogram to store the measurements. Bin content +/// and bin errors are overwrite. +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// Bins which had an uncertainty of zero in the call to SetInput() +/// may acquire bin contents or bin errors different from the +/// original settings in SetInput(). +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + void TUnfold::GetInput(TH1 *out,const Int_t *binMap) const { - // retrieve input distribution - // out: output histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... - ClearHistogram(out); const Int_t *rows_Vyy=fVyy->GetRowIndexArray(); @@ -2899,7 +3039,7 @@ void TUnfold::GetInput(TH1 *out,const Int_t *binMap) const const Double_t *data_Vyy=fVyy->GetMatrixArray(); for (Int_t i = 0; i < GetNy(); i++) { - Int_t destI=binMap ? binMap[i] : i; + Int_t destI=binMap ? binMap[i+1] : i+1; if(destI<0) continue; out->SetBinContent(destI, (*fY) (i, 0)+out->GetBinContent(destI)); @@ -2914,6 +3054,11 @@ void TUnfold::GetInput(TH1 *out,const Int_t *binMap) const } } +//////////////////////////////////////////////////////////////////////////////// +/// Get inverse of the measurement's covariance matrix. +/// +/// \param[out] out histogram to store the inverted covariance + void TUnfold::GetInputInverseEmatrix(TH2 *out) { // calculate the inverse of the contribution to the error matrix @@ -2936,9 +3081,9 @@ void TUnfold::GetInputInverseEmatrix(TH2 *out) } if(out) { // return matrix as histogram - const Int_t *rows_Vyy=fVyy->GetRowIndexArray(); - const Int_t *cols_Vyy=fVyy->GetColIndexArray(); - const Double_t *data_Vyy=fVyy->GetMatrixArray(); + const Int_t *rows_VyyInv=fVyyInv->GetRowIndexArray(); + const Int_t *cols_VyyInv=fVyyInv->GetColIndexArray(); + const Double_t *data_VyyInv=fVyyInv->GetMatrixArray(); for(int i=0;i<=out->GetNbinsX()+1;i++) { for(int j=0;j<=out->GetNbinsY()+1;j++) { @@ -2946,15 +3091,27 @@ void TUnfold::GetInputInverseEmatrix(TH2 *out) } } - for (Int_t i = 0; i < fVyy->GetNrows(); i++) { - for(int index=rows_Vyy[i];index<rows_Vyy[i+1];index++) { - Int_t j=cols_Vyy[index]; - out->SetBinContent(i+1,j+1,data_Vyy[index]); + for (Int_t i = 0; i < fVyyInv->GetNrows(); i++) { + for(int index=rows_VyyInv[i];index<rows_VyyInv[i+1];index++) { + Int_t j=cols_VyyInv[index]; + out->SetBinContent(i+1,j+1,data_VyyInv[index]); } } } } +//////////////////////////////////////////////////////////////////////////////// +/// Get matrix of regularisation conditions squared. +/// +/// \param[out] out histogram to store the squared matrix of +/// regularisation conditions. the bin contents are overwritten +/// +/// This returns the square matrix L^{T}L as a histogram +/// +/// The histogram should have dimension nx times nx, where nx +/// corresponds to the number of histogram bins in the response matrix +/// along the truth axis. + void TUnfold::GetLsquared(TH2 *out) const { // retrieve matrix of regularisation conditions squared @@ -2967,31 +3124,54 @@ void TUnfold::GetLsquared(TH2 *out) const const Double_t *data=lSquared->GetMatrixArray(); for (Int_t i = 0; i < GetNx(); i++) { for (Int_t cindex = rows[i]; cindex < rows[i+1]; cindex++) { - Int_t j=cols[cindex]; - out->SetBinContent(fXToHist[i], fXToHist[j],data[cindex]); + Int_t j=cols[cindex]; + out->SetBinContent(fXToHist[i], fXToHist[j],data[cindex]); } } DeleteMatrix(&lSquared); } +//////////////////////////////////////////////////////////////////////////////// +/// Get number of regularisation conditions. +/// +/// This returns the number of regularisation conditions, useful for +/// booking a histogram for a subsequent call of GetL(). + +Int_t TUnfold::GetNr(void) const { + return fL->GetNrows(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get matrix of regularisation conditions. +/// +/// \param[out] out histogram to store the regularisation conditions. +/// the bin contents are overwritten +/// +/// The histogram should have dimension nr (x-axis) times nx (y-axis). +/// nr corresponds to the number of regularisation conditions, it can +/// be obtained using the method GetNr(). nx corresponds to the number +/// of histogram bins in the response matrix along the truth axis. + void TUnfold::GetL(TH2 *out) const { - // retrieve matrix of regularisation conditions - // out: pre-booked matrix - // loop over sparse matrix const Int_t *rows=fL->GetRowIndexArray(); const Int_t *cols=fL->GetColIndexArray(); const Double_t *data=fL->GetMatrixArray(); for (Int_t row = 0; row < GetNr(); row++) { for (Int_t cindex = rows[row]; cindex < rows[row+1]; cindex++) { - Int_t col=cols[cindex]; - Int_t indexH=fXToHist[col]; - out->SetBinContent(indexH,row+1,data[cindex]); + Int_t col=cols[cindex]; + Int_t indexH=fXToHist[col]; + out->SetBinContent(indexH,row+1,data[cindex]); } } } +//////////////////////////////////////////////////////////////////////////////// +/// Set type of area constraint. +/// +/// results of a previous unfolding are reset + void TUnfold::SetConstraint(EConstraint constraint) { // set type of constraint for the next unfolding @@ -3000,55 +3180,98 @@ void TUnfold::SetConstraint(EConstraint constraint) Info("SetConstraint","fConstraint=%d",fConstraint); } + +//////////////////////////////////////////////////////////////////////////////// +/// Return regularisation parameter. + Double_t TUnfold::GetTau(void) const { // return regularisation parameter return TMath::Sqrt(fTauSquared); } +//////////////////////////////////////////////////////////////////////////////// +/// Get \f$ chi^{2}_{L} \f$ contribution determined in recent unfolding. + Double_t TUnfold::GetChi2L(void) const { // return chi**2 contribution from regularisation conditions return fLXsquared*fTauSquared; } +//////////////////////////////////////////////////////////////////////////////// +/// Get number of truth parameters determined in recent unfolding. +/// +/// empty bins of the response matrix or bins which can not be +/// unfolded due to rank deficits are not counted + Int_t TUnfold::GetNpar(void) const { - // return number of parameters return GetNx(); } +//////////////////////////////////////////////////////////////////////////////// +/// Get value on x-axis of L-curve determined in recent unfolding. +/// +/// \f$ x=log_{10}(GetChi2A()) \f$ + Double_t TUnfold::GetLcurveX(void) const { - // return value on x axis of L curve - return TMath::Log10(fChi2A); + return TMath::Log10(fChi2A); } +//////////////////////////////////////////////////////////////////////////////// +/// Get value on y-axis of L-curve determined in recent unfolding. +/// +/// \f$ y=log_{10}(GetChi2L()) \f$ + Double_t TUnfold::GetLcurveY(void) const { - // return value on y axis of L curve - return TMath::Log10(fLXsquared); + return TMath::Log10(fLXsquared); } +//////////////////////////////////////////////////////////////////////////////// +/// Get output distribution, possibly cumulated over several bins. +/// +/// \param[out] output existing output histogram. content and errors +/// will be updated. +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// If nonzero, the array <b>binMap</b> must have dimension n+2, where n +/// corresponds to the number of bins on the truth axis of the response +/// matrix (the histogram specified with the TUnfold +/// constructor). The indexes of <b>binMap</b> correspond to the truth +/// bins (including underflow and overflow) of the response matrix. +/// The element binMap[i] specifies the histogram number in +/// <b>output</b> where the corresponding truth bin will be stored. It is +/// possible to specify the same <b>output</b> bin number for multiple +/// indexes, in which case these bins are added. Set binMap[i]=-1 to +/// ignore an unfolded truth bin. The uncertainties are +/// calculated from the corresponding parts of the covariance matrix, +/// properly taking care of added truth bins. +/// +/// If the pointer <b>binMap</b> is zero, the bins are mapped +/// one-to-one. Truth bin zero (underflow) is stored in the +/// <b>output</b> underflow, truth bin 1 is stored in bin number 1, etc. +/// +/// - output: output histogram +/// - binMap: for each bin of the original output distribution +/// specify the destination bin. A value of -1 means that the bin +/// is discarded. 0 means underflow bin, 1 first bin, ... +/// - binMap[0] : destination of underflow bin +/// - binMap[1] : destination of first bin +/// ... + void TUnfold::GetOutput(TH1 *output,const Int_t *binMap) const { - // get output distribution, cumulated over several bins - // output: output histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... - ClearHistogram(output); /* Int_t nbin=output->GetNbinsX(); - Double_t *c=new Double_t[nbin+2]; - Double_t *e2=new Double_t[nbin+2]; - for(Int_t i=0;i<nbin+2;i++) { - c[i]=0.0; - e2[i]=0.0; - } */ + Double_t *c=new Double_t[nbin+2]; + Double_t *e2=new Double_t[nbin+2]; + for(Int_t i=0;i<nbin+2;i++) { + c[i]=0.0; + e2[i]=0.0; + } */ std::map<Int_t,Double_t> e2; @@ -3062,7 +3285,7 @@ void TUnfold::GetOutput(TH1 *output,const Int_t *binMap) const Int_t srcBinI=fHistToX[i]; // matrix row index if((destBinI>=0)&&(srcBinI>=0)) { output->SetBinContent - (destBinI, (*fX)(srcBinI,0)+ output->GetBinContent(destBinI)); + (destBinI, (*fX)(srcBinI,0)+ output->GetBinContent(destBinI)); // here we loop over the columns of the error matrix // j: counts histogram bins // index: counts sparse matrix index @@ -3106,18 +3329,23 @@ void TUnfold::GetOutput(TH1 *output,const Int_t *binMap) const } } +//////////////////////////////////////////////////////////////////////////////// +/// Add up an error matrix, also respecting the bin mapping. +/// +/// \param[inout] ematrix error matrix histogram +/// \param[in] emat error matrix stored with internal mapping (member fXToHist) +/// \param[in] binMap mapping of histogram bins +/// \param[in] doClear if true, ematrix is cleared prior to adding +/// elements of emat to it. +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). The +/// matrix emat must have dimension NxN where N=fXToHist.size() +/// The flag <b>doClear</b> may be used to add covariance matrices from +/// several uncertainty sources. + void TUnfold::ErrorMatrixToHist(TH2 *ematrix,const TMatrixDSparse *emat, const Int_t *binMap,Bool_t doClear) const { - // get an error matrix, cumulated over several bins - // ematrix: output error matrix histogram - // emat: error matrix - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... Int_t nbin=ematrix->GetNbinsX(); if(doClear) { for(Int_t i=0;i<nbin+2;i++) { @@ -3160,7 +3388,7 @@ void TUnfold::ErrorMatrixToHist(TH2 *ematrix,const TMatrixDSparse *emat, } else { // add this bin Double_t e2= ematrix->GetBinContent(destBinI,destBinJ) - + data_emat[index_vxx]; + + data_emat[index_vxx]; ematrix->SetBinContent(destBinI,destBinJ,e2); j++; index_vxx++; @@ -3172,29 +3400,33 @@ void TUnfold::ErrorMatrixToHist(TH2 *ematrix,const TMatrixDSparse *emat, } } +//////////////////////////////////////////////////////////////////////////////// +/// Get output covariance matrix, possibly cumulated over several bins. +/// +/// \param[out] ematrix histogram to store the covariance. The bin +/// contents are overwritten. +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + void TUnfold::GetEmatrix(TH2 *ematrix,const Int_t *binMap) const { - // get output error matrix, cumulated over several bins - // ematrix: output error matrix histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... ErrorMatrixToHist(ematrix,fVxx,binMap,kTRUE); } +//////////////////////////////////////////////////////////////////////////////// +/// Get correlation coefficients, possibly cumulated over several bins. +/// +/// \param[out] rhoij histogram to store the correlation coefficients. The bin +/// contents are overwritten. +/// \param[in] binMap (default=0) array for mapping truth bins to histogram bins +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + void TUnfold::GetRhoIJ(TH2 *rhoij,const Int_t *binMap) const { - // get correlation coefficient matrix, cumulated over several bins - // rhoij: correlation coefficient matrix histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... GetEmatrix(rhoij,binMap); Int_t nbin=rhoij->GetNbinsX(); Double_t *e=new Double_t[nbin+2]; @@ -3213,18 +3445,29 @@ void TUnfold::GetRhoIJ(TH2 *rhoij,const Int_t *binMap) const delete[] e; } +//////////////////////////////////////////////////////////////////////////////// +/// Get global correlation coefficients, possibly cumulated over several bins. +/// +/// \param[out] rhoi histogram to store the global correlation +/// coefficients. The bin contents are overwritten. +/// \param[in] binMap (default=0) array for mapping truth bins to +/// histogram bins +/// \param[out] invEmat (default=0) histogram to store the inverted +/// covariance matrix +/// +/// for a given bin, the global correlation coefficient is defined +/// as \f$ \rho_{i} = \sqrt{1-\frac{1}{(V_{ii}*V^{-1}_{ii})}} \f$ +/// +/// such that the calculation of global correlation coefficients +/// possibly involves the inversion of a covariance matrix. +/// +/// return value: maximum global correlation coefficient +/// +/// The use of <b>binMap</b> is explained with the documentation of +/// the GetOutput() method + Double_t TUnfold::GetRhoI(TH1 *rhoi,const Int_t *binMap,TH2 *invEmat) const { - // get global correlation coefficients with arbitrary min map - // rhoi: global correlation histogram - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... - // return value: maximum global correlation - ClearHistogram(rhoi,-1.); if(binMap) { @@ -3272,20 +3515,22 @@ Double_t TUnfold::GetRhoI(TH1 *rhoi,const Int_t *binMap,TH2 *invEmat) const } } +//////////////////////////////////////////////////////////////////////////////// +/// Get global correlation coefficients with arbitrary min map. +/// +/// - rhoi: global correlation histogram +/// - emat: error matrix +/// - binMap: for each bin of the original output distribution +/// specify the destination bin. A value of -1 means that the bin +/// is discarded. 0 means underflow bin, 1 first bin, ... +/// - binMap[0] : destination of underflow bin +/// - binMap[1] : destination of first bin +/// ... +/// return value: maximum global correlation + Double_t TUnfold::GetRhoIFromMatrix(TH1 *rhoi,const TMatrixDSparse *eOrig, const Int_t *binMap,TH2 *invEmat) const { - // get global correlation coefficients with arbitrary min map - // rhoi: global correlation histogram - // emat: error matrix - // binMap: for each bin of the original output distribution - // specify the destination bin. A value of -1 means that the bin - // is discarded. 0 means underflow bin, 1 first bin, ... - // binMap[0] : destination of underflow bin - // binMap[1] : destination of first bin - // ... - // return value: maximum global correlation - Double_t rhoMax=0.; // original number of bins: // fHistToX.GetSize() @@ -3391,9 +3636,16 @@ Double_t TUnfold::GetRhoIFromMatrix(TH1 *rhoi,const TMatrixDSparse *eOrig, return rhoMax; } +//////////////////////////////////////////////////////////////////////////////// +/// Initialize bin contents and bin errors for a given histogram. +/// +/// \param[out] h histogram +/// \param[in] x new histogram content +/// +/// all histgram errors are set to zero, all contents are set to <b>x</b> + void TUnfold::ClearHistogram(TH1 *h,Double_t x) const { - // clear histogram contents and error Int_t nxyz[3]; nxyz[0]=h->GetNbinsX()+1; nxyz[1]=h->GetNbinsY()+1; @@ -3419,3 +3671,18 @@ void TUnfold::SetEpsMatrix(Double_t eps) { // set accuracy for matrix inversion if((eps>0.0)&&(eps<1.0)) fEpsMatrix=eps; } + +//////////////////////////////////////////////////////////////////////////////// +/// Return a string describing the TUnfold version. +/// +/// The version is reported in the form Vmajor.minor +/// Changes of the minor version number typically correspond to +/// bug-fixes. Changes of the major version may result in adding or +/// removing data attributes, such that the streamer methods are not +/// compatible between different major versions. + +const char *TUnfold::GetTUnfoldVersion(void) +{ + return TUnfold_VERSION; +} + diff --git a/hist/unfold/src/TUnfoldBinning.cxx b/hist/unfold/src/TUnfoldBinning.cxx new file mode 100644 index 0000000000000000000000000000000000000000..242eaaf6991095e148d5039d86208c07a5fedc03 --- /dev/null +++ b/hist/unfold/src/TUnfoldBinning.cxx @@ -0,0 +1,2176 @@ +// @(#)root/unfold:$Id$ +// Author: Stefan Schmitt DESY, 10/08/11 + +/** \class TUnfoldBinning +\ingroup Unfold +Binning schemes for use with the unfolding algorithm TUnfoldDensity. + +Binning schemes are used to map analysis bins on a single histogram +axis and back. The analysis bins may include unconnected bins (e.g +nuisances for background normalisation) or various multidimensional +histograms (signal bins, differential background normalisation bins, etc). + +If you use this software, please consider the following citation + +<b>S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201]</b> + +Detailed documentation and updates are available on +http://www.desy.de/~sschmitt + +### Functionality + +The TUnfoldBinning objects are connected by a tree-like structure. +The structure does not hold any data, but is only responsible for +arranging the analysis bins in the proper order. +Each node of the tree is responsible for a group of bins. That group +may consist of + + - several unconnected bins, each with a dedicated name. + - bins organized in a multidimensional distribution, defined by a +set of axes. The axes are defined by a number of bins N and by (N+1) +bin borders. In addition to the N bins inside there may be an underflow and an +overflow bin + +Each bin has a "global" bin number, which can be found using the +GetGlobalBinNumber() methods. The global bin number 0 is reserved and +corresponds to the case where no bin is found in the +TUnfoldBinning tree. + +### Use in the analysis +Booking histograms: + + - Define binning schemes on detector level and on truth level. This +can be done using the XML language, use the class TUnfoldBinningXML to +read the binning scheme. The TUnfoldBinning objects can be written to +a root file, preferentially together with the corresponding histograms. + - For Monte Carlo, book histograms for the response matrix (detector +vs truth level) using the +method CreateHistogramOfMigrations() + - For data and background, book histograms using the +"detector level" binning scheme and the method CreateHistogram() + - (if required) for the data covariance matrix, book a histogram using the +"detector level" binning scheme and the method CreateErrorMatrixHistogram() + - For truth histograms, book histograms using the +"truth level" binning scheme and the method CreateHistogram() + +The histograms which are booked have all analysis bins arranged on one +axis (global bin number). TUnfoldBinning provides methods to locate +the global bin number: + + - Use the method FindNode() to locate a group of bins (e.g. signal, +control distribution, etc) by their name, then: + - Use the method GetGlobalBinNumber() to locate a bin in a +distribution, then: + - Use the TH1::Fill() method and the bin number to fill the +appropriate bin in one of the histograms booked above. + +Unfolding: Specify the response matrix and the binning schemes when +constructing a TUnfoldDensity object. Tell TUnfoldDensity about the +data, background, systematic error histograms using the corresponding +methods of class TUnfoldDensity. Then run the unfolding. Use the +GetXXX() methods to retrieve the unfolding results into properly +binned multidimensional histograms. + +-------------------------------------------------------------------------------- + + This file is part of TUnfold. + + TUnfold 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 3 of the License, or + (at your option) any later version. + + TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. + +<b>Version 17.6, bug fix to avoid possible crash in method +CreateHistogramOfMigrations(). Possible bug fix with NaN in GetGlobalBinNUmber() </b> + +#### History: + - Version 17.5, in parallel to changes in TUnfold + - Version 17.4, bug fix with error handling + - Version 17.3, bug fix with underflow/overflow bins + - Version 17.2, with XML support, bug fix with bin map creation, isPeriodic option for neighbour bins + - Version 17.1, in parallel to changes in TUnfold + - Version 17.0, initial version, numbered in parallel to TUnfold + +*/ + +#include "TUnfoldBinningXML.h" +#include <TVectorD.h> +#include <TAxis.h> +#include <TString.h> +#include <TMath.h> +#include <TF1.h> +#include <TH1D.h> +#include <TH2D.h> +#include <TH3D.h> +#include <TIterator.h> +#include <iomanip> + +// #define DEBUG + +using namespace std; + +ClassImp(TUnfoldBinning) + +//////////////////////////////////////////////////////////////////////////////// +// Destructor. + +TUnfoldBinning::~TUnfoldBinning(void) +{ + // delete all children + while(childNode) delete childNode; + // remove this node from the tree + if(GetParentNode() && (GetParentNode()->GetChildNode()==this)) { + parentNode->childNode=nextNode; + } + if(GetPrevNode()) prevNode->nextNode=nextNode; + if(GetNextNode()) nextNode->prevNode=prevNode; + delete fAxisList; + delete fAxisLabelList; + if(fBinFactorFunction) { + if(!dynamic_cast<TF1 *>(fBinFactorFunction)) + delete fBinFactorFunction; + } +} + +/********************* setup **************************/ + +//////////////////////////////////////////////////////////////////////////////// +/// Initialize variables for a given number of bins. + +void TUnfoldBinning::Initialize(Int_t nBins) +{ + parentNode=0; + childNode=0; + nextNode=0; + prevNode=0; + fAxisList=new TObjArray(); + fAxisLabelList=new TObjArray(); + fAxisList->SetOwner(); + fAxisLabelList->SetOwner(); + fHasUnderflow=0; + fHasOverflow=0; + fDistributionSize=nBins; + fBinFactorFunction=0; + fBinFactorConstant=1.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Update fFirstBin and fLastBin members of this node and its children. +/// +/// \param[in] startWithRootNode if true, start the update with the root node + +Int_t TUnfoldBinning::UpdateFirstLastBin(Bool_t startWithRootNode) +{ + if(startWithRootNode) { + return GetRootNode()->UpdateFirstLastBin(kFALSE); + } + if(GetPrevNode()) { + // if this is not the first node in a sequence, + // start with the end bin of the previous node + fFirstBin=GetPrevNode()->GetEndBin(); + } else if(GetParentNode()) { + // if this is the first node in a sequence but has a parent, + // start with the end bin of the parent's distribution + fFirstBin=GetParentNode()->GetStartBin()+ + GetParentNode()->GetDistributionNumberOfBins(); + } else { + // if this is the top level node, the first bin number is 1 + fFirstBin=1; + // ... unless the top level node is the only node + // ... with dimension=1 + // ... and there are no child nodes + // ... and there is an underflow bin + if((!GetChildNode())&&(GetDistributionDimension()==1)&& + (fHasUnderflow==1)) { + fFirstBin=0; + } + } + fLastBin=fFirstBin+fDistributionSize; + // now update count for all children + for(TUnfoldBinning *node=childNode;node;node=node->nextNode) { + fLastBin=node->UpdateFirstLastBin(kFALSE); + } + return fLastBin; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create a new node without axis. +/// +/// \param[in] name identifier of the node +/// \param[in] nBin number of unconnected bins (could be zero) +/// \param[in] binNames (optional) names of the bins separated by ';' + +TUnfoldBinning::TUnfoldBinning +(const char *name,Int_t nBins,const char *binNames) + : TNamed(name ? name : "",name ? name : "") +{ + Initialize(nBins); + if(binNames) { + TString nameString(binNames); + delete fAxisLabelList; + fAxisLabelList=nameString.Tokenize(";"); + } + UpdateFirstLastBin(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create a new node containing a distribution with one axis. +/// +/// \param[in] axis the axis to represent +/// \param[in] includeUnderflow true if underflow bin should be included +/// \param[in] includeOverflow true if overflow bin should be included + +TUnfoldBinning::TUnfoldBinning +(const TAxis &axis,Int_t includeUnderflow,Int_t includeOverflow) + : TNamed(axis.GetName(),axis.GetTitle()) +{ + Initialize(0); + AddAxis(axis,includeUnderflow,includeOverflow); + UpdateFirstLastBin(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a new binning node as last last child of this node. +/// +/// \param[in] name name of the node +/// \param[in] nBin number of extra bins +/// \param[in] binNames (optional) names of the bins separated by ';' +/// +/// this is a shortcut for AddBinning(new TUnfoldBinning(name,nBins,binNames)) + +TUnfoldBinning *TUnfoldBinning::AddBinning +(const char *name,Int_t nBins,const char *binNames) +{ + return AddBinning(new TUnfoldBinning(name,nBins,binNames)); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a TUnfoldBinning as the last child of this node. +/// +/// \param[in] binning the new binning to be added +/// +/// return value: if succeeded, return "binning" +/// otherwise return 0 + +TUnfoldBinning *TUnfoldBinning::AddBinning(TUnfoldBinning *binning) +{ + TUnfoldBinning *r=0; + if(binning->GetParentNode()) { + Error("AddBinning", + "binning \"%s\" already has parent \"%s\", can not be added to %s", + (char *)binning->GetName(), + (char *)binning->GetParentNode()->GetName(), + (char *)GetName()); + } else if(binning->GetPrevNode()) { + Error("AddBinning", + "binning \"%s\" has previous node \"%s\", can not be added to %s", + (char *)binning->GetName(), + (char *)binning->GetPrevNode()->GetName(), + (char *)GetName()); + } else if(binning->GetNextNode()) { + Error("AddBinning", + "binning \"%s\" has next node \"%s\", can not be added to %s", + (char *)binning->GetName(), + (char *)binning->GetNextNode()->GetName(), + (char *)GetName()); + } else { + r=binning; + binning->parentNode=this; + if(childNode) { + TUnfoldBinning *child=childNode; + // find last child + while(child->nextNode) { + child=child->nextNode; + } + // add as last child + child->nextNode=r; + r->prevNode=child; + } else { + childNode=r; + } + UpdateFirstLastBin(); + r=binning; + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add an axis with equidistant bins. +/// +/// \param[in] name name of the axis +/// \param[in] nBin number of bins +/// \param[in] xMin lower edge of the first bin +/// \param[in] xMax upper edge of the last bin +/// \param[in] hasUnderflow decide whether the axis has an underflow bin +/// \param[in] hasOverflow decide whether the axis has an overflow bin +/// +/// returns true if the axis has been added + +Bool_t TUnfoldBinning::AddAxis +(const char *name,Int_t nBin,Double_t xMin,Double_t xMax, + Bool_t hasUnderflow,Bool_t hasOverflow) +{ + Bool_t r=kFALSE; + if(nBin<=0) { + Fatal("AddAxis","number of bins %d is not positive", + nBin); + } else if((!TMath::Finite(xMin))||(!TMath::Finite(xMax))|| + (xMin>=xMax)) { + Fatal("AddAxis","xmin=%f required to be smaller than xmax=%f", + xMin,xMax); + } else { + Double_t *binBorders=new Double_t[nBin+1]; + Double_t x=xMin; + Double_t dx=(xMax-xMin)/nBin; + for(Int_t i=0;i<=nBin;i++) { + binBorders[i]=x+i*dx; + } + r=AddAxis(name,nBin,binBorders,hasUnderflow,hasOverflow); + delete [] binBorders; + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add an axis to the distribution, using the TAxis as blueprint. +/// +/// \param[in] axis blueprint of the axis +/// \param[in] hasUnderflow decide whether the underflow bin should be included +/// \param[in] hasOverflow decide whether the overflow bin should be included +/// +/// returns true if the axis has been added +/// +/// Note: axis labels are not imported + +Bool_t TUnfoldBinning::AddAxis +(const TAxis &axis,Bool_t hasUnderflow,Bool_t hasOverflow) +{ + Int_t nBin=axis.GetNbins(); + Double_t *binBorders=new Double_t[nBin+1]; + for(Int_t i=0;i<nBin;i++) { + binBorders[i]=axis.GetBinLowEdge(i+1); + } + binBorders[nBin]=axis.GetBinUpEdge(nBin); + Bool_t r=AddAxis(axis.GetTitle(),nBin,binBorders,hasUnderflow,hasOverflow); + delete [] binBorders; + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add an axis with the specified bin borders. +/// +/// \param[in] name name of the axis +/// \param[in] nBin number of bins +/// \param[in] binBorders array of bin borders, with nBin+1 elements +/// \param[in] hasUnderflow decide whether the axis has an underflow bin +/// \param[in] hasOverflow decide whether the axis has an overflow bin +/// +/// returns true if the axis has been added + +Bool_t TUnfoldBinning::AddAxis +(const char *name,Int_t nBin,const Double_t *binBorders, + Bool_t hasUnderflow,Bool_t hasOverflow) +{ + Bool_t r=kFALSE; + if(HasUnconnectedBins()) { + Fatal("AddAxis","node already has %d bins without axis", + GetDistributionNumberOfBins()); + } else if(nBin<=0) { + Fatal("AddAxis","number of bins %d is not positive", + nBin); + } else { + TVectorD *bins=new TVectorD(nBin+1); + r=kTRUE; + for(Int_t i=0;i<=nBin;i++) { + (*bins)(i)=binBorders[i]; + if(!TMath::Finite((*bins)(i))) { + Fatal("AddAxis","bin border %d is not finite",i); + r=kFALSE; + } else if((i>0)&&((*bins)(i)<=(*bins)(i-1))) { + Fatal("AddAxis","bins not in order x[%d]=%f <= %f=x[%d]", + i,(*bins)(i),(*bins)(i-1),i-1); + r=kFALSE; + } + } + if(r) { + Int_t axis=fAxisList->GetEntriesFast(); + Int_t bitMask=1<<axis; + Int_t nBinUO=nBin; + if(hasUnderflow) { + fHasUnderflow |= bitMask; + nBinUO++; + } else { + fHasUnderflow &= ~bitMask; + } + if(hasOverflow) { + fHasOverflow |= bitMask; + nBinUO++; + } else { + fHasOverflow &= ~bitMask; + } + fAxisList->AddLast(bins); + fAxisLabelList->AddLast(new TObjString(name)); + if(!fDistributionSize) fDistributionSize=1; + fDistributionSize *= nBinUO; + UpdateFirstLastBin(); + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Print some information about this binning tree. +/// +/// \param[out] out stream to write to +/// \param[in] indent initial indentation (sub-trees have indent+1) +/// \param[in] debug if debug>0 print more information + +void TUnfoldBinning::PrintStream(ostream &out,Int_t indent,int debug) + const { + for(Int_t i=0;i<indent;i++) out<<" "; + out<<"TUnfoldBinning \""<<GetName()<<"\" has "; + Int_t nBin=GetEndBin()-GetStartBin(); + if(nBin==1) { + out<<"1 bin"; + } else { + out<<nBin<<" bins"; + } + out<<" [" + <<GetStartBin()<<","<<GetEndBin()<<"] nTH1x=" + <<GetTH1xNumberOfBins() + <<"\n"; + if(GetDistributionNumberOfBins()) { + for(Int_t i=0;i<indent;i++) out<<" "; + out<<" distribution: "<<GetDistributionNumberOfBins()<<" bins\n"; + if(fAxisList->GetEntriesFast()) { + /* for(Int_t i=0;i<indent;i++) out<<" "; + out<<" axes:\n"; */ + for(Int_t axis=0;axis<GetDistributionDimension();axis++) { + for(Int_t i=0;i<indent;i++) out<<" "; + out<<" \"" + <<GetDistributionAxisLabel(axis) + <<"\" nbin="<<GetDistributionBinning(axis)->GetNrows()-1; + if(HasUnderflow(axis)) out<<" plus underflow"; + if(HasOverflow(axis)) out<<" plus overflow"; + out<<"\n"; + } + } else { + for(Int_t i=0;i<indent;i++) out<<" "; + out<<" no axis\n"; + for(Int_t i=0;i<indent;i++) out<<" "; + out<<" names: "; + for(Int_t ibin=0;(ibin<GetDistributionNumberOfBins())&& + (ibin<fAxisLabelList->GetEntriesFast());ibin++) { + if(ibin) out<<";"; + if(GetDistributionAxisLabel(ibin)) { + out<<GetDistributionAxisLabel(ibin); + } + } + out<<"\n"; + } + if(debug>0) { + // print all bins with full name, size, status, user factor + for(int iBin=GetStartBin();iBin<GetEndBin();iBin++) { + for(Int_t i=0;i<indent;i++) out<<" "; + out<<GetBinName(iBin) + <<" size="<<GetBinSize(iBin) + <<" factor="<<GetBinFactor(iBin); + out<<"\n"; + } + } + } + TUnfoldBinning const *child=GetChildNode(); + if(child) { + while(child) { + child->PrintStream(out,indent+1,debug); + child=child->GetNextNode(); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set normalisation factors which are used in calls to GetBinFactor(). +/// +/// \param[in] normalisation normalisation factor +/// \param[in] binfactor object which defines a factor for each bin +/// +/// In the present implementation, <b>binfactor</b> can be a TF1 or a +/// TVectorD. The TF1 is evaluated a the bin centers of the +/// relevant axes. The TVectorD is indexed by the global bin number +/// minus the start bin number of this node. + +void TUnfoldBinning::SetBinFactor +(Double_t normalisation,TObject *binfactor) { + fBinFactorConstant=normalisation; + if(fBinFactorFunction) { + if(!dynamic_cast<TF1 *>(fBinFactorFunction)) + delete fBinFactorFunction; + } + fBinFactorFunction=binfactor; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set normalisation factor and function which are used in calls to GetBinFactor(). +/// +/// \param[in] normalisation normalisation factor +/// \param[in] userFunc function evaluated at the (multi-dimensional) +/// bin centres + +void TUnfoldBinning::SetBinFactorFunction +(Double_t normalisation,TF1 *userFunc) { + SetBinFactor(normalisation,userFunc); +} + +/********************* Navigation **********************/ + +//////////////////////////////////////////////////////////////////////////////// +/// Traverse the tree and return the first node which matches the given name. +/// +/// \param[in] name the identifier of the node to find (zero matches +/// the first node) +/// +/// returns the node found or zero + +TUnfoldBinning const *TUnfoldBinning::FindNode(char const *name) const +{ + TUnfoldBinning const *r=0; + if((!name)||(!TString(GetName()).CompareTo(name))) { + r=this; + } + for(TUnfoldBinning const *child=GetChildNode(); + (!r) && child;child=child->GetNextNode()) { + r=child->FindNode(name); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return root node. + +TUnfoldBinning *TUnfoldBinning::GetRootNode(void) +{ + TUnfoldBinning *node=this; + while(node->GetParentNode()) node=node->parentNode; + return node; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return root node. + +TUnfoldBinning const *TUnfoldBinning::GetRootNode(void) const +{ + TUnfoldBinning const *node=this; + while(node->GetParentNode()) node=node->GetParentNode(); + return node; +} + +/********************* Create THxx histograms **********/ + +//////////////////////////////////////////////////////////////////////////////// +/// Construct a title. +/// +/// \param[in] histogramName distribution name +/// \param[in] histogramTitle default title +/// \param[in] axisList array indicating which axis of this node is +/// mapped to which histogram axis +/// +/// if histogramTitle!=0 this title is used. Otherwise, the title is +/// composed as: +/// histogramName;axisname[axisList[0]];axisname[axisList[1]];... + +TString TUnfoldBinning::BuildHistogramTitle +(const char *histogramName,const char *histogramTitle,Int_t const *axisList) + const +{ + TString r; + if(histogramTitle) { + r=histogramTitle; + } else { + r=histogramName; + Int_t iEnd; + for(iEnd=2;iEnd>0;iEnd--) { + if(axisList[iEnd]>=0) break; + } + for(Int_t i=0;i<=iEnd;i++) { + r += ";"; + if(axisList[i]<0) { + r += GetName(); + } else { + r += GetNonemptyNode()->GetDistributionAxisLabel(axisList[i]); + } + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct a histogram title for a 2D histogram with different +/// binning schemes on x and y axis. +/// +/// \param[in] histogramName distribution name +/// \param[in] histogramTitle default title +/// \param[in] xAxis indicates which x-axis name to use +/// \param[in] yAxisBinning binning scheme for y-axis +/// \param[in] yAxis indicates which y-axis name to use +/// +/// build a title +/// - input: +/// histogramTitle : if this is non-zero, use that title +/// - otherwise: +/// title=histogramName;x;y +/// - xAxis : +/// - -1 no title for this axis +/// - >=0 use name of the corresponding axis + +TString TUnfoldBinning::BuildHistogramTitle2D +(const char *histogramName,const char *histogramTitle, + Int_t xAxis,const TUnfoldBinning *yAxisBinning,Int_t yAxis) const +{ + TString r; + if(histogramTitle) { + r=histogramTitle; + } else { + r=histogramName; + r += ";"; + if(xAxis==-1) { + r += GetName(); + } else if(xAxis>=0) { + r += GetNonemptyNode()->GetDistributionAxisLabel(xAxis); + } + r+= ";"; + if(yAxis==-1) { + r += yAxisBinning->GetName(); + } else if(yAxis>=0) { + r += yAxisBinning->GetNonemptyNode()->GetDistributionAxisLabel(yAxis); + } + + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return the number of histogram bins required when storing +/// this binning in a one-dimensional histogram. +/// +/// \param[in] originalAxisBinning if true, try to have the histogram +/// axis reflect precisely the relevant axis of the binning scheme +/// \param[in] axisSteering steering to integrate over axis and/or +/// skip underflow and overflow bins +/// +/// returns the number of bins of the TH1, where the underflow/overflow +/// are not used, unless the distribution has only one axis and +/// originalAxisBinning=true) +/// +/// axisSteering is a string as follows: +/// "axis[options];axis[options];..." +/// where: axis = name or * is an identifier of an axis (* matches all) +/// and: options is any combination of the letters C,U,O (other +/// letters are ignored). +/// +/// The letter C means that the corresponding axis is collapsed into +/// one bin, i.e. one dimension is removed from the counting. +/// The letters U,O remove for the matching axis the underflow.overflow +/// bins from the counting + +Int_t TUnfoldBinning::GetTH1xNumberOfBins +(Bool_t originalAxisBinning,const char *axisSteering) const +{ + Int_t axisBins[3],axisList[3]; + GetTHxxBinning(originalAxisBinning ? 1 : 0,axisBins,axisList, + axisSteering); + return axisBins[0]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create a THxx histogram capable to hold the bins of this binning +/// node and its children. +/// +/// \param[in] histogramName name of the histogram which is created +/// \param[in] originalAxisBinning if true, try to preserve the axis binning +/// \param[out] (default=0) binMap mapping of global bins to histogram bins. +/// if(binMap==0), no binMap is created +/// \param[in] (default=0) histogramTitle title of the histogram. If zero, a title +/// is selected automatically +/// \param[in] (default=0) axisSteering steer the handling of underflow/overflow +/// and projections +/// +/// returns a new histogram (TH1D, TH2D or TH3D) +/// +/// if the parameter <b>originalAxisBinning</b> parameter is true, the +/// resulting histogram has bin widths and histogram dimension (TH1D, +/// TH2D, TH3D) in parallel to this binning node, if possible. +/// +/// The <b>binMap</b> is an array which translates global bin numbers to bin +/// numbers in the histogram returned by this method. The global bin +/// numbers correspond to the bin numbers in a histogram created by +/// calling GetRootNode()->CreateHistogram(name,false,0,0,0) +/// +/// The <b>axisSteering</b> is a string to steer whether underflow and +/// overflow bins are included in the bin map. Furthermore, it is +/// possible to "collapse" axes, such that their content is summed and +/// the axis does not show up in the created histogram. +/// +/// The string looks like this: "axis[options];axis[options];..." where +/// +/// - axis is the name of an axis or equal to *, the latter matches +/// all axes +/// - options is a combination of characters chosen from +/// OUC0123456789 +/// +/// - if O is included, the overflow bin of that axis is discarded +/// - if U is included, the underflow bin of that axis is discarded +/// - if C is included, the bins on that axes are collapsed, +/// i.e. the corresponding histogram axis is not present in the output. +/// The corresponding bin contents are added up +/// (projected onto the remaining axes). Using the characters O and U +/// one can decide to exclude underflow or overflow from the +/// projection. Using a selection of the characters 0123456789 one can +/// restrict the sum further to only include the corresponding +/// bins. In this counting, the first non-underflow bin corresponds to +/// the character 0. This obviously only works for up to ten +/// bins. + +TH1 *TUnfoldBinning::CreateHistogram +(const char *histogramName,Bool_t originalAxisBinning,Int_t **binMap, + const char *histogramTitle,const char *axisSteering) const +{ + Int_t nBin[3],axisList[3]; + Int_t nDim=GetTHxxBinning(originalAxisBinning ? 3 : 0,nBin,axisList, + axisSteering); + const TUnfoldBinning *neNode=GetNonemptyNode(); + TString title=BuildHistogramTitle(histogramName,histogramTitle,axisList); + TH1 *r=0; + if(nDim>0) { + const TVectorD *axisBinsX= + neNode->GetDistributionBinning(axisList[0]); + if(nDim>1) { + const TVectorD *axisBinsY= + neNode->GetDistributionBinning(axisList[1]); + if(nDim>2) { + const TVectorD *axisBinsZ= + neNode->GetDistributionBinning(axisList[2]); + r=new TH3D(histogramName,title, + nBin[0],axisBinsX->GetMatrixArray(), + nBin[1],axisBinsY->GetMatrixArray(), + nBin[2],axisBinsZ->GetMatrixArray()); + } else { + r=new TH2D(histogramName,title, + nBin[0],axisBinsX->GetMatrixArray(), + nBin[1],axisBinsY->GetMatrixArray()); + } + } else { + r=new TH1D(histogramName,title,nBin[0],axisBinsX->GetMatrixArray()); + } + } else { + if(originalAxisBinning) { + Warning("CreateHistogram", + "Original binning can not be represented as THxx"); + } + r=new TH1D(histogramName,title,nBin[0],0.5,nBin[0]+0.5); + nDim=0; + } + if(binMap) { + *binMap=CreateBinMap(r,nDim,axisList,axisSteering); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create a TH2D histogram capable to hold a covariance matrix. +/// +/// +/// \param[in] histogramName name of the histogram which is created +/// \param[in] originalAxisBinning if true, try to preserve the axis binning +/// \param[out] (default=0) binMap mapping of global bins to histogram bins. +/// if(binMap==0), no binMap is created +/// \param[in] (default=0) histogramTitle title of the histogram. If zero, a title +/// is selected automatically +/// \param[in] (default=0) axisSteering steer the handling of underflow/overflow +/// and projections +/// +/// returns a new TH2D. The options are described in greater detail +/// with the CreateHistogram() method. + +TH2D *TUnfoldBinning::CreateErrorMatrixHistogram +(const char *histogramName,Bool_t originalAxisBinning,Int_t **binMap, + const char *histogramTitle,const char *axisSteering) const +{ + Int_t nBin[3],axisList[3]; + Int_t nDim=GetTHxxBinning(originalAxisBinning ? 1 : 0,nBin,axisList, + axisSteering); + TString title=BuildHistogramTitle(histogramName,histogramTitle,axisList); + TH2D *r=0; + if(nDim==1) { + const TVectorD *axisBinsX=(TVectorD const *) + GetNonemptyNode()->fAxisList->At(axisList[0]); + r=new TH2D(histogramName,title,nBin[0],axisBinsX->GetMatrixArray(), + nBin[0],axisBinsX->GetMatrixArray()); + } else { + if(originalAxisBinning) { + Info("CreateErrorMatrixHistogram", + "Original binning can not be represented on one axis"); + } + r=new TH2D(histogramName,title,nBin[0],0.5,nBin[0]+0.5, + nBin[0],0.5,nBin[0]+0.5); + nDim=0; + } + if(binMap) { + *binMap=CreateBinMap(r,nDim,axisList,axisSteering); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create a TH2D histogram capable to hold the bins of the two +/// input binning schemes on the x and y axes, respectively. +/// +/// \paran[in] xAxis binning scheme for the x axis +/// \param[in] yAxis binning scheme for the y axis +/// \param[in] histogramName name of the histogram which is created +/// \param[in] originalXAxisBinning preserve x-axis bin widths if possible +/// \param[in] originalXAxisBinning preserve y-axis bin widths if possible +/// \param[in] histogramTitle if is non-zero, it is taken as histogram title +/// otherwise, the title is created automatically +/// +/// returns a new TH2D. + +TH2D *TUnfoldBinning::CreateHistogramOfMigrations +(TUnfoldBinning const *xAxis,TUnfoldBinning const *yAxis, + char const *histogramName,Bool_t originalXAxisBinning, + Bool_t originalYAxisBinning,char const *histogramTitle) +{ + Int_t nBinX[3],axisListX[3]; + Int_t nDimX= + xAxis->GetTHxxBinning(originalXAxisBinning ? 1 : 0,nBinX,axisListX,0); + const TUnfoldBinning *neNodeX=xAxis->GetNonemptyNode(); + Int_t nBinY[3],axisListY[3]; + Int_t nDimY= + yAxis->GetTHxxBinning(originalYAxisBinning ? 1 : 0,nBinY,axisListY,0); + const TUnfoldBinning *neNodeY=yAxis->GetNonemptyNode(); + TString title=xAxis->BuildHistogramTitle2D + (histogramName,histogramTitle,axisListX[0],yAxis,axisListY[0]); + if(nDimX==1) { + const TVectorD *axisBinsX=(TVectorD const *) + neNodeX->fAxisList->At(axisListX[0]); + if(nDimY==1) { + const TVectorD *axisBinsY=(TVectorD const *) + neNodeY->fAxisList->At(axisListY[0]); + return new TH2D(histogramName,title, + nBinX[0],axisBinsX->GetMatrixArray(), + nBinY[0],axisBinsY->GetMatrixArray()); + } else { + return new TH2D(histogramName,title, + nBinX[0],axisBinsX->GetMatrixArray(), + nBinY[0],0.5,0.5+nBinY[0]); + } + } else { + if(nDimY==1) { + const TVectorD *axisBinsY=(TVectorD const *) + neNodeY->fAxisList->At(axisListY[0]); + return new TH2D(histogramName,title, + nBinX[0],0.5,0.5+nBinX[0], + nBinY[0],axisBinsY->GetMatrixArray()); + } else { + return new TH2D(histogramName,title, + nBinX[0],0.5,0.5+nBinX[0], + nBinY[0],0.5,0.5+nBinY[0]); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate properties of a THxx histogram to store this binning. +/// +/// \param[in] maxDim maximum dimension of the THxx (0 or 1..3) +/// maxDim==0 is used to indicate that the histogram should be +/// dimensional with all bins mapped on one axis, +/// bin centers equal to bin numbers +/// \param[in] axisSteering see method CreateHistogram() +/// \param[out] axisBins[3] number of bins on the THxx axes +/// \param[out] axisList[3] TUnfoldBinning axis number corresponding +/// to the THxx axis +/// +/// returns 1-3 dimension of THxx or 0 for 1-dim THxx with equidistant bins + +Int_t TUnfoldBinning::GetTHxxBinning +(Int_t maxDim,Int_t *axisBins,Int_t *axisList, + const char *axisSteering) const +{ + for(Int_t i=0;i<3;i++) { + axisBins[i]=0; + axisList[i]=-1; + } + const TUnfoldBinning *theNode=GetNonemptyNode(); + if(theNode) { + Int_t r=theNode->GetTHxxBinningSingleNode + (maxDim,axisBins,axisList,axisSteering); + return r; + } else { + axisBins[0]=GetTHxxBinsRecursive(axisSteering); + return 0; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Find a node which has non-empty distributions +/// if there is none or if there are many, return zero. + +const TUnfoldBinning *TUnfoldBinning::GetNonemptyNode(void) const +{ + const TUnfoldBinning *r=GetDistributionNumberOfBins()>0 ? this : 0; + for(TUnfoldBinning const *child=GetChildNode();child; + child=child->GetNextNode()) { + const TUnfoldBinning *c=child->GetNonemptyNode(); + if(!r) { + // new candidate found + r=c; + } else { + if(c) { + // multiple nodes found + r=0; + break; + } + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get the properties of a histogram capable to hold the distribution +/// attached to this node. +/// +/// \param[in] maxDim maximum dimension of the THxx (0 or 1..3) +/// maxDim==0 is used to indicate that the histogram should +/// 1-dimensional with all bins mapped on one axis +/// \param[out] axisBins[3] number of bins on the THxx axes +/// \param[out] axisList[3] TUnfoldBinning axis numbers +/// corresponding to the THxx axis +/// \param[in] axisSteering see method CreateHistogram() +/// and projection +/// +/// returns 1-3 dimension of THxx or use 1-dim THxx, binning structure +/// is not preserved + +Int_t TUnfoldBinning::GetTHxxBinningSingleNode +(Int_t maxDim,Int_t *axisBins,Int_t *axisList,const char *axisSteering) const +{ + // decode axisSteering + // isOptionGiven[0] ('C'): bit vector which axes to collapse + // isOptionGiven[1] ('U'): bit vector to discard underflow bins + // isOptionGiven[2] ('O'): bit vector to discard overflow bins + Int_t isOptionGiven[3]; + DecodeAxisSteering(axisSteering,"CUO",isOptionGiven); + // count number of axes after projecting + Int_t numDimension=GetDistributionDimension(); + Int_t r=0; + for(Int_t i=0;i<numDimension;i++) { + if(isOptionGiven[0] & (1<<i)) continue; + r++; + } + if((r>0)&&(r<=maxDim)) { + // 0<r<=maxDim + // + // -> preserve the original binning + // axisList[] and axisBins[] are overwritten + r=0; + for(Int_t i=0;i<numDimension;i++) { + if(isOptionGiven[0] & (1<<i)) continue; + axisList[r]=i; + axisBins[r]=GetDistributionBinning(i)->GetNrows()-1; + r++; + } + } else { + // map everything on one axis + // axisBins[0] is the number of bins + if(HasUnconnectedBins() || (GetDistributionNumberOfBins()<=0)) { + axisBins[0] = GetDistributionNumberOfBins(); + } else { + Int_t nBin=1; + for(Int_t i=0;i<numDimension;i++) { + Int_t mask=(1<<i); + if(isOptionGiven[0] & mask) continue; + Int_t nBinI=GetDistributionBinning(i)->GetNrows()-1; + if((fHasUnderflow & mask)&& !(isOptionGiven[1] & mask)) nBinI++; + if((fHasOverflow & mask)&& !(isOptionGiven[2] & mask)) nBinI++; + nBin *= nBinI; + } + axisBins[0] = nBin; + } + r=0; + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate number of bins required to store this binning with the +/// given axisSteering. +/// +/// \param[in] axisSteering see method CreateHistogram() +/// +/// returns the number of bins + +Int_t TUnfoldBinning::GetTHxxBinsRecursive(const char *axisSteering) const +{ + + Int_t r=0; + for(TUnfoldBinning const *child=GetChildNode();child; + child=child->GetNextNode()) { + r +=child->GetTHxxBinsRecursive(axisSteering); + } + // here: process distribution of this node + Int_t axisBins[3],axisList[3]; + GetTHxxBinningSingleNode(0,axisBins,axisList,axisSteering); + r += axisBins[0]; + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create an empty bin map, useful together with the getter methods of +/// class TUnfold and TUnfoldSys. +/// +/// returns: a new Int array of the proper size, all elements set to -1 + +Int_t *TUnfoldBinning::CreateEmptyBinMap(void) const { + // create empty bin map which can be manipulated by + // MapGlobalBin() + Int_t nMax=GetRootNode()->GetEndBin()+1; + Int_t *r=new Int_t[nMax]; + for(Int_t i=0;i<nMax;i++) { + r[i]=-1; + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Set one entry in a bin map. +/// +/// \param[out] binMap to be used with TUnfoldSys::GetOutput() etc +/// \param[in] source bin, global bin number in this binning scheme +/// \param[in] destination bin in the output histogram + +void TUnfoldBinning::SetBinMapEntry +(Int_t *binMap,Int_t globalBin,Int_t destBin) const { + Int_t nMax=GetRootNode()->GetEndBin()+1; + if((globalBin<0)||(globalBin>=nMax)) { + Error("SetBinMapEntry","global bin number %d outside range (max=%d)", + globalBin,nMax); + } else { + binMap[globalBin]=destBin; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Map all global bins referenced by this node to the one-dimensional +/// histogram destHist, starting with bin firstBinX +/// +/// \param[out] binMap to be used with TUnfoldSys::GetOutput() etc +/// \param[in] axisSteering steering for underflow/overflow/projections +/// \param[in] firstBinX first bin of destination histogram to be filled +/// +/// returns: highest bin number in destination histogram plus 1 +/// The parameter <b>axisSteering</b> is explained with the +/// method CreateHistogram() + +Int_t TUnfoldBinning::FillBinMap1D +(Int_t *binMap,const char *axisSteering,Int_t firstBinX) const { + Int_t r=firstBinX; + Int_t axisBins[3],axisList[3]; + Int_t nDim=GetTHxxBinningSingleNode(3,axisBins,axisList,axisSteering); + if((nDim==1)|| !GetDistributionDimension()) { + r+=FillBinMapSingleNode(0,r,0,0,axisSteering,binMap); + } else { + Error("FillBinMap1D","distribution %s with steering=%s is not 1D", + (char *)GetName(),axisSteering); + } + for(TUnfoldBinning const *child=GetChildNode();child; + child=child->GetNextNode()) { + r =child->FillBinMap1D(binMap,axisSteering,r); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Create mapping from global bin number to a histogram for this node. +/// +/// \param[in] hist destination histogram +/// \param[in] nDim target dimension +/// \param[in] axisList map axes in the binning scheme to histogram axes +/// \param[in] axisSteering steering for underflow/overflow/projections +/// +/// The <b>axisSteering</b> is explained with the method CreateHistogram() +/// create mapping from global bin number to a histogram for this node +/// global bins are the bins of the root node binning scheme +/// when projecting them on a TH1 histogram "hRootNode" without special +/// axis steering and without attempting to preserve the axis binning +/// +/// The bin map is an array of size hRootNode->GetNbinsX()+2 +/// For each bin of the "hRootNode" histogram it holds the target bin in +/// "hist" or the number -1 if the corresponding "hRootNode" bin is not +/// represented in "hist" +/// +/// input +/// - hist : the histogram (to calculate root bin numbers) +/// - nDim : target dimension of the TUnfoldBinning +/// if(nDim==0) all bins are mapped linearly +/// - axisSteering: +/// "pattern1;pattern2;...;patternN" +/// patternI = axis[mode] +/// axis = name or * +/// mode = C|U|O +/// - C: collapse axis into one bin +/// - U: discard underflow bin +/// - O: discard overflow bin +/// +/// - input used only if nDim>0: +/// axisList : for each THxx axis give the TUnfoldBinning axis number +/// +/// - return value: +/// an new array which holds the bin mapping +/// - r[0] : to which THxx bin to map global bin number 0 +/// - r[1] : to which THxx bin to map global bin number 1 +/// ... +/// - r[nmax] +/// where nmax=GetRootNode()->GetEndBin()+1 + +Int_t *TUnfoldBinning::CreateBinMap +(const TH1 *hist,Int_t nDim,const Int_t *axisList,const char *axisSteering) + const +{ + Int_t *r=CreateEmptyBinMap(); + Int_t startBin=GetRootNode()->GetStartBin(); + if(nDim>0) { + const TUnfoldBinning *nonemptyNode=GetNonemptyNode(); + if(nonemptyNode) { + nonemptyNode-> + FillBinMapSingleNode(hist,startBin,nDim,axisList,axisSteering,r); + } else { + Fatal("CreateBinMap","called with nDim=%d but GetNonemptyNode()=0", + nDim); + } + } else { + FillBinMapRecursive(startBin,axisSteering,r); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Recursively fill bin map. +/// +/// \param[in] startBin first histogram bin +/// \param[in] axisSteering see CreateHistogram() method +/// \param[out] binMap the bin mapping which is to be filled +/// +/// the positions +/// - binMap[GetStartBin()]...binMap[GetEndBin()-1] +/// are filled + +Int_t TUnfoldBinning::FillBinMapRecursive +(Int_t startBin,const char *axisSteering,Int_t *binMap) const +{ + Int_t nbin=0; + nbin = FillBinMapSingleNode(0,startBin,0,0,axisSteering,binMap); + for(TUnfoldBinning const *child=GetChildNode();child; + child=child->GetNextNode()) { + nbin += child->FillBinMapRecursive(startBin+nbin,axisSteering,binMap); + } + return nbin; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill bin map for a single node. +/// +/// \param[in] hist the histogram representing this node (used if nDim>0) +/// \param[in] startBin start bin in the bin map +/// \param[in] nDim number of dimensions to resolve +/// \param[in] axisList[3] TUnfoldBinning axis numbers corresponding +/// to the axes of <b>hist</b> +/// \param[in] axisSteering see documentation of CreateHistogram() +/// \param[out] binMap the bin map to fill +/// +/// returns the number of bins mapped. +/// +/// The result depends on the parameter <b>nDim</b> as follows +/// +/// - nDim==0: bins are mapped in linear order, ignore hist and +/// axisList +/// - nDim==hist->GetDimension(): +/// bins are mapped to "hist" bin numbers +/// the corresponding TUnfoldBinning axes are taken from +/// axisList[] +/// - nDim=1 and hist->GetDimension()>1: +/// bins are mapped to the x-axis of "hist" +/// the corresponding TUnfoldBinning axis is taken from +/// axisList[0] + +Int_t TUnfoldBinning::FillBinMapSingleNode +(const TH1 *hist,Int_t startBin,Int_t nDim,const Int_t *axisList, + const char *axisSteering,Int_t *binMap) const +{ + // first, decode axisSteering + // isOptionGiven[0] ('C'): bit vector which axes to collapse + // isOptionGiven[1] ('U'): bit vector to discard underflow bins + // isOptionGiven[2] ('O'): bit vector to discard overflow bins + Int_t isOptionGiven[3+10]; + DecodeAxisSteering(axisSteering,"CUO0123456789",isOptionGiven); + Int_t haveSelectedBin=0; + for(Int_t i=3;i<3+10;i++) { + haveSelectedBin |= isOptionGiven[i]; + } + + Int_t axisBins[MAXDIM]; + Int_t dimension=GetDistributionDimension(); + Int_t axisNbin[MAXDIM]; + for(Int_t i=0;i<dimension;i++) { + const TVectorD *binning=GetDistributionBinning(i); + axisNbin[i]=binning->GetNrows()-1; + }; + for(Int_t i=0;i<GetDistributionNumberOfBins();i++) { + Int_t globalBin=GetStartBin()+i; + const TUnfoldBinning *dest=ToAxisBins(globalBin,axisBins); + if(dest!=this) { + if(!dest) { + Fatal("FillBinMapSingleNode", + "bin %d outside binning scheme", + globalBin); + } else { + Fatal("FillBinMapSingleNode", + "bin %d located in %s %d-%d rather than %s %d=%d", + i,(const char *)dest->GetName(), + dest->GetStartBin(),dest->GetEndBin(), + (const char *)GetName(),GetStartBin(),GetEndBin()); + } + } + // check whether this bin has to be skipped + Bool_t skip=kFALSE; + for(Int_t axis=0;axis<dimension;axis++) { + Int_t mask=(1<<axis); + // underflow/overflow excluded by steering + if(((axisBins[axis]<0)&&(isOptionGiven[1] & mask))|| + ((axisBins[axis]>=axisNbin[axis])&&(isOptionGiven[2] & mask))) + skip=kTRUE; + // only certain bins selected by steering + if((axisBins[axis]>=0)&&(axisBins[axis]<axisNbin[axis])&& + (haveSelectedBin & mask)) { + if(!(isOptionGiven[3+axisBins[axis]] & mask)) skip=kTRUE; + } + } + if(skip) { + continue; + } + + if(nDim>0) { + // get bin number from THxx function(s) + if(nDim==hist->GetDimension()) { + Int_t ibin[3]; + ibin[0]=ibin[1]=ibin[2]=0; + for(Int_t hdim=0;hdim<nDim;hdim++) { + Int_t axis=axisList[hdim]; + ibin[hdim]=axisBins[axis]+1; + } + binMap[globalBin]=hist->GetBin(ibin[0],ibin[1],ibin[2]); + } else if(nDim==1) { + // histogram has more dimensions than the binning scheme + // and the binning scheme has one axis only + // + // special case: error histogram is 2-d + // create nor error if ndim==1 && hist->GetDimension()==2 + if((nDim!=1)||( hist->GetDimension()!=2)) { + // -> use the first valid axis only + Error("FillBinMapSingleNode","inconsistent dimensions %d %d",nDim, + hist->GetDimension()); + } + for(Int_t ii=0;ii<hist->GetDimension();ii++) { + if(axisList[ii]>=0) { + binMap[globalBin]=axisBins[axisList[ii]]+1; + break; + } + } + } else { + Fatal("FillBinMapSingleNode","inconsistent dimensions %d %d",nDim, + hist->GetDimension()); + } + } else { + // order all bins in sequence + // calculation in parallel to ToGlobalBin() + // but take care of + // startBin,collapseAxis,discardeUnderflow,discardeOverflow + if(dimension>0) { + Int_t r=0; + for(Int_t axis=dimension-1;axis>=0;axis--) { + Int_t mask=(1<<axis); + if(isOptionGiven[0] & mask) { + // bins on this axis are integrated over + continue; + } + Int_t iBin=axisBins[axis]; + Int_t nMax=axisNbin[axis]; + if((fHasUnderflow & ~isOptionGiven[1]) & mask) { + nMax +=1; + iBin +=1; + } + if((fHasOverflow & ~isOptionGiven[2]) & mask) { + nMax += 1; + } + r = r*nMax +iBin; + } + binMap[globalBin] = startBin + r; + } else { + binMap[globalBin] = startBin + axisBins[0]; + } + } + } + Int_t nbin; + if(dimension>0) { + nbin=1; + for(Int_t axis=dimension-1;axis>=0;axis--) { + Int_t mask=(1<<axis); + if(isOptionGiven[0] & mask) { + // bins on this axis are integrated over + continue; + } + Int_t nMax=axisNbin[axis]; + if((fHasUnderflow & ~isOptionGiven[1]) & mask) { + nMax +=1; + } + if((fHasOverflow & ~isOptionGiven[2]) & mask) { + nMax += 1; + } + nbin = nbin*nMax; + } + } else { + nbin=GetDistributionNumberOfBins(); + } + return nbin; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Extract a distribution from the given set of global bins. +/// +/// input: +/// - histogramName : name of the histogram which ic created +/// - globalBins : histogram with all bins +/// - globalBinsEmatrix : corresponding error matrix +/// if this pointer is zero, only diagonal errors +/// are considered +/// - originalAxisBinning : extract histogram with proper binning +/// (if possible) +/// - axisSteering +/// - "pattern1;pattern2;...;patternN" +/// - patternI = axis[mode] +/// - axis = name or * +/// - mode = C|U|O +/// - C: collapse axis into one bin +/// - U: discard underflow bin +/// - O: discard overflow bin + +TH1 *TUnfoldBinning::ExtractHistogram +(const char *histogramName,const TH1 *globalBins, + const TH2 *globalBinsEmatrix,Bool_t originalAxisBinning, + const char *axisSteering) const +{ + Int_t *binMap=0; + TH1 *r=CreateHistogram(histogramName,originalAxisBinning,&binMap,0, + axisSteering); + if(!r) return 0; + TUnfoldBinning const *root=GetRootNode(); + Int_t nMax=-1; + for(Int_t iSrc=root->GetStartBin();iSrc<root->GetEndBin();iSrc++) { + if(binMap[iSrc]>nMax) nMax=binMap[iSrc]; + } + if(nMax<0) { + delete r; + r=0; + } else { + TVectorD eSquared(nMax+1); + for(Int_t iSrc=root->GetStartBin();iSrc<root->GetEndBin();iSrc++) { + Int_t iDest=binMap[iSrc]; + if(iDest>=0) { + Double_t c=r->GetBinContent(iDest); + r->SetBinContent(iDest,c+globalBins->GetBinContent(iSrc)); + if(!globalBinsEmatrix) { + eSquared(iDest)+=TMath::Power(globalBins->GetBinError(iSrc),2.); + } else { + for(Int_t jSrc=root->GetStartBin();jSrc<root->GetEndBin(); + jSrc++) { + if(binMap[jSrc]==iDest) { + eSquared(iDest) += + TMath::Power(globalBins->GetBinError(jSrc),2.); + } + } + } + } + } + for(Int_t i=0;i<nMax;i++) { + Double_t e2=eSquared(i); + if(e2>0.0) { + r->SetBinError(i,TMath::Sqrt(e2)); + } + } + } + delete binMap; + return r; +} + +/********************* Calculate global bin number ******/ + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a one-dimensional distribution. +/// +/// \param[in] x coordinate +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme + +Int_t TUnfoldBinning::GetGlobalBinNumber(Double_t x) const +{ + if(GetDistributionDimension()!=1) { + Fatal("GetBinNumber", + "called with 1 argument for %d dimensional distribution", + GetDistributionDimension()); + } + return GetGlobalBinNumber(&x); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a two-dimensional distribution. +/// +/// \param[in] x coordinate on first axis +/// \param[in] y coordinate on second axis +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme + +Int_t TUnfoldBinning::GetGlobalBinNumber(Double_t x,Double_t y) const +{ + if(GetDistributionDimension()!=2) { + Fatal("GetBinNumber", + "called with 2 arguments for %d dimensional distribution", + GetDistributionDimension()); + } + Double_t xx[2]; + xx[0]=x; + xx[1]=y; + return GetGlobalBinNumber(xx); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a three-dimensional distribution. +/// +/// \param[in] x coordinate on first axis +/// \param[in] y coordinate on second axis +/// \param[in] z coordinate on third axis +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme +/// +/// locate bin on a three-dimensional distribution +/// - input: +/// x,y,z: coordinates to locate + +Int_t TUnfoldBinning::GetGlobalBinNumber +(Double_t x,Double_t y,Double_t z) const +{ + if(GetDistributionDimension()!=3) { + Fatal("GetBinNumber", + "called with 3 arguments for %d dimensional distribution", + GetDistributionDimension()); + } + Double_t xx[3]; + xx[0]=x; + xx[1]=y; + xx[2]=z; + return GetGlobalBinNumber(xx); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a four-dimensional distribution. +/// +/// \param[in] x0 coordinate on first axis +/// \param[in] x1 coordinate on second axis +/// \param[in] x2 coordinate on third axis +/// \param[in] x3 coordinate on fourth axis +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme +/// +/// locate bin on a four-dimensional distribution +/// - input: +/// x0,x1,x2,x3: coordinates to locate + +Int_t TUnfoldBinning::GetGlobalBinNumber +(Double_t x0,Double_t x1,Double_t x2,Double_t x3) const +{ + if(GetDistributionDimension()!=4) { + Fatal("GetBinNumber", + "called with 4 arguments for %d dimensional distribution", + GetDistributionDimension()); + } + Double_t xx[4]; + xx[0]=x0; + xx[1]=x1; + xx[2]=x2; + xx[3]=x3; + return GetGlobalBinNumber(xx); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a five-dimensional distribution. +/// +/// \param[in] x0 coordinate on first axis +/// \param[in] x1 coordinate on second axis +/// \param[in] x2 coordinate on third axis +/// \param[in] x3 coordinate on fourth axis +/// \param[in] x4 coordinate on fifth axis +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme +/// +/// locate bin on a five-dimensional distribution +/// - input: +/// x0,x1,x2,x3,x4: coordinates to locate + +Int_t TUnfoldBinning::GetGlobalBinNumber +(Double_t x0,Double_t x1,Double_t x2,Double_t x3,Double_t x4) const +{ + if(GetDistributionDimension()!=5) { + Fatal("GetBinNumber", + "called with 5 arguments for %d dimensional distribution", + GetDistributionDimension()); + } + Double_t xx[5]; + xx[0]=x0; + xx[1]=x1; + xx[2]=x2; + xx[3]=x3; + xx[4]=x4; + return GetGlobalBinNumber(xx); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Locate a bin in a six-dimensional distribution. +/// +/// \param[in] x0 coordinate on first axis +/// \param[in] x1 coordinate on second axis +/// \param[in] x2 coordinate on third axis +/// \param[in] x3 coordinate on fourth axis +/// \param[in] x4 coordinate on fifth axis +/// \param[in] x5 coordinate on sixth axis +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme +/// +/// locate bin on a five-dimensional distribution +/// - input: +/// x0,x1,x2,x3,x4,x5: coordinates to locate + +Int_t TUnfoldBinning::GetGlobalBinNumber +(Double_t x0,Double_t x1,Double_t x2,Double_t x3,Double_t x4,Double_t x5) const +{ + if(GetDistributionDimension()!=6) { + Fatal("GetBinNumber", + "called with 6 arguments for %d dimensional distribution", + GetDistributionDimension()); + } + Double_t xx[6]; + xx[0]=x0; + xx[1]=x1; + xx[2]=x2; + xx[3]=x3; + xx[4]=x4; + xx[5]=x5; + return GetGlobalBinNumber(xx); +} + +//////////////////////////////////////////////////////////////////////////////// +/// locate a bin in an N-dimensional distribution +/// +/// \param[in] x array of coordinates +/// \param[out] isBelow pointer to an integer (bit vector) to indicate +/// coordinates which do not fit in the binning scheme +/// \param[out] isAbove pointer to an integer (bit vector) to indicate +/// coordinates which do not fit in the binning scheme +/// +/// returns the global bin number within the distribution attached to +/// this node. The global bin number is valid for the root node of the +/// binning scheme. If some coordinates do not fit, zero is returned. +/// The integers pointed to by isBelow and isAbove are set to zero. +/// However, if coordinate i is below +/// the lowest bin border and there is no underflow bin, the bin i is +/// set in (*isBelow). Overflows are handled in a similar manner with +/// (*isAbove). +/// +/// If a coordinate is NaN, the result is undefined for TUnfold +/// Version<17.6. As of version 17.6, NaN is expected to end up in the +/// underflow or by setting the corresponding bit in (*isBelow). +/// +/// locate bin on a n-dimensional distribution +/// - input +/// x[]: coordinates to locate +/// - output: +/// isBelow,isAbove: bit vectors, +/// indicating which cut on which axis failed + +Int_t TUnfoldBinning::GetGlobalBinNumber +(const Double_t *x,Int_t *isBelow,Int_t *isAbove) const +{ + if(!GetDistributionDimension()) { + Fatal("GetBinNumber", + "no axes are defined for node %s", + (char const *)GetName()); + } + Int_t iAxisBins[MAXDIM]; + for(Int_t dim=0;dim<GetDistributionDimension();dim++) { + TVectorD const *bins=(TVectorD const *) fAxisList->At(dim); + Int_t i0=0; + Int_t i1=bins->GetNrows()-1; + Int_t iBin= 0; + if(!(x[dim]>=(*bins)[i0])) { + // underflow or NaN + iBin += i0-1; + } else if(!(x[dim]<(*bins)[i1])) { + // overflow + iBin += i1; + } else { + while(i1-i0>1) { + Int_t i2=(i0+i1)/2; + if(x[dim]<(*bins)[i2]) { + i1=i2; + } else { + i0=i2; + } + } + iBin += i0; + } + iAxisBins[dim]=iBin; + } + Int_t r=ToGlobalBin(iAxisBins,isBelow,isAbove); + if(r<0) r=0; + return r; +} + +/********************* access by global bin number ******/ + +//////////////////////////////////////////////////////////////////////////////// +/// Get the name of a bin. +/// +/// \param[in] iBin global bin number +/// +/// returns a string describing the bin +/// +/// Get the name of a bin in the given tree +/// iBin: bin number + +TString TUnfoldBinning::GetBinName(Int_t iBin) const +{ + Int_t axisBins[MAXDIM]; + TString r=TString::Format("#%d",iBin); + TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); + if(distribution) { + r +=" ("; + r += distribution->GetName(); + Int_t dimension=distribution->GetDistributionDimension(); + if(dimension>0) { + TString axisString; + for(Int_t axis=0;axis<dimension;axis++) { + TString thisAxisString= + distribution->GetDistributionAxisLabel(axis); + TVectorD const *bins=distribution->GetDistributionBinning(axis); + Int_t i=axisBins[axis]; + if(i<0) thisAxisString += "[ufl]"; + else if(i>=bins->GetNrows()-1) thisAxisString += "[ofl]"; + else { + thisAxisString += + TString::Format("[%.3g,%.3g]",(*bins)[i],(*bins)[i+1]); + } + axisString = ":"+thisAxisString+axisString; + } + r += axisString; + } else { + // extra bins + Int_t i=axisBins[0]; + if((i>=0)&&(i<distribution->fAxisLabelList->GetEntriesFast())) { + r += distribution->GetDistributionAxisLabel(i); + } else { + r += TString::Format(" %d",i); + } + } + r +=")"; + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get N-dimensional bin size. +/// +/// \param[in] iBin global bin number +/// +/// includeUO : include underflow/overflow bins or not + +Double_t TUnfoldBinning::GetBinSize(Int_t iBin) const +{ + Int_t axisBins[MAXDIM]; + TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); + Double_t r=0.0; + if(distribution) { + if(distribution->GetDistributionDimension()>0) r=1.0; + for(Int_t axis=0;axis<distribution->GetDistributionDimension();axis++) { + TVectorD const *bins=distribution->GetDistributionBinning(axis); + Int_t pos=axisBins[axis]; + if(pos<0) { + r *= distribution->GetDistributionUnderflowBinWidth(axis); + } else if(pos>=bins->GetNrows()-1) { + r *= distribution->GetDistributionOverflowBinWidth(axis); + } else { + r *= (*bins)(pos+1)-(*bins)(pos); + } + if(r<=0.) break; + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check whether there is only a global scaling factor for this node. + +Bool_t TUnfoldBinning::IsBinFactorGlobal(void) const { + return fBinFactorFunction ? kFALSE : kTRUE; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return global scaling factor for this node. + +Double_t TUnfoldBinning::GetGlobalFactor(void) const { + return fBinFactorConstant; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return scaling factor for the given global bin number. +/// +/// \param[in] iBin global bin number +/// +/// returns the scaling factor for this bin. +/// The scaling factors can be set using the method SetBinFactorFunction() +/// +/// return user factor for a bin +/// iBin : global bin number + +Double_t TUnfoldBinning::GetBinFactor(Int_t iBin) const +{ + Int_t axisBins[MAXDIM]; + TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); + Double_t r=distribution->fBinFactorConstant; + if((r!=0.0) && distribution->fBinFactorFunction) { + TF1 *function=dynamic_cast<TF1 *>(distribution->fBinFactorFunction); + if(function) { + Double_t x[MAXDIM]; + Int_t dimension=distribution->GetDistributionDimension(); + if(dimension>0) { + for(Int_t axis=0;axis<dimension;axis++) { + x[axis]=distribution->GetDistributionBinCenter + (axis,axisBins[axis]); + } + r *= function->EvalPar(x,function->GetParameters()); + } else { + x[0]=axisBins[0]; + r *= function->Eval(x[0]); + } + } else { + TVectorD *vect=dynamic_cast<TVectorD *> + (distribution->fBinFactorFunction); + if(vect) { + r=(*vect)[iBin-GetStartBin()]; + } else { + Error("GetBinFactor", + "internal error: user function is neither TF1 or TVectorD"); + } + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get neighbour bins along the specified axis. +/// +/// \param[in] bin global bin number +/// \param[in] axis axis number of interest +/// \param[out] prev bin number of previous bin or -1 if not existing +/// \param[out] distPrev distance between bin centres +/// \param[out] next bin number of next bin or -1 if not existing +/// \param[out] distNext distance between bin centres +/// \param[in] isPeriodic (default=false) if true, the first bin is counted as neighbour of the last bin +/// +/// return code +/// +/// - 0 everything is fine +/// - 1,2,3 isPeriodic option was reset to false, because underflow/overflow +/// bins are present +/// +/// - +1 invalid isPeriodic option was specified with underflow bin +/// - +2 invalid isPeriodic option was specified with overflow bin + +Int_t TUnfoldBinning::GetBinNeighbours +(Int_t bin,Int_t axis,Int_t *prev,Double_t *distPrev, + Int_t *next,Double_t *distNext,Bool_t isPeriodic) const +{ + Int_t axisBins[MAXDIM]; + TUnfoldBinning const *distribution=ToAxisBins(bin,axisBins); + Int_t dimension=distribution->GetDistributionDimension(); + *prev=-1; + *next=-1; + *distPrev=0.; + *distNext=0.; + Int_t r=0; + if((axis>=0)&&(axis<dimension)) { + //TVectorD const *bins=distribution->GetDistributionBinning(axis); + //Int_t nBin=bins->GetNrows()-1; + Int_t nMax=GetDistributionBinning(axis)->GetNrows()-1; + Int_t centerBin= axisBins[axis]; + axisBins[axis] =centerBin-1; + if(isPeriodic) { + if(HasUnderflow(axis)) { + r +=1; + } else if((axisBins[axis]<0)&&(nMax>=3)) { + axisBins[axis]=nMax-1; + } + } + *prev=ToGlobalBin(axisBins); + if(*prev>=0) { + *distPrev=distribution->GetDistributionBinCenter(axis,axisBins[axis])- + distribution->GetDistributionBinCenter(axis,centerBin); + } + axisBins[axis] =centerBin+1; + if(isPeriodic) { + if(HasOverflow(axis)) { + r +=2; + } else if((axisBins[axis]==nMax)&&(nMax>=3)) { + axisBins[axis]=0; + } + } + *next=ToGlobalBin(axisBins); + if(*next>=0) { + *distNext=distribution->GetDistributionBinCenter(axis,axisBins[axis])- + distribution->GetDistributionBinCenter(axis,centerBin); + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return bit maps indicating underflow and overflow status. +/// +/// \param[in] iBin global bin number +/// \param[out] uStatus bit map indicating whether the bin is underflow +/// \param[out] oStatus bit map indicating whether the bin is overflow + +void TUnfoldBinning::GetBinUnderflowOverflowStatus +(Int_t iBin,Int_t *uStatus,Int_t *oStatus) const +{ + Int_t axisBins[MAXDIM]; + TUnfoldBinning const *distribution=ToAxisBins(iBin,axisBins); + Int_t dimension=distribution->GetDistributionDimension(); + *uStatus=0; + *oStatus=0; + for(Int_t axis=0;axis<dimension;axis++) { + TVectorD const *bins=distribution->GetDistributionBinning(axis); + Int_t nBin=bins->GetNrows()-1; + if(axisBins[axis]<0) *uStatus |= (1<<axis); + if(axisBins[axis]>=nBin) *oStatus |= (1<<axis); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check whether there are bins but no axis. + +Bool_t TUnfoldBinning::HasUnconnectedBins(void) const +{ + return (!GetDistributionDimension())&&(GetDistributionNumberOfBins()>0); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return the bin names of unconnected bins. +/// +/// \param[in] bin local bin number + +const TObjString *TUnfoldBinning::GetUnconnectedBinName(Int_t bin) const { + TObjString *r=0; + if(HasUnconnectedBins()) { + if(bin<fAxisLabelList->GetEntriesFast()) { + r=((TObjString * const)fAxisLabelList->At(bin)); + } + } + return r; +} + + +//////////////////////////////////////////////////////////////////////////////// +/// Get average bin size on the specified axis. +/// +/// \param[in] axis axis number +/// \param[in] includeUnderflow whether to include the underflow bin +/// \param[in] includeOverflow whether to include the overflow bin + +Double_t TUnfoldBinning::GetDistributionAverageBinSize +(Int_t axis,Bool_t includeUnderflow,Bool_t includeOverflow) const +{ + Double_t r=0.0; + if((axis>=0)&&(axis<GetDistributionDimension())) { + TVectorD const *bins=GetDistributionBinning(axis); + Double_t d=(*bins)[bins->GetNrows()-1]-(*bins)[0]; + Double_t nBins=bins->GetNrows()-1; + if(includeUnderflow && HasUnderflow(axis)) { + Double_t w=GetDistributionUnderflowBinWidth(axis); + if(w>0) { + nBins++; + d += w; + } + } + if(includeOverflow && HasOverflow(axis)) { + Double_t w=GetDistributionOverflowBinWidth(axis); + if(w>0.0) { + nBins++; + d += w; + } + } + if(nBins>0) { + r=d/nBins; + } + } else { + Error("GetDistributionAverageBinSize","axis %d does not exist",axis); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return bin width assigned to the underflow bin. +/// +/// \param[in] axis axis number +/// +/// the bin width of the first bin is returned. +/// The method is virtual, so this behaviour can be adjusted. +/// +/// return width of the underflow bin +/// axis: axis number + +Double_t TUnfoldBinning::GetDistributionUnderflowBinWidth(Int_t axis) const +{ + TVectorD const *bins=GetDistributionBinning(axis); + return (*bins)[1]-(*bins)[0]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return bin width assigned to the overflow bin. +/// +/// \param[in] axis axis number +/// +/// the bin width of the last bin is returned. +/// The method is virtual, so this behaviour can be adjusted. +/// +/// return width of the underflow bin +/// axis: axis number + +Double_t TUnfoldBinning::GetDistributionOverflowBinWidth(Int_t axis) const +{ + TVectorD const *bins=GetDistributionBinning(axis); + return (*bins)[bins->GetNrows()-1]-(*bins)[bins->GetNrows()-2]; +} + +//////////////////////////////////////////////////////////////////////////////// +/// return bin center for a given axis and bin number +/// +/// \param[in] axis axis number +/// \param[in] bin local bin number on the specified axis +/// +/// returns the geometrical bin center. +/// for underflow and overflow, the calculation is using the +/// GetDistributionUnderflowBinWidth() and +/// GetDistributionOverflowBinWidth() methods. +/// +/// position of the bin center +/// - input: +/// - axis : axis number +/// - bin : bin number on the axis + +Double_t TUnfoldBinning::GetDistributionBinCenter +(Int_t axis,Int_t bin) const +{ + TVectorD const *bins=GetDistributionBinning(axis); + Double_t r=0.0; + if(bin<0) { + // underflow bin + r=(*bins)[0]-0.5*GetDistributionUnderflowBinWidth(axis); + } else if(bin>=bins->GetNrows()-1) { + // overflow bin + r=(*bins)[bins->GetNrows()-1]+0.5*GetDistributionOverflowBinWidth(axis); + } else { + r=0.5*((*bins)[bin+1]+(*bins)[bin]); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get global bin number, given axis bin numbers. +/// +/// \param[in] axisBins[] bin numbers on each axis +/// \param[out] isBelow indicates bins are in underflow but there is +/// no undeflow bin +/// \param[out] isAbove indicates bins are in overflow but there is +/// no overflow bin +/// +/// return: global bin number or -1 if not matched. + +Int_t TUnfoldBinning::ToGlobalBin +(Int_t const *axisBins,Int_t *isBelow,Int_t *isAbove) const +{ + Int_t dimension=GetDistributionDimension(); + Int_t r=0; + if(isBelow) *isBelow=0; + if(isAbove) *isAbove=0; + if(dimension>0) { + for(Int_t axis=dimension-1;axis>=0;axis--) { + Int_t nMax=GetDistributionBinning(axis)->GetNrows()-1; + Int_t i=axisBins[axis]; + if(HasUnderflow(axis)) { + nMax +=1; + i +=1; + } + if(HasOverflow(axis)) nMax +=1; + if((i>=0)&&(i<nMax)) { + if(r>=0) r = r*nMax +i; + } else { + r=-1; + if((i<0)&&(isBelow)) *isBelow |= 1<<axis; + if((i>=nMax)&&(isAbove)) *isAbove |= 1<<axis; + } + } + if(r>=0) { + r += GetStartBin(); + } + } else { + if((axisBins[0]>=0)&&(axisBins[0]<GetDistributionNumberOfBins())) + r=GetStartBin()+axisBins[0]; + else + Fatal("ToGlobalBin","bad input %d for dimensionless binning %s %d", + axisBins[0],(const char *)GetName(), + GetDistributionNumberOfBins()); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Return distribution in which the bin is located +/// and bin numbers on the corresponding axes. +/// +/// \param[in] globalBin global bin number +/// \param[out] local bin numbers of the distribution's axes +/// +/// returns the distribution in which the globalBin is located +/// or 0 if the globalBin is outside this node and its children + +TUnfoldBinning const *TUnfoldBinning::ToAxisBins +(Int_t globalBin,Int_t *axisBins) const +{ + TUnfoldBinning const *r=0; + if((globalBin>=GetStartBin())&&(globalBin<GetEndBin())) { + TUnfoldBinning const *node; + for(node=GetChildNode();node && !r; node=node->GetNextNode()) { + r=node->ToAxisBins(globalBin,axisBins); + } + if(!r) { + r=this; + Int_t i=globalBin-GetStartBin(); + Int_t dimension=GetDistributionDimension(); + if(dimension>0) { + for(int axis=0;axis<dimension;axis++) { + Int_t nMax=GetDistributionBinning(axis)->GetNrows()-1; + axisBins[axis]=0; + if(HasUnderflow(axis)) { + axisBins[axis] =-1; + nMax += 1; + } + if(HasOverflow(axis)) nMax +=1; + axisBins[axis] += i % nMax; + i /= nMax; + } + } else { + axisBins[0]=i; + } + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Decode axis steering. +/// +/// \param[in] axisSteering the steering to decode +/// \param[in] options the allowed options to extract +/// \param[out] isOptionGiven array of decoded steering options, +/// the dimension equal to the number of +/// characters in <b>options</b> +/// +/// the axis steering is given in the form +/// "axis[option];axis[option];..." +/// axis : the name of the axis for which the optionlist is relevant +/// the character * matches all axes +/// option : a list of characters taken from <b>options</b> +/// for each match the corresponding bit number corresponding to +/// the axis number is set in +/// <b>isOptionGiven</b>[i], where i is the position of the matching option +/// character in <b>options</b> + +void TUnfoldBinning::DecodeAxisSteering +(const char *axisSteering,const char *options,Int_t *isOptionGiven) const +{ + Int_t nOpt=TString(options).Length(); + for(Int_t i=0;i<nOpt;i++) isOptionGiven[i]=0; + if(axisSteering) { + TObjArray *patterns=TString(axisSteering).Tokenize(";"); + Int_t nPattern=patterns->GetEntries(); + Int_t nAxis=fAxisLabelList->GetEntries(); + for(Int_t i=0;i<nPattern;i++) { + TString const &pattern=((TObjString * const)patterns->At(i)) + ->GetString(); + Int_t bracketBegin=pattern.Last('['); + Int_t len=pattern.Length(); + if((bracketBegin>0)&&(pattern[len-1]==']')) { + TString axisId=pattern(0,bracketBegin); + Int_t mask=0; + if((axisId[0]=='*')&&(axisId.Length()==1)) { + // turn all bins on + mask=(1<<nAxis)-1; + } else { + // if axis is there, turn its bit on + for(Int_t j=0;j<nAxis;j++) { + if(!axisId.CompareTo(GetDistributionAxisLabel(j))) { + mask|= (1<<j); + } + } + } + for(Int_t o=0;o<nOpt;o++) { + if(pattern.Last(options[o])>bracketBegin) { + isOptionGiven[o] |= mask; + } + } + } else { + Error("DecodeAxisSteering", + "steering \"%s\" does not end with [options]", + (const char *)pattern); + } + } + } +} + diff --git a/hist/unfold/src/TUnfoldBinningXML.cxx b/hist/unfold/src/TUnfoldBinningXML.cxx new file mode 100644 index 0000000000000000000000000000000000000000..7225f10f4115bae624ee92d7d58db7dad709d9bd --- /dev/null +++ b/hist/unfold/src/TUnfoldBinningXML.cxx @@ -0,0 +1,591 @@ +// @(#)root/unfold:$Id$ +// Author: Stefan Schmitt DESY, 10/08/11 + +/** \class TUnfoldBinningXML +\ingroup Unfold +XML interfate to binning schemes, for use with the unfolding algorithm +TUnfoldDensity. + +Binning schemes are used to map analysis bins on a single histogram +axis and back. The analysis bins may include unconnected bins (e.g +nuisances for background normalisation) or various multidimensional +histograms (signal bins, differential background normalisation bins, etc). + +If you use this software, please consider the following citation + +<b>S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201]</b> + +Detailed documentation and updates are available on +http://www.desy.de/~sschmitt + +Please consult the documentation of the class TUnfoldBinning about how to use +binning schemes. This class provides methods to read and write binning +schemes in the XML language. There is also a method which writes out +a dtd file for validation. + +### Example XML code +The example below encodes two binning schemes, _detector_ and +_generator_. The detector scheme consists of a single, +three-dimensional distribution (pt,eta,discriminator). The generator +scheme consists of two two-dimensional distributions, signal and background. + +~~~ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!DOCTYPE TUnfoldBinning SYSTEM "tunfoldbinning.dtd"> +<TUnfoldBinning> +<BinningNode name="detector" firstbin="1" factor="1"> + <BinningNode name="detectordistribution" firstbin="1" factor="1"> + <Axis name="pt" lowEdge="3.5"> + <Bin repeat="3" width="0.5" /> + <Bin repeat="3" width="1" /> + <Bin width="2" /> + <Bin width="3" /> + <Bin location="overflow"/> + <Axis name="eta" lowEdge="-3"> + <Bin repeat="2" width="0.5" /> + <Bin width="1" /> + <Bin repeat="4" width="0.5" /> + <Bin width="1" /> + <Bin repeat="2" width="0.5" /> + <Axis name="discriminator" lowEdge="0"> + <Bin width="0.15" /> + <Bin repeat="2" width="0.35" /> + <Bin width="0.15" /> + </Axis> + </Axis> + </Axis> + </BinningNode> +</BinningNode> +<BinningNode name="generator" firstbin="1" factor="1"> + <BinningNode name="signal" firstbin="1" factor="1"> + <Axis name="ptgen" lowEdge="4"> + <Bin location="underflow" /> + <Bin width="1" /> + <Bin width="2" /> + <Bin width="3" /> + <Bin location="overflow" /> + <Axis name="etagen" lowEdge="-2"> + <Bin location="underflow" /> + <Bin width="1.5" /> + <Bin width="1" /> + <Bin width="1.5" /> + <Bin location="overflow" /> + </Axis> + </Axis> + </BinningNode> + <BinningNode name="background" firstbin="26" factor="1"> + <Axis name="ptrec" lowEdge="3.5"> + <Bin repeat="3" width="0.5" /> + <Bin repeat="3" width="1" /> + <Bin width="2" /> + <Bin width="3" /> + <Bin location="overflow" /> + <Axis name="etarec" lowEdge="-3"> + <Bin repeat="2" width="0.5" /> + <Bin width="1" /> + <Bin repeat="4" width="0.5" /> + <Bin width="1" /> + <Bin repeat="2" width="0.5" /> + </Axis> + </Axis> + </BinningNode> +</BinningNode> +</TUnfoldBinning> +~~~ + +-------------------------------------------------------------------------------- + This file is part of TUnfold. + + TUnfold 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 3 of the License, or + (at your option) any later version. + + TUnfold 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 TUnfold. If not, see <http://www.gnu.org/licenses/>. + +<b>Version 17.6, with updated doxygen comments</b> + +#### History: + - Version 17.5, in parallel to changes in TUnfold + - Version 17.4, in parallel to changes in TUnfoldBinning + - Version 17.3, support for the "repeat" attribute for element Bin + - Version 17.2, initial version, numbered in parallel to TUnfold + */ + + +#include "TUnfold.h" +#include "TUnfoldBinningXML.h" + +#include <TXMLDocument.h> +#include <TXMLNode.h> +#include <TXMLAttr.h> +#include <TList.h> +#include <TVectorD.h> + +#include <fstream> +#include <sstream> + +// #define DEBUG + +using namespace std; + +ClassImp(TUnfoldBinningXML) + +/********************* XML **********************/ + +//////////////////////////////////////////////////////////////////////////////// +/// Write dtd file. +/// +/// \param[out] out stream for writing the dtd + +void TUnfoldBinningXML::WriteDTD(std::ostream &out) { + out + <<"<!-- TUnfold Version "<<TUnfold::GetTUnfoldVersion()<<" -->\n" + <<"<!ELEMENT TUnfoldBinning (BinningNode)+ >\n" + <<"<!ELEMENT BinningNode (BinningNode+|(Binfactorlist?,Axis)|Bins) >\n" + <<"<!ATTLIST BinningNode name ID #REQUIRED firstbin CDATA \"-1\"\n" + <<" factor CDATA \"1.\">\n" + <<"<!ELEMENT Axis ((Bin+,Axis?)|(Axis)) >\n" + <<"<!ATTLIST Axis name CDATA #REQUIRED lowEdge CDATA #REQUIRED>\n" + <<"<!ELEMENT Binfactorlist (#PCDATA)>\n" + <<"<!ATTLIST Binfactorlist length CDATA #REQUIRED>\n" + <<"<!ELEMENT Bin EMPTY>\n" + <<"<!ATTLIST Bin width CDATA #REQUIRED location CDATA #IMPLIED\n" + <<" center CDATA #IMPLIED repeat CDATA #IMPLIED>\n" + <<"<!ELEMENT Bins (BinLabel)* >\n" + <<"<!ATTLIST Bins nbin CDATA #REQUIRED>\n" + <<"<!ELEMENT BinLabel EMPTY>\n" + <<"<!ATTLIST BinLabel index CDATA #REQUIRED name CDATA #REQUIRED>\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Write dtd file. +/// +/// \param[in] file regular file for writing the dtd + +void TUnfoldBinningXML::WriteDTD(const char *file) { + ofstream out(file); + WriteDTD(out); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Import a binning scheme from an XML file. +/// +/// \param[in] document XMP document tree +/// \param[in] name identifier of the binning scheme +/// +/// returns a new TUnfoldBinningXML, if <b>name</b> is found in <b>document</b> +/// +/// import binning scheme from a XML document +/// - document: the XML document +/// - name: the name of the binning scheme to import +/// if name==0, the first binning scheme found in the tree is imported + +TUnfoldBinningXML *TUnfoldBinningXML::ImportXML +(const TXMLDocument *document,const char *name) { + TUnfoldBinningXML *r=0; + TXMLNode *root=document->GetRootNode(); + TXMLNode *binningNode=0; + if(root && (!TString(root->GetNodeName()).CompareTo("TUnfoldBinning")) && + (root->GetNodeType()==TXMLNode::kXMLElementNode)) { + // loop over all "BinningNode" entities + for(TXMLNode *node=root->GetChildren();node && !binningNode; + node=node->GetNextNode()) { + if(node->GetNodeType()==TXMLNode::kXMLElementNode && + !TString(node->GetNodeName()).CompareTo("BinningNode") && + node->GetAttributes()) { + // localize the BinningNode with the given name + TIterator *i=node->GetAttributes()->MakeIterator(); + TXMLAttr *attr; + while((attr=(TXMLAttr *)i->Next())) { + if((!TString(attr->GetName()).CompareTo("name")) && + ((!TString(attr->GetValue()).CompareTo(name)) || + !name)) { + binningNode=node; + } + } + } + } + } + + if(binningNode) { + r=ImportXMLNode(binningNode); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Recursively import one node from the XML tree. +/// +/// \param[in] node node in the XML document tree +/// +/// returns a new TUnfoldBinningXML +/// +/// import data from a given "BinningNode" + +TUnfoldBinningXML *TUnfoldBinningXML::ImportXMLNode +(TXMLNode *node) { + const char *name=0; + Double_t factor=1.0; + TUnfoldBinningXML *r=0; + Int_t nBins=0; + const char *binNames=0; + TIterator *i=node->GetAttributes()->MakeIterator(); + TXMLAttr *attr; + // extract name and global factor + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("name")) { + name=attr->GetValue(); + } + if(!attName.CompareTo("factor")) { + factor=TString(attr->GetValue()).Atof(); + } + } + if(name) { + TString binNameList=""; + // loop over all children of this BinningNode + for(TXMLNode *child=node->GetChildren();child; + child=child->GetNextNode()) { + // unconnected bins: children are of type "Bins" + if(child->GetNodeType()==TXMLNode::kXMLElementNode && + !TString(child->GetNodeName()).CompareTo("Bins")) { + // this node has unconnected bins, no axes + // extract number of bins + if(child->GetAttributes()) { + i=child->GetAttributes()->MakeIterator(); + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("nbin")) { + // number of unconnected bins + nBins=TString(attr->GetValue()).Atoi(); + } + } + } + // extract names of unconnected bins + TObjArray theBinNames; + for(TXMLNode *binName=child->GetChildren();binName; + binName=binName->GetNextNode()) { + if(binName->GetNodeType()==TXMLNode::kXMLElementNode && + !TString(binName->GetNodeName()).CompareTo("BinLabel")) { + i=binName->GetAttributes()->MakeIterator(); + const char *binLabelName=0; + Int_t index=0; + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("index")) { + index=TString(attr->GetValue()).Atoi(); + } + if(!attName.CompareTo("name")) { + binLabelName=attr->GetValue(); + } + } + if((index>=0)&&(binLabelName)) { + if(index>=theBinNames.GetEntriesFast()) { + theBinNames.AddAtAndExpand + (new TObjString(binLabelName),index); + } + } + } + } + Int_t emptyName=0; + for(Int_t ii=0;ii<theBinNames.GetEntriesFast()&&(ii<nBins);ii++) { + if(theBinNames.At(ii)) { + for(Int_t k=0;k<emptyName;k++) binNameList+=";"; + emptyName=0; + binNameList+= + ((TObjString *)theBinNames.At(ii))->GetString(); + } + emptyName++; + } + if(binNameList.Length()>0) { + binNames=binNameList; + } + } + } + r=new TUnfoldBinningXML(name,nBins,binNames); + + // add add axis information + r->AddAxisXML(node); + + // import per-bin normalisation factors if there are any + TVectorD *perBinFactors=0; + for(TXMLNode *child=node->GetChildren();child; + child=child->GetNextNode()) { + // unconnected bins: children are of type "Bins" + if(child->GetNodeType()==TXMLNode::kXMLElementNode && + !TString(child->GetNodeName()).CompareTo("Binfactorlist")) { + int length=0; + i=child->GetAttributes()->MakeIterator(); + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("length")) { + length=TString(attr->GetValue()).Atoi(); + } + } + int nread=0; + if(length==r->GetDistributionNumberOfBins()) { + perBinFactors=new TVectorD(length); + const char *text=child->GetText(); + if(text) { + stringstream readFactors(text); + for(;nread<length;nread++) { + readFactors>> (*perBinFactors)(nread); + if(readFactors.fail()) break; + } + } + } + if(!perBinFactors) { + child->Error("ImportXMLNode","while reading per-bin factors" + " node=%s length=%d (expected %d)",r->GetName(), + length,r->GetDistributionNumberOfBins()); + } else if(nread!=length) { + child->Error("ImportXMLNode","while reading per-bin factors" + " TUnfoldBinning=%s expected %d found %d", + r->GetName(),length,nread); + delete perBinFactors; + perBinFactors=0; + } + } + } + + // set normalisation factors + r->SetBinFactor(factor,perBinFactors); + + // now: loop over all child binning and add them + for(TXMLNode *child=node->GetChildren();child; + child=child->GetNextNode()) { + if(child->GetNodeType()==TXMLNode::kXMLElementNode && + !TString(child->GetNodeName()).CompareTo("BinningNode") && + child->GetAttributes()) { + TUnfoldBinning *childBinning=ImportXMLNode(child); + r->AddBinning(childBinning); + } + } + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Import axis from XML node. +/// +/// \param[in] node node in the XML document tree +/// +/// find axis if there is one + +void TUnfoldBinningXML::AddAxisXML(TXMLNode *node) { + TXMLNode *axis=0; + for(TXMLNode *child=node->GetChildren();child; + child=child->GetNextNode()) { + if(child->GetNodeType()==TXMLNode::kXMLElementNode) { + TString nodeName(child->GetNodeName()); + if(!nodeName.CompareTo("Axis")) axis=child; + } + } + if(axis) { + const char *axisName=0; + TArrayD binEdges(1); + TIterator *i=axis->GetAttributes()->MakeIterator(); + TXMLAttr *attr; + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("name")) { + axisName=attr->GetValue(); + } + if(!attName.CompareTo("lowEdge")) { + binEdges[0]=TString(attr->GetValue()).Atof(); + } + } + Bool_t hasMoreAxes=kFALSE; + Bool_t underflow=kFALSE,overflow=kFALSE; + for(TXMLNode *child=axis->GetChildren();child; + child=child->GetNextNode()) { + if(child->GetNodeType()==TXMLNode::kXMLElementNode) { + TString nodeName(child->GetNodeName()); + if(!nodeName.CompareTo("Axis")) hasMoreAxes=kTRUE; + if(!nodeName.CompareTo("Bin")) { + Bool_t isUnderflow=kFALSE,isOverflow=kFALSE; + Int_t repeat=1; + i=child->GetAttributes()->MakeIterator(); + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + TString attText(attr->GetValue()); + if(!attName.CompareTo("location")) { + isUnderflow= !attText.CompareTo("underflow"); + isOverflow= !attText.CompareTo("overflow"); + } + if(!attName.CompareTo("repeat")) { + repeat=attText.Atof(); + } + } + if(repeat<1) { + node->Warning("AddAxisXML", + "attribute repeat=%d changed to repeat=1", + repeat); + repeat=1; + } + if((isUnderflow || isOverflow)&&(repeat!=1)) { + node->Error("AddAxisXML", + "underflow/overflow can not have repeat!=1 attribute"); + } + if(isUnderflow || isOverflow) { + underflow |= isUnderflow; + overflow |= isOverflow; + } else { + Int_t iBin0=binEdges.GetSize(); + Int_t iBin1=iBin0+repeat; + Double_t binWidth=0.0; + binEdges.Set(iBin1); + i=child->GetAttributes()->MakeIterator(); + while((attr=(TXMLAttr *)i->Next())) { + TString attName(attr->GetName()); + if(!attName.CompareTo("width")) { + binWidth=TString(attr->GetValue()).Atof(); + } + } + if(binWidth<=0.0) { + node->Error("AddAxisXML", + "bin width can not be smaller than zero"); + } + for(int iBin=iBin0;iBin<iBin1;iBin++) { + binEdges[iBin]=binEdges[iBin0-1]+(iBin-iBin0+1)*binWidth; + } + } + } + } + } + AddAxis(axisName,binEdges.GetSize()-1,binEdges.GetArray(), + underflow,overflow); + if(hasMoreAxes) { + AddAxisXML(axis); + } + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Export a binning scheme to a stream in XML format. +/// +/// \param[in] binning the binning scheme to export +/// \param[out] stream to write to +/// \param[in] writeHeader set true when writing the first binning +/// scheme to this stream +/// \param[in] writeFooter set true when writing the last binning +/// scheme to this stream +/// \param[in] indent indentation of the XML output +/// +/// returns true if the writing succeeded + +Int_t TUnfoldBinningXML::ExportXML +(const TUnfoldBinning &binning,std::ostream &out,Bool_t writeHeader, + Bool_t writeFooter,Int_t indent) { + if(writeHeader) { + out<<"<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n" + <<"<!DOCTYPE TUnfoldBinning SYSTEM \"tunfoldbinning.dtd\">\n" + <<"<TUnfoldBinning>\n"; + } + TString trailer(' ',indent); + out<<trailer<<"<BinningNode name=\""<<binning.GetName()<<"\" firstbin=\"" + <<binning.GetStartBin(); + if(binning.IsBinFactorGlobal()) { + out<<"\" factor=\""<<binning.GetGlobalFactor()<<"\">\n"; + } else { + out<<"\">\n"; + out<<trailer<<" <Binfactorlist length=\"" + <<binning.GetDistributionNumberOfBins()<<"\">\n"; + for(int i=0;i<binning.GetDistributionNumberOfBins();i++) { + if(!(i % 10)) out<<trailer<<" "; + out<<" "<<binning.GetBinFactor(i+binning.GetStartBin()); + if(((i %10)==9)||(i==binning.GetDistributionNumberOfBins()-1)) + out<<"\n"; + } + out<<trailer<<" </Binfactorlist>\n"; + } + if(binning.HasUnconnectedBins()) { + out<<trailer<<" <Bins nbin=\""<<binning.GetDistributionNumberOfBins() + <<"\">\n"; + for(Int_t i=0;i<binning.GetDistributionNumberOfBins();i++) { + const TObjString *name=binning.GetUnconnectedBinName(i); + if(!name) break; + out<<trailer<<" <BinLabel index=\""<<i<<"\" name=\"" + <<name->GetString()<<"\" />\n"; + } + out<<trailer<<" </Bins>\n"; + } else { + for(Int_t axis=0;axis<binning.GetDistributionDimension();axis++) { + TString axisTrailer(' ',indent+1+axis); + TVectorD const *edges=binning.GetDistributionBinning(axis); + out<<axisTrailer<<"<Axis name=\""<<binning.GetDistributionAxisLabel(axis) + <<"\" lowEdge=\""<<(*edges)[0]<<"\">\n"; + if(binning.HasUnderflow(axis)) { + out<<axisTrailer<<" <Bin location=\"underflow\" width=\"" + <<binning.GetDistributionUnderflowBinWidth(axis)<<"\" center=\"" + <<binning.GetDistributionBinCenter(axis,-1)<<"\" />\n"; + } + for(Int_t i=0;i<edges->GetNrows()-1;i++) { + Int_t repeat=1; + Double_t width=(*edges)[i+1]-(*edges)[i]; + Double_t center=binning.GetDistributionBinCenter(axis,i); + for(Int_t j=i+1;j<edges->GetNrows()-1;j++) { + double xEnd=(j-i+1)*width+(*edges)[i]; + double xCent=center+(j-i)*width; + if((TMath::Abs(xEnd-(*edges)[j+1])<width*1.E-7)&& + (TMath::Abs(xCent-binning.GetDistributionBinCenter(axis,j))< + width*1.E-7)) { + ++repeat; + } else { + break; + } + } + if(repeat==1) { + out<<axisTrailer<<" <Bin width=\"" + <<width<<"\" center=\""<<center<<"\" />\n"; + } else { + out<<axisTrailer<<" <Bin repeat=\""<<repeat + <<"\" width=\""<<width<<"\" center=\""<<center<<"\" />\n"; + i += repeat-1; + } + } + if(binning.HasOverflow(axis)) { + out<<axisTrailer<<" <Bin location=\"overflow\" width=\"" + <<binning.GetDistributionOverflowBinWidth(axis)<<"\" center=\"" + <<binning.GetDistributionBinCenter(axis,edges->GetNrows()-1)<<"\"/>\n"; + } + } + for(Int_t axis=binning.GetDistributionDimension()-1;axis>=0;axis--) { + TString axisTrailer(' ',indent+1+axis); + out<<axisTrailer<<"</Axis>\n"; + } + } + for(TUnfoldBinning const *child=binning.GetChildNode();child; + child=child->GetNextNode()) { + ExportXML(*child,out,kFALSE,kFALSE,indent+1); + } + out<<trailer<<"</BinningNode>\n"; + if(writeFooter) { + out<<"</TUnfoldBinning>\n"; + } + return out.fail() ? 0 : 1; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Export this binning scheme to a file. +/// +/// \param[in] fileName name of the file +/// +/// returns true if the writing succeeded +/// +/// export this binning scheme to a file +/// - fileName: name of the xml file + +Int_t TUnfoldBinningXML::ExportXML(char const *fileName) const { + ofstream outFile(fileName); + Int_t r=ExportXML(*this,outFile,kTRUE,kTRUE); + outFile.close(); + return r; +} diff --git a/hist/hist/src/TUnfoldDensity.cxx b/hist/unfold/src/TUnfoldDensity.cxx similarity index 51% rename from hist/hist/src/TUnfoldDensity.cxx rename to hist/unfold/src/TUnfoldDensity.cxx index 18c58726c0ae0de2e37921809003a98cc7ec0811..1bb823ce2a7652625805ba237a38d04e3bf89a33 100644 --- a/hist/hist/src/TUnfoldDensity.cxx +++ b/hist/unfold/src/TUnfoldDensity.cxx @@ -1,111 +1,127 @@ -// Author: Stefan Schmitt, Amnon Harel -// DESY and CERN, 11/08/11 - -// Version 17.1, add scan type RhoSquare, small bug fixes with useAxisBinning -// -// History: -// Version 17.0, support for density regularisation, complex binning schemes, tau scan - -/** \class TUnfoldBinning - \ingroup Hist - TUnfold is used to decompose a measurement y into several sources x - given the measurement uncertainties and a matrix of migrations A - - More details are described with the documentation of TUnfold. - - For most applications, it is best to use TUnfoldDensity - instead of using TUnfoldSys or TUnfold - - If you use this software, please consider the following citation - S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201] - - More documentation and updates are available on - http://www.desy.de/~sschmitt - - As compared to TUnfold, TUndolfDensity adds the following functionality - * background subtraction (see documentation of TUnfoldSys) - * error propagation (see documentation of TUnfoldSys) - * regularisation schemes respecting the bin widths - * support for complex, multidimensional input distributions - - Complex binning schemes are imposed on the measurements y and - on the result vector x with the help of the class TUnfoldBinning - The components of x or y are part of multi-dimensional distributions. - The bin widths along the relevant directions in these distributions - are used to calculate bin densities (number of events divided by bin width) - or to calculate derivatives taking into account the proper distance of - adjacent bin centers - - ## Complex binning schemes - in literature on unfolding, the "standard" test case is a - one-dimensional distribution without underflow or overflow bins. - The migration matrix is almost diagonal. - - This "standard" case is rarely realized for real problems. - - Often one has to deal with multi-dimensional input distributions. - In addition, there are underflow and overflow bins - or other background bins, possibly determined with the help of auxillary - measurements - - In TUnfoldDensity, such complex binning schemes are handled with the help - of the class TUnfoldBinning. For each vector there is a tree - structure. The tree nodes hold multi-dimensiopnal distributions - - For example, the "measurement" tree could have two leaves, one for - the primary distribution and one for auxillary measurements - - Similarly, the "truth" tree could have two leaves, one for the - signal and one for the background. - - each of the leaves may then have a multi-dimensional distribution. - - The class TUnfoldBinning takes care to map all bins of the - "measurement" to the one-dimensional vector y. - Similarly, the "truth" bins are mapped to the vector x. - - ## Choice of the regularisation - In TUnfoldDensity, two methods are implemented to determine tau**2 - 1. ScanLcurve() locate the tau where the L-curve plot has a "kink" - this function is implemented in the TUnfold class - 2. ScanTau() finds the solution such that some variable - (e.g. global correlation coefficient) is minimized - this function is implemented in the TUnfoldDensity class, - such that the variable could be made depend on the binning scheme - - Each of the algorithms has its own advantages and disadvantages - - The algorithm (1) does not work if the input data are too similar to the - MC prediction, that is unfolding with tau=0 gives a least-square sum - of zero. Typical no-go cases of the L-curve scan are: - - (a) the number of measurements is too small (e.g. ny=nx) - - (b) the input data have no statistical fluctuations - [identical MC events are used to fill the matrix of migrations - and the vector y] - - The algorithm (2) only works if the variable does have a real minimum - as a function of tau. - If global correlations are minimized, the situation is as follows: - The matrix of migration typically introduces negative correlations. - The area constraint introduces some positive correlation. - Regularisation on the "size" introduces no correlation. - Regularisation on 1st or 2nd derivatives adds positive correlations. - For this reason, "size" regularisation does not work well with - the tau-scan: the higher tau, the smaller rho, but there is no minimum. - In contrast, the tau-scan is expected to work well with 1st or 2nd - derivative regularisation, because at some point the negative - correlations from migrations are approximately cancelled by the - positive correlations from the regularisation conditions. - - whichever algorithm is used, the output has to be checked: +// @(#)root/unfold:$Id$ +// Authors: Stefan Schmitt, Amnon Harel DESY and CERN, 11/08/11 + +/** \class TUnfoldDensity +\ingroup Unfold +An algorithm to unfold distributions from detector to truth level + +TUnfoldDensity is used to decompose a measurement y into several sources x, +given the measurement uncertainties, background b and a matrix of migrations A. +The method can be applied to a large number of problems, +where the measured distribution y is a linear superposition +of several Monte Carlo shapes. Beyond such a simple template fit, +TUnfoldDensity has an adjustable regularisation term and also supports an +optional constraint on the total number of events. +Background sources can be specified, with a normalisation constant and +normalisation uncertainty. In addition, variants of the response +matrix may be specified, these are taken to determine systematic +uncertainties. Complex, multidimensional arrangements of signal and +background bins are managed with the help of the class TUnfoldBinning. + +If you use this software, please consider the following citation + +<b>S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201]</b> + +Detailed documentation and updates are available on +http://www.desy.de/~sschmitt + +### Brief recipe to use TUnfoldSys: + + - Set up binning schemes for the truth and measured +distributions. The binning schemes may be coded in the XML language, +for reading use TUnfoldBinningXML. + - A matrix (truth,reconstructed) is given as a two-dimensional histogram + as argument to the constructor of TUnfold + - A vector of measurements is given as one-dimensional histogram using + the SetInput() method + - Repeated calls to SubtractBackground() to specify background sources + - Repeated calls to AddSysError() to specify systematic uncertainties + - The unfolding is performed + + - either once with a fixed parameter tau, method DoUnfold(tau) + - or multiple times in a scan to determine the best choice of tau, + method ScanLCurve() + - or multiple times in a scan to determine the best choice of tau, + method ScanTau() + + - Unfolding results are retrieved using various GetXXX() methods + +A detailed documentation of the various GetXXX() methods to control +systematic uncertainties is given with the method TUnfoldSys. + +### Why to use complex binning schemes + +in literature on unfolding, the "standard" test case is a +one-dimensional distribution without underflow or overflow bins. +The migration matrix is almost diagonal. + +<b>This "standard" case is rarely realized for real problems.</b> + +Often one has to deal with multi-dimensional distributions. +In addition, there are underflow and overflow bins +or other background bins, possibly determined with the help of auxiliary +measurements. + +In TUnfoldDensity, such complex binning schemes are handled with the help +of the class TUnfoldBinning. For both the measurement and the truth +there is a tree structure. The tree nodes may correspond to single +bins (e.g. nuisance parameters) or may hold multi-dimensional distributions. + +For example, the "measurement" tree could have two leaves, one for +the primary distribution and one for auxiliary measurements. +Similarly, the "truth" tree could have two leaves, one for the +signal and one for the background. +Each of the leaves may then have a multi-dimensional distribution. + +The class TUnfoldBinning takes care to map all bins of the +"measurement" to a one-dimensional vector y. +Similarly, the "truth" bins are mapped to the vector x. + +### How to choose the regularisation settings + +In TUnfoldDensity, two methods are implemented to determine tau**2 + + 1. ScanLcurve() locate the tau where the L-curve plot has a "kink" + this function is implemented in the TUnfold class + 2. ScanTau() finds the solution such that some variable + (e.g. global correlation coefficient) is minimized. + This function is implemented in the TUnfoldDensity class + +Each of the algorithms has its own advantages and disadvantages. +The algorithm (1) does not work if the input data are too similar to the +MC prediction. Typical no-go cases of the L-curve scan are: + + - the number of measurements is too small (e.g. ny=nx) + - the input data have no statistical fluctuations + [identical MC events are used to fill the matrix of migrations + and the vector y for a "closure test"] + +The algorithm (2) only works if the variable does have a real minimum +as a function of tau. If global correlations are minimized, the situation +is as follows: +The matrix of migration typically introduces negative correlations. +The area constraint introduces some positive correlation. +Regularisation on the "size" introduces no correlation. +Regularisation on 1st or 2nd derivatives adds positive correlations. + +For these reasons, "size" regularisation does not work well with +the tau-scan: the higher tau, the smaller rho, but there is no minimum. +As a result, large values of tau (too strong regularisation) are found. +In contrast, the tau-scan is expected to work better with 1st or 2nd +derivative regularisation, because at some point the negative +correlations from migrations are approximately cancelled by the +positive correlations from the regularisation conditions. + +whichever algorithm is used, the output has to be checked: + 1. The L-curve should have approximate L-shape - and the final choice of tau should not be at the very edge of the - scanned region + and the final choice of tau should not be at the very edge of the + scanned region 2. The scan result should have a well-defined minimum and the - final choice of tau should sit right in the minimum -*/ + final choice of tau should sit right in the minimum + -/* +-------------------------------------------------------------------------------- This file is part of TUnfold. TUnfold is free software: you can redistribute it and/or modify @@ -120,6 +136,16 @@ You should have received a copy of the GNU General Public License along with TUnfold. If not, see <http://www.gnu.org/licenses/>. + +<b>Version 17.6, with updated doxygen comments and bug-fixes in TUnfoldBinning</b> + +#### History: + - Version 17.5, bug fix in TUnfold also corrects GetEmatrixSysUncorr() + - Version 17.4, in parallel to changes in TUnfoldBinning + - Version 17.3, in parallel to changes in TUnfoldBinning + - Version 17.2, with new options 'N' and 'c' for axis regularisation steering + - Version 17.1, add scan type RhoSquare, small bug fixes with useAxisBinning + - Version 17.0, support for density regularisation, complex binning schemes, tau scan */ #include "TUnfoldDensity.h" @@ -129,13 +155,27 @@ #include <iostream> #include <map> -// #define DEBUG +//#define DEBUG + +#ifdef DEBUG +using namespace std; +#endif ClassImp(TUnfoldDensity) +TUnfoldDensity::~TUnfoldDensity(void) +{ + // clean up + if(fOwnedOutputBins) delete fOwnedOutputBins; + if(fOwnedInputBins) delete fOwnedInputBins; + if(fRegularisationConditions) delete fRegularisationConditions; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Only for use by root streamer or derived classes. + TUnfoldDensity::TUnfoldDensity(void) { - // empty constructor, for derived classes fConstOutputBins=0; fConstInputBins=0; fOwnedOutputBins=0; @@ -143,13 +183,36 @@ TUnfoldDensity::TUnfoldDensity(void) fRegularisationConditions=0; } -TUnfoldDensity::~TUnfoldDensity(void) -{ - // clean up - if(fOwnedOutputBins) delete fOwnedOutputBins; - if(fOwnedInputBins) delete fOwnedInputBins; - if(fRegularisationConditions) delete fRegularisationConditions; -} +//////////////////////////////////////////////////////////////////////////////// +/// Eet up response matrix A, uncorrelated uncertainties of A, +/// regularisation scheme and binning schemes. +/// +/// \param[in] hist_A matrix that describes the migrations +/// \param[in] histmap mapping of the histogram axes to the unfolding output +/// \param[in] regmode (default=kRegModeSize) global regularisation mode +/// \param[in] constraint (default=kEConstraintArea) type of constraint +/// \param[in] densityMode (default=kDensityModeBinWidthAndUser) +/// regularisation scale factors to construct the matrix L +/// \param[in] outputBins (default=0) binning scheme for truth (unfolding output) +/// \param[in] inputBins (default=0) binning scheme for measurement (unfolding +/// input) +/// \param[in] regularisationDistribution (default=0) selection of +/// regularized distribution +/// \param[in] regularisationAxisSteering (default=0) detailed +/// regularisation steering for selected distribution +/// +/// The parameters <b>hist_A, histmap, constraint</b> are +/// explained with the TUnfoldSys constructor. +/// +/// The parameters <b>outputBins,inputBins</b> set the binning +/// schemes. If these arguments are zero, simple binning schemes are +/// constructed which correspond to the axes of the histogram +/// <b>hist_A</b>. +/// +/// The parameters +/// <b>regmode, densityMode, regularisationDistribution, regularisationAxisSteering</b> +/// together control how the initial matrix L of regularisation conditions +/// is constructed. as explained in RegularizeDistribution(). TUnfoldDensity::TUnfoldDensity (const TH2 *hist_A, EHistMap histmap,ERegMode regmode,EConstraint constraint, @@ -158,16 +221,6 @@ TUnfoldDensity::TUnfoldDensity const char *regularisationAxisSteering) : TUnfoldSys(hist_A,histmap,kRegModeNone,constraint) { - // set up unfolding matrix and regularisation scheme - // hist_A: matrix that describes the migrations - // histmap: mapping of the histogram axes to the unfolding output - // regmode: global regularisation mode - // constraint: type of constraint to use - // regularisationSteering: detailed steering for the regularisation - // see method RegularizeDistribution() - // outputBins: binning scheme of the output bins - // inputBins: binning scheme of the input bins - fRegularisationConditions=0; // set up binning schemes fConstOutputBins = outputBins; @@ -208,48 +261,77 @@ TUnfoldDensity::TUnfoldDensity // check whether binning scheme matches with the histogram // in terms of total number of bins Int_t nOut=genAxis->GetNbins(); - Int_t nOutMapped=TMath::Abs(fConstOutputBins->GetTH1xNumberOfBins()); - if(nOutMapped!= nOut) { + Int_t nOutMappedT=TMath::Abs(fConstOutputBins->GetTH1xNumberOfBins(kTRUE)); + Int_t nOutMappedF=TMath::Abs(fConstOutputBins->GetTH1xNumberOfBins + (fOwnedOutputBins)); + if((nOutMappedT!= nOut)&&(nOutMappedF!=nOut)) { Error("TUnfoldDensity", - "Output binning incompatible number of bins %d!=%d", - nOutMapped, nOut); + "Output binning incompatible number of bins: axis %d binning scheme %d (%d)", + nOut,nOutMappedT,nOutMappedF); } // check whether binning scheme matches with the histogram Int_t nInput=detAxis->GetNbins(); - Int_t nInputMapped=TMath::Abs(fConstInputBins->GetTH1xNumberOfBins()); - if(nInputMapped!= nInput) { + Int_t nInputMappedT=TMath::Abs(fConstInputBins->GetTH1xNumberOfBins(kTRUE)); + Int_t nInputMappedF=TMath::Abs(fConstInputBins->GetTH1xNumberOfBins + (fOwnedInputBins)); + if((nInputMappedT!= nInput)&&(nInputMappedF!= nInput)) { Error("TUnfoldDensity", - "Input binning incompatible number of bins %d!=%d ", - nInputMapped, nInput); + "Input binning incompatible number of bins:axis %d binning scheme %d (%d) ", + nInput,nInputMappedT,nInputMappedF); } // report detailed list of excluded bins for (Int_t ix = 0; ix <= nOut+1; ix++) { if(fHistToX[ix]<0) { - Info("TUnfold","*NOT* unfolding bin %s",GetOutputBinName(ix).Data()); + Info("TUnfold","*NOT* unfolding bin %s",(char const *)GetOutputBinName(ix)); } } // set up the regularisation here if(regmode !=kRegModeNone) { RegularizeDistribution - (regmode,densityMode,regularisationDistribution, - regularisationAxisSteering); + (regmode,densityMode,regularisationDistribution, + regularisationAxisSteering); } } +//////////////////////////////////////////////////////////////////////////////// +/// Get bin name of an output bin. +/// +/// \param[in] iBinX bin number +/// +/// Return value: name of the bin. The name is constructed from the +/// entries in the binning scheme and includes information about the +/// bin borders etc. + TString TUnfoldDensity::GetOutputBinName(Int_t iBinX) const { if(!fConstOutputBins) return TUnfold::GetOutputBinName(iBinX); else return fConstOutputBins->GetBinName(iBinX); } +//////////////////////////////////////////////////////////////////////////////// +/// Density correction factor for a given bin. +/// +/// \param[in] densityMode type of factor to calculate +/// \param[in] iBin bin number +/// +/// return a multiplicative factor, for scaling the regularisation +/// conditions from this bin. +/// +/// For densityMode=kDensityModeNone the factor is set to unity. +/// For densityMode=kDensityModeBinWidth +/// the factor is set to 1/binArea +/// where the binArea is the product of the bin widths in all +/// dimensions. If the width of a bin is zero or can not be +/// determined, the factor is set to zero. +/// For densityMode=kDensityModeUser the factor is determined from the +/// method TUnfoldBinning::GetBinFactor(). +/// For densityMode=kDensityModeBinWidthAndUser, the results of +/// kDensityModeBinWidth and kDensityModeUser are multiplied. + Double_t TUnfoldDensity::GetDensityFactor (EDensityMode densityMode,Int_t iBin) const { - // density correction factor for a given bin - // distributionName : name of the distribution within the output binning - // densityFlags : type of factor to calculate - // iBin : bin number Double_t factor=1.0; if((densityMode == kDensityModeBinWidth)|| (densityMode == kDensityModeBinWidthAndUser)) { @@ -264,50 +346,61 @@ Double_t TUnfoldDensity::GetDensityFactor return factor; } +//////////////////////////////////////////////////////////////////////////////// +/// Set up regularisation conditions. +/// +/// \param[in] regmode basic regularisation mode (see class TUnfold) +/// \param[in] densityMode how to apply bin-wise factors +/// \param[in] distribution name of the TUnfoldBinning node for which +/// the regularisation conditions shall be set (zero matches all nodes) +/// \param[in] axisSteering regularisation fine-tuning +/// +/// <b>axisSteering</b> is a string with several tokens, separated by +/// a semicolon: `"axisName[options];axisName[options];..."`. +/// +/// - <b>axisName</b>: +/// the name of an axis. The special name * matches all. +/// So the argument <b>distribution</b> selects one (or all) +/// distributions. Within the selected distribution(s), steering options may be +/// specified for each axis (or for all axes) to define the +/// regularisation conditions. +/// - <b>options</b> +/// one or several character as follows: +/// - u : exclude underflow bin from derivatives along this axis +/// - o : exclude overflow bin from derivatives along this axis +/// - U : exclude underflow bin +/// - O : exclude overflow bin +/// - b : use bin width for derivative calculation +/// - B : same as 'b', in addition normalize to average bin width +/// - N : completely exclude derivatives along this axis +/// - p : axis is periodic (e.g. azimuthal angle), so +/// include derivatives built from combinations involving bins at +/// both ends of the axis "wrap around" +/// +/// example: <b>axisSteering</b>=`"*[UOB]"` uses bin widths to calculate +/// derivatives but underflow/overflow bins are not regularized + void TUnfoldDensity::RegularizeDistribution (ERegMode regmode,EDensityMode densityMode,const char *distribution, const char *axisSteering) { - // regularize distribution(s) using the given settings - // regmode: basic regularisation mode (see class TUnfold) - // densityMode: how to apply bin density corrections - // (normalisation to bin width or user factor) - // distribution: name of the distribiution where this regularisation - // is applied to (if zero, apply to all) - // axisSteering: regularisation steering specific to the axes - // The steering is defined as follows - // "steering1;steering2;...steeringN" - // each "steeringX" is defined as - // axisName:[options] - // axisName: the name of an axis where "options" applies - // the special name * matches all axes - // options: one of several character as follows - // u : exclude underflow bin from derivatives along this axis - // o : exclude overflow bin from derivatives along this axis - // U : exclude underflow bin - // O : exclude overflow bin - // b : use bin width for derivative calculation - // B : same as 'b' but in addition normalize to average bin width - // - // example: "*[UOB]" uses bin widths for derivatives and - // underflow/overflow bins are not regularized RegularizeDistributionRecursive(GetOutputBinning(),regmode,densityMode, distribution,axisSteering); } +//////////////////////////////////////////////////////////////////////////////// +/// Recursively add regularisation conditions for this node and its children. +/// +/// \param[in] binning current node +/// \param[in] regmode regularisation mode +/// \param[in] densityMode type of regularisation scaling +/// \param[in] distribution target distribution(s) name +/// \param[in] axisSteering steering within the target distribution(s) + void TUnfoldDensity::RegularizeDistributionRecursive (const TUnfoldBinning *binning,ERegMode regmode, EDensityMode densityMode,const char *distribution,const char *axisSteering) { - // recursively regularize distribution(s) using the given settings - // binning: distributions for this node an its children are considered - // regmode: basic regularisation mode (see class TUnfold) - // densityMode: how to apply bin density corrections - // (normalisation to bin withd or user factor) - // distribution: name of the distribiution where this regularisation - // is applied to (if zero, apply to all) - // axisSteering: regularisation steering specific to the axes - // (see method RegularizeDistribution()) if((!distribution)|| !TString(distribution).CompareTo(binning->GetName())) { RegularizeOneDistribution(binning,regmode,densityMode,axisSteering); } @@ -318,17 +411,23 @@ void TUnfoldDensity::RegularizeDistributionRecursive } } +//////////////////////////////////////////////////////////////////////////////// +/// Regularize the distribution of the given node. +/// +/// \param[in] binning current node +/// \param[in] regmode regularisation mode +/// \param[in] densityMode type of regularisation scaling +/// \param[in] axisSteering detailed steering for the axes of the distribution + void TUnfoldDensity::RegularizeOneDistribution (const TUnfoldBinning *binning,ERegMode regmode, EDensityMode densityMode,const char *axisSteering) { - // regularize the distribution in this node - // binning: the distributions to regularize - // regmode: basic regularisation mode (see class TUnfold) - // densityMode: how to apply bin density corrections - // (normalisation to bin withd or user factor) - // axisSteering: regularisation steering specific to the axes - // (see method RegularizeDistribution()) +#ifdef DEBUG + cout<<"TUnfoldDensity::RegularizeOneDistribution node=" + <<binning->GetName()<<" "<<regmode<<" "<<densityMode + <<" "<<(axisSteering ? axisSteering : "")<<"\n"; +#endif if(!fRegularisationConditions) fRegularisationConditions=new TUnfoldBinning("regularisation"); @@ -336,14 +435,23 @@ void TUnfoldDensity::RegularizeOneDistribution fRegularisationConditions->AddBinning(binning->GetName()); // decode steering - Int_t isOptionGiven[6] = {0}; - binning->DecodeAxisSteering(axisSteering,"uUoObB",isOptionGiven); + Int_t isOptionGiven[8]; + binning->DecodeAxisSteering(axisSteering,"uUoObBpN",isOptionGiven); // U implies u isOptionGiven[0] |= isOptionGiven[1]; // O implies o isOptionGiven[2] |= isOptionGiven[3]; // B implies b isOptionGiven[4] |= isOptionGiven[5]; + // option N is removed if any other option is on + for(Int_t i=0;i<7;i++) { + isOptionGiven[7] &= ~isOptionGiven[i]; + } + // option "c" does not work with options UuOo + if(isOptionGiven[6] & (isOptionGiven[0]|isOptionGiven[2]) ) { + Error("RegularizeOneDistribution", + "axis steering %s is not valid",axisSteering); + } #ifdef DEBUG cout<<" "<<isOptionGiven[0] <<" "<<isOptionGiven[1] @@ -351,12 +459,14 @@ void TUnfoldDensity::RegularizeOneDistribution <<" "<<isOptionGiven[3] <<" "<<isOptionGiven[4] <<" "<<isOptionGiven[5] + <<" "<<isOptionGiven[6] + <<" "<<isOptionGiven[7] <<"\n"; #endif Info("RegularizeOneDistribution","regularizing %s regMode=%d" - " densityMode=%d axisSteering=%s", - binning->GetName(),(Int_t) regmode,(Int_t)densityMode, - axisSteering ? axisSteering : ""); + " densityMode=%d axisSteering=%s", + binning->GetName(),(Int_t) regmode,(Int_t)densityMode, + axisSteering ? axisSteering : ""); Int_t startBin=binning->GetStartBin(); Int_t endBin=startBin+ binning->GetDistributionNumberOfBins(); std::vector<Double_t> factor(endBin-startBin); @@ -400,6 +510,12 @@ void TUnfoldDensity::RegularizeOneDistribution // for each direction Int_t nRegBins=0; Int_t directionMask=(1<<direction); + if(isOptionGiven[7] & directionMask) { +#ifdef DEBUG + cout<<"skip direction "<<direction<<"\n"; +#endif + continue; + } Double_t binDistanceNormalisation= (isOptionGiven[5] & directionMask) ? binning->GetDistributionAverageBinSize @@ -411,8 +527,15 @@ void TUnfoldDensity::RegularizeOneDistribution // for each bin, find the neighbour bins Int_t iPrev,iNext; Double_t distPrev,distNext; - binning->GetBinNeighbours - (bin,direction,&iPrev,&distPrev,&iNext,&distNext); + Int_t error=binning->GetBinNeighbours + (bin,direction,&iPrev,&distPrev,&iNext,&distNext, + isOptionGiven[6] & directionMask); + if(error) { + Error("RegularizeOneDistribution", + "invalid option %s (isPeriodic) for axis %s" + " (has underflow or overflow)",axisSteering, + binning->GetDistributionAxisLabel(direction).Data()); + } if((regmode==kRegModeDerivative)&&(iNext>=0)) { Double_t f0 = -factor[bin-startBin]; Double_t f1 = factor[iNext-startBin]; @@ -479,25 +602,56 @@ void TUnfoldDensity::RegularizeOneDistribution #endif } +/////////////////////////////////////////////////////////////////////// +/// retrieve unfolding result as a new histogram +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// return value: pointer to a new histogram. If +/// <b>useAxisBinning</b> is set and if the selected distribution fits +/// into a root histogram (1,2,3 dimensions) then return a histogram +/// with the proper binning on each axis. Otherwise, return a 1D +/// histogram with equidistant binning. If the histogram title is +/// zero, a title is assigned automatically. +/// +/// The <b>axisSteering</b> is defines as follows: "axis[mode];axis[mode];..." +/// where: +/// +/// - axis = name of an axis or * +/// - mode = any combination of the letters CUO0123456789 +/// +/// - C collapse axis into one bin (add up results). If +/// any of the numbers 0-9 are given in addition, only these bins are added up. +/// Here bins are counted from zero, whereas in root, bins are counted +/// from 1. Obviously, this only works for up to 10 bins. +/// - U discard underflow bin +/// - O discard overflow bin +/// +/// examples: imagine the binning has two axis, named x and y. +/// +/// - "*[UO]" exclude underflow and overflow bins for all axis. +/// So here a TH2 is returned but all undeflow and overflow bins are empty +/// - "x[UOC123]" integrate over the variable x but only using the +/// bins 1,2,3 and not the underflow and overflow in x. +/// Here a TH1 is returned, the axis is labelled "y" and +/// the underflow and overflow (in y) are filled. However only the x-bins +/// 1,2,3 are used to determine the content. +/// - "x[C];y[UO]" integrate over the variable x, including +/// underflow and overflow but exclude underflow and overflow in y. +/// Here a TH1 is returned, the axis is labelled "y". The underflow +/// and overflow in y are empty. + TH1 *TUnfoldDensity::GetOutput (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) const { - // retreive unfolding result as histogram - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -511,18 +665,26 @@ TH1 *TUnfoldDensity::GetOutput return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve bias vector as a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetBias (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) const { - // retreive unfolding bias as histogram - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); + TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram (histogramName,useAxisBinning,&binMap,histogramTitle,axisSteering); @@ -533,18 +695,27 @@ TH1 *TUnfoldDensity::GetBias return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve unfolding result folded back as a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// \param[in] addBgr (default=false) if true, include the background +/// contribution (useful for direct comparison to data) +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetFoldedOutput (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning,Bool_t addBgr) const { - // retreive unfolding result folded back by the matrix - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // addBgr: true if the background shall be included TUnfoldBinning const *binning=fConstInputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -559,44 +730,58 @@ TH1 *TUnfoldDensity::GetFoldedOutput return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve a background source in a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] bgrSource the background source to retrieve +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// \param[in] includeError (default=3) type of background errors to +/// be included (+1 uncorrelated bgr errors, +2 correlated bgr errors) +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetBackground (const char *histogramName,const char *bgrSource,const char *histogramTitle, const char *distributionName,const char *axisSteering,Bool_t useAxisBinning, - Int_t includeError,Bool_t clearHist) const + Int_t includeError) const { - // retreive a background source - // histogramName: name of the histogram - // bgrSource: name of the background source - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // include error: +1 if uncorrelated bgr errors should be included - // +2 if correlated bgr errors should be included - // clearHist: whether the histogram should be cleared - // if false, the background sources are added to the histogram TUnfoldBinning const *binning=fConstInputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram (histogramName,useAxisBinning,&binMap,histogramTitle,axisSteering); if(r) { - TUnfoldSys::GetBackground(r,bgrSource,binMap,includeError,clearHist); + TUnfoldSys::GetBackground(r,bgrSource,binMap,includeError,kTRUE); } if(binMap) delete [] binMap; return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve input distribution in a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetInput (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) const { - // retreive input distribution - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object TUnfoldBinning const *binning=fConstInputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -608,29 +793,27 @@ TH1 *TUnfoldDensity::GetInput return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve global correlation coefficients including all uncertainty sources. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// \param[out] ematInv (default=0) to return the inverse covariance matrix +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments. The inverse of the covariance matrix +/// is stored in a new histogram returned by <b>ematInv</b> if that +/// pointer is non-zero. + TH1 *TUnfoldDensity::GetRhoItotal (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning,TH2 **ematInv) { - // retreive global correlation coefficients, total error - // and inverse of error matrix - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram - // ematInv: retreive inverse of error matrix - // if ematInv==0 the inverse is not returned TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -660,29 +843,28 @@ TH1 *TUnfoldDensity::GetRhoItotal return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve global correlation coefficients including input +/// (statistical) and background uncertainties. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// \param[out] ematInv (default=0) to return the inverse covariance matrix +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments. The inverse of the covariance matrix +/// is stored in a new histogram returned by <b>ematInv</b> if that +/// pointer is non-zero. + TH1 *TUnfoldDensity::GetRhoIstatbgr (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning,TH2 **ematInv) { - // retreive global correlation coefficients, input error - // and inverse of corresponding error matrix - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram - // ematInv: retreive inverse of error matrix - // if ematInv==0 the inverse is not returned TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -712,27 +894,25 @@ TH1 *TUnfoldDensity::GetRhoIstatbgr return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve a correlated systematic 1-sigma shift. +/// +/// \param[in] source identifier of the systematic uncertainty source +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetDeltaSysSource (const char *source,const char *histogramName, const char *histogramTitle,const char *distributionName, const char *axisSteering,Bool_t useAxisBinning) { - // retreive histogram of systematic 1-sigma shifts - // source: name of systematic error - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -747,28 +927,26 @@ TH1 *TUnfoldDensity::GetDeltaSysSource return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve systematic 1-sigma shift corresponding to a background +/// scale uncertainty. +/// +/// \param[in] bgrSource identifier of the background +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetDeltaSysBackgroundScale (const char *bgrSource,const char *histogramName, const char *histogramTitle,const char *distributionName, const char *axisSteering,Bool_t useAxisBinning) { - // retreive histogram of systematic 1-sigma shifts due to a background - // normalisation uncertainty - // source: name of background source - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -783,26 +961,25 @@ TH1 *TUnfoldDensity::GetDeltaSysBackgroundScale return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve 1-sigma shift corresponding to the previously specified uncertainty +/// on tau. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH1 *TUnfoldDensity::GetDeltaSysTau (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering,Bool_t useAxisBinning) { - // retreive histogram of systematic 1-sigma shifts due to tau uncertainty - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH1 *r=binning->CreateHistogram @@ -817,28 +994,25 @@ TH1 *TUnfoldDensity::GetDeltaSysTau return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve correlation coefficients, including all uncertainties. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH2 *TUnfoldDensity::GetRhoIJtotal (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) { - // retreive histogram of total corelation coefficients, including systematic - // uncertainties - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TH2 *r=GetEmatrixTotal (histogramName,histogramTitle,distributionName, axisSteering,useAxisBinning); @@ -848,7 +1022,7 @@ TH2 *TUnfoldDensity::GetRhoIJtotal if(e_i>0.0) e_i=TMath::Sqrt(e_i); else e_i=0.0; for(Int_t j=0;j<=r->GetNbinsY()+1;j++) { - if(i==j) continue; + if(i==j) continue; Double_t e_j=r->GetBinContent(j,j); if(e_j>0.0) e_j=TMath::Sqrt(e_j); else e_j=0.0; @@ -861,37 +1035,36 @@ TH2 *TUnfoldDensity::GetRhoIJtotal } } for(Int_t i=0;i<=r->GetNbinsX()+1;i++) { - if(r->GetBinContent(i,i)>0.0) { - r->SetBinContent(i,i,1.0); - } else { - r->SetBinContent(i,i,0.0); - } + if(r->GetBinContent(i,i)>0.0) { + r->SetBinContent(i,i,1.0); + } else { + r->SetBinContent(i,i,0.0); + } } } return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve covariance contribution from uncorrelated (statistical) +/// uncertainties of the response matrix. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH2 *TUnfoldDensity::GetEmatrixSysUncorr (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) { - // get error matrix contribution from uncorrelated errors on the matrix A - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH2 *r=binning->CreateErrorMatrixHistogram @@ -903,28 +1076,26 @@ TH2 *TUnfoldDensity::GetEmatrixSysUncorr return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Retrieve covariance contribution from uncorrelated background uncertainties. +/// +/// \param[in] bgrSource identifier of the background +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments TH2 *TUnfoldDensity::GetEmatrixSysBackgroundUncorr (const char *bgrSource,const char *histogramName, const char *histogramTitle,const char *distributionName, const char *axisSteering,Bool_t useAxisBinning) { - // get error matrix from uncorrelated error of one background source - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH2 *r=binning->CreateErrorMatrixHistogram @@ -936,27 +1107,26 @@ TH2 *TUnfoldDensity::GetEmatrixSysBackgroundUncorr return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Get covariance contribution from the input uncertainties (data +/// statistical uncertainties). +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH2 *TUnfoldDensity::GetEmatrixInput (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) { - // get error contribution from input vector - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH2 *r=binning->CreateErrorMatrixHistogram @@ -968,15 +1138,21 @@ TH2 *TUnfoldDensity::GetEmatrixInput return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Get matrix of probabilities in a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. if histogramTitle is null, choose a title +/// automatically. + TH2 *TUnfoldDensity::GetProbabilityMatrix (const char *histogramName,const char *histogramTitle, Bool_t useAxisBinning) const { - // get matrix of probabilities - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // useAxisBinning: if true, try to get the histogram using - // the original matrix binning TH2 *r=TUnfoldBinning::CreateHistogramOfMigrations (fConstOutputBins,fConstInputBins,histogramName, useAxisBinning,useAxisBinning,histogramTitle); @@ -984,27 +1160,25 @@ TH2 *TUnfoldDensity::GetProbabilityMatrix return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Get covariance matrix including all contributions. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] distributionName (default=0) identifier of the distribution to be extracted +/// \param[in] axisSteering (default=0) detailed steering within the extracted +/// distribution +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. See method GetOutput() for a detailed +/// description of the arguments + TH2 *TUnfoldDensity::GetEmatrixTotal (const char *histogramName,const char *histogramTitle, const char *distributionName,const char *axisSteering, Bool_t useAxisBinning) { - // get total error including systematic,statistical,background,tau errors - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // distributionName: for complex binning schemes specify the name - // of the requested distribution within the TUnfoldBinning - // object - // axisSteering: - // "pattern1;pattern2;...;patternN" - // patternI = axis[mode] - // axis = name or * - // mode = C|U|O - // C: collapse axis into one bin - // U: discarde underflow bin - // O: discarde overflow bin - // useAxisBinning: if true, try to use the axis bin widths - // on the output histogram TUnfoldBinning const *binning=fConstOutputBins->FindNode(distributionName); Int_t *binMap=0; TH2 *r=binning->CreateErrorMatrixHistogram @@ -1016,15 +1190,20 @@ TH2 *TUnfoldDensity::GetEmatrixTotal return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Access matrix of regularisation conditions in a new histogram. +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// \param[in] useAxisBinning (default=true) if set to true, try to extract a histogram with +/// proper binning and axis labels +/// +/// returns a new histogram. if histogramTitle is null, choose a title +/// automatically. + TH2 *TUnfoldDensity::GetL (const char *histogramName,const char *histogramTitle,Bool_t useAxisBinning) { - // return the matrix of regularisation conditions in a histogram - // input: - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) - // useAxisBinning: if true, try to use the axis bin widths - // on the x-axis of the output histogram if(fRegularisationConditions && (fRegularisationConditions->GetEndBin()- fRegularisationConditions->GetStartBin()!= fL->GetNrows())) { @@ -1045,19 +1224,24 @@ TH2 *TUnfoldDensity::GetL return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Get regularisation conditions multiplied by result vector minus bias +/// L(x-biasScale*biasVector). +/// +/// \param[in] histogramName name of the histogram +/// \param[in] histogramTitle (default=0) title of the histogram +/// +/// returns a new histogram. +/// This is a measure of the level of regularization required per +/// regularisation condition. +/// If there are (negative or positive) spikes, +/// these regularisation conditions dominate +/// over the other regularisation conditions and may introduce +/// the largest biases. + TH1 *TUnfoldDensity::GetLxMinusBias (const char *histogramName,const char *histogramTitle) { - // get regularisation conditions multiplied by result vector minus bias - // L(x-biasScale*biasVector) - // this is a measure of the level of regulartisation required per - // regularisation condition - // if there are (negative or positive) spikes, - // these regularisation conditions dominate - // over the other regularisation conditions - // input - // histogramName: name of the histogram - // histogramTitle: title of the histogram (could be zero) TMatrixD dx(*GetX(), TMatrixD::kMinus, fBiasScale * (*fX0)); TMatrixDSparse *Ldx=MultiplyMSparseM(fL,&dx); if(fRegularisationConditions && @@ -1086,6 +1270,14 @@ TH1 *TUnfoldDensity::GetLxMinusBias return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Locate a binning node for the input (measured) quantities. +/// +/// \param[in] distributionName (default=0) distribution to look +/// for. if zero, return the root node +/// +/// returns: pointer to a TUnfoldBinning object or zero if not found + const TUnfoldBinning *TUnfoldDensity::GetInputBinning (const char *distributionName) const { @@ -1094,6 +1286,14 @@ const TUnfoldBinning *TUnfoldDensity::GetInputBinning return fConstInputBins->FindNode(distributionName); } +//////////////////////////////////////////////////////////////////////////////// +/// Locate a binning node for the unfolded (truth level) quantities. +/// +/// \param[in] distributionName (default=0) distribution to look +/// for. if zero, return the root node +/// +/// returns: pointer to a TUnfoldBinning object or zero if not found + const TUnfoldBinning *TUnfoldDensity::GetOutputBinning (const char *distributionName) const { @@ -1102,28 +1302,49 @@ const TUnfoldBinning *TUnfoldDensity::GetOutputBinning return fConstOutputBins->FindNode(distributionName); } +//////////////////////////////////////////////////////////////////////////////// +/// Scan a function wrt tau and determine the minimum. +/// +/// \param[in] nPoint number of points to be scanned +/// \param[in] tauMin smallest tau value to study +/// \param[in] tauMax largest tau value to study +/// \param[out] scanResult the scanned function wrt log(tau) +/// \param[in] mode 1st parameter for the scan function +/// \param[in] distribution 2nd parameter for the scan function +/// \param[in] projectionMode 3rd parameter for the scan function +/// \param[out] lCurvePlot for monitoring, shows the L-curve +/// \param[out] logTauXPlot for monitoring, L-curve(X) as a function of log(tau) +/// \param[out] logTauYPlot for monitoring, L-curve(Y) as a function of log(tau) +/// +/// Return value: the coordinate number on the curve <b>scanResult</b> +/// which corresponds to the minimum +/// +/// The function is scanned by repeating the following steps <b>nPoint</b> +/// times +/// +/// 1. Choose a value of tau +/// 2. Perform the unfolding for this choice of tau, DoUnfold(tau) +/// 3. Determine the scan variable GetScanVariable() +/// +/// The method GetScanVariable() defines scans of correlation +/// coefficients, where <b>mode</b> is chosen from the enum +/// EScanTauMode. In addition one may set <b>distribution</b> +/// and/or <b>projectionMode</b> to refine the calculation of +/// correlations (e.g. restrict the calculation to the signal +/// distribution and/or exclude underflow and overflow bins). +/// See the documentation of GetScanVariable() for details. +/// Alternative scan variables may be defined by overriding the +/// GetScanVariable() method. +/// +/// Automatic choice of scan range: if (tauMin,tauMax) do not +/// correspond to a valid tau range (e.g. tauMin=tauMax=0.0) then +/// the tau range is determined automatically. Use with care! + Int_t TUnfoldDensity::ScanTau (Int_t nPoint,Double_t tauMin,Double_t tauMax,TSpline **scanResult, Int_t mode,const char *distribution,const char *axisSteering, TGraph **lCurvePlot,TSpline **logTauXPlot,TSpline **logTauYPlot) { - // scan some variable as a function of tau and determine the minimum - // input: - // nPoint: number of points to be scanned on the resulting curve - // tauMin: smallest tau value to study - // tauMax: largest tau value to study - // if (mauMin,tauMax) do not correspond to a valid tau range - // (e.g. tauMin=tauMax=0.0) then the tau range is determined - // automatically - // mode,distribution,axisSteering: argument to GetScanVariable() - // output: - // scanResult: output spline of the variable as a function of tau - // the following plots are produced on request (if pointers are non-zero) - // lCurvePlot: for monitoring: the L-curve - // logTauXPlot: for monitoring: L-curve(x) as a function of log(tau) - // logTauYPlot: for monitoring: L-curve(y) as a function of log(tau) - // return value: the coordinate number (0..nPoint-1) corresponding to the - // final choice of tau typedef std::map<Double_t,Double_t> TauScan_t; typedef std::map<Double_t,std::pair<Double_t,Double_t> > LCurve_t; TauScan_t curve; @@ -1153,7 +1374,7 @@ Int_t TUnfoldDensity::ScanTau if((tauMin<=0)||(tauMax<=0.0)||(tauMin>=tauMax)) { // here no range is given, has to be determined automatically // the maximum tau is determined from the chi**2 values - // observed from unfolding without regulatisation + // observed from unfolding without regularization // first unfolding, without regularisation DoUnfold(0.0); @@ -1302,11 +1523,11 @@ Int_t TUnfoldDensity::ScanTau xMin=cTi[i]; } // find minimum for x[i]<x<x[i+1] - // get spline coefficiencts and solve equation + // get spline coefficients and solve equation // derivative(x)==0 Double_t x,y,b,c,d; splineC->GetCoeff(i,x,y,b,c,d); - // coefficiencts of quadratic equation + // coefficients of quadratic equation Double_t m_p_half=-c/(3.*d); Double_t q=b/(3.*d); Double_t discr=m_p_half*m_p_half-q; @@ -1418,40 +1639,34 @@ Int_t TUnfoldDensity::ScanTau return bestChoice; } +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the function for ScanTau(). +/// +/// \param[in] mode the variable to be calculated +/// \param[in] distribution distribution for which the variable +/// is to be calculated +/// \param[in] axisSteering detailed steering for selecting bins on +/// the axes of the distribution (see method GetRhoItotal()) +/// +/// return value: the scan result for the given choice of tau (for +/// which the unfolding was performed prior to calling this method) +/// +/// In ScanTau() the unfolding is repeated for various choices of tau. +/// For each tau, after unfolding, GetScanVariable() is called to +/// determine the scan result for this choice of tau. +/// +/// the following modes are implemented +/// +/// - kEScanTauRhoAvg : average (stat+bgr) global correlation +/// - kEScanTauRhoSquaredAvg : average (stat+bgr) global correlation squared +/// - kEScanTauRhoMax : maximum (stat+bgr) global correlation +/// - kEScanTauRhoAvgSys : average (stat+bgr+sys) global correlation +/// - kEScanTauRhoAvgSquaredSys : average (stat+bgr+sys) global correlation squared +/// - kEScanTauRhoMaxSys : maximum (stat+bgr+sys) global correlation + Double_t TUnfoldDensity::GetScanVariable (Int_t mode,const char *distribution,const char *axisSteering) { - // calculate variable for ScanTau() - // the unfolding is repeated for various choices of tau. - // For each tau, after unfolding, the ScanTau() method calls - // GetScanVariable() to determine the value of the variable which - // is to be scanned - // - // the variable is expected to have a minimum near the "optimal" choice - // of tau - // - // input: - // mode : define the type of variable to be calculated - // distribution : define the distribution for which the variable - // is to be calculated - // the following modes are implemented: - // kEScanTauRhoAvg : average global correlation from input data - // kEScanTauRhoSquaredAvg : average global correlation squared - // from input data - // kEScanTauRhoMax : maximum global correlation from input data - // kEScanTauRhoAvgSys : average global correlation - // including systematic uncertainties - // kEScanTauRhoAvgSquaredSys : average global correlation squared - // including systematic uncertainties - // kEScanTauRhoMaxSys : maximum global correlation - // including systematic uncertainties - // distribution : name of the TUnfoldBinning node - // for which to calculate the correlations - // axisSteering : axis steering for calculating the correlations - // the distribution - // return: the value of the variable as determined from the present - // unfolding - Double_t r=0.0; TString name("GetScanVariable("); name += TString::Format("%d,",mode); diff --git a/hist/hist/src/TUnfoldSys.cxx b/hist/unfold/src/TUnfoldSys.cxx similarity index 52% rename from hist/hist/src/TUnfoldSys.cxx rename to hist/unfold/src/TUnfoldSys.cxx index 467254335e781840b6028878a9e54b5199a00d87..d233e3c6b2149dc08f5bc980f84146a6b190e907 100644 --- a/hist/hist/src/TUnfoldSys.cxx +++ b/hist/unfold/src/TUnfoldSys.cxx @@ -1,111 +1,95 @@ -// Author: Stefan Schmitt -// DESY, 23/01/09 - -// Version 17.1, bug fix with background uncertainty -// -// History: -// Version 17.0, possibility to specify an error matrix with SetInput -// Version 16.1, parallel to changes in TUnfold -// Version 16.0, parallel to changes in TUnfold -// Version 15, fix bugs with uncorr. uncertainties, add backgnd subtraction -// Version 14, remove some print-out, do not add unused sys.errors -// Version 13, support for systematic errors +// @(#)root/unfold:$Id$ +// Author: Stefan Schmitt DESY, 23/01/09 /** \class TUnfoldSys - \ingroup Hist - TUnfold is used to decompose a measurement y into several sources x - given the measurement uncertainties and a matrix of migrations A - - TUnfoldSys adds error propagation of systematic errors to TUnfold - Also, background sources (with errors) can be subtracted. - - **For most applications, it is better to use TUnfoldDensity - instead of using TUnfoldSys or TUnfold** - - If you use this software, please consider the following citation - S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201] - - More documentation and updates are available on - http://www.desy.de/~sschmitt - - The following sources of systematic error are considered in TUnfoldSys - - (a) uncorrelated errors on the input matrix histA, taken as the - errors provided with the histogram. - These are typically statistical errors from finite Monte Carlo samples - - (b) correlated shifts of the input matrix histA. These shifts are taken - as one-sigma effects when switchig on a given error soure. - several such error sources may be defined - - (c) a systematic error on the regularisation parameter tau - - (d) uncorrelated errors on background sources, taken as the errors - provided with the background histograms - - (e) scale errors on background sources - - In addition there is the (statistical) uncertainty of the input vector (i) - - Source (a) is providede with the original histogram histA - TUnfoldSys(histA,...) - - Sources (b) are added by calls to - AddSysError() - - The systematic uncertainty on tau (c) is set by - SetTauError() - - Backgound sources causing errors of type (d) and (e) are added by - SubtractBackground() - - NOTE: - Systematic errors (a), (b), (c) are propagated to the result - AFTER unfolding - - Background errors (d) and (e) are added to the data errors - BEFORE unfolding - - For this reason: - errors of type (d) and (e) are INCLUDED in the standard error matrix - and other methods provided by the base class TUnfold: - GetOutput() - GetEmatrix() - ... - whereas errors of type (a), (b), (c) are NOT INCLUDED in the methods - provided by the base class TUnfold. - - ## Accessing error matrices: - The error sources (b),(c) and (e) propagate to shifts of the result. - These shifts may be accessed as histograms using the methods - GetDeltaSysSource() corresponds to (b) - GetDeltaSysTau() corresponds to (c) - GetDeltaSysBackgroundScale() corresponds to (e) - The error sources (a) and (d) originate from many uncorrelated errors, - which in general are NOT uncorrelated on the result vector. - Thus, there is no corresponding shift of the output vector, only error - matrices are available - - Method to get error matrix | corresponds to error sources - -----------------------------|--------------------------------- - GetEmatrixSysUncorr() | (a) - GetEmatrixSysSource() | (b) - GetEmatrixSysTau() | (c) - GetEmatrixSysBackgroundUncorr() | (d) - GetEmatrixSysBackgroundScale() | (e) - GetEmatrixInput() | (i) - GetEmatrix() | (i)+(d)+(e) - GetEmatrixTotal() | (i)+(a)+(b)+(c)+(d)+(e) - - Error matrices can be added to existing histograms. - This is useful to retreive the sum of several error matrices. - If the last argument of the GetEmatrixXXX() methods is set to kFALSE, - the histogram is not cleared, but the error matrix is simply added to the - existing histogram -*/ - -/* - This file is part of TUnfold. +\ingroup Unfold +An algorithm to unfold distributions from detector to truth level, +with background subtraction and propagation of systematic uncertainties + +TUnfoldSys is used to decompose a measurement y into several sources x, +given the measurement uncertainties, background b and a matrix of migrations A. +The method can be applied to a large number of problems, +where the measured distribution y is a linear superposition +of several Monte Carlo shapes. Beyond such a simple template fit, +TUnfoldSys has an adjustable regularisation term and also supports an +optional constraint on the total number of events. +Background sources can be specified, with a normalisation constant and +normalisation uncertainty. In addition, variants of the response +matrix may be specified, these are taken to determine systematic +uncertainties. + +<b>For most applications, it is better to use the derived class +TUnfoldDensity instead of TUnfoldSys. TUnfoldDensity adds +features to TUnfoldSys, related to possible complex multidimensional +arrangements of bins. For innocent +users, the most notable improvement of TUnfoldDensity over TUnfoldSys are +the getter functions. For TUnfoldSys, histograms have to be booked by the +user and the getter functions fill the histogram bins. TUnfoldDensity +simply returns a new, already filled histogram.</b> + +If you use this software, please consider the following citation + +<b>S.Schmitt, JINST 7 (2012) T10003 [arXiv:1205.6201]</b> + +Detailed documentation and updates are available on +http://www.desy.de/~sschmitt + +Brief recipy to use TUnfoldSys: + + - a matrix (truth,reconstructed) is given as a two-dimensional histogram + as argument to the constructor of TUnfold + - a vector of measurements is given as one-dimensional histogram using + the SetInput() method + - repeated calls to SubtractBackground() to specify background sources + - repeated calls to AddSysError() to specify systematic uncertainties + - The unfolding is performed + - either once with a fixed parameter tau, method DoUnfold(tau) + - or multiple times in a scan to determine the best chouce of tau, + method ScanLCurve() + - Unfolding results are retrieved using various GetXXX() methods + + +Description of (systematic) uncertainties available in +TUnfoldSys. There are covariance matrix contributions and there are +systematic shifts. Systematic shifts correspond to the variation of a +(buicance) parameter, for example a background normalisation or a +one-sigma variation of a correlated systematic error. + +| | Set by | Access covariance matrix | Access vector of shifts | Description | +|-------------------------|------------------------|---------------------------------|------------------------------|-------------| +| (a) | TUnfoldSys constructor | GetEmatrixSysUncorr() | n.a. | uncorrelated errors on the input matrix histA, taken as the errors provided with the histogram. These are typically statistical errors from finite Monte Carlo samples. | +| (b) | AddSysError() | GetEmatrixSysSource() | GetDeltaSysSource() | correlated shifts of the input matrix histA. These shifts are taken as one-sigma effects when switchig on a given error soure. Several such error sources may be defined | +| (c) | SetTauError() | GetEmatrixSysTau() | GetDeltaSysTau() | A systematic error on the regularisation parameter tau | +| (d) | SubtractBackground() | GetEmatrixSysBackgroundUncorr() | n.a. | uncorrelated errors on background sources, originating from the errors provided with the background histograms | +| (e) | SubtractBackground() | GetEmatrixSysBackgroundScale() | GetDeltaSysBackgroundScale() | scale errors on background sources | +| (i) | SetInput() | GetEmatrixInput() | n.a. | statistical uncertainty of the input (the measurement) | +| (i)+(d)+(e) | see above | GetEmatrix() | n.a. | Partial sun of uncertainties: all sources which are propagated to the covariance before unfolding | +| (i)+(a)+(b)+(c)+(d)+(e) | see above | GetEmatrixTotal() | n.a. | All known error sources summed up | + + +Note: (a), (b), (c) are propagated to the result AFTER unfolding, +whereas the background errors (d) and (e) are added to the data errors +BEFORE unfolding. For this reason the errors of type (d) and (e) are +INCLUDED in the standard error matrix and other methods provided by +the base class TUnfold, whereas errors of type (a), (b), (c) are NOT +INCLUDED in the methods provided by the base class TUnfold. + + +-------------------------------------------------------------------------------- +<b>Version 17.6, with updated doxygen comments</b> + +#### History: + - Version 17.5, in parallel to changes in TUnfold + - Version 17.4, in parallel to changes in TUnfoldBinning + - Version 17.3, in parallel to changes in TUnfoldBinning + - Version 17.2, add methods to find back systematic and background sources + - Version 17.1, bug fix with background uncertainty + - Version 17.0, possibility to specify an error matrix with SetInput + - Version 16.1, parallel to changes in TUnfold + - Version 16.0, parallel to changes in TUnfold + - Version 15, fix bugs with uncorr. uncertainties, add backgnd subtraction + - Version 14, remove some print-out, do not add unused sys.errors + - Version 13, support for systematic errors This file is part of TUnfold. TUnfold is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -125,6 +109,7 @@ #include <TMap.h> #include <TMath.h> #include <TObjString.h> +#include <TSortedList.h> #include <RVersion.h> #include <cmath> @@ -132,29 +117,57 @@ ClassImp(TUnfoldSys) +TUnfoldSys::~TUnfoldSys(void) +{ + // delete all data members + DeleteMatrix(&fDAinRelSq); + DeleteMatrix(&fDAinColRelSq); + delete fBgrIn; + delete fBgrErrUncorrInSq; + delete fBgrErrScaleIn; + delete fSysIn; + ClearResults(); + delete fDeltaCorrX; + delete fDeltaCorrAx; + DeleteMatrix(&fYData); + DeleteMatrix(&fVyyData); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Only for use by root streamer or derived classes. + TUnfoldSys::TUnfoldSys(void) { // set all pointers to zero InitTUnfoldSys(); } +//////////////////////////////////////////////////////////////////////////////// +/// Set up response matrix A, uncorrelated uncertainties of A and +/// regularisation scheme. +/// +/// \param[in] hist_A matrix that describes the migrations +/// \param[in] histmap mapping of the histogram axes to the unfolding output +/// \param[in] regmode (default=kRegModeSize) global regularisation mode +/// \param[in] constraint (default=kEConstraintArea) type of constraint +/// +/// For further details, consult the constructir of the class TUnfold. +/// The uncertainties of hist_A are taken to be uncorrelated and aper +/// propagated to the unfolding result, method GetEmatrixSysUncorr(). + TUnfoldSys::TUnfoldSys (const TH2 *hist_A, EHistMap histmap, ERegMode regmode,EConstraint constraint) : TUnfold(hist_A,histmap,regmode,constraint) { - // arguments: - // hist_A: matrix that describes the migrations - // histmap: mapping of the histogram axes to the unfolding output - // regmode: global regularisation mode // data members initialized to something different from zero: // fDA2, fDAcol - // initialize TUnfold + // initialize TUnfoldSys InitTUnfoldSys(); - // svae underflow and overflow bins + // save underflow and overflow bins fAoutside = new TMatrixD(GetNx(),2); - // save the romalized errors on hist_A + // save the normalized errors on hist_A // to the matrices fDAinRelSq and fDAinColRelSq fDAinColRelSq = new TMatrixD(GetNx(),1); @@ -213,21 +226,36 @@ TUnfoldSys::TUnfoldSys delete[] dataDAinRelSq; } +//////////////////////////////////////////////////////////////////////////////// +/// Specify a correlated systematic uncertainty. +/// +/// \param[in] sysError alternative matrix or matrix of absolute/relative shifts +/// \param[in] name identifier of the error source +/// \param[in] histmap mapping of the histogram axes +/// \param[in] mode format of the error source +/// +/// <b>sysError</b> corresponds to a one-sigma variation. If +/// may be given in various forms, specified by <b>mode</b> +/// +/// - <b>mode=kSysErrModeMatrix</b> the histogram <b>sysError</b> +/// corresponds to an alternative response matrix. +/// - <b>mode=kSysErrModeShift</b> the content of the histogram <b>sysError</b> are the absolute shifts of the response matrix +/// - <b>mode=kSysErrModeRelative</b> the content of the histogram <b>sysError</b> +/// specifies the relative uncertainties +/// +/// Internally, all three cases are transformed to the case <b>mode=kSysErrModeMatrix</b>. + void TUnfoldSys::AddSysError (const TH2 *sysError,const char *name,EHistMap histmap,ESysErrMode mode) { - // add a correlated error source - // sysError: alternative matrix or matrix of absolute/relative shifts - // name: name of the error source - // histmap: mapping of the histogram axes to the unfolding output - // mode: format of the error source if(fSysIn->FindObject(name)) { Error("AddSysError","Source %s given twice, ignoring 2nd call.\n",name); } else { // a copy of fA is made. It can be accessed inside the loop // without having to take care that the sparse structure of *fA - // may be accidentally destroyed by asking for an element which is zero. + // otherwise, *fA may be accidentally destroyed by asking + // for an element which is zero. TMatrixD aCopy(*fA); Int_t nmax= GetNx()*GetNy(); @@ -297,9 +325,14 @@ void TUnfoldSys::AddSysError } } +//////////////////////////////////////////////////////////////////////////////// +/// Perform background subtraction. +/// +/// This prepares the data members for the base class TUnfold, such +/// that the background is properly taken into account. + void TUnfoldSys::DoBackgroundSubtraction(void) { - // performs background subtraction // fY = fYData - fBgrIn // fVyy = fVyyData + fBgrErrUncorr^2 + fBgrErrCorr * fBgrErrCorr# // fVyyinv = fVyy^(-1) @@ -407,24 +440,32 @@ void TUnfoldSys::DoBackgroundSubtraction(void) } } +//////////////////////////////////////////////////////////////////////////////// +/// Define the input data for subsequent calls to DoUnfold(Double_t). +/// +/// input: input distribution with errors +/// - scaleBias: scale factor applied to the bias +/// - oneOverZeroError: for bins with zero error, this number defines 1/error. +/// +/// Return value: number of bins with bad error +/// +10000*number of unconstrained output bins +/// +/// Note: return values>=10000 are fatal errors, +/// for the given input, the unfolding can not be done! +/// +/// Calls the SetInput method of the base class, then renames the input +/// vectors fY and fVyy, then performs the background subtraction +/// +/// Data members modified: +/// fYData,fY,fVyyData,fVyy,fVyyinvData,fVyyinv +/// +/// and those modified by TUnfold::SetInput() +/// and those modified by DoBackgroundSubtraction() + Int_t TUnfoldSys::SetInput(const TH1 *hist_y,Double_t scaleBias, Double_t oneOverZeroError,const TH2 *hist_vyy, const TH2 *hist_vyy_inv) { - // Define the input data for subsequent calls to DoUnfold(Double_t) - // input: input distribution with errors - // scaleBias: scale factor applied to the bias - // oneOverZeroError: for bins with zero error, this number defines 1/error. - // Return value: number of bins with bad error - // +10000*number of unconstrained output bins - // Note: return values>=10000 are fatal errors, - // for the given input, the unfolding can not be done! - // Calls the SetInput method of the base class, then renames the input - // vectors fY and fVyy, then performs the background subtraction - // Data members modified: - // fYData,fY,fVyyData,fVyy,fVyyinvData,fVyyinv - // and those modified by TUnfold::SetInput() - // and those modified by DoBackgroundSubtraction() Int_t r=TUnfold::SetInput(hist_y,scaleBias,oneOverZeroError,hist_vyy, hist_vyy_inv); @@ -437,18 +478,31 @@ Int_t TUnfoldSys::SetInput(const TH1 *hist_y,Double_t scaleBias, return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Specify a source of background. +/// +/// \param[in] bgr background distribution with uncorrelated errors +/// \param[in] name identifier for this background source +/// \param[in] scale normalisation factor applied to the background +/// \param[in] scaleError normalisation uncertainty +/// +/// The contribution <b>scale</b>*<b>bgr</b> is subtracted from the +/// measurement prior to unfolding. The following contributions are +/// added to the input covarianc ematrix +/// +/// - using the uncorrelated histogram errors <b>dbgr</b>, the contribution +/// (<b>scale</b>*<b>dbgr<sub>i</sub></b>)<sup>2</sup> is added to the +/// diagonals of the covariance +/// - using the histogram contents, the background normalisation uncertainty contribution +/// <b>dscale</b>*<b>bgr<sub>i</sub></b> <b>dscale</b>*<b>bgr<sub>j</sub></b> +/// is added to the covariance matrix +/// +/// Data members modified: +/// fBgrIn,fBgrErrUncorrInSq,fBgrErrScaleIn and those modified by DoBackgroundSubtraction() + void TUnfoldSys::SubtractBackground (const TH1 *bgr,const char *name,Double_t scale,Double_t scale_error) { - // Store background source - // bgr: background distribution with uncorrelated errors - // name: name of this background source - // scale: scale factor applied to the background - // scaleError: error on scale factor (correlated error) - // - // Data members modified: - // fBgrIn,fBgrErrUncorrInSq,fBgrErrScaleIn - // and those modified by DoBackgroundSubtraction() // save background source if(fBgrIn->FindObject(name)) { @@ -476,6 +530,23 @@ void TUnfoldSys::SubtractBackground } } +//////////////////////////////////////////////////////////////////////////////// +/// Get background into a histogram. +/// +/// \param[inout] bgrHist target histogram, content and errors will be altered +/// \param[in] bgrSource (default=0) name of backgrond source or zero +/// to add all sources of background +/// \param[in] binMap (default=0) remap histogram bins +/// \param[in] includeError (default=3) include uncorrelated(1), +/// correlated (2) or both (3) sources of uncertainty in the +/// histogram errors +/// \param[in] clearHist (default=true) reset histogram before adding +/// up the specified background sources +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearHist</b> may be used to add background from +/// several sources in successive calls to GetBackground(). + void TUnfoldSys::GetBackground (TH1 *bgrHist,const char *bgrSource,const Int_t *binMap, Int_t includeError,Bool_t clearHist) const @@ -548,10 +619,11 @@ void TUnfoldSys::GetBackground } } +//////////////////////////////////////////////////////////////////////////////// +/// Initialize pointers and TMaps. + void TUnfoldSys::InitTUnfoldSys(void) { - // initialize pointers and TMaps - // input fDAinRelSq = 0; fDAinColRelSq = 0; @@ -589,25 +661,11 @@ void TUnfoldSys::InitTUnfoldSys(void) fVyyData=0; } -TUnfoldSys::~TUnfoldSys(void) -{ - // delete all data members - DeleteMatrix(&fDAinRelSq); - DeleteMatrix(&fDAinColRelSq); - delete fBgrIn; - delete fBgrErrUncorrInSq; - delete fBgrErrScaleIn; - delete fSysIn; - ClearResults(); - delete fDeltaCorrX; - delete fDeltaCorrAx; - DeleteMatrix(&fYData); - DeleteMatrix(&fVyyData); -} +//////////////////////////////////////////////////////////////////////////////// +/// Clear all data members which depend on the unfolding results. void TUnfoldSys::ClearResults(void) { - // clear all data members which depend on the unfolding results TUnfold::ClearResults(); DeleteMatrix(&fEmatUncorrX); DeleteMatrix(&fEmatUncorrAx); @@ -616,11 +674,14 @@ void TUnfoldSys::ClearResults(void) DeleteMatrix(&fDeltaSysTau); } +//////////////////////////////////////////////////////////////////////////////// +/// Matrix calculations required to propagate systematic errors. +/// +/// data members modified: +/// fEmatUncorrX, fEmatUncorrAx, fDeltaCorrX, fDeltaCorrAx + void TUnfoldSys::PrepareSysError(void) { - // calculations required for syst.error - // data members modified - // fEmatUncorrX, fEmatUncorrAx, fDeltaCorrX, fDeltaCorrAx if(!fEmatUncorrX) { fEmatUncorrX=PrepareUncorrEmat(GetDXDAM(0),GetDXDAM(1)); } @@ -700,117 +761,145 @@ void TUnfoldSys::PrepareSysError(void) DeleteMatrix(&AM1); } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance contribution from uncorrelated uncertainties of the +/// response matrix. +/// +/// \param[inout] ematrix covariance matrix histogram +/// \param[in] binMap mapping of histogram bins +/// \param[in] clearEmat if true, ematrix is cleared prior to adding +/// this covariance matrix contribution +/// +/// This method propagates the uncertainties of the response matrix +/// histogram, specified with the constructor, to the unfolding +/// result. It is assumed that the entries of that histogram are +/// bin-to-bin uncorrelated. In many cases this corresponds to the +/// "Monte Carlo statistical uncertainties". +/// +/// The array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. +/// +/// data members modified: +/// fVYAx, fESparse, fEAtV, fErrorAStat + void TUnfoldSys::GetEmatrixSysUncorr (TH2 *ematrix,const Int_t *binMap,Bool_t clearEmat) { - // get output error contribution from statistical fluctuations in A - // ematrix: output error matrix histogram - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors - // data members modified: - // fVYAx, fESparse, fEAtV, fErrorAStat PrepareSysError(); ErrorMatrixToHist(ematrix,fEmatUncorrX,binMap,clearEmat); } +//////////////////////////////////////////////////////////////////////////////// +/// Propagate uncorrelated systematic errors to a covariance matrix. +/// +/// \param[in] m_0 coefficients for error propagation +/// \param[in] m_1 coefficients for error propagation +/// +/// Returns the covariance matrix, propagates uncorrelated systematic errors to +/// a covariance matrix. m_0,m_1 are the coefficients (matrices) for propagating +/// the errors. +/// +/// The error matrix is calculated by standard error propagation, where the +/// derivative of the result vector X wrt the matrix A is given by: +/// +/// \f[ \frac{dX_k}{dA_{ij}} = M0_{kj} Z0_i - M1_{ki} Z1_j \f] +/// +/// where: +// +/// the matrices M0 and M1 are arguments to this function +/// the vectors Z0, Z1 : GetDXDAZ() +/// +/// The matrix A is calculated from a matrix B as +/// +/// \f[ A_{ij} = \frac{B_{ij}}{\sum_k B_{kj}} \f] +/// +/// where k runs over additional indices of B, not present in A. +/// (underflow and overflow bins, used for efficiency corrections) +/// +/// define: \f$ Norm_j = \sum_k B_{kj} \f$ (data member fSumOverY) +/// +/// the derivative of A wrt this input matrix B is given by: +/// +/// \f[ \frac{dA_{ij}}{dB_{kj}} = (\delta_{ik} - A_{ij} ) \frac{1}{Norm_j} \f] +/// +/// The covariance matrix Vxx is: +/// +/// \f[ Vxx_{mn} = \sum_{ijlk} \big[ (\frac{dX_m}{dA_{ij}}) (\frac{dA_{ij}}{dB_{}kj}) DB_{kj} (\frac{dX_n}{dA_{lj}}) (\frac{dA_{lj}}{dB_{kj}}) \big] \f] +/// +/// where \f$ DB_{kj} \f$ is the error on \f$ B_{kj} \f$ squared. +/// +/// Simplify the sum over k: +/// +/// \f[ \sum_k \big[ (\frac{dA_{ij}}{dB_{kj}}) DB_{kj} (\frac{dA_{lj}}{dB_{kj}}) \big] +/// = \sum_k \big[ (\delta_{ik} - A_{ij} ) \frac{1}{Norm_j} DB_{kj} (\delta_{lk} - A_{lj} ) \frac{1}{Norm_j} \big] +/// = \sum_k \big[ (\delta_{ik} \delta_{lk} - \delta_{ik} A_{lj} - \delta_{lk} A_{ij} + A_{ij} A_{lj} ) \frac{DB_{kj}}{Norm_j^2} \big] \f] +/// +/// introduce normalized errors: \f$ Rsq_{kj} = \frac{DB_{kj}}{Norm_j^2} \f$ +/// +/// after summing over k: +/// \f[ \delta_{ik} \delta_{lk} Rsq_{kj} \to \delta_{il} Rsq_{ij} \f] +/// \f[ \delta_{ik} A_{lj} Rsq_{kj} \to A_{lj} Rsq_{ij} \f] +/// \f[ \delta_{lk} A_{ij} Rsq_{kj} \to A_{ij} Rsq_{lj} \f] +/// \f[ A_{ij} A_{lj} Rsq_{kj} \to A_{ij} A_{lj} \sum_k(Rsq_{kj}) \f] +/// +/// introduce sum of normalized errors squared: \f$ SRsq_j = \sum_k(Rsq_{kj}) \f$ +/// +/// Note: \f$ Rsq_{ij} \f$ is stored as `fDAinRelSq` (excludes extra indices of B) +/// and \f$ SRsq_j \f$ is stored as `fDAinColRelSq` (sum includes all indices of B) +/// +/// \f[ Vxx_{nm} = \sum_{ijl} \big[ (\frac{dX_m}{dA_{ij}}) (\frac{dX_n}{dA_{lj}}) +/// (\delta_{il} Rsq_{ij} - A_{lj} Rsq_{ij} - A_{ij} Rsq_{lj} + A_{ij} A_{lj} SRsq_j) \big] \f] +/// +/// \f[ Vxx_nm = \sum_j \big[ F_{mj} F_{nj} SRsq_j \big] +/// - \sum_j \big[ G_{mj} F_{nj} \big] +/// - \sum_j \big[ F_{mj} G_{nj} \big] +/// + \sum_{ij} \big[ (\frac{dX_m}{dA_{ij}}) (\frac{dX_n}{dA_{lj}}) Rsq_{ij} \big] \f] +/// +/// where: +/// +/// \f[ F_{mj} = \sum_i \big[ (\frac{dX_m}{dA_{ij}}) * A_{ij} \big] \f] +/// \f[ G_{mj} = \sum_i \big[ (\frac{dX_m}{dA_{ij}}) Rsq_{ij} \big] \f] +/// +/// In order to avoid explicitly calculating the 3-dimensional tensor +/// \f$(\frac{dX_m}{dA_{ij}}) \f$ the sums are evaluated further, using: +/// +/// \f[ \frac{dX_k}{dA_{ij}} = M0_{kj} Z0_i - M1_{ki} Z1_j \f] +/// \f[ F_{mj} = M0_{mj} * (A\# Z0)_j - (M1 A)_{mj} Z1_j \f] +/// \f[ G_{mj} = M0_{mj} * (Rsq\# Z0)_j - (M1 Rsq)_{mj} Z1_j \f] +/// +/// and +/// +/// \f[ \sum_{ij} \big[ (\frac{dX_m}{dA_{ij}}) (\frac{dX_n}{dA_{ij}}) Rsq_{ij} \big] = +/// \sum_j \big[ M0_{mj} M0_nj \big[ \sum_i (Z0_i)^2 Rsq_{ij} \big] \big] +/// + \sum_i \big[ M1_{mi} M1_{ni} \big[ \sum_j (Z1_j)^2 Rsq_{ij} \big] \big] +/// - \sum_i \big[ M1_{mi} H_{ni} + M1_{ni} H_{mi} \big] \f] +/// +/// where: +/// +/// \f[ H_{mi} = Z0_i \sum_j \big[ M0_{mj} Z1_j Rsq_{ij} \big] \f] +/// +/// collect all contributions: +/// +/// \f[ Vxx_nm = r0 -r1 -r2 +r3 +r4 -r5 -r6 \f] +/// \f[ r0 = \sum_j \big[ F_{mj} F_nj * SRsq_j \big] \f] +/// \f[ r1 = \sum_j \big[ G_{mj} F_nj \big] \f] +/// \f[ r2 = \sum_j \big[ F_{mj} G_nj \big] \f] +/// \f[ r3 = \sum_j \big[ M0_{mj} M0_nj \big[ \sum_i (Z0_i)^2 Rsq_{ij} \big] \big] \f] +/// \f[ r4 = \sum_i \big[ M1_{mi} M1_{ni} \big[ \sum_j (Z1_j)^2 Rsq_{ij} \big] \big] \f] +/// \f[ r5 = \sum_i \big[ M1_{mi} H_{ni} \big] \f] +/// \f[ r6 = \sum_i \big[ M1_{ni} H_{mi} \big] \f] + TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat (const TMatrixDSparse *m_0,const TMatrixDSparse *m_1) { - // propagate uncorrelated systematic errors to a covariance matrix - // m0,m1 : coefficients (matrices) for propagating the errors - // - // the error matrix is calculated by standard error propagation, where the - // derivative of the result vector X wrt the matrix A is given by - // - // dX_k / dA_ij = M0_kj * Z0_i - M1_ki * Z1_j - // - // where: - // the matrices M0 and M1 are arguments to this function - // the vectors Z0, Z1 : GetDXDAZ() - // - // The matrix A is calculated from a matrix B as - // - // A_ij = B_ij / sum_k B_kj - // - // where k runs over additional indices of B, not present in A. - // (underflow and overflow bins, used for efficiency corrections) - // - // define: Norm_j = sum_k B_kj (data member fSumOverY) - // - // the derivative of A wrt this input matrix B is given by: - // - // dA_ij / dB_kj = ( delta_ik - A_ij ) * 1/Norm_j - // - // The covariance matrix Vxx is: - // - // Vxx_mn = sum_ijlk [ (dX_m / dA_ij) * (dA_ij / dB_kj) * DB_kj - // * (dX_n / dA_lj) * (dA_lj / dB_kj) ] - // - // where DB_kj is the error on B_kj squared - // Simplify the sum over k: - // - // sum_k [ (dA_ij / dB_kj) * DB_kj * (dA_lj / dB_kj) ] - // = sum_k [ ( delta_ik - A_ij ) * 1/Norm_j * DB_kj * - // * ( delta_lk - A_lj ) * 1/Norm_j ] - // = sum_k [ ( delta_ik*delta_lk - delta_ik*A_lj - delta_lk*A_ij - // + A_ij * A_lj ) * DB_kj / Norm_j^2 ] - // - // introduce normalized errors: Rsq_kj = DB_kj / Norm_j^2 - // after summing over k: - // delta_ik*delta_lk*Rsq_kj -> delta_il*Rsq_ij - // delta_ik*A_lj*Rsq_kj -> A_lj*Rsq_ij - // delta_lk*A_ij*Rsq_kj -> A_ij*Rsq_lj - // A_ij*A_lj*Rsq_kj -> A_ij*A_lj*sum_k(Rsq_kj) - // - // introduce sum of normalized errors squared: SRsq_j = sum_k(Rsq_kj) - // - // Note: Rsq_ij is stored as fDAinRelSq (excludes extra indices of B) - // and SRsq_j is stored as fDAinColRelSq (sum includes all indices of B) - // - // Vxx_nm = sum_ijl [ (dX_m / dA_ij) * (dX_n / dA_lj) - // (delta_il*Rsq_ij - A_lj*Rsq_ij - A_ij*Rsq_lj + A_ij*A_lj *SRsq_j) ] - // - // Vxx_nm = sum_j [ F_mj * F_nj * SRsq_j - // - sum_j [ G_mj * F_nj ] - // - sum_j [ F_mj * G_nj ] - // + sum_ij [ (dX_m / dA_ij) * (dX_n / dA_lj) * Rsq_ij ] - // - // where: - // F_mj = sum_i [ (dX_m / dA_ij) * A_ij ] - // G_mj = sum_i [ (dX_m / dA_ij) * Rsq_ij ] - // - // In order to avoid explicitly calculating the 3-dimensional tensor - // (dX_m/dA_ij) the sums are evaluated further, using - // dX_k / dA_ij = M0_kj * Z0_i - M1_ki * Z1_j - // - // F_mj = M0_mj * (A# Z0)_j - (M1 A)_mj Z1_j - // G_mj = M0_mj * (Rsq# Z0)_j - (M1 Rsq)_mj Z1_j - // - // and - // - // sum_ij [ (dX_m/dA_ij) * (dX_n/dA_ij) * Rsq_ij ] = - // sum_j [ M0_mj * M0_nj * [ sum_i (Z0_i)^2 * Rsq_ij ] ] - // + sum_i [ M1_mi * M1_ni * [ sum_j (Z1_j)^2 * Rsq_ij ] ] - // - sum_i [ M1_mi * H_ni + M1_ni * H_mi] - // where: - // H_mi = Z0_i * sum_j [ M0_mj * Z1_j * Rsq_ij ] - // - // collect all contributions: - // Vxx_nm = r0 -r1 -r2 +r3 +r4 -r5 -r6 - // r0 = sum_j [ F_mj * F_nj * SRsq_j ] - // r1 = sum_j [ G_mj * F_nj ] - // r2 = sum_j [ F_mj * G_nj ] - // r3 = sum_j [ M0_mj * M0_nj * [ sum_i (Z0_i)^2 * Rsq_ij ] ] - // r4 = sum_i [ M1_mi * M1_ni * [ sum_j (Z1_j)^2 * Rsq_ij ] ] - // r5 = sum_i [ M1_mi * H_ni ] - // r6 = sum_i [ M1_ni * H_mi ] //====================================================== // calculate contributions containing matrices F and G // r0,r1,r2 TMatrixDSparse *r=0; if(fDAinColRelSq && fDAinRelSq) { - // calculate matrices (M1*A)_mj * Z1_j and (M1*Rsq)_mj * Z1_j + // calculate matrices (M1*A)_{mj} * Z1_j and (M1*Rsq)_{mj} * Z1_j TMatrixDSparse *M1A_Z1=MultiplyMSparseMSparse(m_1,fA); ScaleColumnsByVector(M1A_Z1,GetDXDAZ(1)); TMatrixDSparse *M1Rsq_Z1=MultiplyMSparseMSparse(m_1,fDAinRelSq); @@ -820,12 +909,12 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat TMatrixDSparse *RsqZ0= MultiplyMSparseTranspMSparse(fDAinRelSq,GetDXDAZ(0)); //calculate matrix F - // F_mj = M0_mj * (A# Z0)_j - (M1 A)_mj Z1_j + // F_{mj} = M0_{mj} * (A# Z0)_j - (M1 A)_{mj} Z1_j TMatrixDSparse *F=new TMatrixDSparse(*m_0); ScaleColumnsByVector(F,AtZ0); AddMSparse(F,-1.0,M1A_Z1); //calculate matrix G - // G_mj = M0_mj * (Rsq# Z0)_j - (M1 Rsq)_mj Z1_j + // G_{mj} = M0_{mj} * (Rsq# Z0)_j - (M1 Rsq)_{mj} Z1_j TMatrixDSparse *G=new TMatrixDSparse(*m_0); ScaleColumnsByVector(G,RsqZ0); AddMSparse(G,-1.0,M1Rsq_Z1); @@ -833,11 +922,11 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat DeleteMatrix(&M1Rsq_Z1); DeleteMatrix(&AtZ0); DeleteMatrix(&RsqZ0); - // r0 = sum_j [ F_mj * F_nj * SRsq_j ] + // r0 = \sum_j [ F_{mj} * F_nj * SRsq_j ] r=MultiplyMSparseMSparseTranspVector(F,F,fDAinColRelSq); - // r1 = sum_j [ G_mj * F_nj ] + // r1 = \sum_j [ G_{mj} * F_nj ] TMatrixDSparse *r1=MultiplyMSparseMSparseTranspVector(F,G,0); - // r2 = sum_j [ F_mj * G_nj ] + // r2 = \sum_j [ F_{mj} * G_nj ] TMatrixDSparse *r2=MultiplyMSparseMSparseTranspVector(G,F,0); // r = r0-r1-r2 AddMSparse(r,-1.0,r1); @@ -849,7 +938,7 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat } //====================================================== // calculate contribution - // sum_ij [ (dX_m/dA_ij) * (dX_n/dA_ij) * Rsq_ij ] + // \sum_{ij} [ (dX_m/dA_{ij}) * (dX_n/dA_{ij}) * Rsq_{ij} ] // (r3,r4,r5,r6) if(fDAinRelSq) { // (Z0_i)^2 @@ -859,9 +948,9 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat for(int index=0;index<Z0sq_rows[Z0sq.GetNrows()];index++) { Z0sq_data[index] *= Z0sq_data[index]; } - // Z0sqRsq = sum_i (Z_i)^2 * Rsq_ij + // Z0sqRsq = \sum_i (Z_i)^2 * Rsq_{ij} TMatrixDSparse *Z0sqRsq=MultiplyMSparseTranspMSparse(fDAinRelSq,&Z0sq); - // r3 = sum_j [ M0_mj * M0_nj * [ sum_i (Z0_i)^2 * Rsq_ij ] ] + // r3 = \sum_j [ M0_{mj} * M0_nj * [ \sum_i (Z0_i)^2 * Rsq_{ij} ] ] TMatrixDSparse *r3=MultiplyMSparseMSparseTranspVector(m_0,m_0,Z0sqRsq); DeleteMatrix(&Z0sqRsq); @@ -872,23 +961,23 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat for(int index=0;index<Z1sq_rows[Z1sq.GetNrows()];index++) { Z1sq_data[index] *= Z1sq_data[index]; } - // Z1sqRsq = sum_j (Z1_j)^2 * Rsq_ij ] + // Z1sqRsq = \sum_j (Z1_j)^2 * Rsq_{ij} ] TMatrixDSparse *Z1sqRsq=MultiplyMSparseMSparse(fDAinRelSq,&Z1sq); - // r4 = sum_i [ M1_mi * M1_ni * [ sum_j (Z1_j)^2 * Rsq_ij ] ] + // r4 = \sum_i [ M1_{mi} * M1_{ni} * [ \sum_j (Z1_j)^2 * Rsq_{ij} ] ] TMatrixDSparse *r4=MultiplyMSparseMSparseTranspVector(m_1,m_1,Z1sqRsq); DeleteMatrix(&Z1sqRsq); - // sum_j [ M0_mj * Z1_j * Rsq_ij ] + // \sum_j [ M0_{mj} * Z1_j * Rsq_{ij} ] TMatrixDSparse *H=MultiplyMSparseMSparseTranspVector (m_0,fDAinRelSq,GetDXDAZ(1)); - // H_mi = Z0_i * sum_j [ M0_mj * Z1_j * Rsq_ij ] + // H_{mi} = Z0_i * \sum_j [ M0_{mj} * Z1_j * Rsq_{ij} ] ScaleColumnsByVector(H,GetDXDAZ(0)); - // r5 = sum_i [ M1_mi * H_ni ] + // r5 = \sum_i [ M1_{mi} * H_{ni} ] TMatrixDSparse *r5=MultiplyMSparseMSparseTranspVector(m_1,H,0); - // r6 = sum_i [ H_mi * M1_ni ] + // r6 = \sum_i [ H_{mi} * M1_{ni} ] TMatrixDSparse *r6=MultiplyMSparseMSparseTranspVector(H,m_1,0); DeleteMatrix(&H); - // r = r0 -r1 -r2 +r3 +r4 +r5 +r6 + // r = r0 -r1 -r2 +r3 +r4 -r5 -r6 if(r) { AddMSparse(r,1.0,r3); DeleteMatrix(&r3); @@ -906,6 +995,13 @@ TMatrixDSparse *TUnfoldSys::PrepareUncorrEmat return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Propagate correlated systematic shift to an output vector. +/// +/// \param[in] m1 coefficients +/// \param[in] m2 coeffiicients +/// \param[in] dsys matrix of correlated shifts from this source + TMatrixDSparse *TUnfoldSys::PrepareCorrEmat (const TMatrixDSparse *m1,const TMatrixDSparse *m2,const TMatrixDSparse *dsys) { @@ -916,8 +1012,8 @@ TMatrixDSparse *TUnfoldSys::PrepareCorrEmat // delta_m = // sum{i,j} { // ((*m1)(m,j) * (*fVYAx)(i) - (*m2)(m,i) * (*fX)(j))*dsys(i,j) } - // = sum_j (*m1)(m,j) sum_i dsys(i,j) * (*fVYAx)(i) - // - sum_i (*m2)(m,i) sum_j dsys(i,j) * (*fX)(j) + // = \sum_j (*m1)(m,j) \sum_i dsys(i,j) * (*fVYAx)(i) + // - \sum_i (*m2)(m,i) \sum_j dsys(i,j) * (*fX)(j) TMatrixDSparse *dsysT_VYAx = MultiplyMSparseTranspMSparse(dsys,GetDXDAZ(0)); TMatrixDSparse *delta = MultiplyMSparseMSparse(m1,dsysT_VYAx); @@ -930,6 +1026,13 @@ TMatrixDSparse *TUnfoldSys::PrepareCorrEmat return delta; } +//////////////////////////////////////////////////////////////////////////////// +/// Specify an uncertainty on tau. +/// +/// \param[in] delta_tau new uncertainty on tau +/// +/// The default is to have no uncertyainty on tau. + void TUnfoldSys::SetTauError(Double_t delta_tau) { // set uncertainty on tau @@ -937,13 +1040,23 @@ void TUnfoldSys::SetTauError(Double_t delta_tau) DeleteMatrix(&fDeltaSysTau); } +//////////////////////////////////////////////////////////////////////////////// +/// Correlated one-sigma shifts correspinding to a given systematic uncertainty. +/// +/// \param[out] hist_delta histogram to store shifts +/// \param[in] name identifier of the background source +/// \param[in] binMap (default=0) remapping of histogram bins +/// +/// returns true if the error source was found. +/// +/// This method returns the shifts of the unfolding result induced by +/// varying the identified systematic source by one sigma. +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). + Bool_t TUnfoldSys::GetDeltaSysSource(TH1 *hist_delta,const char *name, const Int_t *binMap) { - // calculate systematic shift from a given source - // ematrix: output - // source: name of the error source - // binMap: see method GetEmatrix() PrepareSysError(); const TPair *named_emat=(const TPair *)fDeltaCorrX->FindObject(name); const TMatrixDSparse *delta=0; @@ -954,14 +1067,23 @@ Bool_t TUnfoldSys::GetDeltaSysSource(TH1 *hist_delta,const char *name, return delta !=0; } +//////////////////////////////////////////////////////////////////////////////// +/// Correlated one-sigma shifts from background normalisation uncertainty. +/// +/// \param[out] hist_delta histogram to store shifts +/// \param[in] source identifier of the background source +/// \param[in] binMap (default=0) remapping of histogram bins +/// +/// returns true if the background source was found. +/// +/// This method returns the shifts of the unfolding result induced by +/// varying the normalisation of the identified background by one sigma. +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). + Bool_t TUnfoldSys::GetDeltaSysBackgroundScale (TH1 *hist_delta,const char *source,const Int_t *binMap) { - // get correlated shift induced by a background source - // delta: output shift vector histogram - // source: name of background source - // binMap: see method GetEmatrix() - // see PrepareSysError() PrepareSysError(); const TPair *named_err=(const TPair *)fBgrErrScaleIn->FindObject(source); TMatrixDSparse *dx=0; @@ -977,6 +1099,20 @@ Bool_t TUnfoldSys::GetDeltaSysBackgroundScale return kFALSE; } +//////////////////////////////////////////////////////////////////////////////// +/// Correlated one-sigma shifts from shifting tau. +/// +/// \param[out] hist_delta histogram to store shifts +/// \param[in] source identifier of the background source +/// \param[in] binMap (default=0) remapping of histogram bins +/// +/// returns true if the background source was found. +/// +/// This method returns the shifts of the unfolding result induced by +/// varying the normalisation of the identified background by one sigma. +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). + Bool_t TUnfoldSys::GetDeltaSysTau(TH1 *hist_delta,const Int_t *binMap) { // calculate systematic shift from tau variation @@ -987,14 +1123,26 @@ Bool_t TUnfoldSys::GetDeltaSysTau(TH1 *hist_delta,const Int_t *binMap) return fDeltaSysTau !=0; } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance contribution from a systematic variation of the +/// response matrix. +/// +/// \param[inout] ematrix covariance matrix histogram +/// \param[in] name identifier of the systematic variation +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[in] clearEmat (default=true) if true, clear the histogram +/// prior to adding the covariance matrix contribution +/// +/// Returns the covariance matrix contribution from shifting the given +/// uncertainty source within one sigma +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. + void TUnfoldSys::GetEmatrixSysSource (TH2 *ematrix,const char *name,const Int_t *binMap,Bool_t clearEmat) { - // calculate systematic shift from a given source - // ematrix: output - // source: name of the error source - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors PrepareSysError(); const TPair *named_emat=(const TPair *)fDeltaCorrX->FindObject(name); TMatrixDSparse *emat=0; @@ -1006,14 +1154,26 @@ void TUnfoldSys::GetEmatrixSysSource DeleteMatrix(&emat); } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance contribution from background normalisation uncertainty. +/// +/// \param[inout] ematrix output histogram +/// \param[in] source identifier of the background source +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[in] clearEmat (default=true) if true, clear the histogram +/// prior to adding the covariance matrix contribution +/// +/// this method returns the uncertainties on the unfolding result +/// arising from the background source <b>source</b> and its normalisation +/// uncertainty. See method SubtractBackground() how to set the normalisation uncertainty +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. + void TUnfoldSys::GetEmatrixSysBackgroundScale (TH2 *ematrix,const char *name,const Int_t *binMap,Bool_t clearEmat) { - // calculate systematic shift from a given background scale error - // ematrix: output - // source: name of the error source - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors PrepareSysError(); const TPair *named_err=(const TPair *)fBgrErrScaleIn->FindObject(name); TMatrixDSparse *emat=0; @@ -1027,13 +1187,30 @@ void TUnfoldSys::GetEmatrixSysBackgroundScale DeleteMatrix(&emat); } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance matrix contribution from error on regularisation +/// parameter. +/// +/// \param[inout] ematrix output histogram +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[in] clearEmat (default=true) if true, clear the histogram +/// +/// this method returns the covariance contributions to the unfolding result +/// from the assigned uncertainty on the parameter tau, see method +/// SetTauError(). +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. +/// +/// Calculate error matrix from error in regularisation parameter +/// - ematrix: output +/// - binMap: see method GetEmatrix() +/// - clearEmat: set kTRUE to clear the histogram prior to adding the errors + void TUnfoldSys::GetEmatrixSysTau (TH2 *ematrix,const Int_t *binMap,Bool_t clearEmat) { - // calculate error matrix from error in regularisation parameter - // ematrix: output - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors PrepareSysError(); TMatrixDSparse *emat=0; if(fDeltaSysTau) { @@ -1043,25 +1220,46 @@ void TUnfoldSys::GetEmatrixSysTau DeleteMatrix(&emat); } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance matrix contribution from input measurement uncertainties. +/// +/// \param[inout] ematrix output histogram +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[in] clearEmat (default=true) if true, clear the histogram +/// +/// this method returns the covariance contributions to the unfolding result +/// from the uncertainties or covariance of the input +/// data. In many cases, these are the "statistical uncertainties". +/// +/// The array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. + void TUnfoldSys::GetEmatrixInput (TH2 *ematrix,const Int_t *binMap,Bool_t clearEmat) { - // calculate error matrix from error in input vector alone - // ematrix: output - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors GetEmatrixFromVyy(fVyyData,ematrix,binMap,clearEmat); } +//////////////////////////////////////////////////////////////////////////////// +/// Covariance contribution from background uncorrelated uncertainty. +/// +/// \param[in] ematrix output histogram +/// \param[in] source identifier of the background source +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[in] clearEmat (default=true) if true, clear the histogram +/// +/// this method returns the covariance contributions to the unfolding result +/// arising from the background source <b>source</b> and the uncorrelated +/// (background histogram uncertainties). Also see method SubtractBackground() +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// The flag <b>clearEmat</b> may be used to add covariance matrices from +/// several uncertainty sources. + void TUnfoldSys::GetEmatrixSysBackgroundUncorr (TH2 *ematrix,const char *source,const Int_t *binMap,Bool_t clearEmat) { - // calculate error matrix contribution originating from uncorrelated errors - // of one background source - // ematrix: output - // source: name of the error source - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors const TPair *named_err=(const TPair *)fBgrErrUncorrInSq->FindObject(source); TMatrixDSparse *emat=0; if(named_err) { @@ -1072,14 +1270,24 @@ void TUnfoldSys::GetEmatrixSysBackgroundUncorr DeleteMatrix(&emat); } +//////////////////////////////////////////////////////////////////////////////// +/// Propagate an error matrix on the input vector to the unfolding result. +/// +/// \param[in] vyy input error matrix +/// \param[inout] ematrix histogram to be updated +/// \param[in] binMap mapping of histogram bins +/// \param[in] clearEmat if set, clear histogram before adding this +/// covariance contribution +/// +/// propagate error matrix vyy to the result +/// - vyy: error matrix on input data fY +/// - ematrix: output +/// - binMap: see method GetEmatrix() +/// - clearEmat: set kTRUE to clear the histogram prior to adding the errors + void TUnfoldSys::GetEmatrixFromVyy (const TMatrixDSparse *vyy,TH2 *ematrix,const Int_t *binMap,Bool_t clearEmat) { - // propagate error matrix vyy to the result - // vyy: error matrix on input data fY - // ematrix: output - // binMap: see method GetEmatrix() - // clearEmat: set kTRUE to clear the histogram prior to adding the errors PrepareSysError(); TMatrixDSparse *em=0; if(vyy) { @@ -1091,11 +1299,20 @@ void TUnfoldSys::GetEmatrixFromVyy DeleteMatrix(&em); } +//////////////////////////////////////////////////////////////////////////////// +/// Get total error matrix, summing up all contributions. +/// +/// \param[out] ematrix histogram which will be filled +/// \param[in] binMap (default=0) remapping of histogram bins +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// +/// get total error including statistical error +/// - ematrix: output +/// - binMap: see method GetEmatrix() + void TUnfoldSys::GetEmatrixTotal(TH2 *ematrix,const Int_t *binMap) { - // get total error including statistical error - // ematrix: output - // binMap: see method GetEmatrix() GetEmatrix(ematrix,binMap); // (stat)+(d)+(e) GetEmatrixSysUncorr(ematrix,binMap,kFALSE); // (a) TMapIter sysErrPtr(fDeltaCorrX); @@ -1109,6 +1326,9 @@ void TUnfoldSys::GetEmatrixTotal(TH2 *ematrix,const Int_t *binMap) GetEmatrixSysTau(ematrix,binMap,kFALSE); // (c) } +//////////////////////////////////////////////////////////////////////////////// +/// Determine total error matrix on the vector Ax. + TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixYY(void) { PrepareSysError(); @@ -1117,7 +1337,9 @@ TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixYY(void) TMatrixDSparse *emat_sum=new TMatrixDSparse(*fVyy); // uncorrelated systematic error - AddMSparse(emat_sum,1.0,fEmatUncorrAx); + if(fEmatUncorrAx) { + AddMSparse(emat_sum,1.0,fEmatUncorrAx); + } TMapIter sysErrPtr(fDeltaCorrAx); const TObject *key; @@ -1147,6 +1369,9 @@ TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixYY(void) return emat_sum; } +//////////////////////////////////////////////////////////////////////////////// +/// Determine total error matrix on the vector x. + TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixXX(void) { PrepareSysError(); @@ -1155,7 +1380,9 @@ TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixXX(void) TMatrixDSparse *emat_sum=new TMatrixDSparse(*GetVxx()); // uncorrelated systematic error - AddMSparse(emat_sum,1.0,fEmatUncorrX); + if(fEmatUncorrX) { + AddMSparse(emat_sum,1.0,fEmatUncorrX); + } TMapIter sysErrPtr(fDeltaCorrX); const TObject *key; @@ -1184,9 +1411,11 @@ TMatrixDSparse *TUnfoldSys::GetSummedErrorMatrixXX(void) } +//////////////////////////////////////////////////////////////////////////////// +/// Calculate total chi**2 including all systematic errors. + Double_t TUnfoldSys::GetChi2Sys(void) { - // calculate total chi**2 including systematic errors TMatrixDSparse *emat_sum=GetSummedErrorMatrixYY(); @@ -1208,14 +1437,28 @@ Double_t TUnfoldSys::GetChi2Sys(void) return r; } +//////////////////////////////////////////////////////////////////////////////// +/// Get global correlatiocn coefficients, summing up all contributions. +/// +/// \param[out] rhoi histogram which will be filled +/// \param[in] binMap (default=0) remapping of histogram bins +/// \param[out] invEmat (default=0) inverse of error matrix +/// +/// return the global correlation coefficients, including all error +/// sources. If <b>invEmat</b> is nonzero, the inverse of the error +/// matrix is returned in that histogram +/// +/// the array <b>binMap</b> is explained with the method GetOutput(). +/// +/// get global correlation coefficients including systematic,statistical,background,tau errors +/// - rhoi: output histogram +/// - binMap: for each global bin, indicate in which histogram bin +/// to store its content +/// - invEmat: output histogram for inverse of error matrix +/// (pointer may zero if inverse is not requested) + void TUnfoldSys::GetRhoItotal(TH1 *rhoi,const Int_t *binMap,TH2 *invEmat) { - // get global correlation coefficients including systematic,statistical,background,tau errors - // rhoi: output histogram - // binMap: for each global bin, indicate in which histogram bin - // to store its content - // invEmat: output histogram for inverse of error matrix - // (pointer may zero if inverse is not requested) ClearHistogram(rhoi,-1.); TMatrixDSparse *emat_sum=GetSummedErrorMatrixXX(); GetRhoIFromMatrix(rhoi,emat_sum,binMap,invEmat); @@ -1223,13 +1466,22 @@ void TUnfoldSys::GetRhoItotal(TH1 *rhoi,const Int_t *binMap,TH2 *invEmat) DeleteMatrix(&emat_sum); } +//////////////////////////////////////////////////////////////////////////////// +/// Scale columns of a matrix by the corresponding rows of a vector. +/// +/// \param[inout] m matrix +/// \param[in] v vector +/// +/// the entries m<sub>ij</sub> are multiplied by v<sub>j</sub>. +/// +/// scale columns of m by the corresponding rows of v +/// input: +/// - m: pointer to sparse matrix of dimension NxM +/// - v: pointer to matrix of dimension Mx1 + void TUnfoldSys::ScaleColumnsByVector (TMatrixDSparse *m,const TMatrixTBase<Double_t> *v) const { - // scale columns of m by the corresponding rows of v - // input: - // m: pointer to sparse matrix of dimension NxM - // v: pointer to matrix of dimension Mx1 if((m->GetNcols() != v->GetNrows())||(v->GetNcols()!=1)) { Fatal("ScaleColumnsByVector error", "matrix cols/vector rows %d!=%d OR vector cols %d !=1\n", @@ -1255,19 +1507,34 @@ void TUnfoldSys::ScaleColumnsByVector } } else { for(Int_t i=0;i<m->GetNrows();i++) { - for(Int_t index=rows_m[i];index<rows_m[i+1];index++) { - data_m[index] *= (*v)(cols_m[index],0); + for(Int_t index_m=rows_m[i];index_m<rows_m[i+1];index_m++) { + Int_t j=cols_m[index_m]; + data_m[index_m] *= (*v)(j,0); } } } } +//////////////////////////////////////////////////////////////////////////////// +/// Map delta to hist_delta, possibly summing up bins. +/// +/// \param[out] hist_delta result histogram +/// \param[in] delta vector to be mapped to the histogram +/// \param[in] binMap mapping of histogram bins +/// +/// groups of bins of <b>delta</b> are mapped to bins of +/// <b>hist_delta</b>. The histogram contents are set to the sum over +/// the group of bins. The histogram errors are reset to zero. +/// +/// The array <b>binMap</b> is explained with the method GetOutput() +/// +/// sum over bins of *delta, as defined in binMap,fXToHist +/// - hist_delta: histogram to return summed vector +/// - delta: vector to sum and remap + void TUnfoldSys::VectorMapToHist (TH1 *hist_delta,const TMatrixDSparse *delta,const Int_t *binMap) { - // sum over bins of *delta, as defined in binMap,fXToHist - // hist_delta: histogram to return summed vector - // delta: vector to sum and remap Int_t nbin=hist_delta->GetNbinsX(); Double_t *c=new Double_t[nbin+2]; for(Int_t i=0;i<nbin+2;i++) { @@ -1294,3 +1561,33 @@ void TUnfoldSys::VectorMapToHist } delete[] c; } + +//////////////////////////////////////////////////////////////////////////////// +/// Get a new list of all systematic uuncertainty sources. +/// +/// The user is responsible for deleting the list +/// get list of names of systematic sources + +TSortedList *TUnfoldSys::GetSysSources(void) const { + TSortedList *r=new TSortedList(); + TMapIter i(fSysIn); + for(const TObject *key=i.Next();key;key=i.Next()) { + r->Add(((TObjString *)key)->Clone()); + } + return r; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Get a new list of all background sources. +/// +/// The user is responsible for deleting the list +/// get list of name of background sources + +TSortedList *TUnfoldSys::GetBgrSources(void) const { + TSortedList *r=new TSortedList(); + TMapIter i(fBgrIn); + for(const TObject *key=i.Next();key;key=i.Next()) { + r->Add(((TObjString *)key)->Clone()); + } + return r; +}