# Path: pygp/kernels/_base.py
# class Kernel(Parameterized):
#     """
#     The base Kernel interface.
#     """
#     def __call__(self, x1, x2):
#         return self.get(x1[None], x2[None])[0]
# 
#     @abstractmethod
#     def get(self, X1, X2=None):
#         """
#         Evaluate the kernel.
# 
#         Returns the matrix of covariances between points in `X1` and `X2`. If
#         `X2` is not given this will return the pairwise covariances between
#         points in `X1`.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def dget(self, X):
#         """Evaluate the self covariances."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def grad(self, X1, X2=None):
#         """
#         Evaluate the gradient of the kernel.
# 
#         Returns an iterator over the gradients of the covariances between
#         points in `X1` and `X2`. If `X2` is not given this will iterate over
#         the the gradients of the pairwise covariances.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def dgrad(self, X):
#         """Evaluate the gradients of the self covariances."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def transform(self, X):
#         """Format the inputs X as arrays."""
#         raise NotImplementedError


import numpy as np
import itertools as it
import functools as ft
import operator as op
from ._base import Kernel
    """
    The equivalent object to sum but for products.
    """
    return ft.reduce(op.mul, fiterable, 1)


def product_but(fiterable):
    """
    Given an iterator over function evaluations return an array such that
    `M[i]` is the product of every evaluation except for the ith one.
    """
    A = list(fiterable)

    # allocate memory for M and fill everything but the last element with
    # the product of A[i+1:]. Note that we're using the cumprod in place.
    M = np.empty_like(A)
    np.cumprod(A[:0:-1], axis=0, out=M[:-1][::-1])

    # use an explicit loop to iteratively set M[-1] equal to the product of
    # A[:-1]. While doing this we can multiply M[i] by A[:i].
    M[-1] = A[0]
    for i in xrange(1, len(A)-1):
        M[i] *= M[-1]
        M[-1] *= A[i]

    return M


### GENERAL COMBINATION KERNEL ################################################


# Path: pygp/kernels/_base.py
# class Kernel(Parameterized):
#     """
#     The base Kernel interface.
#     """
#     def __call__(self, x1, x2):
#         return self.get(x1[None], x2[None])[0]
# 
#     @abstractmethod
#     def get(self, X1, X2=None):
#         """
#         Evaluate the kernel.
# 
#         Returns the matrix of covariances between points in `X1` and `X2`. If
#         `X2` is not given this will return the pairwise covariances between
#         points in `X1`.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def dget(self, X):
#         """Evaluate the self covariances."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def grad(self, X1, X2=None):
#         """
#         Evaluate the gradient of the kernel.
# 
#         Returns an iterator over the gradients of the covariances between
#         points in `X1` and `X2`. If `X2` is not given this will iterate over
#         the the gradients of the pairwise covariances.
#         """
#         raise NotImplementedError
# 
#     @abstractmethod
#     def dgrad(self, X):
#         """Evaluate the gradients of the self covariances."""
#         raise NotImplementedError
# 
#     @abstractmethod
#     def transform(self, X):
#         """Format the inputs X as arrays."""
#         raise NotImplementedError


# Path: pygp/kernels/_combo.py
import numpy as np
import itertools as it
import functools as ft
import operator as op
from ._base import Kernel

    """
    The equivalent object to sum but for products.
    """
    return ft.reduce(op.mul, fiterable, 1)


def product_but(fiterable):
    """
    Given an iterator over function evaluations return an array such that
    `M[i]` is the product of every evaluation except for the ith one.
    """
    A = list(fiterable)

    # allocate memory for M and fill everything but the last element with
    # the product of A[i+1:]. Note that we're using the cumprod in place.
    M = np.empty_like(A)
    np.cumprod(A[:0:-1], axis=0, out=M[:-1][::-1])

    # use an explicit loop to iteratively set M[-1] equal to the product of
    # A[:-1]. While doing this we can multiply M[i] by A[:i].
    M[-1] = A[0]
    for i in xrange(1, len(A)-1):
        M[i] *= M[-1]
        M[-1] *= A[i]

    return M


### GENERAL COMBINATION KERNEL ################################################


class ComboKernel(Kernel):