Skip to content

Commit

Permalink
Add support computing entropy on tensors
Browse files Browse the repository at this point in the history
  • Loading branch information
EtienneCmb committed Jun 3, 2023
1 parent b1556f6 commit 17587a1
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 2 deletions.
1 change: 1 addition & 0 deletions docs/source/api/api_core.rst
Expand Up @@ -41,6 +41,7 @@ Gaussian-Copula mutual-information supporting univariate / multivariate multi-di

copnorm_nd
copnorm_cat_nd
ent_nd_g
mi_nd_gg
mi_model_nd_gd
cmi_nd_ggg
Expand Down
2 changes: 1 addition & 1 deletion frites/core/__init__.py
Expand Up @@ -12,4 +12,4 @@
cmi_1d_ggg, gccmi_1d_ccc, gccmi_1d_ccd, cmi_1d_ggd)
from .gcmi_nd import (mi_nd_gg, mi_model_nd_gd, cmi_nd_ggg, gcmi_nd_cc, # noqa
gcmi_model_nd_cd, gccmi_nd_ccnd, gccmi_model_nd_cdnd,
gccmi_nd_ccc, cmi_nd_ggd)
gccmi_nd_ccc, cmi_nd_ggd, ent_nd_g)
57 changes: 57 additions & 0 deletions frites/core/gcmi_nd.py
Expand Up @@ -61,6 +61,63 @@ def nd_shape_checking(x, y, mvaxis, traxis):
assert all([x.shape[k] == y.shape[k] for k in dims])


###############################################################################
###############################################################################
# ENTROPY
###############################################################################
###############################################################################


def ent_nd_g(x, mvaxis=None, traxis=-1, biascorrect=True, shape_checking=True):
"""Entropy of a continuous variable.
Parameters
----------
x : array_like
Array to consider for computing the entropy.
mvaxis : int | None
Spatial location of the axis to consider if multi-variate analysis
is needed
traxis : int | -1
Spatial location of the trial axis. By default the last axis is
considered
biascorrect : bool | True
Specifies whether bias correction should be applied to the estimated MI
shape_checking : bool | True
Perform a reshape and check that x and y shapes are consistents. For
high performances and to avoid extensive memory usage, it's better to
already have x and y with a shape of (..., mvaxis, traxis) and to set
this parameter to False
Returns
-------
mi : array_like
The mutual information with the same shape as x and y, without the
mvaxis and traxis
"""
if shape_checking:
x = nd_reshape(x, mvaxis=mvaxis, traxis=traxis)
nvarx, ntrl = x.shape[-2], x.shape[-1]

# covariance
c = np.einsum('...ij, ...kj->...ik', x, x)
c /= float(ntrl - 1.)
chc = np.linalg.cholesky(c)

# entropy in nats
hx = np.log(np.einsum('...ii->...i', chc)).sum(-1) + 0.5 * nvarx * (
np.log(2 * np.pi) + 1.0)

ln2 = np.log(2)
if biascorrect:
psiterms = psi((ntrl - np.arange(1, nvarx + 1).astype(
float)) / 2.) / 2.
dterm = (ln2 - np.log(ntrl - 1.)) / 2.
hx = hx - nvarx * dterm - psiterms.sum()

return hx


###############################################################################
###############################################################################
# MUTUAL INFORMATION
Expand Down
11 changes: 10 additions & 1 deletion frites/core/tests/test_gcmi_nd.py
Expand Up @@ -3,11 +3,17 @@

from frites.core.gcmi_nd import (mi_nd_gg, mi_model_nd_gd, cmi_nd_ggg,
gcmi_nd_cc, gcmi_model_nd_cd, gccmi_nd_ccnd,
gccmi_model_nd_cdnd, gccmi_nd_ccc)
gccmi_model_nd_cdnd, gccmi_nd_ccc, ent_nd_g)


class TestGcmiNd(object): # noqa

def ent_nd_g(self):
"""Test function ent_nd_g."""
x_g = np.random.normal(size=(10, 1000, 20))
assert ent_nd_g(x_g, traxis=1).shape == (10, 20)
assert ent_nd_g(x_g, traxis=1, mvaxis=0).shape == (20,)

def test_mi_nd_gg(self):
"""Test function mi_nd_gg."""
x_g = np.random.normal(size=(10, 1000, 20))
Expand Down Expand Up @@ -68,3 +74,6 @@ def test_gccmi_nd_ccc(self):
z_c = np.random.uniform(0, 50, size=(10, 1000, 20))
assert gccmi_nd_ccc(x_c, y_c, z_c, traxis=1).shape == (10, 20)
assert gccmi_nd_ccc(x_c, y_c, z_c, traxis=1, mvaxis=0).shape == (20,)

if __name__ == "__main__":
TestGcmiNd().ent_nd_g()

0 comments on commit 17587a1

Please sign in to comment.