# Path: src/qinfer/_due.py
# class InactiveDueCreditCollector(object):
#     def _donothing(self, *args, **kwargs):
#     def dcite(self, *args, **kwargs):
#         def nondecorating_decorator(func):
#     def __repr__(self):
# def _donothing_func(*args, **kwargs):
# 
# Path: src/qinfer/utils.py
# def outer_product(vec):
#     r"""
#     Returns the outer product of a vector :math:`v`
#     with itself, :math:`v v^\T`.
#     """
#     return (
#         np.dot(vec[:, np.newaxis], vec[np.newaxis, :])
#         if len(vec.shape) == 1 else
#         np.dot(vec, vec.T)
#         )
# 
# def particle_meanfn(weights, locations, fn=None):
#     r"""
#     Returns the mean of a function :math:`f` over model
#     parameters.
# 
#     :param numpy.ndarray weights: Weights of each particle.
#     :param numpy.ndarray locations: Locations of each
#         particle.
#     :param callable fn: Function of model parameters to
#         take the mean of. If `None`, the identity function
#         is assumed.
#     """
#     warnings.warn('particle_meanfn is deprecated, please use distributions.ParticleDistribution',
#                   DeprecationWarning)
#     fn_vals = fn(locations) if fn is not None else locations
#     return np.sum(weights * fn_vals.transpose([1, 0]),
#         axis=1)
# 
# def particle_covariance_mtx(weights,locations):
#     """
#     Returns an estimate of the covariance of a distribution
#     represented by a given set of SMC particle.
# 
#     :param weights: An array containing the weights of each
#         particle.
#     :param location: An array containing the locations of
#         each particle.
#     :rtype: :class:`numpy.ndarray`, shape
#         ``(n_modelparams, n_modelparams)``.
#     :returns: An array containing the estimated covariance matrix.
#     """
#     # TODO: add shapes to docstring.
#     warnings.warn('particle_covariance_mtx is deprecated, please use distributions.ParticleDistribution',
#                   DeprecationWarning)
# 
#     # Find the mean model vector, shape (n_modelparams, ).
#     mu = particle_meanfn(weights, locations)
# 
#     # Transpose the particle locations to have shape
#     # (n_modelparams, n_particles).
#     xs = locations.transpose([1, 0])
#     # Give a shorter name to the particle weights, shape (n_particles, ).
#     ws = weights
# 
#     cov = (
#         # This sum is a reduction over the particle index, chosen to be
#         # axis=2. Thus, the sum represents an expectation value over the
#         # outer product $x . x^T$.
#         #
#         # All three factors have the particle index as the rightmost
#         # index, axis=2. Using the Einstein summation convention (ESC),
#         # we can reduce over the particle index easily while leaving
#         # the model parameter index to vary between the two factors
#         # of xs.
#         #
#         # This corresponds to evaluating A_{m,n} = w_{i} x_{m,i} x_{n,i}
#         # using the ESC, where A_{m,n} is the temporary array created.
#         np.einsum('i,mi,ni', ws, xs, xs)
#         # We finish by subracting from the above expectation value
#         # the outer product $mu . mu^T$.
#         - np.dot(mu[..., np.newaxis], mu[np.newaxis, ...])
#     )
# 
#     # The SMC approximation is not guaranteed to produce a
#     # positive-semidefinite covariance matrix. If a negative eigenvalue
#     # is produced, we should warn the caller of this.
#     assert np.all(np.isfinite(cov))
#     if not np.all(la.eig(cov)[0] >= 0):
#         warnings.warn('Numerical error in covariance estimation causing positive semidefinite violation.', ApproximationWarning)
# 
#     return cov
# 
# def sqrtm_psd(A, est_error=True, check_finite=True):
#     """
#     Returns the matrix square root of a positive semidefinite matrix,
#     truncating negative eigenvalues.
#     """
#     w, v = eigh(A, check_finite=check_finite)
#     mask = w <= 0
#     w[mask] = 0
#     np.sqrt(w, out=w)
#     A_sqrt = (v * w).dot(v.conj().T)
# 
#     if est_error:
#         return A_sqrt, np.linalg.norm(np.dot(A_sqrt, A_sqrt) - A, 'fro')
#     else:
#         return A_sqrt


import numpy as np
import scipy.linalg as la
import warnings
import qinfer.clustering
import logging
from ._due import due, BibTeX
from .utils import outer_product, particle_meanfn, particle_covariance_mtx, sqrtm_psd
from abc import ABCMeta, abstractmethod, abstractproperty
from future.utils import with_metaclass
from qinfer._exceptions import ResamplerWarning, ResamplerError
from qinfer.distributions import ParticleDistribution
                particle_weights[cluster_particles],
                particle_locations[cluster_particles],
                **extra_args
            )

            # Renormalize the weights of each resampled particle by the total
            # weight of the cluster to which it belongs.
            cluster_ws /= np.sum(particle_weights[cluster_particles])

            # Store the updated cluster.
            new_weights[cluster_particles] = cluster_ws
            new_locs[cluster_particles]    = cluster_locs

        # Assert that we have not introduced any NaNs or Infs by resampling.
        assert np.all(np.logical_not(np.logical_or(
                np.isnan(new_locs), np.isinf(new_locs)
            )))

        return new_weights, new_locs

class LiuWestResampler(Resampler):
    r"""
    Creates a resampler instance that applies the algorithm of
    [LW01]_ to redistribute the particles.

    :param float a: Value of the parameter :math:`a` of the [LW01]_ algorithm
        to use in resampling.
    :param float h: Value of the parameter :math:`h` to use, or `None` to
        use that corresponding to :math:`a`.
    :param int maxiter: Maximum number of times to attempt to resample within

# Path: src/qinfer/_due.py
# class InactiveDueCreditCollector(object):
#     def _donothing(self, *args, **kwargs):
#     def dcite(self, *args, **kwargs):
#         def nondecorating_decorator(func):
#     def __repr__(self):
# def _donothing_func(*args, **kwargs):
# 
# Path: src/qinfer/utils.py
# def outer_product(vec):
#     r"""
#     Returns the outer product of a vector :math:`v`
#     with itself, :math:`v v^\T`.
#     """
#     return (
#         np.dot(vec[:, np.newaxis], vec[np.newaxis, :])
#         if len(vec.shape) == 1 else
#         np.dot(vec, vec.T)
#         )
# 
# def particle_meanfn(weights, locations, fn=None):
#     r"""
#     Returns the mean of a function :math:`f` over model
#     parameters.
# 
#     :param numpy.ndarray weights: Weights of each particle.
#     :param numpy.ndarray locations: Locations of each
#         particle.
#     :param callable fn: Function of model parameters to
#         take the mean of. If `None`, the identity function
#         is assumed.
#     """
#     warnings.warn('particle_meanfn is deprecated, please use distributions.ParticleDistribution',
#                   DeprecationWarning)
#     fn_vals = fn(locations) if fn is not None else locations
#     return np.sum(weights * fn_vals.transpose([1, 0]),
#         axis=1)
# 
# def particle_covariance_mtx(weights,locations):
#     """
#     Returns an estimate of the covariance of a distribution
#     represented by a given set of SMC particle.
# 
#     :param weights: An array containing the weights of each
#         particle.
#     :param location: An array containing the locations of
#         each particle.
#     :rtype: :class:`numpy.ndarray`, shape
#         ``(n_modelparams, n_modelparams)``.
#     :returns: An array containing the estimated covariance matrix.
#     """
#     # TODO: add shapes to docstring.
#     warnings.warn('particle_covariance_mtx is deprecated, please use distributions.ParticleDistribution',
#                   DeprecationWarning)
# 
#     # Find the mean model vector, shape (n_modelparams, ).
#     mu = particle_meanfn(weights, locations)
# 
#     # Transpose the particle locations to have shape
#     # (n_modelparams, n_particles).
#     xs = locations.transpose([1, 0])
#     # Give a shorter name to the particle weights, shape (n_particles, ).
#     ws = weights
# 
#     cov = (
#         # This sum is a reduction over the particle index, chosen to be
#         # axis=2. Thus, the sum represents an expectation value over the
#         # outer product $x . x^T$.
#         #
#         # All three factors have the particle index as the rightmost
#         # index, axis=2. Using the Einstein summation convention (ESC),
#         # we can reduce over the particle index easily while leaving
#         # the model parameter index to vary between the two factors
#         # of xs.
#         #
#         # This corresponds to evaluating A_{m,n} = w_{i} x_{m,i} x_{n,i}
#         # using the ESC, where A_{m,n} is the temporary array created.
#         np.einsum('i,mi,ni', ws, xs, xs)
#         # We finish by subracting from the above expectation value
#         # the outer product $mu . mu^T$.
#         - np.dot(mu[..., np.newaxis], mu[np.newaxis, ...])
#     )
# 
#     # The SMC approximation is not guaranteed to produce a
#     # positive-semidefinite covariance matrix. If a negative eigenvalue
#     # is produced, we should warn the caller of this.
#     assert np.all(np.isfinite(cov))
#     if not np.all(la.eig(cov)[0] >= 0):
#         warnings.warn('Numerical error in covariance estimation causing positive semidefinite violation.', ApproximationWarning)
# 
#     return cov
# 
# def sqrtm_psd(A, est_error=True, check_finite=True):
#     """
#     Returns the matrix square root of a positive semidefinite matrix,
#     truncating negative eigenvalues.
#     """
#     w, v = eigh(A, check_finite=check_finite)
#     mask = w <= 0
#     w[mask] = 0
#     np.sqrt(w, out=w)
#     A_sqrt = (v * w).dot(v.conj().T)
# 
#     if est_error:
#         return A_sqrt, np.linalg.norm(np.dot(A_sqrt, A_sqrt) - A, 'fro')
#     else:
#         return A_sqrt


# Path: src/qinfer/resamplers.py
import numpy as np
import scipy.linalg as la
import warnings
import qinfer.clustering
import logging
from ._due import due, BibTeX
from .utils import outer_product, particle_meanfn, particle_covariance_mtx, sqrtm_psd
from abc import ABCMeta, abstractmethod, abstractproperty
from future.utils import with_metaclass
from qinfer._exceptions import ResamplerWarning, ResamplerError
from qinfer.distributions import ParticleDistribution

                particle_weights[cluster_particles],
                particle_locations[cluster_particles],
                **extra_args
            )

            # Renormalize the weights of each resampled particle by the total
            # weight of the cluster to which it belongs.
            cluster_ws /= np.sum(particle_weights[cluster_particles])

            # Store the updated cluster.
            new_weights[cluster_particles] = cluster_ws
            new_locs[cluster_particles]    = cluster_locs

        # Assert that we have not introduced any NaNs or Infs by resampling.
        assert np.all(np.logical_not(np.logical_or(
                np.isnan(new_locs), np.isinf(new_locs)
            )))

        return new_weights, new_locs

class LiuWestResampler(Resampler):
    r"""
    Creates a resampler instance that applies the algorithm of
    [LW01]_ to redistribute the particles.

    :param float a: Value of the parameter :math:`a` of the [LW01]_ algorithm
        to use in resampling.
    :param float h: Value of the parameter :math:`h` to use, or `None` to
        use that corresponding to :math:`a`.
    :param int maxiter: Maximum number of times to attempt to resample within

        the space of valid models before giving up.