图的划分是图数据处理中的重要环节,被广泛应用于诸多领域的优化问题中,例如超大规模集成电路的设计、时空数据库的存储与访问、传输管理和数据挖掘等。高效的图划分算法在大规模图数据的分布式计算中至关重要。在并行计算机上进行大规模图数据处理时,需要将图数据进行划分从而分配至不同的计算节点上,从而尽可能满足各个节点上的负载均衡且计算节点间的通讯代价尽可能小,这就对图划分结果的质量提出了较高的要求。
METIS是由Karypis Lab开发的一个具有强大功能的图切分软件包,用于划分大型不规则图、划分大网格以及计算稀疏矩阵的降填充序(本文只介绍图划分部分)。METIS基于George Karypis等人提出的多级图划分范式[1]实现,它提供了一组独立的命令行程序和应用程序接口(API),可通过C/C++或ForTran程序调用。本文介绍METIS软件包的下载、安装和用于图划分的使用过程。更多内容参考Karypis Lab官网:http://glaros.dtc.umn.edu/gkhome.
(1)下载软件包
下载地址:http://glaros.dtc.umn.edu/gkhome/metis/metis/download
软件包:metis-5.1.0.tar.gz
本页面最下面的Documentation中可以下载详细的使用文档manual.pdf,其中详细介绍了METIS软件包的使用,包括参数含义及输入输出格式等。
(2)解压软件包
右击软件包,进行解压。
其中,BUILD-Windows.txt是在Windows下的安装指南,具体内容本文均有涉及。此时,build文件夹为空,后续会在此生成很多文件。
接下来,安装CMake GUI(CMake的图形化界面)来对METIS进行编译。
(1)下载安装包
下载地址:https://cmake.org/download/
安装包:cmake-3.18.0-win64-x64.zip (之前看教程让下载.msi版本的,但笔者用该版本总是报错)
下载后,解压到某个文件夹,地址为全英文命名,便于后续环境变量配置。
(2)配置环境变量
右击“我的电脑”或“此电脑”或“这台电脑”,选择“属性/高级系统设置/环境变量”,点击“系统变量”里的“Path”,选择“编辑”,“新建”变量,输入解压后的cmake文件路径,至“bin”文件夹为止,点击“确定”。
(3)双击图标运行
找到“bin”文件夹下的图标,双击打开cmake的GUI界面。
(1)选择METIS为源代码,输出路径为METIS文件下的build文件夹。
(2)点击Configure按钮,选择本机安装的VS版本的生成器(!!注意一定要对应版本,否则会编译出错!!),点击Finish按钮,等待一会儿,出现如图所示的Configuring Done。
(3)点击Generate按钮,出现Generating done,说明编译完成,生成的METIS.sln文件在METIS的Build文件夹中。
在解压的文件夹metis-5.1.0中打开文件夹GKlib,找到文件gk_arch.h,将图片中的一行代码注释掉,保存并关闭。
(1)打开VS,选择“文件\打开\项目/解决方案”,打开METIS的Build文件夹下的METIS.sln文件,出现下图。
(2)修改VS中下图位置的Debug为Release。
(3)在VS界面左侧的解决方案管理器中,右击ALL_BUILD,选择“生成”,等待生成完成。(若不修改gk_arch.h则会报错)
至此,METIS工具包安装完成,下面通过示例介绍VS中用C++调用METIS提供的API进行图划分的过程。
(1)在VS中创建一个空项目,新建一个cpp文件,将以下代码复制到新建的cpp文件中。
#include "metis.h"
#include
#include
#include
#include
#include
using namespace std;
vector<idx_t> func(vector<idx_t> xadj, vector<idx_t> adjncy, vector<idx_t> vwgt) {
idx_t nVertices = xadj.size() - 1; // 节点数
idx_t nEdges = adjncy.size() / 2; // 边数
idx_t nWeights = 1;
idx_t nParts = 2; // 子图个数
idx_t objval;
std::vector<idx_t> part(nVertices, 0);
int ret = METIS_PartGraphKway(&nVertices, &nWeights, xadj.data(), adjncy.data(),
vwgt.data(), NULL, NULL, &nParts, NULL,
NULL, NULL, &objval, part.data());
std::cout << ret << std::endl;
for (unsigned part_i = 0; part_i < part.size(); part_i++) {
std::cout << part_i << " " << part[part_i] << std::endl;
}
return part;
}
int main() {
ifstream ingraph("文件路径/graph.txt");
if (!ingraph) {
cout << "打开文件失败!" << endl;
exit(1);//失败退回操作系统
}
int vexnum, edgenum;
string line;
getline(ingraph, line);
istringstream tmp(line);
tmp >> vexnum >> edgenum;
vector<idx_t> xadj(0);
vector<idx_t> adjncy(0); //点的id从0开始
vector<idx_t> vwgt(0);
idx_t a, w;
for (int i = 0; i < vexnum; i++) {
xadj.push_back(adjncy.size());
getline(ingraph, line);
istringstream tmp(line);
while (tmp >> a >> w) {
adjncy.push_back(a);
vwgt.push_back(w);
}
}
xadj.push_back(adjncy.size());
ingraph.close();
vector<idx_t> part = func(xadj, adjncy, vwgt);
ofstream outpartition("文件路径/partition.txt");
if (!outpartition) {
cout << "打开文件失败!" << endl;
exit(1);
}
for (int i = 0; i < part.size(); i++) {
outpartition << i << " " << part[i] << endl;
}
outpartition.close();
}
(2)添加METiS第三方依赖。
第一步:添加头文件(metis.h)的目录:项目\属性\配置属性\c/c++\常规\附加包含目录\编辑\选择路径为“…\metis-5.1.0\include”。
第二步:添加文件引用的lib静态库路径:项目\属性\配置属性\连接器\常规\附加库目录\编辑\选择路径为"…\metis-5.1.0\build\libmetis\Release"。
第三步:添加工程引用的lib文件名:\属性\配置属性\连接器\输入\附加依赖项\编辑\输入“metis.lib”。
(3)测试示例:修改cpp文件main函数中的“文件路径”为输入文件所在的路径。
输入文件内容:
7 11 001
4 1 2 2 1 1
0 1 2 2 3 1
4 3 3 2 1 2 0 2
1 1 2 2 5 2 6 5
0 1 2 3 5 2
4 2 3 2 6 6
5 6 3 5
输入文件格式: 第1行的7和11表示图有7个顶点和11条边;第i+1行表示点i所邻接的点,后面紧跟边的权重,例如第2行的前两个数“4 1”,表示顶点1(图示中)与顶点5邻接,邻接边权重为1。(提示:若原始图的顶点id与行数不一致,需做一步转换,可创建一个文件记录行数与原顶点id的对应关系。)
输出文件内容:
0 1
1 1
2 1
3 0
4 1
5 0
6 0
输出文件格式: 第一列的数字为graph.txt中的行号,代表顶点id;原图被划分为两部分,通过第二列的0和1区分,示例中0 1 2 4 为一部分,3 5 6 为另一部分。
[1] George Karypis and Vipin Kumar. A Fast and High Quality Multilevel Scheme for Partitioning Irregular Graphs. SIAM J. Scientific Computing 20(1): 359-392(1998).