Ejemplo n.º 1
0
def test_disequilibrium2():
    """
    Test that while the XOR distribution has non-zero disequilibrium, all its
    marginals have zero disequilibrium (are uniform).
    """
    assert disequilibrium(d2) > 0
    for rvs in combinations(flatten(d2.rvs), 2):
        assert disequilibrium(d2, rvs) == pytest.approx(0)
Ejemplo n.º 2
0
def test_disequilibrium2():
    """
    Test that while the XOR distribution has non-zero disequilibrium, all its
    marginals have zero disequilibrium (are uniform).
    """
    assert disequilibrium(d2) > 0
    for rvs in combinations(flatten(d2.rvs), 2):
        assert disequilibrium(d2, rvs) == pytest.approx(0)
Ejemplo n.º 3
0
def test_disequilibrium2():
    """
    Test that while the XOR distribution has non-zero disequilibrium, all its
    marginals have zero disequilibrium (are uniform).
    """
    assert_true(disequilibrium(d2) > 0)
    for rvs in combinations(flatten(d2.rvs), 2):
        assert_almost_equal(disequilibrium(d2, rvs), 0)
Ejemplo n.º 4
0
    def __init__(self,
                 dist,
                 inputs=None,
                 output=None,
                 reds=None,
                 pis=None,
                 **kwargs):
        """
        Parameters
        ----------
        dist : Distribution
            The distribution to compute the decomposition on.
        inputs : iter of iters, None
            The set of input variables. If None, `dist.rvs` less indices
            in `output` is used.
        output : iter, None
            The output variable. If None, `dist.rvs[-1]` is used.
        reds : dict, None
            Redundancy values pre-assessed.
        pis : dict, None
            Partial information values pre-assessed.
        """
        self._dist = dist

        if output is None:
            output = dist.rvs[-1]
        if inputs is None:
            inputs = [var for var in dist.rvs if var[0] not in output]

        self._inputs = tuple(map(tuple, inputs))
        self._output = tuple(output)
        self._kwargs = kwargs

        self._lattice = full_constraint_lattice(self._inputs)

        # To compute the Mobius inversion reusing dit's code, we reverse the
        # lattice, compute Mobius, and reverse it back again.
        self._lattice = self._lattice.reverse()
        self._lattice.root = next(iter(nx.topological_sort(self._lattice)))

        self._total = coinformation(
            self._dist, [list(flatten(self._inputs)), self._output])

        self._reds = {} if reds is None else reds
        self._pis = {} if pis is None else pis

        self._compute()

        self._lattice = self._lattice.reverse()
        self._lattice.root = next(iter(nx.topological_sort(self._lattice)))
Ejemplo n.º 5
0
def _total_correlation_ksg_scipy(data, rvs, crvs=None, k=4, noise=1e-10):
    """
    Compute the total correlation from observations. The total correlation is computed between the columns
    specified in `rvs`, given the columns specified in `crvs`. This utilizes the KSG kNN density estimator,
    and works on discrete, continuous, and mixed data.

    Parameters
    ----------
    data : np.array
        A set of observations of a distribution.
    rvs : iterable of iterables
        The columns for which the total correlation is to be computed.
    crvs : iterable
        The columns upon which the total correlation should be conditioned.
    k : int
        The number of nearest neighbors to use in estimating the local kernel density.
    noise : float
        The standard deviation of the normally-distributed noise to add to the data.

    Returns
    -------
    tc : float
        The total correlation of `rvs` given `crvs`.

    Notes
    -----
    The total correlation is computed in bits, not nats as most KSG estimators do.
    """
    # KSG suggest adding noise (to break symmetries?)
    data = _fuzz(data, noise)

    if crvs is None:
        crvs = []

    digamma_N = digamma(len(data))
    log_2 = np.log(2)

    all_rvs = list(flatten(rvs)) + crvs
    rvs = [rv + crvs for rv in rvs]

    d_rvs = [len(data[0, rv]) for rv in rvs]

    tree = cKDTree(data[:, all_rvs])
    tree_rvs = [cKDTree(data[:, rv]) for rv in rvs]

    epsilons = tree.query(data[:, all_rvs], k + 1,
                          p=np.inf)[0][:, -1]  # k+1 because of self

    n_rvs = [
        np.array([
            len(t.query_ball_point(point, epsilon, p=np.inf))
            for point, epsilon in zip(data[:, rv], epsilons)
        ]) for rv, t in zip(rvs, tree_rvs)
    ]

    log_epsilons = np.log(epsilons)

    h_rvs = [-digamma(n_rv).mean() for n_rv, d in zip(n_rvs, d_rvs)]

    h_all = -digamma(k)

    if crvs:
        tree_crvs = cKDTree(data[:, crvs])
        n_crvs = np.array([
            len(tree_crvs.query_ball_point(point, epsilon, p=np.inf))
            for point, epsilon in zip(data[:, crvs], epsilons)
        ])
        h_crvs = -digamma(n_crvs).mean()
    else:
        h_rvs = [
            h_rv + digamma_N + d * (log_2 - log_epsilons).mean()
            for h_rv, d in zip(h_rvs, d_rvs)
        ]
        h_all += digamma_N + sum(d_rvs) * (log_2 - log_epsilons).mean()
        h_crvs = 0

    tc = sum(h_rv - h_crvs for h_rv in h_rvs) - (h_all - h_crvs)

    return tc / log_2
Ejemplo n.º 6
0
def _total_correlation_ksg_scipy(data, rvs, crvs=None, k=4, noise=1e-10):
    """
    Compute the total correlation from observations. The total correlation is computed between the columns
    specified in `rvs`, given the columns specified in `crvs`. This utilizes the KSG kNN density estimator,
    and works on discrete, continuous, and mixed data.

    Parameters
    ----------
    data : np.array
        A set of observations of a distribution.
    rvs : iterable of iterables
        The columns for which the total correlation is to be computed.
    crvs : iterable
        The columns upon which the total correlation should be conditioned.
    k : int
        The number of nearest neighbors to use in estimating the local kernel density.
    noise : float
        The standard deviation of the normally-distributed noise to add to the data.

    Returns
    -------
    tc : float
        The total correlation of `rvs` given `crvs`.

    Notes
    -----
    The total correlation is computed in bits, not nats as most KSG estimators do.
    """
    # KSG suggest adding noise (to break symmetries?)
    data = _fuzz(data, noise)

    if crvs is None:
        crvs = []

    digamma_N = digamma(len(data))
    log_2 = np.log(2)

    all_rvs = list(flatten(rvs)) + crvs
    rvs = [rv + crvs for rv in rvs]

    d_rvs = [len(data[0, rv]) for rv in rvs]

    tree = cKDTree(data[:, all_rvs])
    tree_rvs = [cKDTree(data[:, rv]) for rv in rvs]

    epsilons = tree.query(data[:, all_rvs], k + 1, p=np.inf)[0][:, -1]  # k+1 because of self

    n_rvs = [
        np.array([len(t.query_ball_point(point, epsilon, p=np.inf)) for point, epsilon in zip(data[:, rv], epsilons)])
        for rv, t in zip(rvs, tree_rvs)]

    log_epsilons = np.log(epsilons)

    h_rvs = [-digamma(n_rv).mean() for n_rv, d in zip(n_rvs, d_rvs)]

    h_all = -digamma(k)

    if crvs:
        tree_crvs = cKDTree(data[:, crvs])
        n_crvs = np.array([len(tree_crvs.query_ball_point(point, epsilon, p=np.inf)) for point, epsilon in
                           zip(data[:, crvs], epsilons)])
        h_crvs = -digamma(n_crvs).mean()
    else:
        h_rvs = [h_rv + digamma_N + d * (log_2 - log_epsilons).mean() for h_rv, d in zip(h_rvs, d_rvs)]
        h_all += digamma_N + sum(d_rvs) * (log_2 - log_epsilons).mean()
        h_crvs = 0

    tc = sum([h_rv - h_crvs for h_rv in h_rvs]) - (h_all - h_crvs)

    return tc / log_2