CMS 3D CMS Logo

/afs/cern.ch/work/a/aaltunda/public/www/CMSSW_6_2_5/src/TopQuarkAnalysis/TopHitFit/src/Chisq_Constrainer.cc

Go to the documentation of this file.
00001 //
00002 // $Id: Chisq_Constrainer.cc,v 1.2 2011/10/13 09:49:48 snaumann Exp $
00003 //
00004 // File: src/Chisq_Constrainer.cc
00005 // Purpose: Minimize a chisq subject to a set of constraints.
00006 //          Based on the SQUAW algorithm.
00007 // Created: Jul, 2000, sss, based on run 1 mass analysis code.
00008 //
00009 // CMSSW File      : src/Chisq_Constrainer.cc
00010 // Original Author : Scott Stuart Snyder <snyder@bnl.gov> for D0
00011 // Imported to CMSSW by Haryo Sumowidagdo <Suharyo.Sumowidagdo@cern.ch>
00012 //
00013 
00039 #include "TopQuarkAnalysis/TopHitFit/interface/Chisq_Constrainer.h"
00040 #include "TopQuarkAnalysis/TopHitFit/interface/Defaults.h"
00041 #include <cmath>
00042 #include <cassert>
00043 #include <iostream>
00044 #include <iomanip>
00045 
00046 using std::abs;
00047 using std::cout;
00048 using std::fixed;
00049 using std::ios_base;
00050 using std::ostream;
00051 using std::resetiosflags;
00052 using std::setiosflags;
00053 using std::sqrt;
00054 
00055 
00056 //*************************************************************************
00057 
00058 
00059 namespace hitfit {
00060 
00061 
00062 Chisq_Constrainer_Args::Chisq_Constrainer_Args (const Defaults& defs)
00063 //
00064 // Purpose: Constructor.
00065 //
00066 // Inputs:
00067 //   defs -        The Defaults instance from which to initialize.
00068 //
00069   : _base_constrainer_args (defs)
00070 {
00071   _use_G = defs.get_bool ("use_G");
00072   _printfit = defs.get_bool ("printfit");
00073   _constraint_sum_eps = defs.get_float ("constraint_sum_eps");
00074   _chisq_diff_eps = defs.get_float ("chisq_diff_eps");
00075   _maxit = defs.get_int ("maxit");
00076   _max_cut = defs.get_int ("maxcut");
00077   _cutsize = defs.get_float ("cutsize");
00078   _min_tot_cutsize = defs.get_float ("min_tot_cutsize");
00079   _chisq_test_eps = defs.get_float ("chisq_test_eps");
00080 }
00081 
00082 
00083 bool Chisq_Constrainer_Args::printfit () const
00084 //
00085 // Purpose: Return the printfit parameter.
00086 //          See the header for documentation.
00087 //
00088 {
00089   return _printfit;
00090 }
00091 
00092 
00093 bool Chisq_Constrainer_Args::use_G () const
00094 //
00095 // Purpose: Return the use_G parameter.
00096 //          See the header for documentation.
00097 //
00098 {
00099   return _use_G;
00100 }
00101 
00102 
00103 double Chisq_Constrainer_Args::constraint_sum_eps () const
00104 //
00105 // Purpose: Return the constraint_sum_eps parameter.
00106 //          See the header for documentation.
00107 //
00108 {
00109   return _constraint_sum_eps;
00110 }
00111 
00112 
00113 double Chisq_Constrainer_Args::chisq_diff_eps () const
00114 //
00115 // Purpose: Return the chisq_diff_eps parameter.
00116 //          See the header for documentation.
00117 //
00118 {
00119   return _chisq_diff_eps;
00120 }
00121 
00122 
00123 unsigned  Chisq_Constrainer_Args::maxit () const
00124 //
00125 // Purpose: Return the maxit parameter.
00126 //          See the header for documentation.
00127 //
00128 {
00129   return _maxit;
00130 }
00131 
00132 
00133 unsigned  Chisq_Constrainer_Args::max_cut () const
00134 //
00135 // Purpose: Return the max_cut parameter.
00136 //          See the header for documentation.
00137 //
00138 {
00139   return _max_cut;
00140 }
00141 
00142 
00143 double Chisq_Constrainer_Args::cutsize () const
00144 //
00145 // Purpose: Return the cutsize parameter.
00146 //          See the header for documentation.
00147 //
00148 {
00149   return _cutsize;
00150 }
00151 
00152 
00153 double Chisq_Constrainer_Args::min_tot_cutsize () const
00154 //
00155 // Purpose: Return the min_tot_cutsize parameter.
00156 //          See the header for documentation.
00157 //
00158 {
00159   return _min_tot_cutsize;
00160 }
00161 
00162 
00163 double Chisq_Constrainer_Args::chisq_test_eps () const
00164 //
00165 // Purpose: Return the chisq_test_eps parameter.
00166 //          See the header for documentation.
00167 //
00168 {
00169   return _chisq_test_eps;
00170 }
00171 
00172 
00173 const Base_Constrainer_Args&
00174 Chisq_Constrainer_Args::base_constrainer_args () const
00175 //
00176 // Purpose: Return the contained subobject parameters.
00177 //          See the header for documentation.
00178 //
00179 {
00180   return _base_constrainer_args;
00181 }
00182 
00183 
00184 } // namespace hitfit
00185 
00186 
00187 //*************************************************************************
00188 
00189 
00190 namespace {
00191 
00192 
00193 using namespace hitfit;
00194 
00195 
00212 bool solve_linear_system (const Matrix& H,
00213                           const Diagonal_Matrix& Y,
00214                           const Matrix& By,
00215                           const Row_Vector& r,
00216                           Column_Vector& alpha,
00217                           Column_Vector& d,
00218                           Matrix& W,
00219                           Matrix& U,
00220                           Matrix& V)
00221 //
00222 // Purpose: Solve the system
00223 //
00224 //   [ -H  B.t ]   [ alpha ]     [ r ]
00225 //   [         ] * [       ]  =  [   ]
00226 //   [  B  Y   ]   [   d   ]     [ 0 ]
00227 // 
00228 //  for alpha and d.
00229 // 
00230 //  Also returns the inverse matrices:
00231 // 
00232 //   [ W  V.t ]     [ -H  B.t ]
00233 //   [        ]  =  [         ] ^ -1
00234 //   [ V  U   ]     [  B  Y   ]
00235 // 
00236 //  Returns true if successful, false if not.
00237 //
00238 {
00239   int nconstraints = H.num_row();
00240   int nbadvars = Y.num_row();
00241 
00242   // Form the matrix on the LHS from H, By, and Y.
00243   Matrix A (nconstraints+nbadvars, nconstraints+nbadvars);
00244   A.sub (1, 1, -H);
00245   if (nbadvars > 0) {
00246     A.sub (nconstraints+1, nconstraints+1, Y);
00247     A.sub (1, nconstraints+1, By.T());
00248     A.sub (nconstraints+1, 1, By);
00249   }
00250 
00251   // Form the RHS vector from r.
00252   Column_Vector yy(nconstraints + nbadvars, 0);
00253   yy.sub (1, r.T());
00254 
00255   // Invert the matrix.
00256   // Try to handle singularities correctly.
00257   Matrix Ai;
00258   int ierr = 0;
00259   do {
00260     Ai = A.inverse (ierr);
00261     if (ierr) {
00262       int allzero = 0;
00263       for (int i=1; i<=nconstraints; i++) {
00264     allzero = 1;
00265     for (int j=1; j<=nconstraints; j++) {
00266       if (A(i,j) != 0) {
00267         allzero = 0;
00268         break;
00269       }
00270     }
00271     if (allzero) {
00272       A(i,i) = 1;
00273       break;
00274     }
00275       }
00276       if (!allzero) return false;
00277     }
00278   } while (ierr != 0);
00279 
00280   // Solve the system of equations.
00281   Column_Vector xx = Ai * yy;
00282 
00283   // Extract the needed pieces from the inverted matrix
00284   // and the solution vector.
00285   W = Ai.sub (1, nconstraints, 1, nconstraints);
00286   if (nbadvars > 0) {
00287     U = Ai.sub (nconstraints+1, nconstraints+nbadvars,
00288                 nconstraints+1, nconstraints+nbadvars);
00289     V = Ai.sub (nconstraints+1, nconstraints+nbadvars, 1, nconstraints);
00290     d = xx.sub (nconstraints+1, nconstraints+nbadvars);
00291   }
00292 
00293   alpha = xx.sub (1, nconstraints);
00294 
00295   return true;
00296 }
00297 
00298 
00299 } // unnamed namespace
00300 
00301 
00302 namespace hitfit {
00303 
00304 
00305 //*************************************************************************
00306 
00307 
00308 Chisq_Constrainer::Chisq_Constrainer (const Chisq_Constrainer_Args& args)
00309 //
00310 // Purpose: Constructor.
00311 //
00312 // Inputs:
00313 //   args -        The parameter settings for this instance.
00314 //
00315   : Base_Constrainer (args.base_constrainer_args()),
00316     _args (args)
00317 {
00318 }
00319 
00320 
00321 double Chisq_Constrainer::fit (Constraint_Calculator& constraint_calculator,
00322                                const Column_Vector& xm,
00323                                Column_Vector& x,
00324                                const Column_Vector& ym,
00325                                Column_Vector& y,
00326                                const Matrix& G_i,
00327                                const Diagonal_Matrix& Y,
00328                                Column_Vector& pullx,
00329                                Column_Vector& pully,
00330                                Matrix& Q,
00331                                Matrix& R,
00332                                Matrix& S)
00333 //
00334 // Purpose: Do a constrained fit.
00335 //
00336 // Call the number of well-measured variables Nw, the number of
00337 // poorly-measured variables Np, and the number of constraints Nc.
00338 //
00339 // Inputs:
00340 //   constraint_calculator - The object that will be used to evaluate
00341 //                   the constraints.
00342 //   xm(Nw)      - The measured values of the well-measured variables.
00343 //   ym(Np)      - The measured values of the poorly-measured variables.
00344 //   x(Nw)       - The starting values for the well-measured variables.
00345 //   y(Np)       - The starting values for the poorly-measured variables.
00346 //   G_i(Nw,Nw)  - The error matrix for the well-measured variables.
00347 //   Y(Np,Np)    - The inverse error matrix for the poorly-measured variables.
00348 //
00349 // Outputs:
00350 //   x(Nw)       - The fit values of the well-measured variables.
00351 //   y(Np)       - The fit values of the poorly-measured variables.
00352 //   pullx(Nw)   - The pull quantities for the well-measured variables.
00353 //   pully(Nw)   - The pull quantities for the poorly-measured variables.
00354 //   Q(Nw,Nw)    - The final error matrix for the well-measured variables.
00355 //   R(Np,Np)    - The final error matrix for the poorly-measured variables.
00356 //   S(Nw,Np)    - The final cross error matrix for the two sets of variables.
00357 //
00358 // Returns:
00359 //   The minimum chisq satisfying the constraints.
00360 //   Returns a value < 0 if the fit failed to converge.
00361 //
00362 {
00363   // Check that the various matrices we've been passed have consistent
00364   // dimensionalities.
00365   int nvars = x.num_row();
00366   assert (nvars == G_i.num_col());
00367   assert (nvars == xm.num_row());
00368 
00369   int nbadvars = y.num_row();
00370   assert (nbadvars == Y.num_col());
00371   assert (nbadvars == ym.num_row());
00372 
00373   // If we're going to check the chisq calculation by explicitly using G,
00374   // calculate it now from its inverse G_i.
00375   Matrix G (nvars, nvars);
00376   if (_args.use_G()) {
00377     int ierr = 0;
00378     G = G_i.inverse (ierr);
00379     assert (!ierr);
00380   }
00381 
00382   int nconstraints = constraint_calculator.nconstraints ();
00383 
00384   // Results of the constraint evaluation function.
00385   Row_Vector F (nconstraints);             // Constraint vector.
00386   Matrix Bx (nvars, nconstraints);         // Gradients wrt x
00387   Matrix By (nbadvars, nconstraints);      // Gradients wrt y
00388 
00389   // (2) Evaluate the constraints at the starting point.
00390   // If the starting point is rejected as invalid,
00391   // give up and return an error.
00392   if (! call_constraint_fcn (constraint_calculator, x, y, F, Bx, By)) {
00393       //    cout << "Bad initial values!";
00394       //    return -1000;
00395       return -999.0;
00396   }
00397 
00398   // (3) Initialize variables for the fitting loop.
00399   double constraint_sum_last = -1000;
00400   double chisq_last = -1000;
00401   bool near_convergence = false;
00402   double last_step_cutsize = 1;
00403 
00404   unsigned nit = 0;
00405 
00406   // Initialize the displacement vectors c and d.
00407   Column_Vector c = x - xm;
00408   Column_Vector d = y - ym;
00409 
00410   Matrix E (nvars, nconstraints);
00411   Matrix W (nconstraints, nconstraints);
00412   Matrix U (nbadvars, nbadvars);
00413   Matrix V (nbadvars, nconstraints);
00414 
00415   // (4) Fitting loop:
00416   do {
00417     // (5) Calculate E, H, and r.
00418     E = G_i * Bx;
00419     Matrix H = E.T() * Bx;
00420     Row_Vector r = c.T() * Bx + d.T() * By - F;
00421 
00422     // (6) Solve the linearized system for the new values
00423     // of the Lagrange multipliers
00424     // $\alpha$ and the new value for the displacements d.
00425     Column_Vector alpha (nvars);
00426     Column_Vector d1 (nbadvars);
00427     if (!solve_linear_system (H, Y, By, r,
00428                               alpha, d1, W, U, V)) {
00430         //      return -1000;
00431         return -998.0;
00432     }
00433 
00434     // (7) Compute the new values for the displacements c and the chisq.
00435     Column_Vector c1 = -E * alpha;
00436     double chisq =  - scalar (r * alpha);
00437 
00438     double psi_cut = 0;
00439 
00440     // (8) Find where this step is going to be taking us.
00441     x = c1 + xm;
00442     y = d1 + ym;
00443 
00444     // (9) Set up for cutting this step, should we have to.
00445     Matrix save_By = By;
00446     Row_Vector save_negF = - F;
00447     double this_step_cutsize = 1;
00448     double constraint_sum = -1;
00449     unsigned ncut = 0;
00450 
00451     // (10) Evaluate the constraints at the new point.
00452     // If the point is rejected, we have to try to cut the step.
00453     // We accept the step if:
00454     //  The constraint sum is below the convergence threshold
00455     //    constraint_sum_eps, or
00456     //  This is the first iteration, or
00457     //  The constraint sum has decreased since the last iteration.
00458     // Otherwise, the constraints have gotten worse, and we
00459     // try to cut the step.
00460     while (! call_constraint_fcn (constraint_calculator, x, y, F, Bx, By) ||
00461        ((constraint_sum = norm_infinity (F))
00462               > _args.constraint_sum_eps() &&
00463         nit > 0 &&
00464         constraint_sum > constraint_sum_last))
00465     {
00466 
00467       // Doing step cutting...
00468       if (nit > 0 && _args.printfit() && ncut == 0) {
00469     cout << "(" << chisq << " " << chisq_last << ") ";
00470       }
00471 
00472       // (10a) If this is the first time we've tried to cut this step,
00473       // test to see if the chisq is stationary.  If it hasn't changed
00474       // since the last iteration, try a directed step.
00475       if (ncut == 0 &&
00476       abs (chisq - chisq_last) < _args.chisq_diff_eps()) {
00477 
00478     // Trying a directed step now.
00479     // Try to make the smallest step which satisfies the
00480     // (linearized) constraints.
00481     if (_args.printfit())
00482       cout << " directed step ";
00483 
00484     // (10a.i) Solve the linearized system for $\beta$ and
00485     // the y-displacement vector $\delta$.
00486     Column_Vector beta (nconstraints);
00487     Column_Vector delta (nbadvars);
00488     solve_linear_system (H, Y, save_By, save_negF,
00489                  beta, delta, W, U, V);
00490 
00491     // (10a.ii) Get the x-displacement vector $\gamma$.
00492     Column_Vector gamma = -E * beta;
00493 
00494     // (10a.iii) Find the destination of the directed step.
00495     x = c + xm + gamma;
00496     y = d + ym + delta;
00497 
00498     // (10a.iv) Accept this point if it's not rejected by the constraint
00499     // function, and the constraints improve.
00500     if (call_constraint_fcn (constraint_calculator, x, y, F, Bx, By) &&
00501         (constraint_sum = norm_infinity (F)) > 0 &&
00502         (constraint_sum < constraint_sum_last)) {
00503 
00504       // Accept this step.  Calculate the chisq and new displacement
00505       // vectors.
00506       chisq = chisq_last - scalar ((-save_negF + r*2) * beta);
00507       c1 = x - xm;
00508       d1 = y - ym;
00509 
00510       // Exit from step cutting loop.
00511       break;
00512     }
00513       }
00514 
00515       // If this is the first time we're cutting the step,
00516       // initialize $\psi$.
00517       if (ncut == 0)
00518     psi_cut = scalar ((save_negF - r) * alpha);
00519 
00520       // (10b) Give up if we've tried to cut this step too many times.
00521       if (++ncut > _args.max_cut()) {
00522           //    cout << " Too many cut steps ";
00523           //    return -1000;
00524           return -997.0;
00525       }
00526 
00527       // (10c) Set up the size by which we're going to cut this step.
00528       // Normally, this is cutsize.  But if this is the first time we're
00529       // cutting this step and the last step was also cut, set the cut
00530       // size to twice the final cut size from the last step (provided
00531       // that it is less than cutsize).
00532       double this_cutsize = _args.cutsize();
00533       if (ncut == 1 && last_step_cutsize < 1) {
00534     this_cutsize = 2 * last_step_cutsize;
00535     if (this_cutsize > _args.cutsize())
00536       this_cutsize = _args.cutsize();
00537       }
00538 
00539       // (10d) Keep track of the total amount by which we've cut this step.
00540       this_step_cutsize *= this_cutsize;
00541 
00542       // If it falls below min_tot_cutsize, give up.
00543       if (this_step_cutsize < _args.min_tot_cutsize()) {
00544           //    cout << "Cut size underflow ";
00545           //    return -1000;
00546           return -996.0;
00547       }
00548 
00549       // (10e) Cut the step: calculate the new displacement vectors.
00550       double cutleft = 1 - this_cutsize;
00551       c1 = c1 * this_cutsize + c * cutleft;
00552       d1 = d1 * this_cutsize + d * cutleft;
00553 
00554       // (10f) Calculate the new chisq.
00555       if (chisq_last >= 0) {
00556     chisq = this_cutsize*this_cutsize * chisq +
00557             cutleft*cutleft * chisq_last +
00558                2*this_cutsize*cutleft * psi_cut;
00559     psi_cut = this_cutsize * psi_cut + cutleft * chisq_last;
00560       }
00561       else
00562     chisq = chisq_last;
00563 
00564       // Log what we've done.
00565       if (_args.printfit()) {
00566         cout << constraint_sum << " cut " << ncut << " size "
00567              << setiosflags (ios_base::scientific)
00568              << this_cutsize << " tot size " << this_step_cutsize
00569              << resetiosflags (ios_base::scientific)
00570              << " " << chisq << "\n";
00571       }
00572 
00573       // Find the new step destination.
00574       x = c1 + xm;
00575       y = d1 + ym;
00576 
00577       // Now, go and test the step again for acceptability.
00578     }
00579 
00580     // (11) At this point, we have an acceptable step.
00581     // Shuffle things around to prepare for the next step.
00582     last_step_cutsize = this_step_cutsize;
00583 
00584     // If requested, calculate the chisq using G to test for
00585     // possible loss of precision.
00586     double chisq_b = 0;
00587     if (_args.use_G()) {
00588       chisq_b = scalar (c1.T() * G * c1) + scalar (d1.T() * Y * d1);
00589       if (chisq >= 0 &&
00590       abs ((chisq - chisq_b) / chisq) > _args.chisq_test_eps()) {
00591     cout << chisq << " " << chisq_b
00592          << "lost precision?\n";
00593     abort ();
00594       }
00595     }
00596 
00597     // Log what we're doing.
00598     if (_args.printfit()) {
00599       cout << chisq << " ";
00600       if (_args.use_G())
00601     cout << chisq_b << " ";
00602     }
00603 
00604     double z2 = abs (chisq - chisq_last);
00605 
00606     if (_args.printfit()) {
00607       cout << constraint_sum << " " << z2 << "\n";
00608     }
00609 
00610     c = c1;
00611     d = d1;
00612     chisq_last = chisq;
00613     constraint_sum_last = constraint_sum;
00614 
00615     // (12) Test for convergence.  The conditions must be satisfied
00616     // for two iterations in a row.
00617     if (chisq >= 0 && constraint_sum < _args.constraint_sum_eps() &&
00618     z2 < _args.chisq_diff_eps())
00619     {
00620       if (near_convergence) break;  // Converged!  Exit loop.
00621       near_convergence = true;
00622     }
00623     else
00624       near_convergence = false;
00625 
00626     // (13) Give up if we've done this too many times.
00627     if (++nit > _args.maxit()) {
00628         //      cout << "too many iterations";
00629         //      return -1000;
00630         return -995.0;
00631     }
00632 
00633   } while (1);
00634 
00635   // (15) Ok, we have a successful fit!
00636 
00637 
00638   // Calculate the error matrices.
00639   Q = E * W * E.T();
00640   S = - E * V.T();
00641   R = U;
00642 
00643   // And the vectors of pull functions.
00644   pullx = Column_Vector (nvars);
00645   for (int i=1; i<=nvars; i++) {
00646     double a = Q(i,i);
00647     if (a < 0)
00648       pullx(i) = c(i) / sqrt (-a);
00649     else {
00650       pullx(i) = 0;
00651       //      cout << " bad pull fcn for var " << i << " (" << a << ") ";
00652     }
00653   }
00654 
00655   pully = Column_Vector (nbadvars);
00656   for (int i=1; i<=nbadvars; i++) {
00657     double a = 1 - Y(i,i)*R(i,i);
00658     if (a > 0)
00659       pully(i) = d(i) * sqrt (Y(i,i) / a);
00660     else {
00661       pully(i) = 0;
00662       //      cout << " bad pull fcn for badvar " << i << " ";
00663     }
00664   }
00665 
00666   // Finish calculation of Q.
00667   Q = Q + G_i;
00668 
00669   // Return the final chisq.
00670   return chisq_last;
00671 }
00672 
00673 
00674 std::ostream& Chisq_Constrainer::print (std:: ostream& s) const
00675 //
00676 // Purpose: Print our state.
00677 //
00678 // Inputs:
00679 //   s -           The stream to which to write.
00680 //
00681 // Returns:
00682 //   The stream S.
00683 //
00684 {
00685   Base_Constrainer::print (s);
00686   s << " printfit: " << _args.printfit()
00687     << "  use_G: " << _args.use_G() << "\n";
00688   s << " constraint_sum_eps: " << _args.constraint_sum_eps()
00689     << "  chisq_diff_eps: " << _args.chisq_diff_eps()
00690     << "  chisq_test_eps: " << _args.chisq_test_eps() << "\n";
00691   s << " maxit: " << _args.maxit()
00692     << "  max_cut: " << _args.max_cut()
00693     << "  min_tot_cutsize: " << _args.min_tot_cutsize()
00694     << "  cutsize: " << _args.cutsize() << "\n";
00695   return s;
00696 }
00697 
00698 
00699 } // namespace hitfit