Source code for monte_carlo_analysis.uncertainty_metrics.KullbackLeiblerDivergenceDistributionSimilarity
"""
**Author** : Robin Camarasa
**Institution** : Erasmus Medical Center
**Position** : PhD student
**Contact** : r.camarasa@erasmusmc.nl
**Date** : 2020-10-13
**Project** : monte_carlo_analysis
**Implement class KullbackLeiblerDivergenceDistributionSimilarity**
"""
from monte_carlo_analysis.uncertainty_metrics import DistributionSimilarityUncertaintyMetric
import numpy as np
from numba import jit
from scipy.stats import entropy
[docs]class KullbackLeiblerDivergenceDistributionSimilarity(
DistributionSimilarityUncertaintyMetric
):
"""Implement KullbackLeiblerDivergenceDistributionSimilarity class. The formula applied to a
pair of distributions :math:`q_{c'}(y_j|x)` and :math:`q_{c}(y_j|x)` is:
.. math::
S^{k}(q_{c'}(y_j|x), q_{c''}(y_j|x)) = - D_{KL}(q_{c'}(y_j|x)||q_{c''}(y_j|x)) - D_{KL}(q_{c''}(y_j|x)||q_{c'}(y_j|x))
:param nbins: Number of bins of the histogram
:param epsilon: Number to avoid the division by zero
"""
def __init__(self, nbins: int=100, epsilon: float=0.0000001):
super(KullbackLeiblerDivergenceDistributionSimilarity, self)
self.nbins = nbins
self.epsilon = epsilon
self.transformation = self.get_transformation(self.nbins, self.epsilon)
[docs] def get_transformation(self, nbins: int, epsilon) -> callable:
"""Define the transformation applied to a pair of distributions
:param nbins: The discretization step of the integral
:return: transformation to apply to a pair of distributions
"""
@jit
def transformation(distribution_1: np.array, distribution_2: np.array) -> float:
# Discretize the distribution the division
# by the sum is due to np.histogram implementation
discretized_distribution_1 = np.histogram(
distribution_1, bins=nbins, range=(0, 1),
density=True
)[0]
discretized_distribution_1 /= discretized_distribution_1.sum()
discretized_distribution_1[discretized_distribution_1 == 0]=epsilon
discretized_distribution_2 = np.histogram(
distribution_2, bins=nbins, range=(0, 1),
density=True
)[0]
discretized_distribution_2 /= discretized_distribution_2.sum()
discretized_distribution_2[discretized_distribution_2 == 0]=epsilon
return - 1/2 * (
entropy(discretized_distribution_1, discretized_distribution_2) +\
entropy(discretized_distribution_2, discretized_distribution_1)
)
# distribution_diff = discretized_distribution_1 - discretized_distribution_2
# # Treat 0 error cases
# distribution_diff[np.where(discretized_distribution_1 == 0)] = 0
# distribution_diff[np.where(discretized_distribution_2 == 0)] = 0
# discretized_distribution_1[np.where(discretized_distribution_1 == 0)] = 1
# discretized_distribution_2[np.where(discretized_distribution_2 == 0)] = 1
# return np.sum(
# distribution_diff * (np.log(discretized_distribution_1) -\
# distribution_diff * np.log(discretized_distribution_2))
# ) / nbins
return transformation