Skip to content

Commit 65b1cb7

Browse files
committed
updates
1 parent fe4144d commit 65b1cb7

File tree

19 files changed

+698
-2
lines changed

19 files changed

+698
-2
lines changed

notebooks/18.1-BDP-bokeh.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def adj_to_points(adj):
122122
return_class=True,
123123
return_side=True,
124124
return_ids=True,
125-
return_names=True,
125+
return_pair=True,
126126
version=GRAPH_VERSION,
127127
)
128128

@@ -156,6 +156,7 @@ def adj_to_points(adj):
156156
G_adj = _sort_graph(G_adj, sort_labels, side_labels, True)
157157
sort_labels = sort_labels[sort_inds]
158158
id_labels = id_labels[sort_inds]
159+
pair_labels = pair_labels[sort_inds]
159160

160161
# Turn the graph into points
161162

notebooks/35.1-BDP-upper-triang.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ def unshuffle(shuffle_inds, perm_inds):
175175
max_iter=30,
176176
eps=0.0001,
177177
init_method="rand",
178-
n_init=100,
178+
n_init=1,
179179
shuffle_input=False,
180180
maximize=True,
181181
)

notebooks/37.0-BDP-profile-match.py

Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
1+
# %% [markdown]
2+
# ## Imports and functions
3+
4+
import os
5+
from operator import itemgetter
6+
from timeit import default_timer as timer
7+
8+
import matplotlib.pyplot as plt
9+
import networkx as nx
10+
import numpy as np
11+
import pandas as pd
12+
import scipy
13+
import seaborn as sns
14+
from scipy import version
15+
16+
from graspy.embed import LaplacianSpectralEmbed
17+
from graspy.match import FastApproximateQAP
18+
from graspy.plot import heatmap, pairplot
19+
from graspy.simulations import sbm
20+
from graspy.utils import get_lcc
21+
from src.data import load_everything
22+
from src.utils import savefig
23+
24+
print(version)
25+
print(scipy.__version__)
26+
27+
FNAME = os.path.basename(__file__)[:-3]
28+
print(FNAME)
29+
SAVEFIGS = True
30+
DEFAULT_FMT = "png"
31+
DEFUALT_DPI = 150
32+
33+
plt.style.use("seaborn-white")
34+
sns.set_palette("deep")
35+
sns.set_context("talk", font_scale=1)
36+
37+
38+
def stashfig(name, **kws):
39+
if SAVEFIGS:
40+
savefig(name, foldername=FNAME, fmt=DEFAULT_FMT, dpi=DEFUALT_DPI, **kws)
41+
42+
43+
def get_feedforward_B(low_p, diag_p, feedforward_p, n_blocks=5):
44+
B = np.zeros((n_blocks, n_blocks))
45+
B += low_p
46+
B -= np.diag(np.diag(B))
47+
B -= np.diag(np.diag(B, k=1), k=1)
48+
B += np.diag(diag_p * np.ones(n_blocks))
49+
B += np.diag(feedforward_p * np.ones(n_blocks - 1), k=1)
50+
return B
51+
52+
53+
def n_to_labels(n):
54+
n_cumsum = n.cumsum()
55+
labels = np.zeros(n.sum(), dtype=np.int64)
56+
for i in range(1, len(n)):
57+
labels[n_cumsum[i - 1] : n_cumsum[i]] = i
58+
return labels
59+
60+
61+
def signal_flow(A, n_components=5, return_evals=False):
62+
""" Implementation of the signal flow metric from Varshney et al 2011
63+
64+
Parameters
65+
----------
66+
A : [type]
67+
[description]
68+
69+
Returns
70+
-------
71+
[type]
72+
[description]
73+
"""
74+
W = (A + A.T) / 2
75+
76+
D = np.diag(np.sum(W, axis=1))
77+
78+
L = D - W
79+
80+
b = np.sum(W * np.sign(A - A.T), axis=1)
81+
L_pinv = np.linalg.pinv(L)
82+
z = L_pinv @ b
83+
84+
D_root = np.diag(np.diag(D) ** (-1 / 2))
85+
D_root[np.isnan(D_root)] = 0
86+
D_root[np.isinf(D_root)] = 0
87+
Q = D_root @ L @ D_root
88+
evals, evecs = np.linalg.eig(Q)
89+
inds = np.argsort(evals)
90+
evals = evals[inds]
91+
evecs = evecs[:, inds]
92+
evecs = np.diag(np.diag(D) ** (1 / 2)) @ evecs
93+
# return evals, evecs, z, D_root
94+
scatter_df = pd.DataFrame()
95+
for i in range(1, n_components + 1):
96+
scatter_df[f"Lap-{i+1}"] = evecs[:, i]
97+
scatter_df["Signal flow"] = z
98+
if return_evals:
99+
return scatter_df, evals
100+
else:
101+
return scatter_df
102+
103+
104+
def get_template_mat(A):
105+
total_synapses = np.sum(A)
106+
upper_triu_inds = np.triu_indices_from(A, k=1)
107+
filler = total_synapses / len(upper_triu_inds[0])
108+
upper_triu_template = np.zeros_like(A)
109+
upper_triu_template[upper_triu_inds] = filler
110+
return upper_triu_template
111+
112+
113+
def invert_permutation(p):
114+
"""The argument p is assumed to be some permutation of 0, 1, ..., len(p)-1.
115+
Returns an array s, where s[i] gives the index of i in p.
116+
"""
117+
s = np.empty(p.size, p.dtype)
118+
s[p] = np.arange(p.size)
119+
return s
120+
121+
122+
# %% [markdown]
123+
# ## Generate a "perfect" feedforward network (stochastic block model)
124+
low_p = 0.01
125+
diag_p = 0.1
126+
feedforward_p = 0.2
127+
community_sizes = np.array(5 * [20])
128+
block_probs = get_feedforward_B(low_p, diag_p, feedforward_p)
129+
A = sbm(community_sizes, block_probs, directed=True, loops=False)
130+
n_verts = A.shape[0]
131+
132+
133+
plt.figure(figsize=(10, 10))
134+
plt.title("Feedforward SBM block probability matrix")
135+
sns.heatmap(block_probs, annot=True, square=True, cmap="Reds", cbar=False)
136+
stashfig("ffwSBM-B")
137+
plt.show()
138+
139+
heatmap(A, cbar=False, title="Feedforward SBM sampled adjacency matrix")
140+
stashfig("ffwSBM-adj")
141+
plt.show()
142+
143+
labels = n_to_labels(community_sizes).astype(str)
144+
145+
# %% [markdown]
146+
# # Demonstrate that FAQ works
147+
# Shuffle the true adjacency matrix and then show that it can be recovered
148+
149+
150+
shuffle_inds = np.random.permutation(n_verts)
151+
B = A[np.ix_(shuffle_inds, shuffle_inds)]
152+
153+
faq = FastApproximateQAP(
154+
max_iter=30,
155+
eps=0.0001,
156+
init_method="rand",
157+
n_init=10,
158+
shuffle_input=False,
159+
maximize=True,
160+
)
161+
162+
A_found, B_found = faq.fit_predict(A, B)
163+
perm_inds = faq.perm_inds_
164+
165+
heatmap(
166+
A - B_found, title="Diff between true and FAQ-prediced adjacency", vmin=-1, vmax=1
167+
)
168+
169+
170+
# %%
171+
from sgm import ScipyJVClassicSGM, JVSparseSGM
172+
173+
from graspy.match import SinkhornKnopp
174+
175+
176+
def doubly_stochastic(n, barycenter=False):
177+
sk = SinkhornKnopp()
178+
K = np.random.rand(
179+
n, n
180+
) # generate a nxn matrix where each entry is a random integer [0,1]
181+
for i in range(10): # perform 10 iterations of Sinkhorn balancing
182+
K = sk.fit(K)
183+
if barycenter:
184+
J = np.ones((n, n)) / float(n) # initialize J, a doubly stochastic barycenter
185+
P = (K + J) / 2
186+
else:
187+
P = K
188+
return P
189+
190+
191+
doubly_stochastic(10)
192+
193+
#%%
194+
195+
from scipy.sparse import csr_matrix
196+
197+
n_verts = A.shape[0]
198+
n_sims = 10
199+
200+
A = csr_matrix(A)
201+
B = csr_matrix(A)
202+
# P = csr_matrix(P)
203+
204+
205+
for i in range(n_sims):
206+
P = doubly_stochastic(n_verts, barycenter=False)
207+
P = csr_matrix(P)
208+
sgm = JVSparseSGM(A, B, P)
209+
node_map = sgm.run(num_iters=100, tolerance=0, verbose=True)
210+
P_out = csr_matrix((np.ones(n_verts), (np.arange(n_verts), node_map)))
211+
B_out = P_out @ B @ P_out.T
212+
print((A != B_out).sum())
213+
B_out = B_out.todense()
214+
# heatmap(A.todense() - B_out)
215+
216+
A = A.todense()
217+
# B_out = B_out.todense()
218+
heatmap(B_out)
219+
heatmap(A)
220+
221+
222+
# %%
223+
#!/usr/bin/env python
224+
225+
"""
226+
examples/synthetic/main.py
227+
"""
228+
229+
import sys
230+
import numpy as np
231+
from scipy import sparse
232+
233+
from sgm import JVSparseSGM
234+
from
235+
236+
def make_perm(num_nodes, num_seeds):
237+
P = sparse.eye(num_nodes).tocsr()
238+
239+
perm = np.arange(num_nodes)
240+
perm[num_seeds:] = np.random.permutation(perm[num_seeds:])
241+
242+
return P[perm]
243+
244+
245+
def make_init(num_nodes, num_seeds):
246+
P = sparse.csr_matrix((num_nodes, num_nodes))
247+
# P[:num_seeds, :num_seeds] = sparse.eye(num_seeds)
248+
return P
249+
250+
251+
# --
252+
# Create data
253+
254+
num_nodes = 128
255+
num_seeds = 0
256+
257+
# Random symmetric matrix
258+
A = sparse.random(num_nodes, num_nodes, density=0.1)
259+
A = ((A + A.T) > 0).astype(np.float32)
260+
261+
# Random permutation matrix that keeps first `num_seeds` nodes the same
262+
P_act = make_perm(num_nodes=num_nodes, num_seeds=num_seeds)
263+
264+
# Permute A according to P_act
265+
B = P_act @ A @ P_act.T
266+
267+
assert (A[:num_nodes, :num_nodes] != B[:num_nodes, :num_nodes]).sum() > 0
268+
assert (A[:num_seeds, :num_seeds] != B[:num_seeds, :num_seeds]).sum() == 0
269+
270+
# --
271+
# Run SGM
272+
273+
P_init = make_init(num_nodes=num_nodes, num_seeds=num_seeds)
274+
275+
n_sims = 100
276+
best_num_disagreements = np.inf
277+
best_B = 0
278+
for i in range(n_sims):
279+
P_init = doubly_stochastic(num_nodes)
280+
P_init = csr_matrix(P_init)
281+
sgm = JVSparseSGM(A=A, B=B, P=P_init, verbose=False)
282+
node_map = sgm.run(num_iters=100, tolerance=10)
283+
P_out = sparse.csr_matrix((np.ones(num_nodes), (np.arange(num_nodes), node_map)))
284+
B_perm = P_out @ B @ P_out.T
285+
num_disagreements = (
286+
A[:num_nodes, :num_nodes] != B_perm[:num_nodes, :num_nodes]
287+
).sum()
288+
print("num_disagreements=%d" % num_disagreements)
289+
n_edges = A.sum()
290+
print(f"Proportional: {num_disagreements / (2*n_edges)}")
291+
if num_disagreements < best_num_disagreements:
292+
best_B = B_perm
293+
294+
if num_disagreements == 0:
295+
break
296+
297+
heatmap(A.todense() - best_B.todense(), vmin=-1, vmax=1)
298+
299+
#%%
300+
# --
301+
# Check number of disagreements after SGM
302+
303+
304+
heatmap(A.todense()[:100, :100])
305+
heatmap(B_perm.todense()[:100, :100])
306+
307+
308+
# If worked perfectly, `P_out @ P_act` should be identity matrix
309+
# ((P_out @ P_act) != sparse.eye(num_nodes)).sum()
310+
311+
312+
# %%
313+
from sgm import ScipyJVClassicSGM
314+
sgm = ScipyJVClassicSGM(A, B, P_init)
315+
sgm.run(num_iters=100, tolerance=10)
316+
317+
# %%

0 commit comments

Comments
 (0)