Skip to content

Commit 93785eb

Browse files
lchapelrflamary
andauthored
[MRG] Fix bugs for partial OT (#215)
* bugfix * update refs partial OT * fixes small typos in plot_partial_wass_and_gromov * fix small bugs in partial.py * update README * pep8 bugfix * modif doctest * fix bugtests * update on test_partial and test on the numerical precision on ot/partial * resolve merge pb Co-authored-by: Rémi Flamary <remi.flamary@gmail.com>
1 parent 78b44af commit 93785eb

File tree

4 files changed

+60
-42
lines changed

4 files changed

+60
-42
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ You can also post bug reports and feature requests in Github issues. Make sure t
262262

263263
[28] Caffarelli, L. A., McCann, R. J. (2010). [Free boundaries in optimal transport and Monge-Ampere obstacle problems](http://www.math.toronto.edu/~mccann/papers/annals2010.pdf), Annals of mathematics, 673-730.
264264

265-
[29] Chapel, L., Alaya, M., Gasso, G. (2019). [Partial Gromov-Wasserstein with Applications on Positive-Unlabeled Learning](https://arxiv.org/abs/2002.08276), arXiv preprint arXiv:2002.08276.
265+
[29] Chapel, L., Alaya, M., Gasso, G. (2020). [Partial Optimal Transport with Applications on Positive-Unlabeled Learning](https://arxiv.org/abs/2002.08276), Advances in Neural Information Processing Systems (NeurIPS), 2020.
266266

267267
[30] Flamary R., Courty N., Tuia D., Rakotomamonjy A. (2014). [Optimal transport with Laplacian regularization: Applications to domain adaptation and shape matching](https://remi.flamary.com/biblio/flamary2014optlaplace.pdf), NIPS Workshop on Optimal Transport and Machine Learning OTML, 2014.
268268

examples/unbalanced-partial/plot_partial_wass_and_gromov.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
Partial Wasserstein and Gromov-Wasserstein example
55
==================================================
66
7-
This example is designed to show how to use the Partial (Gromov-)Wassertsein
7+
This example is designed to show how to use the Partial (Gromov-)Wasserstein
88
distance computation in POT.
99
"""
1010

@@ -123,11 +123,12 @@
123123
C2 = sp.spatial.distance.cdist(xt, xt)
124124

125125
# transport 100% of the mass
126-
print('-----m = 1')
126+
print('------m = 1')
127127
m = 1
128128
res0, log0 = ot.partial.partial_gromov_wasserstein(C1, C2, p, q, m=m, log=True)
129129
res, log = ot.partial.entropic_partial_gromov_wasserstein(C1, C2, p, q, 10,
130-
m=m, log=True)
130+
m=m, log=True,
131+
verbose=True)
131132

132133
print('Wasserstein distance (m = 1): ' + str(log0['partial_gw_dist']))
133134
print('Entropic Wasserstein distance (m = 1): ' + str(log['partial_gw_dist']))
@@ -136,18 +137,20 @@
136137
pl.title("mass to be transported m = 1")
137138
pl.subplot(1, 2, 1)
138139
pl.imshow(res0, cmap='jet')
139-
pl.title('Wasserstein')
140+
pl.title('Gromov-Wasserstein')
140141
pl.subplot(1, 2, 2)
141142
pl.imshow(res, cmap='jet')
142-
pl.title('Entropic Wasserstein')
143+
pl.title('Entropic Gromov-Wasserstein')
143144
pl.show()
144145

145146
# transport 2/3 of the mass
146-
print('-----m = 2/3')
147+
print('------m = 2/3')
147148
m = 2 / 3
148-
res0, log0 = ot.partial.partial_gromov_wasserstein(C1, C2, p, q, m=m, log=True)
149+
res0, log0 = ot.partial.partial_gromov_wasserstein(C1, C2, p, q, m=m, log=True,
150+
verbose=True)
149151
res, log = ot.partial.entropic_partial_gromov_wasserstein(C1, C2, p, q, 10,
150-
m=m, log=True)
152+
m=m, log=True,
153+
verbose=True)
151154

152155
print('Partial Wasserstein distance (m = 2/3): ' +
153156
str(log0['partial_gw_dist']))
@@ -158,8 +161,8 @@
158161
pl.title("mass to be transported m = 2/3")
159162
pl.subplot(1, 2, 1)
160163
pl.imshow(res0, cmap='jet')
161-
pl.title('Partial Wasserstein')
164+
pl.title('Partial Gromov-Wasserstein')
162165
pl.subplot(1, 2, 2)
163166
pl.imshow(res, cmap='jet')
164-
pl.title('Entropic partial Wasserstein')
167+
pl.title('Entropic partial Gromov-Wasserstein')
165168
pl.show()

ot/partial.py

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -230,9 +230,9 @@ def partial_wasserstein(a, b, M, m=None, nb_dummies=1, log=False, **kwargs):
230230
.. [28] Caffarelli, L. A., & McCann, R. J. (2010) Free boundaries in
231231
optimal transport and Monge-Ampere obstacle problems. Annals of
232232
mathematics, 673-730.
233-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
234-
Wasserstein with Applications on Positive-Unlabeled Learning".
235-
arXiv preprint arXiv:2002.08276.
233+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
234+
Transport with Applications on Positive-Unlabeled Learning".
235+
NeurIPS.
236236
237237
See Also
238238
--------
@@ -254,7 +254,7 @@ def partial_wasserstein(a, b, M, m=None, nb_dummies=1, log=False, **kwargs):
254254
b_extended = np.append(b, [(np.sum(a) - m) / nb_dummies] * nb_dummies)
255255
a_extended = np.append(a, [(np.sum(b) - m) / nb_dummies] * nb_dummies)
256256
M_extended = np.zeros((len(a_extended), len(b_extended)))
257-
M_extended[-1, -1] = np.max(M) * 1e5
257+
M_extended[-nb_dummies:, -nb_dummies:] = np.max(M) * 1e5
258258
M_extended[:len(a), :len(b)] = M
259259

260260
gamma, log_emd = emd(a_extended, b_extended, M_extended, log=True,
@@ -344,14 +344,13 @@ def partial_wasserstein2(a, b, M, m=None, nb_dummies=1, log=False, **kwargs):
344344
.. [28] Caffarelli, L. A., & McCann, R. J. (2010) Free boundaries in
345345
optimal transport and Monge-Ampere obstacle problems. Annals of
346346
mathematics, 673-730.
347-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
348-
Wasserstein with Applications on Positive-Unlabeled Learning".
349-
arXiv preprint arXiv:2002.08276.
347+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
348+
Transport with Applications on Positive-Unlabeled Learning".
349+
NeurIPS.
350350
"""
351351

352352
partial_gw, log_w = partial_wasserstein(a, b, M, m, nb_dummies, log=True,
353353
**kwargs)
354-
355354
log_w['T'] = partial_gw
356355

357356
if log:
@@ -501,14 +500,14 @@ def partial_gromov_wasserstein(C1, C2, p, q, m=None, nb_dummies=1, G0=None,
501500
>>> np.round(partial_gromov_wasserstein(C1, C2, a, b, m=0.25),2)
502501
array([[0. , 0. , 0. , 0. ],
503502
[0. , 0. , 0. , 0. ],
504-
[0. , 0. , 0. , 0. ],
505-
[0. , 0. , 0. , 0.25]])
503+
[0. , 0. , 0.25, 0. ],
504+
[0. , 0. , 0. , 0. ]])
506505
507506
References
508507
----------
509-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
510-
Wasserstein with Applications on Positive-Unlabeled Learning".
511-
arXiv preprint arXiv:2002.08276.
508+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
509+
Transport with Applications on Positive-Unlabeled Learning".
510+
NeurIPS.
512511
513512
"""
514513

@@ -530,20 +529,18 @@ def partial_gromov_wasserstein(C1, C2, p, q, m=None, nb_dummies=1, G0=None,
530529

531530
cpt = 0
532531
err = 1
533-
eps = 1e-20
532+
534533
if log:
535534
log = {'err': []}
536535

537536
while (err > tol and cpt < numItermax):
538537

539-
Gprev = G0
538+
Gprev = np.copy(G0)
540539

541540
M = gwgrad_partial(C1, C2, G0)
542-
M[M < eps] = np.quantile(M, thres)
543-
544541
M_emd = np.zeros(dim_G_extended)
545542
M_emd[:len(p), :len(q)] = M
546-
M_emd[-nb_dummies:, -nb_dummies:] = np.max(M) * 1e5
543+
M_emd[-nb_dummies:, -nb_dummies:] = np.max(M) * 1e2
547544
M_emd = np.asarray(M_emd, dtype=np.float64)
548545

549546
Gc, logemd = emd(p_extended, q_extended, M_emd, log=True, **kwargs)
@@ -565,6 +562,22 @@ def partial_gromov_wasserstein(C1, C2, p, q, m=None, nb_dummies=1, G0=None,
565562
print('{:5d}|{:8e}|{:8e}'.format(cpt, err,
566563
gwloss_partial(C1, C2, G0)))
567564

565+
deltaG = G0 - Gprev
566+
a = gwloss_partial(C1, C2, deltaG)
567+
b = 2 * np.sum(M * deltaG)
568+
if b > 0: # due to numerical precision
569+
gamma = 0
570+
cpt = numItermax
571+
elif a > 0:
572+
gamma = min(1, np.divide(-b, 2.0 * a))
573+
else:
574+
if (a + b) < 0:
575+
gamma = 1
576+
else:
577+
gamma = 0
578+
cpt = numItermax
579+
580+
G0 = Gprev + gamma * deltaG
568581
cpt += 1
569582

570583
if log:
@@ -665,9 +678,9 @@ def partial_gromov_wasserstein2(C1, C2, p, q, m=None, nb_dummies=1, G0=None,
665678
666679
References
667680
----------
668-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
669-
Wasserstein with Applications on Positive-Unlabeled Learning".
670-
arXiv preprint arXiv:2002.08276.
681+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
682+
Transport with Applications on Positive-Unlabeled Learning".
683+
NeurIPS.
671684
672685
"""
673686

@@ -887,12 +900,12 @@ def entropic_partial_gromov_wasserstein(C1, C2, p, q, reg, m=None, G0=None,
887900
>>> y = np.array([3,2,98,199]).reshape((-1,1))
888901
>>> C1 = sp.spatial.distance.cdist(x, x)
889902
>>> C2 = sp.spatial.distance.cdist(y, y)
890-
>>> np.round(entropic_partial_gromov_wasserstein(C1, C2, a, b,50), 2)
903+
>>> np.round(entropic_partial_gromov_wasserstein(C1, C2, a, b, 50), 2)
891904
array([[0.12, 0.13, 0. , 0. ],
892905
[0.13, 0.12, 0. , 0. ],
893906
[0. , 0. , 0.25, 0. ],
894907
[0. , 0. , 0. , 0.25]])
895-
>>> np.round(entropic_partial_gromov_wasserstein(C1, C2, a, b, 50, m=0.25), 2)
908+
>>> np.round(entropic_partial_gromov_wasserstein(C1, C2, a, b, 50,0.25), 2)
896909
array([[0.02, 0.03, 0. , 0.03],
897910
[0.03, 0.03, 0. , 0.03],
898911
[0. , 0. , 0.03, 0. ],
@@ -910,9 +923,9 @@ def entropic_partial_gromov_wasserstein(C1, C2, p, q, reg, m=None, G0=None,
910923
.. [12] Peyré, Gabriel, Marco Cuturi, and Justin Solomon,
911924
"Gromov-Wasserstein averaging of kernel and distance matrices."
912925
International Conference on Machine Learning (ICML). 2016.
913-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
914-
Wasserstein with Applications on Positive-Unlabeled Learning".
915-
arXiv preprint arXiv:2002.08276.
926+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
927+
Transport with Applications on Positive-Unlabeled Learning".
928+
NeurIPS.
916929
917930
See Also
918931
--------
@@ -1044,9 +1057,9 @@ def entropic_partial_gromov_wasserstein2(C1, C2, p, q, reg, m=None, G0=None,
10441057
.. [12] Peyré, Gabriel, Marco Cuturi, and Justin Solomon,
10451058
"Gromov-Wasserstein averaging of kernel and distance matrices."
10461059
International Conference on Machine Learning (ICML). 2016.
1047-
.. [29] Chapel, L., Alaya, M., Gasso, G. (2019). "Partial Gromov-
1048-
Wasserstein with Applications on Positive-Unlabeled Learning".
1049-
arXiv preprint arXiv:2002.08276.
1060+
.. [29] Chapel, L., Alaya, M., Gasso, G. (2020). "Partial Optimal
1061+
Transport with Applications on Positive-Unlabeled Learning".
1062+
NeurIPS.
10501063
"""
10511064

10521065
partial_gw, log_gw = entropic_partial_gromov_wasserstein(C1, C2, p, q, reg,

test/test_partial.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,12 @@ def test_raise_errors():
5151
ot.partial.partial_gromov_wasserstein(M, M, p, q, m=-1, log=True)
5252

5353
with pytest.raises(ValueError):
54-
ot.partial.entropic_partial_gromov_wasserstein(M, M, p, q, reg=1, m=2, log=True)
54+
ot.partial.entropic_partial_gromov_wasserstein(M, M, p, q, reg=1, m=2,
55+
log=True)
5556

5657
with pytest.raises(ValueError):
57-
ot.partial.entropic_partial_gromov_wasserstein(M, M, p, q, reg=1, m=-1, log=True)
58+
ot.partial.entropic_partial_gromov_wasserstein(M, M, p, q, reg=1, m=-1,
59+
log=True)
5860

5961

6062
def test_partial_wasserstein_lagrange():

0 commit comments

Comments
 (0)