Source code for syntropy.neural.shannon
import torch
import nflows
from .utils import initialize_flow, train_flow, evaluate_flow
from typing import Any
[docs]
def differential_entropy(
idxs: tuple[int, ...],
data: torch.Tensor,
data_test: None | torch.Tensor = None,
flow_kwargs: None | dict[str, Any] = None,
train_kwargs: None | dict[str, Any] = None,
verbose: bool = False,
) -> tuple[torch.Tensor, float, nflows.flows.base.Flow]:
"""
Computes the differential entropy of the data.
Parameters
----------
idxs : tuple[int, ...]
The tuple of indices the differential entropy is computed for.
data : torch.Tensor
The training data, in samples x features format.
data_test : None | torch.Tensor
If not None, the testing data in samples x features format.
flow_kwargs : dict
Arguments for the utils.initalize_flow function.
train_kwargs : dict
Arguments for the utils.train_flow function.
verbose : bool
Whether to print the training progress.
Returns
-------
float
nflows.flows.base.Flow
"""
flow_kwargs = flow_kwargs or {}
train_kwargs = train_kwargs or {}
# initialize the flow
dim: int = len(idxs)
flow = initialize_flow(dim=dim, **flow_kwargs)
# train the flow
flow = train_flow(
flow=flow,
data=data[:, idxs],
verbose=verbose,
**train_kwargs,
)
# evaluation
eval_data = data if data_test is None else data_test
ptw, avg = evaluate_flow(flow, eval_data[:, idxs])
return ptw, avg, flow
[docs]
def conditional_entropy(
idxs_x: tuple[int, ...],
idxs_y: tuple[int, ...],
data: torch.Tensor,
data_test: None | torch.Tensor = None,
flow_kwargs: None | dict[str, Any] = None,
train_kwargs: None | dict[str, Any] = None,
verbose: bool = False,
) -> tuple[torch.Tensor, float, nflows.flows.base.Flow]:
"""
Computes the differential entropy of the data.
Parameters
----------
idxs_x : tuple[int, ...]
The tuple of indices the differential entropy is computed for.
idxs_y : tuple[int, ...]
The tuple of indices to condition on.
data : torch.Tensor
The training data, in samples x features format.
data_test : None | torch.Tensor
If not None, the testing data in samples x features format.
flow_kwargs : dict
Arguments for the utils.initalize_flow function.
train_kwargs : dict
Arguments for the utils.train_flow function.
verbose : bool
Whether to print the training progress.
Returns
-------
torch.Tensor
float
nflows.flows.base.Flow
"""
flow_kwargs = flow_kwargs or {}
train_kwargs = train_kwargs or {}
flow_kwargs["dim_context"] = len(idxs_y)
# initialize the flow
dim: int = len(idxs_x)
flow = initialize_flow(dim=dim, **flow_kwargs)
# train the flow
flow = train_flow(
flow=flow,
data=data[:, idxs_x],
context=data[:, idxs_y],
verbose=verbose,
**train_kwargs,
)
# evaluation
eval_data = data if data_test is None else data_test
ptw: torch.Tensor
avg: float
ptw, avg = evaluate_flow(flow, eval_data[:, idxs_x], context=eval_data[:, idxs_y])
return ptw, avg, flow
[docs]
def mutual_information(
idxs_x: tuple[int, ...],
idxs_y: tuple[int, ...],
data: torch.Tensor,
data_test: None | torch.Tensor = None,
flow_kwargs: None | dict[str, Any] = None,
train_kwargs: None | dict[str, Any] = None,
verbose: bool = False,
) -> float:
"""
Computes the mutual information between the elements given by idxs_x and idxs_y.
Parameters
----------
idxs_x : tuple[int, ...]
The tuple of indices the x variable.
idxs_y : tuple[int, ...]
The tuple of indices for the y variable.
data : torch.Tensor
The training data, in samples x features format.
context : None | tuple[int]
If not None, the indices of the conditioning variables.
data_test : None | torch.Tensor
If not None, the testing data in samples x features format.
flow_kwargs : dict
Arguments for the utils.initalize_flow function.
train_kwargs : dict
Arguments for the utils.train_flow function.
verbose : bool
Whether to print the training progress.
Returns
-------
float
"""
flow_kwargs = flow_kwargs or {}
train_kwargs = train_kwargs or {}
ptw_x, avg_x, _ = differential_entropy(
idxs=idxs_x,
data=data,
data_test=data_test,
verbose=verbose,
flow_kwargs=flow_kwargs,
train_kwargs=train_kwargs,
)
ptw_cond, avg_cond, _ = conditional_entropy(
idxs_x=idxs_x,
idxs_y=idxs_y,
data=data,
data_test=data_test,
flow_kwargs=flow_kwargs,
train_kwargs=train_kwargs,
verbose=verbose,
)
ptw: torch.Tensor = ptw_x - ptw_cond
mi: float = avg_x - avg_cond
return ptw, mi
[docs]
def conditional_mutual_information(
idxs_x: tuple[int, ...],
idxs_y: tuple[int, ...],
idxs_z: tuple[int, ...],
data: torch.Tensor,
data_test: None | torch.Tensor = None,
flow_kwargs: None | dict[str, Any] = None,
train_kwargs: None | dict[str, Any] = None,
verbose: bool = False,
) -> float:
"""
Computes the mutual information between the elements given by idxs_x and idxs_y.
Parameters
----------
idxs_x : tuple[int, ...]
The tuple of indices the x variable.
idxs_y : tuple[int, ...]
The tuple of indices for the y variable.
data : torch.Tensor
The training data, in samples x features format.
context : None | tuple[int]
If not None, the indices of the conditioning variables.
data_test : None | torch.Tensor
If not None, the testing data in samples x features format.
flow_kwargs : dict
Arguments for the utils.initalize_flow function.
train_kwargs : dict
Arguments for the utils.train_flow function.
verbose : bool
Whether to print the training progress.
Returns
-------
float
"""
flow_kwargs = flow_kwargs or {}
train_kwargs = train_kwargs or {}
ptw_x_given_z, avg_x_given_z, _ = conditional_entropy(
idxs_x=idxs_x,
idxs_y=idxs_z,
data=data,
data_test=data_test,
verbose=verbose,
flow_kwargs=flow_kwargs,
train_kwargs=train_kwargs,
)
ptw_x_given_yz, avg_x_given_yz, _ = conditional_entropy(
idxs_x=idxs_x,
idxs_y=idxs_y + idxs_z,
data=data,
data_test=data_test,
flow_kwargs=flow_kwargs,
train_kwargs=train_kwargs,
verbose=verbose,
)
ptw: torch.Tensor = ptw_x_given_z - ptw_x_given_yz
avg: float = avg_x_given_z - avg_x_given_yz
return ptw, avg