Source code for infomeasure.estimators.entropy.tsallis

"""Module for Tsallis entropy estimator."""

from numpy import column_stack, issubdtype, integer

from ..base import EntropyEstimator
from ..utils.array import assure_2d_data
from ..utils.exponential_family import (
    calculate_common_entropy_components,
    exponential_family_iq,
    exponential_family_i1,
)
from ... import Config
from ...utils.types import LogBaseType


[docs] class TsallisEntropyEstimator(EntropyEstimator): r"""Estimator for the Tsallis entropy. Attributes ---------- *data : array-like The data used to estimate the entropy. k : int The number of nearest neighbors used in the estimation. q : float The Tsallis parameter, order or exponent. Sometimes denoted as :math:`q`, analogous to the Rényi parameter :math:`\alpha`. Raises ------ ValueError If the Tsallis parameter is not a positive number. ValueError If the number of nearest neighbors is not a positive integer. Notes ----- In the :math:`q \to 1` limit, the Jackson sum (q-additivity) reduces to ordinary summation, and the Tallis entropy reduces to Shannon Entropy. This class of entropy measure is in particularly useful in the study in connection with long-range correlated systems and with non-equilibrium phenomena. """ def __init__( self, *data, k: int = 4, q: float | int = None, base: LogBaseType = Config.get("base"), ): r"""Initialize the TsallisMIEstimator. Parameters ---------- k : int The number of nearest neighbors to consider. q : float | int The Tsallis parameter, order or exponent. Sometimes denoted as :math:`q`, analogous to the Rényi parameter :math:`\alpha`. """ super().__init__(*data, base=base) if not isinstance(q, (int, float)) or q <= 0: raise ValueError("The Tsallis parameter ``q`` must be a positive number.") if not issubdtype(type(k), integer) or k <= 0: raise ValueError( "The number of nearest neighbors must be a positive integer." ) self.k = k self.q = q self.data = tuple(assure_2d_data(var) for var in self.data) def _simple_entropy(self): """Calculate the entropy of the data. Returns ------- float The Tsallis entropy. """ V_m, rho_k, N, m = calculate_common_entropy_components(self.data[0], self.k) if self.q != 1: # Tsallis entropy for q != 1 I_N_k_q = exponential_family_iq(self.k, self.q, V_m, rho_k, N - 1, m) if I_N_k_q == 0: return 0.0 return (1 - I_N_k_q) / (self.q - 1) else: # Shannon entropy (limes for alpha = 1) return exponential_family_i1(self.k, V_m, rho_k, N - 1, m, self._log_base) def _joint_entropy(self): """Calculate the joint Tsallis entropy of the data. This is done by joining the variables into one space and calculating the entropy. Returns ------- float The joint Tsallis entropy. """ self.data = (column_stack(self.data[0]),) return self._simple_entropy() def _cross_entropy(self) -> float: """Calculate the cross-entropy between two distributions. Returns ------- float The calculated cross-entropy. """ V_m, rho_k, M, m = calculate_common_entropy_components( self.data[1], self.k, at=self.data[0] ) if self.q != 1: # Renyi cross-entropy for q != 1 I_N_k_q = exponential_family_iq(self.k, self.q, V_m, rho_k, M, m) if I_N_k_q == 0: return 0.0 return (1 - I_N_k_q) / (self.q - 1) else: # Shannon cross-entropy (limes for q = 1) return exponential_family_i1(self.k, V_m, rho_k, M, m, self._log_base)