Пример #1
0
def main(argv=None):
    parser = argparse.ArgumentParser(
        description=
        "PINT tool for MCMC optimization of timing models using event data from multiple sources."
    )

    parser.add_argument("eventfiles",
                        help="Specify a file listing all event files")
    parser.add_argument("parfile", help="par file to read model from")
    parser.add_argument("--ft2", help="Path to FT2 file.", default=None)
    parser.add_argument("--nwalkers",
                        help="Number of MCMC walkers (def 200)",
                        type=int,
                        default=200)
    parser.add_argument(
        "--burnin",
        help="Number of MCMC steps for burn in (def 100)",
        type=int,
        default=100,
    )
    parser.add_argument(
        "--nsteps",
        help="Number of MCMC steps to compute (def 1000)",
        type=int,
        default=1000,
    )
    parser.add_argument("--minMJD",
                        help="Earliest MJD to use (def 54680)",
                        type=float,
                        default=54680.0)
    parser.add_argument("--maxMJD",
                        help="Latest MJD to use (def 57250)",
                        type=float,
                        default=57250.0)
    parser.add_argument("--phs",
                        help="Starting phase offset [0-1] (def is to measure)",
                        type=float)
    parser.add_argument("--phserr",
                        help="Error on starting phase",
                        type=float,
                        default=0.03)
    parser.add_argument(
        "--minWeight",
        help="Minimum weight to include (def 0.05)",
        type=float,
        default=0.05,
    )
    parser.add_argument(
        "--wgtexp",
        help=
        "Raise computed weights to this power (or 0.0 to disable any rescaling of weights)",
        type=float,
        default=0.0,
    )
    parser.add_argument(
        "--testWeights",
        help="Make plots to evalute weight cuts?",
        default=False,
        action="store_true",
    )
    parser.add_argument(
        "--initerrfact",
        help=
        "Multiply par file errors by this factor when initializing walker starting values",
        type=float,
        default=0.1,
    )
    parser.add_argument(
        "--priorerrfact",
        help=
        "Multiple par file errors by this factor when setting gaussian prior widths",
        type=float,
        default=10.0,
    )
    parser.add_argument(
        "--samples",
        help="Pickle file containing samples from a previous run",
        default=None,
    )

    global nwalkers, nsteps, ftr

    args = parser.parse_args(argv)

    parfile = args.parfile

    if args.ft2 is not None:
        # Instantiate FermiObs once so it gets added to the observatory registry
        FermiObs(name="Fermi", ft2name=args.ft2)

    nwalkers = args.nwalkers
    burnin = args.burnin
    nsteps = args.nsteps
    if burnin >= nsteps:
        log.error("burnin must be < nsteps")
        sys.exit(1)
    nbins = 256  # For likelihood calculation based on gaussians file
    outprof_nbins = 256  # in the text file, for pygaussfit.py, for instance
    minMJD = args.minMJD
    maxMJD = args.maxMJD  # Usually set by coverage of IERS file

    minWeight = args.minWeight
    wgtexp = args.wgtexp

    # Read in initial model
    modelin = pint.models.get_model(parfile)

    # Set the target coords for automatic weighting if necessary
    if "ELONG" in modelin.params:
        tc = SkyCoord(
            modelin.ELONG.quantity,
            modelin.ELAT.quantity,
            frame="barycentrictrueecliptic",
        )
    else:
        tc = SkyCoord(modelin.RAJ.quantity,
                      modelin.DECJ.quantity,
                      frame="icrs")

    eventinfo = load_eventfiles(args.eventfiles,
                                tcoords=tc,
                                minweight=minWeight,
                                minMJD=minMJD,
                                maxMJD=maxMJD)

    nsets = len(eventinfo["toas"])
    log.info("Total number of events:\t%d" %
             np.array([len(t.table) for t in eventinfo["toas"]]).sum())
    log.info("Total number of datasets:\t%d" % nsets)

    funcs = {"prob": lnlikelihood_prob, "resid": lnlikelihood_resid}
    lnlike_funcs = [None] * nsets
    wlist = [None] * nsets
    gtemplates = [None] * nsets

    # Loop over all TOA sets
    for i in range(nsets):
        # Determine lnlikelihood function for this set
        try:
            lnlike_funcs[i] = funcs[eventinfo["lnlikes"][i]]
        except:
            raise ValueError("%s is not a recognized function" %
                             eventinfo["lnlikes"][i])

        # Load in weights
        ts = eventinfo["toas"][i]
        if eventinfo["weightcol"][i] is not None:
            if eventinfo["weightcol"][i] == "CALC":
                weights = np.asarray([x["weight"] for x in ts.table["flags"]])
                log.info(
                    "Original weights have min / max weights %.3f / %.3f" %
                    (weights.min(), weights.max()))
                # Rescale the weights, if requested (by having wgtexp != 0.0)
                if wgtexp != 0.0:
                    weights **= wgtexp
                    wmx, wmn = weights.max(), weights.min()
                    # make the highest weight = 1, but keep min weight the same
                    weights = wmn + ((weights - wmn) * (1.0 - wmn) /
                                     (wmx - wmn))
                for ii, x in enumerate(ts.table["flags"]):
                    x["weight"] = weights[ii]
            weights = np.asarray([x["weight"] for x in ts.table["flags"]])
            log.info(
                "There are %d events, with min / max weights %.3f / %.3f" %
                (len(weights), weights.min(), weights.max()))
        else:
            weights = None
            log.info("There are %d events, no weights are being used." %
                     ts.ntoas)
        wlist[i] = weights

        # Load in templates
        tname = eventinfo["templates"][i]
        if tname == "none":
            continue
        if tname[-6:] == "pickle" or tname == "analytic":
            # Analytic template
            try:
                gtemplate = cPickle.load(file(tname))
            except:
                phases = (modelin.phase(ts)[1]).astype(np.float64)
                phases[phases < 0] += 1 * u.dimensionless_unscaled
                gtemplate = lctemplate.get_gauss2()
                lcf = lcfitters.LCFitter(gtemplate, phases, weights=wlist[i])
                lcf.fit(unbinned=False)
                cPickle.dump(
                    gtemplate,
                    file("%s_template%d.pickle" % (jname, i), "wb"),
                    protocol=2,
                )
            phases = (modelin.phase(ts)[1]).astype(np.float64)
            phases[phases < 0] += 1 * u.dimensionless_unscaled
            lcf = lcfitters.LCFitter(gtemplate,
                                     phases.value,
                                     weights=wlist[i],
                                     binned_bins=200)
            lcf.fit_position(unbinned=False)
            lcf.fit(overall_position_first=True,
                    estimate_errors=False,
                    unbinned=False)
            for prim in lcf.template:
                prim.free[:] = False
            lcf.template.norms.free[:] = False
        else:
            # Binned template
            gtemplate = read_gaussfitfile(tname, nbins)
            gtemplate /= gtemplate.mean()

        gtemplates[i] = gtemplate

    # Set the priors on the parameters in the model, before
    # instantiating the emcee_fitter
    # Currently, this adds a gaussian prior on each parameter
    # with width equal to the par file uncertainty * priorerrfact,
    # and then puts in some special cases.
    # *** This should be replaced/supplemented with a way to specify
    # more general priors on parameters that need certain bounds
    phs = 0.0 if args.phs is None else args.phs

    sampler = EmceeSampler(nwalkers)
    ftr = CompositeMCMCFitter(
        eventinfo["toas"],
        modelin,
        sampler,
        lnlike_funcs,
        templates=gtemplates,
        weights=wlist,
        phs=phs,
        phserr=args.phserr,
        minMJD=minMJD,
        maxMJD=maxMJD,
    )

    fitkeys, fitvals, fiterrs = ftr.get_fit_keyvals()

    # Use this if you want to see the effect of setting minWeight
    if args.testWeights:
        log.info("Checking H-test vs weights")
        ftr.prof_vs_weights(use_weights=True)
        ftr.prof_vs_weights(use_weights=False)
        sys.exit()

    ftr.phaseogram(plotfile=ftr.model.PSR.value + "_pre.png")
    like_start = ftr.lnlikelihood(ftr, ftr.get_parameters())
    log.info("Starting Pulse Likelihood:\t%f" % like_start)

    # Set up the initial conditions for the emcee walkers
    ndim = ftr.n_fit_params
    if args.samples is None:
        pos = None
    else:
        chains = cPickle.load(file(args.samples))
        chains = np.reshape(chains, [nwalkers, -1, ndim])
        pos = chains[:, -1, :]

    ftr.fit_toas(nsteps,
                 pos=pos,
                 priorerrfact=args.priorerrfact,
                 errfact=args.initerrfact)

    def plot_chains(chain_dict, file=False):
        npts = len(chain_dict)
        fig, axes = plt.subplots(npts, 1, sharex=True, figsize=(8, 9))
        for ii, name in enumerate(chain_dict.keys()):
            axes[ii].plot(chain_dict[name], color="k", alpha=0.3)
            axes[ii].set_ylabel(name)
        axes[npts - 1].set_xlabel("Step Number")
        fig.tight_layout()
        if file:
            fig.savefig(file)
            plt.close()
        else:
            plt.show()
            plt.close()

    chains = sampler.chains_to_dict(ftr.fitkeys)
    plot_chains(chains, file=ftr.model.PSR.value + "_chains.png")

    # Make the triangle plot.
    samples = sampler.sampler.chain[:, burnin:, :].reshape(
        (-1, ftr.n_fit_params))
    try:
        import corner

        fig = corner.corner(
            samples,
            labels=ftr.fitkeys,
            bins=50,
            truths=ftr.maxpost_fitvals,
            plot_contours=True,
        )
        fig.savefig(ftr.model.PSR.value + "_triangle.png")
        plt.close()
    except ImportError:
        pass

    # Make a phaseogram with the 50th percentile values
    # ftr.set_params(dict(zip(ftr.fitkeys, np.percentile(samples, 50, axis=0))))
    # Make a phaseogram with the best MCMC result
    ftr.set_parameters(ftr.maxpost_fitvals)
    ftr.phaseogram(plotfile=ftr.model.PSR.value + "_post.png")
    plt.close()

    # Write out the par file for the best MCMC parameter est
    f = open(ftr.model.PSR.value + "_post.par", "w")
    f.write(ftr.model.as_parfile())
    f.close()

    # Print the best MCMC values and ranges
    ranges = map(
        lambda v: (v[1], v[2] - v[1], v[1] - v[0]),
        zip(*np.percentile(samples, [16, 50, 84], axis=0)),
    )
    log.info("Post-MCMC values (50th percentile +/- (16th/84th percentile):")
    for name, vals in zip(ftr.fitkeys, ranges):
        log.info("%8s:" % name + "%25.15g (+ %12.5g  / - %12.5g)" % vals)

    # Put the same stuff in a file
    f = open(ftr.model.PSR.value + "_results.txt", "w")

    f.write("Post-MCMC values (50th percentile +/- (16th/84th percentile):\n")
    for name, vals in zip(ftr.fitkeys, ranges):
        f.write("%8s:" % name + " %25.15g (+ %12.5g  / - %12.5g)\n" % vals)

    f.write("\nMaximum likelihood par file:\n")
    f.write(ftr.model.as_parfile())
    f.close()

    import cPickle

    cPickle.dump(samples, open(ftr.model.PSR.value + "_samples.pickle", "wb"))
Пример #2
0
def test_sampler():
    r = []
    for i in range(2):
        random.seed(0)
        numpy.random.seed(0)
        s = numpy.random.mtrand.RandomState(0)

        parfile = join(datadir, "PSRJ0030+0451_psrcat.par")
        eventfile = join(
            datadir,
            "J0030+0451_P8_15.0deg_239557517_458611204_ft1weights_GEO_wt.gt.0.4.fits",
        )
        gaussianfile = join(datadir, "templateJ0030.3gauss")
        weightcol = "PSRJ0030+0451"

        minWeight = 0.9
        nwalkers = 10
        nsteps = 1
        nbins = 256
        phs = 0.0

        model = pint.models.get_model(parfile)
        tl = fermi.load_Fermi_TOAs(eventfile,
                                   weightcolumn=weightcol,
                                   minweight=minWeight)
        ts = toa.TOAs(toalist=tl)
        # Introduce a small error so that residuals can be calculated
        ts.table["error"] = 1.0
        ts.filename = eventfile
        ts.compute_TDBs()
        ts.compute_posvels(ephem="DE421", planets=False)

        weights, _ = ts.get_flag_value("weight", as_type=float)
        weights = np.array(weights)
        template = read_gaussfitfile(gaussianfile, nbins)
        template /= template.mean()

        sampler = EmceeSampler(nwalkers)
        fitter = MCMCFitterBinnedTemplate(ts,
                                          model,
                                          sampler,
                                          template=template,
                                          weights=weights,
                                          phs=phs)
        fitter.sampler.random_state = s

        # phases = fitter.get_event_phases()
        # maxbin, like_start = marginalize_over_phase(phases, template,
        #                                            weights=fitter.weights,
        #                                            minimize=True,
        #                                            showplot=True)
        # fitter.fitvals[-1] = 1.0 - maxbin[0] / float(len(template))

        # fitter.set_priors(fitter, 10)
        pos = fitter.sampler.get_initial_pos(
            fitter.fitkeys,
            fitter.fitvals,
            fitter.fiterrs,
            0.1,
            minMJD=fitter.minMJD,
            maxMJD=fitter.maxMJD,
        )
        # pos = fitter.clip_template_params(pos)
        fitter.sampler.initialize_sampler(fitter.lnposterior,
                                          fitter.n_fit_params)
        fitter.sampler.run_mcmc(pos, nsteps)
        # fitter.fit_toas(maxiter=nsteps, pos=None)
        # fitter.set_parameters(fitter.maxpost_fitvals)

        # fitter.phaseogram()
        # samples = sampler.sampler.chain[:, 10:, :].reshape((-1, fitter.n_fit_params))

        # r.append(np.random.randn())
        r.append(sampler.sampler.chain[0])
    assert_array_equal(r[0], r[1])
Пример #3
0
def main(argv=None):

    parser = argparse.ArgumentParser(description="PINT tool for MCMC optimization of timing models using event data.")

    parser.add_argument("eventfile",help="event file to use")
    parser.add_argument("parfile",help="par file to read model from")
    parser.add_argument("gaussianfile",help="gaussian file that defines template")
    parser.add_argument("--ft2",help="Path to FT2 file.",default=None)
    parser.add_argument("--weightcol",help="name of weight column (or 'CALC' to have them computed",default=None)
    parser.add_argument("--nwalkers",help="Number of MCMC walkers (def 200)",type=int,
        default=200)
    parser.add_argument("--burnin",help="Number of MCMC steps for burn in (def 100)",
        type=int, default=100)
    parser.add_argument("--nsteps",help="Number of MCMC steps to compute (def 1000)",
        type=int, default=1000)
    parser.add_argument("--minMJD",help="Earliest MJD to use (def 54680)",type=float,
        default=54680.0)
    parser.add_argument("--maxMJD",help="Latest MJD to use (def 57250)",type=float,
        default=57250.0)
    parser.add_argument("--phs",help="Starting phase offset [0-1] (def is to measure)",type=float)
    parser.add_argument("--phserr",help="Error on starting phase",type=float,
        default=0.03)
    parser.add_argument("--minWeight",help="Minimum weight to include (def 0.05)",
        type=float,default=0.05)
    parser.add_argument("--wgtexp",
        help="Raise computed weights to this power (or 0.0 to disable any rescaling of weights)",
        type=float, default=0.0)
    parser.add_argument("--testWeights",help="Make plots to evalute weight cuts?",
        default=False,action="store_true")
    parser.add_argument("--doOpt",help="Run initial scipy opt before MCMC?",
        default=False,action="store_true")
    parser.add_argument("--initerrfact",help="Multiply par file errors by this factor when initializing walker starting values",type=float,default=0.1)
    parser.add_argument("--priorerrfact",help="Multiple par file errors by this factor when setting gaussian prior widths",type=float,default=10.0)
    parser.add_argument("--usepickle",help="Read events from pickle file, if available?",
        default=False,action="store_true")

    global nwalkers, nsteps, ftr

    args = parser.parse_args(argv)

    eventfile = args.eventfile
    parfile = args.parfile
    gaussianfile = args.gaussianfile
    weightcol = args.weightcol

    if args.ft2 is not None:
        # Instantiate FermiObs once so it gets added to the observatory registry
        FermiObs(name='Fermi',ft2name=args.ft2)

    nwalkers = args.nwalkers
    burnin = args.burnin
    nsteps = args.nsteps
    if burnin >= nsteps:
        log.error('burnin must be < nsteps')
        sys.exit(1)
    nbins = 256 # For likelihood calculation based on gaussians file
    outprof_nbins = 256 # in the text file, for pygaussfit.py, for instance
    minMJD = args.minMJD
    maxMJD = args.maxMJD # Usually set by coverage of IERS file

    minWeight = args.minWeight
    do_opt_first = args.doOpt
    wgtexp = args.wgtexp


    # Read in initial model
    modelin = pint.models.get_model(parfile)

    # The custom_timing version below is to manually construct the TimingModel
    # class, which allows it to be pickled. This is needed for parallelizing
    # the emcee call over a number of threads.  So far, it isn't quite working
    # so it is disabled.  The code above constructs the TimingModel class
    # dynamically, as usual.
    #modelin = custom_timing(parfile)

    # Remove the dispersion delay as it is unnecessary
    #modelin.delay_funcs['L1'].remove(modelin.dispersion_delay)
    # Set the target coords for automatic weighting if necessary
    if 'ELONG' in modelin.params:
        tc = SkyCoord(modelin.ELONG.quantity,modelin.ELAT.quantity,
            frame='barycentrictrueecliptic')
    else:
        tc = SkyCoord(modelin.RAJ.quantity,modelin.DECJ.quantity,frame='icrs')

    target = tc if weightcol=='CALC' else None

    # TODO: make this properly handle long double
    if not args.usepickle or (not (os.path.isfile(eventfile+".pickle") or
        os.path.isfile(eventfile+".pickle.gz"))):
        # Read event file and return list of TOA objects
        tl = fermi.load_Fermi_TOAs(eventfile, weightcolumn=weightcol,
                                   targetcoord=target, minweight=minWeight)
        # Limit the TOAs to ones in selected MJD range and above minWeight
        tl = [tl[ii] for ii in range(len(tl)) if (tl[ii].mjd.value > minMJD and tl[ii].mjd.value < maxMJD
            and (weightcol is None or tl[ii].flags['weight'] > minWeight))]
        log.info("There are %d events we will use" % len(tl))
        # Now convert to TOAs object and compute TDBs and posvels
        ts = toa.TOAs(toalist=tl)
        ts.filename = eventfile
        ts.compute_TDBs()
        ts.compute_posvels(ephem="DE421", planets=False)
        ts.pickle()
    else:  # read the events in as a pickle file
        picklefile = toa._check_pickle(eventfile)
        if not picklefile:
            picklefile = eventfile
        ts = toa.TOAs(picklefile)

    if weightcol is not None:
        if weightcol=='CALC':
            weights = np.asarray([x['weight'] for x in ts.table['flags']])
            log.info("Original weights have min / max weights %.3f / %.3f" % \
                (weights.min(), weights.max()))
            # Rescale the weights, if requested (by having wgtexp != 0.0)
            if wgtexp != 0.0:
                weights **= wgtexp
                wmx, wmn = weights.max(), weights.min()
                # make the highest weight = 1, but keep min weight the same
                weights = wmn + ((weights - wmn) * (1.0 - wmn) / (wmx - wmn))
            for ii, x in enumerate(ts.table['flags']):
                x['weight'] = weights[ii]
        weights = np.asarray([x['weight'] for x in ts.table['flags']])
        log.info("There are %d events, with min / max weights %.3f / %.3f" % \
            (len(weights), weights.min(), weights.max()))
    else:
        weights = None
        log.info("There are %d events, no weights are being used." % ts.ntoas)

    # Now load in the gaussian template and normalize it
    gtemplate = read_gaussfitfile(gaussianfile, nbins)
    gtemplate /= gtemplate.mean()

    # Set the priors on the parameters in the model, before
    # instantiating the emcee_fitter
    # Currently, this adds a gaussian prior on each parameter
    # with width equal to the par file uncertainty * priorerrfact,
    # and then puts in some special cases.
    # *** This should be replaced/supplemented with a way to specify
    # more general priors on parameters that need certain bounds
    phs = 0.0 if args.phs is None else args.phs
    
    sampler = EmceeSampler(nwalkers)
    ftr = MCMCFitterBinnedTemplate(ts, modelin, sampler, template=gtemplate, \
        weights=weights, phs=phs, phserr=args.phserr, minMJD=minMJD, maxMJD=maxMJD)

    fitkeys, fitvals, fiterrs = ftr.get_fit_keyvals()

    # Use this if you want to see the effect of setting minWeight
    if args.testWeights:
        log.info("Checking H-test vs weights")
        ftr.prof_vs_weights(use_weights=True)
        ftr.prof_vs_weights(use_weights=False)
        sys.exit()

    # Now compute the photon phases and see if we see a pulse
    phss = ftr.get_event_phases()
    maxbin, like_start = marginalize_over_phase(phss, gtemplate,
        weights=ftr.weights, minimize=True, showplot=False)
    log.info("Starting pulse likelihood: %f" % like_start)

    if args.phs is None:
        fitvals[-1] = 1.0 - maxbin[0] / float(len(gtemplate))
        if fitvals[-1] > 1.0: fitvals[-1] -= 1.0
        if fitvals[-1] < 0.0: fitvals[-1] += 1.0
        log.info("Starting pulse phase: %f" % fitvals[-1])
    else:
        log.info("Measured starting pulse phase is %f, but using %f" % \
            (1.0 - maxbin / float(len(gtemplate)), args.phs))
        fitvals[-1] = args.phs
    ftr.fitvals[-1] = fitvals[-1]
    ftr.phaseogram(plotfile=ftr.model.PSR.value+"_pre.png")
    plt.close()

    # Write out the starting pulse profile
    vs, xs = np.histogram(ftr.get_event_phases(), outprof_nbins, \
        range=[0,1], weights=ftr.weights)
    f = open(ftr.model.PSR.value+"_prof_pre.txt", 'w')
    for x, v in zip(xs, vs):
        f.write("%.5f  %12.5f\n" % (x, v))
    f.close()

    # Try normal optimization first to see how it goes
    if do_opt_first:
        result = op.minimize(ftr.minimize_func, np.zeros_like(ftr.fitvals))
        newfitvals = np.asarray(result['x']) * ftr.fiterrs + ftr.fitvals
        like_optmin = -result['fun']
        log.info("Optimization likelihood: %f" % like_optmin)
        ftr.set_params(dict(zip(ftr.fitkeys, newfitvals)))
        ftr.phaseogram()
    else:
        like_optmin = -np.inf

    # Set up the initial conditions for the emcee walkers.  Use the
    # scipy.optimize newfitvals instead if they are better
    ndim = ftr.n_fit_params
    if like_start > like_optmin:
        pos = None
    else:
        pos = [newfitvals + ftr.fiterrs*args.initerrfact*np.random.randn(ndim)
            for i in range(nwalkers)]
        pos[0] = ftr.fitvals
    
    ftr.fit_toas(maxiter=nsteps, pos=pos, 
        priorerrfact=args.priorerrfact, errfact=args.initerrfact)

    def plot_chains(chain_dict, file=False):
        npts = len(chain_dict)
        fig, axes = plt.subplots(npts, 1, sharex=True, figsize=(8, 9))
        for ii, name in enumerate(chain_dict.keys()):
            axes[ii].plot(chain_dict[name], color="k", alpha=0.3)
            axes[ii].set_ylabel(name)
        axes[npts-1].set_xlabel("Step Number")
        fig.tight_layout()
        if file:
            fig.savefig(file)
            plt.close()
        else:
            plt.show()
            plt.close()

    chains = sampler.chains_to_dict(ftr.fitkeys)
    plot_chains(chains, file=ftr.model.PSR.value+"_chains.png")

    # Make the triangle plot.
    samples = sampler.sampler.chain[:, burnin:, :].reshape((-1, ftr.n_fit_params))
    try:
        import corner
        fig = corner.corner(samples, labels=ftr.fitkeys, bins=50,
            truths=ftr.maxpost_fitvals, plot_contours=True)
        fig.savefig(ftr.model.PSR.value+"_triangle.png")
        plt.close()
    except ImportError:
        pass

    # Make a phaseogram with the 50th percentile values
    #ftr.set_params(dict(zip(ftr.fitkeys, np.percentile(samples, 50, axis=0))))
    # Make a phaseogram with the best MCMC result
    ftr.set_params(dict(zip(ftr.fitkeys[:-1], ftr.maxpost_fitvals[:-1])))
    ftr.phaseogram(plotfile=ftr.model.PSR.value+"_post.png")
    plt.close()

    # Write out the output pulse profile
    vs, xs = np.histogram(ftr.get_event_phases(), outprof_nbins, \
        range=[0,1], weights=ftr.weights)
    f = open(ftr.model.PSR.value+"_prof_post.txt", 'w')
    for x, v in zip(xs, vs):
        f.write("%.5f  %12.5f\n" % (x, v))
    f.close()

    # Write out the par file for the best MCMC parameter est
    f = open(ftr.model.PSR.value+"_post.par", 'w')
    f.write(ftr.model.as_parfile())
    f.close()

    # Print the best MCMC values and ranges
    ranges = map(lambda v: (v[1], v[2]-v[1], v[1]-v[0]),
        zip(*np.percentile(samples, [16, 50, 84], axis=0)))
    log.info("Post-MCMC values (50th percentile +/- (16th/84th percentile):")
    for name, vals in zip(ftr.fitkeys, ranges):
        log.info("%8s:"%name + "%25.15g (+ %12.5g  / - %12.5g)"%vals)

    # Put the same stuff in a file
    f = open(ftr.model.PSR.value+"_results.txt", 'w')

    f.write("Post-MCMC values (50th percentile +/- (16th/84th percentile):\n")
    for name, vals in zip(ftr.fitkeys, ranges):
        f.write("%8s:"%name + " %25.15g (+ %12.5g  / - %12.5g)\n"%vals)

    f.write("\nMaximum likelihood par file:\n")
    f.write(ftr.model.as_parfile())
    f.close()

    import cPickle
    cPickle.dump(samples, open(ftr.model.PSR.value+"_samples.pickle", "wb"))
Пример #4
0
def main(argv=None):
    parser = argparse.ArgumentParser(description="PINT tool for MCMC optimization of timing models using event data from multiple sources.")

    parser.add_argument("eventfiles",help="Specify a file listing all event files")
    parser.add_argument("parfile",help="par file to read model from")
    parser.add_argument("--ft2",help="Path to FT2 file.",default=None)
    parser.add_argument("--nwalkers",help="Number of MCMC walkers (def 200)",type=int,
        default=200)
    parser.add_argument("--burnin",help="Number of MCMC steps for burn in (def 100)",
        type=int, default=100)
    parser.add_argument("--nsteps",help="Number of MCMC steps to compute (def 1000)",
        type=int, default=1000)
    parser.add_argument("--minMJD",help="Earliest MJD to use (def 54680)",type=float,
        default=54680.0)
    parser.add_argument("--maxMJD",help="Latest MJD to use (def 57250)",type=float,
        default=57250.0)
    parser.add_argument("--phs",help="Starting phase offset [0-1] (def is to measure)",type=float)
    parser.add_argument("--phserr",help="Error on starting phase",type=float,
        default=0.03)
    parser.add_argument("--minWeight",help="Minimum weight to include (def 0.05)",
        type=float,default=0.05)
    parser.add_argument("--wgtexp",
        help="Raise computed weights to this power (or 0.0 to disable any rescaling of weights)",
        type=float, default=0.0)
    parser.add_argument("--testWeights",help="Make plots to evalute weight cuts?",
        default=False,action="store_true")
    parser.add_argument("--initerrfact",help="Multiply par file errors by this factor when initializing walker starting values",type=float,default=0.1)
    parser.add_argument("--priorerrfact",help="Multiple par file errors by this factor when setting gaussian prior widths",type=float,default=10.0)
    parser.add_argument("--samples",help="Pickle file containing samples from a previous run",
        default=None)

    global nwalkers, nsteps, ftr

    args = parser.parse_args(argv)

    parfile = args.parfile

    if args.ft2 is not None:
        # Instantiate FermiObs once so it gets added to the observatory registry
        FermiObs(name='Fermi',ft2name=args.ft2)

    nwalkers = args.nwalkers
    burnin = args.burnin
    nsteps = args.nsteps
    if burnin >= nsteps:
        log.error('burnin must be < nsteps')
        sys.exit(1)
    nbins = 256 # For likelihood calculation based on gaussians file
    outprof_nbins = 256 # in the text file, for pygaussfit.py, for instance
    minMJD = args.minMJD
    maxMJD = args.maxMJD # Usually set by coverage of IERS file

    minWeight = args.minWeight
    wgtexp = args.wgtexp

    # Read in initial model
    modelin = pint.models.get_model(parfile)

    # Set the target coords for automatic weighting if necessary
    if 'ELONG' in modelin.params:
        tc = SkyCoord(modelin.ELONG.quantity,modelin.ELAT.quantity,
            frame='barycentrictrueecliptic')
    else:
        tc = SkyCoord(modelin.RAJ.quantity,modelin.DECJ.quantity,frame='icrs')

    eventinfo = load_eventfiles(args.eventfiles, tcoords=tc, minweight=minWeight,
                                minMJD=minMJD, maxMJD=maxMJD)

    nsets = len(eventinfo['toas'])
    log.info('Total number of events:\t%d' % np.array([len(t.table) 
        for t in eventinfo['toas']]).sum())
    log.info('Total number of datasets:\t%d' % nsets)

    funcs = {'prob' : lnlikelihood_prob,
             'resid' : lnlikelihood_resid}
    lnlike_funcs = [None] * nsets
    wlist = [None] * nsets
    gtemplates = [None] * nsets

    #Loop over all TOA sets
    for i in range(nsets):
        #Determine lnlikelihood function for this set
        try:
            lnlike_funcs[i] = funcs[eventinfo['lnlikes'][i]]
        except:
            raise ValueError('%s is not a recognized function' % eventinfo['lnlikes'][i])

        #Load in weights
        ts = eventinfo['toas'][i]
        if eventinfo['weightcol'][i] is not None:
            if eventinfo['weightcol'][i] == 'CALC':
                weights = np.asarray([x['weight'] for x in ts.table['flags']])
                log.info("Original weights have min / max weights %.3f / %.3f" % \
                    (weights.min(), weights.max()))
                # Rescale the weights, if requested (by having wgtexp != 0.0)
                if wgtexp != 0.0:
                    weights **= wgtexp
                    wmx, wmn = weights.max(), weights.min()
                    # make the highest weight = 1, but keep min weight the same
                    weights = wmn + ((weights - wmn) * (1.0 - wmn) / (wmx - wmn))
                for ii, x in enumerate(ts.table['flags']):
                    x['weight'] = weights[ii]
            weights = np.asarray([x['weight'] for x in ts.table['flags']])
            log.info("There are %d events, with min / max weights %.3f / %.3f" % \
                (len(weights), weights.min(), weights.max()))
        else:
            weights = None
            log.info("There are %d events, no weights are being used." % ts.ntoas)
        wlist[i] = weights

        #Load in templates
        tname = eventinfo['templates'][i]
        if tname == 'none':
            continue
        if tname[-6:] == 'pickle' or tname == 'analytic':
            #Analytic template
            try:
                gtemplate = cPickle.load(file(tname))
            except:
                phases = (modelin.phase(ts)[1]).astype(np.float64)
                phases[phases < 0] += 1 * u.cycle
                gtemplate = lctemplate.get_gauss2()
                lcf = lcfitters.LCFitter(gtemplate, phases, weights=wlist[i])
                lcf.fit(unbinned=False)
                cPickle.dump(gtemplate,
                    file('%s_template%d.pickle'%(jname, i), 'wb'),protocol=2)
            phases = (modelin.phase(ts)[1]).astype(np.float64)
            phases[phases <0] += 1*u.cycle
            lcf = lcfitters.LCFitter(
                    gtemplate,phases.value,weights=wlist[i],binned_bins=200)
            lcf.fit_position(unbinned=False)
            lcf.fit(overall_position_first=True,
                    estimate_errors=False,unbinned=False)
            for prim in lcf.template:
                prim.free[:] = False
            lcf.template.norms.free[:] = False
        else:
            #Binned template
            gtemplate = read_gaussfitfile(tname, nbins)
            gtemplate /= gtemplate.mean()
        
        gtemplates[i] = gtemplate

    # Set the priors on the parameters in the model, before
    # instantiating the emcee_fitter
    # Currently, this adds a gaussian prior on each parameter
    # with width equal to the par file uncertainty * priorerrfact,
    # and then puts in some special cases.
    # *** This should be replaced/supplemented with a way to specify
    # more general priors on parameters that need certain bounds
    phs = 0.0 if args.phs is None else args.phs
    
    sampler = EmceeSampler(nwalkers)
    ftr = CompositeMCMCFitter(eventinfo['toas'], modelin, sampler, lnlike_funcs,
                              templates=gtemplates, weights=wlist,
                              phs=phs, phserr=args.phserr,
                              minMJD=minMJD, maxMJD=maxMJD)

    fitkeys, fitvals, fiterrs = ftr.get_fit_keyvals()

    # Use this if you want to see the effect of setting minWeight
    if args.testWeights:
        log.info("Checking H-test vs weights")
        ftr.prof_vs_weights(use_weights=True)
        ftr.prof_vs_weights(use_weights=False)
        sys.exit()

    ftr.phaseogram(plotfile=ftr.model.PSR.value+"_pre.png")
    like_start = ftr.lnlikelihood(ftr, ftr.get_parameters())
    log.info('Starting Pulse Likelihood:\t%f' % like_start)

    # Set up the initial conditions for the emcee walkers
    ndim = ftr.n_fit_params
    if args.samples is None:
        pos = None
    else:
        chains = cPickle.load(file(args.samples))
        chains = np.reshape(chains, [nwalkers, -1, ndim])
        pos = chains[:, -1, :]

    ftr.fit_toas(nsteps, pos=pos, priorerrfact=args.priorerrfact, errfact=args.initerrfact)

    def plot_chains(chain_dict, file=False):
        npts = len(chain_dict)
        fig, axes = plt.subplots(npts, 1, sharex=True, figsize=(8, 9))
        for ii, name in enumerate(chain_dict.keys()):
            axes[ii].plot(chain_dict[name], color="k", alpha=0.3)
            axes[ii].set_ylabel(name)
        axes[npts-1].set_xlabel("Step Number")
        fig.tight_layout()
        if file:
            fig.savefig(file)
            plt.close()
        else:
            plt.show()
            plt.close()

    chains = sampler.chains_to_dict(ftr.fitkeys)
    plot_chains(chains, file=ftr.model.PSR.value+"_chains.png")

    # Make the triangle plot.
    samples = sampler.sampler.chain[:, burnin:, :].reshape((-1, ftr.n_fit_params))
    try:
        import corner
        fig = corner.corner(samples, labels=ftr.fitkeys, bins=50,
            truths=ftr.maxpost_fitvals, plot_contours=True)
        fig.savefig(ftr.model.PSR.value+"_triangle.png")
        plt.close()
    except ImportError:
        pass

    # Make a phaseogram with the 50th percentile values
    #ftr.set_params(dict(zip(ftr.fitkeys, np.percentile(samples, 50, axis=0))))
    # Make a phaseogram with the best MCMC result
    ftr.set_parameters(ftr.maxpost_fitvals)
    ftr.phaseogram(plotfile=ftr.model.PSR.value+"_post.png")
    plt.close()

    # Write out the par file for the best MCMC parameter est
    f = open(ftr.model.PSR.value+"_post.par", 'w')
    f.write(ftr.model.as_parfile())
    f.close()

    # Print the best MCMC values and ranges
    ranges = map(lambda v: (v[1], v[2]-v[1], v[1]-v[0]),
        zip(*np.percentile(samples, [16, 50, 84], axis=0)))
    log.info("Post-MCMC values (50th percentile +/- (16th/84th percentile):")
    for name, vals in zip(ftr.fitkeys, ranges):
        log.info("%8s:"%name + "%25.15g (+ %12.5g  / - %12.5g)"%vals)

    # Put the same stuff in a file
    f = open(ftr.model.PSR.value+"_results.txt", 'w')

    f.write("Post-MCMC values (50th percentile +/- (16th/84th percentile):\n")
    for name, vals in zip(ftr.fitkeys, ranges):
        f.write("%8s:"%name + " %25.15g (+ %12.5g  / - %12.5g)\n"%vals)

    f.write("\nMaximum likelihood par file:\n")
    f.write(ftr.model.as_parfile())
    f.close()

    import cPickle
    cPickle.dump(samples, open(ftr.model.PSR.value+"_samples.pickle", "wb"))