用于 Python 的快速最大流最小切库

Posted

技术标签:

【中文标题】用于 Python 的快速最大流最小切库【英文标题】:Fast max-flow min-cut library for Python 【发布时间】:2011-04-29 20:51:33 【问题描述】:

是否有一个可靠且文档齐全的 Python 库,该库具有 快速 实现算法,可在有向图中找到最大流和最小割?

来自python-graph 的pygraph.algorithms.minmax.maximum_flow 解决了这个问题,但速度非常慢:在具有4000 个节点和11000 个边的有向图中找到最大流和最小切割需要> 1 分钟。我正在寻找至少快一个数量级的东西。

赏金:我在这个问题上提供赏金,看看自提出这个问题以来情况是否发生了变化。如果您对推荐的图书馆有个人经验,则可获得奖励积分!

【问题讨论】:

你尝试过使用 Psyco(psyco.sourceforge.net) 吗?这里的 maximum_flow 代码都是用纯 Python 编写的,因此 Psyco 可以提供巨大的加速。 链接失效 【参考方案1】:

我曾使用graph-tool 完成类似的任务。

Graph-tool 是一个高效的 Python 模块,用于对图形(也称为网络)进行操作和统计分析。他们甚至拥有关于 max-flow algorithms 的出色文档。

目前图形工具支持给定算法:

Edmonds-Karp - 使用 Edmonds-Karp 算法计算图表上的最大流量。 推送重新标记 - 使用推送重新标记算法计算图上的最大流量。 Boykov Kolmogorov - 使用 Boykov-Kolmogorov 算法计算图上的最大流量。

示例取自文档:find maxflow using Boykov-Kolmogorov:

>>> g = gt.load_graph("flow-example.xml.gz") #producing example is in doc
>>> cap = g.edge_properties["cap"]
>>> src, tgt = g.vertex(0), g.vertex(1)
>>> res = gt.boykov_kolmogorov_max_flow(g, src, tgt, cap)
>>> res.a = cap.a - res.a  # the actual flow
>>> max_flow = sum(res[e] for e in tgt.in_edges())
>>> print max_flow
6.92759897841
>>> pos = g.vertex_properties["pos"]
>>> gt.graph_draw(g, pos=pos, pin=True, penwidth=res, output="example-kolmogorov.png")

我使用随机有向图(节点 = 4000,顶点 = 23964)执行了这个示例,所有过程只用了 11 秒

替代库:

igraph - 主要用 C 语言实现,但有 Python 和 R 接口 链接主题"Python packages for graph theory" 或Sage wiki 中的其他选定图表工具。

【讨论】:

Python 包链接已损坏。 Graph-tool 还不支持 Windows。【参考方案2】:

我不知道它是否更快,您需要检查一下,但是您尝试过networkx 吗? 似乎它提供了您正在寻找的functionality,并且根据我的经验,它是一个非常易于使用的库来处理图表。

【讨论】:

如果 networkx 太慢,您可以尝试获取它working in pypy,因为它看起来差不多。 Networkx 是用 Python 实现的,因此比用 C/C++ 实现的 igraph 和 PyMaxflow 慢得多。【参考方案3】:

SciPy,从 1.4.0 开始,在 scipy.sparse.csgraph.maximum_flow 中也有一个实现,它可能更容易用作构建链的一部分(因为该包可通过 pip/conda 获得)。

它通过操纵表示图的邻接矩阵的稀疏矩阵(因此scipy.sparse)工作,因此,底层数据结构接近金属,并且算法本身在 Cython 中实现,性能应该是与例如图形工具。

不同实现在性能方面的比较始终取决于您感兴趣的最大流量图的结构,但作为一个简单的基准测试,我尝试通过 NetworkX、graph-tool 运行具有不同稀疏度的随机图,和 SciPy。它们都可以很好地使用 NumPy 数组,因此为了确保公平竞争,让我们创建方法,以便它们中的每一个都将具有形状 (密度*1000*1000, 3) 的 NumPy 数组作为输入,其行是边,其列是入射到给定边的两个顶点,以及边的容量。

import numpy as np
from scipy.sparse import rand


def make_data(density):
    m = (rand(1000, 1000, density=density, format='coo', random_state=42)*100).astype(np.int32)
    return np.vstack([m.row, m.col, m.data]).T

data01 = make_data(0.1)
data03 = make_data(0.3)
data05 = make_data(0.5)

有了这个,各种框架可以计算出最大流量的值如下:

import graph_tool.all as gt
from scipy.sparse import coo_matrix, csr_matrix
from scipy.sparse.csgraph import maximum_flow
import networkx as nx


def networkx_max_flow(data, primitive):
    m = coo_matrix((data[:, 2], (data[:, 0], data[:, 1])))
    G = nx.from_numpy_array(m.toarray(), create_using=nx.DiGraph())
    return nx.maximum_flow_value(G, 0, 999, capacity='weight', flow_func=primitive)


def graph_tool_max_flow(data, primitive):
    g = gt.Graph()
    cap = g.new_edge_property('int')
    eprops = [cap]
    g.add_edge_list(data, eprops=eprops)
    src, tgt = g.vertex(0), g.vertex(999)
    res = primitive(g, src, tgt, cap)
    res.a = cap.a - res.a
    return sum(res[e] for e in tgt.in_edges())


def scipy_max_flow(data):
    m = csr_matrix((data[:, 2], (data[:, 0], data[:, 1])))
    return maximum_flow(m, 0, 999).flow_value

这样,IPython 基准测试的例子就变成了

%timeit networkx_max_flow(data01, nx.algorithms.flow.shortest_augmenting_path)
%timeit graph_tool_max_flow(data03, gt.push_relabel_max_flow)
%timeit scipy_max_flow(data05)

然后我看到以下结果:

+----------------------------------------------+----------------+----------------+---------------+
|                  Algorithm                   |  Density: 0.1  |  Density: 0.3  |  Density: 0.5 |
+----------------------------------------------+----------------+----------------+---------------+
| nx.algorithms.flow.edmonds_karp              |  1.07s         |  3.2s          |  6.39s        |
| nx.algorithms.flow.preflow_push              |  1.07s         |  3.27s         |  6.18s        |
| nx.algorithms.flow.shortest_augmenting_path  |  1.08s         |  3.25s         |  6.23s        |
| gt.edmonds_karp_max_flow                     |  274ms         |  2.84s         |  10s          |
| gt.push_relabel_max_flow                     |  71ms          |  466ms         |  1.42s        |
| gt.boykov_kolmogorov_max_flow                |  79ms          |  463ms         |  895ms        |
| scipy.sparse.csgraph.maximum_flow            |  64ms          |  234ms         |  580ms        |
+----------------------------------------------+----------------+----------------+---------------+

同样,结果将取决于图形结构,但这至少表明 SciPy 应该为您提供与图形工具相当的性能。

【讨论】:

在此数据上,igraph 比 Scipy 快 6-10 倍,比 Networkx 快 100-115 倍。 SciPy 1.8.0(将于 2021 年 12 月左右发布)将对maximum_flow(#14358、#14392)进行进一步的性能改进,届时上述基准的更新将到期。 【参考方案4】:

为了获得真正好的性能,您可以尝试将问题重新表述为整数线性规划,任何标准 ILP 工具都应该能够为您提供足够的性能。

***包含一个很好的商业和开源tools 列表,其中许多似乎具有 python 绑定。其中最著名的是CPLEX 和lp_solve。

在过去的几年里,我个人大量使用了 lp_solve,发现只需将输入写入 lp_solve 为plain text files 并使用 shell 调用 lp_solve 就足够了。回想起来,我可能应该投入更多的精力来让官方 python 绑定到 lp_solve 工作。

【讨论】:

整数线性规划 (ILP) 是不必要的,最大流量可以表示为一个简单的线性规划 (en.wikipedia.org/wiki/…)。最大流量可以在多项式时间内求解,也可以用线性程序公式求解同一问题。然而,ILP 是一个 NP-hard 问题。【参考方案5】:

检查PyMaxflow 和igraph。

PyMaxflow 是一个用于图形构建和 maxflow 的 Python 库 计算(通常称为图割)。这个库的核心 是 Vladimir Kolmogorov 的 C++ 实现,可以是 从他的主页下载。

除了 C++ 库的包装器之外, PyMaxflow 提供 NumPy 集成, 快速构建计算机视觉和图形中常见图形布局的方法, 以及使用maxflow方法(αβ-swap和α-expansion)的快速能量最小化算法的实现。

igraph 是一个用于创建、操作和分析图形的 C 库。 它旨在尽可能强大(即快速)以启用 处理大图。 igraph 也可用于:R、Python 和 Mathematica。

在我的测试用例中,igraphPyMaxflow 快​​ 2-5 倍,比 Scipy 快 6-10 倍,比 Networkx 快 100-115 倍。

【讨论】:

以上是关于用于 Python 的快速最大流最小切库的主要内容,如果未能解决你的问题,请参考以下文章

网络流--最小费用最大流 (理解)

最小费用最大流模板

最小费用最大流

网络流基础-最大流最小割定理

[日常摸鱼]bzoj1001狼抓兔子-最大流最小割

最小费用最大流