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 main(): global freqs_hz, pv, sigq, sigu parser = OptionParser(usage) parser.add_option("-r", "--rm", type="float", dest="rm", default=0., help="Estimate of the RM") parser.add_option("-n", "--norm", action="store_true", dest="norm", default=False, help="Verbose mode") parser.add_option("-b", "--baseline", action="store_true", dest="baseline", default=False, help="Fit for a baseline") parser.add_option("-f", "--nofit", action="store_true", dest="nofit", default=False, help="Don't fit for the RM with Minuit") parser.add_option("-c", "--clean", action="store_true", dest="clean", default=False, help="Clean the baseline") (opts, args) = parser.parse_args() #if opts.help: # print full_usage arch = psrchive.Archive_load(sys.argv[1]) arch.tscrunch() arch.bscrunch(2) #arch.fscrunch(2) arch.dedisperse() arch.remove_baseline() arch.convert_state('Stokes') arch.centre_max_bin() #arch.rotate(0.5) data = arch.get_data() MJD = arch.get_first_Integration().get_epoch().strtempo() w = arch.get_weights().sum(0) I = data[:,0,:,:].mean(0) Q = data[:,1,:,:].mean(0) U = data[:,2,:,:].mean(0) #V = data[:,3,:,:].mean(0) I = ma.masked_array(I) Q = ma.masked_array(Q) U = ma.masked_array(U) #V = ma.masked_array(V) I[w==0] = np.ma.masked Q[w==0] = np.ma.masked U[w==0] = np.ma.masked # Get Freqs nchan = arch.get_nchan() freqs = np.zeros(nchan) for i in range(nchan): freqs[i] = arch.get_Profile(0,0,i).get_centre_frequency() ## Determine phase range arch.fscrunch() arch.tscrunch() arch.pscrunch() prof = arch.get_Profile(0,0,0) #print prof.get_amps() lowbin, hibin = get_pulse_range(arch) #lowbin, hibin = 458,531 #lowbin, hibin = 20,120 if opts.clean: for n in range(nchan): if n%20: continue baseline_idx = np.append(np.arange(0, lowbin), np.arange(hibin, arch.get_nbin())) baseline_dat = I[n][baseline_idx] print n, baseline_idx, baseline_dat poly = np.polyfit(baseline_idx, baseline_dat, 1) p = np.poly1d(poly) pylab.plot(I[n]) I[n] = I[n] - p(np.arange(0, arch.get_nbin())) pylab.plot(I[n]) pylab.show() for ii, wx in enumerate(w): I[ii] *= wx/np.max(w) Q[ii] *= wx/np.max(w) U[ii] *= wx/np.max(w) #V[ii] *= wx if opts.norm: print "Will normalize by the rms of the noise" scale = np.std(I, axis=1) / np.max(np.std(I, axis=1)) for ii, s in enumerate(scale): I[ii,:] /= s Q[ii,:] /= s U[ii,:] /= s #Q = Q / scale #U = U / scale freq_lo = freqs[0] freq_hi = freqs[-1] lowphase = lowbin / float(arch.get_nbin()) hiphase = hibin / float(arch.get_nbin()) #lowphase= 0.48 #hiphase = 0.515 #lowphase = 0.515 #hiphase = 0.54 print "Pulse phase window: ", lowphase, hiphase #pylab.plot(np.sum(I), axis) #pylab.show() # 2D plot f = pylab.figure() pylab.subplot(321) #print np.sum(I,axis=0) #pylab.plot(np.sum(I, axis=0)) pylab.plot(prof.get_amps()) pylab.xlim([0, arch.get_nbin()]) f.text( .5, 0.95, r'%s'%os.path.split(sys.argv[1])[1], horizontalalignment='center') pylab.subplot(323) pylab.axis([0,1,freq_lo,freq_hi]) pylab.imshow(I,extent=(0,1,freq_lo,freq_hi), origin='lower', aspect='auto', vmax=I.max()/1.5, vmin=I.min()/1.5) pylab.xlabel('Pulse phase') pylab.ylabel('Frequency (MHz)') # Compute errors of Q and U sigq = np.std(Q[:,lowbin:hibin], axis=1) / np.sqrt(hibin-lowbin) sigu = np.std(U[:,lowbin:hibin], axis=1) / np.sqrt(hibin-lowbin) #sigq = np.std(Q[:,:], axis=1) #sigu = np.std(U[:,:], axis=1) pylab.plot([lowphase,lowphase], [freq_lo, freq_hi], 'r--') pylab.plot([hiphase,hiphase], [freq_lo, freq_hi], 'r--') freqs_hz = freqs * 1e6 # Select phase range lowbin = int(lowphase * arch.get_nbin()) hibin = int(hiphase * arch.get_nbin()) dat = Q + 1j * U pv = dat[:,lowbin:hibin].mean(1) # Select phase range and average #rhos = np.arange(-90000, 90000, 10) rhos = np.arange(-2000, 2000, 1) res = np.absolute(getL(rhos, pv)) # Plot I f(RM) pylab.subplot(324) pylab.plot(rhos, res, 'b-') pylab.xlabel('RM') pylab.ylabel('I') # Plot Q pylab.subplot(325) pylab.errorbar(freqs, np.real(pv), yerr=sigq, ls='None') pylab.plot(freqs, np.real(pv), 'bo') pylab.xlabel('Frequency (MHz)') pylab.ylabel('Q') # Plot U pylab.subplot(326) pylab.errorbar(freqs, np.imag(pv), yerr=sigu, ls='None') pylab.plot(freqs, np.imag(pv), 'bo') pylab.xlabel('Frequency (MHz)') pylab.ylabel('U') # Should get the initial RM if opts.rm: initial_RM = -opts.rm else: initial_RM = -rhos[np.argmax(res)] print "Will use initial RM", initial_RM initial_L = getL(np.array([initial_RM]), pv) / (hibin-lowbin) if not opts.nofit: # Minuit part gMinuit = TMinuit(5) #gMinuit.SetErrorDef(1) # 1-Sigma Error gMinuit.SetErrorDef(4) # 2-Sigma Error gMinuit.SetFCN(fcn) arglist = arr('d', 2*[0.01]) ierflg = Long(0) #arglist[0] = 1 #gMinuit.mnexcm("SET ERR", arglist ,1,ierflg) # Set initial parameter values for fit vstart = arr( 'd', (np.real(initial_L), np.imag(initial_L), initial_RM) ) #vstart = arr( 'd', (2*np.real(initial_L), 2*np.imag(initial_L), initial_RM) ) # Set step size for fit step = arr( 'd', (0.001, 0.001, 0.001) ) # Define the parameters for the fit gMinuit.mnparm(0, "Qf", vstart[0], step[0], 0,0,ierflg) gMinuit.mnparm(1, "Uf", vstart[1], step[1], 0,0,ierflg) gMinuit.mnparm(2, "RM", vstart[2], step[2], 0,0,ierflg) gMinuit.mnparm(3, "a", 0.0, step[2], 0,0,ierflg) gMinuit.mnparm(4, "b", 0.0, step[2], 0,0,ierflg) #gMinuit.FixParameter(2) if not opts.baseline: gMinuit.FixParameter(3) gMinuit.FixParameter(4) arglist[0] = 6000 # Number of calls to FCN before giving up. arglist[1] = 0.1 # Tolerance gMinuit.mnexcm("MIGRAD", arglist ,2,ierflg) amin, edm, errdef = Double(0.18), Double(0.19), Double(1.0) nvpar, nparx, icstat = Long(1), Long(2), Long(3) gMinuit.mnstat(amin,edm,errdef,nvpar,nparx,icstat); gMinuit.mnmnos(); gMinuit.mnprin(3,amin); finalQ, finalQ_err = Double(0), Double(0) finalU, finalU_err = Double(0), Double(0) finalRM, finalRM_err = Double(0), Double(0) A, A_err = Double(0), Double(0) B, B_err = Double(0), Double(0) print "\n\n" gMinuit.GetParameter(0, finalQ, finalQ_err) gMinuit.GetParameter(1, finalU, finalU_err) gMinuit.GetParameter(2, finalRM, finalRM_err) gMinuit.GetParameter(3, A, A_err) gMinuit.GetParameter(4, B, B_err) finalRM *= -1. print finalQ, finalU line1 = "Final RM: %.1f"%(finalRM) + "(+/-) %.1f "%(finalRM_err) + " (2-sig) " line2 = "Chi**2r = %.2f"%(get_chi2((finalQ,finalU,-finalRM,A,B)) / ( 2*pv.size - 3 - 1 -2)) print line1 print line2 f.text( .5, 0.92, line1 + line2, horizontalalignment='center') print MJD, np.mean(freqs), finalRM, finalRM_err, get_chi2((finalQ,finalU,finalRM,A,B)), 2*pv.count() # Plot best fit model finalRM *= -1. pylab.subplot(325) pylab.plot(freqs, np.real( -A+getPV(finalQ+1j*finalU, finalRM) )) pylab.subplot(326) pylab.plot(freqs, np.imag( -B+getPV(finalQ+1j*finalU, finalRM) )) pylab.show()
def signalfit(data_hist, signalfunction, signalname, process): binning = HistBinsToList(data_hist) data_x = HistToList(data_hist) data_error = HistErrorList(data_hist) parfunction = signalfunction.GetNumberFreeParameters() partot = signalfunction.GetNumberFreeParameters() print partot ### the fucntion used for TMinuit def fcn(npar, gin, f, par, ifag): L = 0 # calculate likelihood, input par[0] is the N_B, par[1] is N_C, par[2] is N_L for ibin in range(len(binning)): #if (data_x[ibin] ==0): # continue bincen = binning[ibin] mu_x = 0 data = data_x[ibin] if data_error[ibin] == 0: continue #if data<0.1: # continue if signalname == "CrystalBall": if par[3] < 0: mu_x = 0 else: t = (bincen - par[2]) / (par[3]) if (par[0] < 0): t = -t absAlpha = abs(par[0]) if (t >= -absAlpha): mu_x = par[4] * exp(-0.5 * t * t) else: nDivAlpha = par[1] / absAlpha AA = exp(-0.5 * absAlpha * absAlpha) B = nDivAlpha - absAlpha arg = nDivAlpha / (B - t) mu_x = par[4] * (arg**par[1]) if signalname == "CrystalBallGaus": if par[3] < 0: mu_x = 0 else: t = (bincen - par[2]) / (par[3]) if (par[0] < 0): t = -t absAlpha = abs(par[0]) if (t >= -absAlpha): mu_x = par[4] * exp(-0.5 * t * t + exp(-(bincen - par[5])**2 / (2 * par[6]**2))) else: nDivAlpha = par[1] / absAlpha AA = exp(-0.5 * absAlpha * absAlpha) B = nDivAlpha - absAlpha arg = nDivAlpha / (B - t) mu_x = par[4] * (arg**par[1] + exp(-(bincen - par[5])**2 / (2 * par[6]**2))) #print mu_x, data, data_error[ibin] #L = L + mu_x - data*log(mu_x) L = L + ((mu_x - data) / data_error[ibin])**2 f[0] = L # initialize the TMinuit object arglist_p = 10 * [0] arglist = array.array('d') arglist.fromlist(arglist_p) ierflag = Long(0) maxiter = 1000000000 arglist_p = [1] gMinuit = TMinuit(partot) gMinuit.mnexcm('SET PRIntout', arglist, 0, ierflag) gMinuit.SetPrintLevel(1) gMinuit.SetErrorDef(1.0) gMinuit.SetFCN(fcn) arglist_p = [2] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('SET STRategy', arglist, 1, ierflag) arglist_p = [maxiter, 0.0000001] arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnexcm('MIGrad', arglist, 2, ierflag) gMinuit.SetMaxIterations(maxiter) # initialize fitting the variables vstart = [125.0] * partot step = [0.1] * partot upper = [1000000] * partot lower = [-100] * partot varname = [] lower[3] = 0 lower[4] = 0 lower[1] = 0 vstart[4] = data_hist.Integral() if process == "signal": vstart[2] = 125 lower[2] = 110 upper[2] = 140 vstart[3] = 10 lower[3] = 2 upper[3] = 25 if len(vstart) > 5: vstart[5] = 125 lower[5] = 110 upper[5] = 140 vstart[6] = 10 lower[6] = 5 upper[6] = 20 if process == "z": vstart[2] = 90 lower[2] = 70 upper[2] = 110 vstart[3] = 10 lower[3] = 2 upper[3] = 30 if len(vstart) > 5: vstart[5] = 90 lower[5] = 70 upper[5] = 110 vstart[6] = 10 lower[6] = 2 upper[6] = 30 for i in range(parfunction): varname.append("p" + str(i)) for i in range(partot): gMinuit.mnparm(i, varname[i], vstart[i], step[i], lower[i], upper[i], ierflag) # fitting procedure migradstat = gMinuit.Command('MIGrad ' + str(maxiter) + ' ' + str(0.001)) #improvestat = gMinuit.Command('IMProve ' + str(maxiter) + ' ' + str(0.01)) for i in range(partot): arglist_p.append(i + 1) arglist = array.array('d') arglist.fromlist(arglist_p) gMinuit.mnmnos() # get fitting parameters fitval_p = [Double(0)] * partot fiterr_p = [Double(0)] * partot errup_p = [Double(0)] * partot errdown_p = [Double(0)] * partot eparab_p = [Double(0)] * partot gcc_p = [Double(0)] * partot fmin_p = [Double(0)] fedm_p = [Double(0)] errdef_p = [Double(0)] npari_p = Long(0) nparx_p = Long(0) istat_p = Long(0) fitval = array.array('d') fiterr = array.array('d') errup = array.array('d') errdown = array.array('d') eparab = array.array('d') gcc = array.array('d') for i in range(partot): gMinuit.GetParameter(i, fitval_p[i], fiterr_p[i]) fitval.append(fitval_p[i]) fiterr.append(fiterr_p[i]) errup.append(errup_p[i]) errdown.append(errdown_p[i]) eparab.append(eparab_p[i]) gcc.append(gcc_p[i]) gMinuit.mnstat(fmin_p[0], fedm_p[0], errdef_p[0], npari_p, nparx_p, istat_p) for p in range(signalfunction.GetNumberFreeParameters()): signalfunction.SetParameter(p, fitval[p]) print "fit uncert", fiterr_p[p] signalfunction.SetChisquare(fmin_p[0]) print fmin_p[0] return fitval[partot - 1], fitval[partot - 2]
class Minuit(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