From 217f2d01b3585d77a918f7cfbd7ee70459f959fd Mon Sep 17 00:00:00 2001 From: Alexandre Gramfort Date: Sun, 11 Oct 2026 14:50:28 +0200 Subject: [PATCH 1/4] Fix UCOOT partial init_duals crash, replace prints with errors/warnings, fix docstrings - Fix fused_unbalanced_across_spaces_divergence crashing when only one of the sample/feature dual pairs is passed in init_duals - Raise ValueError/ImportError instead of printing and returning None or dummy values (make_data_classif, solve_semi_dual_entropic, nearest_brenier_potential_*), and warn instead of print in bures_barycenter_fixpoint, regpath and entropic partial Wasserstein - Fix docstring parameters that do not match signatures, wrong defaults and typos - Fix file patterns of ruff hooks in .pre-commit-config.yaml --- .pre-commit-config.yaml | 6 +++--- README.md | 2 +- RELEASES.md | 3 +++ ot/batch/_quadratic.py | 12 ++++++------ ot/bregman/_empirical.py | 6 +++--- ot/bregman/_geomloss.py | 2 +- ot/bregman/_sinkhorn.py | 6 +++--- ot/da.py | 2 +- ot/datasets.py | 6 ++---- ot/dr.py | 2 +- ot/gaussian.py | 13 ++++++------- ot/gnn/_utils.py | 4 ++-- ot/gromov/_dictionary.py | 6 +++--- ot/gromov/_quantized.py | 2 +- ot/gromov/_unbalanced.py | 14 ++++++++------ ot/lowrank.py | 4 ++-- ot/lp/_barycenter_solvers.py | 2 -- ot/lp/_network_simplex.py | 4 ---- ot/mapping.py | 12 ++++-------- ot/partial/partial_solvers.py | 6 ++---- ot/regpath.py | 10 ++++++---- ot/sliced/_sliced_distances.py | 2 +- ot/smooth.py | 6 +++--- ot/solvers/_linear.py | 4 ++-- ot/stochastic.py | 13 +++++-------- ot/unbalanced/_sinkhorn.py | 3 --- ot/utils.py | 1 - test/test_datasets.py | 12 ++++++++++++ test/test_gaussian.py | 8 ++++++++ test/test_regpath.py | 15 +++++++++++++++ test/test_stochastic.py | 10 ++++++++++ test/test_ucoot.py | 22 ++++++++++++++++++++++ 32 files changed, 136 insertions(+), 84 deletions(-) create mode 100644 test/test_datasets.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index cd5a35594..9cd9203ff 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,5 @@ repos: - # Ruff skada + # Ruff - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.5.2 hooks: @@ -16,9 +16,9 @@ repos: # D103: missing docstring in public function # D400: docstring first line must end with period args: ["--ignore=D103,D400", "--fix"] - files: ^docs/|^examples/^test/ + files: ^docs/|^examples/|^test/ - id: ruff-format - files: ^ot/|^docs/|^examples/| + files: ^ot/|^docs/|^examples/ # Codespell - repo: https://github.com/codespell-project/codespell diff --git a/README.md b/README.md index 543e3869e..9b3fd3b21 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/RELEASES.md b/RELEASES.md index 860f2babb..bb2843286 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -29,6 +29,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 #XXX) +- 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 #XXX) +- Fix docstring errors (wrong or duplicated parameters, typos) and fix the file patterns of the ruff hooks in `.pre-commit-config.yaml` (PR #XXX) ## 0.9.7.post1 diff --git a/ot/batch/_quadratic.py b/ot/batch/_quadratic.py index ef512e298..052c46437 100644 --- a/ot/batch/_quadratic.py +++ b/ot/batch/_quadratic.py @@ -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. @@ -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() diff --git a/ot/bregman/_empirical.py b/ot/bregman/_empirical.py index 9eb7be6c9..ccc5ac25e 100644 --- a/ot/bregman/_empirical.py +++ b/ot/bregman/_empirical.py @@ -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) @@ -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) @@ -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) diff --git a/ot/bregman/_geomloss.py b/ot/bregman/_geomloss.py index d94bec0f9..66f5d78a2 100644 --- a/ot/bregman/_geomloss.py +++ b/ot/bregman/_geomloss.py @@ -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) diff --git a/ot/bregman/_sinkhorn.py b/ot/bregman/_sinkhorn.py index cf5efadfc..152112364 100644 --- a/ot/bregman/_sinkhorn.py +++ b/ot/bregman/_sinkhorn.py @@ -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 @@ -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: diff --git a/ot/da.py b/ot/da.py index 7e1a7491f..90c0f8de6 100644 --- a/ot/da.py +++ b/ot/da.py @@ -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 diff --git a/ot/datasets.py b/ot/datasets.py index 8aa65a398..c18d6b92d 100644 --- a/ot/datasets.py +++ b/ot/datasets.py @@ -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) @@ -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) diff --git a/ot/dr.py b/ot/dr.py index 9cfabff5f..d68c1e5f9 100644 --- a/ot/dr.py +++ b/ot/dr.py @@ -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. diff --git a/ot/gaussian.py b/ot/gaussian.py index 103d102d8..4d720e403 100644 --- a/ot/gaussian.py +++ b/ot/gaussian.py @@ -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) @@ -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) @@ -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 @@ -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 = {} @@ -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 diff --git a/ot/gnn/_utils.py b/ot/gnn/_utils.py index dc1342bf6..706fa321b 100644 --- a/ot/gnn/_utils.py +++ b/ot/gnn/_utils.py @@ -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). @@ -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. diff --git a/ot/gromov/_dictionary.py b/ot/gromov/_dictionary.py index 6a2b30764..750e247ec 100644 --- a/ot/gromov/_dictionary.py +++ b/ot/gromov/_dictionary.py @@ -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. @@ -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. diff --git a/ot/gromov/_quantized.py b/ot/gromov/_quantized.py index 13a9bd5d0..25422e27c 100644 --- a/ot/gromov/_quantized.py +++ b/ot/gromov/_quantized.py @@ -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. diff --git a/ot/gromov/_unbalanced.py b/ot/gromov/_unbalanced.py index 6971b6a58..092db4cc1 100644 --- a/ot/gromov/_unbalanced.py +++ b/ot/gromov/_unbalanced.py @@ -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) @@ -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: diff --git a/ot/lowrank.py b/ot/lowrank.py index fef964949..c6dcbc4e4 100644 --- a/ot/lowrank.py +++ b/ot/lowrank.py @@ -614,9 +614,9 @@ def sinkhorn_low_rank_kernel( Parameters ---------- - K_1 : array-like, shape (n_samples_a, dim_r) + K1 : array-like, shape (n_samples_a, dim_r) Left factor - K_2 : array-like, shape (n_samples_b, dim_r) + K2 : array-like, shape (n_samples_b, dim_r) Right factor a : array-like, shape (n_samples_a,) samples weights in the source domain diff --git a/ot/lp/_barycenter_solvers.py b/ot/lp/_barycenter_solvers.py index e7207068c..3efca42ef 100644 --- a/ot/lp/_barycenter_solvers.py +++ b/ot/lp/_barycenter_solvers.py @@ -54,8 +54,6 @@ def barycenter(A, M, weights=None, verbose=False, log=False, solver="highs-ipm") n training distributions a_i of size d M : np.ndarray (d,d) loss matrix for OT - reg : float - Regularization term >0 weights : np.ndarray (n,) Weights of each histogram a_i on the simplex (barycentric coordinates) verbose : bool, optional diff --git a/ot/lp/_network_simplex.py b/ot/lp/_network_simplex.py index 10d1e4066..a3f3d548d 100644 --- a/ot/lp/_network_simplex.py +++ b/ot/lp/_network_simplex.py @@ -130,10 +130,6 @@ def estimate_dual_null_weights(alpha0, beta0, a, b, M): Parameters ---------- - alpha0 : (ns,) numpy.ndarray, float64 - Source dual potential - beta0 : (nt,) numpy.ndarray, float64 - Target dual potential alpha0 : (ns,) numpy.ndarray, float64 Source dual potential beta0 : (nt,) numpy.ndarray, float64 diff --git a/ot/mapping.py b/ot/mapping.py index bb1a78e8a..955438de2 100644 --- a/ot/mapping.py +++ b/ot/mapping.py @@ -118,9 +118,8 @@ def nearest_brenier_potential_fit( """ try: import cvxpy as cvx - except ImportError: - print("Please install CVXPY to use this function") - return + except ImportError as e: + raise ImportError("Please install CVXPY to use this function") from e assert ( X.shape == V.shape ), f"point shape should be the same as value shape, yet {X.shape} != {V.shape}" @@ -277,8 +276,6 @@ def nearest_brenier_potential_predict_bounds( ---------- X : array-like (n, d) reference points used to compute the optimal values phi and G - X_classes : array-like (n,) - classes of the reference points phi : array-like (n,) optimal values of the potential at the points X G : array-like (n, d) @@ -326,9 +323,8 @@ def nearest_brenier_potential_predict_bounds( """ try: import cvxpy as cvx - except ImportError: - print("Please install CVXPY to use this function") - return + except ImportError as e: + raise ImportError("Please install CVXPY to use this function") from e nx = get_backend(X, phi, G, Y) X = to_numpy(X) phi = to_numpy(phi) diff --git a/ot/partial/partial_solvers.py b/ot/partial/partial_solvers.py index f6e1c65a6..1b99bf042 100755 --- a/ot/partial/partial_solvers.py +++ b/ot/partial/partial_solvers.py @@ -135,7 +135,7 @@ def partial_wasserstein_lagrange( return nx.zeros((len(a), len(b)), type_as=M) a0, b0, M0 = a, b, M - # convert to humpy + # convert to numpy a, b, M = nx.to_numpy(a, b, M) eps = 1e-20 @@ -628,7 +628,7 @@ def _entropic_partial_wasserstein_sinkhorn( q3 = q3 * K2prev / K if nx.any(nx.isnan(K)) or nx.any(nx.isinf(K)): - print("Warning: numerical errors at iteration", cpt) + warnings.warn("Numerical errors at iteration {}".format(cpt)) break if cpt % 10 == 0: err = nx.norm(Kprev - K) @@ -1559,8 +1559,6 @@ def partial_wasserstein_1d(x_a, x_b, n_transported_samples=None, p=1): number of samples to be transported p : float, optional power of the metric (default: 1) - log : bool, optional - record log if True Returns diff --git a/ot/regpath.py b/ot/regpath.py index aedc35b88..1dfd11526 100644 --- a/ot/regpath.py +++ b/ot/regpath.py @@ -6,6 +6,8 @@ # Author: Haoran Wu # License: MIT License +import warnings + import numpy as np import scipy.sparse as sp @@ -665,7 +667,7 @@ def fully_relaxed_path(a: np.array, b: np.array, C: np.array, reg=1e-4, itmax=50 n_iter += 1 if itmax <= n_iter: - print("maximum iteration has been reached !") + warnings.warn("Maximum number of iterations has been reached.") # correct the last solution and gamma if len(t_list) > 1: @@ -676,7 +678,7 @@ def fully_relaxed_path(a: np.array, b: np.array, C: np.array, reg=1e-4, itmax=50 gamma_list[-1] = reg else: gamma_list[-1] = reg - print("Regularization path does not exist !") + warnings.warn("Regularization path does not exist.") return t_list[-1], t_list, gamma_list @@ -820,7 +822,7 @@ def semi_relaxed_path(a: np.array, b: np.array, C: np.array, reg=1e-4, itmax=500 n_iter += 1 if itmax <= n_iter: - print("maximum iteration has been reached !") + warnings.warn("Maximum number of iterations has been reached.") # correct the last solution and gamma if len(t_list) > 1: @@ -831,7 +833,7 @@ def semi_relaxed_path(a: np.array, b: np.array, C: np.array, reg=1e-4, itmax=500 gamma_list[-1] = reg else: gamma_list[-1] = reg - print("Regularization path does not exist !") + warnings.warn("Regularization path does not exist.") return t_list[-1], t_list, gamma_list diff --git a/ot/sliced/_sliced_distances.py b/ot/sliced/_sliced_distances.py index 007733717..33930fcdc 100644 --- a/ot/sliced/_sliced_distances.py +++ b/ot/sliced/_sliced_distances.py @@ -244,7 +244,7 @@ def max_sliced_wasserstein_distance( samples weights in the target domain n_projections : int, optional Number of projections used for the Monte-Carlo approximation - p: float, optional = + p : float, optional Power p used for computing the sliced Wasserstein projections: shape (dim, n_projections), optional Projection matrix (n_projections and seed are not used in this case) diff --git a/ot/smooth.py b/ot/smooth.py index 9fbac6c73..5d8dc81e2 100644 --- a/ot/smooth.py +++ b/ot/smooth.py @@ -588,7 +588,7 @@ def smooth_ot_dual( See Also -------- ot.lp.emd : Unregularized OT - ot.sinhorn : Entropic regularized OT + ot.sinkhorn : Entropic regularized OT ot.optim.cg : General regularized OT """ @@ -607,7 +607,7 @@ def smooth_ot_dual( raise NotImplementedError("Unknown regularization") a0, b0, M0 = a, b, M - # convert to humpy + # convert to numpy a, b, M = nx.to_numpy(a, b, M) # solve dual @@ -722,7 +722,7 @@ def smooth_ot_semi_dual( See Also -------- ot.lp.emd : Unregularized OT - ot.sinhorn : Entropic regularized OT + ot.sinkhorn : Entropic regularized OT ot.optim.cg : General regularized OT """ diff --git a/ot/solvers/_linear.py b/ot/solvers/_linear.py index ad27c6e91..32d21ec4b 100644 --- a/ot/solvers/_linear.py +++ b/ot/solvers/_linear.py @@ -97,7 +97,7 @@ def solve( a : array-like, shape (dim_a,), optional Samples weights in the source domain (default is uniform) b : array-like, shape (dim_b,), optional - Samples weights in the source domain (default is uniform) + Samples weights in the target domain (default is uniform) reg : float, optional Regularization weight :math:`\lambda_r`, by default None (no reg., exact OT) @@ -684,7 +684,7 @@ def solve_sample( a : array-like, shape (dim_a,), optional Samples weights in the source domain (default is uniform) b : array-like, shape (dim_b,), optional - Samples weights in the source domain (default is uniform) + Samples weights in the target domain (default is uniform) reg : float, optional Regularization weight :math:`\lambda_r`, by default None (no reg., exact OT) diff --git a/ot/stochastic.py b/ot/stochastic.py index 6df3ad771..9628a2e34 100644 --- a/ot/stochastic.py +++ b/ot/stochastic.py @@ -49,7 +49,7 @@ def coordinate_grad_semi_dual(b, M, reg, beta, i): Cost matrix. reg : float Regularization term > 0. - v : ndarray, shape (nt,) + beta : ndarray, shape (nt,) Dual variable. i : int Picked number `i`. @@ -253,7 +253,7 @@ def c_transform_entropic(b, M, reg, beta): Cost matrix reg : float Regularization term > 0 - v : ndarray, shape (nt,) + beta : ndarray, shape (nt,) Dual variable. Returns @@ -325,10 +325,6 @@ def solve_semi_dual_entropic( number of iteration lr : float learning rate - n_source : int - size of the source measure - n_target : int - size of the target measure log : bool, optional record log if True @@ -353,8 +349,9 @@ def solve_semi_dual_entropic( elif method.lower() == "asgd": opt_beta = averaged_sgd_entropic_transport(a, b, M, reg, numItermax, lr) else: - print("Please, select your method between SAG and ASGD") - return None + raise ValueError( + "Unknown method '{}', must be 'SAG' or 'ASGD'".format(method) + ) opt_alpha = c_transform_entropic(b, M, reg, opt_beta) pi = ( diff --git a/ot/unbalanced/_sinkhorn.py b/ot/unbalanced/_sinkhorn.py index d338e1652..45e6fc527 100644 --- a/ot/unbalanced/_sinkhorn.py +++ b/ot/unbalanced/_sinkhorn.py @@ -886,9 +886,6 @@ def sinkhorn_stabilized_unbalanced( :math:`\mathrm{reg_{m}}=(scalar, float("inf"))`. If :math:`\mathrm{reg_{m}}` is an array, it must have the same backend as input arrays `(a, b, M)`. - method : str - method used for the solver either 'sinkhorn', 'sinkhorn_stabilized' or - 'sinkhorn_reg_scaling', see those function for specific parameters reg_type : string, optional Regularizer term. Can take two values: diff --git a/ot/utils.py b/ot/utils.py index 8a40515b1..5f4a5b85a 100644 --- a/ot/utils.py +++ b/ot/utils.py @@ -913,7 +913,6 @@ def get_parameter_pair(parameter): Parameters ---------- parameter : float or indexable object - nx : backend object Returns ------- diff --git a/test/test_datasets.py b/test/test_datasets.py new file mode 100644 index 000000000..3e7a31084 --- /dev/null +++ b/test/test_datasets.py @@ -0,0 +1,12 @@ +"""Tests for module datasets""" + +# License: MIT License + +import pytest + +from ot.datasets import make_data_classif + + +def test_make_data_classif_unknown_dataset(): + with pytest.raises(ValueError, match="Unknown dataset"): + make_data_classif("not_a_dataset", 10) diff --git a/test/test_gaussian.py b/test/test_gaussian.py index 8beadea98..3199e2403 100644 --- a/test/test_gaussian.py +++ b/test/test_gaussian.py @@ -664,3 +664,11 @@ def test_gaussian_gromov_wasserstein_mapping(nx, d_target): if d_target >= 2: np.testing.assert_allclose(Cs, Ctt) + + +def test_bures_barycenter_fixpoint_no_convergence_warning(): + rng = np.random.RandomState(0) + A = rng.randn(3, 4, 4) + C = A @ A.transpose(0, 2, 1) + np.eye(4)[None] + with pytest.warns(UserWarning, match="did not converge"): + ot.gaussian.bures_barycenter_fixpoint(C, num_iter=1) diff --git a/test/test_regpath.py b/test/test_regpath.py index 95d8d9374..1a9c0b063 100644 --- a/test/test_regpath.py +++ b/test/test_regpath.py @@ -5,6 +5,7 @@ # License: MIT License import numpy as np +import pytest import ot @@ -58,3 +59,17 @@ def test_semi_relaxed_path(): G = t.reshape((n_source, n_target)) np.testing.assert_allclose(a, G.sum(1), atol=1e-05) np.testing.assert_allclose(b, G.sum(0), atol=1e-10) + + +def test_regularization_path_max_iter_warning(): + rng = np.random.RandomState(0) + xs = rng.randn(10, 2) + xt = rng.randn(8, 2) + a, b = ot.utils.unif(10), ot.utils.unif(8) + M = ot.dist(xs, xt) + M /= M.max() + for semi_relaxed in [False, True]: + with pytest.warns(UserWarning, match="Maximum number of iterations"): + ot.regpath.regularization_path( + a, b, M, reg=1e-8, semi_relaxed=semi_relaxed, itmax=2 + ) diff --git a/test/test_stochastic.py b/test/test_stochastic.py index 911d52b92..510670eef 100644 --- a/test/test_stochastic.py +++ b/test/test_stochastic.py @@ -14,6 +14,7 @@ # License: MIT License import numpy as np +import pytest import ot @@ -311,3 +312,12 @@ def metric(x, y): assert np.all(nx.to_numpy(G2) >= 0) assert G2.shape[0] == 50 assert G2.shape[1] == 40 + + +def test_solve_semi_dual_entropic_invalid_method(): + rng = np.random.RandomState(0) + x = rng.randn(5, 2) + a, b = ot.utils.unif(5), ot.utils.unif(5) + M = ot.dist(x, x) + with pytest.raises(ValueError, match="Unknown method"): + ot.stochastic.solve_semi_dual_entropic(a, b, M, 1, "bad_method") diff --git a/test/test_ucoot.py b/test/test_ucoot.py index a9e3d6dd8..f35ebd307 100644 --- a/test/test_ucoot.py +++ b/test/test_ucoot.py @@ -1426,3 +1426,25 @@ def ucoot_solver_nx(unbalanced_solver): np.testing.assert_raises(NotImplementedError, ucoot_solver, "solver_not_existed") np.testing.assert_raises(NotImplementedError, ucoot_solver_nx, "solver_not_existed") + + +def test_partial_init_duals(): + # init_duals with only one of the two dual pairs provided + n_samples = 5 + rng = np.random.RandomState(0) + xs = rng.randn(n_samples, 2) + xt = xs[::-1].copy() + + init_duals_samp = (np.zeros(n_samples), np.zeros(n_samples)) + init_duals_feat = (np.zeros(2), np.zeros(2)) + + kwargs = dict( + X=xs, Y=xt, reg_marginals=10, epsilon=1, unbalanced_solver="sinkhorn" + ) + pi_samp, pi_feat = unbalanced_co_optimal_transport(**kwargs) + for init_duals in [(init_duals_samp, None), (None, init_duals_feat)]: + pi_samp2, pi_feat2 = unbalanced_co_optimal_transport( + init_duals=init_duals, **kwargs + ) + np.testing.assert_allclose(pi_samp, pi_samp2, atol=1e-6) + np.testing.assert_allclose(pi_feat, pi_feat2, atol=1e-6) From d1b97ce5341268bcafd87937aaeec7bf85094544 Mon Sep 17 00:00:00 2001 From: Alexandre Gramfort Date: Sun, 11 Oct 2026 17:42:56 +0200 Subject: [PATCH 2/4] Fix ruff errors in tests now checked by the fixed pre-commit hook --- ot/stochastic.py | 4 +--- test/batch/test_solve_batch.py | 2 +- test/batch/test_solve_gromov_batch.py | 3 +-- test/sliced/test_sliced_distances.py | 2 +- test/sliced/test_sliced_plans.py | 3 +-- test/sliced/test_spherical_sliced.py | 3 +-- test/test_backend.py | 8 +++++--- test/test_bspot.py | 4 +--- test/test_ot.py | 1 - test/test_sgot.py | 4 ++-- test/test_solvers.py | 8 ++++---- test/test_ucoot.py | 4 +--- 12 files changed, 19 insertions(+), 27 deletions(-) diff --git a/ot/stochastic.py b/ot/stochastic.py index 9628a2e34..f3f6b77af 100644 --- a/ot/stochastic.py +++ b/ot/stochastic.py @@ -349,9 +349,7 @@ def solve_semi_dual_entropic( elif method.lower() == "asgd": opt_beta = averaged_sgd_entropic_transport(a, b, M, reg, numItermax, lr) else: - raise ValueError( - "Unknown method '{}', must be 'SAG' or 'ASGD'".format(method) - ) + raise ValueError("Unknown method '{}', must be 'SAG' or 'ASGD'".format(method)) opt_alpha = c_transform_entropic(b, M, reg, opt_beta) pi = ( diff --git a/test/batch/test_solve_batch.py b/test/batch/test_solve_batch.py index ff2288e41..14cde06ee 100644 --- a/test/batch/test_solve_batch.py +++ b/test/batch/test_solve_batch.py @@ -170,7 +170,7 @@ def test_gradients_torch(grad, method): loss = res.value_linear.sum() loss_plan = res.plan.sum() if grad == "detach": - assert loss.grad == None + assert loss.grad is None elif grad == "envelope": loss.backward() assert X.grad is not None diff --git a/test/batch/test_solve_gromov_batch.py b/test/batch/test_solve_gromov_batch.py index d59c89d89..eb92fa8dd 100644 --- a/test/batch/test_solve_gromov_batch.py +++ b/test/batch/test_solve_gromov_batch.py @@ -20,7 +20,6 @@ from itertools import product from ot.backend import torch from ot.batch._quadratic import ( - tensor_batch, div_between_product_batch, loss_quadratic_samples_batch, ) @@ -127,7 +126,7 @@ def test_gradients_torch(grad): loss = res.value.sum() loss_plan = res.plan.sum() if grad == "detach": - assert loss.grad == None + assert loss.grad is None elif grad == "envelope": loss.backward() assert C.grad is not None diff --git a/test/sliced/test_sliced_distances.py b/test/sliced/test_sliced_distances.py index b9cc91d4c..73557641d 100644 --- a/test/sliced/test_sliced_distances.py +++ b/test/sliced/test_sliced_distances.py @@ -16,7 +16,7 @@ get_projections_spiral, get_random_orthogonal_directions, ) -from ot.backend import tf, torch +from ot.backend import tf def test_env(): diff --git a/test/sliced/test_sliced_plans.py b/test/sliced/test_sliced_plans.py index eb12e89ff..d21647144 100644 --- a/test/sliced/test_sliced_plans.py +++ b/test/sliced/test_sliced_plans.py @@ -11,8 +11,7 @@ import pytest import ot -from ot.sliced import get_random_projections -from ot.backend import tf, torch +from ot.backend import torch def test_sliced_permutations(): diff --git a/test/sliced/test_spherical_sliced.py b/test/sliced/test_spherical_sliced.py index f2543b864..5b1decf7c 100644 --- a/test/sliced/test_spherical_sliced.py +++ b/test/sliced/test_spherical_sliced.py @@ -9,8 +9,7 @@ import pytest import ot -from ot.sliced import get_random_projections -from ot.backend import tf, torch +from ot.backend import torch def test_projections_stiefel(): diff --git a/test/test_backend.py b/test/test_backend.py index 5a4570578..e5b5ebd94 100644 --- a/test/test_backend.py +++ b/test/test_backend.py @@ -143,7 +143,6 @@ def test_empty_backend(): rnd = np.random.RandomState(0) M = rnd.randn(10, 3) v = rnd.randn(3) - inds = rnd.randint(10) nx = ot.backend.Backend() @@ -849,7 +848,7 @@ def test_random_backends(nx): assert np.allclose(M3, M4) with pytest.raises(ValueError, match="size must be"): - res = nx.randperm(size=[5, 12]) + nx.randperm(size=[5, 12]) def test_numpy_backend_seed_random_state(): @@ -931,7 +930,10 @@ def test_sqrtm_backward_torch(): A = torch.randn(d, d, dtype=torch.float64, device="cpu") A = A @ A.T A.requires_grad_(True) - func = lambda x: nx.sqrtm(x).sum() + + def func(x): + return nx.sqrtm(x).sum() + assert torch.autograd.gradcheck(func, (A,), atol=1e-4, rtol=1e-4) diff --git a/test/test_bspot.py b/test/test_bspot.py index 024292cee..862923e12 100644 --- a/test/test_bspot.py +++ b/test/test_bspot.py @@ -8,6 +8,7 @@ import ot.bsp import numpy as np import pytest +from ot.backend import torch def test_bsp_ot_exact_identity(): @@ -107,9 +108,6 @@ def test_bsp_ot_relative_error(): ) -from ot.backend import torch - - @pytest.mark.skipif(not torch, reason="PyTorch not installed") def test_bsp_ot_torch_backend(): # test that bsp-ot works with torch tensors diff --git a/test/test_ot.py b/test/test_ot.py index 8bbd9355d..9b4c9545a 100644 --- a/test/test_ot.py +++ b/test/test_ot.py @@ -1309,7 +1309,6 @@ def test_emd_sparse_warmstart(): np.testing.assert_allclose(G_warm.sum(0), b, atol=1e-7) # Test sparse solver directly with warmstart via COO format - import scipy.sparse as sp from ot.lp.emd_wrap import emd_c_sparse row_indices = np.repeat(np.arange(n), n).astype(np.uint64) diff --git a/test/test_sgot.py b/test/test_sgot.py index f63483bf7..620d4b1ec 100644 --- a/test/test_sgot.py +++ b/test/test_sgot.py @@ -77,8 +77,8 @@ def test_eigenvalue_cost_matrix_simple(): def test_delta_matrix_1d_identity(): r = 4 - I = np.eye(r, dtype=complex) - delta = _delta_matrix_1d(I, I, I, I) + eye = np.eye(r, dtype=complex) + delta = _delta_matrix_1d(eye, eye, eye, eye) np.testing.assert_allclose(delta, np.eye(r), atol=1e-6) diff --git a/test/test_solvers.py b/test/test_solvers.py index 6cc8137cc..ba16a07a3 100644 --- a/test/test_solvers.py +++ b/test/test_solvers.py @@ -846,8 +846,8 @@ def test_zero_mass_solvers(): res = ot.solve_sample(x, y, a, b) res_reg = ot.solve_sample(x, y, a, b, reg=1.0) - assert np.isnan(res.value) == False - assert np.isnan(res_reg.value) == False + assert not np.isnan(res.value) + assert not np.isnan(res_reg.value) @pytest.skip_backend("tf", reason="Not implemented for tf backend") @@ -1211,7 +1211,7 @@ def custom_metric(X_b, X_a): ValueError, match="ground_bary must be provided in kwargs for true_fixed_point method with callable metrics", ): - sol_fixed_point = ot.solve_bary_sample( + ot.solve_bary_sample( X_listb, n, a_list=a_listb, @@ -1225,7 +1225,7 @@ def custom_metric(X_b, X_a): AssertionError, match=f"Backend {str(nx)} is not compatible with ground_bary=None, it must be provided if not using PyTorch backend", ): - sol_wrong_backend = ot.solve_bary_sample( + ot.solve_bary_sample( X_listb, n, a_list=a_listb, diff --git a/test/test_ucoot.py b/test/test_ucoot.py index f35ebd307..b90aac1ec 100644 --- a/test/test_ucoot.py +++ b/test/test_ucoot.py @@ -1438,9 +1438,7 @@ def test_partial_init_duals(): init_duals_samp = (np.zeros(n_samples), np.zeros(n_samples)) init_duals_feat = (np.zeros(2), np.zeros(2)) - kwargs = dict( - X=xs, Y=xt, reg_marginals=10, epsilon=1, unbalanced_solver="sinkhorn" - ) + kwargs = dict(X=xs, Y=xt, reg_marginals=10, epsilon=1, unbalanced_solver="sinkhorn") pi_samp, pi_feat = unbalanced_co_optimal_transport(**kwargs) for init_duals in [(init_duals_samp, None), (None, init_duals_feat)]: pi_samp2, pi_feat2 = unbalanced_co_optimal_transport( From 1e8d6adc25599e3d3850b9d37cc610f90ca49eb3 Mon Sep 17 00:00:00 2001 From: Alexandre Gramfort Date: Sun, 11 Oct 2026 17:43:24 +0200 Subject: [PATCH 3/4] Add PR number to RELEASES.md --- RELEASES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index bb2843286..6fd321d41 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -29,9 +29,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 #XXX) -- 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 #XXX) -- Fix docstring errors (wrong or duplicated parameters, typos) and fix the file patterns of the ruff hooks in `.pre-commit-config.yaml` (PR #XXX) +- 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 From d12c5470df72fd2d9636747bdc9725cedb47a632 Mon Sep 17 00:00:00 2001 From: Alexandre Gramfort Date: Sun, 11 Oct 2026 17:48:36 +0200 Subject: [PATCH 4/4] Fix ruff errors in examples now checked by the fixed pre-commit hook Ignore E402 for docs/examples/tests since sphinx-gallery examples import modules in later sections on purpose. --- .pre-commit-config.yaml | 3 ++- examples/backends/plot_gradient_descent.py | 1 - examples/backends/plot_ot_batch.py | 6 ++---- .../barycenters/plot_solve_barycenter_variants.py | 1 - examples/gromov/plot_partial_fgw.py | 1 - examples/others/plot_sgot.py | 2 +- examples/plot_bsp_ot.py | 1 - examples/plot_debias_sink_div.py | 1 - .../sliced-wasserstein/plot_uniortho_high_dim.py | 1 - examples/unbalanced-partial/plot_UOT_sliced.py | 14 ++++++++------ 10 files changed, 13 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9cd9203ff..01dc0cb39 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,8 @@ 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"] + # 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/ diff --git a/examples/backends/plot_gradient_descent.py b/examples/backends/plot_gradient_descent.py index 95190f267..fdf7f124a 100644 --- a/examples/backends/plot_gradient_descent.py +++ b/examples/backends/plot_gradient_descent.py @@ -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 diff --git a/examples/backends/plot_ot_batch.py b/examples/backends/plot_ot_batch.py index 54573c5a4..624bb6573 100644 --- a/examples/backends/plot_ot_batch.py +++ b/examples/backends/plot_ot_batch.py @@ -152,7 +152,7 @@ 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 @@ -160,9 +160,7 @@ def benchmark_naive(samples_source, samples_target): 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 diff --git a/examples/barycenters/plot_solve_barycenter_variants.py b/examples/barycenters/plot_solve_barycenter_variants.py index f19dd4ac4..dfaf37266 100644 --- a/examples/barycenters/plot_solve_barycenter_variants.py +++ b/examples/barycenters/plot_solve_barycenter_variants.py @@ -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]) diff --git a/examples/gromov/plot_partial_fgw.py b/examples/gromov/plot_partial_fgw.py index ff2c161f4..f081b21de 100644 --- a/examples/gromov/plot_partial_fgw.py +++ b/examples/gromov/plot_partial_fgw.py @@ -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 ( diff --git a/examples/others/plot_sgot.py b/examples/others/plot_sgot.py index 4dc3b6a6e..c854447a9 100644 --- a/examples/others/plot_sgot.py +++ b/examples/others/plot_sgot.py @@ -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 diff --git a/examples/plot_bsp_ot.py b/examples/plot_bsp_ot.py index deaa5c39b..9ccf5e2b2 100644 --- a/examples/plot_bsp_ot.py +++ b/examples/plot_bsp_ot.py @@ -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 diff --git a/examples/plot_debias_sink_div.py b/examples/plot_debias_sink_div.py index d6f44bafd..c850f4501 100644 --- a/examples/plot_debias_sink_div.py +++ b/examples/plot_debias_sink_div.py @@ -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 diff --git a/examples/sliced-wasserstein/plot_uniortho_high_dim.py b/examples/sliced-wasserstein/plot_uniortho_high_dim.py index d3b7b726f..b027b5e5d 100644 --- a/examples/sliced-wasserstein/plot_uniortho_high_dim.py +++ b/examples/sliced-wasserstein/plot_uniortho_high_dim.py @@ -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) diff --git a/examples/unbalanced-partial/plot_UOT_sliced.py b/examples/unbalanced-partial/plot_UOT_sliced.py index d66b34681..474ebffa4 100644 --- a/examples/unbalanced-partial/plot_UOT_sliced.py +++ b/examples/unbalanced-partial/plot_UOT_sliced.py @@ -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 @@ -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