Revision 39677017583cb3b2655f1af88a1ceb84f688dd64 authored by Tito Dal Canton on 06 March 2017, 20:10:55 UTC, committed by GitHub on 06 March 2017, 20:10:55 UTC
1 parent 096e492
Raw File
inference.rst
###################################################################
PyCBC inference documentation (``pycbc.inference``)
###################################################################

===================
Introduction
===================

This page gives details on how to use the various parameter estimation
executables and modules available in PyCBC. The ``pycbc.inference`` subpackage
contains classes and functions for evaluating probability distributions,
likelihoods, and running Bayesian samplers.

==================================================
Sampling the parameter space (``pycbc_inference``)
==================================================

--------
Overview
--------

The executable ``pycbc_inference`` is designed to sample the parameter space
and save the samples in an HDF file. A high-level description of the
``pycbc_inference`` algorithm is

#. Estimate a PSD from a model or data.

#. Read gravitational-wave strain from a gravitational-wave model or use recolored fake strain.

#. Read priors from configuration file.

#. Construct prior-weighted likelihood function from PSD, gravitational-wave strain, and priors.

#. Run sampler that walks around parameter space and calculates the prior-weighted likelihood function.

---------------------------------------------------
Options for samplers, likelihood models, and priors
---------------------------------------------------

For a full listing of all options run ``pycbc_inference --help``. In this subsection we reference documentation for Python classes that contain more information about choices for samplers, likelihood models, and priors.

The user specifies the sampler on the command line with the ``--sampler`` option.
A complete list of samplers is given in ``pycbc_inference --help``.
These samplers are described in :py:class:`pycbc.inference.sampler_kombine.KombineSampler`, :py:class:`pycbc.inference.sampler_emcee.EmceeEnsembleSampler`, and :py:class:`pycbc.inference.sampler_emcee.EmceePTSampler`.
In addition to ``--sampler`` the user will need to specify the number of walkers to use ``--nwalkers``, the number of iterations to go until ``--niterations``, and for parallel-tempered samplers the number of temperatures ``--ntemps``.
If the sampler has a built-in burn-in function it will be used by default, otherwise you can skill the burn-in with ``--skip-burn-in`` or set a minimum number of iterations for burn-in with ``--min-burn-in``.

The user specifies the likelihood model on the command line with the ``--likelihood`` option.
At the moment there is only a single choice ``--likelihood gaussian`` that is described in :py:class:`pycbc.inference.likelihood.GaussianLikelihood`.

The user specifies a configuration file that defines the priors with the ``--config-files`` option.
The syntax of the configuration file is described in the subsection below.

-------------------------
Configuration file syntax
-------------------------

Configuration files follow the ``ConfigParser`` syntax.
There are two required sections.
One is a ``[variable_args]`` section that contains a list of varying parameters and the other is ``[static_args]`` section that contains a list of parameters that do not vary.

A list of all parameters that can be used is found with

.. literalinclude:: ../examples/inference/list_parameters.py
.. command-output:: python ../examples/inference/list_parameters.py

The mass parameters ``mass1`` and ``mass2`` can be substituted for ``mchirp`` and ``eta``, or ``mchirp`` and ``q``.
The component spin parameters ``spin1x``, ``spin1y``, and ``spin1z`` can be substituted for polar coordinates ``spin1_a``, ``spin1_azimuthal``, and ``spin1_polar``.

Each parameter in ``[variable_args]`` must have a subsection in ``[prior]``.
To create a subsection use the ``-`` char, eg. for chirp mass do ``[prior-mchirp]``.

Each prior subsection must have a ``name`` option that identifies what prior to use.
These distributions are described in :py:mod:`pycbc.inference.distributions`.
A list of all distributions that can be used is found with

.. literalinclude:: ../examples/inference/list_distributions.py
.. command-output:: python ../examples/inference/list_distributions.py

A simple example is given in the subsection below.

------------------------------
BBH software injection example
------------------------------

This example recovers the parameters of a precessing binary black-hole (BBH).

An example configuration file (named ``inference.ini``) is::

    [variable_args]
    ; waveform parameters that will vary in MCMC
    tc =
    mchirp =
    q =
    spin1_a =
    spin1_azimuthal =
    spin1_polar =
    spin2_a =
    spin2_azimuthal =
    spin2_polar =
    distance =
    coa_phase =
    inclination =
    polarization =
    ra =
    dec =

    [static_args]
    ; waveform parameters that will not change in MCMC
    approximant = IMRPhenomPv2
    f_lower = 19.0

    [prior-tc]
    ; coalescence time prior
    name = uniform
    min-tc = 1126259461.8
    max-tc= 1126259462.2

    [prior-mchirp]
    name = uniform
    min-mchirp = 7.
    max-mchirp = 40.

    [prior-q]
    name = uniform
    min-q = 1.
    max-q = 5.

    [prior-spin1_a]
    name = uniform
    min-spin1_a = 0.0
    max-spin1_a = 0.9

    [prior-spin1_azimuthal]
    name = uniform
    min-spin1_azimuthal = 0.
    max-spin1_azimuthal = 6.283185307179586

    [prior-spin1_polar]
    name = sin_angle

    [prior-spin2_a]
    name = uniform
    min-spin2_a = 0.0
    max-spin2_a = 0.9

    [prior-spin2_azimuthal]
    name = uniform
    min-spin2_azimuthal = 0.
    max-spin2_azimuthal = 6.283185307179586

    [prior-spin2_polar]
    name = sin_angle

    [prior-distance]
    ; distance prior
    name = uniform
    min-distance = 10
    max-distance = 500

    [prior-coa_phase]
    ; coalescence phase prior
    name = uniform_angle

    [prior-inclination]
    ; inclination prior
    name = uniform_angle
    min-inclination = 0
    max-inclination = 1

    [prior-ra+dec]
    ; sky position prior
    name = uniform_sky

    [prior-polarization]
    ; polarization prior
    name = uniform_angle

An example of generating an injection::

    # define waveform parameters
    TRIGGER_TIME=1126259462.0
    INJ_APPROX=SEOBNRv2threePointFivePN
    MASS1=37.
    MASS2=32.
    RA=2.21535724066
    DEC=-1.23649695537
    INC=2.5
    COA_PHASE=1.5
    POLARIZATION=1.75
    DISTANCE=100000 # in kpc
    INJ_F_MIN=28.
    TAPER="start"

    # path of injection file that will be created in the example
    INJ_PATH=injection.xml.gz

    # lalapps_inspinj requires degrees on the command line
    LONGITUDE=`python -c "import numpy; print ${RA} * 180/numpy.pi"`
    LATITUDE=`python -c "import numpy; print ${DEC} * 180/numpy.pi"`
    INC=`python -c "import numpy; print ${INC} * 180/numpy.pi"`
    POLARIZATION=`python -c "import numpy; print ${POLARIZATION} * 180/numpy.pi"`
    COA_PHASE=`python -c "import numpy; print ${COA_PHASE} * 180/numpy.pi"`

    # create injection file
    lalapps_inspinj \
        --output ${INJ_PATH} \
        --seed 1000 \
        --f-lower ${INJ_F_MIN} \
        --waveform ${INJ_APPROX} \
        --amp-order 7 \
        --gps-start-time ${TRIGGER_TIME} \
        --gps-end-time ${TRIGGER_TIME} \
        --time-step 1 \
        --t-distr fixed \
        --l-distr fixed \
        --longitude ${LONGITUDE} \
        --latitude ${LATITUDE} \
        --d-distr uniform \
        --min-distance ${DISTANCE} \
        --max-distance ${DISTANCE} \
        --i-distr fixed \
        --fixed-inc ${INC} \
        --coa-phase-distr fixed \
        --fixed-coa-phase ${COA_PHASE} \
        --polarization ${POLARIZATION} \
        --m-distr fixMasses \
        --fixed-mass1 ${MASS1} \
        --fixed-mass2 ${MASS2} \
        --taper-injection ${TAPER} \
        --disable-spin

An example of running ``pycbc_inference`` to analyze the injection in fake data::

    # injection parameters
    TRIGGER_TIME=1126259462.0
    INJ_PATH=injection.xml.gz

    # sampler parameters
    CONFIG_PATH=inference.ini
    OUTPUT_PATH=inference.hdf
    SEGLEN=8
    PSD_INVERSE_LENGTH=4
    IFOS="H1 L1"
    STRAIN="H1:aLIGOZeroDetHighPower L1:aLIGOZeroDetHighPower"
    SAMPLE_RATE=2048
    F_MIN=30.
    N_UPDATE=500
    N_WALKERS=5000
    N_ITERATIONS=12000
    N_CHECKPOINT=1000
    PROCESSING_SCHEME=cpu
    NPROCS=12

    # get coalescence time as an integer
    TRIGGER_TIME_INT=${TRIGGER_TIME%.*}

    # start and end time of data to read in
    GPS_START_TIME=$((${TRIGGER_TIME_INT} - ${SEGLEN}))
    GPS_END_TIME=$((${TRIGGER_TIME_INT} + ${SEGLEN}))

    # run sampler
    # specifies the number of threads for OpenMP
    # Running with OMP_NUM_THREADS=1 stops lalsimulation
    # to spawn multiple jobs that would otherwise be used
    # by pycbc_inference and cause a reduced runtime.
    OMP_NUM_THREADS=1 \
    pycbc_inference --verbose \
        --seed 12 \
        --instruments ${IFOS} \
        --gps-start-time ${GPS_START_TIME} \
        --gps-end-time ${GPS_END_TIME} \
        --psd-model ${STRAIN} \
        --psd-inverse-length ${PSD_INVERSE_LENGTH} \
        --fake-strain ${STRAIN} \
        --fake-strain-seed 44 \
        --sample-rate ${SAMPLE_RATE} \
        --low-frequency-cutoff ${F_MIN} \
        --channel-name H1:FOOBAR L1:FOOBAR \
        --injection-file ${INJ_PATH} \
        --config-file ${CONFIG_PATH} \
        --output-file ${OUTPUT_PATH} \
        --processing-scheme ${PROCESSING_SCHEME} \
        --sampler kombine \
        --skip-burn-in \
        --update-interval ${N_UPDATE} \
        --likelihood-evaluator gaussian \
        --nwalkers ${N_WALKERS} \
        --niterations ${N_ITERATIONS} \
        --checkpoint-interval ${N_CHECKPOINT} \
        --checkpoint-fast \
        --nprocesses ${NPROCS} \
        --save-strain \
        --save-psd \
        --save-stilde \
        --force

----------------
GW150914 example
----------------

You can reuse ``inference.ini`` from the previous example to analyze a signal in real data::

    # trigger parameters
    TRIGGER_TIME=1126259462.0

    # data to use
    # the longest waveform covered by the prior must fit in these times
    SEARCH_BEFORE=6
    SEARCH_AFTER=2

    # use an extra number of seconds of data in addition to the data specified
    PAD_DATA=8

    # PSD estimation options
    PSD_ESTIMATION="H1:median L1:median"
    PSD_INVLEN=4
    PSD_SEG_LEN=8
    PSD_STRIDE=4
    PSD_DATA_LEN=1024

    # frame type and channel
    FRAMES="H1:H1_HOFT_C02 L1:L1_HOFT_C02"
    CHANNELS="H1:H1:DCS-CALIB_STRAIN_C02 L1:L1:DCS-CALIB_STRAIN_C02"

    # sampler parameters
    CONFIG_PATH=inference.ini
    OUTPUT_PATH=inference.hdf
    SEGLEN=8
    IFOS="H1 L1"
    STRAIN="H1:aLIGOZeroDetHighPower L1:aLIGOZeroDetHighPower"
    SAMPLE_RATE=2048
    F_HIGHPASS=20
    F_MIN=30.
    N_UPDATE=500
    N_WALKERS=5000
    N_ITERATIONS=12000
    N_CHECKPOINT=1000
    PROCESSING_SCHEME=cpu
    NPROCS=12

    # get coalescence time as an integer
    TRIGGER_TIME_INT=${TRIGGER_TIME%.*}

    # start and end time of data to read in
    GPS_START_TIME=$((${TRIGGER_TIME_INT} - ${SEGLEN}))
    GPS_END_TIME=$((${TRIGGER_TIME_INT} + ${SEGLEN}))

    # start and end time of data to read in for PSD estimation
    PSD_START_TIME=$((${GPS_START_TIME} - ${PSD_DATA_LEN}/2))
    PSD_END_TIME=$((${GPS_END_TIME} + ${PSD_DATA_LEN}/2))

    # run sampler
    # specifies the number of threads for OpenMP
    # Running with OMP_NUM_THREADS=1 stops lalsimulation
    # to spawn multiple jobs that would otherwise be used
    # by pycbc_inference and cause a reduced runtime.
    OMP_NUM_THREADS=1 \
    pycbc_inference --verbose \
        --seed 12 \
        --instruments ${IFOS} \
        --gps-start-time ${GPS_START_TIME} \
        --gps-end-time ${GPS_END_TIME} \
        --channel-name ${CHANNELS} \
        --frame-type ${FRAMES} \
        --strain-high-pass ${F_HIGHPASS} \
        --pad-data ${PAD_DATA} \
        --psd-estimation ${PSD_ESTIMATION} \
        --psd-start-time ${PSD_START_TIME} \
        --psd-end-time ${PSD_END_TIME} \
        --psd-segment-length ${PSD_SEG_LEN} \
        --psd-segment-stride ${PSD_STRIDE} \
        --psd-inverse-length ${PSD_INVLEN} \
        --sample-rate ${SAMPLE_RATE} \
        --low-frequency-cutoff ${F_MIN} \
        --config-file ${CONFIG_PATH} \
        --output-file ${OUTPUT_PATH} \
        --processing-scheme ${PROCESSING_SCHEME} \
        --sampler kombine \
        --skip-burn-in \
        --update-interval ${N_UPDATE} \
        --likelihood-evaluator gaussian \
        --nwalkers ${N_WALKERS} \
        --niterations ${N_ITERATIONS} \
        --checkpoint-interval ${N_CHECKPOINT} \
        --checkpoint-fast \
        --nprocesses ${NPROCS} \
        --save-strain \
        --save-psd \
        --save-stilde \
        --force

To get data we used ``--frame-type`` which will query the LIGO Data
Replicator (LDR) server to locate the frame files for us. You can also
use ``--frame-files`` or ``--frame-cache`` if you have a list or LAL cache
file of frames you wish to use.

----------------------------------------------------
HDF output file handler (``pycbc.io.InferenceFile``)
----------------------------------------------------

The executable ``pycbc_inference`` will write a HDF file with all the samples from each walker along with the PSDs and some meta-data about the sampler.
There is a handler class ``pycbc.io.InferenceFile`` that extends ``h5py.File``.
To read the output file you can do::

    from pycbc.io import InferenceFile
    fp = InferenceFile("cbc_example-n1e4.hdf", "r")

To get all samples for ``distance`` from the first walker you can do::

    samples = fp.read_samples("distance", walkers=0)
    print samples.distance

The function ``InferenceFile.read_samples`` includes the options to thin the samples.
By default the function will return samples beginning at the end of the burn-in to the last written sample, and will use the autocorrelation length (ACL) calcualted by ``pycbc_inference`` to select the indepdedent samples.
You can supply ``thin_start``, ``thin_end``, and ``thin_interval`` to override this. To read all samples you would do::

    samples = fp.read_samples("distance", walkers=0, thin_start=0, thin_end=-1, thin_interval=1)
    print samples.distance

Some standard parameters that are derived from the variable arguments (listed via ``fp.variable_args``) can also be retrieved. For example, if ``fp.variable_args`` includes ``mass1`` and ``mass2``, then you can retrieve the chirp mass with::

   samples = samples = fp.read_samples("mchirp")
   print samples.mchirp

In this case, ``fp.read_samples`` will retrieve ``mass1`` and ``mass2`` (since they are needed to compute chirp mass); ``samples.mchirp`` then returns an array of the chirp mass computed from ``mass1`` and ``mass2``.

For more information, including the list of predefined derived parameters, see :py:class:`pycbc.io.InferenceFile`.

=============================================================
Plotting the posteriors (``pycbc_inference_plot_posteriors``)
=============================================================

--------
Overview
--------

There is an executable that can plot the posteriors called
``pycbc_inference_plot_posteriors``. You can use ``--plot-scatter``
to plot a each sample as a point or ``--plot-density`` to plot a density map.

By default the plotting executables will plot all the parameters in the input
file. In order to specific a different set of variables to plot use the
``--parameters`` option. Examples how to use this option are shown below.

By default the plotting executables will plot samples beginning at the end of
the burn in. If the burn-in was skipped, then it starts from the first sample.
It will then use a sample every autocorrelation length along the chain.
Examples how to plot a specific iteration or change how the thinning is
performed is shown in the examples below.

You may plot a z-axis on the 2-D histograms using the ``--z-arg`` option.
For a list of options use ``pycbc_inference_plot_posterior --help``.

-----------------------------
Plotting a specific iteration
-----------------------------

An example of plotting the posteriors at a specific iteration::

    ITER=4999
    INPUT_FILE=inference.hdf
    OUTPUT_FILE=scatter.png
    pycbc_inference_plot_posterior \
        --iteration ${ITER} \
        --input-file ${INPUT_FILE} \
        --output-file ${OUTPUT_FILE} \
        --plot-scatter \
        --plot-marginal \
        --z-arg logplr \
        --parameters "ra*12/pi:$\alpha$ (h)" \
                     "dec*180/pi:$\delta$ (deg)" \
                     "polarization*180/pi:$\psi$ (deg)" \
                     mchirp q spin1_a spin1_azimuthal spin1_polar \
                     spin2_a spin2_azimuthal spin2_polar \
                     "inclination*180/pi:$\iota$ (deg)" distance \
                     "coa_phase*180/pi:$\phi_0$ (deg)" tc

-----------------------------------
Plotting a thinned chain of samples
-----------------------------------

There are also options for thinning the chains of samples from the command line, an example starting at the 6000-th iteration and taking every 2000-th iteration until the 12000-th iteration::

    THIN_START=5999
    THIN_INTERVAL=2000
    THIN_END=11999
    INPUT_FILE=inference.hdf
    OUTPUT_FILE=scatter.png
    pycbc_inference_plot_posterior \
        --iteration ${ITER} \
        --input-file ${INPUT_FILE} \
        --output-file ${OUTPUT_FILE} \
        --plot-scatter \
        --plot-marginal \
        --z-arg logplr \
        --parameters "ra*12/pi:$\alpha$ (h)" \
                     "dec*180/pi:$\delta$ (deg)" \
                     "polarization*180/pi:$\psi$ (deg)" \
                     mchirp q spin1_a spin1_azimuthal spin1_polar \
                     spin2_a spin2_azimuthal spin2_polar \
                     "inclination*180/pi:$\iota$ (deg)" distance \
                     "coa_phase*180/pi:$\phi_0$ (deg)" tc
back to top