r"""Calculate the normalized perplexity :math:`\mathcal{P}` of samples
    with ``weights`` :math:`\omega_i`. :math:`\mathcal{P}=0` is
    terrible and :math:`\mathcal{P}=1` is perfect.

    .. math::

        \mathcal{P} = exp(H) / N

    where

    .. math::

        H = - \sum_{i=1}^N \bar{\omega}_i log ~ \bar{\omega}_i

    .. math::

        \bar{\omega}_i = \frac{\omega_i}{\sum_i \omega_i}

    :param weights:

        Vector-like array; the samples' weights

def perp(weights):
    r"""Calculate the normalized perplexity :math:`\mathcal{P}` of samples
    with ``weights`` :math:`\omega_i`. :math:`\mathcal{P}=0` is
    terrible and :math:`\mathcal{P}=1` is perfect.

    .. math::

        \mathcal{P} = exp(H) / N

    where

    .. math::

        H = - \sum_{i=1}^N \bar{\omega}_i log ~ \bar{\omega}_i

    .. math::

        \bar{\omega}_i = \frac{\omega_i}{\sum_i \omega_i}

    :param weights:

        Vector-like array; the samples' weights

    """
    # normalize weights
    w = _np.asarray(weights) / _np.sum(weights)

    # mask zero weights
    w = _np.ma.MaskedArray(w, copy=False, mask=(w == 0))

    # avoid NaN due to log(0) by log(1)=0
    entr = - _np.sum( w * _np.log(w.filled(1.0)))

    return  _np.exp(entr) / len(w)