package edu.cmu.graphchi.apps;
import edu.cmu.graphchi.*;
import edu.cmu.graphchi.datablocks.IntConverter;
import edu.cmu.graphchi.engine.GraphChiEngine;
import edu.cmu.graphchi.engine.VertexInterval;
import edu.cmu.graphchi.preprocessing.EdgeProcessor;
import edu.cmu.graphchi.preprocessing.FastSharder;
import edu.cmu.graphchi.preprocessing.VertexProcessor;
import edu.cmu.graphchi.vertexdata.VertexAggregator;
import edu.cmu.graphchi.vertexdata.VertexIdValue;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Logger;
/**
* Tests that the system works. Processes any graph and runs a
* deterministic algorithm on the graph and checks that the
* result is intended.
* @author akyrola
*/
public class SmokeTest implements GraphChiProgram<Integer, Integer> {
private static Logger logger = ChiLogger.getLogger("smoketest");
// Keep track of loaded vertex values to check vertex value loading works
private static HashMap<Integer, Integer> loadedVertexValues = new HashMap<Integer, Integer>();
public void update(ChiVertex<Integer, Integer> vertex, GraphChiContext context) {
if (context.getIteration() == 0) {
/* Check vertex value was loaded properly */
int originalId = context.getVertexIdTranslate().backward(vertex.getId());
if (loadedVertexValues.containsKey(originalId)) {
if (!vertex.getValue().equals(loadedVertexValues.get(originalId))) {
throw new RuntimeException("Vertex value for vertex " + originalId + " not loaded properly:" +
vertex.getValue() + " !=" + loadedVertexValues.get(originalId));
} else {
logger.info("Loaded vertex value correctly: " + originalId + " = " + loadedVertexValues.get(originalId));
}
}
vertex.setValue(vertex.getId() + context.getIteration());
} else {
int curval = vertex.getValue();
int vexpected = vertex.getId() + context.getIteration() - 1;
if (curval != vexpected) {
throw new RuntimeException("Mismatch (vertex). Expected: " + vexpected + " but had " +
curval);
}
for(int i=0; i<vertex.numInEdges(); i++) {
int has = vertex.inEdge(i).getValue();
int correction = vertex.getId() > vertex.inEdge(i).getVertexId() ? +1 : 0;
int expected = vertex.inEdge(i).getVertexId() + context.getIteration() - 1 + correction;
if (expected != has)
throw new RuntimeException("Mismatch (edge): " + expected + " expected but had "+ has +
". Iteration:" + context.getIteration() + ", edge:" + vertex.inEdge(i).getVertexId()
+ " -> " + vertex.getId());
}
vertex.setValue(vertex.getId() + context.getIteration());
}
int val = vertex.getValue();
for(int i=0; i<vertex.numOutEdges(); i++) {
vertex.outEdge(i).setValue(val);
}
}
public void beginIteration(GraphChiContext ctx) {}
public void endIteration(GraphChiContext ctx) {}
public void beginInterval(GraphChiContext ctx, VertexInterval interval) {}
public void endInterval(GraphChiContext ctx, VertexInterval interval) {}
@Override
public void beginSubInterval(GraphChiContext ctx, VertexInterval interval) {
}
@Override
public void endSubInterval(GraphChiContext ctx, VertexInterval interval) {
}
/**
* Initialize the sharder-program.
* @param graphName
* @param numShards
* @return
* @throws java.io.IOException
*/
protected static FastSharder createSharder(String graphName, int numShards) throws IOException {
return new FastSharder<Integer, Integer>(graphName, numShards, new VertexProcessor<Integer>() {
public Integer receiveVertexValue(int vertexId, String token) {
if (token == null) {
return 0;
} else {
synchronized (this) {
loadedVertexValues.put(vertexId, Integer.parseInt(token));
}
return Integer.parseInt(token);
}
}
}, new EdgeProcessor<Integer>() {
public Integer receiveEdge(int from, int to, String token) {
return (token == null ? 0 : Integer.parseInt(token));
}
}, new IntConverter(), new IntConverter());
}
public static void main(String[] args) throws Exception {
String baseFilename = args[0];
int nShards = Integer.parseInt(args[1]);
String fileType = (args.length >= 3 ? args[2] : null);
/* Create shards */
FastSharder sharder = createSharder(baseFilename, nShards);
sharder.setAllowSparseDegreesAndVertexData(false);
if (baseFilename.equals("pipein")) { // Allow piping graph in
sharder.shard(System.in);
} else {
sharder.shard(new FileInputStream(new File(baseFilename)), fileType);
}
/* Run engine */
GraphChiEngine<Integer, Integer> engine = new GraphChiEngine<Integer, Integer>(baseFilename, nShards);
engine.setEdataConverter(new IntConverter());
engine.setVertexDataConverter(new IntConverter());
engine.setModifiesInedges(false); // Important optimization
engine.run(new SmokeTest(), 5);
/* Test vertex iterator */
Iterator<VertexIdValue<Integer>> iter = VertexAggregator.vertexIterator(engine.numVertices(),
baseFilename, new IntConverter(), engine.getVertexIdTranslate());
int i=0;
while(iter.hasNext()) {
VertexIdValue<Integer> x = iter.next();
int internalId = engine.getVertexIdTranslate().forward(x.getVertexId());
int expectedValue = internalId + 4;
if (expectedValue != x.getValue()) {
throw new IllegalStateException("Expected internal value to be " + expectedValue
+ ", but it was " + x.getValue() + "; internal id=" + internalId + "; orig=" + x.getVertexId());
}
if (i % 10000 == 0) {
logger.info("Scanning vertices: " + i);
}
i++;
}
if (i != engine.numVertices())
throw new IllegalStateException("Error in iterator: did not have numVertices vertices: " + i + "/" + engine.numVertices());
logger.info("Ready.");
}
}