同步图计算:Graph Coloring问题详细解答

终于把这个问题解决了,一直被卡在一个点,想了一天都没想出来,也一直觉得自己的算法是正确的,最后,拿出纸笔,推了一遍算法执行过程,才发现了其中隐藏的大bug,事实证明,千万不能懒,千万不能盲目自信。赶紧写篇博客记录一下,任务如下
在这里插入图片描述
首先看一下给的example的README部分和提前的准备工作,也了解如何运行和编辑文件,在example模板的基础上去修改。

------------------------------------------------------------
Requirements
------------------------------------------------------------
1. JDK 1.7.x
2. Hadoop 2.6.x
3. protocol buffers
   $ apt-get install protobuf-c-compiler libprotobuf-c0 libprotobuf-c0-dev

------------------------------------------------------------
Directory Structure
------------------------------------------------------------
bin/         scripts and graphlite executable
engine/      graphlite engine source code     
example/     PageRank example
include/     header that represents programming API

Input/       a number of small example graphs
Output/      empty, will contain the output of a run

Makefile     this can make both engine and example

LICENSE.txt  Apache License, Version 2.0

README.txt   this file

------------------------------------------------------------
Build graphlite
------------------------------------------------------------
1. source bin/setenv

   (1) edit bin/setenv, set the following paths:
       JAVA_HOME, HADOOP_HOME, GRAPHLITE_HOME

   (2) $ . bin/setenv

2. build graphlite

   $ cd engine
   $ make

   check if bin/graphlite is successfully generated.

------------------------------------------------------------
Compile and Run Vertex Program
------------------------------------------------------------

1. build example

   $ cd example
   $ make

   check if example/PageRankVertex.so is successfully generated.
   
2. run example
   //run in the upper file of example

   $ start-graphlite example/PageRankVertex.so Input/facebookcombined_4w Output/out

   PageRankVertex.cc declares 5 processes, including 1 master and 4 workers.
   So the input graph file is prepared as four files: Input/facebookcombined_4w_[1-4]

   The output of PageRank will be in: Output/out_[1-4]

   Workers generate log files in WorkOut/worker*.out

------------------------------------------------------------
Write Vertex Program
------------------------------------------------------------
Please refer to PageRankVertex.cc

1. change VERTEX_CLASS_NAME(name) definition to use a different class name

2. VERTEX_CLASS_NAME(InputFormatter) can be kept as is
//注意要修改其中的sizeof类型
3. VERTEX_CLASS_NAME(OutputFormatter): this is where the output is generated
//要修改其中writeResult()中value的类型,注意是浮点型还是整型
4. VERTEX_CLASS_NAME(Aggregator): you can implement other types of aggregation

5. VERTEX_CLASS_NAME(): the main vertex program with compute()
//这部分是主要编写的部分,由题目可以知道vertex<int,int,int>
6. VERTEX_CLASS_NAME(Graph): set the running configuration here
//因为指令有变化,所以在主函数中添加m_V0_id=atoi(argv[3]);m_color_number=atoi(argv[4]);
7. Modify Makefile:
   EXAMPLE_ALGOS=PageRankVertex

   if your program is your_program.cc, then 
   EXAMPLE_ALGOS=your_program

   make will produce your_program.so

------------------------------------------------------------
Use Hash Partitioner
------------------------------------------------------------

 bin/hash-partitioner.pl can be used to divide a graph input
 file into multiple partitions.

  $ hash-partitioner.pl Input/facebookcombined 4

  will generate: Input/facebookcombined_4w_[1-4]


下面是和该任务相关的README


# Homework 2 Part 2 Requirements

Note: Please use only English in comments.
First line: /* group, studentId, nameInEnglish */



 Group 2

   #define VERTEX_CLASS_NAME(name) GraphColor##name

   command line:
   $ start-graphlite example/your_program.so <input path> <output path> <v0 id> <num color>

   input file: fields are separated by a single space

     num_vertex_in_this_partition
     num_edge_in_this_partition
     src_vertex_id dest_vertex_id
     src_vertex_id dest_vertex_id
     ... ...

   output file: fields are separated by a single space

     vertexid: colorid
     vertexid: colorid
     ...
  
------------------------------------------------------------
Homework 2 Part 2 Test Commands
------------------------------------------------------------

Graph Coloring:
$ start-graphlite example/your_program.so ${GRAPHLITE_HOME}/part2-input/Color-graph0_4w ${GRAPHLITE_HOME}/out 0 5
$ start-graphlite example/your_program.so ${GRAPHLITE_HOME}/part2-input/Color-graph1_4w ${GRAPHLITE_HOME}/out 0 5

------------------------------------------------------------
Undirected graph vs. directed graph
------------------------------------------------------------

The following is an example directed graph

  0 --> 1 --> 2 --> 3
        |          /|\
        |           |
        + --------- +

The input will look like the following:

  4
  4
  0 1
  1 2
  1 3
  2 3
  
Consider the following undirected graph, where each edge in
the above graph is now an undirected edge.

  0 --- 1 --- 2 --- 3
        |           | 
        |           |
        + --------- +

The input will look like the following:

  4
  8
  0 1
  1 0
  1 2
  1 3
  2 1
  2 3
  3 2
  3 1

Note that every undirected edge is shown as TWO directed edge.
So the vertex compute() method can receive messages from all
the neighbors in the original undirected graph, and can send
messages to all the neighbors in the original undirected graph.


在对example的模板理解的基础上,按照上面需要在每个类中进行修改,并根据任务要求编写自己的compute()函数。代码如下,主要分析compute()函数。

/*2,201928013229142,songshuhan*/
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <algorithm>
#include <vector>
#include "GraphLite.h"

#define VERTEX_CLASS_NAME(name) GraphColor##name

int m_color_number; //全局声明
int m_V0_id;  //全局声明命令里的后两个参数

class VERTEX_CLASS_NAME(InputFormatter): public InputFormatter {
public:
    int64_t getVertexNum() {
        unsigned long long n;
        sscanf(m_ptotal_vertex_line, "%lld", &n);
        m_total_vertex= n;
        return m_total_vertex;
    }
    int64_t getEdgeNum() {
        unsigned long long n;
        sscanf(m_ptotal_edge_line, "%lld", &n);
        m_total_edge= n;
        return m_total_edge;
    }
    int getVertexValueSize() {
        m_n_value_size = sizeof(int);//根据需求修改sizeof类型
        return m_n_value_size;
    }
    int getEdgeValueSize() {
        m_e_value_size = sizeof(int);//根据需求修改sizeof类型
        return m_e_value_size;
    }
    int getMessageValueSize() {
        m_m_value_size = sizeof(int);//根据需求修改sizeof类型
        return m_m_value_size;
    }
    void loadGraph() {
        unsigned long long last_vertex;
        unsigned long long from;
        unsigned long long to;
        double weight = 0;
        
        double value = 1;
        int outdegree = 0;
        
        const char *line= getEdgeLine();

        // Note: modify this if an edge weight is to be read
        //       modify the 'weight' variable

        sscanf(line, "%lld %lld", &from, &to);
        addEdge(from, to, &weight);

        last_vertex = from;
        ++outdegree;
        for (int64_t i = 1; i < m_total_edge; ++i) {
            line= getEdgeLine();

            // Note: modify this if an edge weight is to be read
            //       modify the 'weight' variable

            sscanf(line, "%lld %lld", &from, &to);
            if (last_vertex != from) {
                addVertex(last_vertex, &value, outdegree);
                last_vertex = from;
                outdegree = 1;
            } else {
                ++outdegree;
            }
            addEdge(from, to, &weight);
        }
        addVertex(last_vertex, &value, outdegree);
    }
};

class VERTEX_CLASS_NAME(OutputFormatter): public OutputFormatter {
public:
    void writeResult() {
        int64_t vid;
        int value;
        char s[1024];

        for (ResultIterator r_iter; ! r_iter.done(); r_iter.next() ) {
            r_iter.getIdValue(vid, &value);
            int n = sprintf(s, "%lld: %d\n", (unsigned long long)vid, value);//如果是整型注意修改前面的%f变为%d
            writeNextResLine(s, n);
        }
    }
};

// An aggregator that records a double value tom compute sum
class VERTEX_CLASS_NAME(Aggregator): public Aggregator<int> {//这里也要根据需求调整类型
public:
    void init() {
        m_global = 0;
        m_local = 0;
    }
    void* getGlobal() {
        return &m_global;
    }
    void setGlobal(const void* p) {
        m_global = * (int *)p;//这里也要根据需求调整类型
    }
    void* getLocal() {
        return &m_local;
    }
    void merge(const void* p) {
        m_global += * (int *)p;//这里也要根据需求调整类型
    }
    void accumulate(const void* p) {
        m_local += * (int *)p;//这里也要根据需求调整类型
    }
};

class VERTEX_CLASS_NAME(): public Vertex <int, int, int> {
public:
    void compute(MessageIterator* pmsgs) {
       vector<int> neighbor_array;
       int color_val;
       int tmp_max_num=-1;
       if(getSuperstep() == 0){
           if(getVertexId() == m_V0_id){
               color_val=0;
           } else{
               color_val=-1;
           }             
       } else{
            if (getSuperstep() >= 2) {
                int global_val = * (int *)getAggrGlobal(0);
                if (global_val == 0) {
                    voteToHalt(); return;
                }
            }
            for ( ; ! pmsgs->done(); pmsgs->next() ) {
                if(pmsgs->getValue() > tmp_max_num){
                    tmp_max_num=pmsgs->getValue();
                }
                neighbor_array.push_back(pmsgs->getValue());
            }

            vector <int>::iterator it = find(neighbor_array.begin(), neighbor_array.end(), getValue()); 
            if (it != neighbor_array.end() || getValue() == -1){
                color_val=tmp_max_num+(rand()%(m_color_number-1))+1;
            }else{
                color_val=getValue();
            }
            int acc = fabs(getValue() - color_val);
            accumulateAggr(0, &acc);
       }
        * mutableValue() = color_val;
        sendMessageToAllNeighbors(color_val);
    }
      
};
//start-graphlite GraphColor.so part2-input/Color-graph1_4w Output/out 0 5
//killall -9 graphlite
class VERTEX_CLASS_NAME(Graph): public Graph {
public:
    VERTEX_CLASS_NAME(Aggregator)* aggregator;

public:
    // argv[0]: GraphColor.so
    // argv[1]: <input path>
    // argv[2]: <output path>
    // argv[3]: V0_id
    // argv[4]: num_color
    void init(int argc, char* argv[]) {
        
        setNumHosts(5);
        setHost(0, "localhost", 1411);
        setHost(1, "localhost", 1421);
        setHost(2, "localhost", 1431);
        setHost(3, "localhost", 1441);
        setHost(4, "localhost", 1451);

        if (argc < 3) {
           printf ("Usage: %s <input path> <output path>\n", argv[0]);
           exit(1);
        }

        m_pin_path = argv[1];
        m_pout_path = argv[2];
        m_V0_id=atoi(argv[3]);//添加需要的量,atoi是把字符变为数字的方法
        m_color_number=atoi(argv[4]);
        
        
        aggregator = new VERTEX_CLASS_NAME(Aggregator)[1];
        regNumAggr(1);
        regAggr(0, &aggregator[0]);
    }

    void term() {
        delete[] aggregator;
    }
};

/* STOP: do not change the code below. */
extern "C" Graph* create_graph() {
    Graph* pgraph = new VERTEX_CLASS_NAME(Graph);

    pgraph->m_pin_formatter = new VERTEX_CLASS_NAME(InputFormatter);
    pgraph->m_pout_formatter = new VERTEX_CLASS_NAME(OutputFormatter);
    pgraph->m_pver_base = new VERTEX_CLASS_NAME();

    return pgraph;
}

extern "C" void destroy_graph(Graph* pobject) {
    delete ( VERTEX_CLASS_NAME()* )(pobject->m_pver_base);
    delete ( VERTEX_CLASS_NAME(OutputFormatter)* )(pobject->m_pout_formatter);
    delete ( VERTEX_CLASS_NAME(InputFormatter)* )(pobject->m_pin_formatter);
    delete ( VERTEX_CLASS_NAME(Graph)* )pobject;
}

下面主要分析一下compute部分,也就是如何实现图着色的一种方法,因为在每一个超步内,所有的顶点是并行执行的,同时可以获得来自邻居发送过来的信息,我的思路是,创建一个vector,保存每一个信息传过来的邻居顶点的颜色,并同时记录其中最大的值,这样,如果该顶点的值和邻居顶点的值相同,赋值给该顶点最大值加一个随机数,如果不相同,就保留该顶点原来的值,并记录为0,当所有的顶点都为0的时候,也就是所有顶点都和上次值没有变化了,也就是满足了图着色。我觉得最巧妙的地方就在随机值上,这里也是让我卡了一天的点,加了一个随机值可以保证收敛,而加固定值则有可能会无限制迭代

class VERTEX_CLASS_NAME(): public Vertex <int, int, int> {
public:
    void compute(MessageIterator* pmsgs) {
       vector<int> neighbor_array;
       int color_val;
       int tmp_max_num=-1;
       if(getSuperstep() == 0){
           if(getVertexId() == m_V0_id){ //输入的点定值为0,其他点初始为-1
               color_val=0;
           } else{
               color_val=-1;
           }             
       } else{
            if (getSuperstep() >= 2) {
                int global_val = * (int *)getAggrGlobal(0);
                if (global_val == 0) {
                    voteToHalt(); return;  //结束条件
                }
            }
            for ( ; ! pmsgs->done(); pmsgs->next() ) {  
                if(pmsgs->getValue() > tmp_max_num){
                    tmp_max_num=pmsgs->getValue();//找到邻居顶点的最大值
                }
                neighbor_array.push_back(pmsgs->getValue());//保存所有邻居顶点的信息
            }

            vector <int>::iterator it = find(neighbor_array.begin(), neighbor_array.end(), getValue()); 
            if (it != neighbor_array.end() || getValue() == -1){
                color_val=tmp_max_num+(rand()%(m_color_number-1))+1;//不能加定值,比如加定值1,有一个四个顶点的全连接图,起初为0,-1,-1,-1,经过了一个超步之后,变为0,1,1,1,再经过一个超步变为0,2,2,2,无线迭代没有尽头,也不收敛,而如果每个点加不同的随机数,就可以很好的解决这个问题,达到快速收敛的目的。
            }else{
                color_val=getValue();
            }
            int acc = fabs(getValue() - color_val);
            accumulateAggr(0, &acc);//把一个work中所有的acc记录下来
       }
        * mutableValue() = color_val;
        sendMessageToAllNeighbors(color_val);//把值发送给邻居顶点
    }
      
};

之后,使用检测程序,通过docker cp拷贝到容器中,tar -cxf解压缩包,之后按照操作可以得到满分。
在这里插入图片描述
检测程序使用如下

0. set language to POSIX
   $ export LC_ALL="POSIX"

1. set env for GraphLite
   $ source <Home-of-GraphLite-0.20>/bin/setenv
  
2. set up for GraphColor check program
  
   enter the directory of hw2-check at first, then run

   $ ./setup-test-part2.sh
   

3. make sure ssh is running
   $ service ssh status

   if not, then run sshd (note that this is necessary in a docker container)
   $ service ssh start

4. run test

   $ ./run-test-part2.pl ./score  <your-cc-file>

Your score will be in ./score.  The run-test-part2.pl tests 2 input cases, you will
get one score for each case.  So the output full score for part2 is 2.

./run-test-part2.pl ./score /home/bdms/homework/hw2/part2/2_201928013229142_hw2.cc

运行出错及解决

这是最常见的错误
Sender: connect: Connection refused错误
Sender: connect: Connection refused
Sender: connect: Connection refused
出错的可能原因:

  1. 可能是inputformatter中顶点类型的sizeof没改,或者说是某个类型定义为double,但是传递的是int
  2. 执行路径不对:example/PageRankVertex.so,这个路径就说明要在example的上级目录下执行这条命令
  3. 运行之前还有其它graphlite程序在运行,可能是之前运行graphlite时ctrl+c结束了,但是实际上进行还在后台执行,导致冲突,可以用top命令打开所有进程查看发现有很多graphlite还在运行,这里提供了psmisc包,可以在根目录下使用apt-get install psmisc安装,之后用killall -9 graphlite可以杀死所有的进程

在运行的时候如果有死循环,可以crtl+z退出,然后killall -9 graphlite

学到新知识很开心。晚安

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值