.. _algorithms: Algorithms ========== Summary of Available Algorithms ------------------------------- +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | Algorithm | Class | Parallelization | Applications | +=============================+==================+=================+===========================================================================+ | `Differential Evolution`_ | Population-based | Synchronous or | General-purpose parameter fitting | | | | Asynchronous | | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Scatter Search`_ | Population-based | Synchronous | General-purpose parameter fitting, especially difficult problems with high| | | | | dimensions or many local minima | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Particle Swarm`_ | Population-based | Asynchronous | Fitting models with high variability in runtime | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Metropolis-Hastings MCMC`_ | Metropolis | Independent | Finding probability distributions of parameters (deprecated) | | | sampling | Markov Chains | | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Simulated Annealing`_ | Metropolis | Independent | Problem-specific applications (deprecated) | | | sampling | Markov Chains | | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Parallel Tempering`_ | Metropolis | Synchronized | Finding probability distributions in challenging probablity landscapes | | | sampling | Markov Chains | (deprecated) | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Simplex`_ | Local search | Synchronous | Local optimization, or refinement of a result from another algorithm. | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Powell`_ | Local search | Synchronous | Local optimization, or refinement of a result from another algorithm. | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `CMA-ES`_ | Population-based | Synchronous | Global or local optimization, or refinement; robust on ill-conditioned | | | | | objectives. | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `Adaptive MCMC`_ | Metropolis | Independent | Finding probability distributions in challenging probablity landscapes | | | sampling | Markov Chains | | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ | `DREAM`_ | Hybrid | Synchronous | Finding probability distributions with accelerated convergence | | | Population / | | | | | Metropolis | | | +-----------------------------+------------------+-----------------+---------------------------------------------------------------------------+ In addition to the population-based and Metropolis samplers in the table above, PyBNF provides gradient-based optimizers (``trf``, ``lbfgs``) and profile-likelihood analysis — see :ref:`Gradient-based optimization ` — the :ref:`Hamiltonian Monte Carlo (NUTS) ` and :ref:`Preconditioned DREAM ` samplers, and :ref:`model checking `. Information criteria and posterior export are covered under :ref:`Model selection and posterior analysis `. General implementation features for all algorithms -------------------------------------------------- All algorithms in PyBNF keep track of a list of parameter sets (a "population"), and over the course of the simulation, submit new parameter sets to run on the simulator. Algorithms periodically output the file ``sorted_params.txt`` containing the best parameter sets found so far, and the corresponding objective function values. .. _param-init: Initialization ^^^^^^^^^^^^^^ The initial population of parameter sets is generated based on the keys specified for each free parameter: ``uniform_var``, ``loguniform_var``, ``normal_var`` or ``lognormal_var``. The value of the parameter in each new random parameter set is drawn from the specified probability distribution. PyBNF ships a broad catalog of prior distribution families beyond these four — see :ref:`priors`. The ``latin_hypercube`` option for initialization is enabled by default. This option only affects initialization of ``uniform_var``\ s and ``loguniform_var``\ s. When enabled, instead of drawing an independent random value for each starting parameter set, the starting parameter sets are generated with Latin hypercube sampling, which ensures a roughly even distribution of the parameter sets throughout the search space. .. _objective: Objective functions ^^^^^^^^^^^^^^^^^^^ All algorithms use an objective function to evaluate the quality of fit for each parameter set. The objective function is set with the ``objfunc`` key. The following options are available. Note that :math:`y_i` are the experimental data points and :math:`a_i` are the simulated data points. The summation is over all experimental data points. * Chi squared (``obj_func = chi_sq``): :math:`f(y, a) = \sum_i \frac{(y_i - a_i)^2}{2 \sigma_i^2}` , where :math:`\sigma_i` is the standard deviation of point :math:`y_i`, which must be specified in the :ref:`exp file `. * Sum of squares (``obj_func = sos``): :math:`f(y, a) = \sum_i (y_i - a_i)^2` * Sum of differences (``obj_func = sod``): :math:`f(y, a) = \sum_i |y_i - a_i|` * Normalized sum of squares (``obj_func = norm_sos``): :math:`f(y, a) = \sum_i \frac{(y_i - a_i)^2}{y_i^2}` * Average-normalized sum of squares (``obj_func = ave_norm_sos``): :math:`f(y, a) = \sum_i \frac{(y_i - a_i)^2}{\bar{y}^2}`, where :math:`\bar{y}` is the average of the entire data column :math:`y`. If you include any :ref:`constraints ` in your fit, the constraints add extra terms to the objective function. Changing parameter values ^^^^^^^^^^^^^^^^^^^^^^^^^ All algorithms perform changes to parameter values as the fitting proceeds. The way these changes are calculated depends on the type of parameter. ``loguniform_var``\ s and ``lognormal_var``\ s are moved in logarithmic space (base 10) throughout the entire fitting run. ``uniform_var``\ s and ``loguniform_var``\ s avoid moving outside the defined initialization range. If a move is attempted that would take the parameter outside the bounds, the parameter value is reflected over the boundary, back within bounds. This feature can be disabled by appending ``U`` to the end of the variable definition (e.g. ``uniform_var = x__FREE 10 30 U``) .. _alg-de: Differential Evolution ---------------------- Algorithm ^^^^^^^^^ A population of individuals (points in parameter space) are iteratively evaluated with an objective function. Parent individuals from the current iteration are selected to form new individuals in the next iteration. The new individual's parameters are derived by combining parameters from the parents. New individuals are accepted into the population if they have an objective value lower than that of a member of the current population. Parallelization ^^^^^^^^^^^^^^^ Three versions of differential evolution are available: All run in parallel, but they differ in their level of synchronicity. Asynchronous differential evolution (``fit_type = ade``) never allows processors to sit idle. One new simulation is started every time a simulation completes. This version is the best choice when a large number of processors are available. Synchronous differential evolution (``fit_type = de``) consists of discrete iterations. In each iteration, n simulations are run in parallel, but all must complete before moving on to the next iteration. Island-based differential evolution [Penas2015]_ is partially asynchronous algorithm. To use this version, set ``fit_type = de`` and set a value greater than 1 for the ``islands`` key. In this version, the current population consists of m islands. Each island is able to move on to the next iteration even if other islands are still in progress. If m is set to the number of available processors, then processors will never sit idle. Note however that this might still underperform compared to the synchronous algorithm run on the same number of processors. Implementation details ^^^^^^^^^^^^^^^^^^^^^^ We maintain a list of ``population_size`` current parameter sets, and in each iteration, ``population_size`` new parameter sets are proposed. The method to propose a new parameter set is specified by the config key ``de_strategy``. The default setting ``rand1`` works best for most problems, and runs as follows: We choose 3 random parameter sets p1, p2, and p3 in the current population. For each free parameter P, the new parameter set is assigned the value p1[P] + ``mutation_factor`` * (p2[P]-p3[P]) with probability ``mutation_rate``, or p1[P] with probability 1 - ``mutation_rate``. The new parameter set replaces the parameter set with the same index in the current population if it has a lower objective value. With ``de_strategy`` of ``best1`` or ``best2``, we force the above p1 to be the parameter set with the lowest objective value. With ``de_strategy`` of ``all1`` or ``all2``, we force p1 to be the parameter set at the same index we are proposing to replace. The ``best`` strategy results in fast convergence to what is likely only a local optimum. The ``all`` strategy converges more slowly, and prevents the entire population from converging to the same value. However, there is still a risk of each member of the population becoming stuck in its own local minimum. For the ``de_strategy``\ s ending in ``2``, we instead choose a total of 5 parameter sets, p1 through p5, and set the new parameter value as p1[P] + ``mutation_factor`` * (p2[P]-p3[P] + p4[P]-p5[P]) Asynchronous version """""""""""""""""""" The asynchronous version of the algorithm is identical to the sychronous algorithm, except that whenever a simulation completes, a new parameter set is immediately proposed based on the current population. Therefore, the random parameter sets p1, p2, and p3 might come from different iteration numbers. .. _alg-island: Island-based version """""""""""""""""""" In the island-based version of the algorithm [Penas2015]_, the population is divided into ``num_islands`` islands, which each follow the above update procedure independently. Every ``migrate_every`` iterations, a migration step occurs in which ``num_to_migrate`` individuals from each island are transferred randomly to others (according to a random permutation of the islands, keeping the number of individuals on each island constant). The migration step does not require synchronization of the islands; it is performed when the last island reaches the appropriate iteration number, regardless of whether other islands are already further along. Applications ^^^^^^^^^^^^ In our experience, differential evolution tends to be a good general-purpose algorithm. The asynchronous version has similar advantages to `Particle Swarm`_. .. _alg-ss: Scatter Search -------------- Algorithm ^^^^^^^^^ Scatter Search [Glover2000]_ functions similarly to differential evolution, but maintains a smaller current population than the number of available processors. In each iteration, every possible pair of individuals are combined to propose a new individual. Parallelization ^^^^^^^^^^^^^^^ In a scatter search run of population size n, each iteration requires n\*(n-1) independent simulations that can all be run in parallel. Scatter search requires synchronization at the end of each iteration, waiting for all simulations to complete before moving to the next iteration. Implementation details ^^^^^^^^^^^^^^^^^^^^^^ The PyBNF implementation follows the outline presented in the introduction of [Penas2017]_ and uses the recombination method described in [Egea2009]_. We maintain a reference set of ``population_size`` individuals, recommended to be a small number (~ 9-18). Each newly proposed parameter set is based on a "parent" parameter set and a "helper" parameter set, both from the current reference set. In each iteration, we consider all possible parent-helper combinations, for a total of n\*(n-1) parameter sets. The new parameter set depends on the rank of the parent and helper (call them :math:`p_i` and :math:`h_i`) when the reference set is sorted from best to worst. Then we apply a series of formulas to choose the next parameter value. Let :math:`\alpha` = -1 if :math:`h_i>p_i` or 1 if :math:`p_i` with its proposals computed in a covariance-whitened parameter space. The preconditioning transform is estimated from the sampled history (and adapted as sampling proceeds), so the differential-evolution donor moves are scaled and rotated to the geometry of a correlated posterior rather than the raw parameter axes. On posteriors with strong parameter correlations this markedly improves mixing over plain DREAM; on well-conditioned posteriors it reduces to DREAM(ZS). Parallelization ^^^^^^^^^^^^^^^ As with :ref:`DREAM `, the chains advance synchronously: one generation of all chains is proposed and scored together, and the shared ZS archive and the preconditioning estimate are updated between generations. Applications ^^^^^^^^^^^^ Sampling posteriors with strongly correlated parameters, where an axis-aligned or isotropic proposal mixes slowly. .. _alg-hmc: Hamiltonian Monte Carlo (NUTS) ------------------------------ Algorithm ^^^^^^^^^ Hamiltonian Monte Carlo (HMC), via the No-U-Turn Sampler (NUTS) [Hoffman2014]_, is a **gradient-based** Bayesian sampler. Where the other samplers propose moves blindly and accept or reject them, HMC follows the gradient of the log-posterior, so it mixes far more efficiently on correlated, curved, or high-dimensional posteriors. PyBNF drives NUTS through the `blackjax `_ library and uses HMC as a **reference sampler** — a gradient-quality yardstick against which the gradient-free samplers (``am`` / ``dream`` / ``p_dream``) are benchmarked on hard geometries. HMC needs a differentiable log-density, so it runs **only on a closed-form analytical objective**: a built-in target (``objective = banana, …``) or an inline ``objective = expression`` (see :ref:`Analytical and user-defined objectives `), including a data-bound curve fit. It does **not** run on a simulator (BNGL / SBML) model, nor on ``objective = callable`` (a general Python callable is not differentiable); those cases raise a clear error pointing back to the gradient-free samplers. Select it with ``job_type = hmc`` (requires :ref:`edition ` ``>= 2``). The same closed-form objective is automatically differentiated with `JAX `_: the expression compiled for the score path is also lambdified to a JAX function whose gradient ``jax.grad`` produces exactly, so the sampler and the score path can never drift. The full prior catalog composes — each family contributes a JAX log-density — and constrained parameters (positive, bounded, log-scaled) are sampled through an unconstraining bijection, so NUTS explores an unbounded space and never hits a support wall. Each chain runs an independent NUTS run with window adaptation (dual-averaging step size + mass matrix) over ``num_warmup`` steps, then keeps ``num_samples`` near-independent draws; the draws are written in the standard samples format, so the convergence diagnostics, the credible-interval output, and the :ref:`ArviZ bridge ` all apply unchanged. Parallelization ^^^^^^^^^^^^^^^ ``population_size`` sets the number of independent chains. Each analytical NUTS chain is a tight in-process numeric loop, so the chains run as independent blackjax runs rather than through the per-evaluation dask dispatch the simulator samplers use. Reliability ^^^^^^^^^^^ Alongside the rank-normalized split-:math:`\hat{R}` and bulk/tail ESS shared with the other samplers ([Vehtari2021]_), HMC reports the **number of divergent transitions** per chain — a NUTS-specific signal that the integrator could not traverse a region (too sharp a curvature for the tuned step size). A nonzero divergence count, like a high :math:`\hat{R}`, means the draws are not yet trustworthy: tighten ``target_accept`` (e.g. to 0.95, which shrinks the step size) and re-run. Installation ^^^^^^^^^^^^ HMC needs the optional gradient stack (JAX + blackjax):: pip install pybnf[jax] The gradient-free samplers need no extra. Requires Python with JAX support. .. _alg-sim: Simplex ------- Algorithm ^^^^^^^^^ Simplex is a local search algorithm that operates solely on objective evaluations at single points (i.e. it does not require calculation of gradients). The algorithm maintains a set on N+1 points in N-dimensional parameter space, which are thought of as defining an N-dimensional solid called a *simplex*. Individual points may be reflected through the lower-dimensional solid defined by the other N points, to obtain a local improvement in objective function value. The simplex algorithm has been nicknamed the "amoeba" algorithm because the simplex crawls through parameter space similar to an amoeba, extending protrusions in favorable directions. Parallelization ^^^^^^^^^^^^^^^ The PyBNF Simplex implementation is parallel and synchronous. Synchronization is required at the end of every iteration. Parallelization is achieved by simultaneously evaluating a subset of the N+1 points in the simplex. Therefore, this parallelization can take advantage of at most N+1 processors, where N is the number of free parameters. Implementation details ^^^^^^^^^^^^^^^^^^^^^^ PyBNF implements the parallelized Simplex algorithm described in [Lee2007]_. The initial simplex consists of N+1 points chosen deterministically based on the specified step size (set with the ``simplex_step`` and ``simplex_log_step`` keys, or for individual parameters with the ``var`` and ``log_var`` keys). One point of the simplex is the specified starting point for the search. The other N points are obtained by adding the step size to one parameter, and leaving the other N-1 parameters at the starting values. .. figure:: simplex.png :width: 200px :align: center :figclass: align-center Illustration of the simplex algorithm, modifying point P on a 3-point simplex in 2 dimensions Each iteration, we operate on the k worst points in the simplex, where k is the number of available processors (``parallel_count``). For each point P, we consider the hyperplane defined by the other N points in the simplex (blue line). Let d be the distance from P to the hyperplane. We evaluate point P\ :sub:`1` obtained by reflecting P through the hyperplane, to a distance of d \* ``simplex_reflect`` on the other side. Depending on the resulting objective value, we try another point in the second phase of the iteration. Three cases are possible. 1) The new point is better than the current global minimum: We try a second point continuing in the same direction for a distance of d \* ``simplex_expansion`` away from the hyperplane (P\ :sub:`2,1`). 2) The new point is worse than the global minimum, but better than the next worst point in the simplex: We don't try a second point. 3) The new point is worse than the next worst point in the simplex: We try a second point moving closer to the hyperplane. If P was better than P\ :sub:`1`, we try a point a distance of d \* ``simplex_contraction`` from the hyperplane in the direction of P (P\ :sub:`2,3a`). If P\ :sub:`1` was better than P, we instead try the same distance from the hyperplane in the direction of P\ :sub:`1` (P\ :sub:`2,3b`). In all cases, P in the simplex is set to the best choice among P, P\ :sub:`1`, or whichever second point we tried. If in a given iteration, all k points resulted in Case 3 and did not update to P\ :sub:`2,3a` or P\ :sub:`2,3b`, the iteration did not effectively change the state of the simplex. Then, we contract the simplex towards the best point: We set each point P to ``simplex_contract`` \* P0 + (1 - ``simplex_contract``) \* P, where P0 is the best point in the simplex. Applications ^^^^^^^^^^^^ Local optimization with the simplex algorithm is useful for improving on an already known good solution. In PyBNF, the most common application is to apply the simplex algorithm to the best-fit result obtained from one of the other algorithms. You can automatically refine your final result with the simplex algorithm by setting the ``refine`` key to 1, and setting simplex config keys in addition to the config for your main algorithm. It is also possible to run the Simplex algorithm on its own, using a custom starting point. In this case, you should use the ``var`` and ``log_var`` keys to specify your known starting point. .. _refinement: Refinement (choosing a local optimizer) --------------------------------------- PyBNF offers three derivative-free, black-box local optimizers for the post-fit polish step (``refine = 1``): `Simplex`_ (Nelder–Mead), `Powell`_, and `CMA-ES`_. Set ``refine_method`` to ``sim`` (the default, backward-compatible), ``powell``, or ``cmaes`` to choose; only the chosen optimizer's config keys are read. Each also runs standalone as its own ``fit_type`` (``sim`` / ``powell`` / ``cmaes``), started from a single point given with the ``var`` / ``logvar`` keys. CMA-ES additionally runs standalone as a *global* optimizer over a bounded ``uniform_var`` / ``loguniform_var`` box (see `CMA-ES`_). All three need only objective values (no gradients), which suits PyBNF's black-box, often-noisy simulator objectives. As rough guidance: * **Simplex** — the long-standing default; a robust, low-overhead amoeba search. * **Powell** — conjugate-direction line minimization; often converges faster than Simplex on smooth, well-behaved objectives. * **CMA-ES** — adapts a full covariance model of the search distribution, so it is the most robust of the three on ill-conditioned or rotated (correlated) objectives, at the cost of more evaluations per step. Being population-based, it also parallelizes across the whole generation. .. _alg-powell: Powell ------ Algorithm ^^^^^^^^^ Powell's method is a derivative-free local optimizer that minimizes the objective by a sequence of one-dimensional line minimizations along a set of search directions (initially the coordinate axes). After each cycle of line searches it may replace one direction with the net direction of the cycle's progress, building up *conjugate* directions that give quadratic convergence on a quadratic bowl ([Powell1964]_; see also [NumericalRecipes]_). PyBNF performs each line minimization by **bracketing** the minimum (geometric expansion from ``±powell_step``) and refining it with **Brent's method** (parabolic interpolation with a golden-section fallback) to ``powell_line_tol``. This follows long, curved, non-quadratic valleys and adapts its step length, where a single fixed-step parabola would stall. The line search is confined to the parameter box, so when refining a bounded fit a minimum that lies past a bound is found on the boundary. Parallelization ^^^^^^^^^^^^^^^ Powell is fundamentally serial — each line-search evaluation depends on the previous, so the bracketing + Brent search runs one objective evaluation at a time. (CMA-ES is the derivative-free optimizer that evaluates a whole generation in parallel.) The search runs in the parameter sampling space (log-scaled for log parameters), so a step is geometric for a log parameter. Applications ^^^^^^^^^^^^ Use Powell to refine an already-good solution (``refine = 1`` with ``refine_method = powell``), or standalone (``fit_type = powell``) from a starting point given with the ``var`` / ``logvar`` keys. Tune ``powell_step`` (initial bracketing step), ``powell_line_tol`` (1-D line-minimum precision), ``powell_stop_tol`` (per-cycle convergence), and ``powell_max_iterations`` (cycle budget; defaults to ``max_iterations``). .. _alg-cmaes: CMA-ES ------ Algorithm ^^^^^^^^^ The Covariance Matrix Adaptation Evolution Strategy ([Hansen2001]_) is a derivative-free, population-based optimizer. Each generation it samples ``population_size`` candidate parameter sets from a multivariate normal distribution and, from the best of them, updates the distribution's mean, overall step size, and full covariance matrix. Adapting the covariance lets the search stretch and rotate to match the local objective geometry, which makes CMA-ES notably robust on ill-conditioned and correlated problems. Parallelization ^^^^^^^^^^^^^^^ Synchronous and population-based: every candidate in a generation is evaluated in parallel, then the distribution is updated. Like the other start-point optimizers it searches in the parameter sampling space (log-scaled for log parameters). Applications ^^^^^^^^^^^^ Use CMA-ES to refine a result (``refine = 1`` with ``refine_method = cmaes``) or standalone (``fit_type = cmaes``). Standalone, it accepts either of two starts: * a single starting **point** given with the ``var`` / ``logvar`` keys (local search, like Simplex and Powell); or * a bounded **box** given with the ``uniform_var`` / ``loguniform_var`` keys — its *global-start* mode. CMA-ES begins at the box center and seeds its covariance with the per-coordinate box widths, so the first generation spans the whole box; candidates are reflected back into the box. Because covariance adaptation makes CMA-ES one of the strongest general-purpose black-box global optimizers, this is the recommended way to run it as a primary search (the ergonomics of ``de`` / ``pso``, with covariance adaptation). Use ``var`` / ``logvar`` only with a single value per parameter, and ``uniform_var`` / ``loguniform_var`` only with bounds — do not mix the two. The population size is ``population_size`` (at least 4) and the generation budget is ``max_iterations``. ``cmaes_sigma0`` sets the initial step size — in point-start mode an absolute step in the sampling space, in box mode a fraction of each box width — and ``cmaes_stop_tol`` the convergence threshold on the search distribution's spread. As a refiner the start is always the injected best fit. .. _alg-gradient: Gradient-based optimization --------------------------- For edition-2 fits of ODE-network models, PyBNF offers two gradient-based optimizers driven by exact forward parameter sensitivities: * ``fit_type = trf`` — a trust-region least-squares method (Trust-Region Reflective), for objectives that are a sum of squared residuals; and * ``fit_type = lbfgs`` — a quasi-Newton method (L-BFGS-B) that minimizes a scalar objective from its analytic gradient. Both converge far faster than the metaheuristics near a good fit, and the same sensitivity machinery drives profile-likelihood identifiability analysis (``fit_type = profile_likelihood``). These methods, the noise families and constraints they support, and the capability gate that decides when a gradient fit is possible are documented in full on the :ref:`gradient-based fitting ` page. .. _alg-check: Model checking -------------- ``fit_type = check`` (equivalently ``job_type = check``) is a first-class checking method rather than a search: it evaluates the objective value and constraint satisfaction for a given set of parameter values without exploring parameter space. Use it to verify that a model satisfies a set of qualitative properties (:ref:`constraints ` / BPSL) or to score a specific parameter set — a quick way to test a candidate fit or validate a model against its specification. See the `model-checking tutorial lesson `__ for a worked example. .. _model_selection: Model selection and posterior analysis -------------------------------------- Beyond a single best fit, PyBNF supports comparing models and characterizing posteriors. **Information criteria.** From a fit's maximized log-likelihood PyBNF computes the Akaike Information Criterion (AIC), the Bayesian Information Criterion (BIC), and the small-sample-corrected AIC (AICc), using the *full* normalized log-likelihood (including the noise-model normalizer): .. math:: \mathrm{AIC} = 2k - 2\ln L, \qquad \mathrm{BIC} = k\ln n - 2\ln L, \qquad \mathrm{AICc} = \mathrm{AIC} + \frac{2k(k+1)}{n-k-1}, where :math:`k` is the number of free parameters and :math:`n` the number of data points. Ranking competing models by AIC is the basis of the model-selection tutorial lesson. **ArviZ / LOO / WAIC.** A completed MCMC run can be exported as an ArviZ ``InferenceData`` object by setting :ref:`output_inference_data ` ``= 1`` (needs the optional ``arviz`` extra, ``pip install pybnf[arviz]``). The posterior then plugs into the ArviZ / bayesplot ecosystem for trace, rank, forest, and pair plots and for convergence diagnostics (R-hat, bulk/tail ESS). When the run also records the per-observation log-likelihood, the exported object carries a ``log_likelihood`` group, so leave-one-out cross-validation (``az.loo``) and the widely-applicable information criterion (``az.waic``) can be computed directly. .. [Egea2009] Egea, J. A.; Balsa-Canto, E.; García, M.-S. G.; Banga, J. R. Dynamic Optimization of Nonlinear Processes with an Enhanced Scatter Search Method. Ind. Eng. Chem. Res. 2009, 48 (9), 4388–4401. .. [Glover2000] Glover, F.; Laguna, M.; Martí, R. Fundamentals of Scatter Search and Path Relinking. Control Cybern. 2000, 29 (3), 652–684. .. [Hoffman2014] Hoffman, M. D.; Gelman, A. The No-U-Turn Sampler: Adaptively Setting Path Lengths in Hamiltonian Monte Carlo. J. Mach. Learn. Res. 2014, 15 (1), 1593–1623. .. [Hansen2001] Hansen, N.; Ostermeier, A. Completely Derandomized Self-Adaptation in Evolution Strategies. Evol. Comput. 2001, 9 (2), 159–195. .. [Gupta2018a] Gupta, S.; Hainsworth, L.; Hogg, J. S.; Lee, R. E. C.; Faeder, J. R. Evaluation of Parallel Tempering to Accelerate Bayesian Parameter Estimation in Systems Biology. 2018 26th Euromicro International Conference on Parallel, Distributed and Network-based Processing (PDP) 2018, 690–697. .. [Kozer2013] Kozer, N.; Barua, D.; Orchard, S.; Nice, E. C.; Burgess, A. W.; Hlavacek, W. S.; Clayton, A. H. A. Exploring Higher-Order EGFR Oligomerisation and Phosphorylation—a Combined Experimental and Theoretical Approach. Mol. BioSyst. Mol. BioSyst 2013, 9 (9), 1849–1863. .. [Lee2007] Lee, D.; Wiswall, M. A Parallel Implementation of the Simplex Function Minimization Routine. Comput. Econ. 2007, 30 (2), 171–187. .. [Moraes2015] Moraes, A. O. S.; Mitre, J. F.; Lage, P. L. C.; Secchi, A. R. A Robust Parallel Algorithm of the Particle Swarm Optimization Method for Large Dimensional Engineering Problems. Appl. Math. Model. 2015, 39 (14), 4223–4241. .. [Penas2015] Penas, D. R.; González, P.; Egea, J. A.; Banga, J. R.; Doallo, R. Parallel Metaheuristics in Computational Biology: An Asynchronous Cooperative Enhanced Scatter Search Method. Procedia Comput. Sci. 2015, 51 (1), 630–639. .. [Penas2017] Penas, D. R.; González, P.; Egea, J. A.; Doallo, R.; Banga, J. R. Parameter Estimation in Large-Scale Systems Biology Models: A Parallel and Self-Adaptive Cooperative Strategy. BMC Bioinformatics 2017, 18 (1), 52. .. [Powell1964] Powell, M. J. D. An Efficient Method for Finding the Minimum of a Function of Several Variables without Calculating Derivatives. Comput. J. 1964, 7 (2), 155–162. .. [NumericalRecipes] Press, W. H.; Teukolsky, S. A.; Vetterling, W. T.; Flannery, B. P. Numerical Recipes: The Art of Scientific Computing, 3rd ed.; Cambridge University Press, 2007 (§10.7, Powell's method). .. [terBraak2008] ter Braak, C. J. F.; Vrugt, J. A. Differential Evolution Markov Chain with Snooker Updater and Fewer Chains. Stat. Comput. 2008, 18 (4), 435–446. .. [Vehtari2021] Vehtari, A.; Gelman, A.; Simpson, D.; Carpenter, B.; Bürkner, P.-C. Rank-Normalization, Folding, and Localization: An Improved R-hat for Assessing Convergence of MCMC. Bayesian Anal. 2021, 16 (2), 667–718. .. [Vrugt2016] Vrugt, J. A. Markov Chain Monte Carlo Simulation Using the DREAM Software Package: Theory, Concepts, and MATLAB Implementation. Environ. Model. Softw. 2016, 75, 273–316.