后端和配置#
后端允许你执行一个替代的后端实现,而不是使用NetworkX的纯Python字典实现。配置提供了库级别的配置设置存储,这些设置也可以来自环境变量。
Note
NetworkX的后端和配置系统正在频繁更新和改进。使用后端的用户界面通常是稳定的。在极少数情况下,如果需要对后端或配置API进行兼容性破坏的更改,可能不会遵循NetworkX的 标准弃用政策。此灵活性旨在允许我们快速响应用户反馈并改进可用性,同时将尽量避免不必要的中断。NetworkX后端的开发人员应定期监控更新以保持兼容性。参与每周的 NX-dispatch会议 是保持更新并参与持续讨论的绝佳方式。
Backends#
后端用户文档#
NetworkX采用插件调度架构,这意味着我们可以通过最小的代码更改来插入和拔出后端。一个有效的NetworkX后端指定了 入口点 ,命名为 networkx.backends
,并在安装时(不是导入时)提供一个可选的 networkx.backend_info
。这允许NetworkX将函数调用分派(重定向)到后端,使执行流程流向指定的后端实现,类似于将充电器插入插座将电力重定向到手机的方式。这种设计增强了灵活性和集成性,使NetworkX更具适应性和效率。
在安装包后,有三种主要方式使用后端。您可以设置环境变量并运行与NetworkX相同的代码。您可以使用NetworkX函数的 backend=...
关键字参数。或者,您可以将NetworkX图转换为后端图类型,并调用该后端支持的NetworkX函数。环境变量和后端关键字会自动将您的NetworkX图转换为后端类型。手动转换它允许您在多个函数调用中使用相同的后端图,减少转换时间。
例如,您可以在启动python之前设置环境变量,以请求所有可分派函数自动分派到给定后端:
bash> NETWORKX_AUTOMATIC_BACKENDS=cugraph python my_networkx_script.py
或者您可以指定后端作为关键字参数:
nx.betweenness_centrality(G, k=10, backend="parallel")
或者您可以将NetworkX图对象 G
转换为特定于后端的类似图对象,然后将其传递给NetworkX函数:
H = nx_parallel.ParallelGraph(G)
nx.betweenness_centrality(H, k=10)
第一种方法在您不想更改NetworkX代码并只想在不同后端上运行代码时很有用。第二种方法在需要传递额外的后端特定参数时很方便,例如:
nx.betweenness_centrality(G, k=10, backend="parallel", get_chunks=get_chunks)
这里, get_chunks
不是NetworkX参数,而是nx_parallel特定的参数。
NetworkX还提供了一个非常基本的日志系统,可以帮助您验证您指定的后端是否正在实现。这很可能会在未来变得更加强大。您可以启用networkx的后端日志记录器,如下所示:
import logging
nxl = logging.getLogger("networkx")
nxl.addHandler(logging.StreamHandler())
nxl.setLevel(logging.DEBUG)
您可以通过运行以下命令来禁用它:
nxl.setLevel(logging.CRITICAL)
参考 这里 以了解更多关于Python中的日志记录功能。
这是如何工作的?#
您可能在代码库中的许多NetworkX函数上看到了 @nx._dispatchable
装饰器。这个装饰器函数通过将NetworkX函数分派到指定的后端(如果可用),或者在没有指定或可用后端时使用NetworkX运行它来工作。它检查指定的后端是否有效并已安装。如果没有,它会引发一个 ImportError
。它还解析从提供的 args
和 kwargs
中的图参数,处理图作为位置参数或关键字参数传递的情况。然后,它检查解析的图中是否有来自后端的图,方法是检查它们是否具有 __networkx_backend__
属性。属性 __networkx_backend__
包含一个字符串,其中包含 entry_point
的名称(稍后会详细介绍)。如果有来自后端的图,它根据 backend_priority
配置确定后端的优先级。如果有可分派的图(即来自后端的图),它会检查所有图是否来自同一个后端。如果没有,它会引发一个 TypeError
。如果指定了后端并且与图的后端匹配,它会加载后端并调用后端上的相应函数以及额外的后端特定 backend_kwargs
。调用函数后,如果启用了日志记录,networkx记录器会显示 DEBUG
消息。如果没有找到兼容的后端或后端未实现该函数,它会引发一个 NetworkXNotImplemented
异常。如果函数改变了输入图或返回图、图生成器或加载器,它会尝试使用自动转换的后端转换并运行该函数。并且只有在 backend.should_run(...)
返回 True
时才会转换并运行。如果没有使用后端,它会回退到使用NetworkX运行原始函数。有关更多详细信息,请参阅 _dispatchable
类的 __call__
方法。
NetworkX库不需要知道后端的存在就能工作。只要后端包创建了 entry_point
并提供了正确的接口,当用户使用上述三种方法之一请求时,它就会被调用。一些后端已经与NetworkX开发人员合作,以确保顺利运行。它们如下:
- graphblas :
启用OpenMP的稀疏线性代数后端。
- cugraph :
GPU加速后端。
- parallel :
NetworkX算法的并行后端。
- loopback :
仅用于测试目的,不是一个真正的后端。
请注意, backend_name
例如是 parallel
,安装的包是 nx-parallel
,我们在导入包时使用 nx_parallel
。
后端开发者文档#
创建自定义后端#
定义一个
BackendInterface
对象:注意,
BackendInterface
不一定是一个类。它可以是一个类的实例,或者一个模块。您可以在后端的BackendInterface
对象中定义以下方法或函数:convert_from_nx
和convert_to_nx
方法或函数是后端调度工作所必需的。convert_from_nx
的参数是:G
: NetworkX图edge_attrs
dict, 可选字典映射边属性到默认值,如果
G
中缺少。如果为None,则不会转换边属性,默认值可能为1。
node_attrs
dict, 可选字典映射节点属性到默认值,如果
G
中缺少。如果为None,则不会转换节点属性。
preserve_edge_attrs
bool是否保留所有边属性。
preserve_node_attrs
bool是否保留所有节点属性。
preserve_graph_attrs
bool是否保留所有图属性。
preserve_all_attrs
bool是否保留所有图、节点和边属性。
name
str算法的名称。
graph_name
str正在转换的图参数的名称。
can_run
(可选):如果您的后端仅部分实现了某个算法,您可以在
BackendInterface
对象中定义一个can_run(name, args, kwargs)
函数,该函数返回True或False,指示后端是否可以使用给定的参数运行该算法。您还可以返回一个字符串消息,以告知用户为什么该算法不能运行。
should_run
(可选):后端还可以定义
should_run(name, args, kwargs)
,类似于can_run
,但回答后端是否*应该*运行。should_run
仅在执行后端图转换时运行。与can_run
一样,它接收原始参数,因此可以通过检查参数来决定是否应该运行。can_run
在should_run
之前运行,因此should_run
可以假设can_run
为True。如果后端未实现,can_run
和should_run
假设总是返回True,如果后端实现了该算法。
on_start_tests
(可选):后端可以定义一个特殊的
on_start_tests(items)
函数。它将使用发现的NetworkX测试列表调用。每个项目都是一个测试对象,可以使用item.add_marker(pytest.mark.xfail(reason=...))
标记为xfail,如果后端不支持该测试。
添加入口点
为了被NetworkX发现,您的包必须在包的元数据中注册一个 入口点
networkx.backends
,并使用 指向您的调度对象的键 。例如,如果您使用setuptools
管理后端包,您可以在pyproject.toml
文件中 添加以下内容[project.entry-points."networkx.backends"] backend_name = "your_backend_interface_object"
您还可以添加
backend_info
入口点。它指向返回所有后端信息的get_info
函数,这些信息用于在算法的文档页面末尾构建“附加后端实现”框。请注意,get_info
函数不应该导入您的后端包。:[project.entry-points."networkx.backend_info"] backend_name = "your_get_info_function"
get_info
应该返回一个包含以下键值对的字典:backend_name
str 或 None它是传递给
backend
关键字的名称。
project
str 或 None您的后端项目的名称。
package
str 或 None您的后端包的名称。
url
str 或 None这是指向您的后端代码库或文档的URL,将在“附加后端实现”部分中作为
backend_name
的超链接显示。
short_summary
str 或 None您的后端的单行摘要,将在“附加后端实现”部分中显示。
functions
dict 或 None一个字典,将函数名称映射到一个包含函数信息的字典。信息可以包括以下键:
url
: str 或 None 指向function
的源代码或文档的URL。additional_docs
: str 或 None 关于后端函数实现的简短描述或注释。additional_parameters
: dict 或 None 一个字典,将附加参数头映射到它们的简短描述。例如:"additional_parameters": { 'param1 : str, function (default = "chunks")' : "...", 'param2 : int' : "...", }
如果这些键中的任何一个不存在,相应的信息将不会在NetworkX文档网站的“附加后端实现”部分中显示。
请注意,您的后端文档只有在您的后端是NetworkX的受信任后端,并且存在于NetworkX仓库的
circleci/config.yml
和github/workflows/deploy-docs.yml
文件中时,才会出现在官方NetworkX文档中。定义一个后端图类
后端必须创建一个具有
__networkx_backend__
属性的对象,该属性包含一个带有入口点名称的字符串:class BackendGraph: __networkx_backend__ = "backend_name" ...
后端图实例可能有一个
G.__networkx_cache__
字典来启用缓存,并且应该注意在适当的时候清除缓存。
测试自定义后端#
要测试您的自定义后端,您可以在后端上运行NetworkX测试套件。这还确保自定义后端与NetworkX的API兼容。以下步骤将帮助您运行测试:
- 设置后端环境变量:
NETWORKX_TEST_BACKEND
: 将其设置为您的后端的backend_name
,将使NetworkX的调度机制自动将常规NetworkXGraph
、DiGraph
、MultiGraph
等转换为它们的后端等效物,使用your_backend_interface_object.convert_from_nx(G, ...)
函数。NETWORKX_FALLBACK_TO_NX
(默认=False) : 将此变量设置为True
将指示测试使用NetworkXGraph
运行未由您的自定义后端实现的算法。将其设置为False
将仅运行由您的自定义后端实现的算法的测试,其他算法的测试将xfail
。
- 运行测试:
您可以使用以下命令为您的自定义后端调用NetworkX测试:
NETWORKX_TEST_BACKEND=<backend_name> NETWORKX_FALLBACK_TO_NX=True # 或 False pytest --pyargs networkx
测试是如何运行的?#
在分派到后端实现时,使用
_convert_and_call
函数,而在测试时使用_convert_and_call_for_tests
函数。除了测试之外,它还检查返回numpy标量的函数,对于返回图的函数,它运行后端实现和networkx实现,然后将后端图转换为NetworkX图,然后比较它们,并返回networkx图。这可以被视为(务实的)技术债务。我们可能会在未来替换这些检查。- 运行测试时的转换:
使用
<your_backend_interface_object>.convert_from_nx(G, ...)
将NetworkX图转换为后端图。将后端图对象传递给算法的后端实现。
使用
<your_backend_interface_object>.convert_to_nx(result, ...)
将结果转换为NetworkX测试期望的形式。对于nx-loopback,图使用可分派元数据进行复制。
未由后端实现的
|
一个装饰器函数,用于将 |
Configs#
- config#
alias of NetworkXConfig(backend_priority=[], backends=Config(graphblas=Config(), parallel=Config()), cache_converted_graphs=True)
- class NetworkXConfig(**kwargs)[source]#
网络配置,用于控制行为,例如如何使用后端。
支持使用属性和括号表示法进行配置的获取和设置:
>>> nx.config.backend_priority == nx.config["backend_priority"] True
- Parameters:
- backend_priority后端名称列表
启用图形的自动转换,以便为后端实现的算法使用后端图形。优先级给予列表中较早的后端。 默认是空列表。
- backends后端名称到后端配置的映射
配置映射的键是所有已安装的 NetworkX 后端的名称,值是它们的配置作为配置映射。
- cache_converted_graphs布尔值
如果为 True,则将转换后的图形保存到输入图形的缓存中。当自动使用
backend_priority
中的后端或在使用函数调用的backend=
关键字参数时,可能会发生图形转换。缓存可以通过避免重复转换来提高性能,但会使用更多内存。应注意不要手动修改具有缓存图形的图;例如,G[u][v][k] = val
会更改图形,但不会清除缓存。使用诸如G.add_edge(u, v, weight=val)
的方法将清除缓存以保持一致性。G.__networkx_cache__.clear()
手动清除缓存。 默认是 True。
Notes
可以使用环境变量来控制一些默认配置:
NETWORKX_BACKEND_PRIORITY
: 从逗号分隔的名称设置backend_priority
。NETWORKX_CACHE_CONVERTED_GRAPHS
: 如果非空,则将cache_converted_graphs
设置为 True。
这是一个全局配置。在多线程使用时请谨慎。
- class Config(**kwargs)[source]#
NetworkX 配置的基础类。
有两种方法可以使用这个类来创建配置。推荐的方法是通过子类化
Config
并添加文档和注解。>>> class MyConfig(Config): ... '''早餐!''' ... ... eggs: int ... spam: int ... ... def _check_config(self, key, value): ... assert isinstance(value, int) and value >= 0 >>> cfg = MyConfig(eggs=1, spam=5)
另一种方法是直接将初始配置作为关键字参数传递给
Config
实例:>>> cfg1 = Config(eggs=1, spam=5) >>> cfg1 Config(eggs=1, spam=5)
一旦定义,配置项可以被修改,但默认情况下不能被添加或删除。
Config
是一个Mapping
,可以通过属性或方括号获取和设置配置:>>> cfg.eggs = 2 >>> cfg.eggs 2 >>> cfg["spam"] = 42 >>> cfg["spam"] 42
为了方便,还可以在 “with” 语句中设置配置:
>>> with cfg(spam=3): ... print("spam (在上下文中):", cfg.spam) spam (在上下文中): 3 >>> print("spam (上下文后):", cfg.spam) spam (上下文后): 42
子类也可以定义
_check_config
(如上面的例子所示)来确保被赋值的值是有效的:>>> cfg.spam = -1 Traceback (most recent call last): ... AssertionError
如果需要一个更灵活的配置对象,允许添加和删除配置,那么在定义子类时传递
strict=False
:>>> class FlexibleConfig(Config, strict=False): ... default_greeting: str = "Hello" >>> flexcfg = FlexibleConfig() >>> flexcfg.name = "Mr. Anderson" >>> flexcfg FlexibleConfig(default_greeting='Hello', name='Mr. Anderson')