This release contains several bug fixes and performance improvements, as well as updates to the documentation and examples and tests. We provide below a summary of the main new features and closed issues.
New exact OT solvers. This new release adds many new variants and updates for the exact OT solver. First a new lazy exact OT solver that re-computes distances on-the-fly from coordinates, reducing memory usage from O(n×m) to O(n+m) ( available in ot.solve_sample with lazy=True)). Another major feature is the addition of a warmstart feature for the EMD solver, allowing users to provide initial potentials to speed up convergence (with init_potentials in ot.solve and ot.solve_sample). Finally the release also include a sparse solver and ot.solve now accept sparse cost matrices and provides an OT plan whose support is included in the support of the cost matrix. All implementations are backend-agnostic and preserve gradient computation for automatic differentiation (but are solved on CPU).
The computational time for different solvers (on a laptop CPU) are shown below:
| n | 100 | 500 | 1000 | 5000 |
|---|---|---|---|---|
| Solve | 7.259e-04 | 1.501e-02 | 1.055e-01 | 2.343e+00 |
| Solve Warm Start | 6.970e-04 | 5.413e-03 | 2.291e-02 | 6.036e-01 |
| Solve Lazy | 1.280e-03 | 3.098e-02 | 1.421e-01 | 3.377e+00 |
| Solve Sparse 10% | 3.453e-04 | 6.716e-04 | 1.535e-03 | 3.534e-02 |
BSPOT solver. A new solver relying on Binary Space Partitioning (BSP) has been added to compute sparse transport plans between discrete measures in loglinear time which allows for very large problems to be solved. The release also includes a new ot.unbalanced.uot_1d solver with a Frank-Wolfe solver for unbalanced optimal transport in 1D.
Sliced OT pans Finally we now have a sliced OT plan solver that can be used to compute sliced transport plans (min-pivot sliced and expected sliced) between two measures.
OT between dynamical systems. A novel Spectral-Grassmann Wasserstein metric for operator representations of dynamical systems has been implemented in ot.sgot.
Unified API for barycenter solvers in ot.solve_bary_sample. A new free support solver for barycenter solvers has been added in ot.solve_bary_sample. You can see examples of how to use it here.
OT between high dimensional Gaussian distributions. New methods to compute the linear transport map and the related 2-Wasserstein distance between high-dimensional (HD) Gaussian distributions have been added in ot.gaussian.bures_wasserstein_mapping_hd and ot.gaussian.bures_wasserstein_distance_hd, respectively with empirical versions that estimate the distance from sample.
Batched solver for exact OT. The batch implementations have also been improved and you can now solve exact OT problems in parallel on CPU or GPU using the new proximal point solver in functions ot.solve_batch and ot.solve_sample_batch when reg=0 or no provided. A new batch loss for Fused unbalanced Gromov-Wasserstein is also now available in ot.batch.
New methods in unified API ot.solve_sample. The unified API function
ot.solve_sample has also been updated to allows solving of specific problems such as BSP-OT, distance between high-dimensional (HD) Gaussian distributions and sliced and max-sliced distances.
Data normalization for sliced and ot.solve_sample solvers. The release also includes tools for data normalization and scaling which can not be used in sliced Wasserstein distance computations. A simple normalization class ot.utils.DataScaler has been added and supports all backends for 'standard', 'minmax', and 'l2' methods. Finally an optional scaler parameter has been added to ot.sliced_wasserstein_distance, ot.max_sliced_wasserstein_distance and ot.solve_sample.
New features
- Refactor lazy EMD network simplex storage to avoid dense per-arc cost,
endpoint, flow, and state storage where possible, and return sparse lazy
transport plans instead of materializing dense plans internally (PR #813) - Removed OpenMP implementation in Simplex since no time gains are observed #820
- Add sliced transport plans (min-pivot sliced and expected sliced) solvers (PR #767)
- Add lazy EMD solver with on-the-fly distance computation from coordinates (PR #788)
- Add Warmstart feature to the EMD solver for existing potentials (PR #793)
- Add Warmstart potentials feature to the EMD solver for lazy and sparse solver (PR #795)
- Faster init and result retrieval for EMD solver (PR #796)
- Migrate backend from deprecated
scipy.sparse.coo_matrixto modernscipy.sparse.coo_array(PR #782) - Geomloss function now handles both scalar and slice indices for i and j (PR #785)
- Add support for sparse cost matrices in EMD solver (PR #778, Issue #397)
- Add "BSP-OT: Sparse transport plans between discrete measures in loglinear time" (PR #768)
- Added UOT1D with Frank-Wolfe in
ot.unbalanced.uot_1d(PR #765) - Add Sliced UOT and Unbalanced Sliced OT in
ot/unbalanced/_sliced.py(PR #765) - Add
ot.utils.DataScalerclass for backend-aware joint normalization of input distributions, with sklearn-compatiblefit/transform/fit_transformAPI and support for'standard','minmax', and'l2'methods (PR #808) - Add
ot.utils.apply_scalerhelper that dispatches preprocessing to a scaler object,
a callable, or a no-op (PR #808) - Add optional
scalerparameter tosliced_wasserstein_distanceandmax_sliced_wasserstein_distance(PR #808) - Add SGD based semi-discrete OT solver in
ot.semidiscreteand a gallery example. (PR #812) - Add a numerically stable log-domain solver for entropic partial Wasserstein, selectable via the new
methodparameter ofentropic_partial_wasserstein(method='sinkhorn_log') or directly throughentropic_partial_wasserstein_logscale(Issue #723) - Add cost functions between linear operators following A Spectral-Grassmann Wasserstein metric for operator representations of dynamical systems, implemented in
ot.sgot(PR #792, PR #830) - Add batch FUGW loss to
ot.batchand fix issues in some default parameters in the batch module (PR #775) - Wrapper for barycenter solvers with free support
ot.solvers.bary_free_support(PR #730) - Build wheels on ubuntu ARM to avoid QEMU emulation (PR #818)
- Add new methods to compute the linear transport map and the related 2-Wasserstein distance betweeen high-dimensional (HD) Gaussian distributions as described in [88], implemented in
ot.gaussian.bures_wasserstein_mapping_hdandot.gaussian.bures_wasserstein_distance_hd, respectively. Two additional methods estimate the same quantities from the source and destination observed data and are implemented inot.gaussian.empirical_bures_wasserstein_mapping_hdandot.gaussian.empirical_bures_wasserstein_distance_hd, respectively (PR #814) - Update the geomloss wrapper to the new version and API (PR #826)
- Fix docstrings for
lowrank_gromov_wasserstein_samplesandlowrank_sinkhorn(PR #823) - Reorganize all tests per backend (PR #828)
- Implemented batch proximal point solver for OT problems
ot.batch.proximal_bregman_log_plan_batchfunction and updated wrapper functionsot.batch.solve_batchandot.batch.solve_sample_batch(PR #832) - Implement debiased OT solvers in
ot.solve_sample.
Closed issues
- Fix label-aware cost correction in
ot.datransport classes: the large cost was applied to unlabeled pairs instead of labeled pairs with different labels, soys/ythad no effect when all labels were known (PR #833, Issue #664) - Mitigate NaN regime of
entropic_partial_wassersteinat smallregvia a new log-domain solver, reachable withentropic_partial_wasserstein(..., method='sinkhorn_log')(Issue #723; the defaultmethod='sinkhorn'path is unchanged — callers opt into the log-domain variant) - Fix NumPy 2.x compatibility in Brenier potential bounds (PR #788)
- Fix MSVC Windows build by removing restrict keyword (PR #788)
- Fix O(n³) performance bottleneck in sparse bipartite graph arc iteration (PR #785)
- Fix deprecated JAX function in
ot.backend.JaxBackend(PR #771, Issue #770) - Add test for build from source (PR #772, Issue #764)
- Fix device for batch Ot solver in
ot.batch(PR #784, Issue #783) - Fix openmp flags on macOS (PR #789)
- Clean documentation (PR #787)
- Fix code coverage (PR #791)
- Fix test of the version of jax in
ot.backend(PR #794) - Reverting the openmp fix on macOS (PR #789) for macOS (PR #797)
- Align documentation build dependencies and doc extras (PR #801)
- Debug Debug linux test core dump (PR #815)
- Fix entropic regularization in
gcg(PR #817, Issue #758) - Fix documentation build on master with submodules (PR #818)
- Fix failing test for unbalanced solver with generic regularization (PR #824)
- Fix reference number error introduced in PR #767 (PR #819)
- Fix docstrings for
lowrank_gromov_wasserstein_samplesandlowrank_sinkhorn(PR #823) - Update sgot cost function and example (PR #830)
All Pull Requests
- Fix Manifest and change numbering for post release by @rflamary in #763
- [WIP] Sliced OT Plans by @eloitanguy in #757
- Revert "[WIP] Sliced OT Plans" by @rflamary in #766
- [MRG] Fix JAX deprecation by @rflamary in #771
- [MRG] Add action to test build from source by @rflamary in #772
- [MRG] Update docstring to include 'a' parameter by @caph1993 in #769
- [FIX] unstable TorchBackend.sqrtm() around repeated eigenvalues by @samuelbx in #774
- [MRG] Sparse emd implementation by @nathanneike in #778
- Fix device for minibatch OT and test out of space by @rflamary in #784
- [MRG] Replaced coo_matrix with coo_array better compatability and added tes… by @nathanneike in #782
- [FIX] Sparse initial pivot by @nathanneike in #785
- Add lazy EMD solver with O(n) memory requirement by @nathanneike in #788
- [MRG] Detecting openmp flags on macOS (with libomp from brew) by @dcoeurjo in #789
- [DOC] Improve documentation rendering by @qbarthelemy in #787
- [MRG] Fix coverage by @rflamary in #791
- [MRG] Test Jax version in backend by @clbonet in #794
- [MRG] Warmstart with dual potentials for exact OT solver by @nathanneike in #793
- [MRG] Warmstart for exact sparse and lazy solvers by @nathanneike in #795
- [MRG] Reverting openmp compilation flags for macOS by @dcoeurjo in #797
- [MRG] Optimize simplex setup by @nathanneike in #796
- Fix Wasserstein distance exponent in user guide formula by @Pchambet in #798
- [MRG] Slicing UOT by @clbonet in #765
- Docs: add documentation dependencies by @osheasienna in #801
- [MRG] Debug linux tests (segmentation fault with torch 2.12 on test and/or doc build) by @rflamary in #815
- [MRG] Spectral-Grassmann OT by @thibaut-germain in #792
- [MRG] BSP-OT: Sparse transport plans between discrete measures in loglinear time by @dcoeurjo in #768
- [MRG] Add normalize parameter to sliced_wasserstein_distance by @Harguna in #808
- [MRG] Debug doc on master, speedup wheels with ARM runners and add tests for Python 3.14 by @rflamary in #818
- [MRG] entropic_partial_wasserstein: add
method='sinkhorn_log'stable log-domain solver (rescue of #724) by @hinanohart in #811 - [MRG] Fix entropic regularization in
gcgby @MatDag in #817 - [MRG] Sliced OT Plans by @eloitanguy in #767
- [MRG] Refactor simplex and Lazy simplex improvement by @nathanneike in #813
- [MRG] Fix reference numbers by @eloitanguy in #819
- [MRG] Remove openmp implementation for emd2 by @nathanneike in #820
- [MRG] HD Gaussian empirical bures by @marcogenni in #814
- [FIX] Remove tf backend for generic regularizer in unbalanced by @rflamary in #824
- [MRG] batch FUGW loss by @smazelet in #775
- [MRG] Fix documentation for lowrank_sinkhorn and lowrank_gromov_wasserstein… by @akprasadan in #823
- [MRG] Wrapper for OT barycenter solvers with free support by @cedricvincentcuaz in #730
- [MRG] reorganize tests per backends by @rflamary in #828
- [MRG] Add backend-agnostic semi-discrete OT module and SGD-based solver in ot.semidiscrete by @Ferdinand-Genans in #812
- [MRG] Update wrapper for new geomloss version by @rflamary in #826
- Fixing issue in SGOT example and SGOT cost function. by @thibaut-germain in #830
- [MRG] add debiasing and all new solvers to
ot.solve_sampleby @rflamary in #831 - [MRG] Proximal point solver for batched optimal transport by @thibaut-germain in #832
- [MRG] Fix label-aware cost correction in ot.da (closes #664) by @CodingSelim in #833
- [MRG] Release 0.9.7 by @rflamary in #829
New Contributors
- @caph1993 made their first contribution in #769
- @nathanneike made their first contribution in #778
- @dcoeurjo made their first contribution in #789
- @Pchambet made their first contribution in #798
- @osheasienna made their first contribution in #801
- @thibaut-germain made their first contribution in #792
- @Harguna made their first contribution in #808
- @hinanohart made their first contribution in #811
- @MatDag made their first contribution in #817
- @marcogenni made their first contribution in #814
- @akprasadan made their first contribution in #823
- @Ferdinand-Genans made their first contribution in #812
- @CodingSelim made their first contribution in #833
Full Changelog: 0.9.6...0.9.7