emcee-2.1.0/0000755000076500000240000000000012337423505012465 5ustar dfmstaff00000000000000emcee-2.1.0/AUTHORS.rst0000644000076500000240000000263512337423137014353 0ustar dfmstaff00000000000000Author: - `Dan Foreman-Mackey (NYU) `_ Direct contributions to the code base: - `Ruth Angus (Oxford) `_ - `Bence Béky (Harvard) `_ - `Frederik Beaujean (MPI for Physics) `_ - `Alex Conley (U Colorado, Boulder) `_ - `Will Meierjurgen Farr (Northwestern) `_ - `Júlio Hoffimann Mendes (Federal University of Pernambuco) `_ - `David W. Hogg (NYU) `_ - `Dustin Lang (Princeton/CMU) `_ - `Phil Marshall (Oxford) `_ - `Demitri Muna (OSU) `_ - `Adrian Price-Whelan (Columbia) `_ - `Jeremy Sanders (Cambridge) `_ - `Leo Singer (Caltech) `_ - `Manodeep Sinha (Vanderbilt) `_ - `Marco Tazzari (ESO) `_ - `Simon Walker `_ - `Peter K. G. Williams (CfA) `_ - `Joe Zuntz (Oxford) `_ Comments, corrections & suggestions: - Eric Agol (UWash) - Jo Bovy (IAS) - Andrew Bradshaw (UC Davis) - Jacqueline Chen (MIT) - John Gizis (Delaware) - Jonathan Goodman (NYU) - Jennifer Piscionere (Vanderbilt) emcee-2.1.0/emcee/0000755000076500000240000000000012337423505013543 5ustar dfmstaff00000000000000emcee-2.1.0/emcee/__init__.py0000644000076500000240000000164512337405402015656 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import (division, print_function, absolute_import, unicode_literals) from .sampler import * from .mh import * from .ensemble import * from .ptsampler import * from . import utils from . import autocorr __version__ = "2.1.0" def test(): from inspect import getmembers, ismethod from .tests import Tests print("Starting tests...") failures = 0 tests = Tests() for o in getmembers(tests): tests.setUp() if ismethod(o[1]) and o[0].startswith("test"): print("{0} ...".format(o[0])) try: o[1]() except Exception as e: print("Failed with:\n {0.__class__.__name__}: {0}" .format(e)) failures += 1 else: print(" Passed.") print("{0} tests failed".format(failures)) emcee-2.1.0/emcee/autocorr.py0000644000076500000240000000550612337405402015755 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["function", "integrated_time"] import numpy as np def function(x, axis=0, fast=False): """ Estimate the autocorrelation function of a time series using the FFT. :param x: The time series. If multidimensional, set the time axis using the ``axis`` keyword argument and the function will be computed for every other axis. :param axis: (optional) The time axis of ``x``. Assumed to be the first axis if not specified. :param fast: (optional) If ``True``, only use the largest ``2^n`` entries for efficiency. (default: False) """ x = np.atleast_1d(x) m = [slice(None), ] * len(x.shape) # For computational efficiency, crop the chain to the largest power of # two if requested. if fast: n = int(2**np.floor(np.log2(x.shape[axis]))) m[axis] = slice(0, n) x = x else: n = x.shape[axis] # Compute the FFT and then (from that) the auto-correlation function. f = np.fft.fft(x-np.mean(x, axis=axis), n=2*n, axis=axis) m[axis] = slice(0, n) acf = np.fft.ifft(f * np.conjugate(f), axis=axis)[m].real m[axis] = 0 return acf / acf[m] def integrated_time(x, axis=0, window=50, fast=False): """ Estimate the integrated autocorrelation time of a time series. See `Sokal's notes `_ on MCMC and sample estimators for autocorrelation times. :param x: The time series. If multidimensional, set the time axis using the ``axis`` keyword argument and the function will be computed for every other axis. :param axis: (optional) The time axis of ``x``. Assumed to be the first axis if not specified. :param window: (optional) The size of the window to use. (default: 50) :param fast: (optional) If ``True``, only use the largest ``2^n`` entries for efficiency. (default: False) """ # Compute the autocorrelation function. f = function(x, axis=axis, fast=fast) # Special case 1D for simplicity. if len(f.shape) == 1: return 1 + 2*np.sum(f[1:window]) # N-dimensional case. m = [slice(None), ] * len(f.shape) m[axis] = slice(1, window) tau = 1 + 2*np.sum(f[m], axis=axis) return tau if __name__ == "__main__": import time import acor N = 400000 a = 0.9 d = 3 x = np.empty((N, d)) x[0] = np.zeros(d) for i in xrange(1, N): x[i] = x[i-1] * a + np.random.rand(d) strt = time.time() print(integrated_time(x)) print(time.time() - strt) strt = time.time() print([acor.acor(x[:, i])[0] for i in range(d)]) print(time.time() - strt) emcee-2.1.0/emcee/ensemble.py0000644000076500000240000004437712337411431015721 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ An affine invariant Markov chain Monte Carlo (MCMC) sampler. Goodman & Weare, Ensemble Samplers With Affine Invariance Comm. App. Math. Comp. Sci., Vol. 5 (2010), No. 1, 65–80 """ from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["EnsembleSampler"] import numpy as np from . import autocorr from .sampler import Sampler from .interruptible_pool import InterruptiblePool class EnsembleSampler(Sampler): """ A generalized Ensemble sampler that uses 2 ensembles for parallelization. The ``__init__`` function will raise an ``AssertionError`` if ``k < 2 * dim`` (and you haven't set the ``live_dangerously`` parameter) or if ``k`` is odd. **Warning**: The :attr:`chain` member of this object has the shape: ``(nwalkers, nlinks, dim)`` where ``nlinks`` is the number of steps taken by the chain and ``k`` is the number of walkers. Use the :attr:`flatchain` property to get the chain flattened to ``(nlinks, dim)``. For users of pre-1.0 versions, this shape is different so be careful! :param nwalkers: The number of Goodman & Weare "walkers". :param dim: Number of dimensions in the parameter space. :param lnpostfn: A function that takes a vector in the parameter space as input and returns the natural logarithm of the posterior probability for that position. :param a: (optional) The proposal scale parameter. (default: ``2.0``) :param args: (optional) A list of extra positional arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. :param kwargs: (optional) A list of extra keyword arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. :param postargs: (optional) Alias of ``args`` for backwards compatibility. :param threads: (optional) The number of threads to use for parallelization. If ``threads == 1``, then the ``multiprocessing`` module is not used but if ``threads > 1``, then a ``Pool`` object is created and calls to ``lnpostfn`` are run in parallel. :param pool: (optional) An alternative method of using the parallelized algorithm. If provided, the value of ``threads`` is ignored and the object provided by ``pool`` is used for all parallelization. It can be any object with a ``map`` method that follows the same calling sequence as the built-in ``map`` function. :param runtime_sortingfn: (optional) A function implementing custom runtime load-balancing. See :ref:`loadbalance` for more information. """ def __init__(self, nwalkers, dim, lnpostfn, a=2.0, args=[], kwargs={}, postargs=None, threads=1, pool=None, live_dangerously=False, runtime_sortingfn=None): self.k = nwalkers self.a = a self.threads = threads self.pool = pool self.runtime_sortingfn = runtime_sortingfn if postargs is not None: args = postargs super(EnsembleSampler, self).__init__(dim, lnpostfn, args=args, kwargs=kwargs) # Do a little bit of _magic_ to make the likelihood call with # ``args`` and ``kwargs`` pickleable. self.lnprobfn = _function_wrapper(self.lnprobfn, self.args, self.kwargs) assert self.k % 2 == 0, "The number of walkers must be even." if not live_dangerously: assert self.k >= 2 * self.dim, ( "The number of walkers needs to be more than twice the " "dimension of your parameter space... unless you're " "crazy!") if self.threads > 1 and self.pool is None: self.pool = InterruptiblePool(self.threads) def clear_blobs(self): """ Clear the ``blobs`` list. """ self._blobs = [] def reset(self): """ Clear the ``chain`` and ``lnprobability`` array. Also reset the bookkeeping parameters. """ super(EnsembleSampler, self).reset() self.naccepted = np.zeros(self.k) self._chain = np.empty((self.k, 0, self.dim)) self._lnprob = np.empty((self.k, 0)) # Initialize list for storing optional metadata blobs. self.clear_blobs() def sample(self, p0, lnprob0=None, rstate0=None, blobs0=None, iterations=1, thin=1, storechain=True, mh_proposal=None): """ Advance the chain ``iterations`` steps as a generator. :param p0: A list of the initial positions of the walkers in the parameter space. It should have the shape ``(nwalkers, dim)``. :param lnprob0: (optional) The list of log posterior probabilities for the walkers at positions given by ``p0``. If ``lnprob is None``, the initial values are calculated. It should have the shape ``(k, dim)``. :param rstate0: (optional) The state of the random number generator. See the :attr:`Sampler.random_state` property for details. :param iterations: (optional) The number of steps to run. :param thin: (optional) If you only want to store and yield every ``thin`` samples in the chain, set thin to an integer greater than 1. :param storechain: (optional) By default, the sampler stores (in memory) the positions and log-probabilities of the samples in the chain. If you are using another method to store the samples to a file or if you don't need to analyse the samples after the fact (for burn-in for example) set ``storechain`` to ``False``. :param mh_proposal: (optional) A function that returns a list of positions for ``nwalkers`` walkers given a current list of positions of the same size. See :class:`utils.MH_proposal_axisaligned` for an example. At each iteration, this generator yields: * ``pos`` - A list of the current positions of the walkers in the parameter space. The shape of this object will be ``(nwalkers, dim)``. * ``lnprob`` - The list of log posterior probabilities for the walkers at positions given by ``pos`` . The shape of this object is ``(nwalkers, dim)``. * ``rstate`` - The current state of the random number generator. * ``blobs`` - (optional) The metadata "blobs" associated with the current position. The value is only returned if ``lnpostfn`` returns blobs too. """ # Try to set the initial value of the random number generator. This # fails silently if it doesn't work but that's what we want because # we'll just interpret any garbage as letting the generator stay in # it's current state. self.random_state = rstate0 p = np.array(p0) halfk = int(self.k / 2) # If the initial log-probabilities were not provided, calculate them # now. lnprob = lnprob0 blobs = blobs0 if lnprob is None: lnprob, blobs = self._get_lnprob(p) # Check to make sure that the probability function didn't return # ``np.nan``. if np.any(np.isnan(lnprob)): raise ValueError("The initial lnprob was NaN.") # Store the initial size of the stored chain. i0 = self._chain.shape[1] # Here, we resize chain in advance for performance. This actually # makes a pretty big difference. if storechain: N = int(iterations / thin) self._chain = np.concatenate((self._chain, np.zeros((self.k, N, self.dim))), axis=1) self._lnprob = np.concatenate((self._lnprob, np.zeros((self.k, N))), axis=1) for i in range(int(iterations)): self.iterations += 1 # If we were passed a Metropolis-Hastings proposal # function, use it. if mh_proposal is not None: # Draw proposed positions & evaluate lnprob there q = mh_proposal(p) newlnp, blob = self._get_lnprob(q) # Accept if newlnp is better; and ... acc = (newlnp > lnprob) # ... sometimes accept for steps that got worse worse = np.flatnonzero(~acc) acc[worse] = ((newlnp[worse] - lnprob[worse]) > np.log(self._random.rand(len(worse)))) del worse # Update the accepted walkers. lnprob[acc] = newlnp[acc] p[acc] = q[acc] self.naccepted[acc] += 1 if blob is not None: assert blobs is not None, ( "If you start sampling with a given lnprob, you also " "need to provide the current list of blobs at that " "position.") ind = np.arange(self.k)[acc] for j in ind: blobs[j] = blob[j] else: # Loop over the two ensembles, calculating the proposed # positions. # Slices for the first and second halves first, second = slice(halfk), slice(halfk, self.k) for S0, S1 in [(first, second), (second, first)]: q, newlnp, acc, blob = self._propose_stretch(p[S0], p[S1], lnprob[S0]) if np.any(acc): # Update the positions, log probabilities and # acceptance counts. lnprob[S0][acc] = newlnp[acc] p[S0][acc] = q[acc] self.naccepted[S0][acc] += 1 if blob is not None: assert blobs is not None, ( "If you start sampling with a given lnprob, " "you also need to provide the current list of " "blobs at that position.") ind = np.arange(len(acc))[acc] indfull = np.arange(self.k)[S0][acc] for j in range(len(ind)): blobs[indfull[j]] = blob[ind[j]] if storechain and i % thin == 0: ind = i0 + int(i / thin) self._chain[:, ind, :] = p self._lnprob[:, ind] = lnprob if blobs is not None: self._blobs.append(list(blobs)) # Yield the result as an iterator so that the user can do all # sorts of fun stuff with the results so far. if blobs is not None: # This is a bit of a hack to keep things backwards compatible. yield p, lnprob, self.random_state, blobs else: yield p, lnprob, self.random_state def _propose_stretch(self, p0, p1, lnprob0): """ Propose a new position for one sub-ensemble given the positions of another. :param p0: The positions from which to jump. :param p1: The positions of the other ensemble. :param lnprob0: The log-probabilities at ``p0``. This method returns: * ``q`` - The new proposed positions for the walkers in ``ensemble``. * ``newlnprob`` - The vector of log-probabilities at the positions given by ``q``. * ``accept`` - A vector of type ``bool`` indicating whether or not the proposed position for each walker should be accepted. * ``blob`` - The new meta data blobs or ``None`` if nothing was returned by ``lnprobfn``. """ s = np.atleast_2d(p0) Ns = len(s) c = np.atleast_2d(p1) Nc = len(c) # Generate the vectors of random numbers that will produce the # proposal. zz = ((self.a - 1.) * self._random.rand(Ns) + 1) ** 2. / self.a rint = self._random.randint(Nc, size=(Ns,)) # Calculate the proposed positions and the log-probability there. q = c[rint] - zz[:, np.newaxis] * (c[rint] - s) newlnprob, blob = self._get_lnprob(q) # Decide whether or not the proposals should be accepted. lnpdiff = (self.dim - 1.) * np.log(zz) + newlnprob - lnprob0 accept = (lnpdiff > np.log(self._random.rand(len(lnpdiff)))) return q, newlnprob, accept, blob def _get_lnprob(self, pos=None): """ Calculate the vector of log-probability for the walkers. :param pos: (optional) The position vector in parameter space where the probability should be calculated. This defaults to the current position unless a different one is provided. This method returns: * ``lnprob`` - A vector of log-probabilities with one entry for each walker in this sub-ensemble. * ``blob`` - The list of meta data returned by the ``lnpostfn`` at this position or ``None`` if nothing was returned. """ if pos is None: p = self.pos else: p = pos # Check that the parameters are in physical ranges. if np.any(np.isinf(p)): raise ValueError("At least one parameter value was infinite.") if np.any(np.isnan(p)): raise ValueError("At least one parameter value was NaN.") # If the `pool` property of the sampler has been set (i.e. we want # to use `multiprocessing`), use the `pool`'s map method. Otherwise, # just use the built-in `map` function. if self.pool is not None: M = self.pool.map else: M = map # sort the tasks according to (user-defined) some runtime guess if self.runtime_sortingfn is not None: p, idx = self.runtime_sortingfn(p) # Run the log-probability calculations (optionally in parallel). results = list(M(self.lnprobfn, [p[i] for i in range(len(p))])) try: lnprob = np.array([float(l[0]) for l in results]) blob = [l[1] for l in results] except (IndexError, TypeError): lnprob = np.array([float(l) for l in results]) blob = None # sort it back according to the original order - get the same # chain irrespective of the runtime sorting fn if self.runtime_sortingfn is not None: orig_idx = np.argsort(idx) lnprob = lnprob[orig_idx] p = [p[i] for i in orig_idx] if blob is not None: blob = [blob[i] for i in orig_idx] # Check for lnprob returning NaN. if np.any(np.isnan(lnprob)): # Print some debugging stuff. print("NaN value of lnprob for parameters: ") for pars in p[np.isnan(lnprob)]: print(pars) # Finally raise exception. raise ValueError("lnprob returned NaN.") return lnprob, blob @property def blobs(self): """ Get the list of "blobs" produced by sampling. The result is a list (of length ``iterations``) of ``list`` s (of length ``nwalkers``) of arbitrary objects. **Note**: this will actually be an empty list if your ``lnpostfn`` doesn't return any metadata. """ return self._blobs @property def chain(self): """ A pointer to the Markov chain itself. The shape of this array is ``(k, iterations, dim)``. """ return super(EnsembleSampler, self).chain @property def flatchain(self): """ A shortcut for accessing chain flattened along the zeroth (walker) axis. """ s = self.chain.shape return self.chain.reshape(s[0] * s[1], s[2]) @property def lnprobability(self): """ A pointer to the matrix of the value of ``lnprobfn`` produced at each step for each walker. The shape is ``(k, iterations)``. """ return super(EnsembleSampler, self).lnprobability @property def flatlnprobability(self): """ A shortcut to return the equivalent of ``lnprobability`` but aligned to ``flatchain`` rather than ``chain``. """ return super(EnsembleSampler, self).lnprobability.flatten() @property def acceptance_fraction(self): """ An array (length: ``k``) of the fraction of steps accepted for each walker. """ return super(EnsembleSampler, self).acceptance_fraction @property def acor(self): """ An estimate of the autocorrelation time for each parameter (length: ``dim``). """ return self.get_autocorr_time() def get_autocorr_time(self, window=50, fast=False): """ Compute an estimate of the autocorrelation time for each parameter (length: ``dim``). :param window: (optional) The size of the windowing function. This is equivalent to the maximum number of lags to use. (default: 50) """ return autocorr.integrated_time(np.mean(self.chain, axis=0), axis=0, window=window, fast=fast) class _function_wrapper(object): """ This is a hack to make the likelihood function pickleable when ``args`` or ``kwargs`` are also included. """ def __init__(self, f, args, kwargs): self.f = f self.args = args self.kwargs = kwargs def __call__(self, x): try: return self.f(x, *self.args, **self.kwargs) except: import traceback print("emcee: Exception while calling your likelihood function:") print(" params:", x) print(" args:", self.args) print(" kwargs:", self.kwargs) print(" exception:") traceback.print_exc() raise emcee-2.1.0/emcee/interruptible_pool.py0000644000076500000240000000636112337410734020044 0ustar dfmstaff00000000000000# -*- coding: utf-8 -*- """ Python's multiprocessing.Pool class doesn't interact well with ``KeyboardInterrupt`` signals, as documented in places such as: * ``_ * ``_ * ``_ Various workarounds have been shared. Here, we adapt the one proposed in the last link above, by John Reese, and shared as * ``_ Our version is a drop-in replacement for multiprocessing.Pool ... as long as the map() method is the only one that needs to be interrupt-friendly. Contributed by Peter K. G. Williams . *Added in version 2.1.0* """ from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["InterruptiblePool"] import signal import functools from multiprocessing.pool import Pool from multiprocessing import TimeoutError def _initializer_wrapper(actual_initializer, *rest): """ We ignore SIGINT. It's up to our parent to kill us in the typical condition of this arising from ``^C`` on a terminal. If someone is manually killing us with that signal, well... nothing will happen. """ signal.signal(signal.SIGINT, signal.SIG_IGN) if actual_initializer is not None: actual_initializer(*rest) class InterruptiblePool(Pool): """ A modified version of :class:`multiprocessing.pool.Pool` that has better behavior with regard to ``KeyboardInterrupts`` in the :func:`map` method. :param processes: (optional) The number of worker processes to use; defaults to the number of CPUs. :param initializer: (optional) Either ``None``, or a callable that will be invoked by each worker process when it starts. :param initargs: (optional) Arguments for *initializer*; it will be called as ``initializer(*initargs)``. :param kwargs: (optional) Extra arguments. Python 2.7 supports a ``maxtasksperchild`` parameter. """ wait_timeout = 3600 def __init__(self, processes=None, initializer=None, initargs=(), **kwargs): new_initializer = functools.partial(_initializer_wrapper, initializer) super(InterruptiblePool, self).__init__(processes, new_initializer, initargs, **kwargs) def map(self, func, iterable, chunksize=None): """ Equivalent of ``map()`` built-in, without swallowing ``KeyboardInterrupt``. :param func: The function to apply to the items. :param iterable: An iterable of items that will have `func` applied to them. """ # The key magic is that we must call r.get() with a timeout, because # a Condition.wait() without a timeout swallows KeyboardInterrupts. r = self.map_async(func, iterable, chunksize) while True: try: return r.get(self.wait_timeout) except TimeoutError: pass except KeyboardInterrupt: self.terminate() self.join() raise # Other exceptions propagate up. emcee-2.1.0/emcee/mh.py0000644000076500000240000001134312337405402014517 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ A vanilla Metropolis-Hastings sampler """ from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["MHSampler"] import numpy as np from . import autocorr from .sampler import Sampler # === MHSampler === class MHSampler(Sampler): """ The most basic possible Metropolis-Hastings style MCMC sampler :param cov: The covariance matrix to use for the proposal distribution. :param dim: Number of dimensions in the parameter space. :param lnpostfn: A function that takes a vector in the parameter space as input and returns the natural logarithm of the posterior probability for that position. :param args: (optional) A list of extra positional arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. :param kwargs: (optional) A list of extra keyword arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. """ def __init__(self, cov, *args, **kwargs): super(MHSampler, self).__init__(*args, **kwargs) self.cov = cov def reset(self): super(MHSampler, self).reset() self._chain = np.empty((0, self.dim)) self._lnprob = np.empty(0) def sample(self, p0, lnprob=None, randomstate=None, thin=1, storechain=True, iterations=1): """ Advances the chain ``iterations`` steps as an iterator :param p0: The initial position vector. :param lnprob0: (optional) The log posterior probability at position ``p0``. If ``lnprob`` is not provided, the initial value is calculated. :param rstate0: (optional) The state of the random number generator. See the :func:`random_state` property for details. :param iterations: (optional) The number of steps to run. :param thin: (optional) If you only want to store and yield every ``thin`` samples in the chain, set thin to an integer greater than 1. :param storechain: (optional) By default, the sampler stores (in memory) the positions and log-probabilities of the samples in the chain. If you are using another method to store the samples to a file or if you don't need to analyse the samples after the fact (for burn-in for example) set ``storechain`` to ``False``. At each iteration, this generator yields: * ``pos`` - The current positions of the chain in the parameter space. * ``lnprob`` - The value of the log posterior at ``pos`` . * ``rstate`` - The current state of the random number generator. """ self.random_state = randomstate p = np.array(p0) if lnprob is None: lnprob = self.get_lnprob(p) # Resize the chain in advance. if storechain: N = int(iterations / thin) self._chain = np.concatenate((self._chain, np.zeros((N, self.dim))), axis=0) self._lnprob = np.append(self._lnprob, np.zeros(N)) i0 = self.iterations # Use range instead of xrange for python 3 compatability for i in range(int(iterations)): self.iterations += 1 # Calculate the proposal distribution. q = self._random.multivariate_normal(p, self.cov) newlnprob = self.get_lnprob(q) diff = newlnprob - lnprob # M-H acceptance ratio if diff < 0: diff = np.exp(diff) - self._random.rand() if diff > 0: p = q lnprob = newlnprob self.naccepted += 1 if storechain and i % thin == 0: ind = i0 + int(i / thin) self._chain[ind, :] = p self._lnprob[ind] = lnprob # Heavy duty iterator action going on right here... yield p, lnprob, self.random_state @property def acor(self): """ An estimate of the autocorrelation time for each parameter (length: ``dim``). """ return self.get_autocorr_time() def get_autocorr_time(self, window=50): """ Compute an estimate of the autocorrelation time for each parameter (length: ``dim``). :param window: (optional) The size of the windowing function. This is equivalent to the maximum number of lags to use. (default: 50) """ return autocorr.integrated_time(self.chain, axis=0, window=window) emcee-2.1.0/emcee/mpi_pool.py0000644000076500000240000002057012337422764015745 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["MPIPool"] # If mpi4py is installed, import it. try: from mpi4py import MPI MPI = MPI except ImportError: MPI = None class _close_pool_message(object): def __repr__(self): return "" class _function_wrapper(object): def __init__(self, function): self.function = function def _error_function(task): raise RuntimeError("Pool was sent tasks before being told what " "function to apply.") class MPIPool(object): """ A pool that distributes tasks over a set of MPI processes. MPI is an API for distributed memory parallelism. This pool will let you run emcee without shared memory, letting you use much larger machines with emcee. The pool only support the :func:`map` method at the moment because this is the only functionality that emcee needs. That being said, this pool is fairly general and it could be used for other purposes. Contributed by `Joe Zuntz `_. :param comm: (optional) The ``mpi4py`` communicator. :param debug: (optional) If ``True``, print out a lot of status updates at each step. :param loadbalance: (optional) if ``True`` and ntask > Ncpus, tries to loadbalance by sending out one task to each cpu first and then sending out the rest as the cpus get done. """ def __init__(self, comm=None, debug=False, loadbalance=False): if MPI is None: raise ImportError("Please install mpi4py") self.comm = MPI.COMM_WORLD if comm is None else comm self.rank = self.comm.Get_rank() self.size = self.comm.Get_size() - 1 self.debug = debug self.function = _error_function self.loadbalance = loadbalance if self.size == 0: raise ValueError("Tried to create an MPI pool, but there " "was only one MPI process available. " "Need at least two.") def is_master(self): """ Is the current process the master? """ return self.rank == 0 def wait(self): """ If this isn't the master process, wait for instructions. """ if self.is_master(): raise RuntimeError("Master node told to await jobs.") status = MPI.Status() while True: # Event loop. # Sit here and await instructions. if self.debug: print("Worker {0} waiting for task.".format(self.rank)) # Blocking receive to wait for instructions. task = self.comm.recv(source=0, tag=MPI.ANY_TAG, status=status) if self.debug: print("Worker {0} got task {1} with tag {2}." .format(self.rank, task, status.tag)) # Check if message is special sentinel signaling end. # If so, stop. if isinstance(task, _close_pool_message): if self.debug: print("Worker {0} told to quit.".format(self.rank)) break # Check if message is special type containing new function # to be applied if isinstance(task, _function_wrapper): self.function = task.function if self.debug: print("Worker {0} replaced its task function: {1}." .format(self.rank, self.function)) continue # If not a special message, just run the known function on # the input and return it asynchronously. result = self.function(task) if self.debug: print("Worker {0} sending answer {1} with tag {2}." .format(self.rank, result, status.tag)) self.comm.isend(result, dest=0, tag=status.tag) def map(self, function, tasks): """ Like the built-in :func:`map` function, apply a function to all of the values in a list and return the list of results. :param function: The function to apply to the list. :param tasks: The list of elements. """ ntask = len(tasks) # If not the master just wait for instructions. if not self.is_master(): self.wait() return if function is not self.function: if self.debug: print("Master replacing pool function with {0}." .format(function)) self.function = function F = _function_wrapper(function) # Tell all the workers what function to use. requests = [] for i in range(self.size): r = self.comm.isend(F, dest=i + 1) requests.append(r) # Wait until all of the workers have responded. See: # https://gist.github.com/4176241 MPI.Request.waitall(requests) if (not self.loadbalance) or (ntask <= self.size): # Do not perform load-balancing - the default load-balancing # scheme emcee uses. # Send all the tasks off and wait for them to be received. # Again, see the bug in the above gist. requests = [] for i, task in enumerate(tasks): worker = i % self.size + 1 if self.debug: print("Sent task {0} to worker {1} with tag {2}." .format(task, worker, i)) r = self.comm.isend(task, dest=worker, tag=i) requests.append(r) MPI.Request.waitall(requests) # Now wait for the answers. results = [] for i in range(ntask): worker = i % self.size + 1 if self.debug: print("Master waiting for worker {0} with tag {1}" .format(worker, i)) result = self.comm.recv(source=worker, tag=i) results.append(result) return results else: # Perform load-balancing. The order of the results are likely to # be different from the previous case. for i, task in enumerate(tasks[0:self.size]): worker = i+1 if self.debug: print("Sent task {0} to worker {1} with tag {2}." .format(task, worker, i)) # Send out the tasks asynchronously. self.comm.isend(task, dest=worker, tag=i) ntasks_dispatched = self.size results = [None]*ntask for itask in range(ntask): status = MPI.Status() # Receive input from workers. result = self.comm.recv(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG, status=status) worker = status.source i = status.tag results[i] = result if self.debug: print("Master received from worker {0} with tag {1}" .format(worker, i)) # Now send the next task to this idle worker (if there are any # left). if ntasks_dispatched < ntask: task = tasks[ntasks_dispatched] i = ntasks_dispatched if self.debug: print("Sent task {0} to worker {1} with tag {2}." .format(task, worker, i)) # Send out the tasks asynchronously. self.comm.isend(task, dest=worker, tag=i) ntasks_dispatched += 1 return results def bcast(self, *args, **kwargs): """ Equivalent to mpi4py :func:`bcast` collective operation. """ return self.comm.bcast(*args, **kwargs) def close(self): """ Just send a message off to all the pool members which contains the special :class:`_close_pool_message` sentinel. """ if self.is_master(): for i in range(self.size): self.comm.isend(_close_pool_message(), dest=i + 1) def __enter__(self): return self def __exit__(self, *args): self.close() emcee-2.1.0/emcee/ptsampler.py0000644000076500000240000004737512337411267016146 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["PTSampler"] import numpy as np import numpy.random as nr import multiprocessing as multi from . import autocorr from .sampler import Sampler def default_beta_ladder(ndim, ntemps=None, Tmax=None): """Returns a ladder of :math:`\beta \equiv 1/T` with temperatures geometrically spaced with spacing chosen so that a Gaussian posterior would have a 0.25 temperature swap acceptance rate. :param ndim: The number of dimensions in the parameter space. :param ntemps: (optional) If set, the number of temperatures to use. If ``None``, the ``Tmax`` argument must be given, and the number of temperatures is chosen so that the highest temperature is greater than ``Tmax``. :param Tmax: (optional) If ``ntemps`` is not given, this argument controls the number of temperatures. Temperatures are chosen according to the spacing criteria until the maximum temperature exceeds ``Tmax`` """ tstep = np.array([25.2741, 7., 4.47502, 3.5236, 3.0232, 2.71225, 2.49879, 2.34226, 2.22198, 2.12628, 2.04807, 1.98276, 1.92728, 1.87946, 1.83774, 1.80096, 1.76826, 1.73895, 1.7125, 1.68849, 1.66657, 1.64647, 1.62795, 1.61083, 1.59494, 1.58014, 1.56632, 1.55338, 1.54123, 1.5298, 1.51901, 1.50881, 1.49916, 1.49, 1.4813, 1.47302, 1.46512, 1.45759, 1.45039, 1.4435, 1.4369, 1.43056, 1.42448, 1.41864, 1.41302, 1.40761, 1.40239, 1.39736, 1.3925, 1.38781, 1.38327, 1.37888, 1.37463, 1.37051, 1.36652, 1.36265, 1.35889, 1.35524, 1.3517, 1.34825, 1.3449, 1.34164, 1.33847, 1.33538, 1.33236, 1.32943, 1.32656, 1.32377, 1.32104, 1.31838, 1.31578, 1.31325, 1.31076, 1.30834, 1.30596, 1.30364, 1.30137, 1.29915, 1.29697, 1.29484, 1.29275, 1.29071, 1.2887, 1.28673, 1.2848, 1.28291, 1.28106, 1.27923, 1.27745, 1.27569, 1.27397, 1.27227, 1.27061, 1.26898, 1.26737, 1.26579, 1.26424, 1.26271, 1.26121, 1.25973]) dmax = tstep.shape[0] if ndim > dmax: # An approximation to the temperature step at large # dimension tstep = 1.0 + 2.0*np.sqrt(np.log(4.0))/np.sqrt(ndim) else: tstep = tstep[ndim-1] if ntemps is None and Tmax is None: raise ValueError('must specify one of ``ntemps`` and ``Tmax``') elif ntemps is None: ntemps = int(np.log(Tmax)/np.log(tstep)+2) return np.exp(np.linspace(0, -(ntemps-1)*np.log(tstep), ntemps)) class PTLikePrior(object): """ Wrapper class for logl and logp. """ def __init__(self, logl, logp, loglargs=[], logpargs=[], loglkwargs={}, logpkwargs={}): self.logl = logl self.logp = logp self.loglargs = loglargs self.logpargs = logpargs self.loglkwargs = loglkwargs self.logpkwargs = logpkwargs def __call__(self, x): lp = self.logp(x, *self.logpargs, **self.logpkwargs) if lp == float('-inf'): return lp, lp return self.logl(x, *self.loglargs, **self.loglkwargs), lp class PTSampler(Sampler): """ A parallel-tempered ensemble sampler, using :class:`EnsembleSampler` for sampling within each parallel chain. :param ntemps: The number of temperatures. Can be ``None``, in which case the ``Tmax`` argument sets the maximum temperature. :param nwalkers: The number of ensemble walkers at each temperature. :param dim: The dimension of parameter space. :param logl: The log-likelihood function. :param logp: The log-prior function. :param threads: (optional) The number of parallel threads to use in sampling. :param pool: (optional) Alternative to ``threads``. Any object that implements a ``map`` method compatible with the built-in ``map`` will do here. For example, :class:`multi.Pool` will do. :param betas: (optional) Array giving the inverse temperatures, :math:`\\beta=1/T`, used in the ladder. The default is chosen so that a Gaussian posterior in the given number of dimensions will have a 0.25 tswap acceptance rate. :param a: (optional) Proposal scale factor. :param Tmax: (optional) Maximum temperature for the ladder. If ``ntemps`` is ``None``, this argument is used to set the temperature ladder. :param loglargs: (optional) Positional arguments for the log-likelihood function. :param logpargs: (optional) Positional arguments for the log-prior function. :param loglkwargs: (optional) Keyword arguments for the log-likelihood function. :param logpkwargs: (optional) Keyword arguments for the log-prior function. """ def __init__(self, ntemps, nwalkers, dim, logl, logp, threads=1, pool=None, betas=None, a=2.0, Tmax=None, loglargs=[], logpargs=[], loglkwargs={}, logpkwargs={}): self.logl = logl self.logp = logp self.a = a self.loglargs = loglargs self.logpargs = logpargs self.loglkwargs = loglkwargs self.logpkwargs = logpkwargs self.nwalkers = nwalkers self.dim = dim if betas is None: self._betas = default_beta_ladder(self.dim, ntemps=ntemps, Tmax=Tmax) else: self._betas = betas self.ntemps = self.betas.shape[0] assert self.nwalkers % 2 == 0, \ "The number of walkers must be even." assert self.nwalkers >= 2*self.dim, \ "The number of walkers must be greater than 2*dimension." self._chain = None self._lnprob = None self._lnlikelihood = None self.nswap = np.zeros(self.ntemps, dtype=np.float) self.nswap_accepted = np.zeros(self.ntemps, dtype=np.float) self.nprop = np.zeros((self.ntemps, self.nwalkers), dtype=np.float) self.nprop_accepted = np.zeros((self.ntemps, self.nwalkers), dtype=np.float) self.pool = pool if threads > 1 and pool is None: self.pool = multi.Pool(threads) def reset(self): """ Clear the ``chain``, ``lnprobability``, ``lnlikelihood``, ``acceptance_fraction``, ``tswap_acceptance_fraction`` stored properties. """ self.nswap = np.zeros(self.ntemps, dtype=np.float) self.nswap_accepted = np.zeros(self.ntemps, dtype=np.float) self.nprop = np.zeros((self.ntemps, self.nwalkers), dtype=np.float) self.nprop_accepted = np.zeros((self.ntemps, self.nwalkers), dtype=np.float) self._chain = None self._lnprob = None self._lnlikelihood = None def sample(self, p0, lnprob0=None, lnlike0=None, iterations=1, thin=1, storechain=True): """ Advance the chains ``iterations`` steps as a generator. :param p0: The initial positions of the walkers. Shape should be ``(ntemps, nwalkers, dim)``. :param lnprob0: (optional) The initial posterior values for the ensembles. Shape ``(ntemps, nwalkers)``. :param lnlike0: (optional) The initial likelihood values for the ensembles. Shape ``(ntemps, nwalkers)``. :param iterations: (optional) The number of iterations to preform. :param thin: (optional) The number of iterations to perform between saving the state to the internal chain. :param storechain: (optional) If ``True`` store the iterations in the ``chain`` property. At each iteration, this generator yields * ``p``, the current position of the walkers. * ``lnprob`` the current posterior values for the walkers. * ``lnlike`` the current likelihood values for the walkers. """ p = np.copy(np.array(p0)) # If we have no lnprob or logls compute them if lnprob0 is None or lnlike0 is None: fn = PTLikePrior(self.logl, self.logp, self.loglargs, self.logpargs, self.loglkwargs, self.logpkwargs) if self.pool is None: results = list(map(fn, p.reshape((-1, self.dim)))) else: results = list(self.pool.map(fn, p.reshape((-1, self.dim)))) logls = np.array([r[0] for r in results]).reshape((self.ntemps, self.nwalkers)) logps = np.array([r[1] for r in results]).reshape((self.ntemps, self.nwalkers)) lnlike0 = logls lnprob0 = logls * self.betas.reshape((self.ntemps, 1)) + logps lnprob = lnprob0 logl = lnlike0 # Expand the chain in advance of the iterations if storechain: nsave = iterations / thin if self._chain is None: isave = 0 self._chain = np.zeros((self.ntemps, self.nwalkers, nsave, self.dim)) self._lnprob = np.zeros((self.ntemps, self.nwalkers, nsave)) self._lnlikelihood = np.zeros((self.ntemps, self.nwalkers, nsave)) else: isave = self._chain.shape[2] self._chain = np.concatenate((self._chain, np.zeros((self.ntemps, self.nwalkers, nsave, self.dim))), axis=2) self._lnprob = np.concatenate((self._lnprob, np.zeros((self.ntemps, self.nwalkers, nsave))), axis=2) self._lnlikelihood = np.concatenate((self._lnlikelihood, np.zeros((self.ntemps, self.nwalkers, nsave))), axis=2) for i in range(iterations): for j in [0, 1]: jupdate = j jsample = (j + 1) % 2 pupdate = p[:, jupdate::2, :] psample = p[:, jsample::2, :] zs = np.exp(np.random.uniform(low=-np.log(self.a), high=np.log(self.a), size=(self.ntemps, self.nwalkers/2))) qs = np.zeros((self.ntemps, self.nwalkers/2, self.dim)) for k in range(self.ntemps): js = np.random.randint(0, high=self.nwalkers / 2, size=self.nwalkers / 2) qs[k, :, :] = psample[k, js, :] + zs[k, :].reshape( (self.nwalkers / 2, 1)) * (pupdate[k, :, :] - psample[k, js, :]) fn = PTLikePrior(self.logl, self.logp, self.loglargs, self.logpargs, self.loglkwargs, self.logpkwargs) if self.pool is None: results = list(map(fn, qs.reshape((-1, self.dim)))) else: results = list(self.pool.map(fn, qs.reshape((-1, self.dim)))) qslogls = np.array([r[0] for r in results]).reshape( (self.ntemps, self.nwalkers/2)) qslogps = np.array([r[1] for r in results]).reshape( (self.ntemps, self.nwalkers/2)) qslnprob = qslogls * self.betas.reshape((self.ntemps, 1)) \ + qslogps logpaccept = self.dim*np.log(zs) + qslnprob \ - lnprob[:, jupdate::2] logrs = np.log(np.random.uniform(low=0.0, high=1.0, size=(self.ntemps, self.nwalkers/2))) accepts = logrs < logpaccept accepts = accepts.flatten() pupdate.reshape((-1, self.dim))[accepts, :] = \ qs.reshape((-1, self.dim))[accepts, :] lnprob[:, jupdate::2].reshape((-1,))[accepts] = \ qslnprob.reshape((-1,))[accepts] logl[:, jupdate::2].reshape((-1,))[accepts] = \ qslogls.reshape((-1,))[accepts] accepts = accepts.reshape((self.ntemps, self.nwalkers/2)) self.nprop[:, jupdate::2] += 1.0 self.nprop_accepted[:, jupdate::2] += accepts p, lnprob, logl = self._temperature_swaps(p, lnprob, logl) if (i + 1) % thin == 0: if storechain: self._chain[:, :, isave, :] = p self._lnprob[:, :, isave, ] = lnprob self._lnlikelihood[:, :, isave] = logl isave += 1 yield p, lnprob, logl def _temperature_swaps(self, p, lnprob, logl): """ Perform parallel-tempering temperature swaps on the state in ``p`` with associated ``lnprob`` and ``logl``. """ ntemps = self.ntemps for i in range(ntemps - 1, 0, -1): bi = self.betas[i] bi1 = self.betas[i - 1] dbeta = bi1 - bi iperm = nr.permutation(self.nwalkers) i1perm = nr.permutation(self.nwalkers) raccept = np.log(nr.uniform(size=self.nwalkers)) paccept = dbeta * (logl[i, iperm] - logl[i - 1, i1perm]) self.nswap[i] += self.nwalkers self.nswap[i - 1] += self.nwalkers asel = (paccept > raccept) nacc = np.sum(asel) self.nswap_accepted[i] += nacc self.nswap_accepted[i - 1] += nacc ptemp = np.copy(p[i, iperm[asel], :]) ltemp = np.copy(logl[i, iperm[asel]]) prtemp = np.copy(lnprob[i, iperm[asel]]) p[i, iperm[asel], :] = p[i - 1, i1perm[asel], :] logl[i, iperm[asel]] = logl[i - 1, i1perm[asel]] lnprob[i, iperm[asel]] = lnprob[i - 1, i1perm[asel]] \ - dbeta * logl[i - 1, i1perm[asel]] p[i - 1, i1perm[asel], :] = ptemp logl[i - 1, i1perm[asel]] = ltemp lnprob[i - 1, i1perm[asel]] = prtemp + dbeta * ltemp return p, lnprob, logl def thermodynamic_integration_log_evidence(self, logls=None, fburnin=0.1): """ Thermodynamic integration estimate of the evidence. :param logls: (optional) The log-likelihoods to use for computing the thermodynamic evidence. If ``None`` (the default), use the stored log-likelihoods in the sampler. Should be of shape ``(Ntemps, Nwalkers, Nsamples)``. :param fburnin: (optional) The fraction of the chain to discard as burnin samples; only the final ``1-fburnin`` fraction of the samples will be used to compute the evidence; the default is ``fburnin = 0.1``. :return ``(lnZ, dlnZ)``: Returns an estimate of the log-evidence and the error associated with the finite number of temperatures at which the posterior has been sampled. The evidence is the integral of the un-normalized posterior over all of parameter space: .. math:: Z \\equiv \\int d\\theta \\, l(\\theta) p(\\theta) Thermodymanic integration is a technique for estimating the evidence integral using information from the chains at various temperatures. Let .. math:: Z(\\beta) = \\int d\\theta \\, l^\\beta(\\theta) p(\\theta) Then .. math:: \\frac{d \\ln Z}{d \\beta} = \\frac{1}{Z(\\beta)} \\int d\\theta l^\\beta p \\ln l = \\left \\langle \\ln l \\right \\rangle_\\beta so .. math:: \\ln Z(\\beta = 1) = \\int_0^1 d\\beta \\left \\langle \\ln l \\right\\rangle_\\beta By computing the average of the log-likelihood at the difference temperatures, the sampler can approximate the above integral. """ if logls is None: return self.thermodynamic_integration_log_evidence( logls=self.lnlikelihood, fburnin=fburnin) else: betas = np.concatenate((self.betas, np.array([0]))) betas2 = np.concatenate((self.betas[::2], np.array([0]))) istart = int(logls.shape[2] * fburnin + 0.5) mean_logls = np.mean(np.mean(logls, axis=1)[:, istart:], axis=1) mean_logls2 = mean_logls[::2] lnZ = -np.dot(mean_logls, np.diff(betas)) lnZ2 = -np.dot(mean_logls2, np.diff(betas2)) return lnZ, np.abs(lnZ - lnZ2) @property def betas(self): """ Returns the sequence of inverse temperatures in the ladder. """ return self._betas @property def chain(self): """ Returns the stored chain of samples; shape ``(Ntemps, Nwalkers, Nsteps, Ndim)``. """ return self._chain @property def flatchain(self): """Returns the stored chain, but flattened along the walker axis, so of shape ``(Ntemps, Nwalkers*Nsteps, Ndim)``. """ s = self.chain.shape return self._chain.reshape((s[0], -1, s[3])) @property def lnprobability(self): """ Matrix of lnprobability values; shape ``(Ntemps, Nwalkers, Nsteps)``. """ return self._lnprob @property def lnlikelihood(self): """ Matrix of ln-likelihood values; shape ``(Ntemps, Nwalkers, Nsteps)``. """ return self._lnlikelihood @property def tswap_acceptance_fraction(self): """ Returns an array of accepted temperature swap fractions for each temperature; shape ``(ntemps, )``. """ return self.nswap_accepted / self.nswap @property def acceptance_fraction(self): """ Matrix of shape ``(Ntemps, Nwalkers)`` detailing the acceptance fraction for each walker. """ return self.nprop_accepted / self.nprop @property def acor(self): """ Returns a matrix of autocorrelation lengths for each parameter in each temperature of shape ``(Ntemps, Ndim)``. """ return self.get_autocorr_time() def get_autocorr_time(self, window=50): """ Returns a matrix of autocorrelation lengths for each parameter in each temperature of shape ``(Ntemps, Ndim)``. :param window: (optional) The size of the windowing function. This is equivalent to the maximum number of lags to use. (default: 50) """ acors = np.zeros((self.ntemps, self.dim)) for i in range(self.ntemps): x = np.mean(self._chain[i, :, :, :], axis=0) acors[i, :] = autocorr.integrated_time(x, window=window) return acors emcee-2.1.0/emcee/sampler.py0000644000076500000240000001065412337405403015563 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- """ The base sampler class implementing various helpful functions. """ from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["Sampler"] import numpy as np class Sampler(object): """ An abstract sampler object that implements various helper functions :param dim: The number of dimensions in the parameter space. :param lnpostfn: A function that takes a vector in the parameter space as input and returns the natural logarithm of the posterior probability for that position. :param args: (optional) A list of extra positional arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. :param kwargs: (optional) A list of extra keyword arguments for ``lnpostfn``. ``lnpostfn`` will be called with the sequence ``lnpostfn(p, *args, **kwargs)``. """ def __init__(self, dim, lnprobfn, args=[], kwargs={}): self.dim = dim self.lnprobfn = lnprobfn self.args = args self.kwargs = kwargs # This is a random number generator that we can easily set the state # of without affecting the numpy-wide generator self._random = np.random.mtrand.RandomState() self.reset() @property def random_state(self): """ The state of the internal random number generator. In practice, it's the result of calling ``get_state()`` on a ``numpy.random.mtrand.RandomState`` object. You can try to set this property but be warned that if you do this and it fails, it will do so silently. """ return self._random.get_state() @random_state.setter # NOQA def random_state(self, state): """ Try to set the state of the random number generator but fail silently if it doesn't work. Don't say I didn't warn you... """ try: self._random.set_state(state) except: pass @property def acceptance_fraction(self): """ The fraction of proposed steps that were accepted. """ return self.naccepted / self.iterations @property def chain(self): """ A pointer to the Markov chain. """ return self._chain @property def flatchain(self): """ Alias of ``chain`` provided for compatibility. """ return self._chain @property def lnprobability(self): """ A list of the log-probability values associated with each step in the chain. """ return self._lnprob @property def acor(self): return self.get_autocorr_time() def get_autocorr_time(self, window=50): raise NotImplementedError("The acor method must be implemented " "by subclasses") def get_lnprob(self, p): """Return the log-probability at the given position.""" return self.lnprobfn(p, *self.args, **self.kwargs) def reset(self): """ Clear ``chain``, ``lnprobability`` and the bookkeeping parameters. """ self.iterations = 0 self.naccepted = 0 def clear_chain(self): """An alias for :func:`reset` kept for backwards compatibility.""" return self.reset() def sample(self, *args, **kwargs): raise NotImplementedError("The sampling routine must be implemented " "by subclasses") def run_mcmc(self, pos0, N, rstate0=None, lnprob0=None, **kwargs): """ Iterate :func:`sample` for ``N`` iterations and return the result. :param pos0: The initial position vector. :param N: The number of steps to run. :param lnprob0: (optional) The log posterior probability at position ``p0``. If ``lnprob`` is not provided, the initial value is calculated. :param rstate0: (optional) The state of the random number generator. See the :func:`random_state` property for details. :param kwargs: (optional) Other parameters that are directly passed to :func:`sample`. """ for results in self.sample(pos0, lnprob0, rstate0, iterations=N, **kwargs): pass return results emcee-2.1.0/emcee/tests.py0000644000076500000240000002040112337410734015254 0ustar dfmstaff00000000000000#!/usr/bin/env python # encoding: utf-8 """ Defines various nose unit tests """ import numpy as np from .mh import MHSampler from .ensemble import EnsembleSampler from .ptsampler import PTSampler logprecision = -4 def lnprob_gaussian(x, icov): return -np.dot(x, np.dot(icov, x)) / 2.0 def lnprob_gaussian_nan(x, icov): # if walker's parameters are zeros => return NaN if not (np.array(x)).any(): result = np.nan else: result = -np.dot(x, np.dot(icov, x)) / 2.0 return result def log_unit_sphere_volume(ndim): if ndim % 2 == 0: logfactorial = 0.0 for i in range(1, ndim / 2 + 1): logfactorial += np.log(i) return ndim / 2.0 * np.log(np.pi) - logfactorial else: logfactorial = 0.0 for i in range(1, ndim + 1, 2): logfactorial += np.log(i) return (ndim + 1) / 2.0 * np.log(2.0) \ + (ndim - 1) / 2.0 * np.log(np.pi) - logfactorial class LogLikeGaussian(object): def __init__(self, icov): """Initialize a gaussian PDF with the given inverse covariance matrix. If not ``None``, ``cutoff`` truncates the PDF at the given number of sigma from the origin (i.e. the PDF is non-zero only on an ellipse aligned with the principal axes of the distribution). Without this cutoff, thermodynamic integration with a flat prior is logarithmically divergent.""" self.icov = icov def __call__(self, x): dist2 = lnprob_gaussian(x, self.icov) return dist2 class LogPriorGaussian(object): def __init__(self, icov, cutoff=None): self.icov = icov self.cutoff = cutoff def __call__(self, x): dist2 = lnprob_gaussian(x, self.icov) if self.cutoff is not None: if -dist2 > self.cutoff * self.cutoff / 2.0: return float('-inf') else: return 0.0 else: return 0.0 def ln_flat(x): return 0.0 class Tests: def setUp(self): self.nwalkers = 100 self.ndim = 5 self.ntemp = 20 self.N = 1000 self.mean = np.zeros(self.ndim) self.cov = 0.5 - np.random.rand(self.ndim ** 2) \ .reshape((self.ndim, self.ndim)) self.cov = np.triu(self.cov) self.cov += self.cov.T - np.diag(self.cov.diagonal()) self.cov = np.dot(self.cov, self.cov) self.icov = np.linalg.inv(self.cov) self.p0 = [0.1 * np.random.randn(self.ndim) for i in range(self.nwalkers)] self.truth = np.random.multivariate_normal(self.mean, self.cov, 100000) def check_sampler(self, N=None, p0=None): if N is None: N = self.N if p0 is None: p0 = self.p0 for i in self.sampler.sample(p0, iterations=N): pass assert np.mean(self.sampler.acceptance_fraction) > 0.25 assert np.all(self.sampler.acceptance_fraction > 0) chain = self.sampler.flatchain maxdiff = 10. ** (logprecision) assert np.all((np.mean(chain, axis=0) - self.mean) ** 2 / self.N ** 2 < maxdiff) assert np.all((np.cov(chain, rowvar=0) - self.cov) ** 2 / self.N ** 2 < maxdiff) def check_pt_sampler(self, cutoff, N=None, p0=None): if N is None: N = self.N if p0 is None: p0 = self.p0 for i in self.sampler.sample(p0, iterations=N): pass # Weaker assertions on acceptance fraction assert np.mean(self.sampler.acceptance_fraction) > 0.1, \ "acceptance fraction < 0.1" assert np.mean(self.sampler.tswap_acceptance_fraction) > 0.1, \ "tswap acceptance frac < 0.1" maxdiff = 10.0 ** logprecision chain = np.reshape(self.sampler.chain[0, ...], (-1, self.sampler.chain.shape[-1])) # np.savetxt('/tmp/chain.dat', chain) log_volume = self.ndim * np.log(cutoff) \ + log_unit_sphere_volume(self.ndim) \ + 0.5 * np.log(np.linalg.det(self.cov)) gaussian_integral = self.ndim / 2.0 * np.log(2.0 * np.pi) \ + 0.5 * np.log(np.linalg.det(self.cov)) lnZ, dlnZ = self.sampler.thermodynamic_integration_log_evidence() print(self.sampler.get_autocorr_time()) assert np.abs(lnZ - (gaussian_integral - log_volume)) < 3 * dlnZ, \ ("evidence incorrect: {0:g} versus correct {1:g} (uncertainty " "{2:g})").format(lnZ, gaussian_integral - log_volume, dlnZ) assert np.all((np.mean(chain, axis=0) - self.mean) ** 2.0 / N ** 2.0 < maxdiff), 'mean incorrect' assert np.all((np.cov(chain, rowvar=0) - self.cov) ** 2.0 / N ** 2.0 < maxdiff), 'covariance incorrect' def test_mh(self): self.sampler = MHSampler(self.cov, self.ndim, lnprob_gaussian, args=[self.icov]) self.check_sampler(N=self.N * self.nwalkers, p0=self.p0[0]) def test_ensemble(self): self.sampler = EnsembleSampler(self.nwalkers, self.ndim, lnprob_gaussian, args=[self.icov]) self.check_sampler() def test_nan_lnprob(self): self.sampler = EnsembleSampler(self.nwalkers, self.ndim, lnprob_gaussian_nan, args=[self.icov]) # If a walker is right at zero, ``lnprobfn`` returns ``np.nan``. p0 = self.p0 p0[0] = 0.0 try: self.check_sampler(p0=p0) except ValueError: # This should fail *immediately* with a ``ValueError``. return assert False, "We should never get here." def test_inf_nan_params(self): self.sampler = EnsembleSampler(self.nwalkers, self.ndim, lnprob_gaussian, args=[self.icov]) # Set one of the walkers to have a ``np.nan`` value. p0 = self.p0 p0[0][0] = np.nan try: self.check_sampler(p0=p0) except ValueError: # This should fail *immediately* with a ``ValueError``. pass else: assert False, "The sampler should have failed by now." # Set one of the walkers to have a ``np.inf`` value. p0[0][0] = np.inf try: self.check_sampler(p0=p0) except ValueError: # This should fail *immediately* with a ``ValueError``. pass else: assert False, "The sampler should have failed by now." # Set one of the walkers to have a ``np.inf`` value. p0[0][0] = -np.inf try: self.check_sampler(p0=p0) except ValueError: # This should fail *immediately* with a ``ValueError``. pass else: assert False, "The sampler should have failed by now." def test_parallel(self): self.sampler = EnsembleSampler(self.nwalkers, self.ndim, lnprob_gaussian, args=[self.icov], threads=2) self.check_sampler() def test_pt_sampler(self): cutoff = 10.0 self.sampler = PTSampler(self.ntemp, self.nwalkers, self.ndim, LogLikeGaussian(self.icov), LogPriorGaussian(self.icov, cutoff=cutoff)) p0 = np.random.multivariate_normal(mean=self.mean, cov=self.cov, size=(self.ntemp, self.nwalkers)) self.check_pt_sampler(cutoff, p0=p0, N=1000) def test_blobs(self): lnprobfn = lambda p: (-0.5 * np.sum(p ** 2), np.random.rand()) self.sampler = EnsembleSampler(self.nwalkers, self.ndim, lnprobfn) self.check_sampler() # Make sure that the shapes of everything are as expected. assert (self.sampler.chain.shape == (self.nwalkers, self.N, self.ndim) and len(self.sampler.blobs) == self.N and len(self.sampler.blobs[0]) == self.nwalkers), \ "The blob dimensions are wrong." # Make sure that the blobs aren't all the same. blobs = self.sampler.blobs assert np.any([blobs[-1] != blobs[i] for i in range(len(blobs) - 1)]) emcee-2.1.0/emcee/utils.py0000644000076500000240000000214012337405403015247 0ustar dfmstaff00000000000000#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import (division, print_function, absolute_import, unicode_literals) __all__ = ["sample_ball", "MH_proposal_axisaligned", "MPIPool"] import numpy as np from .mpi_pool import MPIPool def sample_ball(p0, std, size=1): """ Produce a ball of walkers around an initial parameter value. :param p0: The initial parameter value. :param std: The axis-aligned standard deviation. :param size: The number of samples to produce. """ assert(len(p0) == len(std)) return np.vstack([p0 + std * np.random.normal(size=len(p0)) for i in range(size)]) class MH_proposal_axisaligned(object): """ A Metropolis-Hastings proposal, with axis-aligned Gaussian steps, for convenient use as the ``mh_proposal`` option to :func:`EnsembleSampler.sample` . """ def __init__(self, stdev): self.stdev = stdev def __call__(self, X): (nw, npar) = X.shape assert(len(self.stdev) == npar) return X + self.stdev * np.random.normal(size=X.shape) emcee-2.1.0/emcee.egg-info/0000755000076500000240000000000012337423505015235 5ustar dfmstaff00000000000000emcee-2.1.0/emcee.egg-info/dependency_links.txt0000644000076500000240000000000112337423504021302 0ustar dfmstaff00000000000000 emcee-2.1.0/emcee.egg-info/PKG-INFO0000644000076500000240000001222412337423504016332 0ustar dfmstaff00000000000000Metadata-Version: 1.1 Name: emcee Version: 2.1.0 Summary: Kick ass affine-invariant ensemble MCMC sampling Home-page: http://dan.iel.fm/emcee/ Author: Daniel Foreman-Mackey Author-email: danfm@nyu.edu License: MIT Description: emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://secure.travis-ci.org/dfm/emcee.png?branch=master :target: http://travis-ci.org/dfm/emcee .. image:: https://pypip.in/d/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ .. image:: https://pypip.in/v/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ emcee is a stable, well tested Python implementation of the affine-invariant ensemble sampler for Markov chain Monte Carlo (MCMC) proposed by `Goodman & Weare (2010) `_. The code is open source and has already been used in several published projects in the Astrophysics literature. Documentation ------------- Read the docs at `dan.iel.fm/emcee `_. Attribution ----------- Please cite `Foreman-Mackey, Hogg, Lang & Goodman (2012) `_ if you find this code useful in your research and add your paper to `the testimonials list `_. The BibTeX entry for the paper is:: @article{emcee, author = {{Foreman-Mackey}, D. and {Hogg}, D.~W. and {Lang}, D. and {Goodman}, J.}, title = {emcee: The MCMC Hammer}, journal = {PASP}, year = 2013, volume = 125, pages = {306-312}, eprint = {1202.3665}, doi = {10.1086/670067} } License ------- Copyright 2010-2013 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. Changelog --------- .. :changelog: 2.1.0 (2014-05-22) ++++++++++++++++++ - Removing dependence on ``acor`` extension. - Added arguments to ``PTSampler`` function. - Added automatic load-balancing for MPI runs. - Added custom load-balancing for MPI and multiprocessing. - New default multiprocessing pool that supports ``^C``. 2.0.0 (2013-11-17) ++++++++++++++++++ - **Re-licensed under the MIT license!** - Clearer less verbose documentation. - Added checks for parameters becoming infinite or NaN. - Added checks for log-probability becoming NaN. - Improved parallelization and various other tweaks in ``PTSampler``. 1.2.0 (2013-01-30) ++++++++++++++++++ - Added a parallel tempering sampler ``PTSampler``. - Added instructions and utilities for using ``emcee`` with ``MPI``. - Added ``flatlnprobability`` property to the ``EnsembleSampler`` object to be consistent with the ``flatchain`` property. - Updated document for publication in PASP. - Various bug fixes. 1.1.3 (2012-11-22) ++++++++++++++++++ - Made the packaging system more robust even when numpy is not installed. 1.1.2 (2012-08-06) ++++++++++++++++++ - Another bug fix related to metadata blobs: the shape of the final ``blobs`` object was incorrect and all of the entries would generally be identical because we needed to copy the list that was appended at each step. Thanks goes to Jacqueline Chen (MIT) for catching this problem. 1.1.1 (2012-07-30) ++++++++++++++++++ - Fixed bug related to metadata blobs. The sample function was yielding the ``blobs`` object even when it wasn't expected. 1.1.0 (2012-07-28) ++++++++++++++++++ - Allow the ``lnprobfn`` to return arbitrary "blobs" of data as well as the log-probability. - Python 3 compatible (thanks Alex Conley)! - Various speed ups and clean ups in the core code base. - New documentation with better examples and more discussion. 1.0.1 (2012-03-31) ++++++++++++++++++ - Fixed transpose bug in the usage of ``acor`` in ``EnsembleSampler``. 1.0.0 (2012-02-15) ++++++++++++++++++ - Initial release. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python emcee-2.1.0/emcee.egg-info/requires.txt0000644000076500000240000000000512337423504017627 0ustar dfmstaff00000000000000numpyemcee-2.1.0/emcee.egg-info/SOURCES.txt0000644000076500000240000000060112337423504017115 0ustar dfmstaff00000000000000AUTHORS.rst HISTORY.rst LICENSE MANIFEST.in README.rst setup.py emcee/__init__.py emcee/autocorr.py emcee/ensemble.py emcee/interruptible_pool.py emcee/mh.py emcee/mpi_pool.py emcee/ptsampler.py emcee/sampler.py emcee/tests.py emcee/utils.py emcee.egg-info/PKG-INFO emcee.egg-info/SOURCES.txt emcee.egg-info/dependency_links.txt emcee.egg-info/requires.txt emcee.egg-info/top_level.txtemcee-2.1.0/emcee.egg-info/top_level.txt0000644000076500000240000000000612337423504017762 0ustar dfmstaff00000000000000emcee emcee-2.1.0/HISTORY.rst0000644000076500000240000000376412337423335014373 0ustar dfmstaff00000000000000.. :changelog: 2.1.0 (2014-05-22) ++++++++++++++++++ - Removing dependence on ``acor`` extension. - Added arguments to ``PTSampler`` function. - Added automatic load-balancing for MPI runs. - Added custom load-balancing for MPI and multiprocessing. - New default multiprocessing pool that supports ``^C``. 2.0.0 (2013-11-17) ++++++++++++++++++ - **Re-licensed under the MIT license!** - Clearer less verbose documentation. - Added checks for parameters becoming infinite or NaN. - Added checks for log-probability becoming NaN. - Improved parallelization and various other tweaks in ``PTSampler``. 1.2.0 (2013-01-30) ++++++++++++++++++ - Added a parallel tempering sampler ``PTSampler``. - Added instructions and utilities for using ``emcee`` with ``MPI``. - Added ``flatlnprobability`` property to the ``EnsembleSampler`` object to be consistent with the ``flatchain`` property. - Updated document for publication in PASP. - Various bug fixes. 1.1.3 (2012-11-22) ++++++++++++++++++ - Made the packaging system more robust even when numpy is not installed. 1.1.2 (2012-08-06) ++++++++++++++++++ - Another bug fix related to metadata blobs: the shape of the final ``blobs`` object was incorrect and all of the entries would generally be identical because we needed to copy the list that was appended at each step. Thanks goes to Jacqueline Chen (MIT) for catching this problem. 1.1.1 (2012-07-30) ++++++++++++++++++ - Fixed bug related to metadata blobs. The sample function was yielding the ``blobs`` object even when it wasn't expected. 1.1.0 (2012-07-28) ++++++++++++++++++ - Allow the ``lnprobfn`` to return arbitrary "blobs" of data as well as the log-probability. - Python 3 compatible (thanks Alex Conley)! - Various speed ups and clean ups in the core code base. - New documentation with better examples and more discussion. 1.0.1 (2012-03-31) ++++++++++++++++++ - Fixed transpose bug in the usage of ``acor`` in ``EnsembleSampler``. 1.0.0 (2012-02-15) ++++++++++++++++++ - Initial release. emcee-2.1.0/LICENSE0000644000076500000240000000212512301732255013466 0ustar dfmstaff00000000000000The MIT License (MIT) Copyright (c) 2010-2013 Daniel Foreman-Mackey & contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. emcee-2.1.0/MANIFEST.in0000644000076500000240000000006312301732255014216 0ustar dfmstaff00000000000000include README.rst LICENSE HISTORY.rst AUTHORS.rst emcee-2.1.0/PKG-INFO0000644000076500000240000001222412337423505013563 0ustar dfmstaff00000000000000Metadata-Version: 1.1 Name: emcee Version: 2.1.0 Summary: Kick ass affine-invariant ensemble MCMC sampling Home-page: http://dan.iel.fm/emcee/ Author: Daniel Foreman-Mackey Author-email: danfm@nyu.edu License: MIT Description: emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://secure.travis-ci.org/dfm/emcee.png?branch=master :target: http://travis-ci.org/dfm/emcee .. image:: https://pypip.in/d/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ .. image:: https://pypip.in/v/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ emcee is a stable, well tested Python implementation of the affine-invariant ensemble sampler for Markov chain Monte Carlo (MCMC) proposed by `Goodman & Weare (2010) `_. The code is open source and has already been used in several published projects in the Astrophysics literature. Documentation ------------- Read the docs at `dan.iel.fm/emcee `_. Attribution ----------- Please cite `Foreman-Mackey, Hogg, Lang & Goodman (2012) `_ if you find this code useful in your research and add your paper to `the testimonials list `_. The BibTeX entry for the paper is:: @article{emcee, author = {{Foreman-Mackey}, D. and {Hogg}, D.~W. and {Lang}, D. and {Goodman}, J.}, title = {emcee: The MCMC Hammer}, journal = {PASP}, year = 2013, volume = 125, pages = {306-312}, eprint = {1202.3665}, doi = {10.1086/670067} } License ------- Copyright 2010-2013 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. Changelog --------- .. :changelog: 2.1.0 (2014-05-22) ++++++++++++++++++ - Removing dependence on ``acor`` extension. - Added arguments to ``PTSampler`` function. - Added automatic load-balancing for MPI runs. - Added custom load-balancing for MPI and multiprocessing. - New default multiprocessing pool that supports ``^C``. 2.0.0 (2013-11-17) ++++++++++++++++++ - **Re-licensed under the MIT license!** - Clearer less verbose documentation. - Added checks for parameters becoming infinite or NaN. - Added checks for log-probability becoming NaN. - Improved parallelization and various other tweaks in ``PTSampler``. 1.2.0 (2013-01-30) ++++++++++++++++++ - Added a parallel tempering sampler ``PTSampler``. - Added instructions and utilities for using ``emcee`` with ``MPI``. - Added ``flatlnprobability`` property to the ``EnsembleSampler`` object to be consistent with the ``flatchain`` property. - Updated document for publication in PASP. - Various bug fixes. 1.1.3 (2012-11-22) ++++++++++++++++++ - Made the packaging system more robust even when numpy is not installed. 1.1.2 (2012-08-06) ++++++++++++++++++ - Another bug fix related to metadata blobs: the shape of the final ``blobs`` object was incorrect and all of the entries would generally be identical because we needed to copy the list that was appended at each step. Thanks goes to Jacqueline Chen (MIT) for catching this problem. 1.1.1 (2012-07-30) ++++++++++++++++++ - Fixed bug related to metadata blobs. The sample function was yielding the ``blobs`` object even when it wasn't expected. 1.1.0 (2012-07-28) ++++++++++++++++++ - Allow the ``lnprobfn`` to return arbitrary "blobs" of data as well as the log-probability. - Python 3 compatible (thanks Alex Conley)! - Various speed ups and clean ups in the core code base. - New documentation with better examples and more discussion. 1.0.1 (2012-03-31) ++++++++++++++++++ - Fixed transpose bug in the usage of ``acor`` in ``EnsembleSampler``. 1.0.0 (2012-02-15) ++++++++++++++++++ - Initial release. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python emcee-2.1.0/README.rst0000644000076500000240000000312012337410725014150 0ustar dfmstaff00000000000000emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://secure.travis-ci.org/dfm/emcee.png?branch=master :target: http://travis-ci.org/dfm/emcee .. image:: https://pypip.in/d/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ .. image:: https://pypip.in/v/emcee/badge.png :target: https://pypi.python.org/pypi/emcee/ emcee is a stable, well tested Python implementation of the affine-invariant ensemble sampler for Markov chain Monte Carlo (MCMC) proposed by `Goodman & Weare (2010) `_. The code is open source and has already been used in several published projects in the Astrophysics literature. Documentation ------------- Read the docs at `dan.iel.fm/emcee `_. Attribution ----------- Please cite `Foreman-Mackey, Hogg, Lang & Goodman (2012) `_ if you find this code useful in your research and add your paper to `the testimonials list `_. The BibTeX entry for the paper is:: @article{emcee, author = {{Foreman-Mackey}, D. and {Hogg}, D.~W. and {Lang}, D. and {Goodman}, J.}, title = {emcee: The MCMC Hammer}, journal = {PASP}, year = 2013, volume = 125, pages = {306-312}, eprint = {1202.3665}, doi = {10.1086/670067} } License ------- Copyright 2010-2013 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. emcee-2.1.0/setup.cfg0000644000076500000240000000007312337423505014306 0ustar dfmstaff00000000000000[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 emcee-2.1.0/setup.py0000755000076500000240000000316112337411052014175 0ustar dfmstaff00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- import os import sys import re try: from setuptools import setup setup except ImportError: from distutils.core import setup setup if sys.argv[-1] == "publish": os.system("python setup.py sdist upload") sys.exit() # Handle encoding major, minor1, minor2, release, serial = sys.version_info if major >= 3: def rd(filename): f = open(filename, encoding="utf-8") r = f.read() f.close() return r else: def rd(filename): f = open(filename) r = f.read() f.close() return r vre = re.compile("__version__ = \"(.*?)\"") m = rd(os.path.join(os.path.dirname(os.path.abspath(__file__)), "emcee", "__init__.py")) version = vre.findall(m)[0] setup( name="emcee", version=version, author="Daniel Foreman-Mackey", author_email="danfm@nyu.edu", packages=["emcee"], url="http://dan.iel.fm/emcee/", license="MIT", description="Kick ass affine-invariant ensemble MCMC sampling", long_description=rd("README.rst") + "\n\n" + "Changelog\n" + "---------\n\n" + rd("HISTORY.rst"), package_data={"": ["LICENSE", "AUTHORS.rst"]}, include_package_data=True, install_requires=["numpy"], classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", "Intended Audience :: Science/Research", "License :: OSI Approved :: MIT License", "Operating System :: OS Independent", "Programming Language :: Python", ], )