Example #1
0
    def fit(x_train):
        # Setup the K-NN estimator:

        start = time.time()

        # Encoding as KeOps LazyTensors:
        D = x_train.shape[1]
        X_i = Vi(0, D)  # Purely symbolic "i" variable, without any data array
        X_j = Vj(1, D)  # Purely symbolic "j" variable, without any data array

        # Symbolic distance matrix:
        if metric == "euclidean":
            D_ij = ((X_i - X_j)**2).sum(-1)
        # K-NN query operator:
        KNN_fun = D_ij.argKmin(K, dim=1)

        # N.B.: The "training" time here should be negligible.
        elapsed = time.time() - start

        def f(x_test):
            start = time.time()

            # Actual K-NN query:
            indices = KNN_fun(x_test, x_train)

            elapsed = time.time() - start

            indices = indices.cpu().numpy()
            return indices, elapsed

        return f, elapsed
Example #2
0
def plot_kernel(gamma):
    """ Samples 'x -> ∑_j b_j * k_j(x - y_j)' on the grid, and displays it as a heatmap. """
    if gamma.dim() == 2:
        heatmap = (-Vi(x).weightedsqdist(Vj(y), Vj(gamma))).exp() @ b
    else:
        heatmap = (-Vi(x).weightedsqdist(Vj(y), Pm(gamma))).exp() @ b
    heatmap = heatmap.view(
        res, res).cpu().numpy()  # reshape as a 'background' image
    plt.imshow(
        -heatmap,
        interpolation="bilinear",
        origin="lower",
        vmin=-1,
        vmax=1,
        cmap=cm.RdBu,
        extent=(0, 1, 0, 1),
    )
    plt.show()
Example #3
0
def lse_lazytensor(p, D, batchdims=(1, )):
    """Modern LazyTensor implementation."""

    x_i = Vi(0, D)
    y_j = Vj(1, D)
    f_j = Vj(2, 1)
    epsinv = Pm(3, 1)

    x_i.batchdims = batchdims
    y_j.batchdims = batchdims
    f_j.batchdims = batchdims
    epsinv.batchdims = batchdims

    if p == 2:
        D_ij = ((x_i - y_j)**2).sum(-1) / 2
    elif p == 1:
        D_ij = ((x_i - y_j)**2).sum(-1).sqrt()

    smin = (f_j - epsinv * D_ij).logsumexp(2)
    return smin
Example #4
0
    def test_logSumExp_kernels_feature(self):
        ############################################################
        from pykeops.torch import Vi, Vj, Pm

        kernels = {
            "gaussian": lambda xc, yc, sigmac: (
                -Pm(1 / sigmac ** 2) * Vi(xc).sqdist(Vj(yc))
            ),
            "laplacian": lambda xc, yc, sigmac: (
                -(Pm(1 / sigmac ** 2) * Vi(xc).sqdist(Vj(yc))).sqrt()
            ),
            "cauchy": lambda xc, yc, sigmac: (
                1 + Pm(1 / sigmac ** 2) * Vi(xc).sqdist(Vj(yc))
            )
            .power(-1)
            .log(),
            "inverse_multiquadric": lambda xc, yc, sigmac: (
                1 + Pm(1 / sigmac ** 2) * Vi(xc).sqdist(Vj(yc))
            )
            .sqrt()
            .power(-1)
            .log(),
        }

        for k in ["gaussian", "laplacian", "cauchy", "inverse_multiquadric"]:
            with self.subTest(k=k):
                # Call cuda kernel
                gamma_lazy = kernels[k](self.xc, self.yc, self.sigmac)
                gamma_lazy = gamma_lazy.logsumexp(dim=1, weight=Vj(self.gc.exp())).cpu()
                # gamma = kernel_product(params, self.xc, self.yc, self.gc).cpu()

                # Numpy version
                log_K = log_np_kernel(self.x, self.y, self.sigma, kernel=k)
                log_KP = log_K + self.g.T
                gamma_py = log_sum_exp(log_KP, axis=1)

                # compare output
                self.assertTrue(
                    np.allclose(gamma_lazy.data.numpy().ravel(), gamma_py, atol=1e-6)
                )
Example #5
0
    def fit(x_train):
        # Setup the K-NN estimator:
        x_train = tensor(x_train)
        start = timer()

        # Encoding as KeOps LazyTensors:
        D = x_train.shape[1]
        X_i = Vi(0, D)  # Purely symbolic "i" variable, without any data array
        X_j = Vj(1, D)  # Purely symbolic "j" variable, without any data array

        # Symbolic distance matrix:
        if metric == "euclidean":
            D_ij = ((X_i - X_j)**2).sum(-1)
        elif metric == "manhattan":
            D_ij = (X_i - X_j).abs().sum(-1)
        elif metric == "angular":
            D_ij = -(X_i | X_j)
        elif metric == "hyperbolic":
            D_ij = ((X_i - X_j)**2).sum(-1) / (X_i[0] * X_j[0])
        else:
            raise NotImplementedError(
                f"The '{metric}' distance is not supported.")

        # K-NN query operator:
        KNN_fun = D_ij.argKmin(K, dim=1)

        # N.B.: The "training" time here should be negligible.
        elapsed = timer() - start

        def f(x_test):
            x_test = tensor(x_test)
            start = timer()

            # Actual K-NN query:
            indices = KNN_fun(x_test, x_train)

            elapsed = timer() - start

            indices = indices.cpu().numpy()
            return indices, elapsed

        return f, elapsed
Example #6
0
 def log_likelihoods(self, sample):
     """Log-density, sampled on a given point cloud."""
     self.update_covariances()
     K_ij = -Vi(sample).weightedsqdist(Vj(self.mu), Vj(
         self.params["gamma"]))
     return K_ij.logsumexp(dim=1, weight=Vj(self.weights()))
Example #7
0
 def likelihoods(self, sample):
     """Samples the density on a given point cloud."""
     self.update_covariances()
     return (-Vi(sample).weightedsqdist(Vj(self.mu), Vj(
         self.params["gamma"]))).exp() @ self.weights()
###############################################################################
# .. note::
#   This operator uses a conjugate gradient solver and assumes
#   that **formula** defines a **symmetric**, positive and definite
#   **linear** reduction with respect to the alias ``"b"``
#   specified trough the third argument.
#
# Apply our solver on arbitrary point clouds:
#

print(
    "Solving a Gaussian linear system, with {} points in dimension {}.".format(
        N, D))
start = time.time()
K_xx = keops.exp(-keops.sum((Vi(x) - Vj(x))**2, dim=2) / (2 * sigma**2))
cfun = keops.solve(K_xx, Vi(b), alpha=alpha, call=False)
c = cfun()
end = time.time()
print("Timing (KeOps implementation):", round(end - start, 5), "s")

###############################################################################
# Compare with a straightforward PyTorch implementation:
#

start = time.time()
K_xx = alpha * torch.eye(N) + torch.exp(-torch.sum(
    (x[:, None, :] - x[None, :, :])**2, dim=2) / (2 * sigma**2))
c_py = torch.solve(b, K_xx)[0]
end = time.time()
print("Timing (PyTorch implementation):", round(end - start, 5), "s")
Example #9
0
    sigmac = torch.tensor(sigma, dtype=torchtype, device=device)

except:
    pass

####################################################################
# Convolution Benchmarks
# ----------------------
#
# We loop over four different kernels:
#

kernel_to_test = ['gaussian', 'laplacian', 'cauchy', 'inverse_multiquadric']
kernels = {
    'gaussian':
    lambda xc, yc, sigmac: (-Pm(1 / sigmac**2) * Vi(xc).sqdist(Vj(yc))).exp(),
    'laplacian':
    lambda xc, yc, sigmac:
    (-(Pm(1 / sigmac**2) * Vi(xc).sqdist(Vj(yc))).sqrt()).exp(),
    'cauchy':
    lambda xc, yc, sigmac:
    (1 + Pm(1 / sigmac**2) * Vi(xc).sqdist(Vj(yc))).power(-1),
    'inverse_multiquadric':
    lambda xc, yc, sigmac:
    (1 + Pm(1 / sigmac**2) * Vi(xc).sqdist(Vj(yc))).sqrt().power(-1)
}

#####################################################################
# With four backends: Numpy, vanilla PyTorch, Generic KeOps reductions
# and a specific, handmade legacy CUDA code for kernel convolutions:
#
Example #10
0
# Here is how it works, if we
# want to perform a simple gaussian convolution.
#
# We first create the dataset using 2D tensors:

M, N = (100000, 200000) if use_cuda else (1000, 2000)
D = 3

x = torch.randn(M, D).type(tensor)
y = torch.randn(N, D).type(tensor)

############################################################################
# Then we use :func:`Vi <pykeops.torch.Vi>` and :func:`Vj <pykeops.torch.Vj>` to convert to KeOps LazyTensor objects
from pykeops.torch import Vi, Vj

x_i = Vi(x)  # (M, 1, D) LazyTensor, equivalent to LazyTensor( x[:,None,:] )
y_j = Vj(y)  # (1, N, D) LazyTensor, equivalent to LazyTensor( y[None,:,:] )

############################################################################
# and perform our operations:
D2xy = ((x_i - y_j)**2).sum()
gamma = D2xy.sum_reduction(dim=1)

#########################################################################
# Note that in the first line we used ``sum`` without any axis or dim parameter.
# This is equivalent to ``sum(-1)`` or ``sum(dim=2)``, because
# the axis parameter is set to ``2`` by default. But speaking about ``dim=2``
# here with the :func:`Vi <pykeops.torch.Vi>`, :func:`Vj <pykeops.torch.Vj>` helpers could be misleading.
# Similarly we used ``sum_reduction`` instead of ``sum`` to make it clear
# that we perform a reduction, but sum and sum_reduction with ``dim=0`` or ``1``
# are equivalent (however ``sum_reduction`` with ``dim=2`` is forbidden)
Example #11
0
def GaussLinKernel(sigma):
    x, y, u, v, b = Vi(0, 3), Vj(1, 3), Vi(2, 3), Vj(3, 3), Vj(4, 1)
    gamma = 1 / (sigma * sigma)
    D2 = x.sqdist(y)
    K = (-D2 * gamma).exp() * (u * v).sum()**2
    return (K * b).sum_reduction(axis=1)
Example #12
0
def GaussKernel(sigma):
    x, y, b = Vi(0, 3), Vj(1, 3), Vj(2, 3)
    gamma = 1 / (sigma * sigma)
    D2 = x.sqdist(y)
    K = (-D2 * gamma).exp()
    return (K * b).sum_reduction(axis=1)
Example #13
0
def gaussian_kernel(x, y, sigma=0.2):
    x_i = Vi(x)
    y_j = Vj(y)
    D_ij = ((x_i - y_j)**2).sum(-1)
    return (-D_ij / (2 * sigma**2)).exp()
Example #14
0
###############################################################
#  Generate some data, stored on the CPU (host) memory:
#

M = 1000
N = 2000
x = np.random.randn(M, 3).astype(dtype)
y = np.random.randn(N, 3).astype(dtype)
a = np.random.randn(N, 1).astype(dtype)
p = np.random.randn(1).astype(dtype)

#########################################
# Launch our routine on the CPU:
#

xi, yj, aj = Vi(x), Vj(y), Vj(a)
c = ((p - aj)**2 * (xi + yj).exp()).sum(axis=1, backend='CPU')

#########################################
# And on our GPUs, with copies between
# the Host and Device memories:
#
if IsGpuAvailable():
    for gpuid in gpuids:
        d = ((p - aj)**2 * (xi + yj).exp()).sum(axis=1,
                                                backend='GPU',
                                                device_id=gpuid)
        print('Relative error on gpu {}: {:1.3e}'.format(
            gpuid, float(np.sum(np.abs(c - d)) / np.sum(np.abs(c)))))

        # Plot the results next to each other: