# Path: pygp/kernels/_real.py
# class RealKernel(Kernel):
#     """Kernel whose inputs are real-valued vectors."""
# 
#     def __add__(self, other):
#         return SumKernel(*combine(SumKernel, self, other))
# 
#     def __mul__(self, other):
#         return ProductKernel(*combine(ProductKernel, self, other))
# 
#     def transform(self, X):
#         return np.array(X, ndmin=2, dtype=float, copy=False)
# 
#     @abstractmethod
#     def gradx(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to its first argument. Returns
#         an (m,n,d)-array.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def grady(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to its second argument. Returns
#         an (m,n,d)-array.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def gradxy(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to both its first and second
#         arguments. Returns an (m,n,d,d)-array. The (a,b,i,j)th element
#         corresponds to the derivative with respect to `X1[a,i]` and `X2[b,j]`.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def sample_spectrum(self, N, rng=None):
#         """
#         Sample N values from the spectral density of the kernel, returning a
#         set of weights W of size (n,d) and a scalar value representing the
#         normalizing constant.
#         """
#         raise NotImplementedError
# 
# Path: pygp/kernels/_distances.py
# def rescale(ell, X1, X2):
#     """
#     Rescale the two sets of vectors by `ell`.
#     """
#     X1 = X1 / ell
#     X2 = X2 / ell if (X2 is not None) else None
#     return X1, X2
# 
# def diff(X1, X2=None):
#     """
#     Return the differences between vectors in `X1` and `X2`. If `X2` is not
#     given this will return the pairwise differences in `X1`.
#     """
#     X2 = X1 if (X2 is None) else X2
#     return X1[:, None, :] - X2[None, :, :]
# 
# def sqdist(X1, X2=None):
#     """
#     Return the squared-distance between two sets of vector. If `X2` is not
#     given this will return the pairwise squared-distances in `X1`.
#     """
#     X2 = X1 if (X2 is None) else X2
#     return ssd.cdist(X1, X2, 'sqeuclidean')
# 
# def sqdist_foreach(X1, X2=None):
#     """
#     Return an iterator over each dimension returning the squared-distance
#     between two sets of vector. If `X2` is not given this will iterate over the
#     pairwise squared-distances in `X1` in each dimension.
#     """
#     X2 = X1 if (X2 is None) else X2
#     for i in xrange(X1.shape[1]):
#         yield ssd.cdist(X1[:, i, None], X2[:, i, None], 'sqeuclidean')
# 
# Path: pygp/utils/models.py
# def printable(cls):
#     """
#     Decorator which marks classes as being able to be pretty-printed as a
#     function of their hyperparameters. This decorator defines a __repr__ method
#     for the given class which uses the class's `get_hyper` and `_params`
#     methods to print it.
#     """
#     def _repr(obj):
#         """Represent the object as a function of its hyperparameters."""
#         hyper = obj.get_hyper()
#         substrings = []
#         for key, block, log in get_params(obj):
#             val = hyper[block]
#             val = val[0] if (len(val) == 1) else val
#             val = np.exp(val) if log else val
#             substrings += ['%s=%s' % (key, val)]
#         return obj.__class__.__name__ + '(' + ', '.join(substrings) + ')'
#     cls.__repr__ = _repr
#     return cls


import numpy as np
from mwhutils.random import rstate
from ._real import RealKernel
from ._distances import rescale, diff, sqdist, sqdist_foreach
from ..utils.models import printable
            1+r if (self._d == 3) else
            1+r*(1+r/3.))

    def _df(self, r):
        return (
            1 if (self._d == 1) else
            r if (self._d == 3) else
            r*(1+r)/3.)

    def _params(self):
        return [
            ('sf', 1, True),
            ('ell', self.nhyper-1, True),
        ]

    def get_hyper(self):
        return np.r_[self._logsf, self._logell]

    def set_hyper(self, hyper):
        self._logsf = hyper[0]
        self._logell = hyper[1] if self._iso else hyper[1:]

    def get(self, X1, X2=None):
        X1, X2 = rescale(np.exp(self._logell)/np.sqrt(self._d), X1, X2)
        D = np.sqrt(sqdist(X1, X2))
        S = np.exp(self._logsf*2 - D)
        K = S * self._f(D)
        return K

    def grad(self, X1, X2=None):

# Path: pygp/kernels/_real.py
# class RealKernel(Kernel):
#     """Kernel whose inputs are real-valued vectors."""
# 
#     def __add__(self, other):
#         return SumKernel(*combine(SumKernel, self, other))
# 
#     def __mul__(self, other):
#         return ProductKernel(*combine(ProductKernel, self, other))
# 
#     def transform(self, X):
#         return np.array(X, ndmin=2, dtype=float, copy=False)
# 
#     @abstractmethod
#     def gradx(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to its first argument. Returns
#         an (m,n,d)-array.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def grady(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to its second argument. Returns
#         an (m,n,d)-array.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def gradxy(self, X1, X2=None):
#         """
#         Derivatives of the kernel with respect to both its first and second
#         arguments. Returns an (m,n,d,d)-array. The (a,b,i,j)th element
#         corresponds to the derivative with respect to `X1[a,i]` and `X2[b,j]`.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def sample_spectrum(self, N, rng=None):
#         """
#         Sample N values from the spectral density of the kernel, returning a
#         set of weights W of size (n,d) and a scalar value representing the
#         normalizing constant.
#         """
#         raise NotImplementedError
# 
# Path: pygp/kernels/_distances.py
# def rescale(ell, X1, X2):
#     """
#     Rescale the two sets of vectors by `ell`.
#     """
#     X1 = X1 / ell
#     X2 = X2 / ell if (X2 is not None) else None
#     return X1, X2
# 
# def diff(X1, X2=None):
#     """
#     Return the differences between vectors in `X1` and `X2`. If `X2` is not
#     given this will return the pairwise differences in `X1`.
#     """
#     X2 = X1 if (X2 is None) else X2
#     return X1[:, None, :] - X2[None, :, :]
# 
# def sqdist(X1, X2=None):
#     """
#     Return the squared-distance between two sets of vector. If `X2` is not
#     given this will return the pairwise squared-distances in `X1`.
#     """
#     X2 = X1 if (X2 is None) else X2
#     return ssd.cdist(X1, X2, 'sqeuclidean')
# 
# def sqdist_foreach(X1, X2=None):
#     """
#     Return an iterator over each dimension returning the squared-distance
#     between two sets of vector. If `X2` is not given this will iterate over the
#     pairwise squared-distances in `X1` in each dimension.
#     """
#     X2 = X1 if (X2 is None) else X2
#     for i in xrange(X1.shape[1]):
#         yield ssd.cdist(X1[:, i, None], X2[:, i, None], 'sqeuclidean')
# 
# Path: pygp/utils/models.py
# def printable(cls):
#     """
#     Decorator which marks classes as being able to be pretty-printed as a
#     function of their hyperparameters. This decorator defines a __repr__ method
#     for the given class which uses the class's `get_hyper` and `_params`
#     methods to print it.
#     """
#     def _repr(obj):
#         """Represent the object as a function of its hyperparameters."""
#         hyper = obj.get_hyper()
#         substrings = []
#         for key, block, log in get_params(obj):
#             val = hyper[block]
#             val = val[0] if (len(val) == 1) else val
#             val = np.exp(val) if log else val
#             substrings += ['%s=%s' % (key, val)]
#         return obj.__class__.__name__ + '(' + ', '.join(substrings) + ')'
#     cls.__repr__ = _repr
#     return cls


# Path: pygp/kernels/matern.py
import numpy as np
from mwhutils.random import rstate
from ._real import RealKernel
from ._distances import rescale, diff, sqdist, sqdist_foreach
from ..utils.models import printable

            1+r if (self._d == 3) else
            1+r*(1+r/3.))

    def _df(self, r):
        return (
            1 if (self._d == 1) else
            r if (self._d == 3) else
            r*(1+r)/3.)

    def _params(self):
        return [
            ('sf', 1, True),
            ('ell', self.nhyper-1, True),
        ]

    def get_hyper(self):
        return np.r_[self._logsf, self._logell]

    def set_hyper(self, hyper):
        self._logsf = hyper[0]
        self._logell = hyper[1] if self._iso else hyper[1:]

    def get(self, X1, X2=None):
        X1, X2 = rescale(np.exp(self._logell)/np.sqrt(self._d), X1, X2)
        D = np.sqrt(sqdist(X1, X2))
        S = np.exp(self._logsf*2 - D)
        K = S * self._f(D)
        return K

    def grad(self, X1, X2=None):

        X1, X2 = rescale(np.exp(self._logell)/np.sqrt(self._d), X1, X2)