# -*- coding: utf-8 -*-# SPDX-FileCopyrightText: 2015-2023 Tanguy Fardet# SPDX-License-Identifier: GPL-3.0-or-later# nngt/lib/graph_backends.py""" Tools to interact with the graph libraries backends """fromimportlibimportreloadimportloggingimportsysimportnngtfrom.errorsimportnot_implementedfrom.loggerimport_log_messagefrom.test_functionsimportmpi_barrierlogger=logging.getLogger(__name__)# ------------------- ## Graph library usage ## ------------------- #analyze_graph={'adjacency':not_implemented,'assortativity':not_implemented,'betweenness':not_implemented,'diameter':not_implemented,'ebetweenness':not_implemented,'get_edges':not_implemented,'nbetweenness':not_implemented,'reciprocity':not_implemented,'scc':not_implemented,'wcc':not_implemented,}# use library function
[docs]@mpi_barrierdefuse_backend(backend,reloading=True,silent=False):''' Allows the user to switch to a specific graph library as backend. .. warning :: If :class:`~nngt.Graph` objects have already been created, they will no longer be compatible with NNGT methods. Parameters ---------- backend : string Name of a graph library among 'graph_tool', 'igraph', 'networkx', or 'nngt'. reloading : bool, optional (default: True) Whether the graph objects should be reloaded through `reload` (this should always be set to True except when NNGT is first initiated!) silent : bool, optional (default: False) Whether the changes made to the configuration should be logged at the DEBUG (True) or INFO (False) level. '''# save old config except for graph-library dataold_config=nngt.get_config(detailed=True)forkin("graph","backend","library"):delold_config[k]# try to switch graph librarysuccess=Falseerror=Noneifbackend=="graph-tool":try:success=_set_graph_tool()exceptExceptionase:error=eelifbackend=="igraph":try:success=_set_igraph()exceptExceptionase:error=eelifbackend=="networkx":try:success=_set_networkx()exceptExceptionase:error=eelifbackend=="nngt":try:success=_set_nngt()exceptExceptionase:error=eelse:raiseValueError(f"Invalid graph library requested: '{backend}'.")# always reload analysis (necessary if a requested library failed partway)ifnngt._initialized:reload(sys.modules["nngt"].analysis.clustering)reload(sys.modules["nngt"].analysis.graph_analysis)reload(sys.modules["nngt"].analysis)# must come after graph_analysisifreloading:# reload the others if necessaryreload(sys.modules["nngt"].generation.graph_connectivity)reload(sys.modules["nngt"].generation)ifnngt._config['with_plot']:reload(sys.modules["nngt"].plot)reload(sys.modules["nngt"].lib)reload(sys.modules["nngt"].core)# reload first for Graph inheritancereload(sys.modules["nngt"].core.graph)reload(sys.modules["nngt"].core.spatial_graph)reload(sys.modules["nngt"].core.networks)fromnngt.core.graphimportGraphfromnngt.core.spatial_graphimportSpatialGraphfromnngt.core.networksimportNetwork,SpatialNetworksys.modules["nngt"].Graph=Graphsys.modules["nngt"].SpatialGraph=SpatialGraphsys.modules["nngt"].Network=Networksys.modules["nngt"].SpatialNetwork=SpatialNetwork# make sure all other config values are unchangednngt.set_config(old_config,silent=True)# logifsuccess:ifsilent:_log_message(logger,"DEBUG","Successfuly switched to "+backend+".")else:_log_message(logger,"INFO","Successfuly switched to "+backend+".")else:ifsilent:_log_message(logger,"DEBUG","Error, could not switch to "+backend+": ""{}.".format(error))else:_log_message(logger,"WARNING","Error, could not switch to "+backend+": ""{}.".format(error))iferrorisnotNone:raiseerror
# ----------------- ## Loading functions ## ----------------- #def_set_graph_tool():''' Set graph-tool as graph library, store relevant items in config and analyze graph dictionaries. '''importgraph_toolasglibfromgraph_toolimportGraphasGraphLibnngt._config["backend"]="graph-tool"nngt._config["library"]=glibnngt._config["graph"]=GraphLib# store the functionsfrom..analysisimportgt_functions_store_functions(nngt.analyze_graph,gt_functions)returnTruedef_set_igraph():''' Set igraph as graph library, store relevant items in config and analyze graph dictionaries. '''importigraphasglibfromigraphimportGraphasGraphLibnngt._config["backend"]="igraph"nngt._config["library"]=glibnngt._config["graph"]=GraphLib# store the functionsfrom..analysisimportig_functions_store_functions(nngt.analyze_graph,ig_functions)returnTruedef_set_networkx():importnetworkxasglibifglib.__version__<'2.4':raiseImportError("`networkx {} is ".format(glib.__version__)+"installed while version >= 2.4 is required.")fromnetworkximportDiGraphasGraphLibnngt._config["backend"]="networkx"nngt._config["library"]=glibnngt._config["graph"]=GraphLib# store the functionsfrom..analysisimportnx_functions_store_functions(nngt.analyze_graph,nx_functions)returnTruedef_set_nngt():nngt._config["backend"]="nngt"nngt._config["library"]=nngtnngt._config["graph"]=object# analysis functionsdef_notimplemented(*args,**kwargs):raiseNotImplementedError("Install a graph library to use.")defget_edges(g):returng.edges_arrayfromnngt.analysis.nngt_functionsimportreciprocity,adj_mat# store functionsnngt.analyze_graph["assortativity"]=_notimplementednngt.analyze_graph["betweenness"]=_notimplementednngt.analyze_graph["diameter"]=_notimplementednngt.analyze_graph["closeness"]=_notimplementednngt.analyze_graph["reciprocity"]=reciprocitynngt.analyze_graph["connected_components"]=_notimplementednngt.analyze_graph["adjacency"]=adj_matnngt.analyze_graph["get_edges"]=get_edgesreturnTruedef_store_functions(analysis_dict,module):''' Store functions from module '''analysis_dict["assortativity"]=module.assortativityanalysis_dict["betweenness"]=module.betweennessanalysis_dict["closeness"]=module.closenessanalysis_dict["connected_components"]=module.connected_componentsanalysis_dict["diameter"]=module.diameteranalysis_dict["reciprocity"]=module.reciprocityanalysis_dict["adjacency"]=module.adj_matanalysis_dict["get_edges"]=module.get_edges