//Renata Kopecna #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //Log of the covariance matrix status void printCovMatrixStatus(int status, bool squared){ std::string hesse = "Hesse"; if (squared) hesse = "Squared Hesse"; if (status==0) spdlog::warn(hesse + " returns 0: Hesse not calculated."); else if (status==1) spdlog::warn(hesse + " returns 1: Approximation only."); else if (status==2) spdlog::warn(hesse + " returns 2: Full, forced pos def"); else if (status==3) spdlog::info(hesse + " returns 3: ALL GOOD"); else spdlog::warn(hesse + " returns {0:d} -> SOMETHING VERY WRONG", status); return; } //Migrad status log so one doesn't have to look it up all the time void printMigradStatus(int status){ if (status==0) spdlog::info("MIGRAD returns 0: ALL GOOD"); else if (status==1) spdlog::warn("MIGRAD returns 1: Blank command, ignored."); else if (status==2) spdlog::warn("MIGRAD returns 2: Command line unreadable, ignored."); else if (status==3) spdlog::warn("MIGRAD returns 3: Unknown command, ignored"); else if (status==4) spdlog::warn("MIGRAD returns 4: Abnormal termination, MIGRAD not converged or something."); else if (status==9) spdlog::warn("MIGRAD returns 9: Reserved."); else if (status==10) spdlog::warn("MIGRAD returns 10: End command."); else if (status==11) spdlog::warn("MIGRAD returns 11: Exit/Stop command."); else if (status==12) spdlog::warn("MIGRAD returns 12: Return command."); else spdlog::warn("MIGRAD returns {0:d} -> SOMETHING VERY WRONG", status); return; } //set plotter void fcnc::fitter::set_plotters(plotter* plot) { std::vector p; p.push_back(plot); plots = p; return; } ///set plotters void fcnc::fitter::set_plotters(std::vector plotters) { plots = plotters; return; } ///define common parameters for all pdfs void fcnc::fitter::set_common_parameters(std::vector common_pars) { common_params = common_pars; return; } ///is the parameter with name one of the common parameters bool fcnc::fitter::is_common(std::string name) { for (unsigned int i=0; inparameters(); j++){ if (name == params.at(i)->get_parameter(j)->get_name()){ if (i==idx_i && j==idx_j) return true; else return false; } } } spdlog::critical("[FATAL]\t\tThe parameter '"+name+"' was not found!"); assert(0); return true; } ///returns the nth minuit index for parameter with name int fcnc::fitter::nth_minuit_index(std::string name, unsigned int n) { assert(n); assert(n <= params.size()); unsigned int start_param = 0; for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (name == params.at(i)->get_parameter(j)->get_name()){ if (n == 1) return start_param+j; else n--; } } start_param += minuit_one_fitter->params.at(i)->nparameters(); } return -1; } ///returns the first minuit index for parameter with name int fcnc::fitter::first_minuit_index(std::string name) { return nth_minuit_index(name, 1); } ///returns wether this is an analysis with blinded parameters bool fcnc::fitter::is_blind() { for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->is_blind()){ return true; } } } return false; } //WHAT THE FUCK IS THIS DOING HERE? int fcnc::fitter::fit(pdf* probs, parameters* parms, std::vector* ev, std::string index){ std::vector the_probs; the_probs.push_back(probs); std::vector the_params; the_params.push_back(parms); std::vector* > the_events; the_events.push_back(ev); return fit(the_probs, the_params, the_events, index); } void fcnc::fitter::init(pdf* probs, parameters* parms, std::vector* ev, std::string index){ std::vector the_probs; the_probs.push_back(probs); std::vector the_params; the_params.push_back(parms); std::vector* > the_events; the_events.push_back(ev); return init(the_probs, the_params, the_events, index); } void fcnc::fitter::minuit_one_fcn(Int_t &npar, Double_t *grad, Double_t &lh, Double_t *parameters, Int_t iflag){ //set the parameter sets according to minuits current parameter values stored in the params pointer //do not forget to set common parameters correctly. these have no directly corresponding minuit index, use first_index instead unsigned int start_param = 0; for (unsigned int i = 0; i < minuit_one_fitter->params.size(); i++){ for (unsigned int j = 0; j < minuit_one_fitter->params.at(i)->nparameters(); j++){ unsigned int index = start_param + j; std::string par_name = minuit_one_fitter->params.at(i)->get_parameter(j)->get_name(); if (minuit_one_fitter->is_common(par_name)) minuit_one_fitter->params.at(i)->get_parameter(j)->set_value(parameters[minuit_one_fitter->first_minuit_index(par_name)]); else minuit_one_fitter->params.at(i)->get_parameter(j)->set_value(parameters[index]); } start_param += minuit_one_fitter->params.at(i)->nparameters(); } lh = minuit_one_fitter->likelihood(); return; } fcnc::fitter::fitter(options* o): opts(o) { minuit_one = new TMinuit(1000);//params->nparameters()); minuit_one->SetFCN(&(minuit_one_fcn)); //this makes minuit use highest precision int errorcode; double strategy_list[1];// = {2.0} strategy_list[0] = opts->minuit_strategy; minuit_one->mnexcm("SET STR", strategy_list, 1, errorcode); minuit_one->SetMaxIterations(40000); //need to set this value to 0.5 if you do not use -2*ln(LH) minuit_one->SetErrorDef(1.0);//default spdlog::info("Machine precision: {0:f}", minuit_one->fEpsmac); //8.88178e-16 //verbose minuit //minuit_one->SetPrintLevel(o->print_level); //minuit_one->SetPrintLevel(-1); empirical_constant = 0.0; } fcnc::fitter::~fitter(){ delete minuit_one; } void fcnc::fitter::init(std::vector probs, std::vector parms, std::vector< std::vector * > ev, std::string index){ square_weights = false; //set all important pointers and initialize minuit_one_fitter = this; //this could be different for every fit (feldman-cousins) int errorcode; double strategy_list[1];// = {2.0} strategy_list[0] = opts->minuit_strategy; minuit_one->mnexcm("SET STR", strategy_list, 1, errorcode); //prob = probability; pdfs = probs; params = parms; events = ev; reset_param_values(); //this was originally before the reset... put here to catch reset of swave for (unsigned int i=0; iinit(params.at(i)); //update cached efficiencies in three situations //1. q2 fixed and regular acceptance //2. unfolded fit to determine the weight=1/eff //3. per event norm //cache fis if (opts->cache_fis){ spdlog::info("Caching fis"); for (unsigned int i = 0; i < pdfs.size(); i++) pdfs.at(i)->update_cached_integrated_fis(params.at(i)); } for (unsigned int i = 0; i < pdfs.size(); i++){ parameter* effq2 = params.at(i)->get_parameter("eff_q2"); bool q2fixed = false; if (effq2) q2fixed = (effq2->get_step_size() == 0.0); if ((q2fixed && opts->use_angular_acc) || opts->weighted_fit || opts->use_event_norm){ pdfs.at(i)->update_cached_efficiencies(params.at(i), events.at(i)); } } //per event xis if (opts->use_event_norm){ for (unsigned int i = 0; i < pdfs.size(); i++){ pdfs.at(i)->update_cached_xis(params.at(i), events.at(i)); } } } int fcnc::fitter::fit(std::vector probs, std::vector parms, std::vector< std::vector * > ev, std::string index){ //can only use either squared hesse or minos if(opts->minos_errors && opts->squared_hesse){ spdlog::critical("Cannot use both MINOS and squared HESSE for uncertainty deterimination. Choose one!"); assert(0); } square_weights = false; //set all important pointers and initialize minuit_one_fitter = this; //this could be different for every fit (feldman-cousins) int errorcode; double strategy_list[1];// = {2.0} strategy_list[0] = opts->minuit_strategy; minuit_one->mnexcm("SET STR", strategy_list, 1, errorcode); //prob = probability; pdfs = probs; params = parms; events = ev; //make sure to reduce output for blind analysis if (is_blind()) minuit_one->SetPrintLevel(-1); else if (spdlog_debug()) minuit_one->SetPrintLevel(0); else minuit_one->SetPrintLevel(-1); reset_param_values(); //this was originally before the reset... put here to catch reset of swave for (unsigned int i=0; iinit(params.at(i)); } //update cached efficiencies in three situations //1. q2 fixed and regular acceptance //2. unfolded fit to determine the weight=1/eff //3. per event norm //cache fis if (opts->cache_fis){ spdlog::info("Caching fis"); for (unsigned int i = 0; i < pdfs.size(); i++){ pdfs.at(i)->update_cached_integrated_fis(params.at(i)); } } for (unsigned int i = 0; i < pdfs.size(); i++){ parameter* effq2 = params.at(i)->get_parameter("eff_q2"); bool q2fixed = false; if (effq2) q2fixed = (effq2->get_step_size() == 0.0); if ((q2fixed && opts->use_angular_acc) || opts->weighted_fit || opts->use_event_norm){ pdfs.at(i)->update_cached_efficiencies(params.at(i), events.at(i)); } } //per event xis if (opts->use_event_norm){ for (unsigned int i = 0; i < pdfs.size(); i++){ pdfs.at(i)->update_cached_xis(params.at(i), events.at(i)); } } //get empirical factor empirical_constant = 0.0; if (opts->shift_lh){ unsigned int the_size=0; for (unsigned int i=0; isize(); empirical_constant = likelihood()/the_size; } spdlog::debug("Determined empirical constant of: {0:f}", empirical_constant); int result = 0; unsigned int varied_params = 0; //determine the number of varied parameters //count only parameters that are not common among the PDFs! for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0 && !is_common(params.at(i)->get_parameter(j)->get_name())){ varied_params++; spdlog::debug("[PDF{0:d}]\tPAR='"+params.at(i)->get_parameter(j)->get_name()+"'\trange=[{1:f} - {2:f}]", i+1, params.at(i)->get_parameter(j)->get_min(), params.at(i)->get_parameter(j)->get_max()); } } } spdlog::debug("Count number of varied parameters: {0:d} PDF(s)", params.size()); spdlog::debug("Number of varied, non-common parameters: {0:d}", varied_params); //assert(!(varied_params % params.size())); //add the parameters to the counter, that are common AND varied for (unsigned int i = 0; i < common_params.size(); i++){ bool is_varied = false; for (unsigned int j = 0; j < params.size(); j++){ for (unsigned int k = 0; k < params.at(j)->nparameters(); k++){ if (common_params.at(i) == params.at(j)->get_parameter(k)->get_name() && params.at(j)->get_parameter(k)->get_step_size() != 0.0){ is_varied = true; spdlog::debug("[COMMON]\tPAR='"+params.at(j)->get_parameter(k)->get_name()+"'\trange=[{0:f} - {1:f}]", params.at(j)->get_parameter(k)->get_min(), params.at(j)->get_parameter(k)->get_max()); break; } } if(is_varied)break; } if (is_varied){ varied_params++; } } spdlog::info("Including common parameters, number of varied parameters is: {0:d}", varied_params); //open texFile std::ofstream myFile; if (index != ""){ open_Latex_noteFile(latex_fitterFile(index), myFile); } //Use Markov Chain Monte Carlo //TODO when bored, move this to a separate function if (opts->use_mcmc){ unsigned int nlength = 10000; //just run a single chain std::vector chain;//(nlength); for (unsigned int i=0; inparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0 && !is_common(params.at(i)->get_parameter(j)->get_name())){ current[idx] = params.at(i)->get_parameter(j)->get_value(); SNminusone(idx, idx) = params.at(i)->get_parameter(j)->get_step_size();//this is actually quite clever idx++; } } } spdlog::debug("nrows {0:d}\tvaried_params {1:d}", current.GetNrows(), varied_params); for(int i=0; iGaus(0.0, 1.0);//could also use students t distribution } //shift depends on SNminusone TVectorD SW(SNminusone*WN); current = last + SW; finished = true; //this will disallow parameters outside the allowed range const bool constrain_parameters = true; if (constrain_parameters){ idx = 0; for (unsigned int j = 0; j < params.size(); j++){ for (unsigned int k = 0; k < params.at(j)->nparameters(); k++){ if (params.at(j)->get_parameter(k)->get_step_size() != 0.0 && !is_common(params.at(j)->get_parameter(k)->get_name())){ if (current[idx] > params.at(j)->get_parameter(k)->get_max() || current[idx] < params.at(j)->get_parameter(k)->get_min()){ finished = false; } idx++; } } } } } spdlog::debug("event {0:d}", i); //convert index back to parameter* and set to current point idx = 0; for (unsigned int j = 0; j < params.size(); j++){ for (unsigned int k = 0; k < params.at(j)->nparameters(); k++){ if (params.at(j)->get_parameter(k)->get_step_size() != 0.0 && !is_common(params.at(j)->get_parameter(k)->get_name())){ params.at(j)->get_parameter(k)->set_value(current[idx]); idx++; } } } for (unsigned int j=0; jinit(params.at(j)); } //compute likelihood of current point double current_llh = likelihood(); double r = rnd->Rndm(); double alpha = std::min(1.0, exp(0.5*(last_llh-current_llh)));//llhs are in fact Sum_i -2*log(P_i) //debug output if (spdlog_debug()){ for (int i=0; inparameters(); k++){ if (params.at(j)->get_parameter(k)->get_step_size() != 0.0 && !is_common(params.at(j)->get_parameter(k)->get_name())){ std::ostringstream hname; hname << "hist" << idx; histos[idx] = new TH1D(hname.str().c_str(), ";;", nbins, params.at(j)->get_parameter(k)->get_min(), params.at(j)->get_parameter(k)->get_max()); idx++; } } } //loop over chain and fill histos for (unsigned int i=0; iFill(v[row]); } if (true){//smooth all histos const unsigned int ntimes = 1; for (unsigned int i=0; iSmooth(ntimes); } if (spdlog_debug()){ //If in debug mode, just save the histos for now for (unsigned int i=0; iGetName()),"eps"); } } //get maximum from histos (for now no smoothing) and add up highes bins until 1 sigma //probably want to add from highest bin, no? TVectorD max(varied_params); TVectorD eup(varied_params); TVectorD edown(varied_params); for (unsigned int i=0; iGetMaximumBin(); double x = histos[i]->GetBinCenter(maxbin); double left = histos[i]->GetBinLowEdge(maxbin); double right = histos[i]->GetBinLowEdge(maxbin+1); double sum = histos[i]->GetBinContent(maxbin); histos[i]->SetBinContent(maxbin, 0.0); bool finished = sum/nlength > ONE_SIGMA; while (!finished){ maxbin = histos[i]->GetMaximumBin(); double nx = histos[i]->GetBinCenter(maxbin); sum += histos[i]->GetBinContent(maxbin); histos[i]->SetBinContent(maxbin, 0.0); if (nx > right) right = histos[i]->GetBinLowEdge(maxbin+1); if (nx < left) left = histos[i]->GetBinLowEdge(maxbin); finished = sum/nlength > ONE_SIGMA; } max[i] = x; eup[i] = right-x; edown[i] = left-x; delete histos[i]; } //output of the newly determined mean and errors if (spdlog_info()){ spdlog::info("max"); for (int i=0; inparameters(); k++){ if (params.at(j)->get_parameter(k)->get_step_size() != 0.0 && !is_common(params.at(j)->get_parameter(k)->get_name())){ params.at(j)->get_parameter(k)->set_value(max[idx]); params.at(j)->get_parameter(k)->set_error(0.5*(fabs(eup[idx])+fabs(edown[idx]))); params.at(j)->get_parameter(k)->set_error_up(eup[idx]); params.at(j)->get_parameter(k)->set_error_down(edown[idx]); //params.at(j)->get_parameter(k)->set_value(mean[idx]); //params.at(j)->get_parameter(k)->set_error(sqrt(cov(idx,idx))); idx++; } } } //MCMC always returns status OK return 300;//this means all ok } //When using no Markov Chain MC: Nominal fit procedure else{ //TODO when bored, move this to a separate function spdlog::debug("Starting Fit Procedure"); int errorcode; //double eps[1] = {1.0e-10}; //minuit_one->mnexcm("SET EPS", eps, 1, errorcode); //double strategy_list[2] = {0,0.1}; double migrad_options[2] = {MIG_MAX_CALLS, MIG_TOLERANCE}; //Maxcalls and tolerance // The default tolerance is 0.1, and the minimization will stop when the estimated vertical distance to the minimum (EDM) is less than 0.001*[tolerance]*UP(see SET ERR) if (opts->simplex_prerun){ spdlog::info("Running Simplex"); minuit_one->mnexcm("SIM", migrad_options, 2, errorcode); } minuit_one->mnexcm("MIG", migrad_options, 2, errorcode); result = errorcode; printMigradStatus(result); spdlog::warn("EDM: {0:f}", minuit_one->fEDM); if (opts->hesse_postrun){ spdlog::info("Starting hesse"); minuit_one->mnhess(); if (spdlog_debug()) minuit_one->mnmatu(1); //Print the correlation matrix, they are annoying so turned off for now } int nfree, ntot, status_cov; double m_fcn, m_edm, up; minuit_one->mnstat(m_fcn, m_edm, up, nfree, ntot, status_cov); //status_cov 0=not calculated, 1=approximation, 2=full but forced pos. def., 3=full accurate result += 100*status_cov; if (opts->hesse_postrun){ //Hesse calculates the 2nd derivative of the likelihood profile to assign the correct systematics printCovMatrixStatus(status_cov,false); } double tmp_cov[varied_params * varied_params]; //spdlog::info("nvaried params = " << varied_params);; minuit_one->mnemat(tmp_cov, varied_params); if (opts->weighted_fit && opts->asymptotic){//perform correction according to 1911.01303 Eq. 18 spdlog::debug("Starting an assymptotic treatment."); assert(!(opts->squared_hesse));//TODOCL i think hesse_postrun is fine (and will lead to better estimate for the weighted Hessian) //weighted hesse matrix is available as tmp_cov //need to iterate over all events //need to evaluate first derivatives at central values //events in different pdfs are independently distributed //just should count every event separately TMatrixDSym V(varied_params, tmp_cov);//weighted covariance matrix TMatrixDSym C(varied_params);//, 0.0);//tmp_cov_sq); for (unsigned int k=0; k param_names; std::vector param_values; std::vector eps(varied_params, 1.0e-5); for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0){ //if the parameter is common, only add name once if (!is_common(params.at(i)->get_parameter(j)->get_name()) || is_first(params.at(i)->get_parameter(j)->get_name(), i, j)){ double v,e; std::string par_name = params.at(i)->get_parameter(j)->get_name(); unsigned int idx = first_minuit_index(par_name); minuit_one->GetParameter(idx, v, e); param_names.push_back(par_name); param_values.push_back(v); spdlog::debug(par_name+": {0:f}", v); } } } } //the optimized version varies the parameter in the outer loop and performs the update_cached_normalisation only once for + and - //loop over data samples spdlog::debug("Looping over data samples."); std::vector lh_extended_nsig(events.size(),0.0); std::vector lh_extended_nbkg(events.size(),0.0); std::vector lh_extended_nsigbar(events.size(),0.0); std::vector lh_extended_nbkgbar(events.size(),0.0); for (unsigned int i = 0; i > derivs; //first idx varied param, second index event for (unsigned int k=0; kget_parameter(param_names.at(k)); if (!varied){ std::vector derivs_k(events.at(i)->size(),0.0); derivs.push_back(derivs_k); continue; } //set varied parameter + everywhere varied->set_value(param_values.at(k)+eps.at(k)); //cache integrals pdfs.at(i)->update_cached_normalization(params.at(i)); if (opts->use_event_norm) pdfs.at(i)->update_cached_normalization(params.at(i), events.at(i)); //TODOCL spdlog::debug("Getting likelihood."); likelihood();//this is supposed to only get the extended term, for the + varied parameters, should be fast enough //This doesn't do anything! //loop over lh_plus events std::vector loglh_plus_k(events.at(i)->size(),0.0); for (unsigned int j = 0; j < events.at(i)->size(); j++){ //for every event do finite differences const fcnc::event meas = events.at(i)->at(j); double probability = 0.0; probability = pdfs.at(i)->prob(params.at(i), meas); if (probability > 0.0) loglh_plus_k.at(j) = -TMath::Log(probability); //TODOCL if (meas.cp_conjugate){ //TODOCL CHECK!! //LEONS COMMENT meas.cp_conjugate corresponds to n_sig !!!!!!! loglh_plus_k.at(j) += -TMath::Log(lh_extended_nsig.at(i)+lh_extended_nbkg.at(i)); } else{ loglh_plus_k.at(j) += -TMath::Log(lh_extended_nsigbar.at(i)+lh_extended_nbkgbar.at(i)); } } //set varied parameter - everywhere varied->set_value(param_values.at(k)-eps.at(k)); //cache integrals pdfs.at(i)->update_cached_normalization(params.at(i)); if (opts->use_event_norm) pdfs.at(i)->update_cached_normalization(params.at(i), events.at(i)); //TODOCL likelihood();//this is supposed to only get the extended term, for the - varied parameters, should be fast enough //loop over lh_minus events std::vector loglh_minus_k(events.at(i)->size(),0.0); // for (unsigned int j = 0; j < events.at(i)->size(); j++){ // //for every event do finite differences // const fcnc::event meas = events.at(i)->at(j); // double probability = 0.0; // probability = pdfs.at(i)->prob(params.at(i), meas); // if (probability > 0.0) loglh_minus_k.at(j) = -TMath::Log(probability); // //TODOCL // if (meas.cp_conjugate){ //TODOCL CHECK!! //LEONS COMMENT meas.cp_conjugate corresponds to n_sig !!!!!!! // loglh_minus_k.at(j) += -TMath::Log(lh_extended_nsig.at(i)+lh_extended_nbkg.at(i)); // } // else{ // loglh_minus_k.at(j) += -TMath::Log(lh_extended_nsigbar.at(i)+lh_extended_nbkgbar.at(i)); // } // } //calculate derivatives for all events spdlog::debug("Calculating derivatives for all events"); std::vector derivs_k(events.at(i)->size(),0.0); for (unsigned int j = 0; j < events.at(i)->size(); j++){ derivs_k.at(j) = (loglh_plus_k.at(j)-loglh_minus_k.at(j))/(2.0*eps.at(k)); } spdlog::trace("Derivatives:+ "+convert_vector_to_string(derivs_k)); derivs.push_back(derivs_k); //reset varied parameter everywhere varied->set_value(param_values.at(k)); //cache integrals pdfs.at(i)->update_cached_normalization(params.at(i)); if (opts->use_event_norm){ pdfs.at(i)->update_cached_normalization(params.at(i), events.at(i)); } } //afterwards determine derivative via finite differences spdlog::debug("Calculating derivatives for finite differences"); //and save matrix of squared first derivatives for (unsigned int j = 0; j < events.at(i)->size(); j++){ //for every event do finite differences const fcnc::event meas = events.at(i)->at(j); for (unsigned int k=0; kweighted_fit && opts->squared_hesse){ square_weights = true; spdlog::info("Starting hesse with squared weights"); minuit_one->mnhess(); //check that this went fine double m_sq_fcn, m_sq_edm, sq_up; int status_sq_cov; minuit_one->mnstat(m_sq_fcn, m_sq_edm, sq_up, nfree, ntot, status_sq_cov); printCovMatrixStatus(status_sq_cov,true); //change status to invalied if squared weighted fit failed if (status_cov != status_sq_cov){ result -= 100*status_cov; result += 100*((status_cov < status_sq_cov) ? status_cov : status_sq_cov); } // minuit_one->mnmatu(1);//this also sets something internal in minuit! getparameter seems to get its error from here! double tmp_cov_sq[varied_params * varied_params]; minuit_one->mnemat(tmp_cov_sq, varied_params); //lets use root classes for the matrix inversion TMatrixDSym V(varied_params, tmp_cov);//tmp_cov TMatrixDSym C(varied_params, tmp_cov_sq); double det=0.0; C.Invert(&det); TMatrixD VCV(V,TMatrixD::kMult,TMatrixD(C,TMatrixD::kMult,V)); TMatrixDSym VCVsym(varied_params); spdlog::info("printing corrected covariance matrix"); for (unsigned int i=0; i varied_names; for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0){//if the parameter is common, only add name once if (!is_common(params.at(i)->get_parameter(j)->get_name()) || is_first(params.at(i)->get_parameter(j)->get_name(), i, j)){ varied_names.push_back(params.at(i)->get_parameter(j)->get_description()); } } } } //printing error matrix if (spdlog_debug()){ spdlog::debug("printing error matrix"); for (unsigned int x = 0; x < varied_params; x++) std::cout << "\tvar" << x << " "; std::cout << std::endl; for (unsigned int y = 0; y < varied_params; y++){ std::cout << "var" << y; for (unsigned int x = 0; x < varied_params; x++){ std::cout << " " << std::scientific << std::setw(9) << std::setprecision(2) << tmp_cov[varied_params*y + x]; } std::cout << std::endl; } } //calculating correlation matrix from error matrix double cor[varied_params*varied_params]; for (unsigned int x = 0; x < varied_params; x++){ for (unsigned int y = 0; y < varied_params; y++){ cor[varied_params*y + x] = tmp_cov[varied_params*y + x] /(sqrt(fabs(tmp_cov[varied_params*x + x]))*sqrt(fabs(tmp_cov[varied_params*y + y]))); } } //printing correlation matrix if (index != ""){ spdlog::info("printing correlation matrix"); myFile << "Run: " << opts->run << std::endl; myFile << "\\begin{tabular}{c"; for (unsigned int x = 0; x < varied_params; x++){ myFile << "c"; } myFile << "} \\hline" << std::endl; myFile << " "; for (unsigned int x = 0; x < varied_params; x++){ myFile << " & $" << varied_names.at(x) << "$"; } myFile << "\\\\ \\hline" << std::endl; bool upper_half = true; for (unsigned int y = 0; y < varied_params; y++){ myFile << std::setw(20) << "$" << varied_names.at(y) << "$"; for (unsigned int x = 0; x < varied_params; x++){ if (x >= y || !upper_half){ if (fabs(cor[varied_params*y + x]) >= 0.005){ myFile << " & " << std::fixed << std::setw(5) << std::setprecision(2) << cor[varied_params*y + x]; } else myFile << " & " << std::setw(5) << "-"; } else myFile << " & " << std::setw(5) << " "; } myFile << " \\\\ " << std::endl; } myFile <<"\\hline \\end{tabular}" << std::endl; } //saves correlations for all parameters for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j=0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0){ params.at(i)->get_parameter(j)->correlations.clear(); unsigned int apary = 0; for (unsigned int y=0; y < varied_params; y++){ if (varied_names.at(y) == params.at(i)->get_parameter(j)->get_description()){ apary = y; break; } } for (unsigned int x=0; x < varied_params; x++) params.at(i)->get_parameter(j)->correlations.push_back(cor[varied_params*apary + x]); } } } //minos error analysis if (opts->minos_errors && (result%100) == 0){//migrad is successfull spdlog::info("Starting Minos error analysis"); std::vector indices; for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (params.at(i)->get_parameter(j)->get_step_size() != 0.0 && params.at(i)->get_parameter(j)->get_minos()){ std::string par_name = params.at(i)->get_parameter(j)->get_name(); unsigned int idx = 0; if(is_common(par_name) || opts->use_individual_param_names) idx = first_minuit_index(par_name); else idx = nth_minuit_index(par_name, i+1); indices.push_back(idx); } } } if (indices.size() > 0){//run minos just for subset of parameters spdlog::info("MINOS SUBSET"); int errorcode; /* double migrad_options[indices.size()+1]; migrad_options[0] = 100000000.0; for (unsigned int i = 0; i < indices.size(); i++) //migrad_options[i+1] = indices.at(i); migrad_options[i+1] = indices.at(i)+1; minuit_one->mnexcm("MINO", migrad_options, indices.size()+1, errorcode); */ for (unsigned int i = 0; i < indices.size(); i++){ double migrad_options[2]; migrad_options[0] = 100000000.0; migrad_options[1] = indices.at(i)+1; minuit_one->mnexcm("MINO", migrad_options, 2, errorcode); } } else minuit_one->mnmnos();//do minos error analysis on all parameters //write MINOS errors to parameters for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ double e_up, e_down, parab, gcc; if (params.at(i)->get_parameter(j)->get_step_size() != 0.0){ std::string par_name = params.at(i)->get_parameter(j)->get_name(); unsigned int idx = 0; if(is_common(par_name) || opts->use_individual_param_names) idx = first_minuit_index(par_name); else idx = nth_minuit_index(par_name, i+1); minuit_one->mnerrs(idx, e_up, e_down, parab, gcc); params.at(i)->get_parameter(j)->set_error_up(e_up); params.at(i)->get_parameter(j)->set_error_down(e_down); } } } } //finally extract fitted parameters from minuit for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ double v,e; std::string par_name = params.at(i)->get_parameter(j)->get_name(); unsigned int idx; if(is_common(par_name) || opts->use_individual_param_names) idx = first_minuit_index(par_name); else idx = nth_minuit_index(par_name, i + 1); spdlog::trace("[PDF{0:d}]\tPAR{1:d}:\t{2:s}\tTMinuit_idx: {3:d}", i, j, par_name, idx); minuit_one->GetParameter(idx, v, e); params.at(i)->get_parameter(j)->set_value(v); params.at(i)->get_parameter(j)->set_error(e); if (opts->weighted_fit){ //this method is approximate for parameters with limited range! int iidx = minuit_one->fNiofex[idx] - 1; if (iidx>=0){ e = sqrt(fabs(tmp_cov[iidx*varied_params+iidx])); params.at(i)->get_parameter(j)->set_error(e); } } } } if (spdlog_trace()){ for (unsigned int i = 0; i < params.size(); i++){ params.at(i)->print_parameters(); } } if (opts->project){ for (unsigned int i = 0; iplot_data(pdfs.at(i), params.at(i), events.at(i), index); } spdlog::info("Fit procedure finished"); } //Close Latex file if (index != "") myFile.close(); return result; } void fcnc::fitter::define_parameter(int i, const parameter & p){ if (p.get_unlimited()){ minuit_one->DefineParameter(i, p.get_mn_name(), p(), p.get_step_size(), 0.0, 0.0); } else{ minuit_one->DefineParameter(i, p.get_mn_name(), p(), p.get_step_size(), p.get_min(), p.get_max()); } } void fcnc::fitter::reset_param_values(){ spdlog::debug("Resetting parameter values"); minuit_one->mncler(); unsigned int start_param = 0; for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if (opts->reset_start){ params.at(i)->get_parameter(j)->reset_start(); } std::string par_name = params.at(i)->get_parameter(j)->get_name(); if (!is_common(par_name) || is_first(par_name, i, j)){ define_parameter(start_param+j, *(params.at(i)->get_parameter(j))); } } start_param += params.at(i)->nparameters(); } } double fcnc::fitter::likelihood(){ lh_tot = 0.0; //this is called on every parameter change //this can never hurt, also needed in case of weighted/unfolded fit for (unsigned int i = 0; i < pdfs.size(); i++){ pdfs.at(i)->update_cached_normalization(params.at(i));//should do this, but should buffer f1, update f1 on fit command if(opts->use_event_norm){ pdfs.at(i)->update_cached_normalization(params.at(i), events.at(i)); } } //threading the likelihood determination on multiple CPU cores if(opts->ncores > 1){ std::vector threads; for(unsigned int i = 0; i < opts->ncores; i++){ std::thread* t = new std::thread(std::bind( &fitter::likelihood_thread, this, i)); threads.push_back(t); } for(unsigned int i = 0; i < opts->ncores; i++) threads[i]->join(); for(unsigned int i = 0; i < opts->ncores; i++) delete threads[i]; } else{ likelihood_thread(0); } if (spdlog_trace()){ unsigned int start_param = 0; for(unsigned int i = 0; i < params.size(); i++){ for(unsigned int j = 0; j < params.at(i)->nparameters(); j++){ if(params.at(i)->get_parameter(j)->get_step_size() != 0.0){ std::string par_name = params.at(i)->get_parameter(j)->get_name(); if(!is_common(par_name) || is_first(par_name, i, j)){ if(!params.at(i)->get_parameter(j)->is_blind()) std::cout << par_name << ":" << params.at(i)->get_parameter(j)->get_value() << " "; else std::cout << par_name << ":" << "XXX" << " "; } } } start_param += params.at(i)->nparameters(); } std::cout << std::endl; } //there is an additional term for parameters which have been measured previously for (unsigned int i = 0; i < params.size(); i++){ for (unsigned int j = 0; j < params.at(i)->nparameters(); j++){ const parameter* param = params.at(i)->get_parameter(j); if (param->get_gaussian_constraint()){ if (!is_common(param->get_name()) || is_first(param->get_name(), i, j)){ double mean = param->get_previous_measurement(); double x = param->get_value(); double dup = fabs(param->get_previous_error_high()); double ddown = fabs(param->get_previous_error_low()); double sigma = (x>mean ? dup : ddown); //spdlog::trace("mean {0:f}\tx {1:f}\tdup {2:f}\tddown {3:f}\tsigma {4:f}",mean,x,dup,ddown,sigma); if (x>mean+dup) sigma = dup; else if (xchisquaredstudy){ double chi2 = 0.0; for (unsigned int i = 0; i < pdfs.size(); i++) chi2 += pdfs.at(i)->chi2(params.at(i)); lh_tot += chi2; } if (opts->extended_ml){ double lh_ext = 0.0; for(unsigned int i = 0; i < params.size(); i++){ double nobs = 0.0; parameter* pnsig = params.at(i)->get_parameter("n_sig"); parameter* pnbkg = params.at(i)->get_parameter("n_bkg"); assert(pnsig && pnbkg); double nsig = pnsig->get_value(); double nbkg = pnbkg->get_value(); if(opts->weighted_fit){ for(unsigned int j = 0; jsize(); j++){ nobs += events.at(i)->at(j).weight; } } else{ nobs = events.at(i)->size(); } double prob = TMath::Poisson(nsig+nbkg, nobs); if(prob == 0.0) prob = 1e-100; lh_ext += -2.0*TMath::Log( prob ); if (std::isinf(lh_ext) || std::isnan(lh_ext)){ std::cout << "[PDF" << i << "]\t\tlh_ext = " << lh_ext << "\t prob = " << prob << "\t -2log(prob) = " << -2.0*TMath::Log( prob ) << std::endl; std::cout << "[PDF" << i << "]\t\tnsig = " << nsig << "\t n_bkg = " << nbkg << "\t nobs = " << nobs << std::endl; assert(0); } } if (std::isinf(lh_ext)){ spdlog::error("{0:0.16f}", lh_ext); spdlog::error("lh (w/o lh_ext) = {0:0.16f}", lh_tot); assert(0); } lh_tot += lh_ext; } if (std::isinf(lh_tot || std::isnan(lh_tot))){ spdlog::error("lh = {0:0.16f}", lh_tot); assert(0); } if (spdlog_trace()) std::cout << "[debug]\t" << std::setprecision(16) << "lh = " << lh_tot << "\r" << std::flush; return lh_tot; } void fcnc::fitter::likelihood_thread(int thread_id){ double probability = 0.0; event meas; long double result = 0.0; for (unsigned int i = 0; isize()/opts->ncores; //is too small for last vector! min = evts_per_thread * thread_id; max = evts_per_thread * (thread_id+1); int last_thread = opts->ncores - 1; if (thread_id == last_thread){//last thread needs to take all posibly remaining events max = events.at(i)->size(); } std::vector vec; vec.reserve(max - min); for (unsigned int j = min; j < max; j++){ meas = events.at(i)->at(j); probability = pdfs.at(i)->prob(params.at(i), meas); if (probability < 0.0 || std::isnan(probability) || std::isinf(probability)){ std::lock_guard lock(fitter_mutex); spdlog::info("Event probability is {0:d}", probability); print_event(meas); params.at(i)->print_parameters(); assert(0); } if (probability == 0.0){ probability = 1.0;//ignore event } if (probability != 0.0){ if (opts->weighted_fit){ if (square_weights) vec.push_back(-2.0 * meas.weight * meas.weight * TMath::Log(probability) - empirical_constant);//todo: does the empirical constant pose a problem here? else vec.push_back(-2.0 * meas.weight * TMath::Log(probability) - empirical_constant); } else vec.push_back(-2.0 * TMath::Log(probability) - empirical_constant); } else if (!std::isnan(probability)){ spdlog::error("Event probability is {0:d}", probability); print_event(meas); assert(0); } else{//is inf? vec.push_back(8.0);//is inf?//use something like prob max? log(p_min) } } result += add_results::iterator>(vec.begin(), vec.end()); } if (opts->ncores > 1){ std::lock_guard lock(fitter_mutex); lh_tot += result; } else{ lh_tot += result; } } //boost::mutex fitter::fitter_mutex; std::mutex fcnc::fitter::fitter_mutex; fcnc::fitter* fcnc::fitter::minuit_one_fitter;