Ejemplo n.º 1
0
 def K(x, y, u, v, b):
     params = {
         "id": Kernel("gaussian(x,y) * linear(u,v)**2"),
         "gamma": (CpuOrGpu(torch.FloatTensor([1 / sigma**2])), None),
         "backend": backend_keops
     }
     return kernel_product(params, (x, u), (y, v), b)
Ejemplo n.º 2
0
 def K(x, y, b):
     params = {
         'id': Kernel('gaussian(x,y)'),
         'gamma': 1 / (sigma * sigma),
         'backend': 'auto'
     }
     return kernel_product(params, x, y, b)
Ejemplo n.º 3
0
 def K(x, y, b):
     params = {
         "id": Kernel("gaussian(x,y)"),
         "gamma": CpuOrGpu(torch.FloatTensor([1 / sigma**2])),
         "backend": backend_keops
     }
     return kernel_product(params, x, y, b)
Ejemplo n.º 4
0
 def K(x, y, u, v, b):
     params = {
         'id': Kernel('gaussian(x,y) * linear(u,v)**2'),
         'gamma': (1 / (sigma * sigma), None),
         'backend': 'auto'
     }
     return kernel_product(params, (x, u), (y, v), b)
Ejemplo n.º 5
0
    def __init__(self, M, sparsity=0, D=2):
        super(GaussianMixture, self).__init__()

        self.params = {'id': Kernel('gaussian(x,y)')}
        # We initialize our model with random blobs scattered across
        # the unit square, with a small-ish radius:
        self.mu = Parameter(torch.rand(M, D).type(dtype))
        self.A = 15 * torch.ones(M, 1, 1) * torch.eye(D, D).view(1, D, D)
        self.A = Parameter((self.A).type(dtype).contiguous())
        self.w = Parameter(torch.ones(M, 1).type(dtype))
        self.sparsity = sparsity
Ejemplo n.º 6
0
               interpolation='bilinear',
               origin='lower',
               vmin=-1,
               vmax=1,
               cmap=cm.RdBu,
               extent=(0, 1, 0, 1))
    plt.title(title, fontsize=20)


plt.figure()

# TEST ===================================================================================
# Let's use a "gaussian" kernel, i.e.
#        k(x_i,y_j) = exp( - WeightedSquareNorm(gamma, x_i-y_j ) )
params = {
    "id": Kernel("gaussian(x,y)"),
}

# The type of kernel is inferred from the shape of the parameter "gamma",
# used as a "metric multiplier".
# Denoting D == x.shape[1] == y.shape[1] the size of the feature space, rules are :
#   - if "gamma" is a vector    (gamma.shape = [K]),   it is seen as a fixed parameter
#   - if "gamma" is a 2d-tensor (gamma.shape = [M,K]), it is seen as a "j"-variable "gamma_j"
#
#   - if K == 1 , gamma is a scalar factor in front of a simple euclidean squared norm :
#                 WeightedSquareNorm( g, x-y ) = g * |x-y|^2

#   - if K == D , gamma is a diagonal matrix:
#                 WeightedSquareNorm( g, x-y ) = < x-y, diag(g) * (x-y) >
#                                              = \sum_d  ( g[d] * ((x-y)[d])**2 )
#   - if K == D*D, gamma is a (symmetric) matrix:
Ejemplo n.º 7
0
def benchmark(bench_name, N, dev, backend, loops = 10, enable_GC=True, fidelity=None) :

    importlib.reload(torch)

    device = torch.device(dev)
    x_i  = torch.randn(N, D, dtype=torch.float32, device=device, requires_grad=True)
    y_j  = torch.randn(N, D, dtype=torch.float32, device=device)
    mu_i = torch.randn(N, 1, dtype=torch.float32, device=device)
    nu_j = torch.randn(N, 1, dtype=torch.float32, device=device)

    mu_i = mu_i.abs()        ; nu_j = nu_j.abs()
    mu_i = mu_i / mu_i.sum() ; nu_j = nu_j / nu_j.sum()

    s2v = lambda x : torch.tensor([x], dtype=torch.float32, device=device)

    if bench_name == "gaussian_conv" :
        k = { "id"         : Kernel("gaussian(x,y)"),
              "gamma"      : s2v( .25 ),
              "backend"    : backend,                 }

        from pykeops.torch import kernel_product

        _ = kernel_product(k, x_i, y_j, nu_j)
        import gc
        GC = 'gc.enable();' if enable_GC else 'pass;'
        print("{:3} NxN-gaussian-convs, with N ={:7}: {:3}x".format(loops, N, loops), end="")

        elapsed = timeit.Timer('_ = kernel_product(k,x_i,y_j,nu_j)', GC,  
                                        globals = locals(), timer = time.time).timeit(loops)

    elif bench_name == "fidelities" :

        from divergences import kernel_divergence, regularized_ot, hausdorff_divergence, sinkhorn_divergence

        if fidelity == "energy_distance" :
            params = ("energy", None)
            c = kernel_divergence(mu_i,x_i, nu_j,y_j, k=params ) ; c.backward()
            code = "c = kernel_divergence(mu_i,x_i, nu_j,y_j, k=params ) ; c.backward()"

        elif fidelity == "gaussian_kernel" :
            params = ("gaussian", .25)
            c = kernel_divergence(mu_i,x_i, nu_j,y_j, k=params ) ; c.backward()
            code = "c = kernel_divergence(mu_i,x_i, nu_j,y_j, k=params ) ; c.backward()"

        elif fidelity == "log_kernel" :
            params = {
                "p"    : 1,
                "eps"  : .1,
                "nits" : 1,
                "tol"  : 0.,
            }
            c = hausdorff_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()
            code = "c = hausdorff_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()"

        elif fidelity == "hausdorff" :
            params = {
                "p"    : 1,
                "eps"  : .1,
                "nits" : 3,
                "tol"  : 0.,
            }
            c = hausdorff_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()
            code = "c = hausdorff_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()"

        elif fidelity == "sinkhorn" :
            params = {
                "p"    : 1,
                "eps"  : .1,
                "nits" : 20,
                "assume_convergence" : True, # This is true in practice, and lets us win a x2 factor
                "tol"  : 0.,
            }
            c = sinkhorn_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()
            code = "c = sinkhorn_divergence(mu_i,x_i, nu_j,y_j, **params ) ; c.backward()"

        import gc
        GC = 'gc.enable();' if enable_GC else 'pass;'
        print("{:3} NxN fidelities, with N ={:7}: {:3}x".format(loops, N, loops), end="")

        elapsed = timeit.Timer(code, GC, globals = locals(), timer = time.time).timeit(loops)

    print("{:3.6f}s".format(elapsed/loops))
    return elapsed / loops
Ejemplo n.º 8
0

###############################################
# Kernel definition
# ---------------------
# Let's use a **Gaussian kernel** given  through
#
# .. math::
#
#      k(x_i,y_j) = \exp( -\|x - y\|_{\Gamma}^2) = \exp( -  (x_i - y_j)^t \Gamma (x_i-y_j) ),
#
# which is equivalent to the KeOps formula ``exp(-WeightedSquareNorm(gamma, x_i-y_j ))``.
# Using the high-level :class:`pykeops.torch.Kernel`
# and :func:`pykeops.torch.kernel_product` wrappers, we can simply define:

params = {'id': Kernel('gaussian(x,y)')}

###############################################
# The precise meaning of the computation is then defined through the extra entry ``gamma`` of the ``params`` dictionary,
# which will is to be used as a 'metric multiplier'. Denoting ``D == x.shape[1] == y.shape[1]`` the size of the feature space, the integer ``K`` can be ``1``, ``D`` or ``D*D``. Rules are:
#
# - if ``gamma`` is a vector    (``gamma.shape = [K]``),   it is seen as a fixed parameter
# - if ``gamma`` is a 2d-tensor (``gamma.shape = [M,K]``), it is seen as a ``j``-variable
#
# N.B.: Beware of ``Shape([K]) != Shape([1,K])`` confusions!

###############################################
#
# Isotropic Kernels
# -----------------
#
Ejemplo n.º 9
0
    # Pure numpy
    g_numpy = np.matmul(np_kernel(x, y, sigma, kernel=k), b)
    speed_numpy[k] = timeit.repeat(
        'gnumpy = np.matmul( np_kernel(x, y, sigma, kernel=k), b)',
        globals=globals(),
        repeat=5,
        number=1)
    print('Time for NumPy:               {:.4f}s'.format(
        np.median(speed_numpy[k])))

    # Vanilla pytorch (with cuda if available, and cpu otherwise)
    try:
        from pykeops.torch import Kernel, kernel_product

        params = {
            'id': Kernel(k + '(x,y)'),
            'gamma': 1. / (sigmac**2),
            'backend': 'pytorch',
        }

        g_pytorch = kernel_product(params, xc, yc, bc, mode='sum').cpu()
        torch.cuda.synchronize()
        speed_pytorch[k] = np.array(
            timeit.repeat(
                "kernel_product(params, xc, yc, bc, mode='sum'); torch.cuda.synchronize()",
                globals=globals(),
                repeat=REPEAT,
                number=4)) / 4

        print('Time for PyTorch:             {:.4f}s'.format(
            np.median(speed_pytorch[k])),
                globals=globals(),
                timer=time.time).timeit(LOOPS)
            print("Time for keops specific:   {:.4f}s".format(speed_pykeops),
                  end="")
            print("   (absolute error:       ", np.max(np.abs(g1 - gnumpy)),
                  ")")
        except:
            pass
    else:
        print("Time for keops specific:   skipping (no Gpu detected)")

    # keops + pytorch : generic tiled implementation (with cuda if available else uses cpu)
    try:
        # Define a kernel: Wrap it (and its parameters) into a JSON dict structure
        mode = "sum"
        kernel = Kernel(k + "(x,y)")
        params = {
            "id": kernel,
            "gamma": 1. / sigmac**2,
            "backend": "auto",
        }

        aKxy_b = torch.dot(
            ac.view(-1),
            kernel_product(params, xc, yc, bc, mode=mode).view(-1))
        g3 = torch.autograd.grad(aKxy_b, xc, create_graph=False)[0].cpu()
        speed_keops = timeit.Timer(
            'g3 = torch.autograd.grad(torch.dot(ac.view(-1), kernel_product( params, xc,yc,bc, mode=mode).view(-1)), xc, create_graph=False)[0]',
            GC,
            globals=globals(),
            timer=time.time).timeit(LOOPS)
Ejemplo n.º 11
0
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')

# N.B.: PyTorch's default dtype is float32
a = torch.randn(npoints_x, dimsignal, device=device)
x = torch.randn(npoints_x, dimpoints, requires_grad=True, device=device)
y = torch.randn(npoints_y, dimpoints, device=device)
b = torch.randn(npoints_y, dimsignal, device=device)

####################################################################
# A Gaussian convolution
# ----------------------

# Wrap the kernel's parameters into a JSON dict structure
sigma = scal_to_var(-1.5)
params = {
    'id': Kernel('gaussian(x,y)'),
    'gamma': .5 / sigma**2,
}

####################################################################
# Test, using both **PyTorch** and **KeOps** (online) backends:
axes = plt.subplot(2, 1, 1), plt.subplot(2, 1, 2)

for backend, linestyle, label in [("auto", "-", "KeOps"),
                                  ("pytorch", "--", "PyTorch")]:

    Kxy_b = kernel_product(params, x, y, b, backend=backend)
    aKxy_b = scalprod(a, Kxy_b)

    # Computing a gradient is that easy - we can also use the 'aKxy_b.backward()' syntax.
    # Notice the 'create_graph=True', which will allow us to compute
Ejemplo n.º 12
0
import torch
from pykeops.torch import Kernel, kernel_product

s2v = lambda x : torch.tensor([x])

Nt, Nx = 501, 201
t   = torch.linspace( 0, 1, Nt ).view(-1,1)
x_i = torch.linspace( .2, .35, Nx).view(-1,1)
y_j = torch.linspace( .65, .8, Nx).view(-1,1)
mu_i = .15 * torch.ones( Nx ).view(-1,1) / Nx
nu_j = .15 * torch.ones( Nx ).view(-1,1) / Nx


kernels =  {
    "gaussian" : { 
        "id"         : Kernel("gaussian(x,y)"),
        "gamma"      : s2v( 1 / .1**2 ),
    },
    "laplacian" : { 
        "id"         : Kernel("laplacian(x,y)"),
        "gamma"      : s2v( 1 / .1**2 ),
    },
    "energy_distance" : { 
        "id"         : Kernel("-distance(x,y)"),
        "gamma"      : s2v( 1. ),
    },
}

fs = [ t ]
for name, kernel in kernels.items() :
    f = kernel_product( kernel, t, x_i, mu_i) \
Ejemplo n.º 13
0
from scipy import misc
from time  import time

tensor    = torch.cuda.FloatTensor if torch.cuda.is_available() else torch.FloatTensor 

# ================================================================================================
# ================================  The Sinkhorn algorithm  ======================================
# ================================================================================================

# Parameters of our optimal transport cost -------------------------------------------------------
a = 1 # Use a cost function  "C(x,y) = |x-y|^a"
# The Sinkhorn algorithm relies on a kernel "k(x,y) = exp(-C(x,y))" :
kernel_names = { 1 : "laplacian" ,   # exp(-|x|),   Earth-mover distance
                 2 : "gaussian"    } # exp(-|x|^2), Quadratic "Wasserstein" distance
params = {
    "id"          : Kernel( kernel_names[a] + "(x,y)" ),

    # Default set of parameters for the Sinkhorn cost - they make sense on the unit square/cube:
    "nits"        : 30,                 # Number of iterations in the Sinkhorn loop
    "epsilon"     : tensor([ .05**a ]), # Regularization strength, homogeneous to C(x,y)
}

# The Sinkhorn loop ------------------------------------------------------------------------------
dot = lambda a,b : torch.dot(a.view(-1), b.view(-1))
def OT_distance(params, Mu, Nu) :
    """
    Computes an optimal transport cost using the Sinkhorn algorithm, stabilized in the log-domain.
    See the section 4.4 of
       "Gabriel Peyré and Marco Cuturi, Computational Optimal Transport, ArXiv:1803.00567, 2018"
    for reference.
    """
Ejemplo n.º 14
0
a = torch.randn(npoints_x,dimsignal, requires_grad=True, device=device)
x = torch.randn(npoints_x,dimpoints, requires_grad=True, device=device)
y = torch.randn(npoints_y,dimpoints, requires_grad=True, device=device)
b = torch.randn(npoints_y,dimsignal, requires_grad=True, device=device)


#--------------------------------------------------------------#
#                    A first Kernel                            #
#--------------------------------------------------------------#
# N.B.: "sum" is default, "lse" is for "log-sum-exp"
modes = ["sum", "lse"] if dimsignal==1 else ["sum"]

# Wrap the kernel's parameters into a JSON dict structure
sigma = scal_to_var(-1.5)
params = {
    "id"      : Kernel("gaussian(x,y)"),
    "gamma"   : 1./sigma**2,
    "mode"    : "sum",
}


# Test, using a pytorch or keops
for mode in modes : 
    params["mode"] = mode
    print("Mode :", mode, "========================================")
    for backend in ["pytorch", "auto"] :
        params["backend"] = backend
        print("Backend :", backend, "--------------------------")

        Kxy_b  = kernel_product( params, x,y,b )
        aKxy_b = scalprod(a, Kxy_b)