Note
Go to the end to download the full example code.
Introduction to Optimal Transport with Python
This example gives an introduction on how to use Optimal Transport in Python.
# Author: Remi Flamary, Nicolas Courty, Aurelie Boisbunon
#
# License: MIT License
# sphinx_gallery_thumbnail_number = 1
POT Python Optimal Transport Toolbox
POT installation
Install with pip:
pip install pot
Install with conda:
conda install -c conda-forge pot
Import the toolbox
import numpy as np # always need it
import pylab as pl # do the plots
import ot # ot
import time
Getting help
Online documentation : https://pythonot.github.io/all.html
Or inline help:
help(ot.dist)
Help on function dist in module ot.utils:
dist(x1, x2=None, metric='sqeuclidean', p=2, w=None, backend='auto', nx=None, use_tensor=False)
Compute distance between samples in :math:`\mathbf{x_1}` and :math:`\mathbf{x_2}`
.. note:: This function is backend-compatible and will work on arrays
from all compatible backends for the following metrics:
'sqeuclidean', 'euclidean', 'cityblock', 'minkowski', 'cosine', 'correlation'.
Parameters
----------
x1 : array-like, shape (n1,d)
matrix with `n1` samples of size `d`
x2 : array-like, shape (n2,d), optional
matrix with `n2` samples of size `d` (if None then :math:`\mathbf{x_2} = \mathbf{x_1}`)
metric : str | callable, optional
'sqeuclidean' or 'euclidean' on all backends. On numpy the function also
accepts from the scipy.spatial.distance.cdist function : 'braycurtis',
'canberra', 'chebyshev', 'cityblock', 'correlation', 'cosine', 'dice',
'euclidean', 'hamming', 'jaccard', 'kulczynski1', 'mahalanobis',
'matching', 'minkowski', 'rogerstanimoto', 'russellrao', 'seuclidean',
'sokalmichener', 'sokalsneath', 'sqeuclidean', 'wminkowski', 'yule'.
p : float, optional
p-norm for the Minkowski and the Weighted Minkowski metrics. Default value is 2.
w : array-like, rank 1
Weights for the weighted metrics.
backend : str, optional
Backend to use for the computation. If 'auto', the backend is
automatically selected based on the input data. if 'scipy',
the ``scipy.spatial.distance.cdist`` function is used (and gradients are
detached).
use_tensor : bool, optional
If true use tensorized computation for the distance matrix which can
cause memory issues for large datasets. Default is False and the
parameter is used only for the 'cityblock' and 'minkowski' metrics.
nx : Backend, optional
Backend to perform computations on. If omitted, the backend defaults to that of `x1`.
Returns
-------
M : array-like, shape (`n1`, `n2`)
distance matrix computed with given metric
First OT Problem
We will solve the Bakery/Cafés problem of transporting croissants from a number of Bakeries to Cafés in a City (in this case Manhattan). We did a quick google map search in Manhattan for bakeries and Cafés:
We extracted from this search their positions and generated fictional production and sale number (that both sum to the same value).
We have access to the position of Bakeries bakery_pos and their
respective production bakery_prod which describe the source
distribution. The Cafés where the croissants are sold are defined also by
their position cafe_pos and cafe_prod, and describe the target
distribution. For fun we also provide a
map Imap that will illustrate the position of these shops in the city.
Now we load the data
data = np.load("../data/manhattan.npz")
bakery_pos = data["bakery_pos"]
bakery_prod = data["bakery_prod"]
cafe_pos = data["cafe_pos"]
cafe_prod = data["cafe_prod"]
Imap = data["Imap"]
print("Bakery production: {}".format(bakery_prod))
print("Cafe sale: {}".format(cafe_prod))
print("Total croissants : {}".format(cafe_prod.sum()))
Bakery production: [31. 48. 82. 30. 40. 48. 89. 73.]
Cafe sale: [82. 88. 92. 88. 91.]
Total croissants : 441.0
Plotting bakeries in the city
Next we plot the position of the bakeries and cafés on the map. The size of the circle is proportional to their production.
pl.figure(1, (7, 6))
pl.clf()
pl.imshow(Imap, interpolation="bilinear") # plot the map
pl.scatter(
bakery_pos[:, 0], bakery_pos[:, 1], s=bakery_prod, c="r", ec="k", label="Bakeries"
)
pl.scatter(cafe_pos[:, 0], cafe_pos[:, 1], s=cafe_prod, c="b", ec="k", label="Cafés")
pl.legend()
pl.title("Manhattan Bakeries and Cafés")