# Path: elfi/methods/utils.py
# def numgrad(fn, x, h=None, replace_neg_inf=True):
#     """Naive numeric gradient implementation for scalar valued functions.
# 
#     Parameters
#     ----------
#     fn
#     x : np.ndarray
#         A single point in 1d vector
#     h : float or list
#         Stepsize or stepsizes for the dimensions
#     replace_neg_inf : bool
#         Replace neg inf fn values with gradient 0 (useful for logpdf gradients)
# 
#     Returns
#     -------
#     grad : np.ndarray
#         1D gradient vector
# 
#     """
#     h = 0.00001 if h is None else h
#     h = np.asanyarray(h).reshape(-1)
# 
#     x = np.asanyarray(x, dtype=float).reshape(-1)
#     dim = len(x)
#     X = np.zeros((dim * 3, dim))
# 
#     for i in range(3):
#         Xi = np.tile(x, (dim, 1))
#         np.fill_diagonal(Xi, Xi.diagonal() + (i - 1) * h)
#         X[i * dim:(i + 1) * dim, :] = Xi
# 
#     f = fn(X)
#     f = f.reshape((3, dim))
# 
#     if replace_neg_inf:
#         if np.any(np.isneginf(f)):
#             return np.zeros(dim)
# 
#     grad = np.gradient(f, *h, axis=0)
#     return grad[1, :]
# 
# Path: elfi/model/elfi_model.py
# class ComputationContext:
#     """Container object for key components for consistent computation results.
# 
#     Attributes
#     ----------
#     seed : int
#     batch_size : int
#     pool : OutputPool
#     num_submissions : int
#         Number of submissions using this context.
#     sub_seed_cache : dict
#         Caches the sub seed generation state variables. This is
# 
#     Notes
#     -----
#     The attributes are immutable.
# 
#     """
# 
#     def __init__(self, batch_size=None, seed=None, pool=None):
#         """Set up a ComputationContext.
# 
#         Parameters
#         ----------
#         batch_size : int, optional
#         seed : int, None, 'global', optional
#             When None generates a random integer seed. When `'global'` uses the global
#             numpy random state. Only recommended for debugging.
#         pool : elfi.OutputPool, optional
#             Used for storing output.
# 
#         """
#         # Check pool context
#         if pool is not None and pool.has_context:
#             if batch_size is None:
#                 batch_size = pool.batch_size
#             elif batch_size != pool.batch_size:
#                 raise ValueError('Pool batch_size differs from the given batch_size!')
# 
#             if seed is None:
#                 seed = pool.seed
#             elif seed != pool.seed:
#                 raise ValueError('Pool seed differs from the given seed!')
# 
#         self._batch_size = batch_size or 1
#         self._seed = random_seed() if seed is None else seed
#         self._pool = pool
# 
#         # Caches will not be used if they are not found from the caches dict
#         self.caches = {'executor': {}, 'sub_seed': {}}
# 
#         # Count the number of submissions from this context
#         self.num_submissions = 0
# 
#         if pool is not None and not pool.has_context:
#             self._pool.set_context(self)
# 
#     @property
#     def pool(self):
#         """Return the output pool."""
#         return self._pool
# 
#     @property
#     def batch_size(self):
#         """Return the batch size."""
#         return self._batch_size
# 
#     @property
#     def seed(self):
#         """Return the random seed."""
#         return self._seed
# 
#     def callback(self, batch, batch_index):
#         """Add the batch to pool.
# 
#         Parameters
#         ----------
#         batch : dict
#         batch_index : int
# 
#         """
#         if self._pool is not None:
#             self._pool.add_batch(batch, batch_index)


import numpy as np
import elfi.model.augmenter as augmenter
from elfi.clients.native import Client
from elfi.methods.utils import numgrad
from elfi.model.elfi_model import ComputationContext
        rvs = np.column_stack([batch[p] for p in self.parameter_names])

        if self.dim == 1:
            rvs = rvs.reshape(size or 1)

        return rvs[0] if size is None else rvs

    def pdf(self, x):
        """Return the density of the joint prior at x."""
        return self._evaluate_pdf(x)

    def logpdf(self, x):
        """Return the log density of the joint prior at x."""
        return self._evaluate_pdf(x, log=True)

    def _evaluate_pdf(self, x, log=False):
        if log:
            net = self._logpdf_net
            node = self._logpdf_node
        else:
            net = self._pdf_net
            node = self._pdf_node

        x = np.asanyarray(x)
        ndim = x.ndim
        x = x.reshape((-1, self.dim))
        batch = self._to_batch(x)

        # TODO: we could add a seed value that would load a "random state" instance
        #       throwing an error if it is used, for instance seed="not used".

# Path: elfi/methods/utils.py
# def numgrad(fn, x, h=None, replace_neg_inf=True):
#     """Naive numeric gradient implementation for scalar valued functions.
# 
#     Parameters
#     ----------
#     fn
#     x : np.ndarray
#         A single point in 1d vector
#     h : float or list
#         Stepsize or stepsizes for the dimensions
#     replace_neg_inf : bool
#         Replace neg inf fn values with gradient 0 (useful for logpdf gradients)
# 
#     Returns
#     -------
#     grad : np.ndarray
#         1D gradient vector
# 
#     """
#     h = 0.00001 if h is None else h
#     h = np.asanyarray(h).reshape(-1)
# 
#     x = np.asanyarray(x, dtype=float).reshape(-1)
#     dim = len(x)
#     X = np.zeros((dim * 3, dim))
# 
#     for i in range(3):
#         Xi = np.tile(x, (dim, 1))
#         np.fill_diagonal(Xi, Xi.diagonal() + (i - 1) * h)
#         X[i * dim:(i + 1) * dim, :] = Xi
# 
#     f = fn(X)
#     f = f.reshape((3, dim))
# 
#     if replace_neg_inf:
#         if np.any(np.isneginf(f)):
#             return np.zeros(dim)
# 
#     grad = np.gradient(f, *h, axis=0)
#     return grad[1, :]
# 
# Path: elfi/model/elfi_model.py
# class ComputationContext:
#     """Container object for key components for consistent computation results.
# 
#     Attributes
#     ----------
#     seed : int
#     batch_size : int
#     pool : OutputPool
#     num_submissions : int
#         Number of submissions using this context.
#     sub_seed_cache : dict
#         Caches the sub seed generation state variables. This is
# 
#     Notes
#     -----
#     The attributes are immutable.
# 
#     """
# 
#     def __init__(self, batch_size=None, seed=None, pool=None):
#         """Set up a ComputationContext.
# 
#         Parameters
#         ----------
#         batch_size : int, optional
#         seed : int, None, 'global', optional
#             When None generates a random integer seed. When `'global'` uses the global
#             numpy random state. Only recommended for debugging.
#         pool : elfi.OutputPool, optional
#             Used for storing output.
# 
#         """
#         # Check pool context
#         if pool is not None and pool.has_context:
#             if batch_size is None:
#                 batch_size = pool.batch_size
#             elif batch_size != pool.batch_size:
#                 raise ValueError('Pool batch_size differs from the given batch_size!')
# 
#             if seed is None:
#                 seed = pool.seed
#             elif seed != pool.seed:
#                 raise ValueError('Pool seed differs from the given seed!')
# 
#         self._batch_size = batch_size or 1
#         self._seed = random_seed() if seed is None else seed
#         self._pool = pool
# 
#         # Caches will not be used if they are not found from the caches dict
#         self.caches = {'executor': {}, 'sub_seed': {}}
# 
#         # Count the number of submissions from this context
#         self.num_submissions = 0
# 
#         if pool is not None and not pool.has_context:
#             self._pool.set_context(self)
# 
#     @property
#     def pool(self):
#         """Return the output pool."""
#         return self._pool
# 
#     @property
#     def batch_size(self):
#         """Return the batch size."""
#         return self._batch_size
# 
#     @property
#     def seed(self):
#         """Return the random seed."""
#         return self._seed
# 
#     def callback(self, batch, batch_index):
#         """Add the batch to pool.
# 
#         Parameters
#         ----------
#         batch : dict
#         batch_index : int
# 
#         """
#         if self._pool is not None:
#             self._pool.add_batch(batch, batch_index)


# Path: elfi/model/extensions.py
import numpy as np
import elfi.model.augmenter as augmenter
from elfi.clients.native import Client
from elfi.methods.utils import numgrad
from elfi.model.elfi_model import ComputationContext

        rvs = np.column_stack([batch[p] for p in self.parameter_names])

        if self.dim == 1:
            rvs = rvs.reshape(size or 1)

        return rvs[0] if size is None else rvs

    def pdf(self, x):
        """Return the density of the joint prior at x."""
        return self._evaluate_pdf(x)

    def logpdf(self, x):
        """Return the log density of the joint prior at x."""
        return self._evaluate_pdf(x, log=True)

    def _evaluate_pdf(self, x, log=False):
        if log:
            net = self._logpdf_net
            node = self._logpdf_node
        else:
            net = self._pdf_net
            node = self._pdf_node

        x = np.asanyarray(x)
        ndim = x.ndim
        x = x.reshape((-1, self.dim))
        batch = self._to_batch(x)

        # TODO: we could add a seed value that would load a "random state" instance
        #       throwing an error if it is used, for instance seed="not used".

        context = ComputationContext(len(x), seed=0)