def test1MinuitFit(self): """Test minuit callback and fit""" # setup minuit and callback gMinuit = TMinuit(5) gMinuit.SetPrintLevel(-1) # quiet gMinuit.SetGraphicsMode(ROOT.kFALSE) gMinuit.SetFCN(fcn) arglist = array('d', 10 * [0.]) ierflg = Long() arglist[0] = 1 gMinuit.mnexcm("SET ERR", arglist, 1, ierflg) # set starting values and step sizes for parameters vstart = array('d', [3, 1, 0.1, 0.01]) step = array('d', [0.1, 0.1, 0.01, 0.001]) gMinuit.mnparm(0, "a1", vstart[0], step[0], 0, 0, ierflg) gMinuit.mnparm(1, "a2", vstart[1], step[1], 0, 0, ierflg) gMinuit.mnparm(2, "a3", vstart[2], step[2], 0, 0, ierflg) gMinuit.mnparm(3, "a4", vstart[3], step[3], 0, 0, ierflg) # now ready for minimization step arglist[0] = 500 arglist[1] = 1. gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) # verify results amin, edm, errdef = Double(), Double(), Double() nvpar, nparx, icstat = Long(), Long(), Long() gMinuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat) # gMinuit.mnprin( 3, amin ) self.assertEqual(nvpar, 4) self.assertEqual(nparx, 4) # success means that full covariance matrix is available (icstat==3) self.assertEqual(icstat, 3) # check results (somewhat debatable ... ) par, err = Double(), Double() gMinuit.GetParameter(0, par, err) self.assertEqual(round(par - 2.15, 2), 0.) self.assertEqual(round(err - 0.10, 2), 0.) gMinuit.GetParameter(1, par, err) self.assertEqual(round(par - 0.81, 2), 0.) self.assertEqual(round(err - 0.25, 2), 0.) gMinuit.GetParameter(2, par, err) self.assertEqual(round(par - 0.17, 2), 0.) self.assertEqual(round(err - 0.40, 2), 0.) gMinuit.GetParameter(3, par, err) self.assertEqual(round(par - 0.10, 2), 0.) self.assertEqual(round(err - 0.16, 2), 0.)
def test1MinuitFit( self ): """Test minuit callback and fit""" # setup minuit and callback gMinuit = TMinuit(5) gMinuit.SetPrintLevel( -1 ) # quiet gMinuit.SetGraphicsMode( ROOT.kFALSE ) gMinuit.SetFCN( fcn ) arglist = array( 'd', 10*[0.] ) ierflg = Long() arglist[0] = 1 gMinuit.mnexcm( "SET ERR", arglist, 1, ierflg ) # set starting values and step sizes for parameters vstart = array( 'd', [ 3, 1, 0.1, 0.01 ] ) step = array( 'd', [ 0.1, 0.1, 0.01, 0.001 ] ) gMinuit.mnparm( 0, "a1", vstart[0], step[0], 0, 0, ierflg ) gMinuit.mnparm( 1, "a2", vstart[1], step[1], 0, 0, ierflg ) gMinuit.mnparm( 2, "a3", vstart[2], step[2], 0, 0, ierflg ) gMinuit.mnparm( 3, "a4", vstart[3], step[3], 0, 0, ierflg ) # now ready for minimization step arglist[0] = 500 arglist[1] = 1. gMinuit.mnexcm( "MIGRAD", arglist, 2, ierflg ) # verify results amin, edm, errdef = Double(), Double(), Double() nvpar, nparx, icstat = Long(), Long(), Long() gMinuit.mnstat( amin, edm, errdef, nvpar, nparx, icstat ) # gMinuit.mnprin( 3, amin ) self.assertEqual( nvpar, 4 ) self.assertEqual( nparx, 4 ) # success means that full covariance matrix is available (icstat==3) self.assertEqual( icstat, 3 ) # check results (somewhat debatable ... ) par, err = Double(), Double() gMinuit.GetParameter( 0, par, err ) self.assertEqual( round( par - 2.15, 2 ), 0. ) self.assertEqual( round( err - 0.10, 2 ), 0. ) gMinuit.GetParameter( 1, par, err ) self.assertEqual( round( par - 0.81, 2 ), 0. ) self.assertEqual( round( err - 0.25, 2 ), 0. ) gMinuit.GetParameter( 2, par, err ) self.assertEqual( round( par - 0.17, 2 ), 0. ) self.assertEqual( round( err - 0.40, 2 ), 0. ) gMinuit.GetParameter( 3, par, err ) self.assertEqual( round( par - 0.10, 2 ), 0. ) self.assertEqual( round( err - 0.16, 2 ), 0. )
def fit(self): numberOfParameters = len(self.samples) gMinuit = TMinuit(numberOfParameters) if self.method == "logLikelihood": # set function for minimisation gMinuit.SetFCN(self.logLikelihood) gMinuit.SetPrintLevel(-1) # Error definition: 1 for chi-squared, 0.5 for negative log likelihood gMinuit.SetErrorDef(1) # error flag for functions passed as reference.set to as 0 is no error errorFlag = Long(2) N_total = self.normalisation[self.data_label] * 2 N_min = 0 param_index = 0 for sample in self.samples: # all samples but data gMinuit.mnparm(param_index, sample, self.normalisation[sample], 10.0, N_min, N_total, errorFlag) param_index += 1 # N_signal = self.normalisation['signal'] # gMinuit.mnparm(0, "N_signal(ttbar+single_top)", N_signal, 10.0, N_min, N_total, errorFlag) # gMinuit.mnparm(1, "bkg1", 10, 10.0, N_min, N_total, errorFlag) arglist = array("d", 10 * [0.0]) # minimisation strategy: 1 standard, 2 try to improve minimum (a bit slower) arglist[0] = 2 # minimisation itself gMinuit.mnexcm("SET STR", arglist, 1, errorFlag) gMinuit.Migrad() self.module = gMinuit self.performedFit = True if not self.module: raise Exception("No fit results available. Please run fit method first") results = {} param_index = 0 for sample in self.samples: temp_par = Double(0) temp_err = Double(0) self.module.GetParameter(param_index, temp_par, temp_err) if math.isnan(temp_err): self.logger.warning("Template fit error is NAN, setting to sqrt(N).") temp_err = math.sqrt(temp_par) results[sample] = (temp_par, temp_err) param_index += 1 self.results = results
def runMinuit(numParameters): minuit = TMinuit(numParameters) minuit.SetPrintLevel(0) minuit.SetFCN(fcn) arglist = np.zeros(numParameters) + 0.01 internalFlag, arglist[0] = Long(0), 0.5 minuit.mnexcm("SET ERR", arglist, 1, internalFlag) initialValues = np.zeros(numParameters) + 0.01 steps = np.zeros(numParameters) + 0.0001 for i in xrange(numParameters): name = "epsilon%s" % i minuit.mnparm(i, name, initialValues[i], steps[i], 0, 1, internalFlag) # arglist[0] = 2 # minuit.mnexcm("SET STR", arglist, 1, internalFlag) arglist[0], arglist[1] = 10000, 0.1 minuit.mnexcm("SIMPLEX", arglist, 1, internalFlag) minuit.mnexcm("MIGRAD", arglist, 1, internalFlag) print "FIT STATUS is " +str(minuit.GetStatus()) return ratesAndErrors(numParameters, minuit)
def fit_mu(self, background, signal): myMinuit = TMinuit(self.npar) myMinuit.SetFCN(self.fcn) gMinuit.Command('SET PRINT -1') ierflg = Long(0) myMinuit.mnparm(0, 'mu', self.start_value_mu, self.init_step_mu, self.min_mu, self.max_mu, ierflg) arglist = array('d', (0, 0)) arglist[0] = self.max_iterations arglist[1] = self.tolerance myMinuit.mnexcm("MINIMIZE", arglist, 2, ierflg) # check TMinuit status amin, edm, errdef = Double(0.), Double(0.), Double(0.) nvpar, nparx, icstat = Long(0), Long(0), Long(0) myMinuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat) # get final results p, pe = Double(0), Double(0) myMinuit.GetParameter(0, p, pe) # finalPar = float(p) # finalParErr = float(pe) # print_banner('MINUIT fit completed:') # print ' fcn@minimum = %.3g' %(amin)," error code =", ierflg, " status =", icstat # print " Results: \t value error" # print ' %s: \t%10.3e +/- %.1e '%('mu', finalPar, finalParErr) return p
def dofit(self) : #print 'starting fit for hypothesis index %d'%(self.fitindex)#DEBUG #set up the minuit object, etc. parNames = ['fitindex','pZv','scalelep','scaleblep','scalehad1','scalehad2','scalehad3'] parinivals = [self.fitindex,0.,1.,1.,1.,1.,1.] parerrs = [0.0,0.0,0.0,0.0,0.0,0.0,0.0] bestParValues = [] nPars = 6 if self.topology==1 else 7 for i in range(1,nPars) : bestParValues.append(parinivals[i]) ierflag = Long(1) arglist = array( 'd', [-1.0] ) minuit = TMinuit(nPars) minuit.mnexcm('SET PRINT', arglist, 1,ierflag) minuit.mnexcm('SET NOWARNINGS',arglist,1,ierflag) arglist[0] = 100000. #add parameters to the fitter for i in range(nPars) : minuit.mnparm(i,parNames[i],parinivals[i],1.0,0.,0.,ierflag) #fix the first 'fit index' parameter minuit.mnfixp(0,ierflag) #set the minimization function to use if self.topology==1 : minuit.SetFCN(fcnt1) elif self.topology==2 : minuit.SetFCN(fcnt2) elif self.topology==3 : minuit.SetFCN(fcnt3) #minimize and get the error flag minuit.mnexcm('MIGRAD', arglist, 1, ierflag) #print 'index = %d, errflag = %s'%(self.fitindex,ierflag) #DEBUG errflag = int(ierflag) #Check fit Chi2 tmp1 = Double(1.0); tmp2 = Double(1.0); tmp3 = Double(1.0) minuit.mnstat(tmp1,tmp2,tmp3,Long(1),Long(1),Long(1)) #print 'chi2 = %.4f'%(tmp1) #DEBUG chi2value = float(tmp1) #Get the bestfit parameters back from minuit for j in range(1,nPars) : tmp = Double(1.0) minuit.GetParameter(j,tmp,Double(parerrs[j])) bestParValues[j-1] = float(tmp) return errflag, chi2value, bestParValues
def prepareAndRunOptimizer(self, xValues, yValues, erryValues, fixParams, optFunction, parValues, parNames, printOutLevel=0): self.xValues = xValues self.yValues = yValues self.erryValues = erryValues self.fixParams = fixParams initialError = 0.1 nBins = len(self.xValues) gMinuit = TMinuit(nBins) gMinuit.SetFCN(optFunction) arglist = array('d', nBins * [0]) ierflg = c_int(0) arglist[0] = 1 # 1 for chi2, 0.5 for likelihood gMinuit.mnexcm('SET ERR', arglist, 1, ierflg) # Set starting values and step sizes for parameters vStart = parValues vStep = [initialError for i in range(len(parValues))] for i, name in enumerate(parNames): gMinuit.mnparm(i, name, vStart[i], vStep[i], 0, 0, ierflg) # Fix parameters (counting from 1) for i, fix in enumerate(self.fixParams): arglist[i] = fix + 1 gMinuit.mnexcm('FIX', arglist, len(self.fixParams), ierflg) # Define printout level arglist[0] = printOutLevel # -1 = no output except from SHOW # 0 = minimum output (no starting values or intermediate results) default value, normal output # 1 = additional output giving intermediate results. # 2 = maximum output, showing progress of minimizations. gMinuit.mnexcm('SET PRI', arglist, 1, ierflg) # Now ready for minimization step arglist[0] = 5000 # Max calls arglist[1] = 0.1 # Tolerance gMinuit.mnexcm('MIGRAD', arglist, 2, ierflg) # Print results self.dof = self.nMeas - len(parValues) + len(self.fixParams) fmin, fedm, errdef = c_double(0.), c_double(0.), c_double(0.) npari, nparx, istat = c_int(0), c_int(0), c_int(0) gMinuit.mnstat(fmin, fedm, errdef, npari, nparx, istat) print('\nFMIN:', round(fmin.value, 2), '\tFEDM:', '{:.1e}'.format(fedm.value), '\tERRDEF:', errdef.value, '\tNPARI:', npari.value, '\tNPARX:', nparx.value, '\tISTAT:', istat.value) print('chi-2:', round(self.chi2, 2), '\td.o.f.:', self.dof, '\tchi-2/d.o.f.:', round(self.chi2 / self.dof, 2), '\n') return gMinuit, istat
X_predictions_for_fit = np.column_stack([x for x in X_predicted_all]) mpvs=[] print 'here' fit_quantile=[] gMinuit = TMinuit(4) gMinuit.SetPrintLevel(-1) gMinuit.SetFCN( fcn ) arglist = np.array( 10*[0.] ) ierflg = 0 arglist[0] = 1 gMinuit.mnexcm( "SET ERR", arglist, 1, Long(ierflg) ) arglist[0] = 0 gMinuit.mnexcm("SET PRINT", arglist ,0,Long(ierflg)); vstart = np.array( [ -2 , 4,-1, 0.5] ) step = np.array( [ 0.001, 0.001, 0.01, 0.01 ] ) gMinuit.mnparm( 0, "a1", vstart[0], step[0], 0, 0, Long(ierflg) ) gMinuit.mnparm( 1, "a2", vstart[1], step[1], 0, 0, Long(ierflg) ) gMinuit.mnparm( 2, "a3", vstart[2], step[2], 0, 0, Long(ierflg) ) gMinuit.mnparm( 3, "a4", vstart[3], step[3], 0, 0, Long(ierflg) ) arglist[0] = 500 arglist[1] = 1. fParamVal = Double(0.) fParamErr = Double(0.) for i in range(5): changeme(X_predictions_for_fit[i]) gMinuit.mnexcm( "MIGRAD", arglist, 2, Long(ierflg) )
s = error_w1[3]*error_w2[3] t = error_w1[4]*error_w2[4] global kor_matirx kor_matirx = np.array([[g1, s+t], [ s+t, g2]]) kor_matirx_inv = inv(kor_matirx) print kor_matirx # --> set up MINUIT myMinuit = TMinuit(npar) # initialize TMinuit with maximum of npar parameters myMinuit.SetFCN(fcn) # set function to minimize arglist = arr('d', 2*[0.01]) # set error definition ierflg = Long(0) arglist[0] = 1. # 1 sigma is Delta chi^2 = 1 myMinuit.mnexcm("SET ERR", arglist ,1,ierflg) # --> Set starting values and step sizes for parameters # Define the parameters for the fit myMinuit.mnparm(1, "mean", 400, 0.001, 0,0,ierflg) arglist[0] = 6000 # Number of calls to FCN before giving up. arglist[1] = 0.3 # Tolerance myMinuit.mnexcm("MIGRAD", arglist ,2,ierflg) # execute the minimisation # --> check TMinuit status amin, edm, errdef = Double(0.), Double(0.), Double(0.) nvpar, nparx, icstat = Long(0), Long(0), Long(0) myMinuit.mnstat(amin,edm,errdef,nvpar,nparx,icstat) # meaning of parameters:
class Fits: def __init__(self): self.p_n = [ 0, ] * 100 self.e_n = [ 0, ] * 100 self.stored_parameters = [ 0, ] * 100 self.num_bins = 0 self.xmins = [] self.xmaxes = [] self.data = [] self.errors = [] self.data_fits = [] self.model_scale_values = [] self.final = False self.exclude_regions = ((0, 0), ) self.col1 = 1 self.col2 = TColor.GetColor(27, 158, 119) self.col3 = TColor.GetColor(217, 95, 2) self.col4 = TColor.GetColor(117, 112, 179) def run_mass_fit(self, peak_scale_initial, mass=0): self.gMinuit = TMinuit(30) self.gMinuit.SetPrintLevel(-1) self.gMinuit.SetFCN(self.Fitfcn_max_likelihood) arglist = array("d", [ 0, ] * 10) ierflg = ROOT.Long(0) arglist[0] = ROOT.Double(1) # peak_scale_initial = ROOT.Double(peak_scale_initial) tmp = array("d", [ 0, ]) self.gMinuit.mnexcm("SET NOWarnings", tmp, 0, ierflg) self.gMinuit.mnexcm("SET ERR", arglist, 1, ierflg) self.gMinuit.mnparm(0, "p1", 5e-6, 1e-7, 0, 0, ierflg) self.gMinuit.mnparm(1, "p2", 10, 10, 0, 0, ierflg) self.gMinuit.mnparm(2, "p3", -5.3, 1, 0, 0, ierflg) self.gMinuit.mnparm(3, "p4", -4e-2, 1e-2, 0, 0, ierflg) self.gMinuit.mnparm(4, "p5", peak_scale_initial, peak_scale_initial / 50, 0, 0, ierflg) self.background_fit_only = [ 0, ] * len(self.data) arglist[0] = ROOT.Double(0) arglist[1] = ROOT.Double(0) #self.exclude_regions = ((2.2, 3.3),) self.gMinuit.FixParameter(2) self.gMinuit.FixParameter(3) self.gMinuit.FixParameter(4) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) self.gMinuit.Release(2) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) self.gMinuit.Release(3) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) # Find an actual best fit #self.exclude_regions = ((0, 2), (3.3, 100),) self.gMinuit.FixParameter(0) self.gMinuit.FixParameter(1) self.gMinuit.FixParameter(2) self.gMinuit.FixParameter(3) self.gMinuit.Release(4) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) self.exclude_regions = () self.gMinuit.Release(0) self.gMinuit.Release(1) self.gMinuit.Release(2) self.gMinuit.Release(3) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) best_fit_value = ROOT.Double(0) self.gMinuit.mnstat(best_fit_value, ROOT.Double(0), ROOT.Double(0), ROOT.Long(0), ROOT.Long(0), ROOT.Long(0)) #print("Best fit value", best_fit_value) # And prepare for iterating over fit values for N injected events self.gMinuit.Release(0) self.gMinuit.Release(1) self.gMinuit.Release(2) self.gMinuit.Release(3) self.exclude_regions = () #self.data_fits = no_peak_data_fits x_values = [] y_values = [] fitted_N = ROOT.Double(0) self.gMinuit.GetParameter(4, fitted_N, ROOT.Double(0)) best_fit_likelihood = self.calc_likelihood(fitted_N) step = 5 if int(mass) >= 4000: step = 1 if int(mass) >= 5000: step = 0.2 if int(mass) >= 6000: step = 0.1 if int(mass) >= 6500: step = 0.05 N = 0 while N < 5000: start_sum = sum([math.exp(-a) for a in y_values]) fit_likelihood = self.calc_likelihood(N) x_values.append(N) y_values.append(fit_likelihood - best_fit_likelihood) probabilities = [math.exp(-a) for a in y_values] end_sum = sum(probabilities) max_prob = max(probabilities) normalised_probabilities = [a / max_prob for a in probabilities] if N / step > 50 and all( [v > 0.99 for v in normalised_probabilities]): print( "Probability=1 everywhere, probably something wrong with fit" ) print(normalised_probabilities) return None, None # if new value changes total by less than 0.1%, end loop if N > 0 and (end_sum - start_sum) / start_sum < 0.0001: print("Iterated up to {0}".format(N)) break N += step self.iflag = int(ierflg) return x_values, y_values def Fitfcn_max_likelihood(self, npar, gin, fcnVal, par, iflag): likelihood = 0 mf = ROOT.MyMassSpectrum() mf.SetParameters(par) ig = GaussIntegrator() ig.SetFunction(mf) ig.SetRelTolerance(0.00001) for i in range(0, self.num_bins): for lower, higher in self.exclude_regions: if lower < self.xmins[i] < higher: continue model_val = ig.Integral(self.xmins[i], self.xmaxes[i]) / ( self.xmaxes[i] - self.xmins[i]) self.background_fit_only[i] = model_val model_val += self.model_scale_values[i] * par[4] self.data_fits[i] = model_val likelihood += model_val - self.data[i] if self.data[i] > 0 and model_val > 0: likelihood += self.data[i] * (math.log(self.data[i]) - math.log(model_val)) fcnVal[0] = likelihood def calc_likelihood(self, peak_scale): like = 0 for i in range(0, self.num_bins): if self.data_fits[i] <= 0: continue p = peak_scale * self.model_scale_values[i] tmp = ROOT.TMath.PoissonI(self.data[i], self.background_fit_only[i] + p) #if peak_scale == 40000: # print(i, "\txmin", self.xmins[i], "\tdata", self.data[i], "\tdata_fit", self.data_fits[i], "\tp", p, "\tdata_fit+p", self.data_fits[i]+p) if tmp == 0: print("tmp == 0 here") logtmp = math.log(sys.float_info.min) else: logtmp = math.log(tmp) like += logtmp return -like
def signalfit(data_hist, signalfunction, signalname, process): binning = HistBinsToList(data_hist) data_x = HistToList(data_hist) data_error = HistErrorList(data_hist) parfunction = signalfunction.GetNumberFreeParameters() partot = signalfunction.GetNumberFreeParameters() print partot ### the fucntion used for TMinuit def fcn(npar, gin, f, par, ifag): L = 0 # calculate likelihood, input par[0] is the N_B, par[1] is N_C, par[2] is N_L for ibin in range(len(binning)): #if (data_x[ibin] ==0): # continue bincen = binning[ibin] mu_x = 0 data = data_x[ibin] if data_error[ibin] == 0: continue #if data<0.1: # continue if signalname == "CrystalBall": if par[3] < 0: mu_x = 0 else: t = (bincen - par[2]) / (par[3]) if (par[0] < 0): t = -t absAlpha = abs(par[0]) if (t >= -absAlpha): mu_x = par[4] * exp(-0.5 * t * t) else: nDivAlpha = par[1] / absAlpha AA = exp(-0.5 * absAlpha * absAlpha) B = nDivAlpha - absAlpha arg = nDivAlpha / (B - t) mu_x = par[4] * (arg**par[1]) if signalname == "CrystalBallGaus": if par[3] < 0: mu_x = 0 else: t = (bincen - par[2]) / (par[3]) if (par[0] < 0): t = -t absAlpha = abs(par[0]) if (t >= -absAlpha): mu_x = par[4] * exp(-0.5 * t * t + exp(-(bincen - par[5])**2 / (2 * par[6]**2))) else: nDivAlpha = par[1] / absAlpha AA = exp(-0.5 * absAlpha * absAlpha) B = nDivAlpha - absAlpha arg = nDivAlpha / (B - t) mu_x = par[4] * (arg**par[1] + exp(-(bincen - par[5])**2 / (2 * par[6]**2))) #print mu_x, data, data_error[ibin] #L = L + mu_x - data*log(mu_x) L = L + ((mu_x - data) / data_error[ibin])**2 f[0] = L # initialize the TMinuit object arglist_p = 10 * [0] arglist = array.array('d') arglist.fromlist(arglist_p) ierflag = Long(0) maxiter = 1000000000 arglist_p = [1] gMinuit = TMinuit(partot) gMinuit.mnexcm('SET PRIntout', arglist, 0, ierflag) gMinuit.SetPrintLevel(1) gMinuit.SetErrorDef(1.0) gMinuit.SetFCN(fcn) arglist_p = [2] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('SET STRategy', arglist, 1, ierflag) arglist_p = [maxiter, 0.0000001] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('MIGrad', arglist, 2, ierflag) gMinuit.SetMaxIterations(maxiter) # initialize fitting the variables vstart = [125.0] * partot step = [0.1] * partot upper = [1000000] * partot lower = [-100] * partot varname = [] lower[3] = 0 lower[4] = 0 lower[1] = 0 vstart[4] = data_hist.Integral() if process == "signal": vstart[2] = 125 lower[2] = 110 upper[2] = 140 vstart[3] = 10 lower[3] = 2 upper[3] = 25 if len(vstart) > 5: vstart[5] = 125 lower[5] = 110 upper[5] = 140 vstart[6] = 10 lower[6] = 5 upper[6] = 20 if process == "z": vstart[2] = 90 lower[2] = 70 upper[2] = 110 vstart[3] = 10 lower[3] = 2 upper[3] = 30 if len(vstart) > 5: vstart[5] = 90 lower[5] = 70 upper[5] = 110 vstart[6] = 10 lower[6] = 2 upper[6] = 30 for i in range(parfunction): varname.append("p" + str(i)) for i in range(partot): gMinuit.mnparm(i, varname[i], vstart[i], step[i], lower[i], upper[i], ierflag) # fitting procedure migradstat = gMinuit.Command('MIGrad ' + str(maxiter) + ' ' + str(0.001)) #improvestat = gMinuit.Command('IMProve ' + str(maxiter) + ' ' + str(0.01)) for i in range(partot): arglist_p.append(i + 1) arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnmnos() # get fitting parameters fitval_p = [Double(0)] * partot fiterr_p = [Double(0)] * partot errup_p = [Double(0)] * partot errdown_p = [Double(0)] * partot eparab_p = [Double(0)] * partot gcc_p = [Double(0)] * partot fmin_p = [Double(0)] fedm_p = [Double(0)] errdef_p = [Double(0)] npari_p = Long(0) nparx_p = Long(0) istat_p = Long(0) fitval = array.array('d') fiterr = array.array('d') errup = array.array('d') errdown = array.array('d') eparab = array.array('d') gcc = array.array('d') for i in range(partot): gMinuit.GetParameter(i, fitval_p[i], fiterr_p[i]) fitval.append(fitval_p[i]) fiterr.append(fiterr_p[i]) errup.append(errup_p[i]) errdown.append(errdown_p[i]) eparab.append(eparab_p[i]) gcc.append(gcc_p[i]) gMinuit.mnstat(fmin_p[0], fedm_p[0], errdef_p[0], npari_p, nparx_p, istat_p) for p in range(signalfunction.GetNumberFreeParameters()): signalfunction.SetParameter(p, fitval[p]) print "fit uncert", fiterr_p[p] signalfunction.SetChisquare(fmin_p[0]) print fmin_p[0] return fitval[partot - 1], fitval[partot - 2]
class Minuit: ''' A class for communicating with ROOT's function minimizer tool Minuit. ''' def __init__(self, number_of_parameters, function_to_minimize, parameter_names, start_parameters, parameter_errors, quiet=True, verbose=False): ''' Create a Minuit minimizer for a function `function_to_minimize`. Necessary arguments are the number of parameters and the function to be minimized `function_to_minimize`. The function `function_to_minimize`'s arguments must be numerical values. The same goes for its output. Another requirement is for every parameter of `function_to_minimize` to have a default value. These are then used to initialize Minuit. **number_of_parameters** : int The number of parameters of the function to minimize. **function_to_minimize** : function The function which `Minuit` should minimize. This must be a Python function with <``number_of_parameters``> arguments. **parameter_names** : tuple/list of strings The parameter names. These are used to keep track of the parameters in `Minuit`'s output. **start_parameters** : tuple/list of floats The start values of the parameters. It is important to have a good, if rough, estimate of the parameters at the minimum before starting the minimization. Wrong initial parameters can yield a local minimum instead of a global one. **parameter_errors** : tuple/list of floats An initial guess of the parameter errors. These errors are used to define the initial step size. *quiet* : boolean (optional, default: ``True``) If ``True``, suppresses all output from ``TMinuit``. *verbose* : boolean (optional, default: ``False``) If ``True``, sets ``TMinuit``'s print level to a high value, so that all output is logged. ''' #: the name of this minimizer type self.name = "ROOT::TMinuit" #: the actual `FCN` called in ``FCN_wrapper`` self.function_to_minimize = function_to_minimize #: number of parameters to minimize for self.number_of_parameters = number_of_parameters if not quiet: self.out_file = open(log_file("minuit.log"), 'a') else: self.out_file = null_file() # create a TMinuit instance for that number of parameters self.__gMinuit = TMinuit(self.number_of_parameters) # instruct Minuit to use this class's FCN_wrapper method as a FCN self.__gMinuit.SetFCN(self.FCN_wrapper) # set print level according to flag if quiet: self.set_print_level(-1000) # suppress output elif verbose: self.set_print_level(10) # detailed output else: self.set_print_level(0) # frugal output # initialize minimizer self.set_err() self.set_strategy() self.set_parameter_values(start_parameters) self.set_parameter_errors(parameter_errors) self.set_parameter_names(parameter_names) #: maximum number of iterations until ``TMinuit`` gives up self.max_iterations = M_MAX_ITERATIONS #: ``TMinuit`` tolerance self.tolerance = M_TOLERANCE def update_parameter_data(self, show_warnings=False): """ (Re-)Sets the parameter names, values and step size on the C++ side of Minuit. """ error_code = Long(0) try: # Set up the starting fit parameters in TMinuit for i in range(0, self.number_of_parameters): self.__gMinuit.mnparm(i, self.parameter_names[i], self.current_parameters[i], 0.1 * self.parameter_errors[i], 0, 0, error_code) # use 10% of the par. 1-sigma errors as the initial step size except AttributeError as e: if show_warnings: logger.warning("Cannot update Minuit data on the C++ side. " "AttributeError: %s" % (e, )) return error_code # Set methods ############## def set_print_level(self, print_level=P_DETAIL_LEVEL): '''Sets the print level for Minuit. *print_level* : int (optional, default: 1 (frugal output)) Tells ``TMinuit`` how much output to generate. The higher this value, the more output it generates. ''' self.__gMinuit.SetPrintLevel(print_level) # set Minuit print level self.print_level = print_level def set_strategy(self, strategy_id=1): '''Sets the strategy Minuit. *strategy_id* : int (optional, default: 1 (optimized)) Tells ``TMinuit`` to use a certain strategy. Refer to ``TMinuit``'s documentation for available strategies. ''' error_code = Long(0) # execute SET STRATEGY command self.__gMinuit.mnexcm("SET STRATEGY", arr('d', [strategy_id]), 1, error_code) def set_err(self, up_value=1.0): '''Sets the ``UP`` value for Minuit. *up_value* : float (optional, default: 1.0) This is the value by which `FCN` is expected to change. ''' # Tell TMinuit to use an up-value of 1.0 error_code = Long(0) # execute SET ERR command self.__gMinuit.mnexcm("SET ERR", arr('d', [up_value]), 1, error_code) def set_parameter_values(self, parameter_values): ''' Sets the fit parameters. If parameter_values=`None`, tries to infer defaults from the function_to_minimize. ''' if len(parameter_values) == self.number_of_parameters: self.current_parameters = parameter_values else: raise Exception("Cannot get default parameter values from the \ FCN. Not all parameters have default values given.") self.update_parameter_data() def set_parameter_names(self, parameter_names): '''Sets the fit parameters. If parameter_values=`None`, tries to infer defaults from the function_to_minimize.''' if len(parameter_names) == self.number_of_parameters: self.parameter_names = parameter_names else: raise Exception("Cannot set param names. Tuple length mismatch.") self.update_parameter_data() def set_parameter_errors(self, parameter_errors=None): '''Sets the fit parameter errors. If parameter_values=`None`, sets the error to 10% of the parameter value.''' if parameter_errors is None: # set to 0.1% of the parameter value if not self.current_parameters is None: self.parameter_errors = [max(0.1, 0.1 * par) for par in self.current_parameters] else: raise Exception("Cannot set parameter errors. No errors \ provided and no parameters initialized.") elif len(parameter_errors) != len(self.current_parameters): raise Exception("Cannot set parameter errors. \ Tuple length mismatch.") else: self.parameter_errors = parameter_errors self.update_parameter_data() # Get methods ############## def get_error_matrix(self): '''Retrieves the parameter error matrix from TMinuit. return : `numpy.matrix` ''' # set up an array of type `double' to pass to TMinuit tmp_array = arr('d', [0.0]*(self.number_of_parameters**2)) # get parameter covariance matrix from TMinuit self.__gMinuit.mnemat(tmp_array, self.number_of_parameters) # reshape into 2D array return np.asmatrix( np.reshape( tmp_array, (self.number_of_parameters, self.number_of_parameters) ) ) def get_parameter_values(self): '''Retrieves the parameter values from TMinuit. return : tuple Current `Minuit` parameter values ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(p)) return tuple(result) def get_parameter_errors(self): '''Retrieves the parameter errors from TMinuit. return : tuple Current `Minuit` parameter errors ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(pe)) return tuple(result) def get_parameter_info(self): '''Retrieves parameter information from TMinuit. return : list of tuples ``(parameter_name, parameter_val, parameter_error)`` ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append((self.get_parameter_name(i), float(p), float(pe))) return result def get_parameter_name(self, parameter_nr): '''Gets the name of parameter number ``parameter_nr`` **parameter_nr** : int Number of the parameter whose name to get. ''' return self.parameter_names[parameter_nr] def get_fit_info(self, info): '''Retrieves other info from `Minuit`. **info** : string Information about the fit to retrieve. This can be any of the following: - ``'fcn'``: `FCN` value at minimum, - ``'edm'``: estimated distance to minimum - ``'err_def'``: `Minuit` error matrix status code - ``'status_code'``: `Minuit` general status code ''' # declare vars in which to retrieve other info fcn_at_min = Double(0) edm = Double(0) err_def = Double(0) n_var_param = Long(0) n_tot_param = Long(0) status_code = Long(0) # Tell TMinuit to update the variables declared above self.__gMinuit.mnstat(fcn_at_min, edm, err_def, n_var_param, n_tot_param, status_code) if info == 'fcn': return fcn_at_min elif info == 'edm': return edm elif info == 'err_def': return err_def elif info == 'status_code': try: return D_MATRIX_ERROR[status_code] except: return status_code def get_chi2_probability(self, n_deg_of_freedom): ''' Returns the probability that an observed :math:`\chi^2` exceeds the calculated value of :math:`\chi^2` for this fit by chance, even for a correct model. In other words, returns the probability that a worse fit of the model to the data exists. If this is a small value (typically <5%), this means the fit is pretty bad. For values below this threshold, the model very probably does not fit the data. n_def_of_freedom : int The number of degrees of freedom. This is typically :math:`n_\text{datapoints} - n_\text{parameters}`. ''' chi2 = Double(self.get_fit_info('fcn')) ndf = Long(n_deg_of_freedom) return TMath.Prob(chi2, ndf) def get_contour(self, parameter1, parameter2, n_points=21): ''' Returns a list of points (2-tuples) representing a sampling of the :math:`1\\sigma` contour of the TMinuit fit. The ``FCN`` has to be minimized before calling this. **parameter1** : int ID of the parameter to be displayed on the `x`-axis. **parameter2** : int ID of the parameter to be displayed on the `y`-axis. *n_points* : int (optional) number of points used to draw the contour. Default is 21. *returns* : 2-tuple of tuples a 2-tuple (x, y) containing ``n_points+1`` points sampled along the contour. The first point is repeated at the end of the list to generate a closed contour. ''' self.out_file.write('\n') # entry in log-file self.out_file.write('\n') self.out_file.write('#'*(5+28)) self.out_file.write('\n') self.out_file.write('# Contour for parameters %2d, %2d #\n'\ %(parameter1, parameter2) ) self.out_file.write('#'*(5+28)) self.out_file.write('\n\n') self.out_file.flush() # # first, make sure we are at minimum self.minimize(final_fit=True, log_print_level=0) # get the TGraph object from ROOT g = self.__gMinuit.Contour(n_points, parameter1, parameter2) # extract point data into buffers xbuf, ybuf = g.GetX(), g.GetY() N = g.GetN() # generate tuples from buffers x = np.frombuffer(xbuf, dtype=float, count=N) y = np.frombuffer(ybuf, dtype=float, count=N) # return (x, y) def get_profile(self, parid, n_points=21): ''' Returns a list of points (2-tuples) the profile the :math:`\\chi^2` of the TMinuit fit. **parid** : int ID of the parameter to be displayed on the `x`-axis. *n_points* : int (optional) number of points used for profile. Default is 21. *returns* : two arrays, par. values and corresp. :math:`\\chi^2` containing ``n_points`` sampled profile points. ''' self.out_file.write('\n') # entry in log-file self.out_file.write('\n') self.out_file.write('#'*(2+26)) self.out_file.write('\n') self.out_file.write("# Profile for parameter %2d #\n" % (parid)) self.out_file.write('#'*(2+26)) self.out_file.write('\n\n') self.out_file.flush() # redirect stdout stream _redirection_target = None ## -- disable redirection completely, for now ##if log_print_level >= 0: ## _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): pv = [] chi2 = [] error_code = Long(0) self.__gMinuit.mnexcm("SET PRINT", arr('d', [0.0]), 1, error_code) # no printout # first, make sure we are at minimum, i.e. re-minimize self.minimize(final_fit=True, log_print_level=0) minuit_id = Double(parid + 1) # Minuit parameter numbers start with 1 # retrieve information about parameter with id=parid pmin = Double(0) perr = Double(0) self.__gMinuit.GetParameter(parid, pmin, perr) # retrieve fitresult # fix parameter parid ... self.__gMinuit.mnexcm("FIX", arr('d', [minuit_id]), 1, error_code) # ... and scan parameter values, minimizing at each point for v in np.linspace(pmin - 3.*perr, pmin + 3.*perr, n_points): pv.append(v) self.__gMinuit.mnexcm("SET PAR", arr('d', [minuit_id, Double(v)]), 2, error_code) self.__gMinuit.mnexcm("MIGRAD", arr('d', [self.max_iterations, self.tolerance]), 2, error_code) chi2.append(self.get_fit_info('fcn')) # release parameter to back to initial value and release self.__gMinuit.mnexcm("SET PAR", arr('d', [minuit_id, Double(pmin)]), 2, error_code) self.__gMinuit.mnexcm("RELEASE", arr('d', [minuit_id]), 1, error_code) return pv, chi2 # Other methods ################ def fix_parameter(self, parameter_number): ''' Fix parameter number <`parameter_number`>. **parameter_number** : int Number of the parameter to fix. ''' error_code = Long(0) logger.info("Fixing parameter %d in Minuit" % (parameter_number,)) # execute FIX command self.__gMinuit.mnexcm("FIX", arr('d', [parameter_number+1]), 1, error_code) def release_parameter(self, parameter_number): ''' Release parameter number <`parameter_number`>. **parameter_number** : int Number of the parameter to release. ''' error_code = Long(0) logger.info("Releasing parameter %d in Minuit" % (parameter_number,)) # execute RELEASE command self.__gMinuit.mnexcm("RELEASE", arr('d', [parameter_number+1]), 1, error_code) def reset(self): '''Execute TMinuit's `mnrset` method.''' self.__gMinuit.mnrset(0) # reset TMinuit def FCN_wrapper(self, number_of_parameters, derivatives, f, parameters, internal_flag): ''' This is actually a function called in *ROOT* and acting as a C wrapper for our `FCN`, which is implemented in Python. This function is called by `Minuit` several times during a fit. It doesn't return anything but modifies one of its arguments (*f*). This is *ugly*, but it's how *ROOT*'s ``TMinuit`` works. Its argument structure is fixed and determined by `Minuit`: **number_of_parameters** : int The number of parameters of the current fit **derivatives** : C array If the user chooses to calculate the first derivative of the function inside the `FCN`, this value should be written here. This interface to `Minuit` ignores this derivative, however, so calculating this inside the `FCN` has no effect (yet). **f** : C array The desired function value is in f[0] after execution. **parameters** : C array A C array of parameters. Is cast to a Python list **internal_flag** : int A flag allowing for different behaviour of the function. Can be any integer from 1 (initial run) to 4(normal run). See `Minuit`'s specification. ''' # Retrieve the parameters from the C side of ROOT and # store them in a Python list -- resource-intensive # for many calls, but can't be improved (yet?) parameter_list = np.frombuffer(parameters, dtype=float, count=self.number_of_parameters) # call the Python implementation of FCN. f[0] = self.function_to_minimize(*parameter_list) def minimize(self, final_fit=True, log_print_level=2): '''Do the minimization. This calls `Minuit`'s algorithms ``MIGRAD`` for minimization and, if `final_fit` is `True`, also ``HESSE`` for computing/checking the parameter error matrix.''' # Set the FCN again. This HAS to be done EVERY # time the minimize method is called because of # the implementation of SetFCN, which is not # object-oriented but sets a global pointer!!! logger.debug("Updating current FCN") self.__gMinuit.SetFCN(self.FCN_wrapper) # Run minimization algorithm (MIGRAD + HESSE) error_code = Long(0) prefix = "Minuit run on" # set the timestamp prefix # insert timestamp self.out_file.write('\n') self.out_file.write('#'*(len(prefix)+4+20)) self.out_file.write('\n') self.out_file.write("# %s " % (prefix,) + strftime("%Y-%m-%d %H:%M:%S #\n", gmtime())) self.out_file.write('#'*(len(prefix)+4+20)) self.out_file.write('\n\n') self.out_file.flush() # redirect stdout stream _redirection_target = None if log_print_level >= 0: _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): self.__gMinuit.SetPrintLevel(log_print_level) # set Minuit print level logger.debug("Running MIGRAD") self.__gMinuit.mnexcm("MIGRAD", arr('d', [self.max_iterations, self.tolerance]), 2, error_code) if(final_fit): logger.debug("Running HESSE") self.__gMinuit.mnexcm("HESSE", arr('d', [self.max_iterations]), 1, error_code) # return to normal print level self.__gMinuit.SetPrintLevel(self.print_level) def minos_errors(self, log_print_level=1): ''' Get (asymmetric) parameter uncertainties from MINOS algorithm. This calls `Minuit`'s algorithms ``MINOS``, which determines parameter uncertainties using profiling of the chi2 function. returns : tuple A tuple of [err+, err-, parabolic error, global correlation] ''' # Set the FCN again. This HAS to be done EVERY # time the minimize method is called because of # the implementation of SetFCN, which is not # object-oriented but sets a global pointer!!! logger.debug("Updating current FCN") self.__gMinuit.SetFCN(self.FCN_wrapper) # redirect stdout stream _redirection_target = None if log_print_level >= 0: _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): self.__gMinuit.SetPrintLevel(log_print_level) logger.debug("Running MINOS") error_code = Long(0) self.__gMinuit.mnexcm("MINOS", arr('d', [self.max_iterations]), 1, error_code) # return to normal print level self.__gMinuit.SetPrintLevel(self.print_level) output = [] errpos=Double(0) # positive parameter error errneg=Double(0) # negative parameter error err=Double(0) # parabolic error gcor=Double(0) # global correlation coefficient for i in range(0, self.number_of_parameters): self.__gMinuit.mnerrs(i, errpos, errneg, err, gcor) output.append([float(errpos),float(errneg),float(err),float(gcor)]) return output
class Minuit: ''' A class for communicating with ROOT's function minimizer tool Minuit. ''' def __init__(self, number_of_parameters, function_to_minimize, parameter_names, start_parameters, parameter_errors, quiet=True, verbose=False): ''' Create a Minuit minimizer for a function `function_to_minimize`. Necessary arguments are the number of parameters and the function to be minimized `function_to_minimize`. The function `function_to_minimize`'s arguments must be numerical values. The same goes for its output. Another requirement is for every parameter of `function_to_minimize` to have a default value. These are then used to initialize Minuit. **number_of_parameters** : int The number of parameters of the function to minimize. **function_to_minimize** : function The function which `Minuit` should minimize. This must be a Python function with <``number_of_parameters``> arguments. **parameter_names** : tuple/list of strings The parameter names. These are used to keep track of the parameters in `Minuit`'s output. **start_parameters** : tuple/list of floats The start values of the parameters. It is important to have a good, if rough, estimate of the parameters at the minimum before starting the minimization. Wrong initial parameters can yield a local minimum instead of a global one. **parameter_errors** : tuple/list of floats An initial guess of the parameter errors. These errors are used to define the initial step size. *quiet* : boolean (optional, default: ``True``) If ``True``, suppresses all output from ``TMinuit``. *verbose* : boolean (optional, default: ``False``) If ``True``, sets ``TMinuit``'s print level to a high value, so that all output is logged. ''' #: the name of this minimizer type self.name = "ROOT::TMinuit" #: the actual `FCN` called in ``FCN_wrapper`` self.function_to_minimize = function_to_minimize #: number of parameters to minimize for self.number_of_parameters = number_of_parameters if not quiet: self.out_file = open(log_file("minuit.log"), 'a') else: self.out_file = null_file() # create a TMinuit instance for that number of parameters self.__gMinuit = TMinuit(self.number_of_parameters) # instruct Minuit to use this class's FCN_wrapper method as a FCN self.__gMinuit.SetFCN(self.FCN_wrapper) # set print level according to flag if quiet: self.set_print_level(-1000) # suppress output elif verbose: self.set_print_level(10) # detailed output else: self.set_print_level(0) # frugal output # initialize minimizer self.set_err() self.set_strategy() self.set_parameter_values(start_parameters) self.set_parameter_errors(parameter_errors) self.set_parameter_names(parameter_names) #: maximum number of iterations until ``TMinuit`` gives up self.max_iterations = M_MAX_ITERATIONS #: ``TMinuit`` tolerance self.tolerance = M_TOLERANCE def update_parameter_data(self, show_warnings=False): """ (Re-)Sets the parameter names, values and step size on the C++ side of Minuit. """ error_code = Long(0) try: # Set up the starting fit parameters in TMinuit for i in range(0, self.number_of_parameters): self.__gMinuit.mnparm(i, self.parameter_names[i], self.current_parameters[i], 0.1 * self.parameter_errors[i], 0, 0, error_code) # use 10% of the par. 1-sigma errors as the initial step size except AttributeError as e: if show_warnings: logger.warn("Cannot update Minuit data on the C++ side. " "AttributeError: %s" % (e, )) return error_code # Set methods ############## def set_print_level(self, print_level=P_DETAIL_LEVEL): '''Sets the print level for Minuit. *print_level* : int (optional, default: 1 (frugal output)) Tells ``TMinuit`` how much output to generate. The higher this value, the more output it generates. ''' self.__gMinuit.SetPrintLevel(print_level) # set Minuit print level self.print_level = print_level def set_strategy(self, strategy_id=1): '''Sets the strategy Minuit. *strategy_id* : int (optional, default: 1 (optimized)) Tells ``TMinuit`` to use a certain strategy. Refer to ``TMinuit``'s documentation for available strategies. ''' error_code = Long(0) # execute SET STRATEGY command self.__gMinuit.mnexcm("SET STRATEGY", arr('d', [strategy_id]), 1, error_code) def set_err(self, up_value=1.0): '''Sets the ``UP`` value for Minuit. *up_value* : float (optional, default: 1.0) This is the value by which `FCN` is expected to change. ''' # Tell TMinuit to use an up-value of 1.0 error_code = Long(0) # execute SET ERR command self.__gMinuit.mnexcm("SET ERR", arr('d', [up_value]), 1, error_code) def set_parameter_values(self, parameter_values): ''' Sets the fit parameters. If parameter_values=`None`, tries to infer defaults from the function_to_minimize. ''' if len(parameter_values) == self.number_of_parameters: self.current_parameters = parameter_values else: raise Exception("Cannot get default parameter values from the \ FCN. Not all parameters have default values given.") self.update_parameter_data() def set_parameter_names(self, parameter_names): '''Sets the fit parameters. If parameter_values=`None`, tries to infer defaults from the function_to_minimize.''' if len(parameter_names) == self.number_of_parameters: self.parameter_names = parameter_names else: raise Exception("Cannot set param names. Tuple length mismatch.") self.update_parameter_data() def set_parameter_errors(self, parameter_errors=None): '''Sets the fit parameter errors. If parameter_values=`None`, sets the error to 10% of the parameter value.''' if parameter_errors is None: # set to 0.1% of the parameter value if not self.current_parameters is None: self.parameter_errors = [max(0.1, 0.1 * par) for par in self.current_parameters] else: raise Exception("Cannot set parameter errors. No errors \ provided and no parameters initialized.") elif len(parameter_errors) != len(self.current_parameters): raise Exception("Cannot set parameter errors. \ Tuple length mismatch.") else: self.parameter_errors = parameter_errors self.update_parameter_data() # Get methods ############## def get_error_matrix(self): '''Retrieves the parameter error matrix from TMinuit. return : `numpy.matrix` ''' # set up an array of type `double' to pass to TMinuit tmp_array = arr('d', [0.0]*(self.number_of_parameters**2)) # get parameter covariance matrix from TMinuit self.__gMinuit.mnemat(tmp_array, self.number_of_parameters) # reshape into 2D array return np.asmatrix( np.reshape( tmp_array, (self.number_of_parameters, self.number_of_parameters) ) ) def get_parameter_values(self): '''Retrieves the parameter values from TMinuit. return : tuple Current `Minuit` parameter values ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(p)) return tuple(result) def get_parameter_errors(self): '''Retrieves the parameter errors from TMinuit. return : tuple Current `Minuit` parameter errors ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append(float(pe)) return tuple(result) def get_parameter_info(self): '''Retrieves parameter information from TMinuit. return : list of tuples ``(parameter_name, parameter_val, parameter_error)`` ''' result = [] # retrieve fit parameters p, pe = Double(0), Double(0) for i in range(0, self.number_of_parameters): self.__gMinuit.GetParameter(i, p, pe) # retrieve fitresult result.append((self.get_parameter_name(i), float(p), float(pe))) return result def get_parameter_name(self, parameter_nr): '''Gets the name of parameter number ``parameter_nr`` **parameter_nr** : int Number of the parameter whose name to get. ''' return self.parameter_names[parameter_nr] def get_fit_info(self, info): '''Retrieves other info from `Minuit`. **info** : string Information about the fit to retrieve. This can be any of the following: - ``'fcn'``: `FCN` value at minimum, - ``'edm'``: estimated distance to minimum - ``'err_def'``: `Minuit` error matrix status code - ``'status_code'``: `Minuit` general status code ''' # declare vars in which to retrieve other info fcn_at_min = Double(0) edm = Double(0) err_def = Double(0) n_var_param = Long(0) n_tot_param = Long(0) status_code = Long(0) # Tell TMinuit to update the variables declared above self.__gMinuit.mnstat(fcn_at_min, edm, err_def, n_var_param, n_tot_param, status_code) if info == 'fcn': return fcn_at_min elif info == 'edm': return edm elif info == 'err_def': return err_def elif info == 'status_code': try: return D_MATRIX_ERROR[status_code] except: return status_code def get_chi2_probability(self, n_deg_of_freedom): ''' Returns the probability that an observed :math:`\chi^2` exceeds the calculated value of :math:`\chi^2` for this fit by chance, even for a correct model. In other words, returns the probability that a worse fit of the model to the data exists. If this is a small value (typically <5%), this means the fit is pretty bad. For values below this threshold, the model very probably does not fit the data. n_def_of_freedom : int The number of degrees of freedom. This is typically :math:`n_\text{datapoints} - n_\text{parameters}`. ''' chi2 = Double(self.get_fit_info('fcn')) ndf = Long(n_deg_of_freedom) return TMath.Prob(chi2, ndf) def get_contour(self, parameter1, parameter2, n_points=21): ''' Returns a list of points (2-tuples) representing a sampling of the :math:`1\\sigma` contour of the TMinuit fit. The ``FCN`` has to be minimized before calling this. **parameter1** : int ID of the parameter to be displayed on the `x`-axis. **parameter2** : int ID of the parameter to be displayed on the `y`-axis. *n_points* : int (optional) number of points used to draw the contour. Default is 21. *returns* : 2-tuple of tuples a 2-tuple (x, y) containing ``n_points+1`` points sampled along the contour. The first point is repeated at the end of the list to generate a closed contour. ''' self.out_file.write('\n') # entry in log-file self.out_file.write('\n') self.out_file.write('#'*(5+28)) self.out_file.write('\n') self.out_file.write('# Contour for parameters %2d, %2d #\n'\ %(parameter1, parameter2) ) self.out_file.write('#'*(5+28)) self.out_file.write('\n\n') self.out_file.flush() # # first, make sure we are at minimum self.minimize(final_fit=True, log_print_level=0) # get the TGraph object from ROOT g = self.__gMinuit.Contour(n_points, parameter1, parameter2) # extract point data into buffers xbuf, ybuf = g.GetX(), g.GetY() N = g.GetN() # generate tuples from buffers x = np.frombuffer(xbuf, dtype=float, count=N) y = np.frombuffer(ybuf, dtype=float, count=N) # return (x, y) def get_profile(self, parid, n_points=21): ''' Returns a list of points (2-tuples) the profile the :math:`\\chi^2` of the TMinuit fit. **parid** : int ID of the parameter to be displayed on the `x`-axis. *n_points* : int (optional) number of points used for profile. Default is 21. *returns* : two arrays, par. values and corresp. :math:`\\chi^2` containing ``n_points`` sampled profile points. ''' self.out_file.write('\n') # entry in log-file self.out_file.write('\n') self.out_file.write('#'*(2+26)) self.out_file.write('\n') self.out_file.write("# Profile for parameter %2d #\n" % (parid)) self.out_file.write('#'*(2+26)) self.out_file.write('\n\n') self.out_file.flush() # redirect stdout stream _redirection_target = None ## -- disable redirection completely, for now ##if log_print_level >= 0: ## _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): pv = [] chi2 = [] error_code = Long(0) self.__gMinuit.mnexcm("SET PRINT", arr('d', [0.0]), 1, error_code) # no printout # first, make sure we are at minimum, i.e. re-minimize self.minimize(final_fit=True, log_print_level=0) minuit_id = Double(parid + 1) # Minuit parameter numbers start with 1 # retrieve information about parameter with id=parid pmin = Double(0) perr = Double(0) self.__gMinuit.GetParameter(parid, pmin, perr) # retrieve fitresult # fix parameter parid ... self.__gMinuit.mnexcm("FIX", arr('d', [minuit_id]), 1, error_code) # ... and scan parameter values, minimizing at each point for v in np.linspace(pmin - 3.*perr, pmin + 3.*perr, n_points): pv.append(v) self.__gMinuit.mnexcm("SET PAR", arr('d', [minuit_id, Double(v)]), 2, error_code) self.__gMinuit.mnexcm("MIGRAD", arr('d', [self.max_iterations, self.tolerance]), 2, error_code) chi2.append(self.get_fit_info('fcn')) # release parameter to back to initial value and release self.__gMinuit.mnexcm("SET PAR", arr('d', [minuit_id, Double(pmin)]), 2, error_code) self.__gMinuit.mnexcm("RELEASE", arr('d', [minuit_id]), 1, error_code) return pv, chi2 # Other methods ################ def fix_parameter(self, parameter_number): ''' Fix parameter number <`parameter_number`>. **parameter_number** : int Number of the parameter to fix. ''' error_code = Long(0) logger.info("Fixing parameter %d in Minuit" % (parameter_number,)) # execute FIX command self.__gMinuit.mnexcm("FIX", arr('d', [parameter_number+1]), 1, error_code) def release_parameter(self, parameter_number): ''' Release parameter number <`parameter_number`>. **parameter_number** : int Number of the parameter to release. ''' error_code = Long(0) logger.info("Releasing parameter %d in Minuit" % (parameter_number,)) # execute RELEASE command self.__gMinuit.mnexcm("RELEASE", arr('d', [parameter_number+1]), 1, error_code) def reset(self): '''Execute TMinuit's `mnrset` method.''' self.__gMinuit.mnrset(0) # reset TMinuit def FCN_wrapper(self, number_of_parameters, derivatives, f, parameters, internal_flag): ''' This is actually a function called in *ROOT* and acting as a C wrapper for our `FCN`, which is implemented in Python. This function is called by `Minuit` several times during a fit. It doesn't return anything but modifies one of its arguments (*f*). This is *ugly*, but it's how *ROOT*'s ``TMinuit`` works. Its argument structure is fixed and determined by `Minuit`: **number_of_parameters** : int The number of parameters of the current fit **derivatives** : C array If the user chooses to calculate the first derivative of the function inside the `FCN`, this value should be written here. This interface to `Minuit` ignores this derivative, however, so calculating this inside the `FCN` has no effect (yet). **f** : C array The desired function value is in f[0] after execution. **parameters** : C array A C array of parameters. Is cast to a Python list **internal_flag** : int A flag allowing for different behaviour of the function. Can be any integer from 1 (initial run) to 4(normal run). See `Minuit`'s specification. ''' # Retrieve the parameters from the C side of ROOT and # store them in a Python list -- resource-intensive # for many calls, but can't be improved (yet?) parameter_list = np.frombuffer(parameters, dtype=float, count=self.number_of_parameters) # call the Python implementation of FCN. f[0] = self.function_to_minimize(*parameter_list) def minimize(self, final_fit=True, log_print_level=2): '''Do the minimization. This calls `Minuit`'s algorithms ``MIGRAD`` for minimization and, if `final_fit` is `True`, also ``HESSE`` for computing/checking the parameter error matrix.''' # Set the FCN again. This HAS to be done EVERY # time the minimize method is called because of # the implementation of SetFCN, which is not # object-oriented but sets a global pointer!!! logger.debug("Updating current FCN") self.__gMinuit.SetFCN(self.FCN_wrapper) # Run minimization algorithm (MIGRAD + HESSE) error_code = Long(0) prefix = "Minuit run on" # set the timestamp prefix # insert timestamp self.out_file.write('\n') self.out_file.write('#'*(len(prefix)+4+20)) self.out_file.write('\n') self.out_file.write("# %s " % (prefix,) + strftime("%Y-%m-%d %H:%M:%S #\n", gmtime())) self.out_file.write('#'*(len(prefix)+4+20)) self.out_file.write('\n\n') self.out_file.flush() # redirect stdout stream _redirection_target = None if log_print_level >= 0: _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): self.__gMinuit.SetPrintLevel(log_print_level) # set Minuit print level logger.debug("Running MIGRAD") self.__gMinuit.mnexcm("MIGRAD", arr('d', [self.max_iterations, self.tolerance]), 2, error_code) if(final_fit): logger.debug("Running HESSE") self.__gMinuit.mnexcm("HESSE", arr('d', [self.max_iterations]), 1, error_code) # return to normal print level self.__gMinuit.SetPrintLevel(self.print_level) def minos_errors(self, log_print_level=1): ''' Get (asymmetric) parameter uncertainties from MINOS algorithm. This calls `Minuit`'s algorithms ``MINOS``, which determines parameter uncertainties using profiling of the chi2 function. returns : tuple A tuple of [err+, err-, parabolic error, global correlation] ''' # Set the FCN again. This HAS to be done EVERY # time the minimize method is called because of # the implementation of SetFCN, which is not # object-oriented but sets a global pointer!!! logger.debug("Updating current FCN") self.__gMinuit.SetFCN(self.FCN_wrapper) # redirect stdout stream _redirection_target = None if log_print_level >= 0: _redirection_target = self.out_file with redirect_stdout_to(_redirection_target): self.__gMinuit.SetPrintLevel(log_print_level) logger.debug("Running MINOS") error_code = Long(0) self.__gMinuit.mnexcm("MINOS", arr('d', [self.max_iterations]), 1, error_code) # return to normal print level self.__gMinuit.SetPrintLevel(self.print_level) output = [] errpos=Double(0) # positive parameter error errneg=Double(0) # negative parameter error err=Double(0) # parabolic error gcor=Double(0) # global correlation coefficient for i in range(0, self.number_of_parameters): self.__gMinuit.mnerrs(i, errpos, errneg, err, gcor) output.append([float(errpos),float(errneg),float(err),float(gcor)]) return output
def fit(self): numberOfParameters = len(self.samples) gMinuit = TMinuit(numberOfParameters) if self.method == "logLikelihood": # set function for minimisation gMinuit.SetFCN(self.logLikelihood) gMinuit.SetMaxIterations(1000000000000) # set Minuit print level # printlevel = -1 quiet (also suppress all warnings) # = 0 normal # = 1 verbose # = 2 additional output giving intermediate results. # = 3 maximum output, showing progress of minimizations. gMinuit.SetPrintLevel(-1) # Error definition: 1 for chi-squared, 0.5 for negative log likelihood # SETERRDEF<up>: Sets the value of UP (default value= 1.), defining parameter errors. # Minuit defines parameter errors as the change in parameter value required to change the function value by UP. # Normally, for chisquared fits UP=1, and for negative log likelihood, UP=0.5. gMinuit.SetErrorDef(0.5) # error flag for functions passed as reference.set to as 0 is no error errorFlag = Long(2) N_min = 0 N_max = self.fit_data_collection.max_n_data() * 2 param_index = 0 # MNPARM # Implements one parameter definition: # mnparm(k, cnamj, uk, wk, a, b, ierflg) # K (external) parameter number # CNAMK parameter name # UK starting value # WK starting step size or uncertainty # A, B lower and upper physical parameter limits # and sets up (updates) the parameter lists. # Output: IERFLG =0 if no problems # >0 if MNPARM unable to implement definition for sample in self.samples: # all samples but data if self.n_distributions > 1: gMinuit.mnparm( param_index, sample, self.normalisation[self.distributions[0]][sample], 10.0, N_min, N_max, errorFlag, ) else: gMinuit.mnparm(param_index, sample, self.normalisation[sample], 10.0, N_min, N_max, errorFlag) param_index += 1 arglist = array("d", 10 * [0.0]) # minimisation strategy: 1 standard, 2 try to improve minimum (a bit slower) arglist[0] = 2 # minimisation itself # SET STRategy<level>: Sets the strategy to be used in calculating first and second derivatives and in certain minimization methods. # In general, low values of <level> mean fewer function calls and high values mean more reliable minimization. # Currently allowed values are 0, 1 (default), and 2. gMinuit.mnexcm("SET STR", arglist, 1, errorFlag) gMinuit.Migrad() gMinuit.mnscan() # class for minimization using a scan method to find the minimum; allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc. gMinuit.mnmatu(1) # prints correlation matrix (always needed) self.module = gMinuit self.performedFit = True if not self.module: raise Exception("No fit results available. Please run fit method first") results = {} param_index = 0 for sample in self.samples: temp_par = Double(0) temp_err = Double(0) self.module.GetParameter(param_index, temp_par, temp_err) if math.isnan(temp_err): self.logger.warning("Template fit error is NAN, setting to sqrt(N).") temp_err = math.sqrt(temp_par) # gMinuit.Command("SCAn %i %i %i %i" % ( param_index, 100, N_min, N_total ) ); # scan = gMinuit.GetPlot() # results[sample] = ( temp_par, temp_err, scan ) results[sample] = (temp_par, temp_err) param_index += 1 # # gMinuit.Command("CONtour 1 2 3 50") # gMinuit.SetErrorDef(1) # results['contour'] = [gMinuit.Contour(100, 0, 1)] # gMinuit.SetErrorDef(4) # results['contour'].append(gMinuit.Contour(100, 0, 1)) self.results = results
npar: # of parameters deriv: array of derivatives df/dp_i(x), optional f: value of function to be minimised (typically chi2 or negLogL) apar: the array of parameters iflag: internal flag: 1 at first call, 3 at the last, 4 during minimisation """ f[0] = calcChi2(npar, apar) # --> set up MINUIT myMinuit = TMinuit(npar) # initialize TMinuit with maximum of npar parameters myMinuit.setFCN(fcn) # set func to minimize arglist = arr('d', 2*[0.01])# set error definition ieflg = Long(0) arglist[0] = 1. # 1 sigma is Delta chi^2 = 1 myMinuit.mnexcm("SET ERR", arglist, 1, ierflg) #−−>Set starting values and step sizes for parameters for i in range(0, npar): #Define the parameters for the fit myMinuit.mnparm(i, name[i], vstart[i], step[i], 0, 0, ierflg) arglist[0] = 6000 # Number of calls to FCN before giving up. arglist[1] = 0.3 # Tolerance myMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) # execute the minimisation #−−> check TMinuit status amin, edm, errdef = Double(0.), Double(0.), Double(0.) nvpar, nparx, icstat = Long(0), Long(0), Long(0)
class Fits: def __init__(self): self.p_n = [ 0, ] * 100 self.e_n = [ 0, ] * 100 self.stored_parameters = [ 0, ] * 100 self.num_bins = 0 self.xmins = [] self.xmaxes = [] self.data = [] self.errors = [] self.data_fits = [] self.model_scale_values = [] self.final = False self.exclude_regions = ((0, 0), ) self.col1 = 1 self.col2 = TColor.GetColor(27, 158, 119) self.col3 = TColor.GetColor(217, 95, 2) self.col4 = TColor.GetColor(117, 112, 179) def run_mass_fit(self, peak_scale_initial): self.gMinuit = TMinuit(30) self.gMinuit.SetPrintLevel(-1) self.gMinuit.SetFCN(self.Fitfcn_max_likelihood) self.fit_failed = False arglist = array("d", [ 0, ] * 10) ierflg = ROOT.Long(0) arglist[0] = ROOT.Double(1) # peak_scale_initial = ROOT.Double(peak_scale_initial) tmp = array("d", [ 0, ]) self.gMinuit.mnexcm("SET NOWarnings", tmp, 0, ierflg) self.gMinuit.mnexcm("SET ERR", arglist, 1, ierflg) self.gMinuit.mnparm(0, "p1", 30, 20, 0, 100, ierflg) self.gMinuit.mnparm(1, "p2", 10, 1, 0, 0, ierflg) self.gMinuit.mnparm(2, "p3", -5.3, 1, 0, 0, ierflg) self.gMinuit.mnparm(3, "p4", -4e-2, 1e-2, 0, 0, ierflg) self.gMinuit.mnparm(4, "p5", peak_scale_initial, 1, 0, 10000, ierflg) self.background_fit_only = [ 0, ] * len(self.data) arglist[0] = ROOT.Double(0) arglist[1] = ROOT.Double(0) #self.exclude_regions = ((2.2, 3.3),) self.gMinuit.FixParameter(1) self.gMinuit.FixParameter(2) self.gMinuit.FixParameter(3) self.gMinuit.FixParameter(4) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run fit 0") if self.fit_failed: print("Fit failed, returning") return None, None self.gMinuit.Release(1) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run fit 1") if self.fit_failed: print("Fit failed, returning") return None, None self.gMinuit.Release(2) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run fit 2") if self.fit_failed: print("Fit failed, returning") return None, None self.gMinuit.Release(3) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run fit 3") if self.fit_failed: print("Fit failed, returning") return None, None # Find an actual best fit #self.exclude_regions = ((0, 2), (3.3, 100),) self.gMinuit.FixParameter(0) self.gMinuit.FixParameter(1) self.gMinuit.FixParameter(2) self.gMinuit.FixParameter(3) self.gMinuit.Release(4) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run fit 4") if self.fit_failed: print("Fit failed, returning") return None, None self.exclude_regions = () self.gMinuit.Release(0) self.gMinuit.Release(1) self.gMinuit.Release(2) self.gMinuit.Release(3) self.gMinuit.mnexcm("simplex", arglist, 2, ierflg) self.gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) print("Run last fitting stage") if self.fit_failed: print("Fit failed, returning") return None, None best_fit_value = ROOT.Double(0) self.gMinuit.mnstat(best_fit_value, ROOT.Double(0), ROOT.Double(0), ROOT.Long(0), ROOT.Long(0), ROOT.Long(0)) #print("Best fit value", best_fit_value) # And prepare for iterating over fit values for N injected events self.gMinuit.Release(0) self.gMinuit.Release(1) self.gMinuit.Release(2) self.gMinuit.Release(3) self.exclude_regions = () #self.data_fits = no_peak_data_fits x_values = [] y_values = [] for i in range(0, 5): p = ROOT.Double(0) self.gMinuit.GetParameter(i, p, ROOT.Double(0)) print("Parameter", i, p) fitted_N = ROOT.Double(0) self.gMinuit.GetParameter(4, fitted_N, ROOT.Double(0)) best_fit_likelihood = self.calc_likelihood(fitted_N) self.iflag = int(ierflg) print("iflag:", self.iflag) step = 5 if int(MASS_FILE) >= 4000: step = 1 if int(MASS_FILE) >= 5000: step = 0.2 if int(MASS_FILE) >= 6000: step = 0.1 if int(MASS_FILE) >= 6500: step = 0.1 N = 0 print("About to start likelihood testing loop") while N < 10000: start_sum = sum([math.exp(-a) for a in y_values]) fit_likelihood = self.calc_likelihood(N) if fit_likelihood is None: print("Bad fit_likelihood") return None, None x_values.append(N) y_values.append(fit_likelihood - best_fit_likelihood) probabilities = [math.exp(-a) for a in y_values] end_sum = sum(probabilities) max_prob = max(probabilities) if max_prob == 0: print("max_prob == 0") return None, None normalised_probabilities = [a / max_prob for a in probabilities] if N / step > 50 and all( [v > 0.99 for v in normalised_probabilities]): print( "Probability=1 everywhere, probably something wrong with fit" ) print(normalised_probabilities) return None, None # if new value changes total by less than 0.1%, end loop if N > 0 and (end_sum - start_sum) / start_sum < 0.0001: print("Iterated up to {0}".format(N)) break N += step self.iflag = int(ierflg) return x_values, y_values def Fitfcn_max_likelihood(self, npar, gin, fcnVal, par, iflag): likelihood = 0 mf = ROOT.MyMassSpectrum() mf.SetParameters(par) ig = GaussIntegrator() ig.SetFunction(mf) ig.SetRelTolerance(0.00001) for i in range(0, self.num_bins): for lower, higher in self.exclude_regions: if lower < self.xmins[i] < higher: continue model_val = ig.Integral(self.xmins[i], self.xmaxes[i]) / ( self.xmaxes[i] - self.xmins[i]) self.background_fit_only[i] = model_val model_val += self.model_scale_values[i] * par[4] self.data_fits[i] = model_val mv = model_val di = self.data[i] #print("mv", mv, "di", di) #sys.stdout.flush() if di > 1e10 or math.isinf(mv) or math.isnan(mv): self.fit_failed = True return likelihood += mv - di if di > 0 and mv > 0: likelihood += di * (TMath.Log(di) - TMath.Log(mv)) #sys.stderr.flush() fcnVal[0] = likelihood def calc_likelihood(self, peak_scale): like = 0 for i in range(0, self.num_bins): if self.data_fits[i] <= 0: continue p = peak_scale * self.model_scale_values[i] tmp = ROOT.TMath.PoissonI(self.data[i], self.background_fit_only[i] + p) if tmp == 0: return None print("tmp == 0 here") logtmp = math.log(sys.float_info.min) else: logtmp = math.log(tmp) like += logtmp return -like
def bkgfit(data_hist, bkgfunction, bkgname, doFloatZ=False, signal_hist=None, z_hist=None): isBkgPlusZFit = False isSpuriousFit = False binning = HistBinsToList(data_hist) data_x = HistToList(data_hist) data_error = HistErrorList(data_hist) z_x = [] signal_x = [] if z_hist != None: isBkgPlusZFit = True z_x = HistToList(z_hist) if signal_hist != None: isSpuriousFit = True signal_x = HistToList(signal_hist) parfunction = bkgfunction.GetNumberFreeParameters() partot = bkgfunction.GetNumberFreeParameters() + 2 ### the fucntion used for TMinuit def fcn(npar, gin, f, par, ifag): L = 0 # calculate likelihood, input par[0] is the N_B, par[1] is N_C, par[2] is N_L for ibin in range(len(binning)): if (data_x[ibin] < 0.5): continue bincen = binning[ibin] bkg = 0 data = data_x[ibin] if bkgname == "BernsteinO2": bkg = (par[0] * (1 - (bincen - fit_start) / fit_range)**2 + 2 * par[1] * (1 - (bincen - fit_start) / fit_range) * ((bincen - fit_start) / fit_range) + par[2] * ((bincen - fit_start) / fit_range)**2) if bkgname == "BernsteinO3": bkg = par[0] * (1 - ( (bincen - fit_start) / fit_range))**3 + par[1] * ( 3 * ((bincen - fit_start) / fit_range) * (1 - ((bincen - fit_start) / fit_range))**2) + par[2] * ( 3 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))) + par[3] * ( (bincen - fit_start) / fit_range)**3 if bkgname == "BernsteinO4": bkg = par[0] * (1 - ( (bincen - fit_start) / fit_range))**4 + par[1] * ( 4 * ((bincen - fit_start) / fit_range) * (1 - ((bincen - fit_start) / fit_range))**3) + par[2] * ( 6 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))**2 ) + par[3] * ( 4 * ((bincen - fit_start) / fit_range)**3 * (1 - ((bincen - fit_start) / fit_range))) + par[4] * ( (bincen - fit_start) / fit_range)**4 if bkgname == "BernsteinO5": bkg = par[0] * (1 - ( (bincen - fit_start) / fit_range))**5 + par[1] * (5 * ( (bincen - fit_start) / fit_range) * (1 - ( (bincen - fit_start) / fit_range))**4) + par[2] * ( 10 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))**3 ) + par[3] * (10 * ( (bincen - fit_start) / fit_range)**3 * (1 - ( (bincen - fit_start) / fit_range ))**2) + par[4] * (5 * ( (bincen - fit_start) / fit_range)**4 * (1 - ((bincen - fit_start) / fit_range))) + par[5] * ( (bincen - fit_start) / fit_range)**5 if bkgname == "BernsteinO6": bkg = (par[0] * (1 - ((bincen - fit_start) / fit_range))**6 + par[1] * (6 * ((bincen - fit_start) / fit_range)**1 * (1 - ((bincen - fit_start) / fit_range))**5) + par[2] * (15 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))**4) + par[3] * (20 * ((bincen - fit_start) / fit_range)**3 * (1 - ((bincen - fit_start) / fit_range))**3) + par[4] * (15 * ((bincen - fit_start) / fit_range)**4 * (1 - ((bincen - fit_start) / fit_range))**2) + par[5] * (6 * ((bincen - fit_start) / fit_range)**5 * (1 - ((bincen - fit_start) / fit_range))**1) + par[6] * ((bincen - fit_start) / fit_range)**6) if bkgname == "ExpoBernsteinO2": try: bkg = exp(par[0] * (bincen - fit_start) / fit_range) * ( par[1] * (1 - (bincen - fit_start) / fit_range)**2 + 2 * par[2] * (1 - (bincen - fit_start) / fit_range) * ((bincen - fit_start) / fit_range) + par[3] * ((bincen - fit_start) / fit_range)**2) except OverflowError: bkg = 0 if bkgname == "ExpoBernsteinO3": try: bkg = exp(par[0] * (bincen - fit_start) / fit_range) * ( par[1] * (1 - ((bincen - fit_start) / fit_range))**3 + par[2] * (3 * ((bincen - fit_start) / fit_range) * (1 - ((bincen - fit_start) / fit_range))**2) + par[3] * (3 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))) + par[4] * ((bincen - fit_start) / fit_range)**3) except OverflowError: bkg = 0 if bkgname == "ExpoBernsteinO4": try: bkg = exp(par[0] * (bincen - fit_start) / fit_range) * ( par[1] * (1 - ((bincen - fit_start) / fit_range))**4 + par[2] * (4 * ((bincen - fit_start) / fit_range) * (1 - ((bincen - fit_start) / fit_range))**3) + par[3] * (6 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))**2) + par[4] * (4 * ((bincen - fit_start) / fit_range)**3 * (1 - ((bincen - fit_start) / fit_range))) + par[5] * ((bincen - fit_start) / fit_range)**4) except OverflowError: bkg = 0 if bkgname == "ExpoBernsteinO5": try: bkg = exp(par[0] * (bincen - fit_start) / fit_range) * ( par[1] * (1 - ((bincen - fit_start) / fit_range))**5 + par[2] * (5 * ((bincen - fit_start) / fit_range) * (1 - ((bincen - fit_start) / fit_range))**4) + par[3] * (10 * ((bincen - fit_start) / fit_range)**2 * (1 - ((bincen - fit_start) / fit_range))**3) + par[4] * (10 * ((bincen - fit_start) / fit_range)**3 * (1 - ((bincen - fit_start) / fit_range))**2) + par[5] * (5 * ((bincen - fit_start) / fit_range)**4 * (1 - ((bincen - fit_start) / fit_range))) + par[6] * ((bincen - fit_start) / fit_range)**5) except OverflowError: bkg = 0 if bkgname == "ExpoPolO2": bkg = exp(-(par[0] + par[1] * ((bincen - fit_start) / fit_range) + par[2] * ((bincen - fit_start) / fit_range)**2)) if bkgname == "ExpoPolO3": bkg = exp(-(par[0] + par[1] * ((bincen - fit_start) / fit_range) + par[2] * ((bincen - fit_start) / fit_range)**2 + par[3] * ((bincen - fit_start) / fit_range)**3)) if bkgname == "ExpoPolO4": bkg = exp(-(par[0] + par[1] * ((bincen - fit_start) / fit_range) + par[2] * ((bincen - fit_start) / fit_range)**2 + par[3] * ((bincen - fit_start) / fit_range)**3 + par[4] * ((bincen - fit_start) / fit_range)**4)) mu_x = bkg #if isBkgPlusZFit: # mu_x = mu_x + (par[partot-1] *z_x[ibin]) if isSpuriousFit: mu_x = mu_x + par[partot - 2] * signal_x[ibin] #L = L + mu_x - data*log(mu_x) L = L + ((mu_x - data) / data_error[ibin])**2 f[0] = L # initialize the TMinuit object arglist_p = 10 * [0] arglist = array.array('d') arglist.fromlist(arglist_p) ierflag = Long(0) maxiter = 1000000000 arglist_p = [1] gMinuit = TMinuit(partot) gMinuit.mnexcm('SET PRIntout', arglist, 0, ierflag) gMinuit.SetPrintLevel(1) gMinuit.SetErrorDef(1.0) gMinuit.SetFCN(fcn) arglist_p = [2] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('SET STRategy', arglist, 1, ierflag) arglist_p = [maxiter, 0.0000001] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('MIGrad', arglist, 2, ierflag) gMinuit.SetMaxIterations(maxiter) # initialize fitting the variables vstart = [100.0] * partot # start alpha_z with 1 vstart[partot - 1] = 1.0 vstart[partot - 2] = 0 step = [0.1] * partot upper = [100000] * partot lower = [0.1] * partot varname = [] if "ExpoPol" in bkgname: upper = [1000] * partot lower = [-1000] * partot if "ExpoBernstein" in bkgname: vstart[0] = -1 upper[0] = 0 lower[0] = -10 for i in range(parfunction): varname.append("p" + str(i)) varname.append("alpha_sig") varname.append("alpha_z") if doFloatZ: vstart[partot - 1] = 1.0 upper[partot - 1] = 2 lower[partot - 1] = 0 step[partot - 1] = 0.01 if isSpuriousFit: upper[partot - 2] = 10.0 lower[partot - 2] = -10.0 step[partot - 2] = 0.1 vstart[partot - 2] = 1 for i in range(partot): gMinuit.mnparm(i, varname[i], vstart[i], step[i], lower[i], upper[i], ierflag) if not isSpuriousFit: vstart[partot - 2] = 0 gMinuit.FixParameter(partot - 2) if not doFloatZ: lower[partot - 1] = 1 upper[partot - 1] = 1 gMinuit.FixParameter(partot - 1) if not isBkgPlusZFit: vstart[partot - 1] = 0.0 gMinuit.FixParameter(partot - 1) # fitting procedure migradstat = gMinuit.Command('MIGrad ' + str(maxiter) + ' ' + str(0.001)) improvestat = gMinuit.Command('IMProve ' + str(maxiter) + ' ' + str(0.01)) for i in range(partot): arglist_p.append(i + 1) arglist = array.array('d') arglist.fromlist(arglist_p) #gMinuit.mnmnos() # get fitting parameters fitval_p = [Double(0)] * partot fiterr_p = [Double(0)] * partot errup_p = [Double(0)] * partot errdown_p = [Double(0)] * partot eparab_p = [Double(0)] * partot gcc_p = [Double(0)] * partot fmin_p = [Double(0)] fedm_p = [Double(0)] errdef_p = [Double(0)] npari_p = Long(0) nparx_p = Long(0) istat_p = Long(0) fitval = array.array('d') fiterr = array.array('d') errup = array.array('d') errdown = array.array('d') eparab = array.array('d') gcc = array.array('d') for i in range(partot): gMinuit.GetParameter(i, fitval_p[i], fiterr_p[i]) fitval.append(fitval_p[i]) fiterr.append(fiterr_p[i]) errup.append(errup_p[i]) errdown.append(errdown_p[i]) eparab.append(eparab_p[i]) gcc.append(gcc_p[i]) gMinuit.mnstat(fmin_p[0], fedm_p[0], errdef_p[0], npari_p, nparx_p, istat_p) for p in range(bkgfunction.GetNumberFreeParameters()): bkgfunction.SetParameter(p, fitval[p]) print "fit uncert", fiterr_p[p] bkgfunction.SetChisquare(fmin_p[0]) return fitval[partot - 1], fitval[partot - 2]
def fit_model(self, country, time_range): s_data = self.data_sir["susceptible"] i_data = self.data_sir["infected"] r_data = self.data_sir["recovered"] def fcn(nPar, gin, f, par, flag): tr = par[0] rec = par[1] chi2 = 0. n_bins = s_data.GetNbinsX() s_model, i_model, r_model = solve_SIR(tr, rec, time_range) for i in range(1, n_bins + 1): dif = (i_data.GetBinContent(i) - i_model.GetBinContent(i)) / i_data.GetBinError(i) chi2 += dif**2 dif = (r_data.GetBinContent(i) - r_model.GetBinContent(i)) / r_data.GetBinError(i) chi2 += dif**2 f[0] = chi2 return minuit = TMinuit(3) minuit.SetFCN(fcn) vstep = [1e-10, 1e-10] vinit = [1e-10, 1e-10] # vinit = [1e-3, 1e-3] vmin = [1e-15, 1e-15] vmax = [1e0, 1e0] from ctypes import c_int, c_double import array as c_arr arglist = c_arr.array('d', 10 * [0.]) ierflag = 0 arglist[0] = 1 minuit.mnexcm("SET ERR", arglist, 1, c_int(ierflag)) minuit.mnparm(0, "transition rate", vinit[0], vstep[0], vmin[0], vmax[0], c_int(ierflag)) minuit.mnparm(1, "recovery rate", vinit[1], vstep[1], vmin[1], vmax[1], c_int(ierflag)) # minuit.SetErrorDef(1.) # minuit.SetMaxIteration(500) # minuit.Migrad() arglist[0] = 500 arglist[1] = 1. # minuit.mnexcm( "MIGRAD", arglist, 2, c_int(ierflag) ) res_trans = c_double(0.) res_rec = c_double(0.) err_trans = c_double(0.) err_rec = c_double(0.) minuit.GetParameter(0, res_trans, err_trans) minuit.GetParameter(1, res_rec, err_rec) # return res_trans.value, res_rec.value hists = self.solve_SIR(res_trans.value, res_rec.value, time_range) self.hists = { "susceptible": hists[0], "infected": hists[1], "recovered": hists[2] } self.draw()
def make_renormalization_dict(name,alpha,epsilon,muRup,muRdown,muFup,muFdown,scup,scdown,pdfas,pdfasup,pdfasdown,topptrwhist) : global qq_global_hist_projection global gg_global_hist_projection returndict = {} #start with the simple values returndict['muRup']=muRup.GetMean() returndict['muRdown']=muRdown.GetMean() returndict['muFup']=muFup.GetMean() returndict['muFdown']=muFdown.GetMean() returndict['scup']=scup.GetMean() returndict['scdown']=scdown.GetMean() returndict['pdfas']=pdfas.GetMean() returndict['pdfasup']=pdfasup.GetMean() returndict['pdfasdown']=pdfasdown.GetMean() returndict['topptrwmean']=topptrwhist.GetMean() printlines = [] if alpha==1.0 and epsilon==1.0 and (name.find('_TT')!=-1 or name.find('_TTJets')!=-1) : #now we've got to do the fits for the "deweighting" alpha and epsilon values print 'fitting for alpha/epsilon values... ' #Define the Minuit instance we'll use alpha_minuit = TMinuit(1); alpha_minuit.SetFCN(alpha_fcn) epsilon_minuit = TMinuit(1); epsilon_minuit.SetFCN(epsilon_fcn) #miscellaneous minuit stuff ierflag = Long(1); arglist = array('d',[-1]) alpha_minuit.mnexcm('SET PRINT', arglist, 1,ierflag); alpha_minuit.mnexcm('SET NOWARNINGS',arglist,1,ierflag) epsilon_minuit.mnexcm('SET PRINT', arglist, 1,ierflag); epsilon_minuit.mnexcm('SET NOWARNINGS',arglist,1,ierflag) arglist[0]=100000. #for each bin in beta for i in range(1,csvb_qq_global_hist.GetXaxis().GetNbins()+1) : BETA[0] = csvb_qq_global_hist.GetXaxis().GetBinCenter(i) #qq_global_hist_projection = symmetrize(csvb_qq_global_hist.ProjectionY('qq_proj_'+str(i),i,i)) qq_global_hist_projection = csvb_qq_global_hist.ProjectionY('qq_proj_'+str(i),i,i) #add parameter alpha_minuit.mnparm(0,'alpha',0.1,0.5,0.,0.,ierflag) #minimize alpha_minuit.mnexcm('MIGRAD',arglist,1,ierflag) #get back the fitted alpha value fitted_alpha=Double(0.0); fitted_alpha_err=Double(0.0) alpha_minuit.GetParameter(0,fitted_alpha,fitted_alpha_err) #print 'NEW NAME = alpha_'+str(csvb_qq_global_hist.GetXaxis().GetBinLowEdge(i)) returndict['alpha_'+str(csvb_qq_global_hist.GetXaxis().GetBinLowEdge(i))]=fitted_alpha printlines.append('fitted alpha value in bin %d = %.4f +/- %.4f'%(i,fitted_alpha,fitted_alpha_err)) #epsilon stuff for i in range(1,csvb_gg_global_hist.GetXaxis().GetNbins()+1) : BETA[0] = csvb_gg_global_hist.GetXaxis().GetBinCenter(i) #gg_global_hist_projection = symmetrize(csvb_gg_global_hist.ProjectionY('gg_proj_'+str(i),i,i)) gg_global_hist_projection = csvb_gg_global_hist.ProjectionY('gg_proj_'+str(i),i,i) epsilon_minuit.mnparm(0,'epsilon',0.1,0.5,0.,0.,ierflag) epsilon_minuit.mnexcm('MIGRAD',arglist,1,ierflag) fitted_epsilon=Double(0.0); fitted_epsilon_err=Double(0.0) epsilon_minuit.GetParameter(0,fitted_epsilon,fitted_epsilon_err) returndict['epsilon_'+str(csvb_gg_global_hist.GetXaxis().GetBinLowEdge(i))]=fitted_epsilon printlines.append('fitted epsilon value in bin %d = %.4f +/- %.4f'%(i,fitted_epsilon,fitted_epsilon_err)) else : returndict['alpha']=alpha; returndict['epsilon']=epsilon fitted_alpha_err = 1.0; fitted_epsilon_err = 1.0 #print the values print 'muRup value = %.4f'%(returndict['muRup']) print 'muRdown value = %.4f'%(returndict['muRdown']) print 'muFup value = %.4f'%(returndict['muFup']) print 'muFdown value = %.4f'%(returndict['muFdown']) print 'scup value = %.4f'%(returndict['scup']) print 'scdown value = %.4f'%(returndict['scdown']) print 'pdfas value = %.4f'%(returndict['pdfas']) print 'pdfasup value = %.4f'%(returndict['pdfasup']) print 'pdfasdown value = %.4f'%(returndict['pdfasdown']) print 'top pT reweight (v1) mean value = %.4f'%(returndict['topptrwmean']) for line in printlines : print line #return the dictionary return returndict
def fit( p , perr ): global val, err, nBins val = p err = perr nBins=len(val) #name=['q0','q1','q2','q3','q4'] #name=['q0','q1','q2','q3'] #name=['q0','q1','q2'] name = [ 'q0' , 'q1' ] npar=len(name) # the initial values vstart = arr( 'd' , npar*[0.1] ) # the initial step size step = arr( 'd' , npar*[0.000001] ) # --> set up MINUIT gMinuit = TMinuit ( npar ) # initialize TMinuit with maximum of npar parameters gMinuit.SetFCN( fcn ) # set function to minimize arglist = arr( 'd' , npar*[0.01] ) # set error definition ierflg = c_int(0) arglist[0] = 1. # 1 sigma is Delta chi2 = 1 gMinuit.mnexcm("SET ERR", arglist, 1, ierflg ) # --> set starting values and step size for parameters # Define the parameters for the fit for i in range(0,npar): gMinuit.mnparm( i, name[i] , vstart[i] , step[i] , 0, 0, ierflg ) # now ready for minimization step arglist [0] = 500 # Number of calls for FCN before giving up arglist [1] = 1. # Tolerance gMinuit.mnexcm("MIGRAD" , arglist , 2 , ierflg) # execute the minimisation # --> check TMinuit status amin , edm , errdef = c_double(0.) , c_double(0.) , c_double(0.) nvpar , nparx , icstat = c_int(0) , c_int(0) , c_int(0) gMinuit.mnstat (amin , edm , errdef , nvpar , nparx , icstat ) gMinuit.mnprin(3,amin) # print-out by Minuit # meaning of parameters: # amin: value of fcn distance at minimum (=chi^2) # edm: estimated distance to minimum # errdef: delta_fcn used to define 1 sigam errors # nvpar: total number of parameters # icstat: status of error matrix: # 3 = accurate # 2 = forced pos. def # 1 = approximative # 0 = not calculated # # --> get results from MINUIT finalPar = [] finalParErr = [] p, pe = c_double(0.) , c_double(0.) for i in range(0,npar): gMinuit.GetParameter(i, p, pe) # retrieve parameters and errors finalPar.append( float(p.value) ) finalParErr.append( float(pe.value) ) # get covariance matrix buf = arr('d' , npar*npar*[0.]) gMinuit.mnemat( buf , npar ) # retrieve error matrix emat = np.array( buf ).reshape( npar , npar ) # --> provide formatted output of results print "\n" print "*==* MINUIT fit completed:" print'fcn@minimum = %.3g'%( amin.value ) , " error code =" , ierflg.value , " status =" , icstat.value , " (if its 3, mean accurate)" print " Results: \t value error corr. mat." for i in range(0,npar): print'%s: \t%10.3e +/- %.1e'%( name[i] , finalPar[i] , finalParErr[i] ) , for j in range (0,i): print'%+.3g'%( emat[i][j]/np.sqrt(emat[i][i])/np.sqrt(emat[j][j]) ), print "\n" return [ [i,j] for i,j in zip(finalPar , finalParErr) ]
def main(): #The first part of the script is to input the root files# canvas = TCanvas("Canvas_d", "Canvas_d", 533, 76, 1383, 852) input_datafile = TFile(args["input_data"]) EV_data = input_datafile.Get("EventCategorizer subtask 0 stats/ExpecValue") entries_data = EV_data.GetSize() print "# of Entries in Data file:", entries_data print "Experimental Data Inputed" data_arr = [] canvas = TCanvas("Canvas_mc", "Canvas_mc", 533, 76, 1383, 852) input_mcfile = TFile(args["input_mc"]) EV_mc = input_mcfile.Get( "EventCategorizer subtask 0 stats/ExpecValue_Smeared") entries_mc = EV_mc.GetSize() print "# of Entries in MC file:", entries_mc print "MC Data Inputed" mc_arr = [] bin_arr = [] for x in range(entries_data): Data_i = EV_data.GetBinContent(x) MC_i = EV_mc.GetBinContent(x) data_arr.append(Data_i) mc_arr.append(MC_i) Bin_i = EV_data.GetBinCenter(x) bin_arr.append(Bin_i) #print Data_i #print MC_i #print data_arr #print mc_arr #print bin_arr # --> Set parameters and function to f i t name = ["c", "d"] #variable names vstart = arr('d', (1.0, 1.0)) #the initial values step = arr('d', (0.001, 0.001)) #the initial step size npar = len(name) # --> Defining the Chi-Square function to be minimized# def Chi_Induced(Data, MC, BinCenter, C, D): chi = 0. for i in range(0, entries_data): #num1 = Data[i] #num2 = D*((1-(C*BinCenter[i]))*MC[i]) #num = (num1 - num2)**2 #num = 0. #den1 = ((Data[i])**(1/2))**2 #den2 = (D*((1-(C*BinCenter[i])))*((MC[i])**(1/2)))**2 num = 2 den = 2 #den = den1 + den2 chi = chi + num / den return chi # --> set up MINUIT myMinuit = TMinuit( npar) # initialize TMinuit with maximum of npar parameters myMinuit.SetFCN(Chi_Induced) # set function to minimize ierflg = Long(0) arglist = arr('d', 2 * [0.01]) # set error definition arglist[0] = 6000 # Number of calls for FCN before gving up arglist[1] = 0.3 # Toleranceierflg = Long(0) myMinuit.mnexcm("SET ERR", arglist, 1, ierflg) for i in range(0, npar): myMinuit.mnparm(i, name[i], vstart[i], step[i], 0, 0, ierflg) myMinuit.mnexcm("MIGRAD", arglist, 1, ierflg) # execute the minimisation # --> check TMinuit status amin, edm, errdef = Double(0.), Double(0.), Double(0.) nvpar, nparx, icstat = Long(0), Long(0), Long(0) myMinuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat)
def main(): global freqs_hz, pv, sigq, sigu parser = OptionParser(usage) parser.add_option("-r", "--rm", type="float", dest="rm", default=0., help="Estimate of the RM") parser.add_option("-n", "--norm", action="store_true", dest="norm", default=False, help="Verbose mode") parser.add_option("-b", "--baseline", action="store_true", dest="baseline", default=False, help="Fit for a baseline") parser.add_option("-f", "--nofit", action="store_true", dest="nofit", default=False, help="Don't fit for the RM with Minuit") parser.add_option("-c", "--clean", action="store_true", dest="clean", default=False, help="Clean the baseline") (opts, args) = parser.parse_args() #if opts.help: # print full_usage arch = psrchive.Archive_load(sys.argv[1]) arch.tscrunch() arch.bscrunch(2) #arch.fscrunch(2) arch.dedisperse() arch.remove_baseline() arch.convert_state('Stokes') arch.centre_max_bin() #arch.rotate(0.5) data = arch.get_data() MJD = arch.get_first_Integration().get_epoch().strtempo() w = arch.get_weights().sum(0) I = data[:,0,:,:].mean(0) Q = data[:,1,:,:].mean(0) U = data[:,2,:,:].mean(0) #V = data[:,3,:,:].mean(0) I = ma.masked_array(I) Q = ma.masked_array(Q) U = ma.masked_array(U) #V = ma.masked_array(V) I[w==0] = np.ma.masked Q[w==0] = np.ma.masked U[w==0] = np.ma.masked # Get Freqs nchan = arch.get_nchan() freqs = np.zeros(nchan) for i in range(nchan): freqs[i] = arch.get_Profile(0,0,i).get_centre_frequency() ## Determine phase range arch.fscrunch() arch.tscrunch() arch.pscrunch() prof = arch.get_Profile(0,0,0) #print prof.get_amps() lowbin, hibin = get_pulse_range(arch) #lowbin, hibin = 458,531 #lowbin, hibin = 20,120 if opts.clean: for n in range(nchan): if n%20: continue baseline_idx = np.append(np.arange(0, lowbin), np.arange(hibin, arch.get_nbin())) baseline_dat = I[n][baseline_idx] print n, baseline_idx, baseline_dat poly = np.polyfit(baseline_idx, baseline_dat, 1) p = np.poly1d(poly) pylab.plot(I[n]) I[n] = I[n] - p(np.arange(0, arch.get_nbin())) pylab.plot(I[n]) pylab.show() for ii, wx in enumerate(w): I[ii] *= wx/np.max(w) Q[ii] *= wx/np.max(w) U[ii] *= wx/np.max(w) #V[ii] *= wx if opts.norm: print "Will normalize by the rms of the noise" scale = np.std(I, axis=1) / np.max(np.std(I, axis=1)) for ii, s in enumerate(scale): I[ii,:] /= s Q[ii,:] /= s U[ii,:] /= s #Q = Q / scale #U = U / scale freq_lo = freqs[0] freq_hi = freqs[-1] lowphase = lowbin / float(arch.get_nbin()) hiphase = hibin / float(arch.get_nbin()) #lowphase= 0.48 #hiphase = 0.515 #lowphase = 0.515 #hiphase = 0.54 print "Pulse phase window: ", lowphase, hiphase #pylab.plot(np.sum(I), axis) #pylab.show() # 2D plot f = pylab.figure() pylab.subplot(321) #print np.sum(I,axis=0) #pylab.plot(np.sum(I, axis=0)) pylab.plot(prof.get_amps()) pylab.xlim([0, arch.get_nbin()]) f.text( .5, 0.95, r'%s'%os.path.split(sys.argv[1])[1], horizontalalignment='center') pylab.subplot(323) pylab.axis([0,1,freq_lo,freq_hi]) pylab.imshow(I,extent=(0,1,freq_lo,freq_hi), origin='lower', aspect='auto', vmax=I.max()/1.5, vmin=I.min()/1.5) pylab.xlabel('Pulse phase') pylab.ylabel('Frequency (MHz)') # Compute errors of Q and U sigq = np.std(Q[:,lowbin:hibin], axis=1) / np.sqrt(hibin-lowbin) sigu = np.std(U[:,lowbin:hibin], axis=1) / np.sqrt(hibin-lowbin) #sigq = np.std(Q[:,:], axis=1) #sigu = np.std(U[:,:], axis=1) pylab.plot([lowphase,lowphase], [freq_lo, freq_hi], 'r--') pylab.plot([hiphase,hiphase], [freq_lo, freq_hi], 'r--') freqs_hz = freqs * 1e6 # Select phase range lowbin = int(lowphase * arch.get_nbin()) hibin = int(hiphase * arch.get_nbin()) dat = Q + 1j * U pv = dat[:,lowbin:hibin].mean(1) # Select phase range and average #rhos = np.arange(-90000, 90000, 10) rhos = np.arange(-2000, 2000, 1) res = np.absolute(getL(rhos, pv)) # Plot I f(RM) pylab.subplot(324) pylab.plot(rhos, res, 'b-') pylab.xlabel('RM') pylab.ylabel('I') # Plot Q pylab.subplot(325) pylab.errorbar(freqs, np.real(pv), yerr=sigq, ls='None') pylab.plot(freqs, np.real(pv), 'bo') pylab.xlabel('Frequency (MHz)') pylab.ylabel('Q') # Plot U pylab.subplot(326) pylab.errorbar(freqs, np.imag(pv), yerr=sigu, ls='None') pylab.plot(freqs, np.imag(pv), 'bo') pylab.xlabel('Frequency (MHz)') pylab.ylabel('U') # Should get the initial RM if opts.rm: initial_RM = -opts.rm else: initial_RM = -rhos[np.argmax(res)] print "Will use initial RM", initial_RM initial_L = getL(np.array([initial_RM]), pv) / (hibin-lowbin) if not opts.nofit: # Minuit part gMinuit = TMinuit(5) #gMinuit.SetErrorDef(1) # 1-Sigma Error gMinuit.SetErrorDef(4) # 2-Sigma Error gMinuit.SetFCN(fcn) arglist = arr('d', 2*[0.01]) ierflg = Long(0) #arglist[0] = 1 #gMinuit.mnexcm("SET ERR", arglist ,1,ierflg) # Set initial parameter values for fit vstart = arr( 'd', (np.real(initial_L), np.imag(initial_L), initial_RM) ) #vstart = arr( 'd', (2*np.real(initial_L), 2*np.imag(initial_L), initial_RM) ) # Set step size for fit step = arr( 'd', (0.001, 0.001, 0.001) ) # Define the parameters for the fit gMinuit.mnparm(0, "Qf", vstart[0], step[0], 0,0,ierflg) gMinuit.mnparm(1, "Uf", vstart[1], step[1], 0,0,ierflg) gMinuit.mnparm(2, "RM", vstart[2], step[2], 0,0,ierflg) gMinuit.mnparm(3, "a", 0.0, step[2], 0,0,ierflg) gMinuit.mnparm(4, "b", 0.0, step[2], 0,0,ierflg) #gMinuit.FixParameter(2) if not opts.baseline: gMinuit.FixParameter(3) gMinuit.FixParameter(4) arglist[0] = 6000 # Number of calls to FCN before giving up. arglist[1] = 0.1 # Tolerance gMinuit.mnexcm("MIGRAD", arglist ,2,ierflg) amin, edm, errdef = Double(0.18), Double(0.19), Double(1.0) nvpar, nparx, icstat = Long(1), Long(2), Long(3) gMinuit.mnstat(amin,edm,errdef,nvpar,nparx,icstat); gMinuit.mnmnos(); gMinuit.mnprin(3,amin); finalQ, finalQ_err = Double(0), Double(0) finalU, finalU_err = Double(0), Double(0) finalRM, finalRM_err = Double(0), Double(0) A, A_err = Double(0), Double(0) B, B_err = Double(0), Double(0) print "\n\n" gMinuit.GetParameter(0, finalQ, finalQ_err) gMinuit.GetParameter(1, finalU, finalU_err) gMinuit.GetParameter(2, finalRM, finalRM_err) gMinuit.GetParameter(3, A, A_err) gMinuit.GetParameter(4, B, B_err) finalRM *= -1. print finalQ, finalU line1 = "Final RM: %.1f"%(finalRM) + "(+/-) %.1f "%(finalRM_err) + " (2-sig) " line2 = "Chi**2r = %.2f"%(get_chi2((finalQ,finalU,-finalRM,A,B)) / ( 2*pv.size - 3 - 1 -2)) print line1 print line2 f.text( .5, 0.92, line1 + line2, horizontalalignment='center') print MJD, np.mean(freqs), finalRM, finalRM_err, get_chi2((finalQ,finalU,finalRM,A,B)), 2*pv.count() # Plot best fit model finalRM *= -1. pylab.subplot(325) pylab.plot(freqs, np.real( -A+getPV(finalQ+1j*finalU, finalRM) )) pylab.subplot(326) pylab.plot(freqs, np.imag( -B+getPV(finalQ+1j*finalU, finalRM) )) pylab.show()
def fit(self): numberOfParameters = len(self.samples) gMinuit = TMinuit(numberOfParameters) if self.method == 'logLikelihood': # set function for minimisation gMinuit.SetFCN(self.logLikelihood) gMinuit.SetMaxIterations(1000000000000) # set Minuit print level # printlevel = -1 quiet (also suppress all warnings) # = 0 normal # = 1 verbose # = 2 additional output giving intermediate results. # = 3 maximum output, showing progress of minimizations. gMinuit.SetPrintLevel(-1) # Error definition: 1 for chi-squared, 0.5 for negative log likelihood # SETERRDEF<up>: Sets the value of UP (default value= 1.), defining parameter errors. # Minuit defines parameter errors as the change in parameter value required to change the function value by UP. # Normally, for chisquared fits UP=1, and for negative log likelihood, UP=0.5. gMinuit.SetErrorDef(0.5) # error flag for functions passed as reference.set to as 0 is no error errorFlag = Long(2) N_min = 0 N_max = self.fit_data_collection.max_n_data() * 2 param_index = 0 # MNPARM # Implements one parameter definition: # mnparm(k, cnamj, uk, wk, a, b, ierflg) # K (external) parameter number # CNAMK parameter name # UK starting value # WK starting step size or uncertainty # A, B lower and upper physical parameter limits # and sets up (updates) the parameter lists. # Output: IERFLG =0 if no problems # >0 if MNPARM unable to implement definition for sample in self.samples: # all samples but data if self.n_distributions > 1: gMinuit.mnparm( param_index, sample, self.normalisation[self.distributions[0]][sample], 10.0, N_min, N_max, errorFlag) else: gMinuit.mnparm(param_index, sample, self.normalisation[sample], 10.0, N_min, N_max, errorFlag) param_index += 1 arglist = array('d', 10 * [0.]) # minimisation strategy: 1 standard, 2 try to improve minimum (a bit slower) arglist[0] = 2 # minimisation itself # SET STRategy<level>: Sets the strategy to be used in calculating first and second derivatives and in certain minimization methods. # In general, low values of <level> mean fewer function calls and high values mean more reliable minimization. # Currently allowed values are 0, 1 (default), and 2. gMinuit.mnexcm("SET STR", arglist, 1, errorFlag) gMinuit.Migrad() gMinuit.mnscan( ) # class for minimization using a scan method to find the minimum; allows for user interaction: set/change parameters, do minimization, change parameters, re-do minimization etc. gMinuit.mnmatu(1) # prints correlation matrix (always needed) self.module = gMinuit self.performedFit = True if not self.module: raise Exception( 'No fit results available. Please run fit method first') results = {} param_index = 0 for sample in self.samples: temp_par = Double(0) temp_err = Double(0) self.module.GetParameter(param_index, temp_par, temp_err) if (math.isnan(temp_err)): self.logger.warning( 'Template fit error is NAN, setting to sqrt(N).') temp_err = math.sqrt(temp_par) # gMinuit.Command("SCAn %i %i %i %i" % ( param_index, 100, N_min, N_total ) ); # scan = gMinuit.GetPlot() # results[sample] = ( temp_par, temp_err, scan ) results[sample] = (temp_par, temp_err) param_index += 1 # # gMinuit.Command("CONtour 1 2 3 50") # gMinuit.SetErrorDef(1) # results['contour'] = [gMinuit.Contour(100, 0, 1)] # gMinuit.SetErrorDef(4) # results['contour'].append(gMinuit.Contour(100, 0, 1)) self.results = results
def test1MinuitFit(self): """Test minuit callback and fit""" # setup minuit and callback gMinuit = TMinuit(5) gMinuit.SetPrintLevel(-1) # quiet gMinuit.SetGraphicsMode(ROOT.kFALSE) gMinuit.SetFCN(fcn) arglist = array('d', 10 * [0.]) if not exp_pyroot and sys.hexversion < 0x3000000: ierflg = ROOT.Long() else: ierflg = ctypes.c_int() arglist[0] = 1 gMinuit.mnexcm("SET ERR", arglist, 1, ierflg) # set starting values and step sizes for parameters vstart = array('d', [3, 1, 0.1, 0.01]) step = array('d', [0.1, 0.1, 0.01, 0.001]) gMinuit.mnparm(0, "a1", vstart[0], step[0], 0, 0, ierflg) gMinuit.mnparm(1, "a2", vstart[1], step[1], 0, 0, ierflg) gMinuit.mnparm(2, "a3", vstart[2], step[2], 0, 0, ierflg) gMinuit.mnparm(3, "a4", vstart[3], step[3], 0, 0, ierflg) # now ready for minimization step arglist[0] = 500 arglist[1] = 1. gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) # verify results if exp_pyroot: Double = ctypes.c_double else: Double = ROOT.Double amin, edm, errdef = Double(), Double(), Double() if not exp_pyroot and sys.hexversion < 0x3000000: Long = ROOT.Long nvpar, nparx, icstat = Long(), Long(), Long() else: nvpar, nparx, icstat = ctypes.c_int(), ctypes.c_int( ), ctypes.c_int() gMinuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat) # gMinuit.mnprin( 3, amin ) if exp_pyroot or sys.hexversion >= 0x3000000: nvpar, nparx, icstat = map(lambda x: x.value, [nvpar, nparx, icstat]) self.assertEqual(nvpar, 4) self.assertEqual(nparx, 4) # success means that full covariance matrix is available (icstat==3) self.assertEqual(icstat, 3) # check results (somewhat debatable ... ) par, err = Double(), Double() if exp_pyroot: # ctypes.c_double requires the explicit retrieval of the inner value gMinuit.GetParameter(0, par, err) self.assertEqual(round(par.value - 2.15, 2), 0.) self.assertEqual(round(err.value - 0.10, 2), 0.) gMinuit.GetParameter(1, par, err) self.assertEqual(round(par.value - 0.81, 2), 0.) self.assertEqual(round(err.value - 0.25, 2), 0.) gMinuit.GetParameter(2, par, err) self.assertEqual(round(par.value - 0.17, 2), 0.) self.assertEqual(round(err.value - 0.40, 2), 0.) gMinuit.GetParameter(3, par, err) self.assertEqual(round(par.value - 0.10, 2), 0.) self.assertEqual(round(err.value - 0.16, 2), 0.) else: gMinuit.GetParameter(0, par, err) self.assertEqual(round(par - 2.15, 2), 0.) self.assertEqual(round(err - 0.10, 2), 0.) gMinuit.GetParameter(1, par, err) self.assertEqual(round(par - 0.81, 2), 0.) self.assertEqual(round(err - 0.25, 2), 0.) gMinuit.GetParameter(2, par, err) self.assertEqual(round(par - 0.17, 2), 0.) self.assertEqual(round(err - 0.40, 2), 0.) gMinuit.GetParameter(3, par, err) self.assertEqual(round(par - 0.10, 2), 0.) self.assertEqual(round(err - 0.16, 2), 0.)
def ifit(): gMinuit = TMinuit(npars) gMinuit.SetFCN(fcn) arglist = array('d', 10*[0.]) ierflg = Long(1982) arglist[0] = 1 # 1 for chisquared fits, 0.5 for negative log likelihood gMinuit.mnexcm("SET ERR", arglist, 1, ierflg) arglist[0] = 0.00001 # floating point precision gMinuit.mnexcm("SET EPS", arglist, 1, ierflg) arglist[0] = 2 # 1 mean fewer function calls, 2 mean more reliable minimization gMinuit.mnexcm("SET STR", arglist, 1, ierflg) # Set starting values and step sizes for parameters vstart = array('d', npars*[1.0]) #vstart = array('d', [0.909, 1.341, 0.942, 1.300, 1.003]) step = array('d', npars*[0.001]) gMinuit.mnparm(0, "ZjLF", vstart[0], step[0], 0, 0, ierflg) gMinuit.mnparm(1, "ZjHF", vstart[1], step[1], 0, 0, ierflg) gMinuit.mnparm(2, "WjLF", vstart[2], step[2], 0, 0, ierflg) gMinuit.mnparm(3, "WjHF", vstart[3], step[3], 1.3, 2.0, ierflg) gMinuit.mnparm(4, "TT" , vstart[4], step[4], 0, 0, ierflg) # Fix parameter #arglist[0] = 1 #gMinuit.mnexcm("FIX", arglist, 1, ierflg) #arglist[0] = 2 #gMinuit.mnexcm("FIX", arglist, 1, ierflg) #arglist[0] = 3 #gMinuit.mnexcm("FIX", arglist, 1, ierflg) #arglist[0] = 4 #gMinuit.mnexcm("FIX", arglist, 1, ierflg) #arglist[0] = 5 #gMinuit.mnexcm("FIX", arglist, 1, ierflg) # Scan for best parameter values #arglist[0] = 1 #gMinuit.mnexcm("SCAN", arglist, 0, ierflg) # Now ready for minimization step arglist[0] = 500 arglist[1] = 1. # default: 0.1 gMinuit.mnexcm("SIMPLEX", arglist, 2, ierflg) #gMinuit.mnexcm("MIGRAD", arglist, 2, ierflg) # Search for additional distinct local minima #arglist[0] = 10000 #gMinuit.mnexcm("IMP", arglist, 1, ierflg) # Do error analysis gMinuit.mnexcm("HESSE", arglist, 0, ierflg) #arglist[0] = 10000 #gMinuit.mnexcm("MINOS", arglist, 1, ierflg) # Print results amin, edm, errdef = Double(0.18), Double(0.19), Double(0.20) # passing doubles by reference is allowed, as long as on the python side doubles are unique (init them with different values, for example). nvpar, nparx, icstat = Long(1986), Long(1987), Long(1988) gMinuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat) gMinuit.mnprin(5, amin) ndf = 0 for i in xrange(len(data[0])): # loop over all bins mc_ = [m[i] for m in mc] if sum(mc_) < tolerance: continue if data[0][i] < tolerance: continue ndf += 1 print "chi^2 = ", amin, ", NDF = ", ndf, ", chi^2/NDF = ", amin/ndf, ", prob = ", TMath.Prob(amin,ndf) print "amin: ", amin, ", edm: ", edm, ", errdef: ", errdef, ", nvpar: ", nvpar, ", nparx: ", nparx, ", icstat: ", icstat print "c0: ", vstart[0], ", c1: ", vstart[1], ", c2: ", vstart[2], ", c3: ", vstart[3], ", c4: ", vstart[4]
class MinimizerROOTTMinuit(MinimizerBase): def __init__(self, parameter_names, parameter_values, parameter_errors, function_to_minimize, tolerance=1e-9, errordef=MinimizerBase.ERRORDEF_CHI2, strategy=1): self._strategy = strategy self._par_bounds = np.array([None] * len(parameter_names)) self._par_fixed = np.array([False] * len(parameter_names)) self.reset() # sets self.__gMinuit and caches to None super(MinimizerROOTTMinuit, self).__init__(parameter_names=parameter_names, parameter_values=parameter_values, parameter_errors=parameter_errors, function_to_minimize=function_to_minimize, tolerance=tolerance, errordef=errordef) # -- private methods def _save_state(self): if self._par_val is None: self._save_state_dict["par_val"] = self._par_val else: self._save_state_dict["par_val"] = np.array(self._par_val) if self._par_err is None: self._save_state_dict["par_err"] = self._par_err else: self._save_state_dict["par_err"] = np.array(self._par_err) self._save_state_dict['par_fixed'] = np.array(self._par_fixed) self._save_state_dict['gMinuit'] = self.__gMinuit super(MinimizerROOTTMinuit, self)._save_state() def _load_state(self): self.reset() self._par_val = self._save_state_dict["par_val"] if self._par_val is not None: self._par_val = np.array(self._par_val) self._par_err = self._save_state_dict["par_err"] if self._par_err is not None: self._par_err = np.array(self._par_err) self._par_fixed = np.array(self._save_state_dict['par_fixed']) self.__gMinuit = self._save_state_dict['gMinuit'] # call the function to propagate the changes to the nexus: self._func_handle(*self.parameter_values) super(MinimizerROOTTMinuit, self)._load_state() def _recreate_gMinuit(self): self.__gMinuit = TMinuit(self.num_pars) self.__gMinuit.SetPrintLevel(-1) self.__gMinuit.mncomd("SET STRATEGY {}".format(self._strategy), ctypes.c_int(0)) self.__gMinuit.SetFCN(self._minuit_fcn) self.__gMinuit.SetErrorDef(self._err_def) # set gMinuit parameters error_code = ctypes.c_int(0) for _pid, (_pn, _pv, _pe) in enumerate( zip(self._par_names, self._par_val, self._par_err)): self.__gMinuit.mnparm(_pid, _pn, _pv, 0.1 * _pe, 0, 0, error_code) err_code = ctypes.c_int(0) # set fixed parameters for _par_id, _pf in enumerate(self._par_fixed): if _pf: self.__gMinuit.mnfixp(_par_id, err_code) # set parameter limits for _par_id, _pb in enumerate(self._par_bounds): if _pb is not None: _lo_lim, _up_lim = _pb self.__gMinuit.mnexcm( "SET LIM", arr('d', [_par_id + 1, _lo_lim, _up_lim]), 3, error_code) def _get_gMinuit(self): if self.__gMinuit is None: self._recreate_gMinuit() return self.__gMinuit def _migrad(self, max_calls=6000): # need to set the FCN explicitly before every call self._get_gMinuit().SetFCN(self._minuit_fcn) error_code = ctypes.c_int(0) self._get_gMinuit().mnexcm("MIGRAD", arr('d', [max_calls, self.tolerance]), 2, error_code) def _minuit_fcn(self, number_of_parameters, derivatives, f, parameters, internal_flag): """ This is actually a function called in *ROOT* and acting as a C wrapper for our `FCN`, which is implemented in Python. This function is called by `Minuit` several times during a fitters. It doesn't return anything but modifies one of its arguments (*f*). This is *ugly*, but it's how *ROOT*'s ``TMinuit`` works. Its argument structure is fixed and determined by `Minuit`: **number_of_parameters** : int The number of parameters of the current fitters **derivatives** : C array If the user chooses to calculate the first derivative of the function inside the `FCN`, this value should be written here. This interface to `Minuit` ignores this derivative, however, so calculating this inside the `FCN` has no effect (yet). **f** : C array The desired function value is in f[0] after execution. **parameters** : C array A C array of parameters. Is cast to a Python list **internal_flag** : int A flag allowing for different behaviour of the function. Can be any integer from 1 (initial run) to 4(normal run). See `Minuit`'s specification. """ # Retrieve the parameters from the C side of ROOT and # store them in a Python list -- resource-intensive # for many calls, but can't be improved (yet?) parameter_list = np.frombuffer(parameters, dtype=float, count=self.num_pars) # call the Python implementation of FCN. f[0] = self._func_wrapper(*parameter_list) def _calculate_asymmetric_parameter_errors(self): self._get_gMinuit().mnmnos() _asymm_par_errs = np.zeros(shape=(self.num_pars, 2)) for _n in range(self.num_pars): _number = Long(_n) _eplus = ctypes.c_double(0) _eminus = ctypes.c_double(0) _eparab = ctypes.c_double(0) _gcc = ctypes.c_double(0) self._get_gMinuit().mnerrs(_number, _eplus, _eminus, _eparab, _gcc) _asymm_par_errs[_n, 0] = _eminus.value _asymm_par_errs[_n, 1] = _eplus.value self.minimize() return _asymm_par_errs def _get_fit_info(self, info): '''Retrieves other info from `Minuit`. **info** : string Information about the fit to retrieve. This can be any of the following: - ``'fcn'``: `FCN` value at minimum, - ``'edm'``: estimated distance to minimum - ``'err_def'``: `Minuit` error matrix status code - ``'status_code'``: `Minuit` general status code ''' # declare vars in which to retrieve other info fcn_at_min = ctypes.c_double(0) edm = ctypes.c_double(0) err_def = ctypes.c_double(0) n_var_param = ctypes.c_int(0) n_tot_param = ctypes.c_int(0) status_code = ctypes.c_int(0) # Tell TMinuit to update the variables declared above self.__gMinuit.mnstat(fcn_at_min, edm, err_def, n_var_param, n_tot_param, status_code) if info == 'fcn': return fcn_at_min.value elif info == 'edm': return edm.value elif info == 'err_def': return err_def.value elif info == 'status_code': return status_code.value else: raise ValueError("Unknown fit info: %s" % info) # -- public properties @property def hessian(self): if not self.did_fit: return None if self._hessian is None: _submat = self._remove_zeroes_for_fixed(self.cov_mat) _submat_inv = 2.0 * self.errordef * np.linalg.inv(_submat) self._hessian = self._fill_in_zeroes_for_fixed(_submat_inv) return self._hessian.copy() @property def cov_mat(self): if not self.did_fit: return None if self._par_cov_mat is None: _n_pars_total = self.num_pars _tmp_mat_array = arr('d', [0.0] * (_n_pars_total**2)) # get parameter covariance matrix from TMinuit self._get_gMinuit().mnemat(_tmp_mat_array, _n_pars_total) # reshape into 2D array _sub_cov_mat = np.asarray(np.reshape( _tmp_mat_array, (_n_pars_total, _n_pars_total)), dtype=np.float) _num_pars_free = np.sum(np.invert(self._par_fixed)) _sub_cov_mat = _sub_cov_mat[:_num_pars_free, :_num_pars_free] self._par_cov_mat = self._fill_in_zeroes_for_fixed(_sub_cov_mat) return self._par_cov_mat.copy() @property def hessian_inv(self): if not self.did_fit: return None if self._hessian_inv is None: self._hessian_inv = self.cov_mat / (2.0 * self.errordef) return self._hessian_inv.copy() @property def parameter_values(self): return self._par_val.copy() @parameter_values.setter def parameter_values(self, new_values): self._par_val = np.array(new_values) self.reset() @property def parameter_errors(self): return self._par_err.copy() @parameter_errors.setter def parameter_errors(self, new_errors): _err_array = np.array(new_errors) if not np.all(_err_array > 0): raise ValueError("All parameter errors must be > 0! Received: %s" % new_errors) self._par_err = _err_array self.reset() # -- private "properties" # -- public methods def reset(self): super(MinimizerROOTTMinuit, self).reset() self.__gMinuit = None def set(self, parameter_name, parameter_value): if parameter_name not in self._par_names: raise ValueError("No parameter named '%s'!" % (parameter_name, )) _par_id = self.parameter_names.index(parameter_name) self._par_val[_par_id] = parameter_value self.reset() def fix(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_fixed[_par_id]: return # par is already fixed self._par_fixed[_par_id] = True if self.__gMinuit is not None: # also update Minuit instance err_code = ctypes.c_int(0) self.__gMinuit.mnfixp(_par_id, err_code) # self.__gMinuit.mnexcm("FIX", # arr('d', [_par_id+1]), 1, error_code) self._invalidate_cache() def is_fixed(self, parameter_name): _par_id = self.parameter_names.index(parameter_name) return self._par_fixed[_par_id] def release(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if not self._par_fixed[_par_id]: return # par is already released self._par_fixed[_par_id] = False if self.__gMinuit is not None: # also update Minuit instance self.__gMinuit.mnfree(-_par_id - 1) # self.__gMinuit.mnexcm("RELEASE", # arr('d', [_par_id+1]), 1, error_code) self._invalidate_cache() def limit(self, parameter_name, parameter_bounds): assert len(parameter_bounds) == 2 if parameter_bounds[0] is None or parameter_bounds[1] is None: raise MinimizerROOTTMinuitException( "Cannot define one-sided parameter limits when using the ROOT TMinuit Minimizer." ) # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_bounds[_par_id] == parameter_bounds: return # same limits already set if self._par_val[_par_id] < parameter_bounds[0]: self.set(parameter_name, parameter_bounds[0]) elif self._par_val[_par_id] > parameter_bounds[1]: self.set(parameter_name, parameter_bounds[1]) self._par_bounds[_par_id] = parameter_bounds if self.__gMinuit is not None: _lo_lim, _up_lim = self._par_bounds[_par_id] # also update Minuit instance error_code = ctypes.c_int(0) self.__gMinuit.mnexcm("SET LIM", arr('d', [_par_id + 1, _lo_lim, _up_lim]), 3, error_code) self._did_fit = False self._invalidate_cache() def unlimit(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_bounds[_par_id] is None: return # parameter is already unlimited self._par_bounds[_par_id] = None if self.__gMinuit is not None: # also update Minuit instance error_code = ctypes.c_int(0) self.__gMinuit.mnexcm("SET LIM", arr('d', [_par_id + 1]), 1, error_code) self._did_fit = False self._invalidate_cache() def minimize(self, max_calls=6000): if np.all(self._par_fixed): raise MinimizerROOTTMinuitException( "Cannot perform a fit if all parameters are fixed!") self._migrad(max_calls=max_calls) # retrieve fitters parameters self._par_val = np.zeros(self.num_pars) self._par_err = np.zeros(self.num_pars) _pv, _pe = ctypes.c_double(0), ctypes.c_double(0) for _par_id in six.moves.range(0, self.num_pars): self.__gMinuit.GetParameter(_par_id, _pv, _pe) # retrieve fit result self._par_val[_par_id] = _pv.value self._par_err[_par_id] = _pe.value self._did_fit = True def contour(self, parameter_name_1, parameter_name_2, sigma=1.0, **minimizer_contour_kwargs): if not self.did_fit: raise MinimizerROOTTMinuitException( "Need to perform a fit before calling contour()!") _numpoints = minimizer_contour_kwargs.pop("numpoints", 100) if minimizer_contour_kwargs: raise MinimizerROOTTMinuitException( "Unknown parameters: {}".format(minimizer_contour_kwargs)) _id_1 = self.parameter_names.index(parameter_name_1) _id_2 = self.parameter_names.index(parameter_name_2) self.__gMinuit.SetErrorDef(sigma**2) _t_graph = self.__gMinuit.Contour(_numpoints, _id_1, _id_2) self.__gMinuit.SetErrorDef(self._err_def) _x_buffer, _y_buffer = _t_graph.GetX(), _t_graph.GetY() _N = _t_graph.GetN() _x = np.frombuffer(_x_buffer, dtype=float, count=_N) _y = np.frombuffer(_y_buffer, dtype=float, count=_N) self._func_handle(*self.parameter_values) return ContourFactory.create_xy_contour((_x, _y), sigma) def profile(self, parameter_name, bins=21, bound=2, args=None, subtract_min=False): if not self.did_fit: raise MinimizerROOTTMinuitException( "Need to perform a fit before calling profile()!") MAX_ITERATIONS = 6000 _error_code = ctypes.c_int(0) _minuit_id = Long(self.parameter_names.index(parameter_name) + 1) _par_min = ctypes.c_double(0) _par_err = ctypes.c_double(0) self.__gMinuit.GetParameter(_minuit_id - 1, _par_min, _par_err) _par_min = _par_min.value _par_err = _par_err.value _x = np.linspace(start=_par_min - bound * _par_err, stop=_par_min + bound * _par_err, num=bins, endpoint=True) self.__gMinuit.mnexcm("FIX", arr('d', [_minuit_id]), 1, _error_code) _y = np.zeros(bins) for i in range(bins): self.__gMinuit.mnexcm("SET PAR", arr('d', [_minuit_id, Double(_x[i])]), 2, _error_code) self.__gMinuit.mnexcm("MIGRAD", arr('d', [MAX_ITERATIONS, self.tolerance]), 2, _error_code) _y[i] = self._get_fit_info("fcn") self.__gMinuit.mnexcm("RELEASE", arr('d', [_minuit_id]), 1, _error_code) self._migrad() self.__gMinuit.mnexcm("SET PAR", arr('d', [_minuit_id, Double(_par_min)]), 2, _error_code) if subtract_min: _y -= self.function_value return np.asarray((_x, _y))
def Minuit(fun, x0, args=(), bounds=None, verbose=0, step_size=0.01, n_iterations=500, delta_one_sigma=0.5, **options): """ Interface to ROOT Minuit @param bounds list of tuple with limits for each variable @param verbose -1 (silent) 0 (normal) 1 (verbose) @param step_size float or Sequence @param n_iterations maximum number of iterations @param delta_one_sigma delta in loss function which corresponds to one sigma errors """ from ROOT import TMinuit, Double, Long n_parameters = len(x0) def to_minimize(npar, deriv, f, apar, iflag): f[0] = fun(np.array([apar[i] for i in range(n_parameters)]), *args) minuit = TMinuit(n_parameters) minuit.SetFCN(to_minimize) minuit.SetPrintLevel(verbose) ierflg = np.array(0, dtype=np.int32) minuit.mnexcm("SET ERR", np.array([delta_one_sigma]), 1, ierflg) if ierflg > 0: raise RuntimeError("Error during \"SET ERR\" call") for i in range(n_parameters): low, high = 0.0, 0.0 # Zero seems to mean no limit if bounds is not None: low, high = bounds[i] minuit.mnparm( i, 'Parameter_{}'.format(i), x0[i], step_size[i] if isinstance( step_size, collections.Sequence) else step_size, low, high, ierflg) if ierflg > 0: raise RuntimeError( "Error during \"nmparm\" call for parmameter {}".format(i)) minuit.mnexcm("MIGRAD", np.array([n_iterations, 0.01], dtype=np.float64), 2, ierflg) if ierflg > 0: raise RuntimeError("Error during \"MIGRAD\" call") xbest = np.zeros(n_parameters) xbest_error = np.zeros(n_parameters) p, pe = Double(0), Double(0) for i in range(n_parameters): minuit.GetParameter(i, p, pe) xbest[i] = p xbest_error[i] = pe buf = array.array('d', [0.] * (n_parameters**2)) minuit.mnemat(buf, n_parameters) # retrieve error matrix hessian = np.array(buf).reshape(n_parameters, n_parameters) amin = Double(0.) # value of fcn at minimum edm = Double(0.) # estimated distance to mimimum errdef = Double(0.) # delta_fcn used to define 1 sigma errors nvpar = np.array(0, dtype=np.int32) # number of variable parameters nparx = np.array(0, dtype=np.int32) # total number of parameters icstat = np.array( 0, dtype=np.int32 ) # status of error matrix: 3=accurate 2=forced pos. def 1= approximative 0=not calculated minuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat) return scipy.optimize.OptimizeResult(x=xbest, fun=amin, hessian=hessian, success=(ierflg == 0), status=ierflg)
class MinimizerROOTTMinuit(MinimizerBase): def __init__(self, parameter_names, parameter_values, parameter_errors, function_to_minimize, strategy = 1): self._par_names = parameter_names self._strategy = strategy self._func_handle = function_to_minimize self._err_def = 1.0 self._tol = 0.001 # initialize the minimizer parameter specification self._minimizer_param_dict = {} assert len(parameter_names) == len(parameter_values) == len(parameter_errors) self._par_val = [] self._par_err = [] self._par_fixed_mask = [] self._par_limits = [] for _pn, _pv, _pe in zip(parameter_names, parameter_values, parameter_errors): self._par_val.append(_pv) self._par_err.append(_pe) self._par_fixed_mask.append(False) self._par_limits.append(None) # TODO: limits/fixed parameters self.__gMinuit = None # cache for calculations self._hessian = None self._hessian_inv = None self._fval = None self._par_cov_mat = None self._par_cor_mat = None self._par_asymm_err = None self._fmin_struct = None self._pars_contour = None self._min_result_stale = True self._printed_inf_cost_warning = False # -- private methods # def _invalidate_cache(self): # self._par_val = None # self._par_err = None # self._hessian = None # self._hessian_inv = None # self._fval = None # self._par_cov_mat = None # self._par_cor_mat = None # self._par_asymm_err_dn = None # self._par_asymm_err_up = None # self._fmin_struct = None # self._pars_contour = None def _recreate_gMinuit(self): self.__gMinuit = TMinuit(self.n_pars) self.__gMinuit.SetPrintLevel(-1) self.__gMinuit.mncomd("SET STRATEGY {}".format(self._strategy), Long(0)) self.__gMinuit.SetFCN(self._minuit_fcn) self.__gMinuit.SetErrorDef(self._err_def) # set gMinuit parameters error_code = Long(0) for _pid, (_pn, _pv, _pe) in enumerate(zip(self._par_names, self._par_val, self._par_err)): self.__gMinuit.mnparm(_pid, _pn, _pv, 0.1 * _pe, 0, 0, error_code) err_code = Long(0) # set fixed parameters for _par_id, _pf in enumerate(self._par_fixed_mask): if _pf: self.__gMinuit.mnfixp(_par_id, err_code) # set parameter limits for _par_id, _pl in enumerate(self._par_limits): if _pl is not None: _lo_lim, _up_lim = _pl self.__gMinuit.mnexcm("SET LIM", arr('d', [_par_id + 1, _lo_lim, _up_lim]), 3, error_code) def _get_gMinuit(self): if self.__gMinuit is None: self._recreate_gMinuit() return self.__gMinuit def _migrad(self, max_calls=6000): # need to set the FCN explicitly before every call self._get_gMinuit().SetFCN(self._minuit_fcn) error_code = Long(0) self._get_gMinuit().mnexcm("MIGRAD", arr('d', [max_calls, self.tolerance]), 2, error_code) def _hesse(self, max_calls=6000): # need to set the FCN explicitly before every call self._get_gMinuit().SetFCN(self._minuit_fcn) error_code = Long(0) self._get_gMinuit().mnexcm("HESSE", arr('d', [max_calls]), 1, error_code) def _minuit_fcn(self, number_of_parameters, derivatives, f, parameters, internal_flag): """ This is actually a function called in *ROOT* and acting as a C wrapper for our `FCN`, which is implemented in Python. This function is called by `Minuit` several times during a fitters. It doesn't return anything but modifies one of its arguments (*f*). This is *ugly*, but it's how *ROOT*'s ``TMinuit`` works. Its argument structure is fixed and determined by `Minuit`: **number_of_parameters** : int The number of parameters of the current fitters **derivatives** : C array If the user chooses to calculate the first derivative of the function inside the `FCN`, this value should be written here. This interface to `Minuit` ignores this derivative, however, so calculating this inside the `FCN` has no effect (yet). **f** : C array The desired function value is in f[0] after execution. **parameters** : C array A C array of parameters. Is cast to a Python list **internal_flag** : int A flag allowing for different behaviour of the function. Can be any integer from 1 (initial run) to 4(normal run). See `Minuit`'s specification. """ # Retrieve the parameters from the C side of ROOT and # store them in a Python list -- resource-intensive # for many calls, but can't be improved (yet?) parameter_list = np.frombuffer(parameters, dtype=float, count=self.n_pars) # call the Python implementation of FCN. f[0] = self._func_wrapper(*parameter_list) def _insert_zeros_for_fixed(self, submatrix): """ Takes the partial error matrix (submatrix) and adds rows and columns with 0.0 where the fixed parameters should go. """ _mat = submatrix # reduce the matrix before inserting zeros _n_pars_free = self.n_pars_free _mat = _mat[0:_n_pars_free,0:_n_pars_free] _fparam_ids = [_par_id for _par_id, _p in enumerate(self._par_fixed_mask) if _p] for _id in _fparam_ids: _mat = np.insert(np.insert(_mat, _id, 0., axis=0), _id, 0., axis=1) return _mat # -- public properties def get_fit_info(self, info): '''Retrieves other info from `Minuit`. **info** : string Information about the fit to retrieve. This can be any of the following: - ``'fcn'``: `FCN` value at minimum, - ``'edm'``: estimated distance to minimum - ``'err_def'``: `Minuit` error matrix status code - ``'status_code'``: `Minuit` general status code ''' # declare vars in which to retrieve other info fcn_at_min = Double(0) edm = Double(0) err_def = Double(0) n_var_param = Long(0) n_tot_param = Long(0) status_code = Long(0) # Tell TMinuit to update the variables declared above self.__gMinuit.mnstat(fcn_at_min, edm, err_def, n_var_param, n_tot_param, status_code) if info == 'fcn': return fcn_at_min elif info == 'edm': return edm elif info == 'err_def': return err_def elif info == 'status_code': try: return D_MATRIX_ERROR[status_code] except: return status_code @property def n_pars(self): return len(self.parameter_names) @property def n_pars_free(self): return len([_p for _p in self._par_fixed_mask if not _p]) @property def errordef(self): return self._err_def @errordef.setter def errordef(self, err_def): assert err_def > 0 self._err_def = err_def if self.__gMinuit is not None: self.__gMinuit.set_errordef(err_def) self._min_result_stale = True @property def tolerance(self): return self._tol @tolerance.setter def tolerance(self, tolerance): assert tolerance > 0 self._tol = tolerance self._min_result_stale = True @property def hessian(self): # TODO: cache this return 2.0 * self.errordef * np.linalg.inv(self.cov_mat) @property def cov_mat(self): if self._min_result_stale: raise MinimizerROOTTMinuitException("Cannot get cov_mat: Minimizer result is outdated.") if self._par_cov_mat is None: _n_pars_total = self.n_pars _n_pars_free = self.n_pars_free _tmp_mat_array = arr('d', [0.0]*(_n_pars_total**2)) # get parameter covariance matrix from TMinuit self.__gMinuit.mnemat(_tmp_mat_array, _n_pars_total) # reshape into 2D array _sub_cov_mat = np.asarray( np.reshape( _tmp_mat_array, (_n_pars_total, _n_pars_total) ) ) self._par_cov_mat = self._insert_zeros_for_fixed(_sub_cov_mat) return self._par_cov_mat @property def cor_mat(self): if self._min_result_stale: raise MinimizerROOTTMinuitException("Cannot get cor_mat: Minimizer result is outdated.") if self._par_cor_mat is None: _cov_mat = self.cov_mat # TODO: use CovMat object! # Note: for zeros on cov_mat diagonals (which occur for fixed parameters) -> overwrite with 1.0 _sqrt_diag = np.array([_err if _err>0 else 1.0 for _err in np.sqrt(np.diag(_cov_mat))]) self._par_cor_mat = np.asarray(_cov_mat) / np.outer(_sqrt_diag, _sqrt_diag) return self._par_cor_mat @property def hessian_inv(self): return self.cov_mat / 2.0 / self.errordef @property def parameter_values(self): return self._par_val @property def parameter_errors(self): return self._par_err @property def parameter_names(self): return self._par_names # -- private "properties" # -- public methods def fix(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_fixed_mask[_par_id]: return # par is already fixed self._par_fixed_mask[_par_id] = True if self.__gMinuit is not None: # also update Minuit instance err_code = Long(0) self.__gMinuit.mnfixp(_par_id, err_code) # self.__gMinuit.mnexcm("FIX", # arr('d', [_par_id+1]), 1, error_code) self._min_result_stale = True def fix_several(self, parameter_names): for _pn in parameter_names: self.fix(_pn) def release(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if not self._par_fixed_mask[_par_id]: return # par is already released self._par_fixed_mask[_par_id] = False if self.__gMinuit is not None: # also update Minuit instance self.__gMinuit.mnfree(-_par_id-1) # self.__gMinuit.mnexcm("RELEASE", # arr('d', [_par_id+1]), 1, error_code) self._min_result_stale = True def release_several(self, parameter_names): for _pn in parameter_names: self.release(_pn) def limit(self, parameter_name, parameter_bounds): assert len(parameter_bounds) == 2 # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_limits[_par_id] == parameter_bounds: return # same limits already set self._par_limits[_par_id] = parameter_bounds if self.__gMinuit is not None: _lo_lim, _up_lim = self._par_limits[_par_id] # also update Minuit instance error_code = Long(0) self.__gMinuit.mnexcm("SET LIM", arr('d', [_par_id+1, _lo_lim, _up_lim]), 3, error_code) self._min_result_stale = True def unlimit(self, parameter_name): # set local flag _par_id = self.parameter_names.index(parameter_name) if self._par_limits[_par_id] is None: return # parameter is already unlimited self._par_limits[_par_id] = None if self.__gMinuit is not None: # also update Minuit instance error_code = Long(0) self.__gMinuit.mnexcm("SET LIM", arr('d', [_par_id+1]), 1, error_code) self._min_result_stale = True def minimize(self, max_calls=6000): self._migrad(max_calls=max_calls) # retrieve fitters parameters self._par_val = [] self._par_err = [] _pv, _pe = Double(0), Double(0) for _par_id in six.moves.range(0, self.n_pars): self.__gMinuit.GetParameter(_par_id, _pv, _pe) # retrieve fitresult self._par_val.append(float(_pv)) self._par_err.append(float(_pe)) self._min_result_stale = False def contour(self, parameter_name_1, parameter_name_2, sigma=1.0, **minimizer_contour_kwargs): if self.__gMinuit is None: raise MinimizerROOTTMinuitException("Need to perform a fit before calling contour()!") _numpoints = minimizer_contour_kwargs.pop("numpoints", 100) if minimizer_contour_kwargs: raise MinimizerROOTTMinuitException("Unknown parameters: {}".format(minimizer_contour_kwargs)) _id_1 = self.parameter_names.index(parameter_name_1) _id_2 = self.parameter_names.index(parameter_name_2) self.__gMinuit.SetErrorDef(sigma ** 2) _t_graph = self.__gMinuit.Contour(_numpoints, _id_1, _id_2) self.__gMinuit.SetErrorDef(self._err_def) _x_buffer, _y_buffer = _t_graph.GetX(), _t_graph.GetY() _N = _t_graph.GetN() _x = np.frombuffer(_x_buffer, dtype=float, count=_N) _y = np.frombuffer(_y_buffer, dtype=float, count=_N) self._func_handle(*self.parameter_values) return ContourFactory.create_xy_contour((_x, _y), sigma) def profile(self, parameter_name, bins=21, bound=2, args=None, subtract_min=False): if self.__gMinuit is None: raise MinimizerROOTTMinuitException("Need to perform a fit before calling profile()!") MAX_ITERATIONS = 6000 _error_code = Long(0) _minuit_id = Long(self.parameter_names.index(parameter_name) + 1) _par_min = Double(0) _par_err = Double(0) self.__gMinuit.GetParameter(_minuit_id - 1, _par_min, _par_err) _x = np.linspace(start=_par_min - bound * _par_err, stop=_par_min + bound * _par_err, num=bins, endpoint=True) self.__gMinuit.mnexcm("FIX", arr('d', [_minuit_id]), 1, _error_code) _y = np.zeros(bins) for i in range(bins): self.__gMinuit.mnexcm("SET PAR", arr('d', [_minuit_id, Double(_x[i])]), 2, _error_code) self.__gMinuit.mnexcm("MIGRAD", arr('d', [MAX_ITERATIONS, self.tolerance]), 2, _error_code) _y[i] = self.get_fit_info("fcn") self.__gMinuit.mnexcm("RELEASE", arr('d', [_minuit_id]), 1, _error_code) self._migrad() self.__gMinuit.mnexcm("SET PAR", arr('d', [_minuit_id, Double(_par_min)]), 2, _error_code) return np.asarray((_x, _y))