emcee-3.0.0/0000755000224100022410000000000013544412617012463 5ustar dforeman00000000000000emcee-3.0.0/PKG-INFO0000644000224100022410000000634213544412617013565 0ustar dforeman00000000000000Metadata-Version: 1.1 Name: emcee Version: 3.0.0 Summary: The Python ensemble sampling toolkit for affine-invariant MCMC Home-page: http://emcee.readthedocs.io Author: Daniel Foreman-Mackey Author-email: foreman.mackey@gmail.com License: MIT Description: emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://img.shields.io/badge/GitHub-dfm%2Femcee-blue.svg?style=flat :target: https://github.com/dfm/emcee .. image:: http://img.shields.io/travis/dfm/emcee/master.svg?style=flat :target: http://travis-ci.org/dfm/emcee .. image:: https://ci.appveyor.com/api/projects/status/p8smxvleh8mrcn6m?svg=true&style=flat :target: https://ci.appveyor.com/project/dfm/emcee .. image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat :target: https://github.com/dfm/emcee/blob/master/LICENSE .. image:: http://img.shields.io/badge/arXiv-1202.3665-orange.svg?style=flat :target: http://arxiv.org/abs/1202.3665 .. image:: https://coveralls.io/repos/github/dfm/emcee/badge.svg?branch=master&style=flat&v=2 :target: https://coveralls.io/github/dfm/emcee?branch=master .. image:: https://readthedocs.org/projects/emcee/badge/?version=latest :target: http://emcee.readthedocs.io/en/latest/?badge=latest 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 `emcee.readthedocs.io `_. 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-2017 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. 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-3.0.0/LICENSE0000644000224100022410000000212513536255717013477 0ustar dforeman00000000000000The MIT License (MIT) Copyright (c) 2010-2019 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-3.0.0/MANIFEST.in0000644000224100022410000000006313165454634014224 0ustar dforeman00000000000000include README.rst LICENSE HISTORY.rst AUTHORS.rst emcee-3.0.0/setup.py0000755000224100022410000000260213352757173014206 0ustar dforeman00000000000000#! /usr/bin/env python # -*- coding: utf-8 -*- import os import sys from setuptools import setup if sys.argv[-1] == "publish": os.system("python setup.py sdist; twine upload dist/*") sys.exit() # Hackishly inject a constant into builtins to enable importing of the # package before all the dependencies are built. if sys.version_info[0] < 3: import __builtin__ as builtins else: import builtins builtins.__EMCEE_SETUP__ = True import emcee # NOQA setup( name="emcee", version=emcee.__version__, author="Daniel Foreman-Mackey", author_email="foreman.mackey@gmail.com", packages=[ "emcee", "emcee.moves", "emcee.backends", "emcee.tests", "emcee.tests.unit", "emcee.tests.integration", ], url="http://emcee.readthedocs.io", license="MIT", description=("The Python ensemble sampling toolkit for affine-invariant " "MCMC"), long_description=open("README.rst").read(), 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", ], zip_safe=True, ) emcee-3.0.0/HISTORY.rst0000644000224100022410000000516313544412474014364 0ustar dforeman00000000000000.. :changelog: 3.0.0 (2019-09-30) ++++++++++++++++++ - Added progress bars using `tqdm `_. - Added HDF5 backend using `h5py `_. - Added new ``Move`` interface for more flexible specification of proposals. - Improved autocorrelation time estimation algorithm. - Switched documentation to using Jupyter notebooks for tutorials. - More details can be found `on the docs `_. 2.2.0 (2016-07-12) ++++++++++++++++++ - Improved autocorrelation time computation. - Numpy compatibility issues. - Fixed deprecated integer division behavior in PTSampler. 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-3.0.0/emcee.egg-info/0000755000224100022410000000000013544412617015233 5ustar dforeman00000000000000emcee-3.0.0/emcee.egg-info/PKG-INFO0000644000224100022410000000634213544412617016335 0ustar dforeman00000000000000Metadata-Version: 1.1 Name: emcee Version: 3.0.0 Summary: The Python ensemble sampling toolkit for affine-invariant MCMC Home-page: http://emcee.readthedocs.io Author: Daniel Foreman-Mackey Author-email: foreman.mackey@gmail.com License: MIT Description: emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://img.shields.io/badge/GitHub-dfm%2Femcee-blue.svg?style=flat :target: https://github.com/dfm/emcee .. image:: http://img.shields.io/travis/dfm/emcee/master.svg?style=flat :target: http://travis-ci.org/dfm/emcee .. image:: https://ci.appveyor.com/api/projects/status/p8smxvleh8mrcn6m?svg=true&style=flat :target: https://ci.appveyor.com/project/dfm/emcee .. image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat :target: https://github.com/dfm/emcee/blob/master/LICENSE .. image:: http://img.shields.io/badge/arXiv-1202.3665-orange.svg?style=flat :target: http://arxiv.org/abs/1202.3665 .. image:: https://coveralls.io/repos/github/dfm/emcee/badge.svg?branch=master&style=flat&v=2 :target: https://coveralls.io/github/dfm/emcee?branch=master .. image:: https://readthedocs.org/projects/emcee/badge/?version=latest :target: http://emcee.readthedocs.io/en/latest/?badge=latest 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 `emcee.readthedocs.io `_. 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-2017 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. 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-3.0.0/emcee.egg-info/zip-safe0000644000224100022410000000000113172150174016655 0ustar dforeman00000000000000 emcee-3.0.0/emcee.egg-info/SOURCES.txt0000644000224100022410000000236413544412617017124 0ustar dforeman00000000000000AUTHORS.rst HISTORY.rst LICENSE MANIFEST.in README.rst setup.cfg setup.py emcee/__init__.py emcee/autocorr.py emcee/ensemble.py emcee/interruptible_pool.py emcee/model.py emcee/mpi_pool.py emcee/pbar.py emcee/ptsampler.py emcee/state.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.txt emcee.egg-info/zip-safe emcee/backends/__init__.py emcee/backends/backend.py emcee/backends/hdf.py emcee/moves/__init__.py emcee/moves/de.py emcee/moves/de_snooker.py emcee/moves/gaussian.py emcee/moves/kde.py emcee/moves/mh.py emcee/moves/move.py emcee/moves/red_blue.py emcee/moves/stretch.py emcee/moves/walk.py emcee/tests/__init__.py emcee/tests/integration/__init__.py emcee/tests/integration/test_de.py emcee/tests/integration/test_de_snooker.py emcee/tests/integration/test_gaussian.py emcee/tests/integration/test_kde.py emcee/tests/integration/test_proposal.py emcee/tests/integration/test_stretch.py emcee/tests/integration/test_walk.py emcee/tests/unit/__init__.py emcee/tests/unit/test_autocorr.py emcee/tests/unit/test_backends.py emcee/tests/unit/test_blobs.py emcee/tests/unit/test_sampler.py emcee/tests/unit/test_state.py emcee/tests/unit/test_stretch.pyemcee-3.0.0/emcee.egg-info/requires.txt0000644000224100022410000000000613544412617017627 0ustar dforeman00000000000000numpy emcee-3.0.0/emcee.egg-info/top_level.txt0000644000224100022410000000000613544412617017761 0ustar dforeman00000000000000emcee emcee-3.0.0/emcee.egg-info/dependency_links.txt0000644000224100022410000000000113544412617021301 0ustar dforeman00000000000000 emcee-3.0.0/AUTHORS.rst0000644000224100022410000000014613536424561014345 0ustar dforeman00000000000000The list of contributors can be found `on GitHub `_.emcee-3.0.0/setup.cfg0000644000224100022410000000007513544412617014306 0ustar dforeman00000000000000[wheel] universal = 1 [egg_info] tag_build = tag_date = 0 emcee-3.0.0/README.rst0000644000224100022410000000430513200721425014141 0ustar dforeman00000000000000emcee ===== **The Python ensemble sampling toolkit for affine-invariant MCMC** .. image:: https://img.shields.io/badge/GitHub-dfm%2Femcee-blue.svg?style=flat :target: https://github.com/dfm/emcee .. image:: http://img.shields.io/travis/dfm/emcee/master.svg?style=flat :target: http://travis-ci.org/dfm/emcee .. image:: https://ci.appveyor.com/api/projects/status/p8smxvleh8mrcn6m?svg=true&style=flat :target: https://ci.appveyor.com/project/dfm/emcee .. image:: http://img.shields.io/badge/license-MIT-blue.svg?style=flat :target: https://github.com/dfm/emcee/blob/master/LICENSE .. image:: http://img.shields.io/badge/arXiv-1202.3665-orange.svg?style=flat :target: http://arxiv.org/abs/1202.3665 .. image:: https://coveralls.io/repos/github/dfm/emcee/badge.svg?branch=master&style=flat&v=2 :target: https://coveralls.io/github/dfm/emcee?branch=master .. image:: https://readthedocs.org/projects/emcee/badge/?version=latest :target: http://emcee.readthedocs.io/en/latest/?badge=latest 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 `emcee.readthedocs.io `_. 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-2017 Dan Foreman-Mackey and contributors. emcee is free software made available under the MIT License. For details see the LICENSE file. emcee-3.0.0/emcee/0000755000224100022410000000000013544412617013541 5ustar dforeman00000000000000emcee-3.0.0/emcee/moves/0000755000224100022410000000000013544412617014672 5ustar dforeman00000000000000emcee-3.0.0/emcee/moves/stretch.py0000644000224100022410000000171213200076162016707 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .red_blue import RedBlueMove __all__ = ["StretchMove"] class StretchMove(RedBlueMove): """ A `Goodman & Weare (2010) `_ "stretch move" with parallelization as described in `Foreman-Mackey et al. (2013) `_. :param a: (optional) The stretch scale parameter. (default: ``2.0``) """ def __init__(self, a=2.0, **kwargs): self.a = a super(StretchMove, self).__init__(**kwargs) def get_proposal(self, s, c, random): c = np.concatenate(c, axis=0) Ns, Nc = len(s), len(c) ndim = s.shape[1] zz = ((self.a - 1.) * random.rand(Ns) + 1) ** 2. / self.a factors = (ndim - 1.) * np.log(zz) rint = random.randint(Nc, size=(Ns,)) return c[rint] - (c[rint] - s) * zz[:, None], factors emcee-3.0.0/emcee/moves/kde.py0000644000224100022410000000237413200076162016003 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np try: from scipy.stats import gaussian_kde except ImportError: gaussian_kde = None from .red_blue import RedBlueMove __all__ = ["KDEMove"] class KDEMove(RedBlueMove): """A proposal using a KDE of the complementary ensemble This is a simplified version of the method used in `kombine `_. If you use this proposal, you should use *a lot* of walkers in your ensemble. Args: bw_method: The bandwidth estimation method. See `the scipy docs `_ for allowed values. """ def __init__(self, bw_method=None, **kwargs): if gaussian_kde is None: raise ImportError("you need scipy.stats.gaussian_kde to use the " "KDEMove") self.bw_method = bw_method super(KDEMove, self).__init__(**kwargs) def get_proposal(self, s, c, random): c = np.concatenate(c, axis=0) kde = gaussian_kde(c.T, bw_method=self.bw_method) q = kde.resample(len(s)) factor = kde.logpdf(s.T) - kde.logpdf(q) return q.T, factor emcee-3.0.0/emcee/moves/__init__.py0000644000224100022410000000074313413432536017004 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function from .move import Move from .mh import MHMove from .gaussian import GaussianMove from .red_blue import RedBlueMove from .stretch import StretchMove from .walk import WalkMove from .kde import KDEMove from .de import DEMove from .de_snooker import DESnookerMove __all__ = [ "Move", "MHMove", "GaussianMove", "RedBlueMove", "StretchMove", "WalkMove", "KDEMove", "DEMove", "DESnookerMove", ] emcee-3.0.0/emcee/moves/walk.py0000644000224100022410000000216613200076162016175 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .red_blue import RedBlueMove __all__ = ["WalkMove"] class WalkMove(RedBlueMove): """ A `Goodman & Weare (2010) `_ "walk move" with parallelization as described in `Foreman-Mackey et al. (2013) `_. :param s: (optional) The number of helper walkers to use. By default it will use all the walkers in the complement. """ def __init__(self, s=None, **kwargs): self.s = s super(WalkMove, self).__init__(**kwargs) def get_proposal(self, s, c, random): c = np.concatenate(c, axis=0) Ns, Nc = len(s), len(c) ndim = s.shape[1] q = np.empty((Ns, ndim), dtype=np.float64) s0 = Nc if self.s is None else self.s for i in range(Ns): inds = random.choice(Nc, s0, replace=False) cov = np.atleast_2d(np.cov(c[inds], rowvar=0)) q[i] = random.multivariate_normal(s[i], cov) return q, np.zeros(Ns, dtype=np.float64) emcee-3.0.0/emcee/moves/mh.py0000644000224100022410000000461213536251700015646 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .move import Move from ..state import State __all__ = ["MHMove"] class MHMove(Move): r"""A general Metropolis-Hastings proposal Concrete implementations can be made by providing a ``proposal_function`` argument that implements the proposal as described below. For standard Gaussian Metropolis moves, :class:`moves.GaussianMove` can be used. Args: proposal_function: The proposal function. It should take 2 arguments: a numpy-compatible random number generator and a ``(K, ndim)`` list of coordinate vectors. This function should return the proposed position and the log-ratio of the proposal probabilities (:math:`\ln q(x;\,x^\prime) - \ln q(x^\prime;\,x)` where :math:`x^\prime` is the proposed coordinate). ndim (Optional[int]): If this proposal is only valid for a specific dimension of parameter space, set that here. """ def __init__(self, proposal_function, ndim=None): self.ndim = ndim self.get_proposal = proposal_function def propose(self, model, state): """Use the move to generate a proposal and compute the acceptance Args: coords: The initial coordinates of the walkers. log_probs: The initial log probabilities of the walkers. log_prob_fn: A function that computes the log probabilities for a subset of walkers. random: A numpy-compatible random number state. """ # Check to make sure that the dimensions match. nwalkers, ndim = state.coords.shape if self.ndim is not None and self.ndim != ndim: raise ValueError("Dimension mismatch in proposal") # Get the move-specific proposal. q, factors = self.get_proposal(state.coords, model.random) # Compute the lnprobs of the proposed position. new_log_probs, new_blobs = model.compute_log_prob_fn(q) # Loop over the walkers and update them accordingly. lnpdiff = new_log_probs - state.log_prob + factors accepted = np.log(model.random.rand(nwalkers)) < lnpdiff # Update the parameters new_state = State(q, log_prob=new_log_probs, blobs=new_blobs) state = self.update(state, new_state, accepted) return state, accepted emcee-3.0.0/emcee/moves/de_snooker.py0000644000224100022410000000300713200101253017350 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .red_blue import RedBlueMove __all__ = ["DESnookerMove"] class DESnookerMove(RedBlueMove): """A snooker proposal using differential evolution. Based on `Ter Braak & Vrugt (2008) `_. Credit goes to GitHub user `mdanthony17 `_ for proposing this as an addition to the original emcee package. Args: gammas (Optional[float]): The mean stretch factor for the proposal vector. By default, it is :math:`1.7` as recommended by the reference. """ def __init__(self, gammas=1.7, **kwargs): self.gammas = gammas kwargs["nsplits"] = 4 super(DESnookerMove, self).__init__(**kwargs) def get_proposal(self, s, c, random): Ns = len(s) Nc = list(map(len, c)) ndim = s.shape[1] q = np.empty((Ns, ndim), dtype=np.float64) metropolis = np.empty(Ns, dtype=np.float64) for i in range(Ns): w = np.array([c[j][random.randint(Nc[j])] for j in range(3)]) random.shuffle(w) z, z1, z2 = w delta = s[i] - z norm = np.linalg.norm(delta) u = delta / np.sqrt(norm) q[i] = s[i] + u * self.gammas * (np.dot(u, z1) - np.dot(u, z2)) metropolis[i] = np.log(np.linalg.norm(q[i]-z)) - np.log(norm) return q, 0.5 * (ndim - 1.0) * metropolis emcee-3.0.0/emcee/moves/red_blue.py0000644000224100022410000000777413536236145017045 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .move import Move from ..state import State __all__ = ["RedBlueMove"] class RedBlueMove(Move): """ An abstract red-blue ensemble move with parallelization as described in `Foreman-Mackey et al. (2013) `_. Args: nsplits (Optional[int]): The number of sub-ensembles to use. Each sub-ensemble is updated in parallel using the other sets as the complementary ensemble. The default value is ``2`` and you probably won't need to change that. randomize_split (Optional[bool]): Randomly shuffle walkers between sub-ensembles. The same number of walkers will be assigned to each sub-ensemble on each iteration. By default, this is ``True``. live_dangerously (Optional[bool]): By default, an update will fail with a ``RuntimeError`` if the number of walkers is smaller than twice the dimension of the problem because the walkers would then be stuck on a low dimensional subspace. This can be avoided by switching between the stretch move and, for example, a Metropolis-Hastings step. If you want to do this and suppress the error, set ``live_dangerously = True``. Thanks goes (once again) to @dstndstn for this wonderful terminology. """ def __init__(self, nsplits=2, randomize_split=True, live_dangerously=False): self.nsplits = int(nsplits) self.live_dangerously = live_dangerously self.randomize_split = randomize_split def setup(self, coords): pass def get_proposal(self, sample, complement, random): raise NotImplementedError("The proposal must be implemented by " "subclasses") def propose(self, model, state): """Use the move to generate a proposal and compute the acceptance Args: coords: The initial coordinates of the walkers. log_probs: The initial log probabilities of the walkers. log_prob_fn: A function that computes the log probabilities for a subset of walkers. random: A numpy-compatible random number state. """ # Check that the dimensions are compatible. nwalkers, ndim = state.coords.shape if nwalkers < 2 * ndim and not self.live_dangerously: raise RuntimeError("It is unadvisable to use a red-blue move " "with fewer walkers than twice the number of " "dimensions.") # Run any move-specific setup. self.setup(state.coords) # Split the ensemble in half and iterate over these two halves. accepted = np.zeros(nwalkers, dtype=bool) all_inds = np.arange(nwalkers) inds = all_inds % self.nsplits if self.randomize_split: model.random.shuffle(inds) for split in range(self.nsplits): S1 = inds == split # Get the two halves of the ensemble. sets = [state.coords[inds == j] for j in range(self.nsplits)] s = sets[split] c = sets[:split] + sets[split+1:] # Get the move-specific proposal. q, factors = self.get_proposal(s, c, model.random) # Compute the lnprobs of the proposed position. new_log_probs, new_blobs = model.compute_log_prob_fn(q) # Loop over the walkers and update them accordingly. for i, (j, f, nlp) in enumerate(zip( all_inds[S1], factors, new_log_probs)): lnpdiff = f + nlp - state.log_prob[j] if lnpdiff > np.log(model.random.rand()): accepted[j] = True new_state = State(q, log_prob=new_log_probs, blobs=new_blobs) state = self.update(state, new_state, accepted, S1) return state, accepted emcee-3.0.0/emcee/moves/move.py0000644000224100022410000000324413413432536016212 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np __all__ = ["Move"] class Move(object): def tune(self, state, accepted): pass def update(self, old_state, new_state, accepted, subset=None): """Update a given subset of the ensemble with an accepted proposal Args: coords: The original ensemble coordinates. log_probs: The original log probabilities of the walkers. blobs: The original blobs. new_coords: The proposed coordinates. new_log_probs: The proposed log probabilities. new_blobs: The proposed blobs. accepted: A vector of booleans indicating which walkers were accepted. subset (Optional): A boolean mask indicating which walkers were included in the subset. This can be used, for example, when updating only the primary ensemble in a :class:`RedBlueMove`. """ if subset is None: subset = np.ones(len(old_state.coords), dtype=bool) m1 = subset & accepted m2 = accepted[subset] old_state.coords[m1] = new_state.coords[m2] old_state.log_prob[m1] = new_state.log_prob[m2] if new_state.blobs is not None: if old_state.blobs is None: raise ValueError( "If you start sampling with a given log_prob, " "you also need to provide the current list of " "blobs at that position.") old_state.blobs[m1] = new_state.blobs[m2] return old_state emcee-3.0.0/emcee/moves/de.py0000644000224100022410000000313413536251716015637 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .red_blue import RedBlueMove __all__ = ["DEMove"] class DEMove(RedBlueMove): r"""A proposal using differential evolution. This `Differential evolution proposal `_ is implemented following `Nelson et al. (2013) `_. Args: sigma (float): The standard deviation of the Gaussian used to stretch the proposal vector. gamma0 (Optional[float]): The mean stretch factor for the proposal vector. By default, it is :math:`2.38 / \sqrt{2\,\mathrm{ndim}}` as recommended by the two references. """ def __init__(self, sigma=1.0e-5, gamma0=None, **kwargs): self.sigma = sigma self.gamma0 = gamma0 kwargs["nsplits"] = 3 super(DEMove, self).__init__(**kwargs) def setup(self, coords): self.g0 = self.gamma0 if self.g0 is None: # Pure MAGIC: ndim = coords.shape[1] self.g0 = 2.38 / np.sqrt(2 * ndim) def get_proposal(self, s, c, random): Ns = len(s) Nc = list(map(len, c)) ndim = s.shape[1] q = np.empty((Ns, ndim), dtype=np.float64) f = self.sigma * random.randn(Ns) for i in range(Ns): w = np.array([c[j][random.randint(Nc[j])] for j in range(2)]) random.shuffle(w) g = np.diff(w, axis=0) * self.g0 + f[i] q[i] = s[i] + g return q, np.zeros(Ns, dtype=np.float64) emcee-3.0.0/emcee/moves/gaussian.py0000644000224100022410000000766413171442636017073 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from .mh import MHMove __all__ = ["GaussianMove"] class GaussianMove(MHMove): """A Metropolis step with a Gaussian proposal function. Args: cov: The covariance of the proposal function. This can be a scalar, vector, or matrix and the proposal will be assumed isotropic, axis-aligned, or general respectively. mode (Optional): Select the method used for updating parameters. This can be one of ``"vector"``, ``"random"``, or ``"sequential"``. The ``"vector"`` mode updates all dimensions simultaneously, ``"random"`` randomly selects a dimension and only updates that one, and ``"sequential"`` loops over dimensions and updates each one in turn. factor (Optional[float]): If provided the proposal will be made with a standard deviation uniformly selected from the range ``exp(U(-log(factor), log(factor))) * cov``. This is invalid for the ``"vector"`` mode. Raises: ValueError: If the proposal dimensions are invalid or if any of any of the other arguments are inconsistent. """ def __init__(self, cov, mode="vector", factor=None): # Parse the proposal type. try: float(cov) except TypeError: cov = np.atleast_1d(cov) if len(cov.shape) == 1: # A diagonal proposal was given. ndim = len(cov) proposal = _diagonal_proposal(np.sqrt(cov), factor, mode) elif len(cov.shape) == 2 and cov.shape[0] == cov.shape[1]: # The full, square covariance matrix was given. ndim = cov.shape[0] proposal = _proposal(cov, factor, mode) else: raise ValueError("Invalid proposal scale dimensions") else: # This was a scalar proposal. ndim = None proposal = _isotropic_proposal(np.sqrt(cov), factor, mode) super(GaussianMove, self).__init__(proposal, ndim=ndim) class _isotropic_proposal(object): allowed_modes = ["vector", "random", "sequential"] def __init__(self, scale, factor, mode): self.index = 0 self.scale = scale if factor is None: self._log_factor = None else: if factor < 1.0: raise ValueError("'factor' must be >= 1.0") self._log_factor = np.log(factor) if mode not in self.allowed_modes: raise ValueError(("'{0}' is not a recognized mode. " "Please select from: {1}") .format(mode, self.allowed_modes)) self.mode = mode def get_factor(self, rng): if self._log_factor is None: return 1.0 return np.exp(rng.uniform(-self._log_factor, self._log_factor)) def get_updated_vector(self, rng, x0): return x0 + self.get_factor(rng) * self.scale * rng.randn(*(x0.shape)) def __call__(self, x0, rng): nw, nd = x0.shape xnew = self.get_updated_vector(rng, x0) if self.mode == "random": m = (range(nw), rng.randint(x0.shape[-1], size=nw)) elif self.mode == "sequential": m = (range(nw), self.index % nd + np.zeros(nw, dtype=int)) self.index = (self.index + 1) % nd else: return xnew, np.zeros(nw) x = np.array(x0) x[m] = xnew[m] return x, np.zeros(nw) class _diagonal_proposal(_isotropic_proposal): def get_updated_vector(self, rng, x0): return x0 + self.get_factor(rng) * self.scale * rng.randn(*(x0.shape)) class _proposal(_isotropic_proposal): allowed_modes = ["vector"] def get_updated_vector(self, rng, x0): return x0 + self.get_factor(rng) * rng.multivariate_normal( np.zeros(len(self.scale)), self.scale) emcee-3.0.0/emcee/ptsampler.py0000644000224100022410000000076113200721403016107 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, absolute_import try: from ptemcee import Sampler as PTSampler except ImportError: class PTSampler(object): def __init__(self, *args, **kwargs): raise ImportError( "The PTSampler from emcee has been forked to " "https://github.com/willvousden/ptemcee, " "please install that package to continue using the PTSampler" ) __all__ = ["PTSampler"] emcee-3.0.0/emcee/mpi_pool.py0000644000224100022410000000073313171442636015734 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, absolute_import try: from schwimmbad import MPIPool except ImportError: class MPIPool(object): def __init__(self, *args, **kwargs): raise ImportError( "The MPIPool from emcee has been forked to " "https://github.com/adrn/schwimmbad, " "please install that package to continue using the MPIPool" ) __all__ = ["MPIPool"] emcee-3.0.0/emcee/autocorr.py0000644000224100022410000000752213353175363015761 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import logging import numpy as np __all__ = ["function_1d", "integrated_time", "AutocorrError"] def next_pow_two(n): """Returns the next power of two greater than or equal to `n`""" i = 1 while i < n: i = i << 1 return i def function_1d(x): """Estimate the normalized autocorrelation function of a 1-D series Args: x: The series as a 1-D numpy array. Returns: array: The autocorrelation function of the time series. """ x = np.atleast_1d(x) if len(x.shape) != 1: raise ValueError("invalid dimensions for 1D autocorrelation function") n = next_pow_two(len(x)) # Compute the FFT and then (from that) the auto-correlation function f = np.fft.fft(x - np.mean(x), n=2*n) acf = np.fft.ifft(f * np.conjugate(f))[:len(x)].real acf /= acf[0] return acf def auto_window(taus, c): m = np.arange(len(taus)) < c * taus if np.any(m): return np.argmin(m) return len(taus) - 1 def integrated_time(x, c=5, tol=50, quiet=False): """Estimate the integrated autocorrelation time of a time series. This estimate uses the iterative procedure described on page 16 of `Sokal's notes `_ to determine a reasonable window size. Args: 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. c (Optional[float]): The step size for the window search. (default: ``5``) tol (Optional[float]): The minimum number of autocorrelation times needed to trust the estimate. (default: ``50``) quiet (Optional[bool]): This argument controls the behavior when the chain is too short. If ``True``, give a warning instead of raising an :class:`AutocorrError`. (default: ``False``) Returns: float or array: An estimate of the integrated autocorrelation time of the time series ``x`` computed along the axis ``axis``. Raises AutocorrError: If the autocorrelation time can't be reliably estimated from the chain and ``quiet`` is ``False``. This normally means that the chain is too short. """ x = np.atleast_1d(x) if len(x.shape) == 1: x = x[:, np.newaxis, np.newaxis] if len(x.shape) == 2: x = x[:, :, np.newaxis] if len(x.shape) != 3: raise ValueError("invalid dimensions") n_t, n_w, n_d = x.shape tau_est = np.empty(n_d) windows = np.empty(n_d, dtype=int) # Loop over parameters for d in range(n_d): f = np.zeros(n_t) for k in range(n_w): f += function_1d(x[:, k, d]) f /= n_w taus = 2.0*np.cumsum(f)-1.0 windows[d] = auto_window(taus, c) tau_est[d] = taus[windows[d]] # Check convergence flag = tol * tau_est > n_t # Warn or raise in the case of non-convergence if np.any(flag): msg = ( "The chain is shorter than {0} times the integrated " "autocorrelation time for {1} parameter(s). Use this estimate " "with caution and run a longer chain!\n" ).format(tol, np.sum(flag)) msg += "N/{0} = {1:.0f};\ntau: {2}".format(tol, n_t/tol, tau_est) if not quiet: raise AutocorrError(tau_est, msg) logging.warning(msg) return tau_est class AutocorrError(Exception): """Raised if the chain is too short to estimate an autocorrelation time. The current estimate of the autocorrelation time can be accessed via the ``tau`` attribute of this exception. """ def __init__(self, tau, *args, **kwargs): self.tau = tau super(AutocorrError, self).__init__(*args, **kwargs) emcee-3.0.0/emcee/backends/0000755000224100022410000000000013544412617015313 5ustar dforeman00000000000000emcee-3.0.0/emcee/backends/backend.py0000644000224100022410000002000613352757173017260 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["Backend"] import numpy as np from .. import autocorr from ..state import State class Backend(object): """A simple default backend that stores the chain in memory""" def __init__(self): self.initialized = False def reset(self, nwalkers, ndim): """Clear the state of the chain and empty the backend Args: nwakers (int): The size of the ensemble ndim (int): The number of dimensions """ self.nwalkers = int(nwalkers) self.ndim = int(ndim) self.iteration = 0 self.accepted = np.zeros(self.nwalkers) self.chain = np.empty((0, self.nwalkers, self.ndim)) self.log_prob = np.empty((0, self.nwalkers)) self.blobs = None self.random_state = None self.initialized = True def has_blobs(self): """Returns ``True`` if the model includes blobs""" return self.blobs is not None def get_value(self, name, flat=False, thin=1, discard=0): if self.iteration <= 0: raise AttributeError("you must run the sampler with " "'store == True' before accessing the " "results") if name == "blobs" and not self.has_blobs(): return None v = getattr(self, name)[discard+thin-1:self.iteration:thin] if flat: s = list(v.shape[1:]) s[0] = np.prod(v.shape[:2]) return v.reshape(s) return v def get_chain(self, **kwargs): """Get the stored chain of MCMC samples Args: flat (Optional[bool]): Flatten the chain across the ensemble. (default: ``False``) thin (Optional[int]): Take only every ``thin`` steps from the chain. (default: ``1``) discard (Optional[int]): Discard the first ``discard`` steps in the chain as burn-in. (default: ``0``) Returns: array[..., nwalkers, ndim]: The MCMC samples. """ return self.get_value("chain", **kwargs) def get_blobs(self, **kwargs): """Get the chain of blobs for each sample in the chain Args: flat (Optional[bool]): Flatten the chain across the ensemble. (default: ``False``) thin (Optional[int]): Take only every ``thin`` steps from the chain. (default: ``1``) discard (Optional[int]): Discard the first ``discard`` steps in the chain as burn-in. (default: ``0``) Returns: array[..., nwalkers]: The chain of blobs. """ return self.get_value("blobs", **kwargs) def get_log_prob(self, **kwargs): """Get the chain of log probabilities evaluated at the MCMC samples Args: flat (Optional[bool]): Flatten the chain across the ensemble. (default: ``False``) thin (Optional[int]): Take only every ``thin`` steps from the chain. (default: ``1``) discard (Optional[int]): Discard the first ``discard`` steps in the chain as burn-in. (default: ``0``) Returns: array[..., nwalkers]: The chain of log probabilities. """ return self.get_value("log_prob", **kwargs) def get_last_sample(self): """Access the most recent sample in the chain""" if (not self.initialized) or self.iteration <= 0: raise AttributeError("you must run the sampler with " "'store == True' before accessing the " "results") it = self.iteration blobs = self.get_blobs(discard=it-1) if blobs is not None: blobs = blobs[0] return State( self.get_chain(discard=it-1)[0], log_prob=self.get_log_prob(discard=it-1)[0], blobs=blobs, random_state=self.random_state, ) def get_autocorr_time(self, discard=0, thin=1, **kwargs): """Compute an estimate of the autocorrelation time for each parameter Args: thin (Optional[int]): Use only every ``thin`` steps from the chain. The returned estimate is multiplied by ``thin`` so the estimated time is in units of steps, not thinned steps. (default: ``1``) discard (Optional[int]): Discard the first ``discard`` steps in the chain as burn-in. (default: ``0``) Other arguments are passed directly to :func:`emcee.autocorr.integrated_time`. Returns: array[ndim]: The integrated autocorrelation time estimate for the chain for each parameter. """ x = self.get_chain(discard=discard, thin=thin) return thin * autocorr.integrated_time(x, **kwargs) @property def shape(self): """The dimensions of the ensemble ``(nwalkers, ndim)``""" return self.nwalkers, self.ndim def _check_blobs(self, blobs): has_blobs = self.has_blobs() if has_blobs and blobs is None: raise ValueError("inconsistent use of blobs") if self.iteration > 0 and blobs is not None and not has_blobs: raise ValueError("inconsistent use of blobs") def grow(self, ngrow, blobs): """Expand the storage space by some number of samples Args: ngrow (int): The number of steps to grow the chain. blobs: The current list of blobs. This is used to compute the dtype for the blobs array. """ self._check_blobs(blobs) i = ngrow - (len(self.chain) - self.iteration) a = np.empty((i, self.nwalkers, self.ndim)) self.chain = np.concatenate((self.chain, a), axis=0) a = np.empty((i, self.nwalkers)) self.log_prob = np.concatenate((self.log_prob, a), axis=0) if blobs is not None: dt = np.dtype((blobs[0].dtype, blobs[0].shape)) a = np.empty((i, self.nwalkers), dtype=dt) if self.blobs is None: self.blobs = a else: self.blobs = np.concatenate((self.blobs, a), axis=0) def _check(self, state, accepted): self._check_blobs(state.blobs) nwalkers, ndim = self.shape has_blobs = self.has_blobs() if state.coords.shape != (nwalkers, ndim): raise ValueError("invalid coordinate dimensions; expected {0}" .format((nwalkers, ndim))) if state.log_prob.shape != (nwalkers, ): raise ValueError("invalid log probability size; expected {0}" .format(nwalkers)) if state.blobs is not None and not has_blobs: raise ValueError("unexpected blobs") if state.blobs is None and has_blobs: raise ValueError("expected blobs, but none were given") if state.blobs is not None and len(state.blobs) != nwalkers: raise ValueError("invalid blobs size; expected {0}" .format(nwalkers)) if accepted.shape != (nwalkers, ): raise ValueError("invalid acceptance size; expected {0}" .format(nwalkers)) def save_step(self, state, accepted): """Save a step to the backend Args: state (State): The :class:`State` of the ensemble. accepted (ndarray): An array of boolean flags indicating whether or not the proposal for each walker was accepted. """ self._check(state, accepted) self.chain[self.iteration, :, :] = state.coords self.log_prob[self.iteration, :] = state.log_prob if state.blobs is not None: self.blobs[self.iteration, :] = state.blobs self.accepted += accepted self.random_state = state.random_state self.iteration += 1 def __enter__(self): return self def __exit__(self, exception_type, exception_value, traceback): pass emcee-3.0.0/emcee/backends/hdf.py0000644000224100022410000001500013352757173016430 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["HDFBackend", "TempHDFBackend"] import os from tempfile import NamedTemporaryFile import numpy as np try: import h5py except ImportError: h5py = None from .backend import Backend from .. import __version__ class HDFBackend(Backend): """A backend that stores the chain in an HDF5 file using h5py .. note:: You must install `h5py `_ to use this backend. Args: filename (str): The name of the HDF5 file where the chain will be saved. name (str; optional): The name of the group where the chain will be saved. read_only (bool; optional): If ``True``, the backend will throw a ``RuntimeError`` if the file is opened with write access. """ def __init__(self, filename, name="mcmc", read_only=False): if h5py is None: raise ImportError("you must install 'h5py' to use the HDFBackend") self.filename = filename self.name = name self.read_only = read_only @property def initialized(self): if not os.path.exists(self.filename): return False try: with self.open() as f: return self.name in f except (OSError, IOError): return False def open(self, mode="r"): if self.read_only and mode != "r": raise RuntimeError("The backend has been loaded in read-only " "mode. Set `read_only = False` to make " "changes.") return h5py.File(self.filename, mode) def reset(self, nwalkers, ndim): """Clear the state of the chain and empty the backend Args: nwakers (int): The size of the ensemble ndim (int): The number of dimensions """ with self.open("a") as f: if self.name in f: del f[self.name] g = f.create_group(self.name) g.attrs["version"] = __version__ g.attrs["nwalkers"] = nwalkers g.attrs["ndim"] = ndim g.attrs["has_blobs"] = False g.attrs["iteration"] = 0 g.create_dataset("accepted", data=np.zeros(nwalkers)) g.create_dataset("chain", (0, nwalkers, ndim), maxshape=(None, nwalkers, ndim), dtype=np.float64) g.create_dataset("log_prob", (0, nwalkers), maxshape=(None, nwalkers), dtype=np.float64) def has_blobs(self): with self.open() as f: return f[self.name].attrs["has_blobs"] def get_value(self, name, flat=False, thin=1, discard=0): if not self.initialized: raise AttributeError("You must run the sampler with " "'store == True' before accessing the " "results") with self.open() as f: g = f[self.name] iteration = g.attrs["iteration"] if iteration <= 0: raise AttributeError("You must run the sampler with " "'store == True' before accessing the " "results") if name == "blobs" and not g.attrs["has_blobs"]: return None v = g[name][discard+thin-1:self.iteration:thin] if flat: s = list(v.shape[1:]) s[0] = np.prod(v.shape[:2]) return v.reshape(s) return v @property def shape(self): with self.open() as f: g = f[self.name] return g.attrs["nwalkers"], g.attrs["ndim"] @property def iteration(self): with self.open() as f: return f[self.name].attrs["iteration"] @property def accepted(self): with self.open() as f: return f[self.name]["accepted"][...] @property def random_state(self): with self.open() as f: elements = [ v for k, v in sorted(f[self.name].attrs.items()) if k.startswith("random_state_") ] return elements if len(elements) else None def grow(self, ngrow, blobs): """Expand the storage space by some number of samples Args: ngrow (int): The number of steps to grow the chain. blobs: The current list of blobs. This is used to compute the dtype for the blobs array. """ self._check_blobs(blobs) with self.open("a") as f: g = f[self.name] ntot = g.attrs["iteration"] + ngrow g["chain"].resize(ntot, axis=0) g["log_prob"].resize(ntot, axis=0) if blobs is not None: has_blobs = g.attrs["has_blobs"] if not has_blobs: nwalkers = g.attrs["nwalkers"] dt = np.dtype((blobs[0].dtype, blobs[0].shape)) g.create_dataset("blobs", (ntot, nwalkers), maxshape=(None, nwalkers), dtype=dt) else: g["blobs"].resize(ntot, axis=0) g.attrs["has_blobs"] = True def save_step(self, state, accepted): """Save a step to the backend Args: state (State): The :class:`State` of the ensemble. accepted (ndarray): An array of boolean flags indicating whether or not the proposal for each walker was accepted. """ self._check(state, accepted) with self.open("a") as f: g = f[self.name] iteration = g.attrs["iteration"] g["chain"][iteration, :, :] = state.coords g["log_prob"][iteration, :] = state.log_prob if state.blobs is not None: g["blobs"][iteration, :] = state.blobs g["accepted"][:] += accepted for i, v in enumerate(state.random_state): g.attrs["random_state_{0}".format(i)] = v g.attrs["iteration"] = iteration + 1 class TempHDFBackend(object): def __enter__(self): f = NamedTemporaryFile("w", delete=False) f.close() self.filename = f.name return HDFBackend(f.name, "test") def __exit__(self, exception_type, exception_value, traceback): os.remove(self.filename) emcee-3.0.0/emcee/backends/__init__.py0000644000224100022410000000066413206046210017415 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = [ "Backend", "HDFBackend", "TempHDFBackend", "get_test_backends", ] from .backend import Backend from .hdf import HDFBackend, TempHDFBackend def get_test_backends(): backends = [Backend] try: import h5py # NOQA except ImportError: pass else: backends.append(TempHDFBackend) return backends emcee-3.0.0/emcee/tests/0000755000224100022410000000000013544412617014703 5ustar dforeman00000000000000emcee-3.0.0/emcee/tests/unit/0000755000224100022410000000000013544412617015662 5ustar dforeman00000000000000emcee-3.0.0/emcee/tests/unit/test_autocorr.py0000644000224100022410000000241713352757173021143 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import pytest import numpy as np from emcee.autocorr import integrated_time, AutocorrError def get_chain(seed=1234, ndim=3, N=100000): np.random.seed(seed) a = 0.9 x = np.empty((N, ndim)) x[0] = np.zeros(ndim) for i in range(1, N): x[i] = x[i-1] * a + np.random.rand(ndim) return x def test_1d(seed=1234, ndim=1, N=250000): x = get_chain(seed=seed, ndim=ndim, N=N) tau = integrated_time(x) assert np.all(np.abs(tau - 19.0) / 19. < 0.2) def test_nd(seed=1234, ndim=3, N=150000): x = get_chain(seed=seed, ndim=ndim, N=N) tau = integrated_time(x) assert np.all(np.abs(tau - 19.0) / 19. < 0.2) def test_too_short(seed=1234, ndim=3, N=100): x = get_chain(seed=seed, ndim=ndim, N=N) with pytest.raises(AutocorrError): integrated_time(x) tau = integrated_time(x, quiet=True) # NOQA def test_autocorr_multi_works(): np.random.seed(42) xs = np.random.randn(16384, 2) # This throws exception unconditionally in buggy impl's acls_multi = integrated_time(xs) acls_single = np.array([integrated_time(xs[:, i]) for i in range(xs.shape[1])]) assert np.all(np.abs(acls_multi - acls_single) < 2) emcee-3.0.0/emcee/tests/unit/test_stretch.py0000644000224100022410000000166113413432536020750 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import warnings import pytest import numpy as np from emcee import moves from emcee.state import State from emcee.model import Model __all__ = ["test_live_dangerously"] def test_live_dangerously(nwalkers=32, nsteps=3000, seed=1234): warnings.filterwarnings("error") # Set up the random number generator. np.random.seed(seed) state = State(np.random.randn(nwalkers, 2 * nwalkers), log_prob=np.random.randn(nwalkers)) model = Model( None, lambda x: (np.zeros(len(x)), None), map, np.random ) proposal = moves.StretchMove() # Test to make sure that the error is thrown if there aren't enough # walkers. with pytest.raises(RuntimeError): proposal.propose(model, state) # Living dangerously... proposal.live_dangerously = True proposal.propose(model, state) emcee-3.0.0/emcee/tests/unit/test_blobs.py0000644000224100022410000000254213352757173020405 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import pytest import numpy as np from emcee import backends, EnsembleSampler __all__ = ["test_blob_shape"] class BlobLogProb(object): def __init__(self, blob_function): self.blob_function = blob_function def __call__(self, params): return -0.5 * np.sum(params**2), self.blob_function(params) @pytest.mark.parametrize("backend", backends.get_test_backends()) @pytest.mark.parametrize("blob_spec", [ (True, 5, lambda x: np.random.randn(5)), (True, 0, lambda x: np.random.randn()), (False, 2, lambda x: (1.0, np.random.randn(3))), (False, 0, lambda x: "face"), (False, 2, lambda x: (np.random.randn(5), "face")), ]) def test_blob_shape(backend, blob_spec): # HDF backends don't support the object type if backend in (backends.TempHDFBackend, ) and not blob_spec[0]: return with backend() as be: np.random.seed(42) model = BlobLogProb(blob_spec[2]) coords = np.random.randn(32, 3) nwalkers, ndim = coords.shape sampler = EnsembleSampler(nwalkers, ndim, model, backend=be) nsteps = 10 sampler.run_mcmc(coords, nsteps) shape = [nsteps, nwalkers] if blob_spec[1] > 0: shape += [blob_spec[1]] assert sampler.get_blobs().shape == tuple(shape) emcee-3.0.0/emcee/tests/unit/__init__.py0000644000224100022410000000000013352757173017767 0ustar dforeman00000000000000emcee-3.0.0/emcee/tests/unit/test_state.py0000644000224100022410000000216113352757173020421 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from emcee.state import State from emcee import EnsembleSampler def test_back_compat(seed=1234): np.random.seed(seed) coords = np.random.randn(16, 3) log_prob = np.random.randn(len(coords)) blobs = np.random.randn(len(coords)) rstate = np.random.get_state() state = State(coords, log_prob, blobs, rstate) c, l, r, b = state assert np.allclose(coords, c) assert np.allclose(log_prob, l) assert np.allclose(blobs, b) assert all(np.allclose(a, b) for a, b in zip(rstate[1:], r[1:])) state = State(coords, log_prob, None, rstate) c, l, r = state assert np.allclose(coords, c) assert np.allclose(log_prob, l) assert all(np.allclose(a, b) for a, b in zip(rstate[1:], r[1:])) def test_overwrite(seed=1234): np.random.seed(seed) def ll(x): return -0.5 * np.sum(x**2) nwalkers = 64 p0 = np.random.normal(size=(nwalkers, 1)) init = np.copy(p0) sampler = EnsembleSampler(nwalkers, 1, ll) sampler.run_mcmc(p0, 10) assert np.allclose(init, p0) emcee-3.0.0/emcee/tests/unit/test_backends.py0000644000224100022410000001514313352757173021057 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import os from itertools import product import numpy as np import pytest from emcee import backends, EnsembleSampler import h5py __all__ = ["test_backend", "test_reload"] all_backends = backends.get_test_backends() other_backends = all_backends[1:] dtypes = [ None, [("log_prior", float), ("mean", int)] ] def normal_log_prob(params): return -0.5 * np.sum(params**2) def normal_log_prob_blobs(params): return normal_log_prob(params), 0.1, int(5) def run_sampler(backend, nwalkers=32, ndim=3, nsteps=25, seed=1234, thin_by=1, dtype=None, blobs=True, lp=None): if lp is None: lp = normal_log_prob_blobs if blobs else normal_log_prob if seed is not None: np.random.seed(seed) coords = np.random.randn(nwalkers, ndim) sampler = EnsembleSampler(nwalkers, ndim, lp, backend=backend, blobs_dtype=dtype) sampler.run_mcmc(coords, nsteps, thin_by=thin_by) return sampler def _custom_allclose(a, b): if a.dtype.fields is None: assert np.allclose(a, b) else: for n in a.dtype.names: assert np.allclose(a[n], b[n]) def test_uninit(tmpdir): fn = str(tmpdir.join("EMCEE_TEST_FILE_DO_NOT_USE.h5")) if os.path.exists(fn): os.remove(fn) with backends.HDFBackend(fn) as be: run_sampler(be) assert os.path.exists(fn) os.remove(fn) @pytest.mark.parametrize("backend", all_backends) def test_uninit_errors(backend): with backend() as be: with pytest.raises(AttributeError): be.get_last_sample() for k in ["chain", "log_prob", "blobs"]: with pytest.raises(AttributeError): getattr(be, "get_" + k)() @pytest.mark.parametrize("backend", all_backends) def test_blob_usage_errors(backend): with backend() as be: run_sampler(be, blobs=True) with pytest.raises(ValueError): run_sampler(be, blobs=False) with backend() as be: run_sampler(be, blobs=False) with pytest.raises(ValueError): run_sampler(be, blobs=True) @pytest.mark.parametrize("backend,dtype,blobs", product(other_backends, dtypes, [True, False])) def test_backend(backend, dtype, blobs): # Run a sampler with the default backend. sampler1 = run_sampler(backends.Backend(), dtype=dtype, blobs=blobs) with backend() as be: sampler2 = run_sampler(be, dtype=dtype, blobs=blobs) values = ["chain", "log_prob"] if blobs: values += ["blobs"] else: assert sampler1.get_blobs() is None assert sampler2.get_blobs() is None # Check all of the components. for k in values: a = getattr(sampler1, "get_" + k)() b = getattr(sampler2, "get_" + k)() _custom_allclose(a, b) last1 = sampler1.get_last_sample() last2 = sampler2.get_last_sample() assert np.allclose(last1.coords, last2.coords) assert np.allclose(last1.log_prob, last2.log_prob) assert all(np.allclose(l1, l2) for l1, l2 in zip(last1.random_state[1:], last2.random_state[1:])) if blobs: _custom_allclose(last1.blobs, last2.blobs) else: assert last1.blobs is None and last2.blobs is None a = sampler1.acceptance_fraction b = sampler2.acceptance_fraction assert np.allclose(a, b), "inconsistent acceptance fraction" @pytest.mark.parametrize("backend,dtype", product(other_backends, dtypes)) def test_reload(backend, dtype): with backend() as backend1: run_sampler(backend1, dtype=dtype) # Test the state state = backend1.random_state np.random.set_state(state) # Load the file using a new backend object. backend2 = backends.HDFBackend(backend1.filename, backend1.name, read_only=True) with pytest.raises(RuntimeError): backend2.reset(32, 3) assert state[0] == backend2.random_state[0] assert all(np.allclose(a, b) for a, b in zip(state[1:], backend2.random_state[1:])) # Check all of the components. for k in ["chain", "log_prob", "blobs"]: a = backend1.get_value(k) b = backend2.get_value(k) _custom_allclose(a, b) last1 = backend1.get_last_sample() last2 = backend2.get_last_sample() assert np.allclose(last1.coords, last2.coords) assert np.allclose(last1.log_prob, last2.log_prob) assert all(np.allclose(l1, l2) for l1, l2 in zip(last1.random_state[1:], last2.random_state[1:])) _custom_allclose(last1.blobs, last2.blobs) a = backend1.accepted b = backend2.accepted assert np.allclose(a, b), "inconsistent accepted" @pytest.mark.parametrize("backend,dtype", product(other_backends, dtypes)) def test_restart(backend, dtype): # Run a sampler with the default backend. b = backends.Backend() run_sampler(b, dtype=dtype) sampler1 = run_sampler(b, seed=None, dtype=dtype) with backend() as be: run_sampler(be, dtype=dtype) sampler2 = run_sampler(be, seed=None, dtype=dtype) # Check all of the components. for k in ["chain", "log_prob", "blobs"]: a = getattr(sampler1, "get_" + k)() b = getattr(sampler2, "get_" + k)() _custom_allclose(a, b) last1 = sampler1.get_last_sample() last2 = sampler2.get_last_sample() assert np.allclose(last1.coords, last2.coords) assert np.allclose(last1.log_prob, last2.log_prob) assert all(np.allclose(l1, l2) for l1, l2 in zip(last1.random_state[1:], last2.random_state[1:])) _custom_allclose(last1.blobs, last2.blobs) a = sampler1.acceptance_fraction b = sampler2.acceptance_fraction assert np.allclose(a, b), "inconsistent acceptance fraction" def test_multi_hdf5(): with backends.TempHDFBackend() as backend1: run_sampler(backend1) backend2 = backends.HDFBackend(backend1.filename, name='mcmc2') run_sampler(backend2) chain2 = backend2.get_chain() with h5py.File(backend1.filename, 'r') as f: assert set(f.keys()) == {backend1.name, 'mcmc2'} backend1.reset(10, 2) assert np.allclose(backend2.get_chain(), chain2) with pytest.raises(AttributeError): backend1.get_chain() emcee-3.0.0/emcee/tests/unit/test_sampler.py0000644000224100022410000001656513544406211020744 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import pickle from itertools import product import pytest import numpy as np from emcee import moves, backends, EnsembleSampler __all__ = ["test_shapes", "test_errors", "test_thin", "test_vectorize"] all_backends = backends.get_test_backends() def normal_log_prob(params): return -0.5 * np.sum(params**2) @pytest.mark.parametrize("backend, moves", product( all_backends, [ None, moves.GaussianMove(0.5), [moves.StretchMove(), moves.GaussianMove(0.5)], [(moves.StretchMove(), 0.3), (moves.GaussianMove(0.5), 0.1)], ]) ) def test_shapes(backend, moves, nwalkers=32, ndim=3, nsteps=10, seed=1234): # Set up the random number generator. np.random.seed(seed) with backend() as be: # Initialize the ensemble, moves and sampler. coords = np.random.randn(nwalkers, ndim) sampler = EnsembleSampler(nwalkers, ndim, normal_log_prob, moves=moves, backend=be) # Run the sampler. sampler.run_mcmc(coords, nsteps) chain = sampler.get_chain() assert len(chain) == nsteps, "wrong number of steps" tau = sampler.get_autocorr_time(quiet=True) assert tau.shape == (ndim,) # Check the shapes. with pytest.warns(DeprecationWarning): assert sampler.chain.shape == (nwalkers, nsteps, ndim), \ "incorrect coordinate dimensions" with pytest.warns(DeprecationWarning): assert sampler.lnprobability.shape == (nwalkers, nsteps), \ "incorrect probability dimensions" assert sampler.get_chain().shape == (nsteps, nwalkers, ndim), \ "incorrect coordinate dimensions" assert sampler.get_log_prob().shape == (nsteps, nwalkers), \ "incorrect probability dimensions" assert sampler.acceptance_fraction.shape == (nwalkers,), \ "incorrect acceptance fraction dimensions" # Check the shape of the flattened coords. assert sampler.get_chain(flat=True).shape == \ (nsteps * nwalkers, ndim), "incorrect coordinate dimensions" assert sampler.get_log_prob(flat=True).shape == \ (nsteps*nwalkers,), "incorrect probability dimensions" @pytest.mark.parametrize("backend", all_backends) def test_errors(backend, nwalkers=32, ndim=3, nsteps=5, seed=1234): # Set up the random number generator. np.random.seed(seed) with backend() as be: # Initialize the ensemble, proposal, and sampler. coords = np.random.randn(nwalkers, ndim) sampler = EnsembleSampler(nwalkers, ndim, normal_log_prob, backend=be) # Test for not running. with pytest.raises(AttributeError): sampler.get_chain() with pytest.raises(AttributeError): sampler.get_log_prob() # What about not storing the chain. sampler.run_mcmc(coords, nsteps, store=False) with pytest.raises(AttributeError): sampler.get_chain() # Now what about if we try to continue using the sampler with an # ensemble of a different shape. sampler.run_mcmc(coords, nsteps, store=False) coords2 = np.random.randn(nwalkers, ndim+1) with pytest.raises(ValueError): list(sampler.run_mcmc(coords2, nsteps)) # Ensure that a warning is logged if the inital coords don't allow # the chain to explore all of parameter space, and that one is not # if we explicitly disable it, or the initial coords can. with pytest.warns(RuntimeWarning) as recorded_warnings: sampler.run_mcmc(np.ones((nwalkers, ndim)), nsteps) assert len(recorded_warnings) == 1 with pytest.warns(None) as recorded_warnings: sampler.run_mcmc(np.ones((nwalkers, ndim)), nsteps, skip_initial_state_check=True) sampler.run_mcmc(np.random.randn(nwalkers, ndim), nsteps) assert len(recorded_warnings) == 0 def run_sampler(backend, nwalkers=32, ndim=3, nsteps=25, seed=1234, thin=None, thin_by=1, progress=False, store=True): np.random.seed(seed) coords = np.random.randn(nwalkers, ndim) sampler = EnsembleSampler(nwalkers, ndim, normal_log_prob, backend=backend) sampler.run_mcmc(coords, nsteps, thin=thin, thin_by=thin_by, progress=progress, store=store) return sampler @pytest.mark.parametrize("backend", all_backends) def test_thin(backend): with backend() as be: with pytest.raises(ValueError): with pytest.warns(DeprecationWarning): run_sampler(be, thin=-1) with pytest.raises(ValueError): with pytest.warns(DeprecationWarning): run_sampler(be, thin=0.1) thinby = 3 sampler1 = run_sampler(None) with pytest.warns(DeprecationWarning): sampler2 = run_sampler(be, thin=thinby) for k in ["get_chain", "get_log_prob"]: a = getattr(sampler1, k)()[thinby-1::thinby] b = getattr(sampler2, k)() c = getattr(sampler1, k)(thin=thinby) assert np.allclose(a, b), "inconsistent {0}".format(k) assert np.allclose(a, c), "inconsistent {0}".format(k) @pytest.mark.parametrize("backend,progress", product(all_backends, [True, False])) def test_thin_by(backend, progress): with backend() as be: with pytest.raises(ValueError): run_sampler(be, thin_by=-1) with pytest.raises(ValueError): run_sampler(be, thin_by=0.1) nsteps = 25 thinby = 3 sampler1 = run_sampler(None, nsteps=nsteps*thinby, progress=progress) sampler2 = run_sampler(be, thin_by=thinby, progress=progress, nsteps=nsteps) for k in ["get_chain", "get_log_prob"]: a = getattr(sampler1, k)()[thinby-1::thinby] b = getattr(sampler2, k)() c = getattr(sampler1, k)(thin=thinby) assert np.allclose(a, b), "inconsistent {0}".format(k) assert np.allclose(a, c), "inconsistent {0}".format(k) assert sampler1.iteration == sampler2.iteration*thinby @pytest.mark.parametrize("backend", all_backends) def test_restart(backend): with backend() as be: sampler = run_sampler(be, nsteps=0) with pytest.raises(ValueError): sampler.run_mcmc(None, 10) sampler = run_sampler(be) sampler.run_mcmc(None, 10) with backend() as be: sampler = run_sampler(be, store=False) sampler.run_mcmc(None, 10) def test_vectorize(): def lp_vec(p): return -0.5 * np.sum(p**2, axis=1) np.random.seed(42) nwalkers, ndim = 32, 3 coords = np.random.randn(nwalkers, ndim) sampler = EnsembleSampler(nwalkers, ndim, lp_vec, vectorize=True) sampler.run_mcmc(coords, 10) assert sampler.get_chain().shape == (10, nwalkers, ndim) @pytest.mark.parametrize("backend", all_backends) def test_pickle(backend): with backend() as be: sampler1 = run_sampler(be) s = pickle.dumps(sampler1, -1) sampler2 = pickle.loads(s) for k in ["get_chain", "get_log_prob"]: a = getattr(sampler1, k)() b = getattr(sampler2, k)() assert np.allclose(a, b), "inconsistent {0}".format(k) emcee-3.0.0/emcee/tests/integration/0000755000224100022410000000000013544412617017226 5ustar dforeman00000000000000emcee-3.0.0/emcee/tests/integration/test_stretch.py0000644000224100022410000000132013352757173022315 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import pytest from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_stretch", "test_uniform_stretch", "test_nsplits_stretch"] @pytest.mark.parametrize("blobs", [True, False]) def test_normal_stretch(blobs, **kwargs): kwargs["blobs"] = blobs _test_normal(moves.StretchMove(), **kwargs) def test_uniform_stretch(**kwargs): _test_uniform(moves.StretchMove(), **kwargs) def test_nsplits_stretch(**kwargs): _test_normal(moves.StretchMove(nsplits=5), **kwargs) def test_randomize_stretch(**kwargs): _test_normal(moves.StretchMove(randomize_split=True), **kwargs) emcee-3.0.0/emcee/tests/integration/test_kde.py0000644000224100022410000000072313352757173021412 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_kde", "test_uniform_kde", "test_nsplits_kde"] def test_normal_kde(**kwargs): _test_normal(moves.KDEMove(), **kwargs) def test_uniform_kde(**kwargs): _test_uniform(moves.KDEMove(), **kwargs) def test_nsplits_kde(**kwargs): _test_normal(moves.KDEMove(nsplits=5), **kwargs) emcee-3.0.0/emcee/tests/integration/test_proposal.py0000644000224100022410000000502613413432536022476 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import numpy as np from scipy import stats import emcee __all__ = ["_test_normal", "_test_uniform"] def normal_log_prob_blobs(params): return -0.5 * np.sum(params**2), params def normal_log_prob(params): return -0.5 * np.sum(params**2) def uniform_log_prob(params): if np.any(params > 1) or np.any(params < 0): return -np.inf return 0.0 def _test_normal(proposal, ndim=1, nwalkers=32, nsteps=2000, seed=1234, check_acceptance=True, pool=None, blobs=False): # Set up the random number generator. np.random.seed(seed) # Initialize the ensemble and proposal. coords = np.random.randn(nwalkers, ndim) if blobs: lp = normal_log_prob_blobs else: lp = normal_log_prob sampler = emcee.EnsembleSampler(nwalkers, ndim, lp, moves=proposal, pool=pool) if hasattr(proposal, "ntune") and proposal.ntune > 0: coords = sampler.run_mcmc(coords, proposal.ntune, tune=True) sampler.reset() sampler.run_mcmc(coords, nsteps) # Check the acceptance fraction. if check_acceptance: acc = sampler.acceptance_fraction assert np.all((acc < 0.9) * (acc > 0.1)), \ "Invalid acceptance fraction\n{0}".format(acc) # Check the resulting chain using a K-S test and compare to the mean and # standard deviation. samps = sampler.get_chain(flat=True) mu, sig = np.mean(samps, axis=0), np.std(samps, axis=0) assert np.all(np.abs(mu) < 0.08), "Incorrect mean" assert np.all(np.abs(sig - 1) < 0.05), "Incorrect standard deviation" if ndim == 1: ks, _ = stats.kstest(samps[:, 0], "norm") assert ks < 0.05, "The K-S test failed" def _test_uniform(proposal, nwalkers=32, nsteps=2000, seed=1234): # Set up the random number generator. np.random.seed(seed) # Initialize the ensemble and proposal. coords = np.random.rand(nwalkers, 1) sampler = emcee.EnsembleSampler(nwalkers, 1, normal_log_prob, moves=proposal) sampler.run_mcmc(coords, nsteps) # Check the acceptance fraction. acc = sampler.acceptance_fraction assert np.all((acc < 0.9) * (acc > 0.1)), \ "Invalid acceptance fraction\n{0}".format(acc) # Check that the resulting chain "fails" the K-S test. samps = sampler.get_chain(flat=True) np.random.shuffle(samps) ks, _ = stats.kstest(samps[::100], "uniform") assert ks > 0.1, "The K-S test failed" emcee-3.0.0/emcee/tests/integration/test_de_snooker.py0000644000224100022410000000065413352757173023002 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_de_snooker", "test_uniform_de_snooker"] def test_normal_de_snooker(**kwargs): kwargs["nsteps"] = 4000 _test_normal(moves.DESnookerMove(), **kwargs) def test_uniform_de_snooker(**kwargs): _test_uniform(moves.DESnookerMove(), **kwargs) emcee-3.0.0/emcee/tests/integration/__init__.py0000644000224100022410000000000013352757173021333 0ustar dforeman00000000000000emcee-3.0.0/emcee/tests/integration/test_gaussian.py0000644000224100022410000000402713352757173022462 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function import pytest import numpy as np from itertools import product from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_gaussian", "test_uniform_gaussian", "test_normal_gaussian_nd"] @pytest.mark.parametrize("mode,factor", product( ["vector"], [None, 2.0, 5.0] )) def test_normal_gaussian(mode, factor, **kwargs): _test_normal(moves.GaussianMove(0.5, mode=mode, factor=factor), **kwargs) @pytest.mark.parametrize("mode,factor", product( ["vector", "random", "sequential"], [None, 2.0] )) def test_normal_gaussian_nd(mode, factor, **kwargs): ndim = 3 kwargs["nsteps"] = 8000 # Isotropic. _test_normal(moves.GaussianMove(0.5, factor=factor, mode=mode), ndim=ndim, **kwargs) # Axis-aligned. _test_normal(moves.GaussianMove(0.5 * np.ones(ndim), factor=factor, mode=mode), ndim=ndim, **kwargs) with pytest.raises(ValueError): _test_normal(moves.GaussianMove(0.5 * np.ones(ndim-1), factor=factor, mode=mode), ndim=ndim, **kwargs) # Full matrix. if mode == "vector": _test_normal(moves.GaussianMove(np.diag(0.5 * np.ones(ndim)), factor=factor, mode=mode), ndim=ndim, **kwargs) with pytest.raises(ValueError): _test_normal(moves.GaussianMove(np.diag(0.5 * np.ones(ndim-1))), ndim=ndim, **kwargs) else: with pytest.raises(ValueError): _test_normal(moves.GaussianMove(np.diag(0.5 * np.ones(ndim)), factor=factor, mode=mode), ndim=ndim, **kwargs) @pytest.mark.parametrize("mode,factor", product( ["vector"], [None, 2.0, 5.0] )) def test_uniform_gaussian(mode, factor, **kwargs): _test_uniform(moves.GaussianMove(0.5, factor=factor, mode=mode), **kwargs) emcee-3.0.0/emcee/tests/integration/test_walk.py0000644000224100022410000000056413352757173021610 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_walk", "test_uniform_walk"] def test_normal_walk(**kwargs): _test_normal(moves.WalkMove(s=3), **kwargs) def test_uniform_walk(**kwargs): _test_uniform(moves.WalkMove(s=3), **kwargs) emcee-3.0.0/emcee/tests/integration/test_de.py0000644000224100022410000000073313353175363021234 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function from emcee import moves from .test_proposal import _test_normal, _test_uniform __all__ = ["test_normal_de", "test_normal_de_no_gamma", "test_uniform_de"] def test_normal_de(**kwargs): _test_normal(moves.DEMove(), **kwargs) def test_normal_de_no_gamma(**kwargs): _test_normal(moves.DEMove(gamma0=1.0), **kwargs) def test_uniform_de(**kwargs): _test_uniform(moves.DEMove(), **kwargs) emcee-3.0.0/emcee/tests/__init__.py0000644000224100022410000000000013352757173017010 0ustar dforeman00000000000000emcee-3.0.0/emcee/__init__.py0000644000224100022410000000133313544406313015646 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import print_function, absolute_import __version__ = "3.0.0" __bibtex__ = """ @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} } """ # NOQA try: __EMCEE_SETUP__ except NameError: __EMCEE_SETUP__ = False if not __EMCEE_SETUP__: from .ensemble import EnsembleSampler from .state import State from . import moves from . import autocorr from . import backends __all__ = ["EnsembleSampler", "State", "moves", "autocorr", "backends"] emcee-3.0.0/emcee/interruptible_pool.py0000644000224100022410000000033313171442636020033 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["InterruptiblePool"] # The standard library now has an interruptible pool from multiprocessing.pool import Pool as InterruptiblePool emcee-3.0.0/emcee/model.py0000644000224100022410000000034513413432536015212 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["Model"] from collections import namedtuple Model = namedtuple( "Model", ("log_prob_fn", "compute_log_prob_fn", "map_fn", "random") ) emcee-3.0.0/emcee/utils.py0000644000224100022410000000330313353233411015241 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["sample_ball", "deprecated", "deprecation_warning"] import warnings from functools import wraps import numpy as np def deprecation_warning(msg): warnings.warn(msg, category=DeprecationWarning, stacklevel=2) def deprecated(alternate): def wrapper(func, alternate=alternate): msg = "'{0}' is deprecated.".format(func.__name__) if alternate is not None: msg += " Use '{0}' instead.".format(alternate) @wraps(func) def f(*args, **kwargs): deprecation_warning(msg) return func(*args, **kwargs) return f return wrapper @deprecated(None) 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)]) @deprecated(None) def sample_ellipsoid(p0, covmat, size=1): """ Produce an ellipsoid of walkers around an initial parameter value, according to a covariance matrix. :param p0: The initial parameter value. :param covmat: The covariance matrix. Must be symmetric-positive definite or it will raise the exception numpy.linalg.LinAlgError :param size: The number of samples to produce. """ return np.random.multivariate_normal(np.atleast_1d(p0), np.atleast_2d(covmat), size=size) emcee-3.0.0/emcee/ensemble.py0000644000224100022410000004651513544406211015711 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["EnsembleSampler"] try: from collections.abc import Iterable except ImportError: # for py2.7, will be an Exception in 3.8 from collections import Iterable import numpy as np import warnings from .state import State from .model import Model from .backends import Backend from .moves import StretchMove from .pbar import get_progress_bar from .utils import deprecated, deprecation_warning class EnsembleSampler(object): """An ensemble MCMC sampler Args: nwalkers (int): The number of walkers in the ensemble. ndim (int): Number of dimensions in the parameter space. log_prob_fn (callable): A function that takes a vector in the parameter space as input and returns the natural logarithm of the posterior probability (up to an additive constant) for that position. moves (Optional): This can be a single move object, a list of moves, or a "weighted" list of the form ``[(emcee.moves.StretchMove(), 0.1), ...]``. When running, the sampler will randomly select a move from this list (optionally with weights) for each proposal. (default: :class:`StretchMove`) args (Optional): A list of extra positional arguments for ``log_prob_fn``. ``log_prob_fn`` will be called with the sequence ``log_pprob_fn(p, *args, **kwargs)``. kwargs (Optional): A dict of extra keyword arguments for ``log_prob_fn``. ``log_prob_fn`` will be called with the sequence ``log_pprob_fn(p, *args, **kwargs)``. pool (Optional): An object with a ``map`` method that follows the same calling sequence as the built-in ``map`` function. This is generally used to compute the log-probabilities for the ensemble in parallel. backend (Optional): Either a :class:`backends.Backend` or a subclass (like :class:`backends.HDFBackend`) that is used to store and serialize the state of the chain. By default, the chain is stored as a set of numpy arrays in memory, but new backends can be written to support other mediums. vectorize (Optional[bool]): If ``True``, ``log_prob_fn`` is expected to accept a list of position vectors instead of just one. Note that ``pool`` will be ignored if this is ``True``. (default: ``False``) """ def __init__( self, nwalkers, ndim, log_prob_fn, pool=None, moves=None, args=None, kwargs=None, backend=None, vectorize=False, blobs_dtype=None, # Deprecated... a=None, postargs=None, threads=None, live_dangerously=None, runtime_sortingfn=None, ): # Warn about deprecated arguments if a is not None: deprecation_warning( "The 'a' argument is deprecated, use 'moves' instead" ) if threads is not None: deprecation_warning("The 'threads' argument is deprecated") if runtime_sortingfn is not None: deprecation_warning( "The 'runtime_sortingfn' argument is deprecated" ) if live_dangerously is not None: deprecation_warning( "The 'live_dangerously' argument is deprecated" ) # Parse the move schedule if moves is None: self._moves = [StretchMove()] self._weights = [1.0] elif isinstance(moves, Iterable): try: self._moves, self._weights = zip(*moves) except TypeError: self._moves = moves self._weights = np.ones(len(moves)) else: self._moves = [moves] self._weights = [1.0] self._weights = np.atleast_1d(self._weights).astype(float) self._weights /= np.sum(self._weights) self.pool = pool self.vectorize = vectorize self.blobs_dtype = blobs_dtype self.ndim = ndim self.nwalkers = nwalkers self.backend = Backend() if backend is None else backend # Deal with re-used backends if not self.backend.initialized: self._previous_state = None self.reset() state = np.random.get_state() else: # Check the backend shape if self.backend.shape != (self.nwalkers, self.ndim): raise ValueError( ( "the shape of the backend ({0}) is incompatible with the " "shape of the sampler ({1})" ).format(self.backend.shape, (self.nwalkers, self.ndim)) ) # Get the last random state state = self.backend.random_state if state is None: state = np.random.get_state() # Grab the last step so that we can restart it = self.backend.iteration if it > 0: self._previous_state = self.get_last_sample() # 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._random.set_state(state) # Do a little bit of _magic_ to make the likelihood call with # ``args`` and ``kwargs`` pickleable. self.log_prob_fn = _FunctionWrapper(log_prob_fn, args, kwargs) @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 iteration(self): return self.backend.iteration def reset(self): """ Reset the bookkeeping parameters """ self.backend.reset(self.nwalkers, self.ndim) def __getstate__(self): # In order to be generally picklable, we need to discard the pool # object before trying. d = self.__dict__ d["pool"] = None return d def sample( self, initial_state, log_prob0=None, # Deprecated rstate0=None, # Deprecated blobs0=None, # Deprecated iterations=1, tune=False, skip_initial_state_check=False, thin_by=1, thin=None, store=True, progress=False, ): """Advance the chain as a generator Args: initial_state (State or ndarray[nwalkers, ndim]): The initial :class:`State` or positions of the walkers in the parameter space. iterations (Optional[int]): The number of steps to generate. tune (Optional[bool]): If ``True``, the parameters of some moves will be automatically tuned. thin_by (Optional[int]): If you only want to store and yield every ``thin_by`` samples in the chain, set ``thin_by`` to an integer greater than 1. When this is set, ``iterations * thin_by`` proposals will be made. store (Optional[bool]): 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 analyze the samples after the fact (for burn-in for example) set ``store`` to ``False``. progress (Optional[bool or str]): If ``True``, a progress bar will be shown as the sampler progresses. If a string, will select a specific ``tqdm`` progress bar - most notable is ``'notebook'``, which shows a progress bar suitable for Jupyter notebooks. If ``False``, no progress bar will be shown. skip_initial_state_check (Optional[bool]): If ``True``, a check that the initial_state can fully explore the space will be skipped. (default: ``False``) Every ``thin_by`` steps, this generator yields the :class:`State` of the ensemble. """ # Interpret the input as a walker state and check the dimensions. state = State(initial_state, copy=True) if np.shape(state.coords) != (self.nwalkers, self.ndim): raise ValueError("incompatible input dimensions") if (not skip_initial_state_check) and np.isclose( np.linalg.det( np.cov(state.coords, rowvar=False).reshape( (self.ndim, self.ndim) ) ), 0, ): warnings.warn( "Initial state is not linearly independent and it will not " "allow a full exploration of parameter space", category=RuntimeWarning, ) # 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. if rstate0 is not None: deprecation_warning( "The 'rstate0' argument is deprecated, use a 'State' " "instead" ) state.random_state = rstate0 self.random_state = state.random_state # If the initial log-probabilities were not provided, calculate them # now. if log_prob0 is not None: deprecation_warning( "The 'log_prob0' argument is deprecated, use a 'State' " "instead" ) state.log_prob = log_prob0 if blobs0 is not None: deprecation_warning( "The 'blobs0' argument is deprecated, use a 'State' instead" ) state.blobs = blobs0 if state.log_prob is None: state.log_prob, state.blobs = self.compute_log_prob(state.coords) if np.shape(state.log_prob) != (self.nwalkers,): raise ValueError("incompatible input dimensions") # Check to make sure that the probability function didn't return # ``np.nan``. if np.any(np.isnan(state.log_prob)): raise ValueError("The initial log_prob was NaN") # Deal with deprecated thin argument if thin is not None: deprecation_warning( "The 'thin' argument is deprecated. " "Use 'thin_by' instead." ) # Check that the thin keyword is reasonable. thin = int(thin) if thin <= 0: raise ValueError("Invalid thinning argument") yield_step = 1 checkpoint_step = thin iterations = int(iterations) if store: nsaves = iterations // checkpoint_step self.backend.grow(nsaves, state.blobs) else: # Check that the thin keyword is reasonable. thin_by = int(thin_by) if thin_by <= 0: raise ValueError("Invalid thinning argument") yield_step = thin_by checkpoint_step = thin_by iterations = int(iterations) if store: self.backend.grow(iterations, state.blobs) # Set up a wrapper around the relevant model functions if self.pool is not None: map_fn = self.pool.map else: map_fn = map model = Model( self.log_prob_fn, self.compute_log_prob, map_fn, self._random ) # Inject the progress bar total = iterations * yield_step with get_progress_bar(progress, total) as pbar: i = 0 for _ in range(iterations): for _ in range(yield_step): # Choose a random move move = self._random.choice(self._moves, p=self._weights) # Propose state, accepted = move.propose(model, state) state.random_state = self.random_state if tune: move.tune(state, accepted) # Save the new step if store and (i + 1) % checkpoint_step == 0: self.backend.save_step(state, accepted) pbar.update(1) i += 1 # Yield the result as an iterator so that the user can do all # sorts of fun stuff with the results so far. yield state def run_mcmc(self, initial_state, nsteps, **kwargs): """ Iterate :func:`sample` for ``nsteps`` iterations and return the result Args: initial_state: The initial state or position vector. Can also be ``None`` to resume from where :func:``run_mcmc`` left off the last time it executed. nsteps: The number of steps to run. Other parameters are directly passed to :func:`sample`. This method returns the most recent result from :func:`sample`. """ if initial_state is None: if self._previous_state is None: raise ValueError( "Cannot have `initial_state=None` if run_mcmc has never " "been called." ) initial_state = self._previous_state results = None for results in self.sample(initial_state, iterations=nsteps, **kwargs): pass # Store so that the ``initial_state=None`` case will work self._previous_state = results return results def compute_log_prob(self, coords): """Calculate the vector of log-probability for the walkers Args: coords: (ndarray[..., ndim]) The position vector in parameter space where the probability should be calculated. This method returns: * log_prob: A vector of log-probabilities with one entry for each walker in this sub-ensemble. * blob: The list of meta data returned by the ``log_post_fn`` at this position or ``None`` if nothing was returned. """ p = coords # 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") # Run the log-probability calculations (optionally in parallel). if self.vectorize: results = self.log_prob_fn(p) else: # 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: map_func = self.pool.map else: map_func = map results = list( map_func(self.log_prob_fn, (p[i] for i in range(len(p)))) ) try: log_prob = np.array([float(l[0]) for l in results]) blob = [l[1:] for l in results] except (IndexError, TypeError): log_prob = np.array([float(l) for l in results]) blob = None else: # Get the blobs dtype if self.blobs_dtype is not None: dt = self.blobs_dtype else: try: dt = np.atleast_1d(blob[0]).dtype except ValueError: dt = np.dtype("object") blob = np.array(blob, dtype=dt) # Deal with single blobs properly shape = blob.shape[1:] if len(shape): axes = np.arange(len(shape))[np.array(shape) == 1] + 1 if len(axes): blob = np.squeeze(blob, tuple(axes)) # Check for log_prob returning NaN. if np.any(np.isnan(log_prob)): raise ValueError("Probability function returned NaN") return log_prob, blob @property def acceptance_fraction(self): """The fraction of proposed steps that were accepted""" return self.backend.accepted / float(self.backend.iteration) @property @deprecated("get_chain()") def chain(self): # pragma: no cover chain = self.get_chain() return np.swapaxes(chain, 0, 1) @property @deprecated("get_chain(flat=True)") def flatchain(self): # pragma: no cover return self.get_chain(flat=True) @property @deprecated("get_log_prob()") def lnprobability(self): # pragma: no cover log_prob = self.get_log_prob() return np.swapaxes(log_prob, 0, 1) @property @deprecated("get_log_prob(flat=True)") def flatlnprobability(self): # pragma: no cover return self.get_log_prob(flat=True) @property @deprecated("get_blobs()") def blobs(self): # pragma: no cover return self.get_blobs() @property @deprecated("get_blobs(flat=True)") def flatblobs(self): # pragma: no cover return self.get_blobs(flat=True) @property @deprecated("get_autocorr_time") def acor(self): # pragma: no cover return self.get_autocorr_time() def get_chain(self, **kwargs): return self.get_value("chain", **kwargs) get_chain.__doc__ = Backend.get_chain.__doc__ def get_blobs(self, **kwargs): return self.get_value("blobs", **kwargs) get_blobs.__doc__ = Backend.get_blobs.__doc__ def get_log_prob(self, **kwargs): return self.get_value("log_prob", **kwargs) get_log_prob.__doc__ = Backend.get_log_prob.__doc__ def get_last_sample(self, **kwargs): return self.backend.get_last_sample() get_last_sample.__doc__ = Backend.get_last_sample.__doc__ def get_value(self, name, **kwargs): return self.backend.get_value(name, **kwargs) def get_autocorr_time(self, **kwargs): return self.backend.get_autocorr_time(**kwargs) get_autocorr_time.__doc__ = Backend.get_autocorr_time.__doc__ class _FunctionWrapper(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 = [] if args is None else args self.kwargs = {} if kwargs is None else kwargs def __call__(self, x): try: return self.f(x, *self.args, **self.kwargs) except: # pragma: no cover 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-3.0.0/emcee/pbar.py0000644000224100022410000000247713536252760015053 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["get_progress_bar"] import logging try: import tqdm except ImportError: tqdm = None class _NoOpPBar(object): """This class implements the progress bar interface but does nothing""" def __init__(self): pass def __enter__(self, *args, **kwargs): return self def __exit__(self, *args, **kwargs): pass def update(self, count): pass def get_progress_bar(display, total): """Get a progress bar interface with given properties If the tqdm library is not installed, this will always return a "progress bar" that does nothing. Args: display (bool or str): Should the bar actually show the progress? Or a string to indicate which tqdm bar to use. total (int): The total size of the progress bar. """ if display: if tqdm is None: logging.warning( "You must install the tqdm library to use progress " "indicators with emcee" ) return _NoOpPBar() else: if display is True: return tqdm.tqdm(total=total) else: return getattr(tqdm, "tqdm_" + display)(total=total) return _NoOpPBar() emcee-3.0.0/emcee/state.py0000644000224100022410000000371013352757173015242 0ustar dforeman00000000000000# -*- coding: utf-8 -*- from __future__ import division, print_function __all__ = ["State"] import numpy as np from copy import deepcopy class State(object): """The state of the ensemble during an MCMC run For backwards compatibility, this will unpack into ``coords, log_prob, (blobs), random_state`` when iterated over (where ``blobs`` will only be included if it exists and is not ``None``). Args: coords (ndarray[nwalkers, ndim]): The current positions of the walkers in the parameter space. log_prob (ndarray[nwalkers, ndim], Optional): Log posterior probabilities for the walkers at positions given by ``coords``. blobs (Optional): The metadata “blobs” associated with the current position. The value is only returned if lnpostfn returns blobs too. random_state (Optional): The current state of the random number generator. """ __slots__ = "coords", "log_prob", "blobs", "random_state" def __init__(self, coords, log_prob=None, blobs=None, random_state=None, copy=False): dc = deepcopy if copy else lambda x: x if hasattr(coords, "coords"): self.coords = dc(coords.coords) self.log_prob = dc(coords.log_prob) self.blobs = dc(coords.blobs) self.random_state = dc(coords.random_state) return self.coords = dc(np.atleast_2d(coords)) self.log_prob = dc(log_prob) self.blobs = dc(blobs) self.random_state = dc(random_state) def __repr__(self): return "State({0}, log_prob={1}, blobs={2}, random_state={3})".format( self.coords, self.log_prob, self.blobs, self.random_state ) def __iter__(self): if self.blobs is None: return iter((self.coords, self.log_prob, self.random_state)) return iter((self.coords, self.log_prob, self.random_state, self.blobs))