# Path: emcee3/pools/default.py
# class DefaultPool(object):
# 
#     def map(self, fn, iterable):
#         return imap(fn, iterable)
# 
# Path: emcee3/model.py
# def is_model(model):
#     return (
#         hasattr(model, "compute_log_probability") and
#         callable(model.compute_log_probability)
#     )
# 
# class SimpleModel(Model):
#     """The simplest modeling interface.
# 
#     This model interface wraps functions describing the components of the
#     model. At a minimum, a function evaluating the log likelihood (up to a
#     constant) must be provided. In this case, the prior function is assumed to
#     be uniform and improper. All functions must have the call structure::
# 
#         log_likelihood(coords, *args)
# 
#     where ``coords`` is a coordinate vector and ``*args`` can be provided
#     using the ``args`` keyword argument. The ``log_likelihood`` and
#     ``log_prior`` functions should return scalars and the ``grad_*`` functions
#     should return ``numpy.array`` objects of the same length as the input
#     ``coords``.
# 
#     Args:
#         log_likelihood (callable): A function that evaluates the log
#             likelihood of the model.
#         log_prior (Optional[callable]): A function that evaluates the log
#             prior of the model. If not provided, this will be assumed to be
#             uniform and improper.
#         grad_log_likelihood (Optional[callable]): A function that evaluates the
#             gradient of the log likelihood of the model. If needed but not
#             provided, this will be evaluated numerically using a first order
#             method.
#         grad_log_prior (Optional[callable]): A function that evaluates the
#             gradient of the log prior of the model. If needed but not
#             provided, this will be evaluated numerically using a first order
#             method.
#         args (Optional[tuple]): Any other arguments to be provided to the
#             probability functions.
# 
#     """
# 
#     def __init__(self,
#                  log_likelihood,
#                  log_prior=None,
#                  grad_log_likelihood=None,
#                  grad_log_prior=None,
#                  args=tuple()):
#         # If no prior function is provided, we'll assume it to be flat and
#         # improper.
#         if log_prior is None:
#             log_prior = default_log_prior_function
#             grad_log_prior = default_grad_log_prior_function
# 
#         self.log_prior_func = log_prior
#         self.log_likelihood_func = log_likelihood
#         self.args = args
# 
#         # By default, numerically compute gradients.
#         if grad_log_prior is None:
#             grad_log_prior = numerical_gradient_1(self.log_prior_func)
#         self.grad_log_prior_func = grad_log_prior
#         if grad_log_likelihood is None:
#             grad_log_likelihood = numerical_gradient_1(
#                 self.log_likelihood_func)
#         self.grad_log_likelihood_func = grad_log_likelihood
# 
#     def compute_log_prior(self, state, **kwargs):
#         state.log_prior = self.log_prior_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_log_prior.__doc__ = Model.compute_log_prior.__doc__
# 
#     def compute_grad_log_prior(self, state, **kwargs):
#         state.grad_log_prior = self.grad_log_prior_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_grad_log_prior.__doc__ = Model.compute_grad_log_prior.__doc__
# 
#     def compute_log_likelihood(self, state, **kwargs):
#         state.log_likelihood = self.log_likelihood_func(state.coords,
#                                                         *(self.args))
#         return state
#     compute_log_likelihood.__doc__ = Model.compute_log_likelihood.__doc__
# 
#     def compute_grad_log_likelihood(self, state, **kwargs):
#         state.grad_log_likelihood = self.grad_log_likelihood_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_grad_log_likelihood.__doc__ = \
#         Model.compute_grad_log_likelihood.__doc__
# 
#     def check_grad(self, coords, **kwargs):
#         """Check the gradients numerically.
# 
#         Args:
#             coords (array): The coordinates.
# 
#         Returns:
#             bool: If the numerical gradients and analytic gradients satisfy
#                 ``numpy.allclose``.
# 
#         """
#         com_g = (
#             self.grad_log_likelihood_func(coords, *(self.args)) +
#             self.grad_log_prior_func(coords, *(self.args))
#         )
#         num_g = numerical_gradient_2(self.get_lnprob, coords, **kwargs)
#         return np.allclose(com_g, num_g)


import numpy as np
from .pools import DefaultPool
from .model import is_model, SimpleModel
__all__ = ["Ensemble"]


class Ensemble(object):
    """The state of the ensemble of walkers.

    Args:
        model (callable or Model): The model specification. This can be a
            callable, an instance of :class:`SimpleModel`, or another instance
            that quacks like a :class:`Model`. If ``model`` is a callable, it
            should take a coordinate vector as input and return the evaluated
            log-probability up to an additive constant.
        coords (Optional[array[nwalkers, ndim]]): The array of walker
            coordinate vectors.
        pool (Optional): A pool object that exposes a map function for
            parallelization purposes.
        random (Optional): A numpy-compatible random number generator. By
            default, this will be the built-in ``numpy.random`` module but if
            you want the ensemble to own its own state, you can supply an
            instance of ``numpy.random.RandomState``.

    """
    def __init__(self, model, coords, pool=None, random=None):
        if is_model(model):
            self.model = model
        else:
            if not callable(model):
                raise ValueError("the 'model' must have a "
                                 "'compute_log_probability' method or be "
                                 "callable")

# Path: emcee3/pools/default.py
# class DefaultPool(object):
# 
#     def map(self, fn, iterable):
#         return imap(fn, iterable)
# 
# Path: emcee3/model.py
# def is_model(model):
#     return (
#         hasattr(model, "compute_log_probability") and
#         callable(model.compute_log_probability)
#     )
# 
# class SimpleModel(Model):
#     """The simplest modeling interface.
# 
#     This model interface wraps functions describing the components of the
#     model. At a minimum, a function evaluating the log likelihood (up to a
#     constant) must be provided. In this case, the prior function is assumed to
#     be uniform and improper. All functions must have the call structure::
# 
#         log_likelihood(coords, *args)
# 
#     where ``coords`` is a coordinate vector and ``*args`` can be provided
#     using the ``args`` keyword argument. The ``log_likelihood`` and
#     ``log_prior`` functions should return scalars and the ``grad_*`` functions
#     should return ``numpy.array`` objects of the same length as the input
#     ``coords``.
# 
#     Args:
#         log_likelihood (callable): A function that evaluates the log
#             likelihood of the model.
#         log_prior (Optional[callable]): A function that evaluates the log
#             prior of the model. If not provided, this will be assumed to be
#             uniform and improper.
#         grad_log_likelihood (Optional[callable]): A function that evaluates the
#             gradient of the log likelihood of the model. If needed but not
#             provided, this will be evaluated numerically using a first order
#             method.
#         grad_log_prior (Optional[callable]): A function that evaluates the
#             gradient of the log prior of the model. If needed but not
#             provided, this will be evaluated numerically using a first order
#             method.
#         args (Optional[tuple]): Any other arguments to be provided to the
#             probability functions.
# 
#     """
# 
#     def __init__(self,
#                  log_likelihood,
#                  log_prior=None,
#                  grad_log_likelihood=None,
#                  grad_log_prior=None,
#                  args=tuple()):
#         # If no prior function is provided, we'll assume it to be flat and
#         # improper.
#         if log_prior is None:
#             log_prior = default_log_prior_function
#             grad_log_prior = default_grad_log_prior_function
# 
#         self.log_prior_func = log_prior
#         self.log_likelihood_func = log_likelihood
#         self.args = args
# 
#         # By default, numerically compute gradients.
#         if grad_log_prior is None:
#             grad_log_prior = numerical_gradient_1(self.log_prior_func)
#         self.grad_log_prior_func = grad_log_prior
#         if grad_log_likelihood is None:
#             grad_log_likelihood = numerical_gradient_1(
#                 self.log_likelihood_func)
#         self.grad_log_likelihood_func = grad_log_likelihood
# 
#     def compute_log_prior(self, state, **kwargs):
#         state.log_prior = self.log_prior_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_log_prior.__doc__ = Model.compute_log_prior.__doc__
# 
#     def compute_grad_log_prior(self, state, **kwargs):
#         state.grad_log_prior = self.grad_log_prior_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_grad_log_prior.__doc__ = Model.compute_grad_log_prior.__doc__
# 
#     def compute_log_likelihood(self, state, **kwargs):
#         state.log_likelihood = self.log_likelihood_func(state.coords,
#                                                         *(self.args))
#         return state
#     compute_log_likelihood.__doc__ = Model.compute_log_likelihood.__doc__
# 
#     def compute_grad_log_likelihood(self, state, **kwargs):
#         state.grad_log_likelihood = self.grad_log_likelihood_func(
#             state.coords, *(self.args)
#         )
#         return state
#     compute_grad_log_likelihood.__doc__ = \
#         Model.compute_grad_log_likelihood.__doc__
# 
#     def check_grad(self, coords, **kwargs):
#         """Check the gradients numerically.
# 
#         Args:
#             coords (array): The coordinates.
# 
#         Returns:
#             bool: If the numerical gradients and analytic gradients satisfy
#                 ``numpy.allclose``.
# 
#         """
#         com_g = (
#             self.grad_log_likelihood_func(coords, *(self.args)) +
#             self.grad_log_prior_func(coords, *(self.args))
#         )
#         num_g = numerical_gradient_2(self.get_lnprob, coords, **kwargs)
#         return np.allclose(com_g, num_g)


# Path: emcee3/ensemble.py
import numpy as np
from .pools import DefaultPool
from .model import is_model, SimpleModel

__all__ = ["Ensemble"]


class Ensemble(object):
    """The state of the ensemble of walkers.

    Args:
        model (callable or Model): The model specification. This can be a
            callable, an instance of :class:`SimpleModel`, or another instance
            that quacks like a :class:`Model`. If ``model`` is a callable, it
            should take a coordinate vector as input and return the evaluated
            log-probability up to an additive constant.
        coords (Optional[array[nwalkers, ndim]]): The array of walker
            coordinate vectors.
        pool (Optional): A pool object that exposes a map function for
            parallelization purposes.
        random (Optional): A numpy-compatible random number generator. By
            default, this will be the built-in ``numpy.random`` module but if
            you want the ensemble to own its own state, you can supply an
            instance of ``numpy.random.RandomState``.

    """
    def __init__(self, model, coords, pool=None, random=None):
        if is_model(model):
            self.model = model
        else:
            if not callable(model):
                raise ValueError("the 'model' must have a "
                                 "'compute_log_probability' method or be "
                                 "callable")

            self.model = SimpleModel(model)