Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
repos:
# Ruff skada
# Ruff
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.5.2
hooks:
Expand All @@ -15,10 +15,11 @@ repos:
name: ruff lint doc, tutorials, tests and examples
# D103: missing docstring in public function
# D400: docstring first line must end with period
args: ["--ignore=D103,D400", "--fix"]
files: ^docs/|^examples/^test/
# E402: module level import not at top of file (sphinx-gallery sections)
args: ["--ignore=D103,D400,E402", "--fix"]
files: ^docs/|^examples/|^test/
- id: ruff-format
files: ^ot/|^docs/|^examples/|
files: ^ot/|^docs/|^examples/

# Codespell
- repo: https://github.com/codespell-project/codespell
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ POT provides the following generic OT solvers:
circle](https://pythonot.github.io/auto_examples/sliced-wasserstein/plot_compute_wasserstein_circle.html)
\[44, 45] and [Spherical Sliced Wasserstein](https://pythonot.github.io/auto_examples/sliced-wasserstein/plot_variance_ssw.html) \[46]
* [Graph Dictionary Learning solvers](https://pythonot.github.io/auto_examples/gromov/plot_gromov_wasserstein_dictionary_learning.html) \[38].
* [Semi-relaxed (Fused) Gromov-Wasserstein divergences](https://pythonot.github.io/auto_examples/gromov/plot_semirelaxed_fgw.html) with corresponding [barycenter solvers](https://pythonot.github.io/auto_examples/gromov/plot_semirelaxed_gromov_wasserstein_barycenter.hmtl) (exact and regularized \[48]).
* [Semi-relaxed (Fused) Gromov-Wasserstein divergences](https://pythonot.github.io/auto_examples/gromov/plot_semirelaxed_fgw.html) with corresponding [barycenter solvers](https://pythonot.github.io/auto_examples/gromov/plot_semirelaxed_gromov_wasserstein_barycenter.html) (exact and regularized \[48]).
* [Quantized (Fused) Gromov-Wasserstein distances](https://pythonot.github.io/auto_examples/gromov/plot_quantized_gromov_wasserstein.html) \[68].
* [Efficient Discrete Multi Marginal Optimal Transport Regularization](https://pythonot.github.io/auto_examples/others/plot_demd_gradient_minimize.html) \[50].
* [Several backends](https://pythonot.github.io/quickstart.html#solving-ot-with-multiple-backends) for easy use of POT with [Pytorch](https://pytorch.org/)/[jax](https://github.com/google/jax)/[Numpy](https://numpy.org/)/[Cupy](https://cupy.dev/)/[Tensorflow](https://www.tensorflow.org/) arrays.
Expand Down
3 changes: 3 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
- Fix `UnbalancedSinkhornTransport` `transform` failing with `AttributeError: 'NoneType' object has no attribute 'array_equal'` when `fit` was called with missing parameters (PR #837, Issue #650)
- Fix quantized (F)GW solvers that ordered OT based on clusters and not initial node ordering (PR #857, Issue #786)
- Update CircleCI config to use `version: 2.1` instead of the deprecated `version: 2.` (PR #882)
- Fix `ot.gromov.fused_unbalanced_across_spaces_divergence` crashing with a `TypeError` when only one of the sample or feature dual pairs was passed in `init_duals` (PR #888)
- Raise errors instead of printing a message and returning `None` or dummy values in `ot.datasets.make_data_classif` (unknown dataset), `ot.stochastic.solve_semi_dual_entropic` (unknown method) and `ot.mapping.nearest_brenier_potential_*` (missing CVXPY), and emit warnings instead of prints in `ot.gaussian.bures_barycenter_fixpoint`, `ot.regpath` and `ot.partial.entropic_partial_wasserstein` (PR #888)
- Fix docstring errors (wrong or duplicated parameters, typos) and fix the file patterns of the ruff hooks in `.pre-commit-config.yaml` (PR #888)

## 0.9.7.post1

Expand Down
1 change: 0 additions & 1 deletion examples/backends/plot_gradient_descent.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
from time import perf_counter
import ot
from ot.batch._quadratic import loss_quadratic_batch, tensor_batch
from ot.gromov import fused_unbalanced_gromov_wasserstein
from sklearn.manifold import MDS


Expand Down
6 changes: 2 additions & 4 deletions examples/backends/plot_ot_batch.py
Original file line number Diff line number Diff line change
Expand Up @@ -152,17 +152,15 @@ def benchmark_naive(samples_source, samples_target):
start = perf_counter()
for i in range(n_problems):
M = ot.dist(samples_source[i], samples_target[i])
res = ot.solve(M, reg=reg, max_iter=max_iter, tol=tol, reg_type="entropy")
ot.solve(M, reg=reg, max_iter=max_iter, tol=tol, reg_type="entropy")
end = perf_counter()
return end - start


def benchmark_batch(samples_source, samples_target):
start = perf_counter()
M_batch = ot.dist_batch(samples_source, samples_target)
res_batch = ot.solve_batch(
M=M_batch, reg=reg, max_iter=max_iter, tol=tol, reg_type="entropy"
)
ot.solve_batch(M=M_batch, reg=reg, max_iter=max_iter, tol=tol, reg_type="entropy")
end = perf_counter()
return end - start

Expand Down
1 change: 0 additions & 1 deletion examples/barycenters/plot_solve_barycenter_variants.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,6 @@ def plot_list_res(

X = lst_res[i].X
list_P = [lst_res[i].list_res[k].plan for k in range(2)]
loss = lst_res[i].value

plot2D_samples_mat(x1, X, list_P[0])
plot2D_samples_mat(x2, X, list_P[1])
Expand Down
1 change: 0 additions & 1 deletion examples/gromov/plot_partial_fgw.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import pylab as pl
import networkx as nx
import math
from scipy.sparse.csgraph import shortest_path
import matplotlib.colors as mcol
from matplotlib import cm
from ot.gromov import (
Expand Down
2 changes: 1 addition & 1 deletion examples/others/plot_sgot.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
import numpy as np
import matplotlib.pyplot as plt

from ot.sgot import sgot_metric, sgot_cost_matrix
from ot.sgot import sgot_metric

from scipy.linalg import eig

Expand Down
1 change: 0 additions & 1 deletion examples/plot_bsp_ot.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import ot.utils
import ot.bsp
import numpy as np
import time
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

Expand Down
1 change: 0 additions & 1 deletion examples/plot_debias_sink_div.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
import matplotlib.pylab as pl
import ot
import ot.plot
from ot.datasets import make_1D_gauss as gauss

##############################################################################
# Generate data
Expand Down
1 change: 0 additions & 1 deletion examples/sliced-wasserstein/plot_uniortho_high_dim.py
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@
# Wasserstein in 3D") are. We use the same exact closed-form reference as
# below (a pure translation).

from ot.sliced import get_projections_spiral # noqa: E402 (kept local to this section)

d3 = 3
rng3 = np.random.RandomState(1)
Expand Down
14 changes: 8 additions & 6 deletions examples/unbalanced-partial/plot_UOT_sliced.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,10 @@ def generate_dataset(n_samples):

# %%

get_rot = lambda theta: np.array(
[[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]
)

def get_rot(theta):
return np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])


# visu parameters
nb_slices = 180 # 60
Expand Down Expand Up @@ -480,9 +481,10 @@ def plot_slices(

# %%

get_rot = lambda theta: np.array(
[[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]]
)

def get_rot(theta):
return np.array([[np.cos(theta), -np.sin(theta)], [np.sin(theta), np.cos(theta)]])


n_samples = 500
nb_outliers = 200
Expand Down
12 changes: 6 additions & 6 deletions ot/batch/_quadratic.py
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,7 @@ def solve_gromov_batch(
Gromov problem (when M is not provided). By default ``alpha=None``
corresponds to ``alpha=1`` for Gromov problem (``M==None``) and
``alpha=0.5`` for Fused Gromov-Wasserstein problem (``M!=None``)
epsilon : float, optional
reg : float, optional
Regularization parameter for proximal gradient descent. Default is 1e-2.
T_init : array-like, shape (B, n, m), optional
Initial transport plan. If None, it is initialized to uniform distribution.
Expand All @@ -584,11 +584,11 @@ def solve_gromov_batch(
Type of gradient computation, either or 'autodiff', 'envelope' or 'detach'. 'autodiff' provides gradients wrt all
outputs (`plan, value, value_linear`) but with important memory cost.
'envelope' provides gradients only for (`value, value_linear`)`. `detach`` is the fastest option but
provides no gradients. Default is 'detach'.
assume_inner_convergence : bool, optional
If True, assumes that the inner Bregman projection always converged i.e. the transport plan satisfies the marginal constraints.
This enables faster computations of the tensor product but might results in inaccurate results (e.g. negative values of the loss).
Default is True.
provides no gradients. Default is 'envelope'.
logits : bool, optional
For KL loss, whether Ca and Cb are logits (unnormalized log
probabilities). Default is None.

Returns
-------
res : OTResult()
Expand Down
6 changes: 3 additions & 3 deletions ot/bregman/_empirical.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ def empirical_sinkhorn(
samples weights in the source domain
b : array-like, shape (n_samples_b,)
samples weights in the target domain
numItermax : int, optional
numIterMax : int, optional
Max number of iterations
stopThr : float, optional
Stop threshold on error (>0)
Expand Down Expand Up @@ -359,7 +359,7 @@ def empirical_sinkhorn2(
samples weights in the source domain
b : array-like, shape (n_samples_b,)
samples weights in the target domain
numItermax : int, optional
numIterMax : int, optional
Max number of iterations
stopThr : float, optional
Stop threshold on error (>0)
Expand Down Expand Up @@ -598,7 +598,7 @@ def empirical_sinkhorn_divergence(
samples weights in the source domain
b : array-like, shape (n_samples_b,)
samples weights in the target domain
numItermax : int, optional
numIterMax : int, optional
Max number of iterations
stopThr : float, optional
Stop threshold on error (>0)
Expand Down
2 changes: 1 addition & 1 deletion ot/bregman/_geomloss.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ def get_sinkhorn_geomloss_lazytensor(
----------
X_a : array-like, shape (n_samples_a, dim)
samples in the source domain
X_torch: array-like, shape (n_samples_b, dim)
X_b : array-like, shape (n_samples_b, dim)
samples in the target domain
f : array-like, shape (n_samples_a,)
First dual potentials (log space)
Expand Down
6 changes: 3 additions & 3 deletions ot/bregman/_sinkhorn.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ def sinkhorn2(
of the regularization (and using warm start) sometimes leads to better
solutions. Note that the greedy version of the sinkhorn
:py:func:`ot.bregman.greenkhorn` can also lead to a speedup and the screening
version of the sinkhorn :py:func:`ot.bregman.screenkhorn` aim a providing a
version of the sinkhorn :py:func:`ot.bregman.screenkhorn` aims at providing a
fast approximation of the Sinkhorn problem. For use of GPU and gradient
computation with small number of iterations we strongly recommend the
:py:func:`ot.bregman.sinkhorn_log` solver that will no need to check for
Expand Down Expand Up @@ -1491,9 +1491,9 @@ def sinkhorn_epsilon_scaling(
dim_a = len(a)
dim_b = len(b)

# nrelative umerical precision with 64 bits
# relative numerical precision with 64 bits
numItermin = 35
numItermax = max(numItermin, numItermax) # ensure that last velue is exact
numItermax = max(numItermin, numItermax) # ensure that last value is exact

ii = 0
if log:
Expand Down
2 changes: 1 addition & 1 deletion ot/da.py
Original file line number Diff line number Diff line change
Expand Up @@ -889,7 +889,7 @@ class LinearTransport(BaseTransport):
Parameters
----------
reg : float,optional
regularization added to the daigonals of covariances (>0)
regularization added to the diagonals of covariances (>0)
bias: boolean, optional
estimate bias :math:`\mathbf{b}` else :math:`\mathbf{b} = 0` (default:True)
log : bool, optional
Expand Down
6 changes: 2 additions & 4 deletions ot/datasets.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,7 @@ def make_data_classif(dataset, n, nz=0.5, theta=0, p=0.5, random_state=None, **k
x[:, 1] += kwargs["bias"][1]

else:
x = np.array(0)
y = np.array(0)
print("unknown dataset")
raise ValueError("Unknown dataset '{}'".format(dataset))

return x, y.astype(int)

Expand All @@ -197,7 +195,7 @@ def make_gauss_hd(
p : int
dimension of the ambient space the data live in
dim : (int,int) or int
the intrinsic dimensions of the source and target Gaussian HD distriutions. If a single int the intrinsic dimension is assumed to be the same
the intrinsic dimensions of the source and target Gaussian HD distributions. If a single int the intrinsic dimension is assumed to be the same
m_diff : float
the shift in the first coordinate of the means of the Gaussian HD distributions, i.e. ms_0 and mt_0, respectively (see code)
a : (float, float)
Expand Down
2 changes: 1 addition & 1 deletion ot/dr.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ def ewca(
k : int, optional
Subspace dimension.
method : str, optional
Eather 'BCD' or 'MM' (Block Coordinate Descent or Majorization-Minimization).
Either 'BCD' or 'MM' (Block Coordinate Descent or Majorization-Minimization).
Prefer MM when d is large.
sinkhorn_method : str
Method used for the Sinkhorn solver, see :ref:`ot.bregman.sinkhorn` for more details.
Expand Down
13 changes: 6 additions & 7 deletions ot/gaussian.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@ def empirical_bures_wasserstein_mapping_hd(
samples in the source domain
xt : array-like (nt,p)
samples in the target domain
d_instrinsic : int or list of two ints
d_intrinsic : int or list of two ints
the intrinsic dimensions of the source and destination distribution, respectively. If d_intrinsic is int it is assumed the intrinsic dimension is the same
reg : float,optional
regularization added to the diagonals of covariances (null by default)
Expand Down Expand Up @@ -818,7 +818,7 @@ def empirical_bures_wasserstein_distance_hd(
samples in the source domain
xt : array-like (nt,p)
samples in the target domain
d_instrinsic : int or list of two ints
d_intrinsic : int or list of two ints
the intrinsic dimensions of the source and destination distribution, respectively. If d_intrinsic is int it is assumed the intrinsic dimension is the same
reg : float,optional
regularization added to the diagonals of covariances (null by default)
Expand Down Expand Up @@ -937,8 +937,6 @@ def bures_barycenter_fixpoint(
covariance of k distributions
weights : array-like (k), optional
weights for each distribution
method : str
method used for the solver, either 'fixed_point' or 'gradient_descent'
num_iter : int, optional
number of iteration for the fixed point algorithm
eps : float, optional
Expand Down Expand Up @@ -990,7 +988,10 @@ def bures_barycenter_fixpoint(
Cb = Cnew

if diff > eps:
print("Dit not converge.")
warnings.warn(
"Bures barycenter fixed point did not converge, consider "
"increasing num_iter or eps."
)

if log:
log = {}
Expand Down Expand Up @@ -1027,8 +1028,6 @@ def bures_barycenter_gradient_descent(
covariance of k distributions
weights : array-like (k), optional
weights for each distribution
method : str
method used for the solver, either 'fixed_point' or 'gradient_descent'
num_iter : int, optional
number of iteration for the fixed point algorithm
eps : float, optional
Expand Down
4 changes: 2 additions & 2 deletions ot/gnn/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def FGW_distance_to_templates(
Graph node features.
tplt_features : list of torch.Tensor, shape (n_templates, n_template_nodes, n_features)
List of the node features of the templates.
weights : torch.Tensor, shape (n_templates, n_template_nodes)
tplt_weights : torch.Tensor, shape (n_templates, n_template_nodes)
Weights on the nodes of the templates.
alpha : float, optional
Trade-off parameter (0 < alpha < 1).
Expand Down Expand Up @@ -243,7 +243,7 @@ def wasserstein_distance_to_templates(
Node features of the graph.
tplt_features : list of torch.Tensor, shape (n_templates, n_template_nodes, n_features)
List of the node features of the templates.
weights : torch.Tensor, shape (n_templates, n_template_nodes)
tplt_weights : torch.Tensor, shape (n_templates, n_template_nodes)
Weights on the nodes of the templates.
batch: torch.Tensor, optional
Batch vector which assigns each node to its graph.
Expand Down
6 changes: 3 additions & 3 deletions ot/gromov/_dictionary.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,8 @@ def gromov_wasserstein_dictionary_learning(
projection: str , optional
If 'nonnegative' and/or 'symmetric' is in projection, the corresponding projection will be performed at each stochastic update of the dictionary
Else the set of atoms is :math:`R^{nt * nt}`. Default is 'nonnegative_symmetric'
log: bool, optional
If set to True, losses evolution by batches and epochs are tracked. Default is False.
use_log: bool, optional
If set to True, losses evolution by batches and epochs are tracked. Default is True.
use_adam_optimizer: bool, optional
If set to True, adam optimizer with default settings is used as adaptative learning rate strategy.
Else perform SGD with fixed learning rate. Default is True.
Expand Down Expand Up @@ -670,7 +670,7 @@ def fused_gromov_wasserstein_dictionary_learning(
projection: str, optional
If 'nonnegative' and/or 'symmetric' is in projection, the corresponding projection will be performed at each stochastic update of the dictionary
Else the set of atoms is :math:`R^{nt * nt}`. Default is 'nonnegative_symmetric'
log: bool, optional
use_log: bool, optional
If set to True, losses evolution by batches and epochs are tracked. Default is False.
use_adam_optimizer: bool, optional
If set to True, adam optimizer with default settings is used as adaptative learning rate strategy.
Expand Down
2 changes: 1 addition & 1 deletion ot/gromov/_quantized.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ def quantized_fused_gromov_wasserstein_partitioned(
List of relations between representants and their associated samples in the target space.
list_p1 : list of npart1 arrays,
List of node distributions within each partition of the source space.
list_p : list of npart2 arrays,
list_p2 : list of npart2 arrays,
List of node distributions within each partition of the target space.
part1 : list of npart1 arrays, optional. Default is None.
List of arrays containing the indices of nodes in each partition of the source space.
Expand Down
14 changes: 8 additions & 6 deletions ot/gromov/_unbalanced.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,11 +203,13 @@ def fused_unbalanced_across_spaces_divergence(
# MAIN FUNCTION

if reg_type not in ["joint", "independent"]:
raise (NotImplementedError('Unknown reg_type="{}"'.format(reg_type)))
raise NotImplementedError('Unknown reg_type="{}"'.format(reg_type))
if divergence not in ["kl", "l2"]:
raise (NotImplementedError('Unknown divergence="{}"'.format(divergence)))
raise NotImplementedError('Unknown divergence="{}"'.format(divergence))
if unbalanced_solver not in ["sinkhorn", "sinkhorn_log", "mm", "lbfgsb"]:
raise (NotImplementedError('Unknown method="{}"'.format(unbalanced_solver)))
raise NotImplementedError(
'Unknown unbalanced_solver="{}"'.format(unbalanced_solver)
)

# hyperparameters
alpha_samp, alpha_feat = get_parameter_pair(alpha)
Expand Down Expand Up @@ -242,9 +244,9 @@ def fused_unbalanced_across_spaces_divergence(

arr = [X, Y]

for tuple in [duals_samp, duals_feat]:
if tuple is not None:
d1, d2 = duals_feat
for duals in [duals_samp, duals_feat]:
if duals is not None:
d1, d2 = duals
if d1 is not None:
arr.append(list_to_array(d1))
if d2 is not None:
Expand Down
Loading
Loading