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)
Example #2
0
 def __init__( self, fcn, pars, parerrors, parnames, ndof, maxpars=50 ):
       
     if len( pars ) > maxpars:
          raise MinuitError( "More than 50 parameters, increase maxpars" )
     self.__minuit= TMinuit( maxpars )
     self.minuitCommand( "SET PRI -1" )
     self.__minuit.SetFCN( fcn )
     self.__pars= pars
     self.__parerrors= parerrors
     self.__parnames= parnames
     self.__setParameters()
     self.__ndof= ndof
     return
Example #3
0
    def __init__(self, myFCN, params, **kwargs):

        from ROOT import TMinuit, Long, Double

        self.limits = np.zeros((len(params), 2))
        self.steps = .04 * np.ones(
            len(params))  # about 10 percent in log10 space
        self.tolerance = .001
        self.maxcalls = 10000
        self.printMode = 0
        self.up = 0.5
        self.param_names = ['p%i' % i for i in xrange(len(params))]
        self.erflag = Long()
        self.npars = len(params)
        self.args = ()
        self.gradient = None
        self.force_gradient = 0
        self.strategy = 1
        self.fixed = np.zeros_like(params)
        self.__dict__.update(kwargs)

        self.params = np.asarray(params, dtype='float')
        self.fixed = np.asarray(self.fixed, dtype=bool)
        self.fcn = FCN(myFCN,
                       self.params,
                       args=self.args,
                       gradient=self.gradient)
        self.fval = self.fcn.fval
        self.minuit = TMinuit(self.npars)
        self.minuit.SetPrintLevel(self.printMode)
        self.minuit.SetFCN(self.fcn)
        if self.gradient:
            self.minuit.mncomd('SET GRA %i' % (self.force_gradient),
                               self.erflag)
        self.minuit.mncomd('SET STR %i' % self.strategy, Long())

        for i in xrange(self.npars):

            if self.limits[i][0] is None: self.limits[i][0] = 0.0
            if self.limits[i][1] is None: self.limits[i][1] = 0.0

            self.minuit.DefineParameter(i, self.param_names[i], self.params[i],
                                        self.steps[i], self.limits[i][0],
                                        self.limits[i][1])

        self.minuit.SetErrorDef(self.up)

        for index in np.where(self.fixed)[0]:
            self.minuit.FixParameter(int(index))
Example #4
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
Example #5
0
    def __init__(self, fcn, pars, parerrors, parnames, ndof, maxpars=50):

        if len(pars) > maxpars:
            raise MinuitError("More than 50 parameters, increase maxpars")
        self.__minuit = TMinuit(maxpars)
        self.minuitCommand("SET PRI -1")
        # Hold on to fcn or python will kill it after passing to TMinuit
        self.__fcn = fcn
        self.__minuit.SetFCN(fcn)
        self.__pars = pars
        self.__parerrors = parerrors
        self.__parnames = parnames
        self.__setParameters()
        self.__ndof = ndof
        return
Example #6
0
 def __init__( self, fcn, pars, parerrors, parnames, ndof, maxpars=50 ):
       
     if len( pars ) > maxpars:
          raise MinuitError( "More than 50 parameters, increase maxpars" )
     self.__minuit= TMinuit( maxpars )
     self.minuitCommand( "SET PRI -1" )
     self.__minuit.SetFCN( fcn )
     self.__pars= pars
     self.__parerrors= parerrors
     self.__parnames= parnames
     self.__setParameters()
     self.__ndof= ndof
     return
Example #7
0
    def __init__(self,myFCN,params,**kwargs):

        from ROOT import TMinuit,Long,Double

        self.limits = np.zeros((len(params),2))
        self.steps = .04*np.ones(len(params)) # about 10 percent in log10 space
        self.tolerance = .001
        self.maxcalls = 10000
        self.printMode = 0
        self.up = 0.5
        self.param_names = ['p%i'%i for i in xrange(len(params))]
        self.erflag = Long()
        self.npars = len(params)
        self.args = ()
        self.gradient = None
        self.force_gradient = 0
        self.strategy = 1
        self.fixed = np.zeros_like(params)
        self.__dict__.update(kwargs)

        self.params = np.asarray(params,dtype='float')
        self.fixed=np.asarray(self.fixed,dtype=bool)
        self.fcn = FCN(myFCN,self.params,args=self.args,gradient=self.gradient)
        self.fval = self.fcn.fval
        self.minuit = TMinuit(self.npars)
        self.minuit.SetPrintLevel(self.printMode)
        self.minuit.SetFCN(self.fcn)
        if self.gradient:
            self.minuit.mncomd('SET GRA %i'%(self.force_gradient),self.erflag)
        self.minuit.mncomd('SET STR %i'%self.strategy,Long())


        for i in xrange(self.npars):

            if self.limits[i][0] is None: self.limits[i][0] = 0.0
            if self.limits[i][1] is None: self.limits[i][1] = 0.0
            
            self.minuit.DefineParameter(i,self.param_names[i],self.params[i],self.steps[i],self.limits[i][0],self.limits[i][1])

        self.minuit.SetErrorDef(self.up)

        for index in np.where(self.fixed)[0]:
            self.minuit.FixParameter(int(index))
Example #8
0
 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
Example #9
0
# --- fcn -- called by MINUIT repeatedly with varying parameters
# NOTE: the function name is set via TMinuit.SetFCN
def fcn(npar, deriv, f, apar, iflag):
    """ meaning of parameters:
        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
    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
Example #11
0
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)
Example #12
0
def signalfit(data_hist, signalfunction, signalname):

    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[3] = data_hist.GetRMS()
    lower[3] = data_hist.GetRMS() / 3.0
    upper[3] = data_hist.GetRMS() * 3.0
    vstart[3] = 125
    lower[3] = 5
    upper[3] = 20

    vstart[4] = data_hist.Integral()

    vstart[2] = data_hist.GetMean()
    lower[2] = data_hist.GetMean() - 10
    upper[2] = data_hist.GetMean() + 10

    vstart[2] = 125
    lower[2] = 110
    upper[2] = 130

    if len(vstart) > 5:
        vstart[5] = data_hist.GetMean()
        lower[5] = data_hist.GetMean() - 10
        upper[5] = data_hist.GetMean() + 10
        vstart[5] = 125
        lower[5] = 110
        upper[5] = 150

        #vstart[6] = data_hist.GetRMS()
        #lower[6] = data_hist.GetRMS()/3.0
        #upper[6] = data_hist.GetRMS()*3.0

        vstart[6] = 10
        lower[6] = 5
        upper[6] = 20

    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]
Example #13
0
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]
Example #14
0
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)
Example #15
0
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))
Example #16
0
    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
Example #17
0
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
Example #18
0
    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
Example #19
0
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
Example #20
0
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 xrange(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, e:
            if show_warnings:
                logger.warn("Cannot update Minuit data on the C++ side. "
                            "AttributeError: %s" % (e, ))
        return error_code
Example #21
0
    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
Example #22
0
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

        self.out_file = open(log_file("minuit.log"), 'a')

        # 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 xrange(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, e:
            if show_warnings:
                logger.warn("Cannot update Minuit data on the C++ side. "
                            "AttributeError: %s" % (e, ))
        return error_code
Example #23
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.])
        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.)
Example #24
0
class minuitSolver():
    def __init__(self, fcn, pars, parerrors, parnames, ndof, maxpars=50):

        if len(pars) > maxpars:
            raise MinuitError("More than 50 parameters, increase maxpars")
        self.__minuit = TMinuit(maxpars)
        self.minuitCommand("SET PRI -1")
        # Hold on to fcn or python will kill it after passing to TMinuit
        self.__fcn = fcn
        self.__minuit.SetFCN(fcn)
        self.__pars = pars
        self.__parerrors = parerrors
        self.__parnames = parnames
        self.__setParameters()
        self.__ndof = ndof
        return

    def __setParameters(self):
        for par, parerror, parname, i in zip(self.__pars, self.__parerrors,
                                             self.__parnames,
                                             range(len(self.__pars))):
            ierflg = self.__minuit.DefineParameter(i, parname, par, parerror,
                                                   0.0, 0.0)
        if ierflg != 0:
            message = "Minuit define parameter error: " + str(ierflg)
            raise MinuitError(message)
        return

    def minuitCommand(self, command):
        errorcode = self.__minuit.Command(command)
        if errorcode != 0:
            message = "Minuit command " + command + " failed: " + str(
                errorcode)
            raise MinuitError(message)
        return

    def solve(self, lBlobel=True):
        self.__setParameters()
        self.minuitCommand("MIGRAD")
        return

    def getChisq(self):
        hstat = self.__getStat()
        return hstat["min"]

    def getNdof(self):
        return self.__ndof

    def __printPars(self, par, parerrors, parnames, ffmt=".4f"):
        for ipar in range(len(par)):
            name = parnames[ipar]
            print("{0:>15s}:".format(name), end=" ")
            fmtstr = "{0:10" + ffmt + "} +/- {1:10" + ffmt + "}"
            print(fmtstr.format(par[ipar], parerrors[ipar]))
        return

    def printResults(self, ffmt=".4f", cov=False, corr=False):
        print("\nMinuit least squares")
        print("\nResults after minuit fit")
        hstat = self.__getStat()
        chisq = hstat["min"]
        ndof = self.__ndof
        fmtstr = "\nChi^2= {0:" + ffmt + "} for {1:d} d.o.f, Chi^2/d.o.f= {2:" + ffmt + "}, P-value= {3:" + ffmt + "}"
        print(
            fmtstr.format(chisq, ndof, chisq / float(ndof),
                          TMath.Prob(chisq, ndof)))
        fmtstr = "Est. dist. to min: {0:.3e}, minuit status: {1}"
        print(fmtstr.format(hstat["edm"], hstat["status"]))
        print("\nFitted parameters and errors")
        print("           Name       Value          Error")
        pars = self.getPars()
        parerrors = self.getParErrors()
        self.__printPars(pars, parerrors, self.__parnames, ffmt=ffmt)
        if cov:
            self.printCovariances()
        if corr:
            self.printCorrelations()
        return

    def __printMatrix(self, m, ffmt):
        mshape = m.shape
        print("{0:>10s}".format(""), end=" ")
        for i in range(mshape[0]):
            print("{0:>10s}".format(self.__parnames[i]), end=" ")
        print()
        for i in range(mshape[0]):
            print("{0:>10s}".format(self.__parnames[i]), end=" ")
            for j in range(mshape[1]):
                fmtstr = "{0:10" + ffmt + "}"
                print(fmtstr.format(m[i, j]), end=" ")
            print()
        return

    def printCovariances(self):
        print("\nCovariance matrix:")
        self.__printMatrix(self.getCovariancematrix(), ".3e")
        return

    def printCorrelations(self):
        print("\nCorrelation matrix:")
        self.__printMatrix(self.getCorrelationmatrix(), ".3f")
        return

    def getPars(self):
        pars, parerrors = self.__getPars()
        return pars

    def getUparv(self):
        pars = self.getPars()
        parv = matrix(pars)
        parv.shape = (len(pars), 1)
        return parv

    def getParErrors(self):
        pars, parerrors = self.__getPars()
        return parerrors

    def __getPars(self):
        pars = []
        parerrors = []
        for ipar in range(len(self.__pars)):
            par = c_double()
            pare = c_double()
            ivarbl = self.__minuit.GetParameter(ipar, par, pare)
            if ivarbl < 0:
                message = "Parameter " + str(ipar) + " not defined"
                raise MinuitError(message)
            pars.append(par.value)
            parerrors.append(pare.value)
        return pars, parerrors

    def getCovariancematrix(self):
        npar = len(self.__pars)
        covm = array(npar**2 * [0.0], dtype="double")
        self.__minuit.mnemat(covm, npar)
        covm.shape = (npar, npar)
        return covm

    def getCorrelationmatrix(self):
        covm = self.getCovariancematrix()
        corrm = covm.copy()
        npar = len(self.__pars)
        for i in range(npar):
            for j in range(npar):
                corrm[i, j] = covm[i, j] / sqrt(covm[i, i] * covm[j, j])
        return corrm

    def __getStat(self):
        fmin = c_double()
        fedm = c_double()
        errdef = c_double()
        npari = c_int()
        nparx = c_int()
        istat = c_int()
        self.__minuit.mnstat(fmin, fedm, errdef, npari, nparx, istat)
        hstat = {
            "min": fmin.value,
            "edm": fedm.value,
            "errdef": errdef.value,
            "npari": npari.value,
            "nparx": nparx.value,
            "status": istat.value
        }
        return hstat
Example #25
0
    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
Example #26
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.)
Example #27
0
	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
Example #28
0
class Minuit(object):
    """A wrapper class to initialize a minuit object with a numpy array.

    Positional args:
        myFCN : A python callable
        params : An array (or other python sequence) of parameters

    Keyword args:

        limits [None] : a nested sequence of (lower_limit,upper_limit) for each parameter.
        steps [[.1]*npars] : Estimated errors for the parameters, used as an initial step size.
        tolerance [.001] : Tolerance to test for convergence.  Minuit considers convergence to be
                            when the estimated distance to the minimum (edm) is <= .001*up*tolerance,
                            or 5e-7 by default.
        up [.5]  : Change in the objective function that determines 1-sigma error.  .5 if 
                          the objective function is -log(Likelihood), 1 for chi-squared.
        max_calls [10000] : Maximum number of calls to the objective function.
        param_names ['p0','p1',...] : a list of names for the parameters
        args [()] : a tuple of extra arguments to pass to myFCN and gradient.
        gradient [None] : a function that takes a list of parameters and returns a list of 
                          first derivatives of myFCN.  Assumed to take the same args as myFCN.
        force_gradient [0] : Set to 1 to force Minuit to use the user-provided gradient function.
        strategy[1] : Strategy for minuit to use, from 0 (fast) to 2 (safe) 
        fixed [False, False, ...] : If passed, an array of all the parameters to fix
    """


    def __init__(self,myFCN,params,**kwargs):

        from ROOT import TMinuit,Long,Double

        self.limits = np.zeros((len(params),2))
        self.steps = .04*np.ones(len(params)) # about 10 percent in log10 space
        self.tolerance = .001
        self.maxcalls = 10000
        self.printMode = 0
        self.up = 0.5
        self.param_names = ['p%i'%i for i in xrange(len(params))]
        self.erflag = Long()
        self.npars = len(params)
        self.args = ()
        self.gradient = None
        self.force_gradient = 0
        self.strategy = 1
        self.fixed = np.zeros_like(params)
        self.__dict__.update(kwargs)

        self.params = np.asarray(params,dtype='float')
        self.fixed=np.asarray(self.fixed,dtype=bool)
        self.fcn = FCN(myFCN,self.params,args=self.args,gradient=self.gradient)
        self.fval = self.fcn.fval
        self.minuit = TMinuit(self.npars)
        self.minuit.SetPrintLevel(self.printMode)
        self.minuit.SetFCN(self.fcn)
        if self.gradient:
            self.minuit.mncomd('SET GRA %i'%(self.force_gradient),self.erflag)
        self.minuit.mncomd('SET STR %i'%self.strategy,Long())


        for i in xrange(self.npars):

            if self.limits[i][0] is None: self.limits[i][0] = 0.0
            if self.limits[i][1] is None: self.limits[i][1] = 0.0
            
            self.minuit.DefineParameter(i,self.param_names[i],self.params[i],self.steps[i],self.limits[i][0],self.limits[i][1])

        self.minuit.SetErrorDef(self.up)

        for index in np.where(self.fixed)[0]:
            self.minuit.FixParameter(int(index))



    def minimize(self,method='MIGRAD'):

        from ROOT import TMinuit,Long,Double

        self.minuit.mncomd('%s %i %f'%(method, self.maxcalls,self.tolerance),self.erflag)
        for i in xrange(self.npars):
            val,err = Double(),Double()
            self.minuit.GetParameter(i,val,err)
            self.params[i] = val
        self.fval = self.fcn.fcn(self.params)
        return (self.params,self.fval)

    def errors(self,method='HESSE'):
        """method ['HESSE']   : how to calculate the errors; Currently, only 'HESSE' works."""
        if not np.any(self.fixed):
            mat = np.zeros(self.npars**2)
            if method == 'HESSE':
                self.minuit.mnhess()
            else:
                raise Exception("Method %s not recognized." % method)
            self.minuit.mnemat(mat,self.npars)
            return mat.reshape((self.npars,self.npars))
        else:
            # Kind of ugly, but for fixed parameters, you need to expand out the covariance matrix.
            nf=int(np.sum(~self.fixed))
            mat = np.zeros(nf**2)
            if method == 'HESSE':
                self.minuit.mnhess()
            else:
                raise Exception("Method %s not recognized." % method)
            self.minuit.mnemat(mat,nf)

            # Expand out the covariance matrix.
            cov = np.zeros((self.npars,self.npars))
            cov[np.outer(~self.fixed,~self.fixed)] = np.ravel(mat)
            return cov

    def uncorrelated_minos_error(self):
        """ Kind of a kludge, but a compromise between the speed of
            HESSE errors and the accuracy of MINOS errors.  It accounts
            for non-linearities in the likelihood by varying each function
            until the value has fallen by the desired amount using hte
            MINOS code. But does not account for correlations between
            the parameters by fixing all other parameters during the
            minimzation. 
            
            Again kludgy, but what is returned is an effective covariance
            matrix. The diagonal errors are calcualted by averaging the
            upper and lower errors and the off diagonal terms are set
            to 0. """

        cov = np.zeros((self.npars,self.npars))

        # fix all parameters
        for i in range(self.npars):
            self.minuit.FixParameter(int(i))

        # loop over free paramters getting error
        for i in np.where(self.fixed==False)[0]:
            self.minuit.Release(int(i))

            # compute error
            self.minuit.mnmnos() 
            low,hi,parab,gcc=ROOT.Double(),ROOT.Double(),ROOT.Double(),ROOT.Double()

            # get error
            self.minuit.mnerrs(int(i),low,hi,parab,gcc)
            cov[i,i]=((abs(low)+abs(hi))/2.0)**2
            self.minuit.FixParameter(int(i))

        for i,fixed in enumerate(self.fixed):
            if fixed:
                self.minuit.FixParameter(int(i))
            else:
                self.minuit.Release(int(i))

        return cov
Example #29
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. )
g1 = 0.
g2 = 0.
for i in range(len(error_w1)):
  g1 += error_w1[i]**2
  g2 += error_w2[i]**2

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
Example #31
0
	outTag =outTag_name+str(q)
	loaded_model = joblib.load(os.path.expanduser('~/HHbbgg_ETH_devel/bregression/output_files/regression_heppy_'+outTag+'.pkl'))   
	X_pred_data = loaded_model.predict(X_test_features).astype(np.float64)
	X_predicted_all.append(X_pred_data)

n_evt = len(X_predicted_all[0])

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
Example #32
0
g1 = 0.
g2 = 0.
for i in range(len(error_w1)):
    g1 += error_w1[i]**2
    g2 += error_w2[i]**2

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
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
Example #34
0
class minuitSolver():

    def __init__( self, fcn, pars, parerrors, parnames, ndof, maxpars=50 ):
          
        if len( pars ) > maxpars:
             raise MinuitError( "More than 50 parameters, increase maxpars" )
        self.__minuit= TMinuit( maxpars )
        self.minuitCommand( "SET PRI -1" )
        self.__minuit.SetFCN( fcn )
        self.__pars= pars
        self.__parerrors= parerrors
        self.__parnames= parnames
        self.__setParameters()
        self.__ndof= ndof
        return
   
    def __setParameters( self ):
        for par, parerror, parname, i in zip( self.__pars,
                                              self.__parerrors,
                                              self.__parnames, 
                                              range( len( self.__pars ) ) ):
             ierflg= self.__minuit.DefineParameter( i, parname, par, parerror, 
                                                    0.0, 0.0 )
        if ierflg != 0:
             message= "Minuit define parameter error: " + str( ierflg )
             raise MinuitError( message )
        return

    def minuitCommand( self, command ):
        errorcode= self.__minuit.Command( command )
        if errorcode != 0:
            message= "Minuit command " + command + " failed: " + str( errorcode )
            raise MinuitError( message )
        return

    def solve( self, lBlobel=True ):
        self.__setParameters()
        self.minuitCommand( "MIGRAD" )
        return

    def getChisq( self ):
        hstat= self.__getStat()
        return hstat["min"]

    def getNdof( self ):
        return self.__ndof

    def __printPars( self, par, parerrors, parnames, ffmt=".4f" ):
        for ipar in range( len( par ) ):
            name= parnames[ipar]
            print "{0:>15s}:".format( name ),
            fmtstr= "{0:10" + ffmt + "} +/- {1:10" + ffmt + "}"
            print fmtstr.format( par[ipar], parerrors[ipar] )
        return

    def printResults( self, ffmt=".4f", cov=False, corr=False ):
        print "\nMinuit least squares"
        print "\nResults after minuit fit"
        hstat= self.__getStat()
        chisq= hstat["min"]
        ndof= self.__ndof
        fmtstr= "\nChi^2= {0:"+ffmt+"} for {1:d} d.o.f, Chi^2/d.o.f= {2:"+ffmt+"}, P-value= {3:"+ffmt+"}"
        print fmtstr.format( chisq, ndof, chisq/float(ndof), 
                             TMath.Prob( chisq, ndof ) )
        fmtstr= "Est. dist. to min: {0:.3e}, minuit status: {1}"
        print fmtstr.format( hstat["edm"], hstat["status"] )
        print "\nFitted parameters and errors"
        print "           Name       Value          Error"
        pars= self.getPars()
        parerrors= self.getParErrors()
        self.__printPars( pars, parerrors, self.__parnames, ffmt=ffmt )
        if cov:
            self.printCovariances()
        if corr:
            self.printCorrelations()
        return

    def __printMatrix( self, m, ffmt ):
        mshape= m.shape
        print "{0:>10s}".format( "" ),
        for i in range(mshape[0]):
            print "{0:>10s}".format( self.__parnames[i] ),
        print
        for i in range(mshape[0]):
            print "{0:>10s}".format( self.__parnames[i] ),
            for j in range(mshape[1]):
                fmtstr= "{0:10"+ffmt+"}"
                print fmtstr.format( m[i,j] ),
            print
        return
    def printCovariances( self ):
        print "\nCovariance matrix:"
        self.__printMatrix( self.getCovariancematrix(), ".3e" )
        return
    def printCorrelations( self ):
        print "\nCorrelation matrix:"
        self.__printMatrix( self.getCorrelationmatrix(), ".3f" )
        return

    def getPars( self ):
        pars, parerrors= self.__getPars()
        return pars
    def getUparv( self ):
        pars= self.getPars()
        parv= matrix( pars )
        parv.shape= (len(pars),1)
        return parv
    def getParErrors( self ):
        pars, parerrors= self.__getPars()
        return parerrors
    def __getPars( self ):
        pars= []
        parerrors= []
        for ipar in range( len( self.__pars ) ):
            par= Double()
            pare= Double()
            ivarbl= self.__minuit.GetParameter( ipar, par, pare )
            if ivarbl < 0:
                message= "Parameter " + str(ipar) + " not defined"
                raise MinuitError( message )
            pars.append( par )
            parerrors.append( pare )
        return pars, parerrors

    def getCovariancematrix( self ):
        npar= len( self.__pars )
        covm= array( npar**2*[ 0.0 ], dtype="double" )
        self.__minuit.mnemat( covm, npar )
        covm.shape= (npar,npar)
        return covm

    def getCorrelationmatrix( self ):
        covm= self.getCovariancematrix()
        corrm= covm.copy()
        npar= len( self.__pars )
        for i in range( npar ):
            for j in range( npar ):
                corrm[i,j]= covm[i,j]/sqrt(covm[i,i]*covm[j,j])
        return corrm

    def __getStat( self ):
        fmin= Double()
        fedm= Double()
        errdef= Double()
        npari= Long()
        nparx= Long()
        istat= Long()
        self.__minuit.mnstat( fmin, fedm, errdef, npari, nparx, istat )
        hstat= { "min": fmin, 
                 "edm": fedm, 
                 "errdef": errdef, 
                 "npari": npari, 
                 "nparx": nparx, 
                 "status": istat }
        return hstat
Example #35
0
    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
Example #36
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
Example #37
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]
Example #38
0
    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()
Example #39
0
class Minuit(object):
    """A wrapper class to initialize a minuit object with a numpy array.

    Positional args:
        myFCN : A python callable
        params : An array (or other python sequence) of parameters

    Keyword args:

        limits [None] : a nested sequence of (lower_limit,upper_limit) for each parameter.
        steps [[.1]*npars] : Estimated errors for the parameters, used as an initial step size.
        tolerance [.001] : Tolerance to test for convergence.  Minuit considers convergence to be
                            when the estimated distance to the minimum (edm) is <= .001*up*tolerance,
                            or 5e-7 by default.
        up [.5]  : Change in the objective function that determines 1-sigma error.  .5 if 
                          the objective function is -log(Likelihood), 1 for chi-squared.
        max_calls [10000] : Maximum number of calls to the objective function.
        param_names ['p0','p1',...] : a list of names for the parameters
        args [()] : a tuple of extra arguments to pass to myFCN and gradient.
        gradient [None] : a function that takes a list of parameters and returns a list of 
                          first derivatives of myFCN.  Assumed to take the same args as myFCN.
        force_gradient [0] : Set to 1 to force Minuit to use the user-provided gradient function.
        strategy[1] : Strategy for minuit to use, from 0 (fast) to 2 (safe) 
        fixed [False, False, ...] : If passed, an array of all the parameters to fix
    """
    def __init__(self, myFCN, params, **kwargs):

        from ROOT import TMinuit, Long, Double

        self.limits = np.zeros((len(params), 2))
        self.steps = .04 * np.ones(
            len(params))  # about 10 percent in log10 space
        self.tolerance = .001
        self.maxcalls = 10000
        self.printMode = 0
        self.up = 0.5
        self.param_names = ['p%i' % i for i in xrange(len(params))]
        self.erflag = Long()
        self.npars = len(params)
        self.args = ()
        self.gradient = None
        self.force_gradient = 0
        self.strategy = 1
        self.fixed = np.zeros_like(params)
        self.__dict__.update(kwargs)

        self.params = np.asarray(params, dtype='float')
        self.fixed = np.asarray(self.fixed, dtype=bool)
        self.fcn = FCN(myFCN,
                       self.params,
                       args=self.args,
                       gradient=self.gradient)
        self.fval = self.fcn.fval
        self.minuit = TMinuit(self.npars)
        self.minuit.SetPrintLevel(self.printMode)
        self.minuit.SetFCN(self.fcn)
        if self.gradient:
            self.minuit.mncomd('SET GRA %i' % (self.force_gradient),
                               self.erflag)
        self.minuit.mncomd('SET STR %i' % self.strategy, Long())

        for i in xrange(self.npars):

            if self.limits[i][0] is None: self.limits[i][0] = 0.0
            if self.limits[i][1] is None: self.limits[i][1] = 0.0

            self.minuit.DefineParameter(i, self.param_names[i], self.params[i],
                                        self.steps[i], self.limits[i][0],
                                        self.limits[i][1])

        self.minuit.SetErrorDef(self.up)

        for index in np.where(self.fixed)[0]:
            self.minuit.FixParameter(int(index))

    def minimize(self, method='MIGRAD'):

        from ROOT import TMinuit, Long, Double

        self.minuit.mncomd(
            '%s %i %f' % (method, self.maxcalls, self.tolerance), self.erflag)
        for i in xrange(self.npars):
            val, err = Double(), Double()
            self.minuit.GetParameter(i, val, err)
            self.params[i] = val
        self.fval = self.fcn.fcn(self.params)
        return (self.params, self.fval)

    def errors(self, method='HESSE'):
        """method ['HESSE']   : how to calculate the errors; Currently, only 'HESSE' works."""
        if not np.any(self.fixed):
            mat = np.zeros(self.npars**2)
            if method == 'HESSE':
                self.minuit.mnhess()
            else:
                raise Exception("Method %s not recognized." % method)
            self.minuit.mnemat(mat, self.npars)
            return mat.reshape((self.npars, self.npars))
        else:
            # Kind of ugly, but for fixed parameters, you need to expand out the covariance matrix.
            nf = int(np.sum(~self.fixed))
            mat = np.zeros(nf**2)
            if method == 'HESSE':
                self.minuit.mnhess()
            else:
                raise Exception("Method %s not recognized." % method)
            self.minuit.mnemat(mat, nf)

            # Expand out the covariance matrix.
            cov = np.zeros((self.npars, self.npars))
            cov[np.outer(~self.fixed, ~self.fixed)] = np.ravel(mat)
            return cov

    def uncorrelated_minos_error(self):
        """ Kind of a kludge, but a compromise between the speed of
            HESSE errors and the accuracy of MINOS errors.  It accounts
            for non-linearities in the likelihood by varying each function
            until the value has fallen by the desired amount using hte
            MINOS code. But does not account for correlations between
            the parameters by fixing all other parameters during the
            minimzation. 
            
            Again kludgy, but what is returned is an effective covariance
            matrix. The diagonal errors are calcualted by averaging the
            upper and lower errors and the off diagonal terms are set
            to 0. """

        cov = np.zeros((self.npars, self.npars))

        # fix all parameters
        for i in range(self.npars):
            self.minuit.FixParameter(int(i))

        # loop over free paramters getting error
        for i in np.where(self.fixed == False)[0]:
            self.minuit.Release(int(i))

            # compute error
            self.minuit.mnmnos()
            low, hi, parab, gcc = ROOT.Double(), ROOT.Double(), ROOT.Double(
            ), ROOT.Double()

            # get error
            self.minuit.mnerrs(int(i), low, hi, parab, gcc)
            cov[i, i] = ((abs(low) + abs(hi)) / 2.0)**2
            self.minuit.FixParameter(int(i))

        for i, fixed in enumerate(self.fixed):
            if fixed:
                self.minuit.FixParameter(int(i))
            else:
                self.minuit.Release(int(i))

        return cov
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 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)
Example #42
0
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) ]
Example #43
0
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()
Example #44
0
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))