From d62877b7c8d03274022f7c6c795d2d789de7b572 Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Tue, 21 Jan 2025 16:08:13 +0100 Subject: [PATCH 1/7] Add entropic_partial_wasserstein function I also fix tests in `sinkhorn_unbalanced` and `sinkhorn_unbalanced2` --- Project.toml | 2 +- docs/src/api.md | 6 ++++ src/PythonOT.jl | 3 +- src/lib.jl | 95 +++++++++++++++++++++++++++++++------------------ 4 files changed, 70 insertions(+), 36 deletions(-) diff --git a/Project.toml b/Project.toml index d8894ac..edfce28 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PythonOT" uuid = "3c485715-4278-42b2-9b5f-8f00e43c12ef" authors = ["David Widmann"] -version = "0.1.6" +version = "0.1.7" [deps] PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" diff --git a/docs/src/api.md b/docs/src/api.md index 00409b1..f9d7320 100644 --- a/docs/src/api.md +++ b/docs/src/api.md @@ -37,3 +37,9 @@ sinkhorn_unbalanced2 barycenter_unbalanced mm_unbalanced ``` + +## Partial optimal transport + +```@docs +entropic_partial_wasserstein +``` diff --git a/src/PythonOT.jl b/src/PythonOT.jl index d801f3d..4a1c71b 100644 --- a/src/PythonOT.jl +++ b/src/PythonOT.jl @@ -13,7 +13,8 @@ export emd, sinkhorn_unbalanced, sinkhorn_unbalanced2, empirical_sinkhorn_divergence, - mm_unbalanced + mm_unbalanced, + entropic_partial_wasserstein const pot = PyCall.PyNULL() diff --git a/src/lib.jl b/src/lib.jl index 07bb9af..c81ba30 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -306,29 +306,18 @@ Python function. # Examples ```jldoctest sinkhorn_unbalanced -julia> μ = [0.5, 0.2, 0.3]; +julia> μ = [0.5, 0.5]; -julia> ν = [0.0, 1.0]; +julia> ν = [0.5, 0.5]; -julia> C = [0.0 1.0; 2.0 0.0; 0.5 1.5]; +julia> C = [0.0 1.0; 1.0 0.0]; -julia> round.(sinkhorn_unbalanced(μ, ν, C, 0.01, 1_000); sigdigits=4) -3×2 Matrix{Float64}: - 0.0 0.5 - 0.0 0.2002 - 0.0 0.2998 +julia> round.(sinkhorn_unbalanced(μ, ν, C, 1, 1); sigdigits=7) +2×2 Matrix{Float64}: + 0.322054 0.118477 + 0.118477 0.322054 ``` -It is possible to provide multiple target marginals as columns of a matrix. In this case the -optimal transport costs are returned: - -```jldoctest sinkhorn_unbalanced -julia> ν = [0.0 0.5; 1.0 0.5]; - -julia> round.(sinkhorn_unbalanced(μ, ν, C, 0.01, 1_000); sigdigits=4) -2-element Vector{Float64}: - 0.9497 - 0.4494 ``` See also: [`sinkhorn_unbalanced2`](@ref) @@ -365,25 +354,14 @@ Python function. # Examples ```jldoctest sinkhorn_unbalanced2 -julia> μ = [0.5, 0.2, 0.3]; - -julia> ν = [0.0, 1.0]; - -julia> C = [0.0 1.0; 2.0 0.0; 0.5 1.5]; +julia> μ = [0.5, 0.1]; -julia> round.(sinkhorn_unbalanced2(μ, ν, C, 0.01, 1_000); sigdigits=4) -0.9497 -``` - -It is possible to provide multiple target marginals as columns of a matrix: +julia> ν = [0.5, 0.5]; -```jldoctest sinkhorn_unbalanced2 -julia> ν = [0.0 0.5; 1.0 0.5]; +julia> C = [0.0 1.0; 1.0 0.0]; -julia> round.(sinkhorn_unbalanced2(μ, ν, C, 0.01, 1_000); sigdigits=4) -2-element Vector{Float64}: - 0.9497 - 0.4494 +julia> round.(sinkhorn_unbalanced2(μ, ν, C, 1., 1.); sigdigits=8) +0.19600125 ``` See also: [`sinkhorn_unbalanced`](@ref) @@ -566,3 +544,52 @@ julia> round.(mm_unbalanced(a, b, M, 5, div="l2"), digits=2) function mm_unbalanced(a, b, M, reg_m; kwargs...) return pot.unbalanced.mm_unbalanced(a, b, M, reg_m; kwargs...) end + + +""" + entropic_partial_wasserstein(a, b, M, reg; kwargs...) + +Solves the partial optimal transport problem and returns the OT plan +The function considers the following problem: + +```math +\\gamma = \\mathop{\\arg \\min}_\\gamma \\quad \\langle \\gamma, +\\mathbf{M} \\rangle_F + \\mathrm{reg} \\cdot\\Omega(\\gamma) + +s.t. \\gamma \\mathbf{1} &\\leq \\mathbf{a} \\\\ + \\gamma^T \\mathbf{1} &\\leq \\mathbf{b} \\\\ + \\gamma &\\geq 0 \\\\ + \\mathbf{1}^T \\gamma^T \\mathbf{1} = m + &\\leq \\min\\{\\|\\mathbf{a}\\|_1, \\|\\mathbf{b}\\|_1\\} \\\\ +``` + +where : + +- `M` is the metric cost matrix +- ``\\Omega`` is the entropic regularization term, ``\\Omega=\\sum_{i,j} \\gamma_{i,j}\\log(\\gamma_{i,j})`` +- `a` and `b` are the sample weights +- `m` is the amount of mass to be transported + +This function is a wrapper of the function +[`entropic_partial_wasserstein`](https://pythonot.github.io/gen_modules/ot.partial.html#ot.partial.entropic_partial_wasserstein) in the +Python Optimal Transport package. Keyword arguments are listed in the documentation of the +Python function. + + +# Examples + +```jldoctest +julia> a = [.1, .2]; + +julia> b = [.1, .1]; + +julia> M = [0. 1.; 2. 3.]; + +julia> round.(entropic_partial_wasserstein(a, b, M, 1, m=0.1), digits=2) +2×2 Matrix{Float64}: + 0.06 0.02 + 0.01 0.0 +""" +function entropic_partial_wasserstein(a, b, M, reg; kwargs...) + return pot.partial.entropic_partial_wasserstein(a, b, M, reg; kwargs...) +end From 90877fa627b62cfd66d8fa150a6231f52a14af94 Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Tue, 21 Jan 2025 16:21:51 +0100 Subject: [PATCH 2/7] Update lib.jl Fix problems in doc generation --- src/lib.jl | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/src/lib.jl b/src/lib.jl index c81ba30..65460cb 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -555,20 +555,11 @@ The function considers the following problem: ```math \\gamma = \\mathop{\\arg \\min}_\\gamma \\quad \\langle \\gamma, \\mathbf{M} \\rangle_F + \\mathrm{reg} \\cdot\\Omega(\\gamma) - -s.t. \\gamma \\mathbf{1} &\\leq \\mathbf{a} \\\\ - \\gamma^T \\mathbf{1} &\\leq \\mathbf{b} \\\\ - \\gamma &\\geq 0 \\\\ - \\mathbf{1}^T \\gamma^T \\mathbf{1} = m - &\\leq \\min\\{\\|\\mathbf{a}\\|_1, \\|\\mathbf{b}\\|_1\\} \\\\ ``` -where : - -- `M` is the metric cost matrix -- ``\\Omega`` is the entropic regularization term, ``\\Omega=\\sum_{i,j} \\gamma_{i,j}\\log(\\gamma_{i,j})`` - `a` and `b` are the sample weights -- `m` is the amount of mass to be transported +- `M` is the metric cost matrix +- `reg` is a regularization term > 0 This function is a wrapper of the function [`entropic_partial_wasserstein`](https://pythonot.github.io/gen_modules/ot.partial.html#ot.partial.entropic_partial_wasserstein) in the @@ -589,6 +580,8 @@ julia> round.(entropic_partial_wasserstein(a, b, M, 1, m=0.1), digits=2) 2×2 Matrix{Float64}: 0.06 0.02 0.01 0.0 +``` + """ function entropic_partial_wasserstein(a, b, M, reg; kwargs...) return pot.partial.entropic_partial_wasserstein(a, b, M, reg; kwargs...) From f080ec7256a66f20459fe62226047ca0ad9c627e Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Fri, 21 Nov 2025 15:06:00 +0100 Subject: [PATCH 3/7] Add partial_wasserstein2 function and export Introduces the partial_wasserstein2 function in lib.jl, wrapping the corresponding PythonOT method, and exports it in PythonOT.jl for external use. --- src/PythonOT.jl | 3 ++- src/lib.jl | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/PythonOT.jl b/src/PythonOT.jl index 4a1c71b..8053369 100644 --- a/src/PythonOT.jl +++ b/src/PythonOT.jl @@ -14,7 +14,8 @@ export emd, sinkhorn_unbalanced2, empirical_sinkhorn_divergence, mm_unbalanced, - entropic_partial_wasserstein + entropic_partial_wasserstein, + partial_wasserstein2 const pot = PyCall.PyNULL() diff --git a/src/lib.jl b/src/lib.jl index 65460cb..462f3c5 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -586,3 +586,7 @@ julia> round.(entropic_partial_wasserstein(a, b, M, 1, m=0.1), digits=2) function entropic_partial_wasserstein(a, b, M, reg; kwargs...) return pot.partial.entropic_partial_wasserstein(a, b, M, reg; kwargs...) end + +function partial_wasserstein2(a, b, M; kwargs...) + return pot.partial.partial_wasserstein2(a, b, M; kwargs...) +end From 984f0ce1f189887ea15752c07c7e679060e52c8c Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Fri, 21 Nov 2025 15:07:21 +0100 Subject: [PATCH 4/7] Update Project.toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index edfce28..4a8f3f9 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PythonOT" uuid = "3c485715-4278-42b2-9b5f-8f00e43c12ef" authors = ["David Widmann"] -version = "0.1.7" +version = "0.1.8" [deps] PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" From e569c662b32df544752ada6b4fc7aff35735c6af Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Fri, 21 Nov 2025 15:27:17 +0100 Subject: [PATCH 5/7] Add partial_wasserstein function to API Introduces the partial_wasserstein function to the Julia API, wrapping pot.partial.partial_wasserstein2. Also updates the package version to 0.1.9 and exports the new function. --- Project.toml | 2 +- src/PythonOT.jl | 1 + src/lib.jl | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index 4a8f3f9..1d38470 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "PythonOT" uuid = "3c485715-4278-42b2-9b5f-8f00e43c12ef" authors = ["David Widmann"] -version = "0.1.8" +version = "0.1.9" [deps] PyCall = "438e738f-606a-5dbb-bf0a-cddfbfd45ab0" diff --git a/src/PythonOT.jl b/src/PythonOT.jl index 8053369..f6f24df 100644 --- a/src/PythonOT.jl +++ b/src/PythonOT.jl @@ -15,6 +15,7 @@ export emd, empirical_sinkhorn_divergence, mm_unbalanced, entropic_partial_wasserstein, + partial_wasserstein, partial_wasserstein2 const pot = PyCall.PyNULL() diff --git a/src/lib.jl b/src/lib.jl index 462f3c5..c3e6442 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -587,6 +587,10 @@ function entropic_partial_wasserstein(a, b, M, reg; kwargs...) return pot.partial.entropic_partial_wasserstein(a, b, M, reg; kwargs...) end +function partial_wasserstein(a, b, M; kwargs...) + return pot.partial.partial_wasserstein2(a, b, M; kwargs...) +end + function partial_wasserstein2(a, b, M; kwargs...) return pot.partial.partial_wasserstein2(a, b, M; kwargs...) end From e285ca3ff6d00d1ddca6c531c73ceaa63b873c4f Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Wed, 26 Nov 2025 15:30:01 +0100 Subject: [PATCH 6/7] Add entropic_partial_gromov_wasserstein2 function Exports and implements the entropic_partial_gromov_wasserstein2 function, providing a Julia wrapper for the corresponding POT library method. --- src/PythonOT.jl | 1 + src/lib.jl | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/PythonOT.jl b/src/PythonOT.jl index f6f24df..bc50251 100644 --- a/src/PythonOT.jl +++ b/src/PythonOT.jl @@ -15,6 +15,7 @@ export emd, empirical_sinkhorn_divergence, mm_unbalanced, entropic_partial_wasserstein, + entropic_partial_gromov_wasserstein2, partial_wasserstein, partial_wasserstein2 diff --git a/src/lib.jl b/src/lib.jl index c3e6442..341bc96 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -594,3 +594,7 @@ end function partial_wasserstein2(a, b, M; kwargs...) return pot.partial.partial_wasserstein2(a, b, M; kwargs...) end + +function entropic_partial_gromov_wasserstein2(C1, C2, p, q; kwargs...) + return pot.partial.entropic_partial_gromov_wasserstein2(C1, C2, p, q; kwargs...) +end From 8d7b1c6980d234275f594030b3b706507d8a2a40 Mon Sep 17 00:00:00 2001 From: Pierre Navaro Date: Wed, 26 Nov 2025 15:43:17 +0100 Subject: [PATCH 7/7] Add entropic_partial_gromov_wasserstein function Introduces the entropic_partial_gromov_wasserstein function to the API and exports it. This provides access to the corresponding functionality from the underlying pot.partial module. --- src/PythonOT.jl | 1 + src/lib.jl | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/src/PythonOT.jl b/src/PythonOT.jl index bc50251..dc51d6d 100644 --- a/src/PythonOT.jl +++ b/src/PythonOT.jl @@ -15,6 +15,7 @@ export emd, empirical_sinkhorn_divergence, mm_unbalanced, entropic_partial_wasserstein, + entropic_partial_gromov_wasserstein, entropic_partial_gromov_wasserstein2, partial_wasserstein, partial_wasserstein2 diff --git a/src/lib.jl b/src/lib.jl index 341bc96..f78db7c 100644 --- a/src/lib.jl +++ b/src/lib.jl @@ -595,6 +595,10 @@ function partial_wasserstein2(a, b, M; kwargs...) return pot.partial.partial_wasserstein2(a, b, M; kwargs...) end +function entropic_partial_gromov_wasserstein(C1, C2, p, q, reg; kwargs...) + return pot.partial.entropic_partial_gromov_wasserstein(C1, C2, p, q, reg; kwargs...) +end + function entropic_partial_gromov_wasserstein2(C1, C2, p, q; kwargs...) return pot.partial.entropic_partial_gromov_wasserstein2(C1, C2, p, q; kwargs...) end