# Source code for secml.ml.kernels.c_kernel_euclidean

```"""
.. module:: CKernelEuclidean
:synopsis: Euclidean distance kernel.

.. moduleauthor:: Marco Melis <marco.melis@unica.it>
.. moduleauthor:: Battista Biggio <battista.biggio@unica.it>
.. moduleauthor:: Angelo Sotgiu <angelo.sotgiu@unica.it>

"""
from sklearn import metrics

from secml.array import CArray
from secml.ml.kernels import CKernel

[docs]class CKernelEuclidean(CKernel):
"""Euclidean distance kernel.

Given matrices X and RV, this is computed as the negative Euclidean dist.::

K(x, rv) = -sqrt(dot(x, x) - 2 * dot(x, rv) + dot(rv, rv))

for each pair of rows in X and in RV.
If parameter squared is True (default False), sqrt() operation is avoided.

Parameters
----------
squared : bool, optional
If True, return squared Euclidean distances. Default False.

Attributes
----------
class_type : 'euclidean'

Examples
--------
>>> from secml.array import CArray
>>> from secml.ml.kernels.c_kernel_euclidean import CKernelEuclidean

>>> print(CKernelEuclidean().k(CArray([[1,2],[3,4]]), CArray([[10,20],[30,40]])))
CArray([[-20.124612 -47.801674]
[-17.464249 -45.      ]])

>>> print(CKernelEuclidean().k(CArray([[1,2],[3,4]])))
CArray([[0.       -2.828427]
[-2.828427 0.      ]])

"""
__class_type = 'euclidean'

def __init__(self, squared=False):
self._squared = squared
self._x_norm_squared = None
self._rv_norm_squared = None
super(CKernelEuclidean, self).__init__()

@property
def squared(self):
"""If True, squared Euclidean distances are computed."""
return self._squared

@squared.setter
def squared(self, value):
"""Sets the squared parameter.

Parameters
----------
value : bool
If True, squared Euclidean distances are computed.

"""
self._squared = value

@property
def x_norm_squared(self):
"""Pre-computed dot-products of vectors in x
(e.g., (x**2).sum(axis=1)).

"""
return self._x_norm_squared

@x_norm_squared.setter
def x_norm_squared(self, value):
"""Sets the pre-computed dot-products of vectors in x.

Parameters
----------
value : CArray
Pre-computed dot-products of vectors in x.

"""
self._x_norm_squared = value

@property
def rv_norm_squared(self):
"""Pre-computed dot-products of vectors in rv
(e.g., (rv**2).sum(axis=1)).

"""
return self._rv_norm_squared

@rv_norm_squared.setter
def rv_norm_squared(self, value):
"""Sets the pre-computed dot-products of vectors in rv.

Parameters
----------
value : CArray
Pre-computed dot-products of vectors in rv.

"""
self._rv_norm_squared = value

def _forward(self, x):
"""Compute this kernel as the negative Euclidean dist. between x and
cached rv.

Parameters
----------
x : CArray
Array of shape (n_x, n_features).

Returns
-------
kernel : CArray
Kernel between x and cached rv, shape (n_x, n_rv).

"""
k = -CArray(metrics.pairwise.euclidean_distances(
x.get_data(), self._rv.get_data(), squared=self._squared,
X_norm_squared=self._x_norm_squared,
Y_norm_squared=self._rv_norm_squared))
self._cached_kernel = None if self._cached_x is None or self._squared \
else k
return k

def _backward(self, w=None):
"""Compute the kernel gradient wrt cached vector 'x'.

The gradient of Euclidean distance kernel is given by::

dK(rv,x)/dx = - (rv - x) / k(rv,x)    if squared = False (default)
dK(rv,x)/dx = 2 * (rv - x)        if squared = True

Parameters
----------
w : CArray of shape (1, n_rv) or None
if CArray, it is pre-multiplied to the gradient
of the module, as in standard reverse-mode autodiff.

Returns
-------
Kernel gradient of rv with respect to vector x,
shape (n_rv, n_features) if n_rv > 1 and w is None,
else (1, n_features).

"""
# Checking if cached x is a vector
if not self._cached_x.is_vector_like:
raise ValueError(
"kernel gradient can be computed only wrt vector-like arrays.")

if self._rv is None or (not self._squared
and self._cached_kernel is None):
raise ValueError("Please run forward with caching=True first.")

# Format of output array should be the same as cached x
self._rv = self._rv.tosparse() if self._cached_x.issparse \
else self._rv.todense()

if self._squared is True:  # 2 * (rv - x)
diff = (self._rv - self._cached_x)
return 2 * diff if w is None else w.dot(2 * diff)

diff = (self._rv - self._cached_x)

# Casting the kernel to sparse if needed for efficient broadcasting
if diff.issparse is True:

# - (rv - x) / k(rv,x)

# Casting to sparse if necessary