The Python ensemble sampling toolkit for affine-invariant MCMC
—
emcee provides a comprehensive collection of proposal move algorithms for generating new walker positions during MCMC sampling. These moves can be used individually or combined in weighted ensembles to optimize sampling performance for different types of problems.
Abstract base classes that define the move interface and common functionality.
class Move:
"""Abstract base class for all moves."""
def tune(self, state, accepted):
"""
Tune move parameters based on acceptance history.
Args:
state: Current ensemble state
accepted: Boolean array indicating accepted proposals
"""
def update(self, old_state, new_state, accepted, subset=None):
"""
Update walker states with accepted proposals.
Args:
old_state: Current ensemble state
new_state: Proposed ensemble state
accepted: Boolean array indicating accepted proposals
subset: Subset of walkers to update
Returns:
State: Updated ensemble state
"""
class RedBlueMove(Move):
"""Base class for red-blue ensemble moves."""
def get_proposal(self, s, c, random):
"""
Generate proposals for red-blue moves.
Args:
s: Active walker positions
c: Complementary walker positions
random: Random number generator
Returns:
tuple: (proposed_positions, log_proposal_factors)
"""
class MHMove(Move):
"""Base class for Metropolis-Hastings style moves."""
def propose(self, model, state):
"""
Generate proposal state.
Args:
model: Model object containing log probability function
state: Current ensemble state
Returns:
State: Proposed ensemble state
"""The default Goodman & Weare stretch move, highly effective for most problems.
class StretchMove(RedBlueMove):
def __init__(self, a: float = 2.0):
"""
Initialize stretch move.
Args:
a: Stretch scale parameter (default: 2.0)
"""Simple random walk move for local exploration.
class WalkMove(RedBlueMove):
def __init__(self, s: float = None):
"""
Initialize walk move.
Args:
s: Step size parameter
"""Moves based on differential evolution algorithms for efficient exploration.
class DEMove(RedBlueMove):
def __init__(self, sigma: float = 1e-5, gamma0: float = None):
"""
Initialize differential evolution move.
Args:
sigma: Scaling parameter for random perturbation
gamma0: Base scaling factor (default: 2.38 / sqrt(2 * ndim))
"""
class DESnookerMove(RedBlueMove):
def __init__(self, sigma: float = 1e-5, gammas: list = None):
"""
Initialize differential evolution snooker move.
Args:
sigma: Scaling parameter
gammas: List of scaling factors for different move types
"""Advanced move using kernel density estimation for proposal generation.
class KDEMove(RedBlueMove):
def __init__(self, bw_method=None):
"""
Initialize KDE move.
Args:
bw_method: Bandwidth selection method for KDE
"""Metropolis-Hastings move with Gaussian proposals.
class GaussianMove(MHMove):
def __init__(self, cov, mode: str = "vector", factor=None):
"""
Initialize Gaussian move.
Args:
cov: Covariance matrix or proposal scale
mode: Proposal mode ("vector", "random", "sequential")
factor: Scaling factor for covariance
"""Generic MH move for custom proposal functions.
class MHMove(Move):
def __init__(self, proposal_function):
"""
Initialize generic MH move.
Args:
proposal_function: Function generating proposals
"""import emcee
from emcee import moves
import numpy as np
def log_prob(theta):
return -0.5 * np.sum(theta**2)
# Use stretch move (default)
sampler = emcee.EnsembleSampler(32, 2, log_prob)
# Use specific move
stretch_move = moves.StretchMove(a=2.5)
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=stretch_move)
# Use DE move
de_move = moves.DEMove(sigma=1e-4)
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=de_move)# List of moves (equal weight)
move_list = [
moves.StretchMove(),
moves.DEMove(),
moves.WalkMove()
]
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=move_list)
# Weighted moves
weighted_moves = [
(moves.StretchMove(), 0.6),
(moves.DEMove(), 0.3),
(moves.WalkMove(), 0.1)
]
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=weighted_moves)# Define covariance matrix
cov = np.array([[1.0, 0.5], [0.5, 2.0]])
gaussian_move = moves.GaussianMove(cov)
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=gaussian_move)# KDE move adapts to the shape of the distribution
kde_move = moves.KDEMove()
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=kde_move)
# Can specify bandwidth method
kde_move = moves.KDEMove(bw_method='scott')
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=kde_move)# Move that adapts during sampling
class AdaptiveMove(moves.Move):
def __init__(self, initial_scale=1.0):
self.scale = initial_scale
self.n_accepted = 0
self.n_total = 0
def tune(self, state, accepted):
self.n_accepted += np.sum(accepted)
self.n_total += len(accepted)
# Adjust scale based on acceptance rate
acceptance_rate = self.n_accepted / self.n_total
if acceptance_rate > 0.5:
self.scale *= 1.1
elif acceptance_rate < 0.2:
self.scale *= 0.9
# Use with tuning enabled
custom_move = AdaptiveMove()
sampler = emcee.EnsembleSampler(32, 2, log_prob, moves=custom_move)
sampler.run_mcmc(pos, 1000, tune=True)# Different strategies for different problem types
# For well-behaved, unimodal distributions
standard_moves = moves.StretchMove(a=2.0)
# For multimodal distributions
multimodal_moves = [
(moves.StretchMove(), 0.4),
(moves.DEMove(), 0.4),
(moves.KDEMove(), 0.2)
]
# For highly correlated parameters
correlated_moves = [
(moves.StretchMove(), 0.5),
(moves.DEMove(), 0.3),
(moves.GaussianMove(cov_matrix), 0.2)
]
# For local exploration
local_moves = moves.WalkMove(s=0.1)Install with Tessl CLI
npx tessl i tessl/pypi-emcee