Running polarimetric fits with an MCMC

Weights used:

  • Spatial

  • Spectral

LeakageLib provides a simple way to run polarimetric fits with an MCMC. You would use the fit_mcmc function instead of fit. This is a great way to handle non-Gaussian posteriors.

This example recreates the simulated data point source fit example, with mcmc fitting. First, we repeat the setup described in that example.

[1]:
import leakagelib

datas = leakagelib.IXPEData.load_all_detectors_with_path("data", "ps")
for data in datas:
    data.iterative_centroid_center()
    data.retain(data.evt_energies > 2)
    data.retain(data.evt_energies < 8)

settings = leakagelib.FitSettings(datas)
settings.apply_circular_roi(280)

settings.add_point_source() # Named "src" by default
settings.fix_flux("src", 1)
settings.set_initial_qu("src", (0.5,0))
settings.set_spectrum("src", lambda e: e**-1.5) # Gamma=1.5, unabsorbed powerlaw

settings.add_background() # Named "bkg" by default
settings.fix_qu("bkg", (0, 0)) # Assume an unpolarized background
settings.set_spectrum("bkg", lambda e: e**-2.5) # Gamma=2.5, unabsorbed powerlaw

fitter = leakagelib.Fitter(settings)
>>> PyXSPEC is not installed, you will no be able to use it.
>>> Reading (in memory) /opt/homebrew/anaconda3/lib/python3.12/site-packages/ixpeobssim/caldb/ixpe/xrt/bcf/vign/ixpe_d1_obssim20240101_vign_v013.fits...
6282 events were cut for being outside the region of interest.
>>> Reading (in memory) /opt/homebrew/anaconda3/lib/python3.12/site-packages/ixpeobssim/caldb/ixpe/gpd/cpf/arf/ixpe_d1_obssim20240101_v013.arf...
>>> Using cached xEffectiveArea object at /opt/homebrew/anaconda3/lib/python3.12/site-packages/ixpeobssim/caldb/ixpe/gpd/cpf/arf/ixpe_d1_obssim20240101_v013.arf...
Data set ps DU 1 had no exposure map loaded. Please load an exposure map if you are fitting to events in the vignetted portion.

Now we perform an MCMC fit simply by using the fit_mcmc function instead of fit. Note that you can save the mcmc samples to a file if you wish, using the save_samples argument.

[2]:
result = fitter.fit_mcmc(n_iter=1000)
100%|██████████| 1000/1000 [00:59<00:00, 16.68it/s]

Plotting the results shows that they suffer from a burnin problem:

[3]:
result.display_samples();
../_images/examples_fit-mcmc_6_0.png

We can address this by removing the first few events

[4]:
result.burnin(2000)
result.display_samples();
../_images/examples_fit-mcmc_8_0.png

The corner plot shows nice uncertainties as well

[5]:
result.display_corner();
../_images/examples_fit-mcmc_10_0.png

This fit result still acts like a normal fit result, and you can still print it to get Gaussian uncertainties (the covariance of the parameters). These are quite similar to the results obtained through the Gaussian-approxmating LeakageLib fit (see the point source example).

You can also access the result.samples array to get all the fit samples, and result.samples[result. sample_mask] to get all the samples after the burnin was removed.

[6]:
print(result)

print("\nAll samples: ", result.samples)
print("\nSamples after burnin: ", result.samples[result.sample_mask])
FitResult:
        q (src) = 0.4666 +/- 0.0721
        u (src) = -0.0260 +/- 0.0749
        f (bkg) = 2.1820 +/- 0.0436

Polarization:
        PD (src): 0.4674 +/- 0.0721
        PA (src): -1.5933 deg +/- 4.5935
Likelihood 17886.600143040232, dof 15891
MCMC fit

All samples:  [[ 7.23558699e-01  1.22015827e-03  1.33389783e+01]
 [ 7.83897305e-01 -3.89750924e-01  2.09590413e+01]
 [ 9.52895964e-01  5.36160994e-01  2.01392027e+01]
 ...
 [ 4.92181657e-01 -1.64708106e-02  2.18119864e+00]
 [ 4.91316536e-01 -3.91099556e-02  2.20751338e+00]
 [ 4.59765405e-01 -1.92532946e-02  2.23687075e+00]]

Samples after burnin:  [[ 0.42312855 -0.03353723  2.17694902]
 [ 0.33382468 -0.01372145  2.16199754]
 [ 0.58382084 -0.03081405  2.17405479]
 ...
 [ 0.49218166 -0.01647081  2.18119864]
 [ 0.49131654 -0.03910996  2.20751338]
 [ 0.4597654  -0.01925329  2.23687075]]