pint.gridutils.grid_chisq

pint.gridutils.grid_chisq(ftr, parnames, parvalues, extraparnames=[], executor=None, ncpu=None, chunksize=1, printprogress=True, **fitargs)[source]

Compute chisq over a grid of parameters

Parameters:
Returns:

  • np.ndarray (array of chisq values)

  • extraout (dict of np.ndarray) – Parameter values computed at each grid point for extraparnames

Examples

>>> import astropy.units as u
>>> import numpy as np
>>> import pint.config
>>> import pint.gridutils
>>> from pint.fitter import WLSFitter
>>> from pint.models.model_builder import get_model, get_model_and_toas
>>> # Load in a basic dataset
>>> parfile = pint.config.examplefile("NGC6440E.par")
>>> timfile = pint.config.examplefile("NGC6440E.tim")
>>> m, t = get_model_and_toas(parfile, timfile)
>>> f = WLSFitter(t, m)
>>> # find the best-fit
>>> f.fit_toas()
>>> bestfit = f.resids.chi2
>>> # We'll do something like 3-sigma around the best-fit values of  F0
>>> F0 = np.linspace(f.model.F0.quantity - 3 * f.model.F0.uncertainty,f.model.F0.quantity + 3 * f.model.F0.uncertainty,25)
>>> chi2_F0,_ = pint.gridutils.grid_chisq(f, ("F0",), (F0,))

A 2D example with a plot:

>>> import astropy.units as u
>>> import numpy as np
>>> import pint.gridutils
>>> from pint.fitter import WLSFitter
>>> from pint.models.model_builder import get_model_and_toas
>>> import scipy.stats
>>> import matplotlib.pyplot as plt
>>> # Load in a basic dataset
>>> parfile = pint.config.examplefile("NGC6440E.par")
>>> timfile = pint.config.examplefile("NGC6440E.tim")
>>> m, t = get_model_and_toas(parfile, timfile)
>>> f = WLSFitter(t, m)
>>> # find the best-fit
>>> f.fit_toas()
>>> bestfit = f.resids.chi2
>>> F0 = np.linspace(f.model.F0.quantity - 3 * f.model.F0.uncertainty,f.model.F0.quantity + 3 * f.model.F0.uncertainty,25)
>>> F1 = np.linspace(f.model.F1.quantity - 3 * f.model.F1.uncertainty,f.model.F1.quantity + 3 * f.model.F1.uncertainty,27)
>>> chi2grid = pint.gridutils.grid_chisq(f, ("F0", "F1"), (F0, F1))[0]
>>> # 1, 2, and 3 sigma confidence limits
>>> nsigma = np.arange(1, 4)
>>> # these are the CDFs going from -infinity to nsigma.  So subtract away 0.5 and double for the 2-sided values
>>> CIs = (scipy.stats.norm().cdf(nsigma) - 0.5) * 2
>>> print(f"Confidence intervals for {nsigma} sigma: {CIs}")
>>> # chi^2 random variable for 2 parameters
>>> rv = scipy.stats.chi2(2)
>>> # the ppf = Percent point function is the inverse of the CDF
>>> contour_levels = rv.ppf(CIs)
>>> fig, ax = plt.subplots(figsize=(16, 9))
>>> # just plot the values offset from the best-fit values
>>> twod = ax.contour(F0 - f.model.F0.quantity,F1 - f.model.F1.quantity,chi2grid - bestfit,levels=contour_levels,colors="b")
>>> ax.errorbar(0, 0, xerr=f.model.F0.uncertainty.value, yerr=f.model.F1.uncertainty.value, fmt="ro")
>>> ax.set_xlabel("$\Delta F_0$ (Hz)", fontsize=24)
>>> ax.set_ylabel("$\Delta F_1$ (Hz/s)", fontsize=24)
>>> plt.show()

Notes

By default, it will create ProcessPoolExecutor with max_workers equal to the desired number of cpus. However, if you are running this as a script you may need something like:

import multiprocessing

if __name__ == "__main__":
    multiprocessing.freeze_support()
    ...
    grid_chisq(...)

If an instantiated Executor is passed instead, it will be used as-is.

The behavior for different combinations of executor and ncpu is: +—————–+——–+————————+ | executor | ncpu | result | +=================+========+========================+ | existing object | any | uses existing executor | +—————–+——–+————————+ | None | 1 | uses single-processor | +—————–+——–+————————+ | None | None | creates default | | | | executor with | | | | cpu_count workers | +—————–+——–+————————+ | None | >1 | creates default | | | | executor with desired | | | | number of workers | +—————–+——–+————————+

Other Executors can be found for different computing environments: * [1] for MPI * [2] for SLURM or Condor