"""
The cross mutual information and its multivariate generalizations.
The cross mutual information [Gohil, Cliff, Shine, Fulcher, and Lizier, "Cross
Mutual Information", IEEE Information Theory Workshop 2025, arXiv:2507.15372]
measures the expected strength of the dependence between random variables
exhibited by samples from a *test* distribution ``p``, where the pointwise
information of each sample is evaluated using a *reference* distribution ``q``:
.. math::
CI_{pq} = \\mathbb{E}_{p}\\{i_q(x;y)\\}
= \\sum_{x,y} p(x,y) \\log_2 \\frac{q(x,y)}{q(x)q(y)}
When ``p == q`` the cross mutual information reduces to the ordinary mutual
information. Each of the standard multivariate mutual informations
(co-information, total correlation, dual total correlation, and CAEKL mutual
information) is a signed sum of joint entropies; the corresponding cross measure
replaces each entropy with the analogous cross entropy between ``p`` and ``q``.
Unlike their conventional counterparts, the cross measures can be negative.
"""
from ..divergences.cross_entropy import cross_entropy
from ..helpers import normalize_rvs
from ..utils import partitions, powerset
__all__ = (
"cross_caekl_mutual_information",
"cross_coinformation",
"cross_dual_total_correlation",
"cross_total_correlation",
)
[docs]
def cross_total_correlation(dist, ref_dist, rvs=None, crvs=None):
"""
Computes the cross total correlation, the cross-distribution generalization
of the total correlation.
Parameters
----------
dist : Distribution
The test distribution `p`, over which the expectation is taken.
ref_dist : Distribution
The reference distribution `q`, used to evaluate the pointwise
information of each outcome.
rvs : list, None
A list of lists. Each inner list specifies the indexes of the random
variables used to calculate the cross total correlation. If None, then
the cross total correlation is calculated over all random variables,
which is equivalent to passing `rvs=dist.rvs`.
crvs : list, None
A single list of indexes specifying the random variables to condition
on. If None, then no variables are conditioned on.
Returns
-------
CT : float
The cross total correlation.
Raises
------
ditException
Raised if `dist` is not a joint distribution or if `rvs` or `crvs`
contain non-existant random variables.
"""
rvs, crvs = normalize_rvs(dist, rvs, crvs)
one = sum(cross_entropy(dist, ref_dist, rv, crvs) for rv in rvs)
two = cross_entropy(dist, ref_dist, list(set().union(*rvs)), crvs)
CT = one - two
return CT
[docs]
def cross_dual_total_correlation(dist, ref_dist, rvs=None, crvs=None):
"""
Calculates the cross dual total correlation, the cross-distribution
generalization of the dual total correlation.
Parameters
----------
dist : Distribution
The test distribution `p`, over which the expectation is taken.
ref_dist : Distribution
The reference distribution `q`, used to evaluate the pointwise
information of each outcome.
rvs : list, None
The indexes of the random variable used to calculate the cross dual
total correlation. If None, then the cross dual total correlation is
calculated over all random variables.
crvs : list, None
The indexes of the random variables to condition on. If None, then no
variables are condition on.
Returns
-------
CB : float
The cross dual total correlation.
Raises
------
ditException
Raised if `dist` is not a joint distribution or if `rvs` or `crvs`
contain non-existant random variables.
"""
rvs, crvs = normalize_rvs(dist, rvs, crvs)
others = lambda rv, rvs: set(set().union(*rvs)) - set(rv)
one = cross_entropy(dist, ref_dist, list(set().union(*rvs)), crvs)
two = sum(cross_entropy(dist, ref_dist, rv, list(others(rv, rvs).union(crvs))) for rv in rvs)
CB = one - two
return CB