2323 # anndata not installed
2424 pass
2525
26- from .utils import (elementwise_minimum ,
27- elementwise_maximum ,
28- set_diagonal )
26+ from . import utils
2927
3028
3129class Base (object ):
@@ -318,6 +316,10 @@ class BaseGraph(with_metaclass(abc.ABCMeta, Base)):
318316 Min-max symmetrization constant.
319317 K = `theta * min(K, K.T) + (1 - theta) * max(K, K.T)`
320318
319+ anisotropy : float, optional (default: 0)
320+ Level of anisotropy between 0 and 1
321+ (alpha in Coifman & Lafon, 2006)
322+
321323 initialize : `bool`, optional (default : `True`)
322324 if false, don't create the kernel matrix.
323325
@@ -336,8 +338,10 @@ class BaseGraph(with_metaclass(abc.ABCMeta, Base)):
336338 diff_op : synonym for `P`
337339 """
338340
339- def __init__ (self , kernel_symm = '+' ,
341+ def __init__ (self ,
342+ kernel_symm = '+' ,
340343 theta = None ,
344+ anisotropy = 0 ,
341345 gamma = None ,
342346 initialize = True , ** kwargs ):
343347 if gamma is not None :
@@ -351,6 +355,10 @@ def __init__(self, kernel_symm='+',
351355 self .kernel_symm = kernel_symm
352356 self .theta = theta
353357 self ._check_symmetrization (kernel_symm , theta )
358+ if not (isinstance (anisotropy , numbers .Real ) and 0 <= anisotropy <= 1 ):
359+ raise ValueError ("Expected 0 <= anisotropy <= 1. "
360+ "Got {}" .format (anisotropy ))
361+ self .anisotropy = anisotropy
354362
355363 if initialize :
356364 tasklogger .log_debug ("Initializing kernel..." )
@@ -395,6 +403,7 @@ def _build_kernel(self):
395403 """
396404 kernel = self .build_kernel ()
397405 kernel = self .symmetrize_kernel (kernel )
406+ kernel = self .apply_anisotropy (kernel )
398407 if (kernel - kernel .T ).max () > 1e-5 :
399408 warnings .warn ("K should be symmetric" , RuntimeWarning )
400409 if np .any (kernel .diagonal == 0 ):
@@ -412,8 +421,8 @@ def symmetrize_kernel(self, K):
412421 elif self .kernel_symm == 'theta' :
413422 tasklogger .log_debug (
414423 "Using theta symmetrization (theta = {})." .format (self .theta ))
415- K = self .theta * elementwise_minimum (K , K .T ) + \
416- (1 - self .theta ) * elementwise_maximum (K , K .T )
424+ K = self .theta * utils . elementwise_minimum (K , K .T ) + \
425+ (1 - self .theta ) * utils . elementwise_maximum (K , K .T )
417426 elif self .kernel_symm is None :
418427 tasklogger .log_debug ("Using no symmetrization." )
419428 pass
@@ -424,11 +433,27 @@ def symmetrize_kernel(self, K):
424433 "Got {}" .format (self .theta ))
425434 return K
426435
436+ def apply_anisotropy (self , K ):
437+ if self .anisotropy == 0 :
438+ # do nothing
439+ return K
440+ else :
441+ if sparse .issparse (K ):
442+ d = np .array (K .sum (1 )).flatten ()
443+ K = K .tocoo ()
444+ K .data = K .data / ((d [K .row ] * d [K .col ]) ** self .anisotropy )
445+ K = K .tocsr ()
446+ else :
447+ d = K .sum (1 )
448+ K = K / (np .outer (d , d ) ** self .anisotropy )
449+ return K
450+
427451 def get_params (self ):
428452 """Get parameters from this object
429453 """
430454 return {'kernel_symm' : self .kernel_symm ,
431- 'theta' : self .theta }
455+ 'theta' : self .theta ,
456+ 'anisotropy' : self .anisotropy }
432457
433458 def set_params (self , ** params ):
434459 """Set parameters on this object
@@ -450,6 +475,9 @@ def set_params(self, **params):
450475 """
451476 if 'theta' in params and params ['theta' ] != self .theta :
452477 raise ValueError ("Cannot update theta. Please create a new graph" )
478+ if 'anisotropy' in params and params ['anisotropy' ] != self .anisotropy :
479+ raise ValueError (
480+ "Cannot update anisotropy. Please create a new graph" )
453481 if 'kernel_symm' in params and \
454482 params ['kernel_symm' ] != self .kernel_symm :
455483 raise ValueError (
@@ -580,6 +608,30 @@ def to_pygsp(self, **kwargs):
580608 precomputed = "affinity" , use_pygsp = True ,
581609 ** kwargs )
582610
611+ def to_igraph (self , attribute = "weight" , ** kwargs ):
612+ """Convert to an igraph Graph
613+
614+ Uses the igraph.Graph.Weighted_Adjacency constructor
615+
616+ Parameters
617+ ----------
618+ attribute : str, optional (default: "weight")
619+ kwargs : additional arguments for igraph.Graph.Weighted_Adjacency
620+ """
621+ try :
622+ import igraph as ig
623+ except ImportError :
624+ raise ImportError ("Please install igraph with "
625+ "`pip install --user python-igraph`." )
626+ try :
627+ W = self .W
628+ except AttributeError :
629+ # not a pygsp graph
630+ W = self .K .copy ()
631+ W = utils .set_diagonal (W , 0 )
632+ return ig .Graph .Weighted_Adjacency (utils .to_dense (W ).tolist (),
633+ attr = attribute , ** kwargs )
634+
583635
584636class PyGSPGraph (with_metaclass (abc .ABCMeta , pygsp .graphs .Graph , Base )):
585637 """Interface between BaseGraph and PyGSP.
@@ -634,7 +686,7 @@ def _build_weight_from_kernel(self, kernel):
634686
635687 weight = kernel .copy ()
636688 self ._diagonal = weight .diagonal ().copy ()
637- weight = set_diagonal (weight , 0 )
689+ weight = utils . set_diagonal (weight , 0 )
638690 return weight
639691
640692
0 commit comments