diff --git a/hist/hist/inc/TUnfold.h b/hist/hist/inc/TUnfold.h index 779e4fadc915a2a9f28647798888e0f045257e56..df338532a5bb2c65071dbd893e093e7463b594ea 100644 --- a/hist/hist/inc/TUnfold.h +++ b/hist/hist/inc/TUnfold.h @@ -1,9 +1,14 @@ // Author: Stefan Schmitt // DESY, 13/10/08 -// Version 6, completely remove definition of class XY +// Version 11, regularisation methods have return values // // History: +// Version 10, with bug-fix in TUnfold.cxx +// Version 9, implements method for optimized inversion of sparse matrix +// Version 8, replace all TMatrixSparse matrix operations by private code +// Version 7, fix problem with TMatrixDSparse,TMatrixD multiplication +// Version 6, completely remove definition of class XY // Version 5, move definition of class XY from TUnfold.C to this file // Version 4, with bug-fix in TUnfold.C // Version 3, with bug-fix in TUnfold.C @@ -75,11 +80,16 @@ class TUnfold:public TObject { Double_t fChi2L; // Result: chi**2 contribution from tau(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 protected: TUnfold(void); // for derived classes virtual Double_t DoUnfold(void); // the unfolding algorithm virtual void CalculateChi2Rho(void); // supplementory calculations - static TMatrixDSparse *CreateSparseMatrix(Int_t nr, Int_t nc, Int_t * row, Int_t * col, Double_t const *data); // create matrices + static TMatrixDSparse *MultiplyMSparseM(TMatrixDSparse const &a,TMatrixD const &b); // multiply sparse and non-sparse matrix + static TMatrixDSparse *MultiplyMSparseMSparse(TMatrixDSparse const &a,TMatrixDSparse const &b); // multiply sparse and sparse matrix + static TMatrixDSparse *MultiplyMSparseTranspMSparse(TMatrixDSparse const &a,TMatrixDSparse const &b); // multiply transposed sparse and sparse matrix + static Double_t MultiplyVecMSparseVec(TMatrixDSparse const &a,TMatrixD const &v); // scalar product of v and Av + static TMatrixD *InvertMSparse(TMatrixDSparse const &A); // invert sparse matrix inline Int_t GetNx(void) const { return fA->GetNcols(); } // number of non-zero output bins @@ -96,19 +106,19 @@ class TUnfold:public TObject { 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 + kRegModeCurvature = 3, // regularize the 2nd derivative of the output }; TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode = kRegModeSize); // constructor virtual ~ TUnfold(void); // delete data members void SetBias(TH1 const *bias); // set alternative bias - void RegularizeSize(int bin, Double_t const &scale = 1.0); // regularise the size of one output bin - void RegularizeDerivative(int left_bin, int right_bin, Double_t const &scale = 1.0); // regularize difference of two output bins (1st derivative) - void RegularizeCurvature(int left_bin, int center_bin, int right_bin, Double_t const &scale_left = 1.0, Double_t const &scale_right = 1.0); // regularize curvature of three output bins (2nd derivative) - void RegularizeBins(int start, int step, int nbin, ERegMode regmode); // regularize a 1-dimensional curve - void RegularizeBins2D(int start_bin, int step1, int nbin1, int step2, int nbin2, ERegMode regmode); // regularize a 2-dimensional grid + Int_t RegularizeSize(int bin, Double_t const &scale = 1.0); // regularise the size of one output bin + Int_t RegularizeDerivative(int left_bin, int right_bin, Double_t const &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 const &scale_left = 1.0, Double_t const &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 const &tau, TH1 const *hist_y, Double_t const &scaleBias=0.0); // do the unfolding - void SetInput(TH1 const *hist_y, Double_t const &scaleBias=0.0); // define input distribution for ScanLCurve + Int_t SetInput(TH1 const *hist_y, Double_t const &scaleBias=0.0,Double_t oneOverZeroError=0.0); // define input distribution for ScanLCurve virtual Double_t DoUnfold(Double_t const &tau); // Unfold with given choice of tau virtual Int_t ScanLcurve(Int_t nPoint,Double_t const &tauMin, Double_t const &tauMax,TGraph **lCurve, @@ -134,8 +144,10 @@ class TUnfold:public TObject { Double_t const &GetChi2L(void) const; // chi**2 contribution from L Double_t GetLcurveX(void) const; // x axis of L curve Double_t GetLcurveY(void) const; // y axis of L curve + Int_t GetNdf(void) const; // number of degrees of freedom + Int_t GetNpar(void) const; // number of parameters - ClassDef(TUnfold, 0) + ClassDef(TUnfold, 0) //Unfolding with support for L-curve analysis }; #endif diff --git a/hist/hist/src/TUnfold.cxx b/hist/hist/src/TUnfold.cxx index e3706daecc5f745d72ece04a531fdf651551bd9a..b70b0fc429bc4e17bead3eb87247809904305898 100644 --- a/hist/hist/src/TUnfold.cxx +++ b/hist/hist/src/TUnfold.cxx @@ -1,10 +1,14 @@ // Author: Stefan Schmitt // DESY, 13/10/08 - -// Version 6, replace class XY by std::pair +// Version 11, reduce the amount of printout // // History: +// 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 @@ -55,9 +59,19 @@ // // References: // =========== -// Talk by V. Blobel, Terascale Statistics school +// A nice overview of the 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 +// The relevant equations are (1), (2) for the unfolding +// and (14) for the L-curve curvature definition +// +// Related literature on unfolding: +// Talk by V. Blobel, Terascale Statistics school // https://indico.desy.de/contributionDisplay.py?contribId=23&confId=1149 -// References quoted in Blobel's talk: +// References quoted in Blobel's talk: // Per Chistian Hansen, Rank-Deficient and Discrete Ill-posed Problems, // Siam (1998) // Jari Kaipio and Erkki Somersalo, Statistical and Computational @@ -274,6 +288,7 @@ // RegularizeDerivative() regularize the slope given by two bins // RegularizeCurvature() regularize the curvature given by three bins + #include <iostream> #include <TMatrixD.h> #include <TMatrixDSparse.h> @@ -282,6 +297,22 @@ #include "TUnfold.h" #include <map> +#include <vector> + + +// this option enables the use of Schur complement for matrix inversion +// with large diagonal sub-matrices +#define SCHUR_COMPLEMENT_MATRIX_INVERSION + +// this option saves the spline of the L curve curvature to a file +// named splinec.ps for debugging + +// #define DEBUG_LCURVE + + +#ifdef DEBUG_LCURVE +#include <TCanvas.h> +#endif ClassImp(TUnfold) //______________________________________________________________________________ @@ -355,48 +386,48 @@ Double_t TUnfold::DoUnfold(void) // T // fA fV = mAt_V // - TMatrixDSparse mAt_V(TMatrixDSparse(TMatrixDSparse::kTransposed, *fA), - TMatrixDSparse::kMult, *fV); + TMatrixDSparse *mAt_V=MultiplyMSparseTranspMSparse(*fA,*fV); // // get T // fA fV fY + fTau fBiasScale Lsquared fX0 = rhs // - TMatrixDSparse rhs(mAt_V, TMatrixDSparse::kMult, *fY); + TMatrixDSparse *rhs=MultiplyMSparseM(*mAt_V,*fY); if (fBiasScale != 0.0) { - rhs += - (fTau * fBiasScale) * TMatrixDSparse(*fLsquared, - TMatrixDSparse::kMult, - *fX0); + TMatrixDSparse *rhs2=MultiplyMSparseM(*fLsquared,*fX0); + (*rhs) += (fTau * fBiasScale) * (*rhs2); + delete rhs2; } // // get matrix // T // (fA fV)fA + fTau*fLsquared = fEinv // - fEinv = new TMatrixDSparse(mAt_V, TMatrixDSparse::kMult, *fA); - *fEinv += fTau * (*fLsquared); + TMatrixDSparse *tmp=MultiplyMSparseMSparse(*mAt_V,*fA); + fEinv=new TMatrixDSparse(*tmp,TMatrixDSparse::kPlus,fTau * (*fLsquared)); + delete tmp; // // get matrix // -1 // fEinv = fE // -#ifdef MEASURE_TIMING - struct timespec t0, t1; - clockid_t type; - clock_getcpuclockid(0, &type); - clock_gettime(type, &t0); -#endif - fE = new TMatrixD(TMatrixD::kInverted, *fEinv); + fE = InvertMSparse(*fEinv); // // get result // fE rhs = x // - fX = new TMatrixD(*fE, TMatrixD::kMult, rhs); + fX = new TMatrixD(*fE, TMatrixD::kMult, *rhs); // // get result // fA x = fAx // - fAx = new TMatrixDSparse(*fA, TMatrixDSparse::kMult, *fX); + fAx = MultiplyMSparseM(*fA,*fX); + + // + // clean up + // + delete rhs; + delete mAt_V; + // // calculate chi**2 etc // @@ -414,24 +445,10 @@ void TUnfold::CalculateChi2Rho(void) // fChi2A,fChi2L,fRhoMax,fRhoAvg // chi**2 contribution from (y-Ax)V(y-Ax) - fChi2A = 0.0; TMatrixD dy(*fY, TMatrixD::kMinus, *fAx); - // Vd is made sparse, because it is made from sparse matrix V - TMatrixDSparse Vd(*fV, TMatrixDSparse::kMult, dy); - for (int iy = 0; iy < GetNy(); iy++) { - fChi2A += dy(iy, 0) * Vd(iy, 0); - } - - // chi**2 contribution from tau(x-s*x0)Lsquared(x-s*x0) - fChi2L = 0.0; + fChi2A = MultiplyVecMSparseVec(*fV,dy); TMatrixD dx(*fX, TMatrixD::kMinus, fBiasScale * (*fX0)); - // LsquaredDx is made sparse, because it is made from sparse matrix - // fLsquared - TMatrixDSparse LsquaredDx(*fLsquared, TMatrixDSparse::kMult, dx); - for (int ix = 0; ix < GetNx(); ix++) { - fChi2L += fTau * dx(ix, 0) * LsquaredDx(ix, 0); - } - + fChi2L = fTau*MultiplyVecMSparseVec(*fLsquared,dx); // maximum global correlation coefficient Double_t rho_squared_max = 0.0; Double_t rho_sum = 0.0; @@ -448,30 +465,423 @@ void TUnfold::CalculateChi2Rho(void) } fRhoMax = TMath::Sqrt(rho_squared_max); fRhoAvg = (n_rho>0) ? (rho_sum/n_rho) : -1.0; + } -TMatrixDSparse *TUnfold::CreateSparseMatrix(Int_t nr, Int_t nc, - Int_t * row, Int_t * col, - Double_t const *data) +Double_t TUnfold::MultiplyVecMSparseVec(TMatrixDSparse const &a,TMatrixD const &v) { - // Create a sparse matrix, to set up fA or fV. - // nr,nc: number of rows and columns - // row: row index array - // col: column index array - // data: non-zero matrix elements - // Return value: - // a new matrix - // - // This implementation cirumvents certain problems with TMatrixDSparse - // constructors. Eventually calls to this method should be replaced - // by something like - // new TMatrixDSparse( some_arguments ) - TMatrixDSparse *m = new TMatrixDSparse(nr, nc); - m->SetSparseIndex(row[nr]); - m->SetRowIndexArray(row); - m->SetColIndexArray(col); - m->SetMatrixArray(data); - return m; + // calculate the product v# a v + // where v# is the transposed vector v + // a: a matrix + // v: a vector + Double_t r=0.0; + if((a.GetNrows()!=a.GetNcols())|| + (v.GetNrows()!=a.GetNrows())|| + (v.GetNcols()!=1)) { + std::cout<<"TUnfold::MultiplyVecMSparseVec inconsistent row/col numbers " + <<" a("<<a.GetNrows()<<","<<a.GetNcols() + <<") v("<<v.GetNrows()<<","<<v.GetNcols()<<")\n"; + } + const Int_t *a_rows=a.GetRowIndexArray(); + const Int_t *a_cols=a.GetColIndexArray(); + const Double_t *a_data=a.GetMatrixArray(); + for (Int_t irow = 0; irow < a.GetNrows(); irow++) { + for(Int_t i=a_rows[irow];i<a_rows[irow+1];i++) { + r += v(irow,0) * a_data[i] * v(a_cols[i],0); + } + } + return r; +} + +TMatrixDSparse *TUnfold::MultiplyMSparseMSparse(TMatrixDSparse const &a,TMatrixDSparse const &b) +{ + // calculate the product of two sparse matrices + // a,b: two 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()) { + std::cout<<"TUnfold::MultiplyMSparseMSparse inconsistent row/col number " + <<a.GetNcols()<<" "<<b.GetNrows()<<"\n"; + } + + TMatrixDSparse *r=new TMatrixDSparse(a.GetNrows(),b.GetNcols()); + const Int_t *a_rows=a.GetRowIndexArray(); + const Int_t *a_cols=a.GetColIndexArray(); + const Double_t *a_data=a.GetMatrixArray(); + const Int_t *b_rows=b.GetRowIndexArray(); + const Int_t *b_cols=b.GetColIndexArray(); + const Double_t *b_data=b.GetMatrixArray(); + // maximum size of the output matrix + int nMax=0; + for (Int_t irow = 0; irow < a.GetNrows(); irow++) { + if(a_rows[irow+1]>a_rows[irow]) nMax += b.GetNcols(); + } + if(nMax>0) { + Int_t *r_rows=new Int_t[nMax]; + Int_t *r_cols=new Int_t[nMax]; + Double_t *r_data=new Double_t[nMax]; + Double_t *row_data=new Double_t[b.GetNcols()]; + Int_t n=0; + for (Int_t irow = 0; irow < a.GetNrows(); irow++) { + if(a_rows[irow+1]<=a_rows[irow]) continue; + // clear row data + for(Int_t icol=0;icol<b.GetNcols();icol++) { + row_data[icol]=0.0; + } + // loop over a-columns in this a-row + for(Int_t ia=a_rows[irow];ia<a_rows[irow+1];ia++) { + Int_t k=a_cols[ia]; + // loop over b-columns in b-row k + for(Int_t ib=b_rows[k];ib<b_rows[k+1];ib++) { + row_data[b_cols[ib]] += a_data[ia]*b_data[ib]; + } + } + // store nonzero elements + for(Int_t icol=0;icol<b.GetNcols();icol++) { + if(row_data[icol] != 0.0) { + r_rows[n]=irow; + r_cols[n]=icol; + r_data[n]=row_data[icol]; + n++; + } + } + } + r->SetMatrixArray(n,r_rows,r_cols,r_data); + delete [] r_rows; + delete [] r_cols; + delete [] r_data; + delete [] row_data; + } + + return r; +} + + +TMatrixDSparse *TUnfold::MultiplyMSparseTranspMSparse(TMatrixDSparse const &a,TMatrixDSparse const &b) +{ + // multiply a transposed Sparse matrix with another Sparse matrix + // a: sparse matrix (to be transposed + // b: sparse matrix + // this is a replacement for the call + // new TMatrixDSparse(TMatrixDSparse(TMatrixDSparse::kTransposed,a), + // TMatrixDSparse::kMult,b) + if(a.GetNrows() != b.GetNrows()) { + std::cout<<"TUnfold::MultiplyMSparseTranspMSparse " + <<"inconsistent row numbers " + <<a.GetNrows()<<" "<<b.GetNrows()<<"\n"; + } + + TMatrixDSparse *r=new TMatrixDSparse(a.GetNcols(),b.GetNcols()); + const Int_t *a_rows=a.GetRowIndexArray(); + const Int_t *a_cols=a.GetColIndexArray(); + const Double_t *a_data=a.GetMatrixArray(); + const Int_t *b_rows=b.GetRowIndexArray(); + const Int_t *b_cols=b.GetColIndexArray(); + const Double_t *b_data=b.GetMatrixArray(); + // maximum size of the output matrix + + // matrix multiplication + typedef std::map<Int_t,Double_t> MMatrixRow_t; + typedef std::map<Int_t, MMatrixRow_t > MMatrix_t; + MMatrix_t matrix; + + + for(Int_t iRowAB=0;iRowAB<a.GetNrows();iRowAB++) { + for(Int_t ia=a_rows[iRowAB];ia<a_rows[iRowAB+1];ia++) { + for(Int_t ib=b_rows[iRowAB];ib<b_rows[iRowAB+1];ib++) { + MMatrixRow_t &row=matrix[a_cols[ia]]; // creates a new row if necessary + MMatrixRow_t::iterator icol=row.find(b_cols[ib]); + if(icol!=row.end()) { + // update existing row + (*icol).second += a_data[ia]*b_data[ib]; + } else { + // create new row + row[b_cols[ib]] = a_data[ia]*b_data[ib]; + } + } + } + } + + Int_t n=0; + for(MMatrix_t::const_iterator irow=matrix.begin(); + irow!=matrix.end();irow++) { + n += (*irow).second.size(); + } + if(n>0) { + // pack matrix into arrays + Int_t *r_rows=new Int_t[n]; + Int_t *r_cols=new Int_t[n]; + Double_t *r_data=new Double_t[n]; + n=0; + for(MMatrix_t::const_iterator irow=matrix.begin(); + irow!=matrix.end();irow++) { + for(MMatrixRow_t::const_iterator icol=(*irow).second.begin(); + icol!=(*irow).second.end();icol++) { + r_rows[n]=(*irow).first; + r_cols[n]=(*icol).first; + r_data[n]=(*icol).second; + n++; + } + } + // pack arrays into TMatrixDSparse + r->SetMatrixArray(n,r_rows,r_cols,r_data); + delete [] r_rows; + delete [] r_cols; + delete [] r_data; + } + + return r; +} + +TMatrixDSparse *TUnfold::MultiplyMSparseM(TMatrixDSparse const &a,TMatrixD const &b) +{ + // multiply a Sparse matrix with a non-sparse matrix + // a: sparse matrix + // b: non-sparse matrix + // this is a replacement for the call + // new TMatrixDSparse(a,TMatrixDSparse::kMult,b); + if(a.GetNcols()!=b.GetNrows()) { + std::cout<<"TUnfold::MultiplyMSparseM inconsistent row/col number " + <<a.GetNcols()<<" "<<b.GetNrows()<<"\n"; + } + + TMatrixDSparse *r=new TMatrixDSparse(a.GetNrows(),b.GetNcols()); + const Int_t *a_rows=a.GetRowIndexArray(); + const Int_t *a_cols=a.GetColIndexArray(); + const Double_t *a_data=a.GetMatrixArray(); + // maximum size of the output matrix + int nMax=0; + for (Int_t irow = 0; irow < a.GetNrows(); irow++) { + if(a_rows[irow+1]-a_rows[irow]>0) nMax += b.GetNcols(); + } + if(nMax>0) { + Int_t *r_rows=new Int_t[nMax]; + Int_t *r_cols=new Int_t[nMax]; + Double_t *r_data=new Double_t[nMax]; + + Int_t n=0; + // fill matrix r + for (Int_t irow = 0; irow < a.GetNrows(); irow++) { + if(a_rows[irow+1]-a_rows[irow]<=0) continue; + for(Int_t icol=0;icol<b.GetNcols();icol++) { + r_rows[n]=irow; + r_cols[n]=icol; + r_data[n]=0.0; + for(Int_t i=a_rows[irow];i<a_rows[irow+1];i++) { + Int_t j=a_cols[i]; + r_data[n] += a_data[i]*b(j,icol); + } + if(r_data[n]!=0.0) n++; + } + } + + r->SetMatrixArray(n,r_rows,r_cols,r_data); + delete [] r_rows; + delete [] r_cols; + delete [] r_data; + } + return r; +} + +TMatrixD *TUnfold::InvertMSparse(TMatrixDSparse const &A) { + // get the inverse of a sparse matrix + // A: the original matrix + // this is a replacement of the call + // new TMatrixD(TMatrixD::kInverted, a); + // the matrix inversion is optimized for the case + // where a large submatrix of A is diagonal + + if(A.GetNcols()!=A.GetNrows()) { + std::cout<<"TUnfold::InvertMSparse inconsistent row/col number " + <<A.GetNcols()<<" "<<A.GetNrows()<<"\n"; + } + +#ifdef SCHUR_COMPLEMENT_MATRIX_INVERSION + + const Int_t *a_rows=A.GetRowIndexArray(); + const Int_t *a_cols=A.GetColIndexArray(); + const Double_t *a_data=A.GetMatrixArray(); + + Int_t nmin=0,nmax=0; + // find largest diagonal submatrix + for(Int_t imin=0;imin<A.GetNrows();imin++) { + Int_t imax=A.GetNrows(); + for(Int_t i2=imin;i2<imax;i2++) { + for(Int_t i=a_rows[i2];i<a_rows[i2+1];i++) { + if(a_data[i]==0.0) continue; + Int_t icol=a_cols[i]; + if(icol<imin) continue; // ignore first part of the matrix + if(icol==i2) continue; // ignore diagonals + if(icol<i2) { + // this row spoils the matrix, so do not use + imax=i2; + break; + } else { + // this entry limits imax + imax=icol; + break; + } + } + } + if(imax-imin>nmax-nmin) { + nmin=imin; + nmax=imax; + } + } + // special case: complete matrix is diagonal + // make sure the "non-diagonal" submatrix dimension is nonzero + if((nmin==0)&&(nmax==A.GetNrows())) { + nmin++; + } + // if the diagonal part has size zero or one, use standard matrix inversion + if(nmin>=nmax-1) return new TMatrixD(TMatrixD::kInverted, A); + // A B + // ( ) + // C D + // + // get inverse of diagonal part + std::vector<Double_t> Dinv; + Dinv.resize(nmax-nmin); + for(Int_t irow=nmin;irow<nmax;irow++) { + for(Int_t i=a_rows[irow];i<a_rows[irow+1];i++) { + if(a_cols[i]==irow) { + Dinv[irow-nmin]=1./a_data[i]; + break; + } + } + } + // B*Dinv and C + Int_t nBDinv=0,nC=0; + for(Int_t irow_a=0;irow_a<A.GetNrows();irow_a++) { + if((irow_a<nmin)||(irow_a>=nmax)) { + for(Int_t i=a_rows[irow_a];i<a_rows[irow_a+1];i++) { + Int_t icol=a_cols[i]; + if((icol>=nmin)&&(icol<nmax)) nBDinv++; + } + } else { + for(Int_t i=a_rows[irow_a];i<a_rows[irow_a+1];i++) { + Int_t icol=a_cols[i]; + if((icol<nmin)||(icol>=nmax)) nC++; + } + } + } + Int_t *row_BDinv=new Int_t[nBDinv+1]; + Int_t *col_BDinv=new Int_t[nBDinv+1]; + Double_t *data_BDinv=new Double_t[nBDinv+1]; + + Int_t *row_C=new Int_t[nC+1]; + Int_t *col_C=new Int_t[nC+1]; + Double_t *data_C=new Double_t[nC+1]; + + TMatrixD Aschur(A.GetNrows()-(nmax-nmin),A.GetNcols()-(nmax-nmin)); + + nBDinv=0; + nC=0; + for(Int_t irow_a=0;irow_a<A.GetNrows();irow_a++) { + if((irow_a<nmin)||(irow_a>=nmax)) { + Int_t row=(irow_a<nmin) ? irow_a : (irow_a-(nmax-nmin)); + for(Int_t i=a_rows[irow_a];i<a_rows[irow_a+1];i++) { + Int_t icol_a=a_cols[i]; + if(icol_a<nmin) { + Aschur(row,icol_a)=a_data[i]; + } else if(icol_a>=nmax) { + Aschur(row,icol_a-(nmax-nmin))=a_data[i]; + } else { + row_BDinv[nBDinv]=row; + col_BDinv[nBDinv]=icol_a-nmin; + data_BDinv[nBDinv]=a_data[i]*Dinv[icol_a-nmin]; + nBDinv++; + } + } + } else { + for(Int_t i=a_rows[irow_a];i<a_rows[irow_a+1];i++) { + Int_t icol_a=a_cols[i]; + if(icol_a<nmin) { + row_C[nC]=irow_a-nmin; + col_C[nC]=icol_a; + data_C[nC]=a_data[i]; + nC++; + } else if(icol_a>=nmax) { + row_C[nC]=irow_a-nmin; + col_C[nC]=icol_a-(nmax-nmin); + data_C[nC]=a_data[i]; + nC++; + } + } + } + } + TMatrixDSparse BDinv(A.GetNrows()-(nmax-nmin),nmax-nmin); + BDinv.SetMatrixArray(nBDinv,row_BDinv,col_BDinv,data_BDinv); + delete[] row_BDinv; + delete[] col_BDinv; + delete[] data_BDinv; + + TMatrixDSparse C(nmax-nmin,A.GetNcols()-(nmax-nmin)); + C.SetMatrixArray(nC,row_C,col_C,data_C); + delete[] row_C; + delete[] col_C; + delete[] data_C; + + TMatrixDSparse *BDinvC=MultiplyMSparseMSparse(BDinv,C); + Aschur -= *BDinvC; + Aschur.Invert(); + delete BDinvC; + + TMatrixD *r=new TMatrixD(A.GetNrows(),A.GetNcols()); + + for(Int_t row_a=0;row_a<Aschur.GetNrows();row_a++) { + for(Int_t col_a=0;col_a<Aschur.GetNcols();col_a++) { + (*r)((row_a<nmin) ? row_a : (row_a+nmax-nmin), + (col_a<nmin) ? col_a : (col_a+nmax-nmin))=Aschur(row_a,col_a); + } + } + + TMatrixDSparse *CAschur=MultiplyMSparseM(C,Aschur); + TMatrixDSparse *CAschurBDinv=MultiplyMSparseMSparse(*CAschur,BDinv); + + const Int_t *CAschurBDinv_row=CAschurBDinv->GetRowIndexArray(); + const Int_t *CAschurBDinv_col=CAschurBDinv->GetColIndexArray(); + const Double_t *CAschurBDinv_data=CAschurBDinv->GetMatrixArray(); + for(Int_t row=0;row<CAschurBDinv->GetNrows();row++) { + bool has_diagonal=false; + for(Int_t i=CAschurBDinv_row[row];i<CAschurBDinv_row[row+1];i++) { + Int_t col=CAschurBDinv_col[i]; + (*r)(row+nmin,col+nmin)=CAschurBDinv_data[i]*Dinv[row]; + } + (*r)(row+nmin,row+nmin) += Dinv[row]; + } + + delete CAschurBDinv; + + const Int_t *CAschur_row=CAschur->GetRowIndexArray(); + const Int_t *CAschur_col=CAschur->GetColIndexArray(); + const Double_t *CAschur_data=CAschur->GetMatrixArray(); + for(Int_t row=0;row<CAschur->GetNrows();row++) { + for(Int_t i=CAschur_row[row];i<CAschur_row[row+1];i++) { + Int_t col=CAschur_col[i]; + (*r)(row+nmin, + (col<nmin) ? col : (col+nmax-nmin))= -CAschur_data[i]*Dinv[row]; + } + } + delete CAschur; + + const Int_t *BDinv_row=BDinv.GetRowIndexArray(); + const Int_t *BDinv_col=BDinv.GetColIndexArray(); + const Double_t *BDinv_data=BDinv.GetMatrixArray(); + for(Int_t row_aschur=0;row_aschur<Aschur.GetNrows();row_aschur++) { + Int_t row=(row_aschur<nmin) ? row_aschur : (row_aschur+nmax-nmin); + for(Int_t row_bdinv=0;row_bdinv<BDinv.GetNrows();row_bdinv++) { + for(Int_t i=BDinv_row[row_bdinv];i<BDinv_row[row_bdinv+1];i++) { + (*r)(row,BDinv_col[i]+nmin) -= Aschur(row_aschur,row_bdinv)* + BDinv_data[i]; + } + } + } + + return r; +#else + return new TMatrixD(TMatrixD::kInverted, A); +#endif } TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) @@ -511,7 +921,7 @@ TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) TArrayD sum_over_y(nx0); fXToHist.Set(nx0); fHistToX.Set(nx0); - TArrayI colCount_A(ny); + Int_t nonzeroA=0; // loop // - calculate bias distribution // sum_over_y @@ -520,8 +930,9 @@ TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) // - histogram bins are added to the lookup-tables // fHistToX[] and fXToHist[] // these convert histogram bins to matrix indices and vice versa - // - the number of columns per row of the final matrix is counted - // colCount_A + // - the number of non-zero elements is counted + // nonzeroA + Int_t nskipped=0; for (Int_t ix = 0; ix < nx0; ix++) { // calculate sum over all detector bins // excluding the overflow bins @@ -534,7 +945,7 @@ TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) z = hist_A->GetBinContent(iy + 1, ix); } if (z > 0.0) { - colCount_A[iy]++; + nonzeroA++; sum += z; } } @@ -556,27 +967,46 @@ TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) } nx++; } else { + nskipped++; // histogram bin pointing to -1 (non-existing matrix entry) fHistToX[ix] = -1; - std::cout << "TUnfold: bin " << ix - << " has no influence on the data -> skipped!\n"; } } + if(nskipped) { + std::cout << "TUnfold: the following output bins " + <<"are not connected to the input side\n"; + Int_t nprint=0; + Int_t ixfirst=-1,ixlast=-1; + for (Int_t ix = 0; ix < nx0; ix++) { + if(fHistToX[ix]<0) { + nprint++; + if(ixlast<0) { + std::cout<<" "<<ix; + ixfirst=ix; + } + ixlast=ix; + } + if(((fHistToX[ix]>=0)&&(ixlast>=0))|| + (nprint==nskipped)) { + if(ixlast>ixfirst) std::cout<<"-"<<ixlast; + ixfirst=-1; + ixlast=-1; + } + if(nprint==nskipped) { + std::cout<<"\n"; + break; + } + } + } // store bias as matrix fX0 = new TMatrixD(nx, 1, sum_over_y.GetArray()); // store non-zero elements in sparse matrix fA // construct sparse matrix fA - // row structure - Int_t *row_A = new Int_t[ny + 1]; - row_A[0] = 0; - for (Int_t iy = 0; iy < ny; iy++) { - row_A[iy + 1] = row_A[iy] + colCount_A[iy]; - } - // column structure and data points - Int_t *col_A= new Int_t[row_A[ny]]; - Double_t *data_A=new Double_t[row_A[ny]]; + Int_t *rowA = new Int_t[nonzeroA]; + Int_t *colA = new Int_t[nonzeroA]; + Double_t *dataA = new Double_t[nonzeroA]; + Int_t index=0; for (Int_t iy = 0; iy < ny; iy++) { - Int_t index = row_A[iy]; for (Int_t ix = 0; ix < nx; ix++) { Int_t ibinx = fXToHist[ix]; Double_t z; @@ -586,20 +1016,29 @@ TUnfold::TUnfold(TH2 const *hist_A, EHistMap histmap, ERegMode regmode) z = hist_A->GetBinContent(iy + 1, ibinx); } if (z > 0.0) { - col_A[index] = ix; - data_A[index] = z / sum_over_y[ix]; - index++; + rowA[index]=iy; + colA[index] = ix; + dataA[index] = z / sum_over_y[ix]; + index++; } } } - fA = CreateSparseMatrix(ny, nx, row_A, col_A, data_A); - delete[] row_A; - delete[] col_A; - delete[] data_A; + fA = new TMatrixDSparse(ny,nx); + fA->SetMatrixArray(index,rowA,colA,dataA); + delete[] rowA; + delete[] colA; + delete[] dataA; // regularisation conditions squared fLsquared = new TMatrixDSparse(GetNx(), GetNx()); if (regmode != kRegModeNone) { - RegularizeBins(0, 1, nx0, regmode); + Int_t nError=RegularizeBins(0, 1, nx0, regmode); + if(nError>0) { + if(nError>1) { + std::cout<<"TUnfold: "<<nError<<" regularisation conditions have been skipped\n"; + } else { + std::cout<<"TUnfold: "<<nError<<" regularisation condition has been skipped\n"; + } + } } } @@ -640,44 +1079,45 @@ void TUnfold::SetBias(TH1 const *bias) } } -void TUnfold::RegularizeSize(int bin, Double_t const &scale) +Int_t TUnfold::RegularizeSize(int bin, Double_t const &scale) { // add regularisation on the size of bin i // bin: bin number // scale: size of the regularisation + // return value: number of conditions which have been skipped // modifies data member fLsquared Int_t i = fHistToX[bin]; if (i < 0) { - std::cout << "TUnfold::RegularizeSize skip bin " << bin << "\n"; - return; + return 1; } (*fLsquared) (i, i) = scale * scale; + return 0; } -void TUnfold::RegularizeDerivative(int left_bin, int right_bin, +Int_t TUnfold::RegularizeDerivative(int left_bin, int right_bin, Double_t const &scale) { // add regularisation on the difference of two bins // left_bin: 1st bin // right_bin: 2nd bin // scale: size of the regularisation + // return value: number of conditions which have been skipped // modifies data member fLsquared Int_t il = fHistToX[left_bin]; Int_t ir = fHistToX[right_bin]; if ((il < 0) || (ir < 0)) { - std::cout << "TUnfold::RegularizeDerivative skip bins " - << left_bin << "," << right_bin << "\n"; - return; + return 1; } Double_t scale_squared = scale * scale; (*fLsquared) (il, il) += scale_squared; (*fLsquared) (il, ir) -= scale_squared; (*fLsquared) (ir, il) -= scale_squared; (*fLsquared) (ir, ir) += scale_squared; + return 0; } -void TUnfold::RegularizeCurvature(int left_bin, int center_bin, +Int_t TUnfold::RegularizeCurvature(int left_bin, int center_bin, int right_bin, Double_t const &scale_left, Double_t const &scale_right) @@ -688,6 +1128,7 @@ void TUnfold::RegularizeCurvature(int left_bin, int center_bin, // right_bin: 3rd bin // scale_left: scale factor on center-left difference // scale_right: scale factor on right-center difference + // return value: number of conditions which have been skipped // modifies data member fLsquared Int_t il, ic, ir; @@ -695,9 +1136,7 @@ void TUnfold::RegularizeCurvature(int left_bin, int center_bin, ic = fHistToX[center_bin]; ir = fHistToX[right_bin]; if ((il < 0) || (ic < 0) || (ir < 0)) { - std::cout << "TUnfold::RegularizeCurvature skip bins " - << left_bin << "," << center_bin << "," << right_bin << "\n"; - return; + return 1; } Double_t r[3]; r[0] = -scale_left; @@ -713,9 +1152,10 @@ void TUnfold::RegularizeCurvature(int left_bin, int center_bin, (*fLsquared) (ir, il) += r[2] * r[0]; (*fLsquared) (ir, ic) += r[2] * r[1]; (*fLsquared) (ir, ir) += r[2] * r[2]; + return 0; } -void TUnfold::RegularizeBins(int start, int step, int nbin, +Int_t TUnfold::RegularizeBins(int start, int step, int nbin, ERegMode regmode) { // set regulatisation on a 1-dimensional curve @@ -723,6 +1163,8 @@ void TUnfold::RegularizeBins(int start, int step, int nbin, // step: distance between neighbouring bins // nbin: total number of bins // regmode: regularisation mode + // return value: + // number of errors (i.e. conditions which have been skipped) // modifies data member fLsquared Int_t i0, i1, i2; @@ -730,25 +1172,27 @@ void TUnfold::RegularizeBins(int start, int step, int nbin, i1 = i0 + step; i2 = i1 + step; Int_t nSkip = 0; + Int_t nError= 0; if (regmode == kRegModeDerivative) nSkip = 1; else if (regmode == kRegModeCurvature) nSkip = 2; for (Int_t i = nSkip; i < nbin; i++) { if (regmode == kRegModeSize) { - RegularizeSize(i0); + nError += RegularizeSize(i0); } else if (regmode == kRegModeDerivative) { - RegularizeDerivative(i0, i1); + nError += RegularizeDerivative(i0, i1); } else if (regmode == kRegModeCurvature) { - RegularizeCurvature(i0, i1, i2); + nError += RegularizeCurvature(i0, i1, i2); } i0 = i1; i1 = i2; i2 += step; } + return nError; } -void TUnfold::RegularizeBins2D(int start_bin, int step1, int nbin1, +Int_t TUnfold::RegularizeBins2D(int start_bin, int step1, int nbin1, int step2, int nbin2, ERegMode regmode) { // set regularisation on a 2-dimensional grid of bins @@ -757,14 +1201,18 @@ void TUnfold::RegularizeBins2D(int start_bin, int step1, int nbin1, // nbin1: number of bins in 1st direction // step2: distance between bins in 2nd direction // nbin2: number of bins in 2nd direction - // modifies data member fLsquared + // return value: + // number of errors (i.e. conditions which have been skipped) + // modifies data member fLsquared + Int_t nError = 0; for (Int_t i1 = 0; i1 < nbin1; i1++) { - RegularizeBins(start_bin + step1 * i1, step2, nbin2, regmode); + nError += RegularizeBins(start_bin + step1 * i1, step2, nbin2, regmode); } for (Int_t i2 = 0; i2 < nbin2; i2++) { - RegularizeBins(start_bin + step2 * i2, step1, nbin1, regmode); + nError += RegularizeBins(start_bin + step2 * i2, step1, nbin1, regmode); } + return nError; } Double_t TUnfold::DoUnfold(Double_t const &tau_reg, TH1 const *input, @@ -789,35 +1237,61 @@ Double_t TUnfold::DoUnfold(Double_t const &tau_reg, TH1 const *input, return DoUnfold(tau_reg); } -void TUnfold::SetInput(TH1 const *input, Double_t const &scaleBias) { +Int_t TUnfold::SetInput(TH1 const *input, Double_t const &scaleBias, + Double_t oneOverZeroError) { // 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 // Data members modified: - // fY, fV, fBiasScale + // fY, fV, fBiasScale, fNdf fBiasScale = scaleBias; + // construct inverted error matrix of measured quantities // from errors of input histogram - Int_t *row_V=new Int_t[GetNy() + 1]; - Int_t *col_V=new Int_t[GetNy()]; - Double_t *data_V=new Double_t[GetNy()]; - // fV is set up in increasing row/column order - // to avoid too much memory management + // and count number of degrees of freedom + fNdf = -GetNpar(); + Int_t *rowColV=new Int_t[GetNy()]; + Double_t *dataV=new Double_t[GetNy()]; + Int_t nError=0; for (Int_t iy = 0; iy < GetNy(); iy++) { Double_t dy = input->GetBinError(iy + 1); - if (dy <= 0.0) - dy = 1.0; - row_V[iy] = iy; - col_V[iy] = iy; - data_V[iy] = 1 / dy / dy; + rowColV[iy] = iy; + if (dy <= 0.0) { + nError++; + dataV[iy] = oneOverZeroError*oneOverZeroError; + } else { + dataV[iy] = 1. / dy / dy; + } + if( dataV[iy]>0.0) fNdf ++; + } + if(nError>0) { + if(nError>1) { + std::cout + <<"TUnfold::SetInput "<<nError<<" input bins have zero error. "; + } else { + std::cout + <<"TUnfold::SetInput "<<nError<<" input bin has zero error. "; + } + if(oneOverZeroError !=0.0) { + std::cout<<"1/error is set to "<<oneOverZeroError<<"\n"; + } else { + if(nError>1) { + std::cout + <<"These bins are ignored.\n"; + } else { + std::cout + <<"This bin is ignored.\n"; + } + } } - row_V[GetNy()] = GetNy(); if (fV) delete fV; - fV = CreateSparseMatrix(GetNy(), GetNy(), row_V, col_V, data_V); - delete[] row_V; - delete[] col_V; - delete[] data_V; + fV = new TMatrixDSparse(GetNy(),GetNy()); + fV->SetMatrixArray(GetNy(),rowColV,rowColV, dataV); + delete[] rowColV; + delete[] dataV; // // get input vector if (fY) @@ -825,7 +1299,8 @@ void TUnfold::SetInput(TH1 const *input, Double_t const &scaleBias) { fY = new TMatrixD(GetNy(), 1); for (Int_t i = 0; i < GetNy(); i++) { (*fY) (i, 0) = input->GetBinContent(i + 1); - } + } + return nError; } Double_t TUnfold::DoUnfold(Double_t const &tau) { @@ -944,12 +1419,19 @@ Int_t TUnfold::ScanLcurve(Int_t nPoint, } // create curvature Spline TSpline3 *splineC=new TSpline3("L curve curvature",cTi,cCi,n-1); - Double_t cCmax=cCi[0]; - Double_t cTmax=cTi[0]; // find the maximum of the curvature - for(Int_t i=0;i<n-2;i++) { + // 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 teh 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] and x[i+1] + // check boundary conditions for x[i+1] Double_t xMax=cTi[i+1]; Double_t yMax=cCi[i+1]; if(cCi[i]>yMax) { @@ -1004,6 +1486,15 @@ Int_t TUnfold::ScanLcurve(Int_t nPoint, 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; @@ -1054,14 +1545,7 @@ TH1D *TUnfold::GetOutput(char const *name, char const *title, xmax = nbins; } TH1D *out = new TH1D(name, title, nbins, xmin, xmax); -#ifdef UNUSED - for (Int_t i = 0; i < GetNx(); i++) { - out->SetBinContent(fXToHist[i], (*fX) (i, 0)); - out->SetBinError(fXToHist[i], TMath::Sqrt((*fE) (i, i))); - } -#else GetOutput(out); -#endif return out; } @@ -1132,11 +1616,13 @@ TH1D *TUnfold::GetInput(char const *name, char const *title, y1 = GetNy(); } TH1D *out = new TH1D(name, title, GetNy(), y0, y1); - TMatrixD Vinv(TMatrixD::kInverted, *fV); + + TMatrixD *Vinv=InvertMSparse(*fV); for (Int_t i = 0; i < GetNy(); i++) { out->SetBinContent(i + 1, (*fY) (i, 0)); - out->SetBinError(i + 1, TMath::Sqrt(Vinv(i, i))); + out->SetBinError(i + 1, TMath::Sqrt((*Vinv)(i, i))); } + delete Vinv; return out; } @@ -1156,19 +1642,7 @@ TH2D *TUnfold::GetRhoIJ(char const *name, char const *title, xmax = nbins; } TH2D *out = new TH2D(name, title, nbins, xmin, xmax, nbins, xmin, xmax); -#ifdef UNUSED - for (Int_t i = 0; i < GetNx(); i++) { - for (Int_t j = 0; j < GetNx(); j++) { - out->SetBinContent(fXToHist[i], fXToHist[j], - (*fE) (i, - j) / TMath::Sqrt((*fE) (i, - i) * (*fE) (j, - j))); - } - } -#else GetRhoIJ(out); -#endif return out; } @@ -1187,15 +1661,7 @@ TH2D *TUnfold::GetEmatrix(char const *name, char const *title, xmax = nbins; } TH2D *out = new TH2D(name, title, nbins, xmin, xmax, nbins, xmin, xmax); -#ifdef UNUSED - for (Int_t i = 0; i < GetNx(); i++) { - for (Int_t j = 0; j < GetNx(); j++) { - out->SetBinContent(fXToHist[i], fXToHist[j], (*fE) (i, j)); - } - } -#else GetEmatrix(out); -#endif return out; } @@ -1295,9 +1761,21 @@ Double_t const &TUnfold::GetChi2L(void) const return fChi2L; } +Int_t TUnfold::GetNdf(void) const +{ + // return number of degrees of freedom + return fNdf; +} + +Int_t TUnfold::GetNpar(void) const +{ + // return number of parameters + return GetNx(); +} + Double_t TUnfold::GetLcurveX(void) const { // return value on x axis of L curve - return TMath::Log10(fChi2A+fChi2L); + return TMath::Log10(fChi2A); } Double_t TUnfold::GetLcurveY(void) const { diff --git a/tutorials/math/testUnfold1.C b/tutorials/math/testUnfold1.C index 1032dde2d1c07d2e0871a5070a39c6311e668ddb..17ac7fdc46d04db05811e5ad711351d01cdb5c74 100644 --- a/tutorials/math/testUnfold1.C +++ b/tutorials/math/testUnfold1.C @@ -1,10 +1,14 @@ -// Test program for the class TUnfold // Author: Stefan Schmitt // DESY, 14.10.2008 -// Version 6a, fix problem with dynamic array allocation under windows +// Version 11, print chi**2 and number of degrees of freedom // // History: +// Version 10, with bug-fix in TUnfold.cxx +// Version 9, with bug-fix in TUnfold.cxx and TUnfold.h +// Version 8, with bug-fix in TUnfold.cxx and TUnfold.h +// Version 7, with bug-fix in TUnfold.cxx and TUnfold.h +// Version 6a, fix problem with dynamic array allocation under windows // Version 6, bug-fixes in TUnfold.C // Version 5, replace main() by testUnfold1() // Version 4, with bug-fix in TUnfold.C @@ -253,11 +257,12 @@ int testUnfold1() Int_t iBest; TSpline *logTauX,*logTauY; TGraph *lCurve; - // this method scans the parameter tau and finds the kink in the L curve // finally, the unfolding is done for the best choice of tau iBest=unfold.ScanLcurve(nScan,tauMin,tauMax,&lCurve,&logTauX,&logTauY); std::cout<<"tau="<<unfold.GetTau()<<"\n"; + std::cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L() + <<" / "<<unfold.GetNdf()<<"\n"; // save graphs with one point to visualize best choice of tau Double_t t[1],x[1],y[1]; @@ -302,7 +307,6 @@ int testUnfold1() // fit Breit-Wigner shape to unfolded data, using the full error matrix // here we use a "user" chi**2 function to take into account // the full covariance matrix - //TVirtualFitter::SetDefaultFitter("TMinuit"); gFitter=TVirtualFitter::Fitter(histMunfold); gFitter->SetFCN(chisquare_corr); @@ -372,6 +376,7 @@ int testUnfold1() bestLcurve->Draw("*"); output.SaveAs("c1.ps"); + return 0; } diff --git a/tutorials/math/testUnfold2.C b/tutorials/math/testUnfold2.C index 61c69dec7ffef7c0914b88d8325d09104ce7ca85..7477c93a95007a05f0afc350019877ac596f4188 100644 --- a/tutorials/math/testUnfold2.C +++ b/tutorials/math/testUnfold2.C @@ -1,10 +1,14 @@ -// Test program for the class MyUnfold, derived from TUnfold // Author: Stefan Schmitt // DESY, 14.10.2008 -// Version 6a, fix problem with dynamic array allocation under windows +// Version 11, print chi**2 and number of degrees of freedom // // History: +// Version 10, with bug-fix in TUnfold.cxx +// Version 9, with bug-fix in TUnfold.cxx, TUnfold.h +// Version 8, with bug-fix in TUnfold.cxx, TUnfold.h +// Version 7, with bug-fix in TUnfold.cxx, TUnfold.h +// Version 6a, fix problem with dynamic array allocation under windows // Version 6, re-include class MyUnfold in the example // Version 5, move class MyUnfold to seperate files // Version 4, with bug-fix in TUnfold.C @@ -67,7 +71,7 @@ protected: Int_t const *fBinMap; // bin mapping to extract the global correlation Double_t fTauBest; // tau with the smallest correlation Double_t fRhoMin; // smallest correlation - ClassDef(MyUnfold,0); + //ClassDef(MyUnfold,0); }; //ClassImp(MyUnfold) @@ -350,6 +354,8 @@ int testUnfold2() // finally, the unfolding is done for the best choice of tau iBest=unfold.ScanLcurve(nScan,tauMin,tauMax,&lCurve,&logTauX,&logTauY); std::cout<<"tau="<<unfold.GetTau()<<"\n"; + std::cout<<"chi**2="<<unfold.GetChi2A()<<"+"<<unfold.GetChi2L() + <<" / "<<unfold.GetNdf()<<"\n"; Double_t t[1],x[1],y[1]; logTauX->GetKnot(iBest,t[0],x[0]); logTauY->GetKnot(iBest,t[0],y[0]);