Skip to content

Background ​

This page builds intuition for what SDOT computes, how the solvers work, and what quantities you can extract from an OT plan.

The base problem ​

Given:

  • f — a discrete measure: a finite collection of weighted Dirac masses at positions y1,…,yn with masses m1,…,mn (with ∑imi=1)
  • g — a continuous density on some domain Ω

Find the transport map T:Ω→{y1,…,yn} that moves g onto f at minimum cost:

W22(f,g)=minT∫Ω∥x−T(x)∥2g(x)dxs.t.∫T−1(yi)g(x)dx=mi∀i

There are variants for the ground metric, notably for periodicity (see ground metrics).

Non euclidean norms are also planed (see roadmap)

Power Diagrams (Laguerre Cells) ​

The optimal transport map in the semi-discrete setting is always a power diagram (also called a Laguerre tessellation): a partition of Ω into cells Ci(w), each associated to a Dirac yi, where membership is defined by a weighted distance:

x∈Ci(w)⟺∥x−yi∥2−wi≤∥x−yj∥2−wj∀j≠i

When all weights wi=0, this reduces to the standard Voronoi diagram. The weights "inflate" or "deflate" each cell to match the target mass.

The transport plan then simply sends every point x in cell Ci to position yi.


The Newton Solver ​

SDOT finds the optimal weights by solving:

∫Ci(w)g(x)dx=mi∀i

The weights maximize a concave function, whose gradient is the vector of the mass errors: a damped Newton (+ some specific optimizations) method converges in a few iterations to a few dozen. The key cost is computing the power diagram and its integrals at each step, which runs in O(n log n) time.

Reference: Kitagawa, Mérigot, Thibert — Convergence of a Newton algorithm for semi-discrete optimal transport, JEMS 2019.

Computing a Plan ​

Functions like sdot.w2 (for the 2 norm) give only the distance. If you want more information, you can use sdot.plan (with some default values)

python
p = sdot.plan( f, g )

p.cost            # W2^2 (by default)
p.barycenters     # the barycenter of each cell, [ n, d ]
p.weights         # the weights w of the power diagram, [ n ]
p.power_diagram() # the geometry

See plans and settings.

Barycenters and the Lloyd Algorithm ​

The barycenters p.barycenters[ i ] are the centroids of each transport cell:

bi=1mi∫Ci(w)xg(x)dx

Here is an example of use of the barycenter, in a Lloyd algorithm : moving each Dirac to its barycenter and repeating gives converges to an optimal quantization of g:

py
import numpy as np
import sdot

rng = np.random.default_rng( 0 )
positions = rng.random( ( 100, 2 ) )

pb = sdot.OtProblem( sdot.SumOfDiracs( positions ), sdot.Image( rng.random( ( 10, 10 ) ) ) )
for _ in range( 10 ):
    p = pb.solve()                                       # restarts from the previous solution by itself
    positions = p.barycenters
    pb.source = sdot.SumOfDiracs( positions )

Actually, if you are intersted in optimal quantization, sdot.quantization does it for you: see the Lloyd quantization example (the snippet is here to show how to get the barycenters).

What's Next ​

SDOT — H. Leclerc, Q. Mérigot, T. Gallouët · LMO / INRIA PARMA