Skip to content

Commit ff89080

Browse files
Circle CICircle CI
authored andcommitted
CircleCI update of dev docs (1757).
1 parent 1867b98 commit ff89080

503 files changed

Lines changed: 1339009 additions & 295836 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎_downloads/059a63fc6cb655dcd2f1c9a61bb9d7ec/plot_OT_2D_samples.ipynb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
"name": "python",
137137
"nbconvert_exporter": "python",
138138
"pygments_lexer": "ipython3",
139-
"version": "3.9.12"
139+
"version": "3.9.16"
140140
}
141141
},
142142
"nbformat": 4,
Binary file not shown.

‎_downloads/0e15d87ff8ae3d0dd037bf7ddc80d8a7/plot_variance.ipynb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
"name": "python",
130130
"nbconvert_exporter": "python",
131131
"pygments_lexer": "ipython3",
132-
"version": "3.9.12"
132+
"version": "3.9.16"
133133
}
134134
},
135135
"nbformat": 4,

‎_downloads/0e77b32207676919c68b7f28c6029b1b/plot_unmix_optim_torch.ipynb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@
136136
"name": "python",
137137
"nbconvert_exporter": "python",
138138
"pygments_lexer": "ipython3",
139-
"version": "3.9.12"
139+
"version": "3.9.16"
140140
}
141141
},
142142
"nbformat": 4,

‎_downloads/0eb518a5cb2af5a1870fc1f120d1483e/plot_partial_wass_and_gromov.ipynb‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@
118118
"name": "python",
119119
"nbconvert_exporter": "python",
120120
"pygments_lexer": "ipython3",
121-
"version": "3.9.12"
121+
"version": "3.9.16"
122122
}
123123
},
124124
"nbformat": 4,
Lines changed: 301 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,301 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
==========================
4+
Semi-relaxed (Fused) Gromov-Wasserstein example
5+
==========================
6+
7+
This example is designed to show how to use the semi-relaxed Gromov-Wasserstein
8+
and the semi-relaxed Fused Gromov-Wasserstein divergences.
9+
10+
sr(F)GW between two graphs G1 and G2 searches for a reweighing of the nodes of
11+
G2 at a minimal (F)GW distance from G1.
12+
13+
First, we generate two graphs following Stochastic Block Models, then show
14+
how to compute their srGW matchings and illustrate them. These graphs are then
15+
endowed with node features and we follow the same process with srFGW.
16+
17+
[48] Cédric Vincent-Cuaz, Rémi Flamary, Marco Corneli, Titouan Vayer, Nicolas Courty.
18+
"Semi-relaxed Gromov-Wasserstein divergence and applications on graphs"
19+
International Conference on Learning Representations (ICLR), 2021.
20+
"""
21+
22+
# Author: Cédric Vincent-Cuaz <cedvincentcuaz@gmail.com>
23+
#
24+
# License: MIT License
25+
26+
# sphinx_gallery_thumbnail_number = 1
27+
28+
import numpy as np
29+
import matplotlib.pylab as pl
30+
from ot.gromov import semirelaxed_gromov_wasserstein, semirelaxed_fused_gromov_wasserstein, gromov_wasserstein, fused_gromov_wasserstein
31+
import networkx
32+
from networkx.generators.community import stochastic_block_model as sbm
33+
34+
#############################################################################
35+
#
36+
# Generate two graphs following Stochastic Block models of 2 and 3 clusters.
37+
# ---------------------------------------------
38+
39+
40+
N2 = 20 # 2 communities
41+
N3 = 30 # 3 communities
42+
p2 = [[1., 0.1],
43+
[0.1, 0.9]]
44+
p3 = [[1., 0.1, 0.],
45+
[0.1, 0.95, 0.1],
46+
[0., 0.1, 0.9]]
47+
G2 = sbm(seed=0, sizes=[N2 // 2, N2 // 2], p=p2)
48+
G3 = sbm(seed=0, sizes=[N3 // 3, N3 // 3, N3 // 3], p=p3)
49+
50+
51+
C2 = networkx.to_numpy_array(G2)
52+
C3 = networkx.to_numpy_array(G3)
53+
54+
h2 = np.ones(C2.shape[0]) / C2.shape[0]
55+
h3 = np.ones(C3.shape[0]) / C3.shape[0]
56+
57+
# Add weights on the edges for visualization later on
58+
weight_intra_G2 = 5
59+
weight_inter_G2 = 0.5
60+
weight_intra_G3 = 1.
61+
weight_inter_G3 = 1.5
62+
63+
weightedG2 = networkx.Graph()
64+
part_G2 = [G2.nodes[i]['block'] for i in range(N2)]
65+
66+
for node in G2.nodes():
67+
weightedG2.add_node(node)
68+
for i, j in G2.edges():
69+
if part_G2[i] == part_G2[j]:
70+
weightedG2.add_edge(i, j, weight=weight_intra_G2)
71+
else:
72+
weightedG2.add_edge(i, j, weight=weight_inter_G2)
73+
74+
weightedG3 = networkx.Graph()
75+
part_G3 = [G3.nodes[i]['block'] for i in range(N3)]
76+
77+
for node in G3.nodes():
78+
weightedG3.add_node(node)
79+
for i, j in G3.edges():
80+
if part_G3[i] == part_G3[j]:
81+
weightedG3.add_edge(i, j, weight=weight_intra_G3)
82+
else:
83+
weightedG3.add_edge(i, j, weight=weight_inter_G3)
84+
85+
#############################################################################
86+
#
87+
# Compute their semi-relaxed Gromov-Wasserstein divergences
88+
# ---------------------------------------------
89+
90+
# 0) GW(C2, h2, C3, h3) for reference
91+
OT, log = gromov_wasserstein(C2, C3, h2, h3, symmetric=True, log=True)
92+
gw = log['gw_dist']
93+
94+
# 1) srGW(C2, h2, C3)
95+
OT_23, log_23 = semirelaxed_gromov_wasserstein(C2, C3, h2, symmetric=True,
96+
log=True, G0=None)
97+
srgw_23 = log_23['srgw_dist']
98+
99+
# 2) srGW(C3, h3, C2)
100+
101+
OT_32, log_32 = semirelaxed_gromov_wasserstein(C3, C2, h3, symmetric=None,
102+
log=True, G0=OT.T)
103+
srgw_32 = log_32['srgw_dist']
104+
105+
print('GW(C2, C3) = ', gw)
106+
print('srGW(C2, h2, C3) = ', srgw_23)
107+
print('srGW(C3, h3, C2) = ', srgw_32)
108+
109+
110+
#############################################################################
111+
#
112+
# Visualization of the semi-relaxed Gromov-Wasserstein matchings
113+
# ---------------------------------------------
114+
#
115+
# We color nodes of the graph on the right - then project its node colors
116+
# based on the optimal transport plan from the srGW matching
117+
118+
119+
def draw_graph(G, C, nodes_color_part, Gweights=None,
120+
pos=None, edge_color='black', node_size=None,
121+
shiftx=0, seed=0):
122+
123+
if (pos is None):
124+
pos = networkx.spring_layout(G, scale=1., seed=seed)
125+
126+
if shiftx != 0:
127+
for k, v in pos.items():
128+
v[0] = v[0] + shiftx
129+
130+
alpha_edge = 0.7
131+
width_edge = 1.8
132+
if Gweights is None:
133+
networkx.draw_networkx_edges(G, pos, width=width_edge, alpha=alpha_edge, edge_color=edge_color)
134+
else:
135+
# We make more visible connections between activated nodes
136+
n = len(Gweights)
137+
edgelist_activated = []
138+
edgelist_deactivated = []
139+
for i in range(n):
140+
for j in range(n):
141+
if Gweights[i] * Gweights[j] * C[i, j] > 0:
142+
edgelist_activated.append((i, j))
143+
elif C[i, j] > 0:
144+
edgelist_deactivated.append((i, j))
145+
146+
networkx.draw_networkx_edges(G, pos, edgelist=edgelist_activated,
147+
width=width_edge, alpha=alpha_edge,
148+
edge_color=edge_color)
149+
networkx.draw_networkx_edges(G, pos, edgelist=edgelist_deactivated,
150+
width=width_edge, alpha=0.1,
151+
edge_color=edge_color)
152+
153+
if Gweights is None:
154+
for node, node_color in enumerate(nodes_color_part):
155+
networkx.draw_networkx_nodes(G, pos, nodelist=[node],
156+
node_size=node_size, alpha=1,
157+
node_color=node_color)
158+
else:
159+
scaled_Gweights = Gweights / (0.5 * Gweights.max())
160+
nodes_size = node_size * scaled_Gweights
161+
for node, node_color in enumerate(nodes_color_part):
162+
networkx.draw_networkx_nodes(G, pos, nodelist=[node],
163+
node_size=nodes_size[node], alpha=1,
164+
node_color=node_color)
165+
return pos
166+
167+
168+
def draw_transp_colored_srGW(G1, C1, G2, C2, part_G1,
169+
p1, p2, T, pos1=None, pos2=None,
170+
shiftx=4, switchx=False, node_size=70,
171+
seed_G1=0, seed_G2=0):
172+
starting_color = 0
173+
# get graphs partition and their coloring
174+
part1 = part_G1.copy()
175+
unique_colors = ['C%s' % (starting_color + i) for i in np.unique(part1)]
176+
nodes_color_part1 = []
177+
for cluster in part1:
178+
nodes_color_part1.append(unique_colors[cluster])
179+
180+
nodes_color_part2 = []
181+
# T: getting colors assignment from argmin of columns
182+
for i in range(len(G2.nodes())):
183+
j = np.argmax(T[:, i])
184+
nodes_color_part2.append(nodes_color_part1[j])
185+
pos1 = draw_graph(G1, C1, nodes_color_part1, Gweights=p1,
186+
pos=pos1, node_size=node_size, shiftx=0, seed=seed_G1)
187+
pos2 = draw_graph(G2, C2, nodes_color_part2, Gweights=p2, pos=pos2,
188+
node_size=node_size, shiftx=shiftx, seed=seed_G2)
189+
for k1, v1 in pos1.items():
190+
for k2, v2 in pos2.items():
191+
if (T[k1, k2] > 0):
192+
pl.plot([pos1[k1][0], pos2[k2][0]],
193+
[pos1[k1][1], pos2[k2][1]],
194+
'-', lw=0.8, alpha=0.5,
195+
color=nodes_color_part1[k1])
196+
return pos1, pos2
197+
198+
199+
node_size = 40
200+
fontsize = 10
201+
seed_G2 = 0
202+
seed_G3 = 4
203+
204+
pl.figure(1, figsize=(8, 2.5))
205+
pl.clf()
206+
pl.subplot(121)
207+
pl.axis('off')
208+
pl.axis
209+
pl.title(r'srGW$(\mathbf{C_2},\mathbf{h_2},\mathbf{C_3}) =%s$' % (np.round(srgw_23, 3)), fontsize=fontsize)
210+
211+
hbar2 = OT_23.sum(axis=0)
212+
pos1, pos2 = draw_transp_colored_srGW(
213+
weightedG2, C2, weightedG3, C3, part_G2, p1=None, p2=hbar2, T=OT_23,
214+
shiftx=1.5, node_size=node_size, seed_G1=seed_G2, seed_G2=seed_G3)
215+
pl.subplot(122)
216+
pl.axis('off')
217+
hbar3 = OT_32.sum(axis=0)
218+
pl.title(r'srGW$(\mathbf{C_3}, \mathbf{h_3},\mathbf{C_2}) =%s$' % (np.round(srgw_32, 3)), fontsize=fontsize)
219+
pos1, pos2 = draw_transp_colored_srGW(
220+
weightedG3, C3, weightedG2, C2, part_G3, p1=None, p2=hbar3, T=OT_32,
221+
pos1=pos2, pos2=pos1, shiftx=3., node_size=node_size, seed_G1=0, seed_G2=0)
222+
pl.tight_layout()
223+
224+
pl.show()
225+
226+
#############################################################################
227+
#
228+
# Add node features
229+
# ---------------------------------------------
230+
231+
# We add node features with given mean - by clusters
232+
# and inversely proportional to clusters' intra-connectivity
233+
234+
F2 = np.zeros((N2, 1))
235+
for i, c in enumerate(part_G2):
236+
F2[i, 0] = np.random.normal(loc=c, scale=0.01)
237+
238+
F3 = np.zeros((N3, 1))
239+
for i, c in enumerate(part_G3):
240+
F3[i, 0] = np.random.normal(loc=2. - c, scale=0.01)
241+
242+
#############################################################################
243+
#
244+
# Compute their semi-relaxed Fused Gromov-Wasserstein divergences
245+
# ---------------------------------------------
246+
247+
alpha = 0.5
248+
# Compute pairwise euclidean distance between node features
249+
M = (F2 ** 2).dot(np.ones((1, N3))) + np.ones((N2, 1)).dot((F3 ** 2).T) - 2 * F2.dot(F3.T)
250+
251+
# 0) FGW_alpha(C2, F2, h2, C3, F3, h3) for reference
252+
253+
OT, log = fused_gromov_wasserstein(
254+
M, C2, C3, h2, h3, symmetric=True, alpha=alpha, log=True)
255+
fgw = log['fgw_dist']
256+
257+
# 1) srFGW(C2, F2, h2, C3, F3)
258+
OT_23, log_23 = semirelaxed_fused_gromov_wasserstein(
259+
M, C2, C3, h2, symmetric=True, alpha=0.5, log=True, G0=None)
260+
srfgw_23 = log_23['srfgw_dist']
261+
262+
# 2) srFGW(C3, F3, h3, C2, F2)
263+
264+
OT_32, log_32 = semirelaxed_fused_gromov_wasserstein(
265+
M.T, C3, C2, h3, symmetric=None, alpha=alpha, log=True, G0=None)
266+
srfgw_32 = log_32['srfgw_dist']
267+
268+
print('FGW(C2, F2, C3, F3) = ', fgw)
269+
print('srGW(C2, F2, h2, C3, F3) = ', srfgw_23)
270+
print('srGW(C3, F3, h3, C2, F2) = ', srfgw_32)
271+
272+
#############################################################################
273+
#
274+
# Visualization of the semi-relaxed Fused Gromov-Wasserstein matchings
275+
# ---------------------------------------------
276+
#
277+
# We color nodes of the graph on the right - then project its node colors
278+
# based on the optimal transport plan from the srFGW matching
279+
# NB: colors refer to clusters - not to node features
280+
281+
pl.figure(2, figsize=(8, 2.5))
282+
pl.clf()
283+
pl.subplot(121)
284+
pl.axis('off')
285+
pl.axis
286+
pl.title(r'srFGW$(\mathbf{C_2},\mathbf{F_2},\mathbf{h_2},\mathbf{C_3},\mathbf{F_3}) =%s$' % (np.round(srfgw_23, 3)), fontsize=fontsize)
287+
288+
hbar2 = OT_23.sum(axis=0)
289+
pos1, pos2 = draw_transp_colored_srGW(
290+
weightedG2, C2, weightedG3, C3, part_G2, p1=None, p2=hbar2, T=OT_23,
291+
shiftx=1.5, node_size=node_size, seed_G1=seed_G2, seed_G2=seed_G3)
292+
pl.subplot(122)
293+
pl.axis('off')
294+
hbar3 = OT_32.sum(axis=0)
295+
pl.title(r'srFGW$(\mathbf{C_3}, \mathbf{F_3}, \mathbf{h_3}, \mathbf{C_2}, \mathbf{F_2}) =%s$' % (np.round(srfgw_32, 3)), fontsize=fontsize)
296+
pos1, pos2 = draw_transp_colored_srGW(
297+
weightedG3, C3, weightedG2, C2, part_G3, p1=None, p2=hbar3, T=OT_32,
298+
pos1=pos2, pos2=pos1, shiftx=3., node_size=node_size, seed_G1=0, seed_G2=0)
299+
pl.tight_layout()
300+
301+
pl.show()

0 commit comments

Comments
 (0)