Model Fitting Introduction#
In this section we provide some guidelines and caveats for mode fitting and minimizers in JetSeT. In general to perform a model fitting to data, you can use the ModelMinimizer class from the minimizer module, or you can use specific plugins as for the case of sherpa and gammapy, documented here:
In the following we will describe the
frequentist model fitting using the
ModelMinimizerusing theminuitminimizer (wrapping the iminuit package ) and thelsbminimizer (wrapping the scipy least_squares package),the Bayesian approach using
McmcSampler(backend: emcee) andUltraNestSampler(backend: ultranest).
Data handling#
Before starting with the description of the model fitting implementation in JeSeT, please read Handling errors and systematics section in the Data format and SED data.
Frequentist model fitting#
FitModel creation#
The first step consists in creating a fit FitModel object. Assuming you have already a Jet, in this example is the object prefit_jet (either constrained from data, see Phenomenological model constraining: application and the examples in model fitting examples, or any instance of the Jet class)
from jetset.model_manager import FitModel
fit_model_lsb=FitModel( jet=prefit_jet, name='SSC-best-fit-lsb',template=None)
Note
Starting from JetSeT version>=1.1.2 the implementation of the composite model FitModel for setting parameters requires to specify the model component.
and this holds also for the freeze method and for setting fit_range intervals, and for the methods relate to parameters setting in general.
FitModel
Before proceeding with the minimization, it is important to freeze or free the needed parameters, and provide fit boundaries. As default, boundaries will be set to the physical boundaries of the parameters, but, depending on your particular source and state, you want to provide tighter boundaries eg:
fit_model_minuit.freeze('jet_leptonic','z_cosm')
fit_model_minuit.freeze('jet_leptonic','R_H')
fit_model_minuit.freeze('jet_leptonic','R')
fit_model_minuit.jet_leptonic.parameters.R.fit_range=[5E15,1E17]
fit_model_minuit.jet_leptonic.parameters.gmin.fit_range=[10,1000]
fit_model_minuit.jet_leptonic.parameters.gmax.fit_range=[5E5,1E7]
fit_model_minuit.jet_leptonic.parameters.gamma0_log_parab.fit_range=[1E3,1E5]
fit_model_minuit.jet_leptonic.parameters.beam_obj.fit_range=[5,50]
Note
Setting fit_range can speed up and improve the fit convergence and is advised. Anyhow, the ranges should be used judged by the user each time according to the physics of the particular source. Please, have look at the examples in model fitting examples to have a broader view of this topic.
A good strategy is to run first a lsb fit and then, using the same fit_model, run a fit with minuit. Using minuit we notice that sometimes the fit will converge, but the quality will not be enough (valid==false) to run minos.
Anyhow, as shown in the MCMC sampling, it still possible to estimate asymmetric errors by means of MCMC sampling.
ModelMinimizer creation#
The second step consists in the creation of a ModelMinimizer object
for
lsb(wrapping: scipy.optimize.least_squares)from jetset.minimizer import ModelMinimizer model_minimizer=ModelMinimizer('lsb')
for
minuit:from jetset.minimizer import ModelMinimizer model_minimizer=ModelMinimizer('minuit')
Note
For
minuit, starting from JetSeTversion==1.3.0,simplexwill be run beforemigrad, this allows a better sampling of the parameter space. You can skipsimplexas follows:model_minimizer.minimizer.add_simplex=False
both for the case of lsb and minuit specific kw for the minimizer can be accessed by the dictionary:
model_minimizer.minimizer.conf_dict
eg: for the case of lsb that dictionary would be:
{'xtol': None,
'ftol': 1e-08,
'gtol': 1e-08,
'jac': '3-point',
'loss': 'linear',
'tr_solver': None,
'f_scale': 1}
Warning
avoid to change those parameters, unless you are completely aware of what you are doing, and you had a full reading the of the lsb / minuit documentation.
Running the minimization process#
The third step will consist in actually calling the minimization process
best_fit_res=model_minimizer.fit(fit_model=fit_model,
sed_data=sed_data,
nu_fit_start=1E11,
nu_fit_stop=1E29,
max_ev=None,
use_UL=True,
fitname='SSC-best-fit',
repeat=1)
notice that:
sed_datais yourObsDataobject (see Data format and SED data section from more info)nu_fit_startandnu_fit_stopcorrespond to the range of the data, in Hz, used for the model fittinguse_ULif set toTruewill take into account also the upper limits, and will skip them inuse_UL=Falserepeat, introduced in version 1.1.2, allows to repeat the fit process, and will provide a better fit convergence. For example, settingrepeat=3the fit process will be repeated 3 times.
See the examples in model fitting examples to inspect the output for each minimizer. The plot for the covariance matrix can be obtained
p=model_minimizer.plot_corr_matrix()
Bayesian model fitting with emcee and ultranest#
JetSeT provides two Bayesian samplers with a common workflow:
McmcSampler(backend: emcee)UltraNestSamplerfromjetset.mcmc_ultranest(backend: UltraNest)
Both samplers start from a ModelMinimizer, use the same parameter/bounds setup, and share the same post-processing helpers
(sampler_parameters, plot_chain, corner_plot, plot_model, save/load).
Building the sampler object#
Starting from a frequentist best-fit result:#
from jetset.minimizer import ModelMinimizer
model_minimizer = ModelMinimizer.load_model('model_minimizer_minuit.pkl')
emcee backend:
from jetset.mcmc import McmcSampler mcmc = McmcSampler(model_minimizer)
ultranest backend:
from jetset.mcmc_ultranest import UltraNestSampler mcmc = UltraNestSampler(model_minimizer)
As default, the mcmc sampler will inherit nu_fit_start, nu_fit_stop, and use_UL from the model_minimizer object. To change these values, you can use the following code, setting the values according to your choice.
model_minimizer.prepare_fit(fit_model,
sed_data,
nu_fit_start=1E7,
nu_fit_stop=1E29,
use_UL=True)
starting from a plain model#
It is also possible to run Bayesian sampling starting from a plain model (without a previous frequentist minimization) by creating a ModelMinimizer('mcmc') and calling prepare_fit.
This plain-model workflow is suggested mainly for UltraNestSampler.
from jetset.minimizer import ModelMinimizer
model_minimizer = ModelMinimizer('mcmc')
model_minimizer.prepare_fit(fit_model,
sed_data,
nu_fit_start=1E7,
nu_fit_stop=1E29,
use_UL=True)
from jetset.mcmc_ultranest import UltraNestSampler
mcmc = UltraNestSampler(model_minimizer)
New interface after the MCMC refactoring#
Important
Starting from v1.4.0, McmcSampler does not use labels and use_labels_dict anymore.
All the free parameters are sampled. To include/exclude parameters, freeze/free them.
You can inspect sampler parameters with:
mcmc.sampler_parameters
To exclude/include parameters from the sampler:
mcmc.model.radio_spectrum.parameters.nu_ssa.freeze()
mcmc.model.radio_spectrum.parameters.nu_ssa.free()
Setting the priors/bounds#
Priors are uniform within the bounds set for each sampled parameter.
Global bounds for all currently free parameters:
mcmc.set_bounds(bound=5.0, bound_rel=True)
Custom bounds for specific parameters:
mcmc.set_bounds(comp_name='jet_leptonic', par_name='N', par_bounds=[1E-5,10]) mcmc.set_bounds(comp_name='radio_spectrum', par_name='alpha_radio', par_bounds=[0,1])
By default preserve_fit_range=True; this keeps MCMC bounds consistent with the fit ranges set in the frequentist stage.
Warning
For UltraNestSampler all sampled parameters must have finite and valid [min,max] bounds; otherwise a RuntimeError is raised.
Running the sampler#
emcee:
mcmc.run_sampler(nwalkers=20, burnin=50, steps=500, progress='notebook')
ultranest:
mcmc.run_sampler(min_num_live_points=400, dlogz=0.5, frac_remain=0.01, ultranest_output_dir='ultranest_runs/mrk421_plain')
ultranest_output_dircontrols where UltraNest writes its run files.ultranest with Open MPI helper:
Important
- To run ultranest via openmpi, you have to install
mpi4py (pip/mamba install mpi4py)
OpenMPI (https://docs.open-mpi.org/en/main/installing-open-mpi/quickstart.html#binary-packages)
from jetset.mcmc_ultranest import run_open_mpi mcmc = run_open_mpi(mcmc, n_proc=8, ultranest_output_dir='ultranest_runs/mrk421_openmpi')
ultranest_output_dircontrols where UltraNest writes its run files, and will be placed under therun_mpidirectory.
Corner plot for composite models#
For composite models, corner_plot can be used in two modes:
per_component=True(default): one corner plot per model component.f = mcmc.corner_plot(per_component=True)
per_component=False: a single global corner plot including parameters from all sampled components.f = mcmc.corner_plot(per_component=False)
Caveats and differences: emcee vs ultranest#
Common caveat:
In all cases, the quality of parameter bounds is critical for convergence and for physically meaningful posteriors.
emcee specific caveats:
It is strongly recommended to start from a converged frequentist best-fit.
nwalkersdefault is \(4 \times\) (number of sampled parameters).nwalkersmust be at least \(2 \times\) (number of sampled parameters), otherwise aRuntimeErroris raised.burninandstepsmust be tuned case by case.After the run, you can retune burn-in using autocorrelation time:
mcmc.tune_burnin()
This recomputes the posterior samples after updating
burninfromsampler.get_autocorr_time.tune_burninis meaningful only aftermcmc.run_sampler(...)has been executed.Autocorrelation-time estimates can be unstable for short or non-converged chains; if needed, increase
stepsand rerun before trusting the tuned burn-in.Always inspect chains (e.g. with
mcmc.plot_chain()) before and aftertune_burnin().For advanced usage and diagnostics, see the emcee autocorrelation documentation: https://emcee.readthedocs.io/en/stable/tutorials/autocorr/.
ultranest specific caveats:
Starting from a minimized model is not mandatory; a plain-model workflow is supported and can be effective.
If you start from a plain model, use physically motivated finite bounds and robust sampling settings (e.g. not quick-test values for
min_num_live_points,nsteps,min_ess).There is no burn-in phase (internally
burnin=0);tune_burninis not part of the ultranest workflow.acceptance_fractionis not used as a diagnostic (set tonan).UltraNest provides Bayesian evidence estimates:
print(mcmc.logz, mcmc.logzerr)
Notebook configurations such as very low
min_num_live_points/nstepsare quick-test setups only; for robust analyses use stricter/default settings.- To run ultranest via openmpi, you have to install
mpi4py (pip/mamba install mpi4py)
OpenMPI (https://docs.open-mpi.org/en/main/installing-open-mpi/quickstart.html#binary-packages)
Please, read the Bayesian sections in model fitting examples for end-to-end workflows with both backends.