@@ -42,8 +42,9 @@ def reduce_graph(nx_graph, output_dim):
4242 return reduce_graph_efficiently (nx_graph , output_dim , add_supernode = True )
4343
4444
45- def reduce_graph_efficiently (nx_graph , output_dim , add_supernode = False ,
46- eigendecomp_strategy = 'smart' ):
45+ def reduce_graph_efficiently (
46+ nx_graph , output_dim , add_supernode = False , eigendecomp_strategy = "smart"
47+ ):
4748 """
4849 Run PCA on the ETCD of the input NetworkX graph
4950
@@ -90,38 +91,38 @@ def reduce_graph_efficiently(nx_graph, output_dim, add_supernode=False,
9091 :class:`numpy.ndarray`
9192 The reduced data in output_dim dimensions
9293 """
93- LOG .debug (' Entering reduce_graph' )
94+ LOG .debug (" Entering reduce_graph" )
9495 assert output_dim < len (nx_graph )
95- LOG .info (' Calculating Laplacian L' )
96+ LOG .info (" Calculating Laplacian L" )
9697 L = nx .laplacian_matrix (nx_graph )
97- LOG .debug (' L.shape: {}' .format (L .shape ))
98+ LOG .debug (" L.shape: {}" .format (L .shape ))
9899 if add_supernode :
99100 L = _add_supernode_to_laplacian (L )
100- LOG .info (' Calculating nullity of L as connected components of nx_graph' )
101+ LOG .info (" Calculating nullity of L as connected components of nx_graph" )
101102 nullity = nx .number_connected_components (nx_graph )
102- LOG .info (' Calculating smallest eigenvalues of L & corresponding eigenvectors' )
103- (E , U ) = _eigendecomp (eigendecomp_strategy , L , output_dim + nullity , which = 'SM' )
104- LOG .debug (' Eigenvalues: {}' .format (E ))
105- LOG .info (' Assembling PCA result' )
103+ LOG .info (" Calculating smallest eigenvalues of L & corresponding eigenvectors" )
104+ (E , U ) = _eigendecomp (eigendecomp_strategy , L , output_dim + nullity , which = "SM" )
105+ LOG .debug (" Eigenvalues: {}" .format (E ))
106+ LOG .info (" Assembling PCA result" )
106107 # If we added a supernode, now remove it
107108 if add_supernode :
108109 # Remove data row
109110 U = U [:- 1 , :]
110111 # Remove eigenpair with negative value, which correspond to supernode
111112 neg_indexes = np .where (E < 0.0 )
112- LOG .debug (' Neg indexes: {}' .format (neg_indexes ))
113+ LOG .debug (" Neg indexes: {}" .format (neg_indexes ))
113114 E = np .delete (E , neg_indexes )
114115 U = np .delete (U , neg_indexes , axis = 1 )
115116 # Remove the 0 eigenvalues and corresponding eigenvectors
116117 # Use tolerance value 10 x from numpy.linalg.matrix_rank
117118 tol = E .max () * max (L .shape ) * np .finfo (float ).eps * 10
118- LOG .debug (' Using tolerance {}' .format (tol ))
119+ LOG .debug (" Using tolerance {}" .format (tol ))
119120 zero_indexes = [i for i in range (len (E )) if abs (E [i ]) < tol ]
120121 E = np .delete (E , zero_indexes )
121122 U = np .delete (U , zero_indexes , axis = 1 )
122123 # Invert eigenvalues to get largest eigenvalues of L-pseudoinverse
123- Ep = 1 / E
124- LOG .debug (' Filtered & Inverted Eigenvalues: {}' .format (Ep ))
124+ Ep = 1 / E
125+ LOG .debug (" Filtered & Inverted Eigenvalues: {}" .format (Ep ))
125126 # Orient Eigenvectors
126127 _orient_eigenvectors (U )
127128 # Assemble into the right structure
@@ -132,7 +133,7 @@ def reduce_graph_efficiently(nx_graph, output_dim, add_supernode=False,
132133 return X
133134
134135
135- def reduce_graph_naively (nx_graph , output_dim , eigendecomp_strategy = ' exact' ):
136+ def reduce_graph_naively (nx_graph , output_dim , eigendecomp_strategy = " exact" ):
136137 """
137138 Run PCA on the ETCD of a NetworkX graph using a slow but precise method
138139
@@ -165,17 +166,19 @@ def reduce_graph_naively(nx_graph, output_dim, eigendecomp_strategy='exact'):
165166 :class:`numpy.ndarray`
166167 The reduced data in output_dim dimensions
167168 """
168- LOG .debug (' Entering naive_reduce_graph' )
169+ LOG .debug (" Entering naive_reduce_graph" )
169170 L = nx .laplacian_matrix (nx_graph ).todense ()
170- LOG .info (' Calculating Moore-Penrose inverse of the Laplacian L' )
171+ LOG .info (" Calculating Moore-Penrose inverse of the Laplacian L" )
171172 Li = np .linalg .pinv (L )
172- LOG .info ('Calculating largest eigenvalues of L-inverse & corresponding eigenvectors' )
173- (E , U ) = _eigendecomp (eigendecomp_strategy , Li , output_dim , which = 'LM' )
173+ LOG .info (
174+ "Calculating largest eigenvalues of L-inverse & corresponding eigenvectors"
175+ )
176+ (E , U ) = _eigendecomp (eigendecomp_strategy , Li , output_dim , which = "LM" )
174177 # Flip so largest eigen first
175178 E = E [::- 1 ]
176179 U = np .fliplr (U )
177- LOG .debug (' Eigenvalues: {}' .format (E ))
178- LOG .info (' Assembling PCA result' )
180+ LOG .debug (" Eigenvalues: {}" .format (E ))
181+ LOG .info (" Assembling PCA result" )
179182 # Assemble into the right structure
180183 X = np .zeros ((output_dim , len (nx_graph )))
181184 sqrtE = np .sqrt (E )
@@ -185,7 +188,7 @@ def reduce_graph_naively(nx_graph, output_dim, eigendecomp_strategy='exact'):
185188
186189
187190def _add_supernode_to_laplacian (L ):
188- L_padded = np .ones ([n + 1 for n in L .shape ])
191+ L_padded = np .ones ([n + 1 for n in L .shape ])
189192 L_padded [:- 1 , :- 1 ] = L .todense ()
190193 return L_padded
191194
@@ -195,9 +198,9 @@ def _orient_eigenvectors(U):
195198 for i in range (U .shape [1 ]):
196199 try :
197200 if next (u for u in U [:, i ] if np .fabs (u ) > threshold ) < 0.0 :
198- U [:, i ] = - U [:, i ]
201+ U [:, i ] = - U [:, i ]
199202 except StopIteration :
200- LOG .debug (' Zero eigenvector at index {}' .format (i ))
203+ LOG .debug (" Zero eigenvector at index {}" .format (i ))
201204 continue
202205 return U
203206
@@ -235,9 +238,9 @@ def _eigendecomp(eigendecomp_strategy, M, output_dim, which, *args, **kwargs):
235238 The corresponding eigenvectors of M
236239
237240 """
238- if eigendecomp_strategy == ' exact' :
241+ if eigendecomp_strategy == " exact" :
239242 return _exact_eigendecomp (M , output_dim , which )
240- elif eigendecomp_strategy == ' sparse' :
243+ elif eigendecomp_strategy == " sparse" :
241244 return _sparse_eigendecomp (M , output_dim , which , * args , ** kwargs )
242245 else :
243246 if M .shape [0 ] < 1000 :
@@ -247,50 +250,58 @@ def _eigendecomp(eigendecomp_strategy, M, output_dim, which, *args, **kwargs):
247250
248251
249252def _exact_eigendecomp (M , output_dim , which ):
250- LOG .debug (' Using _exact_eigendecomp' )
253+ LOG .debug (" Using _exact_eigendecomp" )
251254 if scipy .sparse .issparse (M ):
252255 M = M .todense ()
253256 E , U = scipy .linalg .eigh (M )
254257 # Cut out eigenpairs
255- if which == 'SM' :
258+ if which == "SM" :
256259 E = E [:output_dim ]
257260 U = U [:, :output_dim ]
258261 U = _orient_eigenvectors (U )
259- elif which == 'LM' :
260- E = E [E .shape [0 ] - output_dim :]
261- U = U [:, U .shape [1 ] - output_dim :]
262+ elif which == "LM" :
263+ E = E [E .shape [0 ] - output_dim :]
264+ U = U [:, U .shape [1 ] - output_dim :]
262265 U = _orient_eigenvectors (U )
263266 else :
264- raise NotImplementedError (' Unknown setting for `which`: {}' .format (which ))
267+ raise NotImplementedError (" Unknown setting for `which`: {}" .format (which ))
265268 return E , U
266269
267270
268271def _sparse_eigendecomp (M , output_dim , which , tol = 0.000000001 , _attempt = 0 , ** kwargs ):
269- LOG .debug (' Using _sparse_eigendecomp' )
272+ LOG .debug (" Using _sparse_eigendecomp" )
270273 try :
271- M = M .astype ('d' )
272- if which == 'SM' :
274+ M = M .astype ("d" )
275+ if which == "SM" :
273276 # Use shift-invert method to calculate smallest eigenpairs.
274277 # Use very small sigma since `sigma=0.0` fails with
275278 # RuntimeError: Factor is exactly singular
276- E , U = scipy .sparse .linalg .eigsh (M , output_dim , sigma = 0.00001 ,
277- which = 'LM' , tol = tol , ** kwargs )
279+ E , U = scipy .sparse .linalg .eigsh (
280+ M , output_dim , sigma = 0.00001 , which = "LM" , tol = tol , ** kwargs
281+ )
278282 else :
279- E , U = scipy .sparse .linalg .eigsh (M , output_dim , which = which , tol = tol , ** kwargs )
283+ E , U = scipy .sparse .linalg .eigsh (
284+ M , output_dim , which = which , tol = tol , ** kwargs
285+ )
280286 U = _orient_eigenvectors (U )
281287 return E , U
282288 except ArpackNoConvergence as e :
283289 if _attempt > 2 :
284- LOG .error (' Eigendecomp did not converge. Bailing.' )
285- raise e
290+ LOG .error (" Eigendecomp did not converge. Bailing." )
291+ raise e
286292 LOG .info (e )
287293 new_tol = tol * 10
288- LOG .info ('Eigendecomp failed to converge, retrying with tolerance {}' .format (new_tol ))
289- return _sparse_eigendecomp (M , output_dim , which = which , tol = new_tol , _attempt = _attempt + 1 )
294+ LOG .info (
295+ "Eigendecomp failed to converge, retrying with tolerance {}" .format (new_tol )
296+ )
297+ return _sparse_eigendecomp (
298+ M , output_dim , which = which , tol = new_tol , _attempt = _attempt + 1
299+ )
290300
291301
292- def plot_2d (pca_output_2d , colormap_name = ' winter' ):
302+ def plot_2d (pca_output_2d , colormap_name = " winter" ):
293303 import matplotlib .pyplot as plt
304+
294305 x = pca_output_2d [0 , :]
295306 y = pca_output_2d [1 , :]
296307 colormap = plt .get_cmap (colormap_name )
@@ -312,9 +323,12 @@ def draw_graph(nx_graph):
312323 The graph to be plotted
313324 """
314325 import matplotlib .pyplot as plt
326+
315327 reduced_2 = reduce_graph (nx_graph , 2 )
316328 for edge in nx_graph .edges ():
317- plt .plot ([reduced_2 [0 , edge [0 ]], reduced_2 [0 , edge [1 ]]],
318- [reduced_2 [1 , edge [0 ]], reduced_2 [1 , edge [1 ]]],
319- 'b-' )
329+ plt .plot (
330+ [reduced_2 [0 , edge [0 ]], reduced_2 [0 , edge [1 ]]],
331+ [reduced_2 [1 , edge [0 ]], reduced_2 [1 , edge [1 ]]],
332+ "b-" ,
333+ )
320334 plot_2d (reduced_2 )
0 commit comments