






import numpy as np

import datasets


__snake_case    :    Tuple         =     """
Compute the Mahalanobis Distance

Mahalonobis distance is the distance between a point and a distribution.
And not between two distinct points. It is effectively a multivariate equivalent of the Euclidean distance.
It was introduced by Prof. P. C. Mahalanobis in 1936
and has been used in various statistical applications ever since
[source: https://www.machinelearningplus.com/statistics/mahalanobis-distance/]
"""

__snake_case    :    Dict         =     """\
@article{de2000mahalanobis,
  title={The mahalanobis distance},
  author={De Maesschalck, Roy and Jouan-Rimbaud, Delphine and Massart, D{\'e}sir{\'e} L},
  journal={Chemometrics and intelligent laboratory systems},
  volume={50},
  number={1},
  pages={1--18},
  year={2000},
  publisher={Elsevier}
}
"""

__snake_case    :    str         =     """
Args:
    X: List of datapoints to be compared with the `reference_distribution`.
    reference_distribution: List of datapoints from the reference distribution we want to compare to.
Returns:
    mahalanobis: The Mahalonobis distance for each datapoint in `X`.
Examples:

    >>> mahalanobis_metric = datasets.load_metric(\"mahalanobis\")
    >>> results = mahalanobis_metric.compute(reference_distribution=[[0, 1], [1, 0]], X=[[0, 1]])
    >>> print(results)
    {\'mahalanobis\': array([0.5])}
"""


@datasets.utils.file_utils.add_start_docstrings(_DESCRIPTION		,					_KWARGS_DESCRIPTION)
class     __SCREAMING_SNAKE_CASE					(   datasets.Metric):






						def       UpperCamelCase__      (	self   ):






												"""simple docstring"""
												return datasets.MetricInfo(
												    description=_DESCRIPTION     ,						citation=_CITATION     ,						inputs_description=_KWARGS_DESCRIPTION     ,						features=datasets.Features(
												        {
												            'X': datasets.Sequence(datasets.Value('float'     ,						id='sequence'   )     ,						id='X'   ),
												        }   )     ,						)







						def       UpperCamelCase__      (	self     ,						_UpperCamelCase     ,						_UpperCamelCase   ):






												"""simple docstring"""
												lowerCAmelCase__    						=						np.array(a_   )
												lowerCAmelCase__    						=						np.array(a_   )

												# Assert that arrays are 2D
												if len(X.shape   ) != 2:
																		raise ValueError('Expected `X` to be a 2D vector'   )
												if len(reference_distribution.shape   ) != 2:
																		raise ValueError('Expected `reference_distribution` to be a 2D vector'   )
												if reference_distribution.shape[0] < 2:
																		raise ValueError(
																		    'Expected `reference_distribution` to be a 2D vector with more than one element in the first dimension'   )

												# Get mahalanobis distance for each prediction
												lowerCAmelCase__    						=						X - np.mean(a_   )
												lowerCAmelCase__    						=						np.cov(reference_distribution.T   )
												try:
																		lowerCAmelCase__    						=						np.linalg.inv(a_   )
												except np.linalg.LinAlgError:
																		lowerCAmelCase__    						=						np.linalg.pinv(a_   )
												lowerCAmelCase__    						=						np.dot(a_     ,						a_   )
												lowerCAmelCase__    						=						np.dot(a_     ,						X_minus_mu.T   ).diagonal()

												return {"mahalanobis": mahal_dist}













from collections import deque

from .hash_table import HashTable


class     __SCREAMING_SNAKE_CASE					(   __lowercase):






						def __init__(	self     ,						*_UpperCamelCase     ,						**_UpperCamelCase   ):






												"""simple docstring"""
												super().__init__(*_UpperCamelCase     ,						**_UpperCamelCase   )






						def       UpperCamelCase__      (	self     ,						_UpperCamelCase     ,						_UpperCamelCase   ):






												"""simple docstring"""
												lowerCAmelCase__    						=						deque([]   ) if self.values[key] is None else self.values[key]
												self.values[key].appendleft(_UpperCamelCase   )
												lowerCAmelCase__    						=						self.values[key]






						def       UpperCamelCase__      (	self   ):






												"""simple docstring"""
												return (
												    sum(self.charge_factor - len(_UpperCamelCase   ) for slot in self.values   )
												    / self.size_table
												    * self.charge_factor
												)







						def       UpperCamelCase__      (	self     ,						_UpperCamelCase     ,						_UpperCamelCase=None   ):






												"""simple docstring"""
												if not (
												    len(self.values[key]   ) == self.charge_factor and self.values.count(_UpperCamelCase   ) == 0
												):
																		return key
												return super()._collision_resolution(_UpperCamelCase     ,						_UpperCamelCase   )





