"""Built-in metrics.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import abc
import numpy as np
import six
import types

from . import backend as K
from .layers import Layer
from .losses import mean_squared_error
from .losses import mean_absolute_error
from .losses import mean_absolute_percentage_error
from .losses import mean_squared_logarithmic_error
from .losses import hinge
from .losses import logcosh
from .losses import squared_hinge
from .losses import categorical_hinge
from .losses import categorical_crossentropy
from .losses import sparse_categorical_crossentropy
from .losses import binary_crossentropy
from .losses import kullback_leibler_divergence
from .losses import poisson
from .utils import losses_utils
from .utils import metrics_utils
from .utils.generic_utils import deserialize_keras_object
from .utils.generic_utils import serialize_keras_object

@six.add_metaclass(abc.ABCMeta)
class Metric(Layer):
    """Encapsulates metric logic and state.

    Standalone usage:
    ```python
    m = SomeMetric(...)
    for input in ...:
        m.update_state(input)
    m.result()
    ```

    Usage with the `compile` API:
    ```python
    model.compile(optimizer='rmsprop',
                  loss=keras.losses.categorical_crossentropy,
                  metrics=[keras.metrics.CategoricalAccuracy()])
    ```

    To be implemented by subclasses:
    * `__init__()`: All state variables should be created in this method by
        calling `self.add_weight()` like: `self.var = self.add_weight(...)`
    * `update_state()`: Has all updates to the state variables like:
        self.var.assign_add(...).
    * `result()`: Computes and returns a value for the metric
        from the state variables.
    """

    def __init__(self, name=None, dtype=None, **kwargs):
        super(Metric, self).__init__(name=name, dtype=dtype, **kwargs)
        self.stateful = True  # All metric layers are stateful.
        self.built = True
        self.dtype = dtype or K.floatx()

    def __new__(cls, *args, **kwargs):
        obj = super(Metric, cls).__new__(cls)

        obj.update_state = types.MethodType(
            metrics_utils.update_state_wrapper(obj.update_state), obj)

        obj.result = types.MethodType(
            metrics_utils.result_wrapper(obj.result), obj)
        return obj

    @K.symbolic
    def __call__(self, *args, **kwargs):
        """Accumulates statistics and then computes metric result value."""
        update_op = self.update_state(*args, **kwargs)
        with K.control_dependencies(update_op):  # For TF
            result_t = self.result()

            # We are adding the metric object as metadata on the result tensor.
            # This is required when we want to use a metric with `add_metric` API on
            # a Model/Layer in graph mode. This metric instance will later be used
            # to reset variable state after each epoch of training.
            # Example:
            #   model = Model()
            #   mean = Mean()
            #   model.add_metric(mean(values), name='mean')
            result_t._metric_obj = self
            return result_t

    def get_config(self):
        """Returns the serializable config of the metric."""
        return {'name': self.name, 'dtype': self.dtype}

    def reset_states(self):
        """Resets all of the metric state variables.

        This function is called between epochs/steps,
        when a metric is evaluated during training.
        """
        K.batch_set_value([(v, 0) for v in self.weights])

    @abc.abstractmethod
    def update_state(self, *args, **kwargs):
        """Accumulates statistics for the metric. """
        raise NotImplementedError('Must be implemented in subclasses.')

    @abc.abstractmethod
    def result(self):
        """Computes and returns the metric value tensor.

        Result computation is an idempotent operation that simply calculates the
        metric value using the state variables.
        """
        raise NotImplementedError('Must be implemented in subclasses.')

    # For use by subclasses #
    def add_weight(self,
                   name,
                   shape=(),
                   initializer=None,
                   dtype=None):
        """Adds state variable. Only for use by subclasses."""
        return super(Metric, self).add_weight(
            name=name,
            shape=shape,
            dtype=self.dtype if dtype is None else dtype,
            trainable=False,
            initializer=initializer)

    # End: For use by subclasses ###

class Reduce(Metric):
    """Encapsulates metrics that perform a reduce operation on the values."""

    def __init__(self, reduction, name, dtype=None):
        """Creates a `Reduce` instance.

        # Arguments
            reduction: a metrics `Reduction` enum value.
            name: string name of the metric instance.
            dtype: (Optional) data type of the metric result.
        """
        super(Reduce, self).__init__(name=name, dtype=dtype)
        self.reduction = reduction
        self.total = self.add_weight(
            'total', initializer='zeros')
        if reduction in [metrics_utils.Reduction.SUM_OVER_BATCH_SIZE,
                         metrics_utils.Reduction.WEIGHTED_MEAN]:
            self.count = self.add_weight(
                'count', initializer='zeros')

    def update_state(self, values, sample_weight=None):
        """Accumulates statistics for computing the reduction metric.

        For example, if `values` is [1, 3, 5, 7] and reduction=SUM_OVER_BATCH_SIZE,
        then the value of `result()` is 4. If the `sample_weight` is specified as
        [1, 1, 0, 0] then value of `result()` would be 2.

        # Arguments
            values: Per-example value.
            sample_weight: Optional weighting of each example. Defaults to 1.

        # Returns
            List of update ops.
        """
        values = K.cast(values, self.dtype)
        if sample_weight is not None:
            sample_weight = K.cast(sample_weight, self.dtype)

            # Update dimensions of weights to match with values if possible.
            values, _, sample_weight = losses_utils.squeeze_or_expand_dimensions(
                values, sample_weight=sample_weight)

            # Broadcast weights if possible.
            sample_weight = losses_utils.broadcast_weights(values, sample_weight)

            values = values * sample_weight

        value_sum = K.sum(values)
        update_total_op = K.update_add(self.total, value_sum)

        # Exit early if the reduction doesn't have a denominator.
        if self.reduction == metrics_utils.Reduction.SUM:
            return [update_total_op]

        # Update `count` for reductions that require a denominator.
        if self.reduction == metrics_utils.Reduction.SUM_OVER_BATCH_SIZE:
            num_values = K.cast(K.size(values), self.dtype)
        elif self.reduction == metrics_utils.Reduction.WEIGHTED_MEAN:
            if sample_weight is None:
                num_values = K.cast(K.size(values), self.dtype)
            else:
                num_values = K.sum(sample_weight)
        else:
            raise NotImplementedError(
                'reduction [%s] not implemented' % self.reduction)

        return [update_total_op, K.update_add(self.count, num_values)]

    def result(self):
        if self.reduction == metrics_utils.Reduction.SUM:
            return self.total
        elif self.reduction in [
            metrics_utils.Reduction.WEIGHTED_MEAN,
            metrics_utils.Reduction.SUM_OVER_BATCH_SIZE
        ]:
            return self.total / self.count
        else:
            raise NotImplementedError(
                'reduction [%s] not implemented' % self.reduction)

class Sum(Reduce):
    """Computes the (weighted) sum of the given values.

    For example, if values is [1, 3, 5, 7] then the sum is 16.
    If the weights were specified as [1, 1, 0, 0] then the sum would be 4.

    This metric creates one variable, `total`, that is used to compute the sum of
    `values`. This is ultimately returned as `sum`.
    If `sample_weight` is `None`, weights default to 1.  Use `sample_weight` of 0
    to mask values.

    Standalone usage:
    ```python
    m = keras.metrics.Sum()
    m.update_state([1, 3, 5, 7])
    m.result()
    ```
    """

    def __init__(self, name='sum', dtype=None):
        """Creates a `Sum` instance.

        # Arguments
            name: (Optional) string name of the metric instance.
            dtype: (Optional) data type of the metric result.
        """
        super(Sum, self).__init__(reduction=metrics_utils.Reduction.SUM,
                                  name=name, dtype=dtype)

class Mean(Reduce):
    """Computes the (weighted) mean of the given values.

    For example, if values is [1, 3, 5, 7] then the mean is 4.
    If the weights were specified as [1, 1, 0, 0] then the mean would be 2.

    This metric creates two variables, `total` and `count` that are used to
    compute the average of `values`. This average is ultimately returned as `mean`
    which is an idempotent operation that simply divides `total` by `count`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage:

    ```python
    m = keras.metrics.Mean()
    m.update_state([1, 3, 5, 7])
    m.result()
    ```
    """

    def __init__(self, name='mean', dtype=None):
        """Creates a `Mean` instance.

        #Arguments
            name: (Optional) string name of the metric instance.
            dtype: (Optional) data type of the metric result.
        """
        super(Mean, self).__init__(
            reduction=metrics_utils.Reduction.WEIGHTED_MEAN, name=name, dtype=dtype)

class MeanMetricWrapper(Mean):
    """Wraps a stateless metric function with the Mean metric."""

    def __init__(self, fn, name=None, dtype=None, **kwargs):
        """Creates a `MeanMetricWrapper` instance.

        # Arguments
            fn: The metric function to wrap, with signature
                `fn(y_true, y_pred, **kwargs)`.
            name: (Optional) string name of the metric instance.
            dtype: (Optional) data type of the metric result.
            **kwargs: The keyword arguments that are passed on to `fn`.
        """
        super(MeanMetricWrapper, self).__init__(name=name, dtype=dtype)
        self._fn = fn
        self._fn_kwargs = kwargs

    def update_state(self, y_true, y_pred, sample_weight=None):
        """Accumulates metric statistics.

        `y_true` and `y_pred` should have the same shape.

        # Arguments
            y_true: The ground truth values.
            y_pred: The predicted values.
            sample_weight: Optional weighting of each example. Defaults to 1. Can be
                a `Tensor` whose rank is either 0, or the same rank as `y_true`,
                and must be broadcastable to `y_true`.

        # Returns
            Update op.
        """
        y_true = K.cast(y_true, self.dtype)
        y_pred = K.cast(y_pred, self.dtype)
        y_pred, y_true = losses_utils.squeeze_or_expand_dimensions(y_pred, y_true)

        matches = self._fn(y_true, y_pred, **self._fn_kwargs)
        return super(MeanMetricWrapper, self).update_state(
            matches, sample_weight=sample_weight)

    def get_config(self):
        config = {}
        for k, v in six.iteritems(self._fn_kwargs):
            config[k] = K.eval(v) if K.is_tensor(v) else v
        base_config = super(MeanMetricWrapper, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class MeanSquaredError(MeanMetricWrapper):
    """Computes the mean squared error between `y_true` and `y_pred`.

    Standalone usage:

    ```python
    m = keras.metrics.MeanSquaredError()
    m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.])
    m.result()
    ```

    Usage with compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.MeanSquaredError()])
    ```
    """

    def __init__(self, name='mean_squared_error', dtype=None):
        super(MeanSquaredError, self).__init__(
            mean_squared_error, name, dtype=dtype)

class Hinge(MeanMetricWrapper):
    """Computes the hinge metric between `y_true` and `y_pred`.

    `y_true` values are expected to be -1 or 1. If binary (0 or 1) labels are
    provided we will convert them to -1 or 1.
    For example, if `y_true` is [-1., 1., 1.], and `y_pred` is [0.6, -0.7, -0.5]
    the hinge metric value is 1.6.

    Usage:

    ```python
    m = keras.metrics.Hinge()
    m.update_state([-1., 1., 1.], [0.6, -0.7, -0.5])
    # result = max(0, 1-y_true * y_pred) = [1.6 + 1.7 + 1.5] / 3
    # Final result: 1.6
    ```

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.Hinge()])
    ```
    """

    def __init__(self, name='hinge', dtype=None):
        super(Hinge, self).__init__(hinge, name, dtype=dtype)

class SquaredHinge(MeanMetricWrapper):
    """Computes the squared hinge metric between `y_true` and `y_pred`.

    `y_true` values are expected to be -1 or 1. If binary (0 or 1) labels are
    provided we will convert them to -1 or 1.
    For example, if `y_true` is [-1., 1., 1.], and `y_pred` is [0.6, -0.7, -0.5]
    the squared hinge metric value is 2.6.

    Usage:

    ```python
    m = keras.metrics.SquaredHinge()
    m.update_state([-1., 1., 1.], [0.6, -0.7, -0.5])
    # result = max(0, 1-y_true * y_pred) = [1.6^2 + 1.7^2 + 1.5^2] / 3
    # Final result: 2.6
    ```

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.SquaredHinge()])
    ```
    """

    def __init__(self, name='squared_hinge', dtype=None):
        super(SquaredHinge, self).__init__(squared_hinge, name, dtype=dtype)

class CategoricalHinge(MeanMetricWrapper):
    """Computes the categorical hinge metric between `y_true` and `y_pred`.

    For example, if `y_true` is [0., 1., 1.], and `y_pred` is [1., 0., 1.]
    the categorical hinge metric value is 1.0.

    Usage:

    ```python
    m = keras.metrics.CategoricalHinge()
    m.update_state([0., 1., 1.], [1., 0., 1.])
    # Final result: 1.0
    ```

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.CategoricalHinge()])
    ```
    """

    def __init__(self, name='categorical_hinge', dtype=None):
        super(CategoricalHinge, self).__init__(
            categorical_hinge, name, dtype=dtype)

class Accuracy(MeanMetricWrapper):
    """Calculates how often predictions matches labels.

    For example, if `y_true` is [1, 2, 3, 4] and `y_pred` is [0, 2, 3, 4]
    then the accuracy is 3/4 or .75.  If the weights were specified as
    [1, 1, 0, 0] then the accuracy would be 1/2 or .5.

    This metric creates two local variables, `total` and `count` that are used to
    compute the frequency with which `y_pred` matches `y_true`. This frequency is
    ultimately returned as `binary accuracy`: an idempotent operation that simply
    divides `total` by `count`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    ```

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.Accuracy()])
    ```
    """

    def __init__(self, name='accuracy', dtype=None):
        super(Accuracy, self).__init__(accuracy, name, dtype=dtype)

class BinaryAccuracy(MeanMetricWrapper):
    """Calculates how often predictions matches labels.

    For example, if `y_true` is [1, 1, 0, 0] and `y_pred` is [0.98, 1, 0, 0.6]
    then the binary accuracy is 3/4 or .75.  If the weights were specified as
    [1, 0, 0, 1] then the binary accuracy would be 1/2 or .5.

    This metric creates two local variables, `total` and `count` that are used to
    compute the frequency with which `y_pred` matches `y_true`. This frequency is
    ultimately returned as `binary accuracy`: an idempotent operation that simply
    divides `total` by `count`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.BinaryAccuracy()])
    ```

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        threshold: (Optional) Float representing the threshold for deciding
            whether prediction values are 1 or 0.
    """

    def __init__(self, name='binary_accuracy', dtype=None, threshold=0.5):
        super(BinaryAccuracy, self).__init__(
            binary_accuracy, name, dtype=dtype, threshold=threshold)

class CategoricalAccuracy(MeanMetricWrapper):
    """Calculates how often predictions matches labels.

    For example, if `y_true` is [[0, 0, 1], [0, 1, 0]] and `y_pred` is
    [[0.1, 0.9, 0.8], [0.05, 0.95, 0]] then the categorical accuracy is 1/2 or .5.
    If the weights were specified as [0.7, 0.3] then the categorical accuracy
    would be .3. You can provide logits of classes as `y_pred`, since argmax of
    logits and probabilities are same.

    This metric creates two local variables, `total` and `count` that are used to
    compute the frequency with which `y_pred` matches `y_true`. This frequency is
    ultimately returned as `categorical accuracy`: an idempotent operation that
    simply divides `total` by `count`.

    `y_pred` and `y_true` should be passed in as vectors of probabilities, rather
    than as labels. If necessary, use `K.one_hot` to expand `y_true` as a vector.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        loss='mse',
        metrics=[keras.metrics.CategoricalAccuracy()])
    ```

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, name='categorical_accuracy', dtype=None):
        super(CategoricalAccuracy, self).__init__(
            categorical_accuracy, name, dtype=dtype)

class SparseCategoricalAccuracy(MeanMetricWrapper):
    """Calculates how often predictions matches integer labels.

    For example, if `y_true` is [[2], [1]] and `y_pred` is
    [[0.1, 0.9, 0.8], [0.05, 0.95, 0]] then the categorical accuracy is 1/2 or .5.
    If the weights were specified as [0.7, 0.3] then the categorical accuracy
    would be .3. You can provide logits of classes as `y_pred`, since argmax of
    logits and probabilities are same.

    This metric creates two local variables, `total` and `count` that are used to
    compute the frequency with which `y_pred` matches `y_true`. This frequency is
    ultimately returned as `sparse categorical accuracy`: an idempotent operation
    that simply divides `total` by `count`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        loss='mse',
        metrics=[keras.metrics.SparseCategoricalAccuracy()])
    ```
    """

    def __init__(self, name='sparse_categorical_accuracy', dtype=None):
        super(SparseCategoricalAccuracy, self).__init__(
            sparse_categorical_accuracy, name, dtype=dtype)

class TopKCategoricalAccuracy(MeanMetricWrapper):
    """Computes how often targets are in the top `K` predictions.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.TopKCategoricalAccuracy()])
    ```

    # Arguments
        k: (Optional) Number of top elements to look at for computing accuracy.
            Defaults to 5.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, k=5, name='top_k_categorical_accuracy', dtype=None):
        super(TopKCategoricalAccuracy, self).__init__(
            top_k_categorical_accuracy, name, dtype=dtype, k=k)

class SparseTopKCategoricalAccuracy(MeanMetricWrapper):
    """Computes how often integer targets are in the top `K` predictions.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        metrics=[keras.metrics.SparseTopKCategoricalAccuracy()])
    ```

    # Arguments
        k: (Optional) Number of top elements to look at for computing accuracy.
            Defaults to 5.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, k=5, name='sparse_top_k_categorical_accuracy', dtype=None):
        super(SparseTopKCategoricalAccuracy, self).__init__(
            sparse_top_k_categorical_accuracy, name, dtype=dtype, k=k)

class LogCoshError(MeanMetricWrapper):
    """Computes the logarithm of the hyperbolic cosine of the prediction error.

    `metric = log((exp(x) + exp(-x))/2)`, where x is the error (y_pred - y_true)

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.LogCoshError()])
    ```
    """

    def __init__(self, name='logcosh', dtype=None):
        super(LogCoshError, self).__init__(logcosh, name, dtype=dtype)

class Poisson(MeanMetricWrapper):
    """Computes the Poisson metric between `y_true` and `y_pred`.

    `metric = y_pred - y_true * log(y_pred)`

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.Poisson()])
    ```
    """

    def __init__(self, name='poisson', dtype=None):
        super(Poisson, self).__init__(poisson, name, dtype=dtype)

class KLDivergence(MeanMetricWrapper):
    """Computes Kullback-Leibler divergence metric between `y_true` and `y_pred`.

    `metric = y_true * log(y_true / y_pred)`

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.KLDivergence()])
    ```
    """

    def __init__(self, name='kullback_leibler_divergence', dtype=None):
        super(KLDivergence, self).__init__(
            kullback_leibler_divergence, name, dtype=dtype)

class CosineSimilarity(MeanMetricWrapper):
    """Computes the cosine similarity between the labels and predictions.

    cosine similarity = (a . b) / ||a|| ||b||
    [Cosine Similarity](https://en.wikipedia.org/wiki/Cosine_similarity)
    For example, if `y_true` is [0, 1, 1], and `y_pred` is [1, 0, 1], the cosine
    similarity is 0.5.

    This metric keeps the average cosine similarity between `predictions` and
    `labels` over a stream of data.

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        axis: (Optional) Defaults to -1. The dimension along which the cosine
        similarity is computed.

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
      'sgd',
      loss='mse',
      metrics=[keras.metrics.CosineSimilarity(axis=1)])
    ```
    """

    def __init__(self, name='cosine_similarity', dtype=None, axis=-1):
        super(CosineSimilarity, self).__init__(
            cosine_similarity, name, dtype=dtype, axis=axis)

class MeanAbsoluteError(MeanMetricWrapper):
    """Computes the mean absolute error between the labels and predictions.

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.MeanAbsoluteError()])
    ```
    """

    def __init__(self, name='mean_absolute_error', dtype=None):
        super(MeanAbsoluteError, self).__init__(
            mean_absolute_error, name, dtype=dtype)

class MeanAbsolutePercentageError(MeanMetricWrapper):
    """Computes the mean absolute percentage error between `y_true` and `y_pred`.

    For example, if `y_true` is [0., 0., 1., 1.], and `y_pred` is [1., 1., 1., 0.]
    the mean absolute percentage error is 5e+08.

    Usage:

    ```python
    m = keras.metrics.MeanAbsolutePercentageError()
    m.update_state([0., 0., 1., 1.], [1., 1., 1., 0.])
    # Final result: 5e+08
    ```

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.MeanAbsolutePercentageError()])
    ```
    """

    def __init__(self, name='mean_absolute_percentage_error', dtype=None):
        super(MeanAbsolutePercentageError, self).__init__(
            mean_absolute_percentage_error, name, dtype=dtype)

class MeanSquaredError(MeanMetricWrapper):
    """Computes the mean squared error between `y_true` and `y_pred`.

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.MeanSquaredError()])
    ```
    """

    def __init__(self, name='mean_squared_error', dtype=None):
        super(MeanSquaredError, self).__init__(
            mean_squared_error, name, dtype=dtype)

class MeanSquaredLogarithmicError(MeanMetricWrapper):
    """Computes the mean squared logarithmic error between `y_true` and `y_pred`.

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.MeanSquaredLogarithmicError()])
    ```
    """

    def __init__(self, name='mean_squared_logarithmic_error', dtype=None):
        super(MeanSquaredLogarithmicError, self).__init__(
            mean_squared_logarithmic_error, name, dtype=dtype)

class RootMeanSquaredError(Mean):
    """Computes root mean squared error metric between `y_true` and `y_pred`.

    Usage with the `compile` API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', metrics=[keras.metrics.RootMeanSquaredError()])
    ```
    """

    def __init__(self, name='root_mean_squared_error', dtype=None):
        super(RootMeanSquaredError, self).__init__(name, dtype=dtype)

    def update_state(self, y_true, y_pred, sample_weight=None):
        """Accumulates root mean squared error statistics.

        # Arguments
            y_true: The ground truth values.
            y_pred: The predicted values.
            sample_weight: Optional weighting of each example. Defaults to 1.
                Can be a `Tensor` whose rank is either 0,
                or the same rank as `y_true`,
                and must be broadcastable to `y_true`.

        # Returns
            List of update ops.
        """
        error_sq = K.square(y_pred - y_true)
        return super(RootMeanSquaredError, self).update_state(
            error_sq, sample_weight=sample_weight)

    def result(self):
        return K.sqrt(self.total / self.count)

class BinaryCrossentropy(MeanMetricWrapper):
    """Computes the crossentropy metric between the labels and predictions.

    This is the crossentropy metric class to be used when there are only two
    label classes (0 and 1).

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
      'sgd',
      loss='mse',
      metrics=[keras.metrics.BinaryCrossentropy()])
    ```

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        from_logits: (Optional )Whether output is expected to be a logits tensor.
            By default, we consider that output encodes a probability distribution.
        label_smoothing: (Optional) Float in [0, 1]. When > 0, label values are
            smoothed, meaning the confidence on label values are relaxed.
            e.g. `label_smoothing=0.2` means that we will use a value of `0.1` for
            label `0` and `0.9` for label `1`"
    """

    def __init__(self,
                 name='binary_crossentropy',
                 dtype=None,
                 from_logits=False,
                 label_smoothing=0):
        super(BinaryCrossentropy, self).__init__(
            binary_crossentropy,
            name,
            dtype=dtype,
            from_logits=from_logits,
            label_smoothing=label_smoothing)

class CategoricalCrossentropy(MeanMetricWrapper):
    """Computes the crossentropy metric between the labels and predictions.

    This is the crossentropy metric class to be used when there are multiple
    label classes (2 or more). Here we assume that labels are given as a `one_hot`
    representation. eg., When labels values are [2, 0, 1],
    `y_true` = [[0, 0, 1], [1, 0, 0], [0, 1, 0]].

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
    'sgd',
    loss='mse',
    metrics=[keras.metrics.CategoricalCrossentropy()])
    ```

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        from_logits: (Optional ) Whether `y_pred` is expected to be a logits tensor.
            By default, we assume that `y_pred` encodes a probability distribution.
        label_smoothing: Float in [0, 1]. When > 0, label values are smoothed,
            meaning the confidence on label values are relaxed. e.g.
            `label_smoothing=0.2` means that we will use a value of `0.1` for label
            `0` and `0.9` for label `1`"
    """

    def __init__(self,
                 name='categorical_crossentropy',
                 dtype=None,
                 from_logits=False,
                 label_smoothing=0):
        super(CategoricalCrossentropy, self).__init__(
            categorical_crossentropy,
            name,
            dtype=dtype,
            from_logits=from_logits,
            label_smoothing=label_smoothing)

class SparseCategoricalCrossentropy(MeanMetricWrapper):
    """Computes the crossentropy metric between the labels and predictions.

    Use this crossentropy metric when there are two or more label classes.
    We expect labels to be provided as integers. If you want to provide labels
    using `one-hot` representation, please use `CategoricalCrossentropy` metric.
    There should be `# classes` floating point values per feature for `y_pred`
    and a single floating point value per feature for `y_true`.

    In the snippet below, there is a single floating point value per example for
    `y_true` and `# classes` floating pointing values per example for `y_pred`.
    The shape of `y_true` is `[batch_size]` and the shape of `y_pred` is
    `[batch_size, num_classes]`.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
    'sgd',
    loss='mse',
    metrics=[keras.metrics.SparseCategoricalCrossentropy()])
    ```

    # Arguments
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        from_logits: (Optional ) Whether `y_pred` is expected to be a logits tensor.
            By default, we assume that `y_pred` encodes a probability distribution.
        axis: (Optional) Defaults to -1. The dimension along which the metric is
            computed.
    """

    def __init__(self,
                 name='sparse_categorical_crossentropy',
                 dtype=None,
                 from_logits=False,
                 axis=-1):
        super(SparseCategoricalCrossentropy, self).__init__(
            sparse_categorical_crossentropy,
            name,
            dtype=dtype,
            from_logits=from_logits,
            axis=axis)

class _ConfusionMatrixConditionCount(Metric):
    """Calculates the number of the given confusion matrix condition.

    # Arguments
        confusion_matrix_cond: One of `metrics_utils.ConfusionMatrix` conditions.
        thresholds: (Optional) Defaults to 0.5. A float value or a python
            list/tuple of float threshold values in [0, 1]. A threshold is compared
            with prediction values to determine the truth value of predictions
            (i.e., above the threshold is `true`, below is `false`). One metric
            value is generated for each threshold value.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self,
                 confusion_matrix_cond,
                 thresholds=None,
                 name=None,
                 dtype=None):
        super(_ConfusionMatrixConditionCount, self).__init__(name=name, dtype=dtype)
        self._confusion_matrix_cond = confusion_matrix_cond
        self.init_thresholds = thresholds
        self.thresholds = metrics_utils.parse_init_thresholds(
            thresholds, default_threshold=0.5)
        self.accumulator = self.add_weight(
            'accumulator',
            shape=(len(self.thresholds),),
            initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        return metrics_utils.update_confusion_matrix_variables(
            {self._confusion_matrix_cond: self.accumulator},
            y_true,
            y_pred,
            thresholds=self.thresholds,
            sample_weight=sample_weight)

    def result(self):
        if len(self.thresholds) == 1:
            return self.accumulator[0]
        return self.accumulator

    def reset_states(self):
        num_thresholds = len(metrics_utils.to_list(self.thresholds))
        K.batch_set_value(
            [(v, np.zeros((num_thresholds,))) for v in self.weights])

    def get_config(self):
        config = {'thresholds': self.init_thresholds}
        base_config = super(_ConfusionMatrixConditionCount, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class FalsePositives(_ConfusionMatrixConditionCount):
    """Calculates the number of false positives.

    For example, if `y_true` is [0, 1, 0, 0] and `y_pred` is [0, 0, 1, 1]
    then the false positives value is 2.  If the weights were specified as
    [0, 0, 1, 0] then the false positives value would be 1.

    If `sample_weight` is given, calculates the sum of the weights of
    false positives. This metric creates one local variable, `accumulator`
    that is used to keep track of the number of false positives.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.FalsePositives()])
    ```

    # Arguments
        thresholds: (Optional) Defaults to 0.5. A float value or a python
            list/tuple of float threshold values in [0, 1]. A threshold is
            compared with prediction values to determine the truth value of
            predictions (i.e., above the threshold is `true`, below is `false`).
            One metric value is generated for each threshold value.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, thresholds=None, name=None, dtype=None):
        super(FalsePositives, self).__init__(
            confusion_matrix_cond=metrics_utils.ConfusionMatrix.FALSE_POSITIVES,
            thresholds=thresholds,
            name=name,
            dtype=dtype)

class TruePositives(_ConfusionMatrixConditionCount):
    """Calculates the number of true positives.

    For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1]
    then the true positives value is 2.  If the weights were specified as
    [0, 0, 1, 0] then the true positives value would be 1.

    If `sample_weight` is given, calculates the sum of the weights of
    true positives. This metric creates one local variable, `accumulator`
    that is used to keep track of the number of true positives.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.TruePositives()])
    ```

    # Arguments
        thresholds: (Optional) Defaults to 0.5. A float value or a python
            list/tuple of float threshold values in [0, 1]. A threshold is compared
            with prediction values to determine the truth value of predictions
            (i.e., above the threshold is `true`, below is `false`). One metric
            value is generated for each threshold value.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, thresholds=None, name=None, dtype=None):
        super(TruePositives, self).__init__(
            confusion_matrix_cond=metrics_utils.ConfusionMatrix.TRUE_POSITIVES,
            thresholds=thresholds,
            name=name,
            dtype=dtype)

class TrueNegatives(_ConfusionMatrixConditionCount):
    """Calculates the number of true negatives.

    For example, if `y_true` is [0, 1, 0, 0] and `y_pred` is [1, 1, 0, 0]
    then the true negatives value is 2.  If the weights were specified as
    [0, 0, 1, 0] then the true negatives value would be 1.

    If `sample_weight` is given, calculates the sum of the weights of
    true negatives. This metric creates one local variable, `accumulator`
    that is used to keep track of the number of true negatives.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.TrueNegatives()])
    ```

    # Arguments
        thresholds: (Optional) Defaults to 0.5. A float value or a python
            list/tuple of float threshold values in [0, 1]. A threshold is compared
            with prediction values to determine the truth value of predictions
            (i.e., above the threshold is `true`, below is `false`). One metric
            value is generated for each threshold value.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, thresholds=None, name=None, dtype=None):
        super(TrueNegatives, self).__init__(
            confusion_matrix_cond=metrics_utils.ConfusionMatrix.TRUE_NEGATIVES,
            thresholds=thresholds,
            name=name,
            dtype=dtype)

class FalseNegatives(_ConfusionMatrixConditionCount):
    """Calculates the number of false negatives.

    For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [0, 1, 0, 0]
    then the false negatives value is 2.  If the weights were specified as
    [0, 0, 1, 0] then the false negatives value would be 1.

    If `sample_weight` is given, calculates the sum of the weights of
    false negatives. This metric creates one local variable, `accumulator`
    that is used to keep track of the number of false negatives.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.FalseNegatives()])
    ```

    # Arguments
        thresholds: (Optional) Defaults to 0.5. A float value or a python
            list/tuple of float threshold values in [0, 1]. A threshold is compared
            with prediction values to determine the truth value of predictions
            (i.e., above the threshold is `true`, below is `false`). One metric
            value is generated for each threshold value.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, thresholds=None, name=None, dtype=None):
        super(FalseNegatives, self).__init__(
            confusion_matrix_cond=metrics_utils.ConfusionMatrix.FALSE_NEGATIVES,
            thresholds=thresholds,
            name=name,
            dtype=dtype)

class SensitivitySpecificityBase(Metric):
    """Abstract base class for computing sensitivity and specificity.

    For additional information about specificity and sensitivity, see the
    following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity
    """

    def __init__(self, value, num_thresholds=200, name=None, dtype=None):
        super(SensitivitySpecificityBase, self).__init__(name=name, dtype=dtype)
        if num_thresholds <= 0:
            raise ValueError('`num_thresholds` must be > 0.')
        self.value = value
        self.true_positives = self.add_weight(
            'true_positives',
            shape=(num_thresholds,),
            initializer='zeros')
        self.true_negatives = self.add_weight(
            'true_negatives',
            shape=(num_thresholds,),
            initializer='zeros')
        self.false_positives = self.add_weight(
            'false_positives',
            shape=(num_thresholds,),
            initializer='zeros')
        self.false_negatives = self.add_weight(
            'false_negatives',
            shape=(num_thresholds,),
            initializer='zeros')

        # Compute `num_thresholds` thresholds in [0, 1]
        if num_thresholds == 1:
            self.thresholds = [0.5]
        else:
            thresholds = [(i + 1) * 1.0 / (num_thresholds - 1)
                          for i in range(num_thresholds - 2)]
            self.thresholds = [0.0] + thresholds + [1.0]

    def update_state(self, y_true, y_pred, sample_weight=None):
        return metrics_utils.update_confusion_matrix_variables(
            {
                metrics_utils.ConfusionMatrix.TRUE_POSITIVES: self.true_positives,
                metrics_utils.ConfusionMatrix.TRUE_NEGATIVES: self.true_negatives,
                metrics_utils.ConfusionMatrix.FALSE_POSITIVES: self.false_positives,
                metrics_utils.ConfusionMatrix.FALSE_NEGATIVES: self.false_negatives,
            },
            y_true,
            y_pred,
            thresholds=self.thresholds,
            sample_weight=sample_weight)

    def reset_states(self):
        num_thresholds = len(self.thresholds)
        K.batch_set_value(
            [(v, np.zeros((num_thresholds,))) for v in self.weights])

class SensitivityAtSpecificity(SensitivitySpecificityBase):
    """Computes the sensitivity at a given specificity.

    `Sensitivity` measures the proportion of actual positives that are correctly
    identified as such (tp / (tp + fn)).
    `Specificity` measures the proportion of actual negatives that are correctly
    identified as such (tn / (tn + fp)).

    This metric creates four local variables, `true_positives`, `true_negatives`,
    `false_positives` and `false_negatives` that are used to compute the
    sensitivity at the given specificity. The threshold for the given specificity
    value is computed and used to evaluate the corresponding sensitivity.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    For additional information about specificity and sensitivity, see the
    following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        loss='mse',
        metrics=[keras.metrics.SensitivityAtSpecificity()])
    ```

    # Arguments
        specificity: A scalar value in range `[0, 1]`.
        num_thresholds: (Optional) Defaults to 200. The number of thresholds to
            use for matching the given specificity.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, specificity, num_thresholds=200, name=None, dtype=None):
        if specificity < 0 or specificity > 1:
            raise ValueError('`specificity` must be in the range [0, 1].')
        self.specificity = specificity
        self.num_thresholds = num_thresholds
        super(SensitivityAtSpecificity, self).__init__(
            specificity, num_thresholds=num_thresholds, name=name, dtype=dtype)

    def result(self):
        # Calculate specificities at all the thresholds.
        specificities = K.switch(
            K.greater(self.true_negatives + self.false_positives, 0),
            (self.true_negatives / (self.true_negatives + self.false_positives)),
            K.zeros_like(self.thresholds))

        # Find the index of the threshold where the specificity is closest to the
        # given specificity.
        min_index = K.argmin(
            K.abs(specificities - self.value), axis=0)
        min_index = K.cast(min_index, 'int32')

        # Compute sensitivity at that index.
        denom = self.true_positives[min_index] + self.false_negatives[min_index]
        return K.switch(
            K.greater(denom, 0),
            self.true_positives[min_index] / denom,
            K.zeros_like(self.true_positives[min_index]))

    def get_config(self):
        config = {
            'num_thresholds': self.num_thresholds,
            'specificity': self.specificity
        }
        base_config = super(SensitivityAtSpecificity, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class SpecificityAtSensitivity(SensitivitySpecificityBase):
    """Computes the specificity at a given sensitivity.

    `Sensitivity` measures the proportion of actual positives that are correctly
    identified as such (tp / (tp + fn)).
    `Specificity` measures the proportion of actual negatives that are correctly
    identified as such (tn / (tn + fp)).

    This metric creates four local variables, `true_positives`, `true_negatives`,
    `false_positives` and `false_negatives` that are used to compute the
    specificity at the given sensitivity. The threshold for the given sensitivity
    value is computed and used to evaluate the corresponding specificity.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    For additional information about specificity and sensitivity, see the
    following: https://en.wikipedia.org/wiki/Sensitivity_and_specificity

    Usage with the compile API:
    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        loss='mse',
        metrics=[keras.metrics.SpecificityAtSensitivity()])
    ```

    # Arguments
        sensitivity: A scalar value in range `[0, 1]`.
        num_thresholds: (Optional) Defaults to 200. The number of thresholds to
            use for matching the given specificity.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self, sensitivity, num_thresholds=200, name=None, dtype=None):
        if sensitivity < 0 or sensitivity > 1:
            raise ValueError('`sensitivity` must be in the range [0, 1].')
        self.sensitivity = sensitivity
        self.num_thresholds = num_thresholds
        super(SpecificityAtSensitivity, self).__init__(
            sensitivity, num_thresholds=num_thresholds, name=name, dtype=dtype)

    def result(self):
        # Calculate sensitivities at all the thresholds.
        sensitivities = K.switch(
            K.greater(self.true_positives + self.false_negatives, 0),
            (self.true_positives / (self.true_positives + self.false_negatives)),
            K.zeros_like(self.thresholds))

        # Find the index of the threshold where the sensitivity is closest to the
        # given specificity.
        min_index = K.argmin(
            K.abs(sensitivities - self.value), axis=0)
        min_index = K.cast(min_index, 'int32')

        # Compute specificity at that index.
        denom = (self.true_negatives[min_index] + self.false_positives[min_index])
        return K.switch(
            K.greater(denom, 0),
            self.true_negatives[min_index] / denom,
            K.zeros_like(self.true_negatives[min_index]))

    def get_config(self):
        config = {
            'num_thresholds': self.num_thresholds,
            'sensitivity': self.sensitivity
        }
        base_config = super(SpecificityAtSensitivity, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class Precision(Metric):
    """Computes the precision of the predictions with respect to the labels.

    For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1]
    then the precision value is 2/(2+1) ie. 0.66. If the weights were specified as
    [0, 0, 1, 0] then the precision value would be 1.

    The metric creates two local variables, `true_positives` and `false_positives`
    that are used to compute the precision. This value is ultimately returned as
    `precision`, an idempotent operation that simply divides `true_positives`
    by the sum of `true_positives` and `false_positives`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    If `top_k` is set, we'll calculate precision as how often on average a class
    among the top-k classes with the highest predicted values of a batch entry is
    correct and can be found in the label for that entry.

    If `class_id` is specified, we calculate precision by considering only the
    entries in the batch for which `class_id` is above the threshold and/or in the
    top-k highest predictions, and computing the fraction of them for which
    `class_id` is indeed a correct label.

    Usage with the compile API:
    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.Precision()])
    ```

    # Arguments
        thresholds: (Optional) A float value or a python list/tuple of float
            threshold values in [0, 1]. A threshold is compared with prediction
            values to determine the truth value of predictions (i.e., above the
            threshold is `true`, below is `false`). One metric value is generated
            for each threshold value. If neither thresholds nor top_k are set, the
            default is to calculate precision with `thresholds=0.5`.
        top_k: (Optional) Unset by default. An int value specifying the top-k
            predictions to consider when calculating precision.
        class_id: (Optional) Integer class ID for which we want binary metrics.
            This must be in the half-open interval `[0, num_classes)`, where
            `num_classes` is the last dimension of predictions.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self,
                 thresholds=None,
                 top_k=None,
                 class_id=None,
                 name=None,
                 dtype=None):
        super(Precision, self).__init__(name=name, dtype=dtype)
        self.init_thresholds = thresholds
        if top_k is not None and K.backend() != 'tensorflow':
            raise RuntimeError(
                '`top_k` argument for `Precision` metric is currently supported '
                'only with TensorFlow backend.')

        self.top_k = top_k
        self.class_id = class_id

        default_threshold = 0.5 if top_k is None else metrics_utils.NEG_INF
        self.thresholds = metrics_utils.parse_init_thresholds(
            thresholds, default_threshold=default_threshold)
        self.true_positives = self.add_weight(
            'true_positives',
            shape=(len(self.thresholds),),
            initializer='zeros')
        self.false_positives = self.add_weight(
            'false_positives',
            shape=(len(self.thresholds),),
            initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        return metrics_utils.update_confusion_matrix_variables(
            {
                metrics_utils.ConfusionMatrix.TRUE_POSITIVES: self.true_positives,
                metrics_utils.ConfusionMatrix.FALSE_POSITIVES: self.false_positives
            },
            y_true,
            y_pred,
            thresholds=self.thresholds,
            top_k=self.top_k,
            class_id=self.class_id,
            sample_weight=sample_weight)

    def result(self):
        denom = (self.true_positives + self.false_positives)
        result = K.switch(
            K.greater(denom, 0),
            self.true_positives / denom,
            K.zeros_like(self.true_positives))

        return result[0] if len(self.thresholds) == 1 else result

    def reset_states(self):
        num_thresholds = len(metrics_utils.to_list(self.thresholds))
        K.batch_set_value(
            [(v, np.zeros((num_thresholds,))) for v in self.weights])

    def get_config(self):
        config = {
            'thresholds': self.init_thresholds,
            'top_k': self.top_k,
            'class_id': self.class_id
        }
        base_config = super(Precision, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class Recall(Metric):
    """Computes the recall of the predictions with respect to the labels.

    For example, if `y_true` is [0, 1, 1, 1] and `y_pred` is [1, 0, 1, 1]
    then the recall value is 2/(2+1) ie. 0.66. If the weights were specified as
    [0, 0, 1, 0] then the recall value would be 1.

    This metric creates two local variables, `true_positives` and
    `false_negatives`, that are used to compute the recall. This value is
    ultimately returned as `recall`, an idempotent operation that simply divides
    `true_positives` by the sum of `true_positives` and `false_negatives`.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    If `top_k` is set, recall will be computed as how often on average a class
    among the labels of a batch entry is in the top-k predictions.

    If `class_id` is specified, we calculate recall by considering only the
    entries in the batch for which `class_id` is in the label, and computing the
    fraction of them for which `class_id` is above the threshold and/or in the
    top-k predictions.

    Usage with the compile API:
    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.Recall()])
    ```

    # Arguments
        thresholds: (Optional) A float value or a python list/tuple of float
            threshold values in [0, 1]. A threshold is compared with prediction
            values to determine the truth value of predictions (i.e., above the
            threshold is `true`, below is `false`). One metric value is generated
            for each threshold value. If neither thresholds nor top_k are set, the
            default is to calculate recall with `thresholds=0.5`.
        top_k: (Optional) Unset by default. An int value specifying the top-k
            predictions to consider when calculating recall.
        class_id: (Optional) Integer class ID for which we want binary metrics.
            This must be in the half-open interval `[0, num_classes)`, where
            `num_classes` is the last dimension of predictions.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """

    def __init__(self,
                 thresholds=None,
                 top_k=None,
                 class_id=None,
                 name=None,
                 dtype=None):
        super(Recall, self).__init__(name=name, dtype=dtype)
        self.init_thresholds = thresholds
        if top_k is not None and K.backend() != 'tensorflow':
            raise RuntimeError(
                '`top_k` argument for `Recall` metric is currently supported only '
                'with TensorFlow backend.')

        self.top_k = top_k
        self.class_id = class_id

        default_threshold = 0.5 if top_k is None else metrics_utils.NEG_INF
        self.thresholds = metrics_utils.parse_init_thresholds(
            thresholds, default_threshold=default_threshold)
        self.true_positives = self.add_weight(
            'true_positives',
            shape=(len(self.thresholds),),
            initializer='zeros')
        self.false_negatives = self.add_weight(
            'false_negatives',
            shape=(len(self.thresholds),),
            initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        return metrics_utils.update_confusion_matrix_variables(
            {
                metrics_utils.ConfusionMatrix.TRUE_POSITIVES: self.true_positives,
                metrics_utils.ConfusionMatrix.FALSE_NEGATIVES: self.false_negatives
            },
            y_true,
            y_pred,
            thresholds=self.thresholds,
            top_k=self.top_k,
            class_id=self.class_id,
            sample_weight=sample_weight)

    def result(self):
        denom = (self.true_positives + self.false_negatives)
        result = K.switch(
            K.greater(denom, 0),
            self.true_positives / denom,
            K.zeros_like(self.true_positives))
        return result[0] if len(self.thresholds) == 1 else result

    def reset_states(self):
        num_thresholds = len(metrics_utils.to_list(self.thresholds))
        K.batch_set_value(
            [(v, np.zeros((num_thresholds,))) for v in self.weights])

    def get_config(self):
        config = {
            'thresholds': self.init_thresholds,
            'top_k': self.top_k,
            'class_id': self.class_id
        }
        base_config = super(Recall, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

class AUC(Metric):
    """Computes the approximate AUC (Area under the curve) via a Riemann sum.

    This metric creates four local variables, `true_positives`, `true_negatives`,
    `false_positives` and `false_negatives` that are used to compute the AUC.
    To discretize the AUC curve, a linearly spaced set of thresholds is used to
    compute pairs of recall and precision values. The area under the ROC-curve is
    therefore computed using the height of the recall values by the false positive
    rate, while the area under the PR-curve is the computed using the height of
    the precision values by the recall.

    This value is ultimately returned as `auc`, an idempotent operation that
    computes the area under a discretized curve of precision versus recall values
    (computed using the aforementioned variables). The `num_thresholds` variable
    controls the degree of discretization with larger numbers of thresholds more
    closely approximating the true AUC. The quality of the approximation may vary
    dramatically depending on `num_thresholds`. The `thresholds` parameter can be
    used to manually specify thresholds which split the predictions more evenly.

    For best results, `predictions` should be distributed approximately uniformly
    in the range [0, 1] and not peaked around 0 or 1. The quality of the AUC
    approximation may be poor if this is not the case. Setting `summation_method`
    to 'minoring' or 'majoring' can help quantify the error in the approximation
    by providing lower or upper bound estimate of the AUC.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile('sgd', loss='mse', metrics=[keras.metrics.AUC()])
    ```

    # Arguments
        num_thresholds: (Optional) Defaults to 200. The number of thresholds to
            use when discretizing the roc curve. Values must be > 1.
            curve: (Optional) Specifies the name of the curve to be computed, 'ROC'
            [default] or 'PR' for the Precision-Recall-curve.
        summation_method: (Optional) Specifies the Riemann summation method used
            (https://en.wikipedia.org/wiki/Riemann_sum): 'interpolation' [default],
              applies mid-point summation scheme for `ROC`. For PR-AUC, interpolates
              (true/false) positives but not the ratio that is precision (see Davis
              & Goadrich 2006 for details); 'minoring' that applies left summation
              for increasing intervals and right summation for decreasing intervals;
              'majoring' that does the opposite.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
        thresholds: (Optional) A list of floating point values to use as the
            thresholds for discretizing the curve. If set, the `num_thresholds`
            parameter is ignored. Values should be in [0, 1]. Endpoint thresholds
            equal to {-epsilon, 1+epsilon} for a small positive epsilon value will
            be automatically included with these to correctly handle predictions
            equal to exactly 0 or 1.
    """

    def __init__(self,
                 num_thresholds=200,
                 curve='ROC',
                 summation_method='interpolation',
                 name=None,
                 dtype=None,
                 thresholds=None):
        # Validate configurations.
        if (isinstance(curve, metrics_utils.AUCCurve) and
                curve not in list(metrics_utils.AUCCurve)):
            raise ValueError('Invalid curve: "{}". Valid options are: "{}"'.format(
                curve, list(metrics_utils.AUCCurve)))
        if isinstance(
            summation_method,
            metrics_utils.AUCSummationMethod) and summation_method not in list(
                metrics_utils.AUCSummationMethod):
            raise ValueError(
                'Invalid summation method: "{}". Valid options are: "{}"'.format(
                    summation_method, list(metrics_utils.AUCSummationMethod)))

        # Update properties.
        if thresholds is not None:
            # If specified, use the supplied thresholds.
            self.num_thresholds = len(thresholds) + 2
            thresholds = sorted(thresholds)
        else:
            if num_thresholds <= 1:
                raise ValueError('`num_thresholds` must be > 1.')

            # Otherwise, linearly interpolate (num_thresholds - 2) thresholds in
            # (0, 1).
            self.num_thresholds = num_thresholds
            thresholds = [(i + 1) * 1.0 / (num_thresholds - 1)
                          for i in range(num_thresholds - 2)]

        # Add an endpoint "threshold" below zero and above one for either
        # threshold method to account for floating point imprecisions.
        self.thresholds = [0.0 - K.epsilon()] + thresholds + [1.0 + K.epsilon()]

        if isinstance(curve, metrics_utils.AUCCurve):
            self.curve = curve
        else:
            self.curve = metrics_utils.AUCCurve.from_str(curve)
        if isinstance(summation_method, metrics_utils.AUCSummationMethod):
            self.summation_method = summation_method
        else:
            self.summation_method = metrics_utils.AUCSummationMethod.from_str(
                summation_method)
        super(AUC, self).__init__(name=name, dtype=dtype)

        # Create metric variables
        self.true_positives = self.add_weight(
            'true_positives',
            shape=(self.num_thresholds,),
            initializer='zeros')
        self.true_negatives = self.add_weight(
            'true_negatives',
            shape=(self.num_thresholds,),
            initializer='zeros')
        self.false_positives = self.add_weight(
            'false_positives',
            shape=(self.num_thresholds,),
            initializer='zeros')
        self.false_negatives = self.add_weight(
            'false_negatives',
            shape=(self.num_thresholds,),
            initializer='zeros')

    def update_state(self, y_true, y_pred, sample_weight=None):
        return metrics_utils.update_confusion_matrix_variables({
            metrics_utils.ConfusionMatrix.TRUE_POSITIVES: self.true_positives,
            metrics_utils.ConfusionMatrix.TRUE_NEGATIVES: self.true_negatives,
            metrics_utils.ConfusionMatrix.FALSE_POSITIVES: self.false_positives,
            metrics_utils.ConfusionMatrix.FALSE_NEGATIVES: self.false_negatives,
        }, y_true, y_pred, self.thresholds, sample_weight=sample_weight)

    def interpolate_pr_auc(self):
        """Interpolation formula inspired by section 4 of Davis & Goadrich 2006.

        https://www.biostat.wisc.edu/~page/rocpr.pdf

        Note here we derive & use a closed formula not present in the paper
        as follows:

          Precision = TP / (TP + FP) = TP / P

        Modeling all of TP (true positive), FP (false positive) and their sum
        P = TP + FP (predicted positive) as varying linearly within each interval
        [A, B] between successive thresholds, we get

          Precision slope = dTP / dP
                          = (TP_B - TP_A) / (P_B - P_A)
                          = (TP - TP_A) / (P - P_A)
          Precision = (TP_A + slope * (P - P_A)) / P

        The area within the interval is (slope / total_pos_weight) times

          int_A^B{Precision.dP} = int_A^B{(TP_A + slope * (P - P_A)) * dP / P}
          int_A^B{Precision.dP} = int_A^B{slope * dP + intercept * dP / P}

        where intercept = TP_A - slope * P_A = TP_B - slope * P_B, resulting in

          int_A^B{Precision.dP} = TP_B - TP_A + intercept * log(P_B / P_A)

        Bringing back the factor (slope / total_pos_weight) we'd put aside, we get

          slope * [dTP + intercept *  log(P_B / P_A)] / total_pos_weight

        where dTP == TP_B - TP_A.

        Note that when P_A == 0 the above calculation simplifies into

          int_A^B{Precision.dTP} = int_A^B{slope * dTP} = slope * (TP_B - TP_A)

        which is really equivalent to imputing constant precision throughout the
        first bucket having >0 true positives.

        # Returns
            pr_auc: an approximation of the area under the P-R curve.
        """
        dtp = self.true_positives[:self.num_thresholds -
                                  1] - self.true_positives[1:]
        p = self.true_positives + self.false_positives
        dp = p[:self.num_thresholds - 1] - p[1:]

        dp = K.maximum(dp, 0)
        prec_slope = K.switch(
            K.greater(dp, 0),
            dtp / dp,
            K.zeros_like(dtp))
        intercept = self.true_positives[1:] - (prec_slope * p[1:])

        # Logical and
        pMin = K.expand_dims(p[:self.num_thresholds - 1] > 0, 0)
        pMax = K.expand_dims(p[1:] > 0, 0)
        are_different = K.concatenate([pMin, pMax], axis=0)
        switch_condition = K.all(are_different, axis=0)

        safe_p_ratio = K.switch(
            switch_condition,
            K.switch(
                K.greater(p[1:], 0),
                p[:self.num_thresholds - 1] / p[1:],
                K.zeros_like(p[:self.num_thresholds - 1])),
            K.ones_like(p[1:]))

        numer = prec_slope * (dtp + intercept * K.log(safe_p_ratio))
        denom = K.maximum(self.true_positives[1:] + self.false_negatives[1:], 0)
        return K.sum(K.switch(
            K.greater(denom, 0),
            numer / denom,
            K.zeros_like(numer)))

    def result(self):
        if (self.curve == metrics_utils.AUCCurve.PR and
                (self.summation_method ==
                 metrics_utils.AUCSummationMethod.INTERPOLATION)):
            # This use case is different and is handled separately.
            return self.interpolate_pr_auc()

        # Set `x` and `y` values for the curves based on `curve` config.
        recall = K.switch(
            K.greater((self.true_positives), 0),
            (self.true_positives /
                (self.true_positives + self.false_negatives)),
            K.zeros_like(self.true_positives))
        if self.curve == metrics_utils.AUCCurve.ROC:
            fp_rate = K.switch(
                K.greater((self.false_positives), 0),
                (self.false_positives /
                    (self.false_positives + self.true_negatives)),
                K.zeros_like(self.false_positives))
            x = fp_rate
            y = recall
        else:  # curve == 'PR'.
            precision = K.switch(
                K.greater((self.true_positives), 0),
                (self.true_positives / (self.true_positives + self.false_positives)),
                K.zeros_like(self.true_positives))
            x = recall
            y = precision

        # Find the rectangle heights based on `summation_method`.
        if self.summation_method == metrics_utils.AUCSummationMethod.INTERPOLATION:
            # Note: the case ('PR', 'interpolation') has been handled above.
            heights = (y[:self.num_thresholds - 1] + y[1:]) / 2.
        elif self.summation_method == metrics_utils.AUCSummationMethod.MINORING:
            heights = K.minimum(y[:self.num_thresholds - 1], y[1:])
        else:  # self.summation_method = metrics_utils.AUCSummationMethod.MAJORING:
            heights = K.maximum(y[:self.num_thresholds - 1], y[1:])

        # Sum up the areas of all the rectangles.
        return K.sum((x[:self.num_thresholds - 1] - x[1:]) * heights)

    def reset_states(self):
        K.batch_set_value(
            [(v, np.zeros((self.num_thresholds,))) for v in self.weights])

    def get_config(self):
        config = {
            'num_thresholds': self.num_thresholds,
            'curve': self.curve.value,
            'summation_method': self.summation_method.value,
            # We remove the endpoint thresholds as an inverse of how the thresholds
            # were initialized. This ensures that a metric initialized from this
            # config has the same thresholds.
            'thresholds': self.thresholds[1:-1],
        }
        base_config = super(AUC, self).get_config()
        return dict(list(base_config.items()) + list(config.items()))

BaseMeanIoU = object
if K.backend() == 'tensorflow':
    import tensorflow as tf
    if tf.__version__ >= '2.0.0':
        BaseMeanIoU = tf.keras.metrics.MeanIoU

class MeanIoU(BaseMeanIoU):
    """Computes the mean Intersection-Over-Union metric.

    Mean Intersection-Over-Union is a common evaluation metric for semantic image
    segmentation, which first computes the IOU for each semantic class and then
    computes the average over classes. IOU is defined as follows:
    IOU = true_positive / (true_positive + false_positive + false_negative).
    The predictions are accumulated in a confusion matrix, weighted by
    `sample_weight` and the metric is then calculated from it.

    If `sample_weight` is `None`, weights default to 1.
    Use `sample_weight` of 0 to mask values.

    Usage with the compile API:

    ```python
    model = keras.Model(inputs, outputs)
    model.compile(
        'sgd',
        loss='mse',
        metrics=[keras.metrics.MeanIoU(num_classes=2)])
    ```

    # Arguments
        num_classes: The possible number of labels the prediction task can have.
            This value must be provided, since a confusion matrix of dimension =
            [num_classes, num_classes] will be allocated.
        name: (Optional) string name of the metric instance.
        dtype: (Optional) data type of the metric result.
    """
    def __init__(self, num_classes, name=None, dtype=None):
        if K.backend() != 'tensorflow' or BaseMeanIoU is object:
            raise RuntimeError(
                '`MeanIoU` metric is currently supported only '
                'with TensorFlow backend and TF version >= 2.0.0.')
        super(MeanIoU, self).__init__(num_classes, name=name, dtype=dtype)

def accuracy(y_true, y_pred):
    if not K.is_tensor(y_pred):
        y_pred = K.constant(y_pred)
    y_true = K.cast(y_true, y_pred.dtype)
    return K.cast(K.equal(y_true, y_pred), K.floatx())

def binary_accuracy(y_true, y_pred, threshold=0.5):
    if threshold != 0.5:
        threshold = K.cast(threshold, y_pred.dtype)
        y_pred = K.cast(y_pred > threshold, y_pred.dtype)
    return K.mean(K.equal(y_true, K.round(y_pred)), axis=-1)

def categorical_accuracy(y_true, y_pred):
    return K.cast(K.equal(K.argmax(y_true, axis=-1),
                          K.argmax(y_pred, axis=-1)),
                  K.floatx())

def sparse_categorical_accuracy(y_true, y_pred):
    # reshape in case it's in shape (num_samples, 1) instead of (num_samples,)
    if K.ndim(y_true) == K.ndim(y_pred):
        y_true = K.squeeze(y_true, -1)
    # convert dense predictions to labels
    y_pred_labels = K.argmax(y_pred, axis=-1)
    y_pred_labels = K.cast(y_pred_labels, K.floatx())
    return K.cast(K.equal(y_true, y_pred_labels), K.floatx())

def top_k_categorical_accuracy(y_true, y_pred, k=5):
    return K.cast(K.in_top_k(y_pred, K.argmax(y_true, axis=-1), k), K.floatx())

def sparse_top_k_categorical_accuracy(y_true, y_pred, k=5):
    # If the shape of y_true is (num_samples, 1), flatten to (num_samples,)
    return K.cast(K.in_top_k(y_pred, K.cast(K.flatten(y_true), 'int32'), k),
                  K.floatx())

def cosine_proximity(y_true, y_pred, axis=-1):
    y_true = K.l2_normalize(y_true, axis=axis)
    y_pred = K.l2_normalize(y_pred, axis=axis)
    return K.sum(y_true * y_pred, axis=axis)

def clone_metric(metric):
    """Returns a clone of the metric if stateful, otherwise returns it as is."""
    if isinstance(metric, Metric):
        return metric.__class__.from_config(metric.get_config())
    return metric

def clone_metrics(metrics):
    """Clones the given metric list/dict."""
    if metrics is None:
        return None
    if isinstance(metrics, dict):
        return {key: clone_metric(value) for key, value in metrics.items()}
    return [clone_metric(metric) for metric in metrics]

# Aliases

mse = MSE = mean_squared_error
mae = MAE = mean_absolute_error
mape = MAPE = mean_absolute_percentage_error
msle = MSLE = mean_squared_logarithmic_error
cosine = cosine_similarity = cosine_proximity

def serialize(metric):
    return serialize_keras_object(metric)

def deserialize(config, custom_objects=None):
    return deserialize_keras_object(config,
                                    module_objects=globals(),
                                    custom_objects=custom_objects,
                                    printable_module_name='metric function')

def get(identifier):
    if isinstance(identifier, dict):
        config = {'class_name': str(identifier), 'config': {}}
        return deserialize(config)
    elif isinstance(identifier, six.string_types):
        return deserialize(str(identifier))
    elif callable(identifier):
        return identifier
    else:
        raise ValueError('Could not interpret '
                         'metric function identifier:', identifier)


from __future__ import division

import math
import numpy as np
import rospy
import tf.transformations
import threading
import uuid

from iarc7_msgs.msg import Obstacle, ObstacleArray
from nav_msgs.msg import Odometry

from iarc7_sensors.roomba_filter.kalman_filter_2d import KalmanFilter2d

class SingleObstacleFilter(object):
    def __init__(self, world_fixed_frame):
        self._lock = threading.Lock()
        with self._lock:
            self._id = uuid.uuid4()
            self._world_fixed_frame = world_fixed_frame
            self._frame = 'obstacle-{}/base_link'.format(self._id)
            self._kf = KalmanFilter2d()

            self._curr_base_radius = 0.0
            self._curr_base_height = 0.0
            self._curr_pipe_radius = 0.0
            self._curr_pipe_height = 0.0

    def _predict(self, time):
        self._kf.predict(time)

    def predict(self, time):
        with self._lock:
            self._predict(time)

    def update(self, time, obstacle_detection_msg):
        with self._lock:
            if self._kf.initialized():
                self._predict(time)

            state = np.array([obstacle_detection_msg.odom.pose.pose.position.x,
                              obstacle_detection_msg.odom.pose.pose.position.y],
                              dtype=float)

            covariance_msg = obstacle_detection_msg.odom.pose.covariance
            covariance = np.array([
                [covariance_msg[0], covariance_msg[1]],
                [covariance_msg[6], covariance_msg[7]]], dtype=float)

            self._kf.update(time, state, covariance)

            self._curr_base_radius = max(self._curr_base_radius,
                                         obstacle_detection_msg.base_radius)
            self._curr_base_height = max(self._curr_base_height,
                                         obstacle_detection_msg.base_height)
            self._curr_pipe_radius = max(self._curr_pipe_radius,
                                         obstacle_detection_msg.pipe_radius)
            self._curr_pipe_height = max(self._curr_pipe_height,
                                         obstacle_detection_msg.pipe_height)

    def get_state(self, time):
        with self._lock:
            self._predict(time)

            _, state, cov = self._kf.get_state()
            pos = state[:2,0]
            pos_cov = cov[:2,:2]
            vel = state[2:,0]
            theta = math.atan2(vel[1], vel[0])
            z_rate = 0

            obstacle = Obstacle()
            obstacle.header.stamp = time
            obstacle.header.frame_id = self._world_fixed_frame

            obstacle.base_height = self._curr_base_height
            obstacle.base_radius = self._curr_base_radius
            obstacle.pipe_height = self._curr_pipe_height
            obstacle.pipe_radius = self._curr_pipe_radius

            odom = Odometry()
            odom.header.stamp = time
            odom.header.frame_id = self._world_fixed_frame
            odom.child_frame_id = self._frame
            odom.pose.pose.position.x = pos[0]
            odom.pose.pose.position.y = pos[1]
            odom.pose.covariance[0] = pos_cov[0,0]
            odom.pose.covariance[1] = pos_cov[0,1]
            odom.pose.covariance[6] = pos_cov[0,1]
            odom.pose.covariance[7] = pos_cov[1,1]

            quat = tf.transformations.quaternion_from_euler(0, 0, theta)
            odom.pose.pose.orientation.x = quat[0]
            odom.pose.pose.orientation.y = quat[1]
            odom.pose.pose.orientation.z = quat[2]
            odom.pose.pose.orientation.w = quat[3]

            odom.twist.twist.linear.x = vel[0]
            odom.twist.twist.linear.y = vel[1]
            odom.twist.twist.angular.z = z_rate

            obstacle.odom = odom

            return obstacle


from keras.datasets import mnist
import matplotlib.pyplot as plot
import numpy as npy
from sklearn.neighbors import KNeighborsClassifier
from sklearn.metrics import accuracy_score
import math

# storing the mnist in two train arrays and two test arrays
# the x arrays carry the images and the y arrays carry the values of the images
(x_train, y_train), (x_test, y_test) = mnist.load_data()

"""
- This function splits the image into grids.
- It takes the image and the grids number as parameters
  and check if the grids number could cause a problem.
  if it causes a problem of splitting uneven grids, it handles it.
- Then, it stores the splitted grids in grids list and return that list.
"""

def split(image, grids_number):
    # Calculating the dimension of the grid
    pixels_number = math.ceil(28 / grids_number)
    image_cpy = image

    # To handle the uneven grids problem
    if (28 % grids_number != 0):

        new_size = pixels_number * grids_number
        image_cpy = npy.zeros([new_size, new_size])

        for k in range(0, len(image)):
            for o in range(0, len(image[0])):
                image_cpy[k][o] = image[k][o]

    # The grids list and its counter to store the grids in it
    grids_index = 0
    grids = [0 for i in range(0, grids_number * grids_number)]

    # Splitting and storing the grids into the grids list
    for i in range(0, grids_number):
        for j in range(0, grids_number):
            grids[grids_index] = image_cpy[i * pixels_number:(i + 1) * pixels_number,
                                 j * pixels_number:(j + 1) * pixels_number]
            grids_index += 1

    return grids

"""
- This function calculates the centroid of an image grids.
- It takes an image grids list and the grids_number as a parameter.
- Then, it calculates the centroid of each grid and put the values (Xc and Yc)
  of each grid in the feature vector list and return that vector.
"""

def centroid(grids, grids_number):
    # Calculating the dimension of the grid and creating the feature vector
    pixels_number = math.ceil(28 / grids_number)
    feature_vector = [0 for i in range(0, grids_number * grids_number * 2)]
    Xc = 0
    Yc = 0

    # Calculating the centroid of each grid
    for i in range(0, grids_number * grids_number):

        pixels_sum = 0
        for j in range(0, pixels_number):
            for k in range(0, pixels_number):
                Xc += grids[i][j][k] * (j + 1)
                Yc += grids[i][j][k] * (k + 1)
                pixels_sum = pixels_sum + grids[i][j][k]

        if (pixels_sum != 0):
            feature_vector[2 * i] = Xc / pixels_sum
            feature_vector[2 * i + 1] = Yc / pixels_sum

        else:
            feature_vector[2 * i] = 0
            feature_vector[2 * i + 1] = 0

    return feature_vector

"""
- This function predict the digit value by using the nearst neighbour algorithm
- It takes the grids number as a paramter.
- It splits the images existed in the train and the test datasets into grids
  and gets the centroid value of each grid and stores the values in two lists
  a list for the train dataset images and another for the test ones.
- Then, it uses the built-in classifier to fit and predict the two sets.
- Finally, it calculates the accuracy of the predection and return its value.
"""

def nearest_neighbour(grids_number):
    # Splitting the image into grids and calculating and storing the centroid
    # for the train images
    fv_train = npy.zeros([len(x_train), grids_number * grids_number * 2])
    for i in range(len(x_train)):
        grids = split(x_train[i], grids_number)
        fv_train[i] = centroid(grids, grids_number)

    # Splitting the image into grids and calculating and storing the centroid
    # for the test images
    fv_test = npy.zeros([len(x_test), grids_number * grids_number * 2])
    for i in range(len(x_test)):
        grids = split(x_test[i], grids_number)
        fv_test[i] = centroid(grids, grids_number)

    # Using the calssifier to fit the train dataset images
    model = KNeighborsClassifier(n_neighbors=7)
    model.fit(fv_train, y_train)

    # Predicting the value of the test dataset images
    prediction = model.predict(fv_test)

    # Calculating the accuracy of the prediction operation
    accuracy = accuracy_score(y_test, prediction)

    return accuracy

accuracy = nearest_neighbour(4)
print("Accuracy: ", accuracy)


# -*- coding: utf-8 -*-
from __future__ import print_function, unicode_literals

import django.contrib.postgres.fields
from django.conf import settings
from django.db import migrations, models

def migrate_messageactions(apps, schema_editor):
    MessageActionOld = apps.get_model("cases", "MessageAction")
    MessageAction = apps.get_model("msgs", "MessageAction")

    old_actions = list(MessageActionOld.objects.all())

    for old_action in old_actions:
        MessageAction.objects.create(
            org=old_action.org,
            messages=old_action.messages,
            action=old_action.action,
            created_by=old_action.created_by,
            created_on=old_action.created_on,
            label=old_action.label,
        )

    if old_actions:
        print("Migrated %d message actions to new model in msgs app" % len(old_actions))

class Migration(migrations.Migration):

    dependencies = [
        ("cases", "0020_delete_messageexport"),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
        ("orgs", "0015_auto_20160209_0926"),
        ("msgs", "0007_unhandled_index"),
    ]

    operations = [
        migrations.CreateModel(
            name="MessageAction",
            fields=[
                ("id", models.AutoField(verbose_name="ID", serialize=False, auto_created=True, primary_key=True)),
                ("messages", django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)),
                (
                    "action",
                    models.CharField(
                        max_length=1,
                        choices=[
                            ("F", "Flag"),
                            ("N", "Un-flag"),
                            ("L", "Label"),
                            ("U", "Remove Label"),
                            ("A", "Archive"),
                            ("R", "Restore"),
                        ],
                    ),
                ),
                ("created_on", models.DateTimeField(auto_now_add=True)),
                (
                    "created_by",
                    models.ForeignKey(
                        related_name="message_actions", to=settings.AUTH_USER_MODEL, on_delete=models.PROTECT
                    ),
                ),
                ("label", models.ForeignKey(to="cases.Label", null=True, on_delete=models.PROTECT)),
                (
                    "org",
                    models.ForeignKey(
                        related_name="message_actions",
                        verbose_name="Organization",
                        to="orgs.Org",
                        on_delete=models.PROTECT,
                    ),
                ),
            ],
        ),
        migrations.RunPython(migrate_messageactions),
    ]


import json

from django.conf import settings
from django.core.mail import mail_admins
from restless.views import Endpoint
from restless.modelviews import ListEndpoint, DetailEndpoint
from restless.models import serialize
from restless.http import HttpError, Http200, Http201

from modeldiff.models import Geomodeldiff

class GeomodeldiffList(ListEndpoint):
    model = Geomodeldiff

    def get_query_set(self, request, *args, **kwargs):
        last_id = request.GET.get('last_id', 0)
        try:
            last_id = int(last_id)
        except Exception:
            last_id = 0
        limit = request.GET.get('limit', 0)
        try:
            limit = int(limit)
        except Exception:
            limit = 0

        if self.model:
            queryset = (
                self.model.objects.all()
                .filter(key=settings.MODELDIFF_KEY)
                .filter(pk__gt=last_id)
                .order_by('id')
            )
            if limit > 0:
                queryset = queryset[:limit]
            return queryset
        else:
            raise HttpError(404, 'Resource Not Found')

    def serialize(self, objs):
        return serialize(objs, exclude=('applied',))

    def post(self, request, *args, **kwargs):
        data = json.loads(request.body.decode('utf-8'))
        qs = Geomodeldiff.objects.filter(key=data['key'],
                                         key_id=data['key_id'])
        if qs.count() == 0:
            obj = Geomodeldiff(**data)
            try:
                obj.save()
                return Http201(self.serialize(obj))
            except:
                raise HttpError(400, 'Invalid Data')
        else:
            print('Already exists!')
            print('key=%s, key_id=%s' % (data['key'], data['key_id']))
            return Http200('Already exists!')

class Update(Endpoint):
    def get(self, request):
        from .update import apply_modeldiffs
        result = apply_modeldiffs()

        if len(result['rows_skipped']) > 0:
            mail_admins('Some modeldiffs failed to apply!', 'ERROR!')

        return serialize(result['qs'], exclude=('the_geom'))


import os

import numpy as np
import pandas as pd
import torch
from PIL import Image
from skimage import io
from sklearn.preprocessing import MultiLabelBinarizer
from torch.utils.data import Dataset, DataLoader

from utils import SequentialDistributedSampler

# from utils.gcloud import download_chestxray_unzip

Labels = {
    "No Finding": 14,
    "Atelectasis": 0,
    "Cardiomegaly": 1,
    "Effusion": 2,
    "Infiltration": 3,
    "Mass": 4,
    "Nodule": 5,
    "Pneumonia": 6,
    "Pneumothorax": 7,
    "Consolidation": 8,
    "Edema": 9,
    "Emphysema": 10,
    "Fibrosis": 11,
    "Pleural_Thickening": 12,
    "Hernia": 13,
}
mlb = MultiLabelBinarizer(classes=[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

class ChestDataset(Dataset):
    def __init__(self, root_dir, transform, mode, runtime=1, ratio=100) -> None:
        self.transform = transform
        self.root_dir = root_dir
        self.mode = mode

        gr_path = os.path.join(root_dir, "Data_Entry_2017.csv")
        gr = pd.read_csv(gr_path, index_col=0)
        gr = gr.to_dict()["Finding Labels"]

        img_list = os.path.join(
            root_dir,
            "test_list.txt"
            if mode == "test"
            else "train_val_list_{}_{}.txt".format(ratio, runtime),
        )
        with open(img_list) as f:
            names = f.read().splitlines()
        self.labeled_imgs = np.asarray([x for x in names])

        all_img_list = os.path.join(root_dir, "train_val_list.txt")
        with open(all_img_list) as f:
            all_names = f.read().splitlines()

        labeled_gr = np.asarray([gr[i] for i in self.labeled_imgs])

        self.labeled_gr = np.zeros((labeled_gr.shape[0], 15))
        for idx, i in enumerate(labeled_gr):
            target = i.split("|")
            binary_result = mlb.fit_transform([[Labels[i] for i in target]]).squeeze()
            self.labeled_gr[idx] = binary_result
        self.all_imgs = np.asarray([x for x in all_names])
        self.unlabeled_imgs = np.setdiff1d(self.all_imgs, self.labeled_imgs)
        unlabeled_gr = np.asarray([gr[i] for i in self.unlabeled_imgs])
        self.unlabeled_gr = np.zeros((unlabeled_gr.shape[0], 15))
        for idx, i in enumerate(unlabeled_gr):
            target = i.split("|")
            binary_result = mlb.fit_transform([[Labels[i] for i in target]]).squeeze()
            self.unlabeled_gr[idx] = binary_result

    def x_add_pl(self, pl, idxs):
        self.labeled_imgs = np.concatenate(
            (self.labeled_imgs, self.unlabeled_imgs[idxs])
        )
        self.labeled_gr = np.concatenate((self.labeled_gr, pl))

    def x_update_pl(self, idxs, args):
        print(self.labeled_imgs.shape)
        self.labeled_imgs = self.labeled_imgs[idxs]
        self.labeled_gr = self.labeled_gr[idxs]
        # mask = self.labeled_gr > (
        #     np.amax(self.labeled_gr, axis=1) - args.max_interval
        # ).reshape(-1, 1)
        # self.labeled_gr[mask] = 1
        # self.labeled_gr[~mask] = 0
        print(self.labeled_imgs.shape)

    def u_update_pl(self, idxs):
        self.unlabeled_imgs = np.delete(self.unlabeled_imgs, idxs)
        self.unlabeled_gr = np.delete(self.unlabeled_gr, idxs, axis=0)

    def __getitem__(self, item):

        if self.mode == "labeled" or self.mode == "anchor":
            img_path = os.path.join(self.root_dir, "data", self.labeled_imgs[item])
            input_path = float(self.labeled_imgs[item].split(".")[0].replace("_", "."))
            target = self.labeled_gr[item]
        elif self.mode == "unlabeled":
            img_path = os.path.join(self.root_dir, "data", self.unlabeled_imgs[item])
            input_path = float(
                self.unlabeled_imgs[item].split(".")[0].replace("_", ".")
            )
            target = self.unlabeled_gr[item]
        elif self.mode == "test":
            img_path = os.path.join(self.root_dir, "data", self.labeled_imgs[item])
            input_path = float(self.labeled_imgs[item].split(".")[0].replace("_", "."))
            target = self.labeled_gr[item]

        img = Image.fromarray(io.imread(img_path)).convert("RGB")
        img_w = self.transform(img)
        return (img_w, target, item, input_path)

    def __len__(self):
        if self.mode == "labeled" or self.mode == "test" or self.mode == "anchor":
            return self.labeled_imgs.shape[0]
        elif self.mode == "unlabeled":
            return self.unlabeled_imgs.shape[0]

class ChestDataloader:
    def __init__(
        self,
        batch_size=128,
        num_workers=8,
        img_resize=512,
        root_dir=None,
    ):
        self.batch_size = batch_size
        self.num_workers = num_workers
        self.img_resize = img_resize

        self.root_dir = root_dir

    def run(self, mode, dataset=None, transform=None, exp=None, ratio=100, runtime=1):
        use_transform = transform

        if dataset:
            all_dataset = dataset
        else:
            all_dataset = ChestDataset(
                root_dir=self.root_dir,
                transform=use_transform,
                mode=mode,
                ratio=ratio,
                runtime=runtime,
            )
        batch_size = (
            (self.batch_size * 2)
            if mode == "test"
            else (self.batch_size * 3)
            if mode == "unlabeled" or mode == "anchor"
            else self.batch_size
        )
        sampler = (
            torch.utils.data.distributed.DistributedSampler(all_dataset)
            if mode == "labeled"
            else SequentialDistributedSampler(all_dataset)
        )
        loader = DataLoader(
            dataset=all_dataset,
            batch_size=batch_size,
            shuffle=False,
            sampler=sampler,
            num_workers=self.num_workers,
            pin_memory=True,
            drop_last=True if mode == "labeled" else False,
        )

        return loader, all_dataset, sampler

    # def update(self,pl):


import logging

LOGLEVEL = "info"
LOGFILE = ""

def configure_logger(name: str) -> logging.Logger:
    logger = logging.getLogger(name)
    if logger.hasHandlers():
        # already configured
        return logger

    levels = {
        "debug": logging.DEBUG,
        "info": logging.INFO,
        "warn": logging.WARN,
        "critical": logging.CRITICAL,
    }
    logger.setLevel(levels[LOGLEVEL])

    if LOGFILE:
        handler = logging.FileHandler(LOGFILE)
    else:
        handler = logging.StreamHandler()

    handler.setLevel(levels[LOGLEVEL])

    formatter = logging.Formatter(
        "%(asctime)s - %(name)s (%(threadName)-9s) - %(levelname)s - %(message)s"
    )
    handler.setFormatter(formatter)

    logger.addHandler(handler)
    return logger


import sys,os
import time
import numpy as np
import argparse
import warnings
from torch import nn, optim
from torch.autograd import Variable
from scipy.spatial.distance import pdist, squareform
from utils import readpickle, savepickle
import torch.nn.functional as F
import torch
from transformers import AutoTokenizer, AutoModel, pipeline
from transformers import logging
logging.set_verbosity_error()
import re
from tqdm import tqdm
import json

class2idx = readpickle('../save/class2idx.pkl')
fold2idx = readpickle('../save/fold2idx.pkl')
lab2idx = readpickle('../save/lab2idx.pkl')
idx2lab = {value: key for key, value in lab2idx.items()}
sid2label = readpickle('../save/sid2label.pkl')

tokenizer = AutoTokenizer.from_pretrained("Rostlab/prot_bert", do_lower_case=False)
model = AutoModel.from_pretrained("Rostlab/prot_bert")
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
fe = pipeline('feature-extraction', model=model, tokenizer=tokenizer, device=device)

def sequencechange(sequence):
    new_seq = ""
    count = 0
    for i in sequence:
        if i == ' ':
            continue
        new_seq += i
        count += 1
        if count == len(sequence):
            continue
        new_seq += ' '
    return new_seq

test_json_list = ['../dataset/json/d3r1pa_.json', '../dataset/json/d6jl3a_.json', '../dataset/json/d7ndyg_.json']
print('processing test set')
data_test = []
for i in tqdm(range(len(test_json_list))):
    fn = test_json_list[i]
    sid = os.path.splitext(os.path.basename(fn))[0]

    with open(fn, 'r') as f: scop = json.load(f)
    model = scop['model']
    sequence = ""
    for idx, res in model.items():
        AA = res["res"]
        sequence += AA

    seq_len = len(sequence)
    sequence = sequencechange(sequence)
    sequences_Example = [sequence]
    sequences_Example = [re.sub(r"[UZOB]", "X", sequence) for sequence in sequences_Example]
    embedding = fe(sequences_Example)
    embedding = np.array(embedding)
    embedding = embedding[0,0,:,:]
    start_Idx = 1
    end_Idx = seq_len+1
    embedding = embedding[start_Idx:end_Idx]

    size = len(model)
    coord = np.ones((size, 3), dtype=np.float32) * np.inf
    for i in model.keys():
        ii = int(i) - 1
        coord[ii, 0] = model[i]['x']
        coord[ii, 1] = model[i]['y']
        coord[ii, 2] = model[i]['z']
    dist = np.nan_to_num(squareform(pdist(np.array(coord, dtype=np.float32))), nan=np.inf)
    dist = {"coord": coord, "dist": dist}
    savepickle(dist, '../dataset/dist/{}_dist.pkl'.format(sid))

    pclass = sid2label[sid]['class']
    pclass = class2idx[pclass]
    fold = sid2label[sid]['fold']
    fold = fold2idx[fold]
    label = sid2label[sid]['superfamily']
    label = lab2idx[label]
    savepickle(embedding, '../dataset/embedding/{}.pkl'.format(sid))
    data_test.append(dict(sid = sid, length = seq_len, label = [label], pclass = [pclass], fold = [fold]))

savepickle(data_test, '../save/data_test.pkl')


# -*- coding: utf-8 -*-
"""
Created on Sun Nov 27 16:55:21 2022

@author: jungu
"""

import matplotlib.pyplot as plt
import numpy as np

N = 10000
size = 20 # font size
a = 2
X = 5
x = np.linspace(-X, X, N)
U = abs(x*x/a/a - 1)**1.5 * 3
Ee = 2
Eg = 0.25

fig, ax = plt.subplots(figsize = (3.5,3))
ax.plot(x, U, label=r'U(X)', linewidth=4)
# ax.annotate('', xy=(-a,-A/5), xytext=(a,-A/5),
#             arrowprops=dict(arrowstyle='<->'))
# ax.annotate('a', xy=(0,-A*.5), fontsize=size)

# sigma = a/2.5
# x1 = np.linspace(-1.25*X, 1.25*X, N)
# phiL = A/2*np.exp(-(x1+a)**2/sigma**2)
# phiR = A/2*np.exp(-(x1-a)**2/sigma**2)
# ax.plot(x1,phiL, label=r'$\phi_L(X)$')
# ax.plot(x1,phiR, label=r'$\phi_R(X)$')

sigma = a*0.8
x1 = np.linspace(-1.25*X, 1.25*X, N)
phiL = np.exp(-(x1+a)**2/sigma**2)
phiR = np.exp(-(x1-a)**2/sigma**2)
phig = (phiL + phiR)/np.sqrt(2) + Eg
phie = (phiL - phiR)/np.sqrt(2) + Ee
ax.plot(x1,phie, label=r'$\phi_e(X)$')
ax.plot(x1,phig, label=r'$\phi_g(X)$')
ax.annotate('', xy=(0,Eg), xytext=(0,Ee),
            arrowprops=dict(arrowstyle='<->'))
ax.annotate(r'$J$', xy=(0.1,(Ee+Eg)/2), fontsize=size)

ax.annotate('', xy=(-a,Ee/5), xytext=(a,Ee/5),
            arrowprops=dict(arrowstyle='<->'))
ax.annotate(r'$a$', xy=(0,0), fontsize=size)

ax.set_xlabel('X')
ax.grid(True)
ax.legend()
ax.set_ylim([0,5])
ax.set_xticklabels([])
ax.set_yticklabels([])
fig.set_tight_layout(True)