/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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.
*/
package org.apache.giraph.examples.scc;
import static org.apache.giraph.examples.scc.SccPhaseMasterCompute.PHASE;
import static org.apache.giraph.examples.scc.SccPhaseMasterCompute.NEW_MAXIMUM;
import static org.apache.giraph.examples.scc.SccPhaseMasterCompute.CONVERGED;
import java.io.IOException;
import org.apache.giraph.Algorithm;
import org.apache.giraph.examples.scc.SccPhaseMasterCompute.Phases;
import org.apache.giraph.graph.BasicComputation;
import org.apache.giraph.graph.Vertex;
import org.apache.hadoop.io.BooleanWritable;
import org.apache.hadoop.io.IntWritable;
import org.apache.hadoop.io.LongWritable;
import org.apache.hadoop.io.NullWritable;
/**
* Finds strongly connected components of the graph.
*/
@Algorithm(name = "Strongly Connected Components",
description = "Finds strongly connected components of the graph")
public class SccComputation extends
BasicComputation<LongWritable, SccVertexValue, NullWritable, LongWritable> {
/**
* Current phase of the computation as defined in SccPhaseMasterCompute
*/
private Phases currPhase;
/**
* Reusable object to encapsulate message value, in order to avoid
* creating a new instance every time a message is sent.
*/
private LongWritable messageValue = new LongWritable();
/**
* Reusable object to encapsulate a parent vertex id.
*/
private LongWritable parentId = new LongWritable();
@Override
public void preSuperstep() {
IntWritable phaseInt = getAggregatedValue(PHASE);
currPhase = SccPhaseMasterCompute.getPhase(phaseInt);
}
@Override
public void compute(
Vertex<LongWritable, SccVertexValue, NullWritable> vertex,
Iterable<LongWritable> messages) throws IOException {
SccVertexValue vertexValue = vertex.getValue();
if (!vertexValue.isActive()) {
vertex.voteToHalt();
return;
}
switch (currPhase) {
case TRANSPOSE :
vertexValue.clearParents();
sendMessageToAllEdges(vertex, vertex.getId());
break;
case TRIMMING :
trim(vertex, messages);
break;
case FORWARD_TRAVERSAL :
forwardTraversal(vertex, messages);
break;
case BACKWARD_TRAVERSAL_START :
backwardTraversalStart(vertex);
break;
case BACKWARD_TRAVERSAL_REST :
backwardTraversalRest(vertex, messages);
break;
default :
break;
}
}
/**
* Creates list of parents based on the received ids and halts the vertices
* that don't have any parent or outgoing edge, hence, they can't be
* part of an SCC.
* @param vertex Current vertex.
* @param messages Received ids from the Transpose phase.
*/
private void trim(Vertex<LongWritable, SccVertexValue, NullWritable> vertex,
Iterable<LongWritable> messages) {
SccVertexValue vertexValue = vertex.getValue();
// Keep the ids of the parent nodes to allow for backwards traversal
for (LongWritable parent : messages) {
vertexValue.addParent(parent.get());
}
// If this node doesn't have any parents or outgoing edges,
// it can't be part of an SCC
vertexValue.set(vertex.getId().get());
if (vertex.getNumEdges() == 0 || vertexValue.getParents() == null) {
vertexValue.deactivate();
} else {
messageValue.set(vertexValue.get());
sendMessageToAllEdges(vertex, messageValue);
}
}
/**
* Traverse the graph through outgoing edges and keep the maximum vertex
* value.
* If a new maximum value is found, propagate it until convergence.
* @param vertex Current vertex.
* @param messages Received values from neighbor vertices.
*/
private void forwardTraversal(
Vertex<LongWritable, SccVertexValue, NullWritable> vertex,
Iterable<LongWritable> messages) {
SccVertexValue vertexValue = vertex.getValue();
boolean changed = setMaxValue(vertexValue, messages);
if (changed) {
messageValue.set(vertexValue.get());
sendMessageToAllEdges(vertex, messageValue);
aggregate(NEW_MAXIMUM, new BooleanWritable(true));
}
}
/**
* Traverse the transposed graph and keep the maximum vertex value.
* @param vertex Current vertex.
*/
private void backwardTraversalStart(
Vertex<LongWritable, SccVertexValue, NullWritable> vertex) {
SccVertexValue vertexValue = vertex.getValue();
if (vertexValue.get() == vertex.getId().get()) {
messageValue.set(vertexValue.get());
sendMessageToAllParents(vertex, messageValue);
}
}
/**
* Traverse the transposed graph and keep the maximum vertex value.
* @param vertex Current vertex.
* @param messages Received values from children vertices.
*/
private void backwardTraversalRest(
Vertex<LongWritable, SccVertexValue, NullWritable> vertex,
Iterable<LongWritable> messages) {
SccVertexValue vertexValue = vertex.getValue();
for (LongWritable m : messages) {
if (vertexValue.get() == m.get()) {
sendMessageToAllParents(vertex, m);
aggregate(CONVERGED, new BooleanWritable(true));
vertexValue.deactivate();
vertex.voteToHalt();
break;
}
}
}
/**
* Compares the messages values with the current vertex value and finds
* the maximum.
* If the maximum value is different from the vertex value, makes it the
* new vertex value and returns true, otherwise, returns false.
* @param vertexValue Current vertex value.
* @param messages Messages containing neighbors' vertex values.
* @return True if a new maximum was found, otherwise, returns false.
*/
private boolean setMaxValue(SccVertexValue vertexValue,
Iterable<LongWritable> messages) {
boolean changed = false;
for (LongWritable m : messages) {
if (vertexValue.get() < m.get()) {
vertexValue.set(m.get());
changed = true;
}
}
return changed;
}
/**
* Send message to all parents.
* @param vertex Current vertex.
* @param message Message to be sent.
*/
private void sendMessageToAllParents(
Vertex<LongWritable, SccVertexValue, NullWritable> vertex,
LongWritable message) {
for (Long id : vertex.getValue().getParents()) {
parentId.set(id);
sendMessage(parentId, message);
}
}
}