/**
* @file
* @author Aapo Kyrola
* @version 1.0
*
* @section LICENSE
*
* Copyright [2012] [Aapo Kyrola, Guy Blelloch, Carlos Guestrin / Carnegie Mellon University]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @section DESCRIPTION
*
* Application for computing the connected components of a graph.
* The algorithm is simple: on first iteration each vertex sends its
* id to neighboring vertices. On subsequent iterations, each vertex chooses
* the smallest id of its neighbors and broadcasts its (new) label to
* its neighbors. The algorithm terminates when no vertex changes label.
*
* @section REMARKS
*
* This application is interesting demonstration of the asyncronous capabilities
* of GraphChi, improving the convergence considerably. Consider
* a chain graph 0->1->2->...->n. First, vertex 0 will write its value to its edges,
* which will be observed by vertex 1 immediatelly, changing its label to 0. Nexgt,
* vertex 2 changes its value to 0, and so on. This all happens in one iteration.
* A subtle issue is that as any pair of vertices ab share an edge, they will
* overwrite each others value. However, because they will be never run in parallel
* (due to deterministic parallellism of graphchi), this does not compromise correctness.
*
* @author Aapo Kyrola
*/
#include
#include
#include "graphchi_basic_includes.hpp"
#include "util/labelanalysis.hpp"
using namespace graphchi;
int iterationcount = 0;
bool scheduler = false;
/**
* Type definitions. Remember to create suitable graph shards using the
* Sharder-program.
*/
typedef vid_t VertexDataType; // vid_t is the vertex id type
typedef vid_t EdgeDataType;
/**
* GraphChi programs need to subclass GraphChiProgram
* class. The main logic is usually in the update function.
*/
struct ConnectedComponentsProgram : public GraphChiProgram {
bool converged;
/**
* Vertex update function.
* On first iteration ,each vertex chooses a label = the vertex id.
* On subsequent iterations, each vertex chooses the minimum of the neighbor's
* label (and itself).
*/
void update(graphchi_vertex &vertex, graphchi_context &gcontext) {
if (scheduler) gcontext.scheduler->remove_tasks(vertex.id(), vertex.id());
if (gcontext.iteration == 0) {
vertex.set_data(vertex.id());
if (scheduler) gcontext.scheduler->add_task(vertex.id());
}
/* On subsequent iterations, find the minimum label of my neighbors */
vid_t curmin = vertex.get_data();
for(int i=0; i < vertex.num_edges(); i++) {
vid_t nblabel = vertex.edge(i)->get_data();
if (gcontext.iteration == 0) nblabel = vertex.edge(i)->vertex_id(); // Note!
curmin = std::min(nblabel, curmin);
}
/* Set my label */
vertex.set_data(curmin);
/**
* Broadcast new label to neighbors by writing the value
* to the incident edges.
* Note: on first iteration, write only to out-edges to avoid
* overwriting data (this is kind of a subtle point)
*/
vid_t label = vertex.get_data();
if (gcontext.iteration > 0) {
for(int i=0; i < vertex.num_edges(); i++) {
if (label < vertex.edge(i)->get_data()) {
vertex.edge(i)->set_data(label);
/* Schedule neighbor for update */
if (scheduler) gcontext.scheduler->add_task(vertex.edge(i)->vertex_id(), true);
converged = false;
}
}
} else if (gcontext.iteration == 0) {
for(int i=0; i < vertex.num_outedges(); i++) {
vertex.outedge(i)->set_data(label);
}
}
}
/**
* Called before an iteration starts.
*/
void before_iteration(int iteration, graphchi_context &info) {
iterationcount++;
converged = iteration > 0;
}
/**
* Called after an iteration has finished.
*/
void after_iteration(int iteration, graphchi_context &ginfo) {
if (converged) {
std::cout