emcee Python Toolkit

repository·main·Indexed 23 days ago

https://github.com/dfm/emcee

A Python toolkit for performing affine-invariant ensemble sampling using Markov chain Monte Carlo (MCMC) methods. It features the EnsembleSampler class for drawing samples from probability distributions, support for various sampling moves like StretchMove, DEMove, and DESnookerMove, and tools for estimating integrated autocorrelation time. The library supports parallelization via multiprocessing.Pool or MPI using the schwimmbad library.

Tokens
12.1K
Snippets
18
Records
78
Agent score
80%

What's inside emcee

  1. Overview of emcee

    main
    emcee is a stable, well-tested Python implementation of the affine-invariant ensemble sampler for Markov chain Monte Carlo (MCMC). It is based on the algorithm proposed by Goodman & Weare (2010) and is widely used in scientific research, particularly in Astrophysics.
  2. Understand what walkers are in emcee

    main
    In emcee, walkers are the individual members of the ensemble. While they behave similarly to separate Metropolis-Hastings chains, they are interconnected: the proposal distribution for any single walker depends on the current positions of all other walkers in the ensemble.
  3. What are blobs in emcee?

    main

    Blobs allow you to track arbitrary metadata associated with every sample in the MCMC chain. In version 3, blobs are stored as NumPy arrays, and the sampler performs type inference to determine the simplest representation.

    To use blobs, your log_prob function must return more than one value. The first value is always assumed to be the log probability, and all subsequent values are treated as blobs.

    Note on non-finite probabilities: If log_prob returns -np.inf for the log probability, the sampler does not inspect or track the blobs. However, you must still return the correct number of arguments to match your expected blob structure.

  4. How moves work in emcee

    main

    In emcee version 3 and later, a "move" is an algorithm used to update the coordinates of walkers in an ensemble sampler. Instead of relying solely on the traditional "stretch move", you can provide a mixture of different moves to create a more efficient sampler, especially for high-dimensional or multi-modal probability surfaces.

    Key concepts:

    • Mixture of Moves: You can specify a mixture of moves using the moves keyword in the EnsembleSampler. This mixture can be weighted. At each sampling step, a move is randomly selected from the mixture to serve as the proposal.
    • Parallelization: Most ensemble moves are designed to update the ensemble in parallel (following Foreman-Mackey et al. 2013), allowing computationally expensive models to leverage multiple CPUs.
    • Complementary Ensembles: Updates for a walker are typically based on the coordinates of a complementary set of walkers in the ensemble.
    • Move Types:
      • Ensemble Moves: Update walkers based on the current state of the ensemble (e.g., StretchMove, WalkMove, KDEMove). Many of these inherit from RedBlueMove to support parallelization.
      • Metropolis–Hastings Moves: Use independent proposals to update walkers (e.g., GaussianMove, which inherits from MHMove).
  5. Work with State objects in emcee

    main
    Several methods within the EnsembleSampler class are designed to return or consume State objects. A State object encapsulates the current status of the sampler, including the positions of the walkers and potentially other metadata required for sampling or resuming processes.
  6. Install emcee via pip

    main

    The recommended way to install the stable version of emcee is using pip. Ensure you have numpy installed as it is a dependency.

    Run the following commands to upgrade your build tools and install emcee:

    python -m pip install -U pip
    pip install -U setuptools setuptools_scm pep517
    pip install -U emcee
  7. Initialize walkers for sampling

    main
    To initialize walkers effectively, start them in a small cluster (a "small ball") around the preferred position based on your a priori knowledge. The walkers are designed to quickly branch out from this cluster to explore the rest of the parameter space.
  8. Install emcee from source

    main

    To install the latest development version from the GitHub repository, clone the repository and install it in editable mode using pip:

    python -m pip install -U pip
    python -m pip install -U setuptools setuptools_scm pep517
    git clone https://github.com/dfm/emcee.git
    cd emcee
    python -m pip install -e .
  9. Handle parameter limits and boundaries

    main

    To confine walkers to a finite volume of the parameter space, you should ensure your log-probability function returns negative infinity for any parameter values that fall outside the allowed volume (i.e., where the log of the prior probability is 0).

        return -numpy.inf
  10. Use `blobs_dtype` to define named blobs

    main

    If you want to save multiple pieces of metadata with specific names and types, use the blobs_dtype argument when initializing emcee.EnsembleSampler. This results in get_blobs() returning a structured NumPy array.

    If blobs_dtype is not provided, the sampler automatically guesses the dtype from the first call to log_prob.

    Example of using named blobs:

    import emcee
    import numpy as np
    
    def log_prob(params):
        lp = log_prior(params)
        if not np.isfinite(lp):
            return -np.inf, None, None
        ll = log_like(params)
        if not np.isfinite(ll):
            return -np.inf, None, None
    
        # Return log probability, log prior, and parameter mean
        return lp + ll, lp, np.mean(params)
    
    coords = np.random.randn(32, 3)
    nwalkers, ndim = coords.shape
    
    # Define the structured dtype for named blobs
    dtype = [("log_prior", float), ("mean", float)]
    sampler = emcee.EnsembleSampler(nwalkers, ndim, log_prob, blobs_dtype=dtype)
    
    sampler.run_mcmc(coords, 100)
    
    # Access named blobs via keys
    blobs = sampler.get_blobs()
    log_prior_samps = blobs["log_prior"]
    mean_samps = blobs["mean"]
    
    # Access flattened named blobs
    flat_blobs = sampler.get_blobs(flat=True)
    flat_log_prior_samps = flat_blobs["log_prior"]
    flat_mean_samps = flat_blobs["mean"]
    def log_prob(params):
        lp = log_prior(params)
        if not np.isfinite(lp):
            return -np.inf, None, None
        ll = log_like(params)
        if not np.isfinite(ll):
            return -np.inf, None, None
    
        return lp + ll, lp, np.mean(params)
    
    coords = np.random.randn(32, 3)
    nwalkers, ndim = coords.shape
    
    # Here are the important lines for defining the blobs_dtype
    dtype = [("log_prior", float), ("mean", float)]
    sampler = emcee.EnsembleSampler(nwalkers, ndim, log_prob,
                                    blobs_dtype=dtype)
    
    sampler.run_mcmc(coords, 100)
    
    blobs = sampler.get_blobs()
    log_prior_samps = blobs["log_prior"]
    mean_samps = blobs["mean"]
    print(log_prior_samps.shape)  # (100, 32)
    print(mean_samps.shape)  # (100, 32)
    
    flat_blobs = sampler.get_blobs(flat=True)
    flat_log_prior_samps = flat_blobs["log_prior"]
    flat_mean_samps = flat_blobs["mean"]
    print(flat_log_prior_samps.shape)  # (3200,)
    print(flat_mean_samps.shape)  # (3200,)
  11. Replace MPIPool with schwimmbad

    main
    The MPIPool implementation has been removed from emcee. For MPI-based parallel sampling, use the schwimmbad project, which is a fork of the original emcee implementation designed to fix memory leaks and crashes.