package edu.cmu.graphchi.walks;
import edu.cmu.graphchi.*;
import edu.cmu.graphchi.datablocks.BytesToValueConverter;
import edu.cmu.graphchi.engine.GraphChiEngine;
import edu.cmu.graphchi.engine.VertexInterval;
import edu.cmu.graphchi.preprocessing.VertexIdTranslate;
import edu.cmu.graphchi.walks.distributions.RemoteDrunkardCompanion;
import java.io.IOException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.logging.Logger;
/**
* Class for running DrunkardMob random walk applications.
* This can run multiple distinct random walk computations. They are executed
* simultaneously when iterating over the graph.
* @author Aapo Kyrola
*/
public class DrunkardMobEngine<VertexDataType, EdgeDataType> {
private GraphChiEngine<VertexDataType, EdgeDataType> engine;
private List<DrunkardDriver> drivers;
private static Logger logger = ChiLogger.getLogger("drunkardmob-engine");
private DrunkardFactory<VertexDataType, EdgeDataType> factory;
/**
* Create the engine
* @param factory we allow walks to be represented either as ints or as longs (if more
* information needs to be stored, e.g. to retrieve path information from the walks). In order
* to avoid autoboxing, we do a little bit of fancy footwork here. The caller must pass in an
* IntDrunkardFactory or a LongDrunkardFactory, and then when processing WalkArrays and
* DrunkardContexts, they must be cast to IntWalkArrays or LongWalkArrays (and Contexts, and
* whatever else) in order to get the actual values out. This way we can keep the primitive
* typing while still sharing as much code as possible between the int and the long processing.
*/
public DrunkardMobEngine(String baseFilename, int nShards,
DrunkardFactory<VertexDataType, EdgeDataType> factory) throws IOException {
createGraphChiEngine(baseFilename, nShards);
this.drivers = new ArrayList<DrunkardDriver>();
this.factory = factory;
// Disable all edge directions by default
engine.setDisableInedges(true);
engine.setDisableOutEdges(true);
engine.setModifiesInedges(false);
engine.setModifiesOutedges(false);
}
private void createGraphChiEngine(String baseFilename, int nShards) throws IOException {
this.engine = new GraphChiEngine<VertexDataType, EdgeDataType>(baseFilename, nShards);
this.engine.setOnlyAdjacency(true);
this.engine.setVertexDataConverter(null);
this.engine.setEdataConverter(null);
}
public GraphChiEngine<VertexDataType, EdgeDataType> getEngine() {
return engine;
}
public DrunkardJob getJob(String name) {
for(DrunkardDriver driver : drivers) {
if (driver.getJob().getName().equals(name)) {
return driver.getJob();
}
}
return null;
}
/**
* Configure edge data type converter - if you need edge values
* @param edataConverter
*/
public void setEdataConverter(BytesToValueConverter<EdgeDataType> edataConverter) {
engine.setEdataConverter(edataConverter);
if (edataConverter != null) {
engine.setOnlyAdjacency(false);
} else {
engine.setOnlyAdjacency(true);
}
}
/**
* Configure vertex data type converter - if you need vertex values
* @param vertexDataConverter
*/
public void setVertexDataConverter(BytesToValueConverter<VertexDataType> vertexDataConverter) {
engine.setVertexDataConverter(vertexDataConverter);
}
/**
* Adds a random walk job. Use run() to run all the jobs.
* @param edgeDirection which direction edges need to be considered
* @param callback your walk logic
* @param companion object that keeps track of the walks
* @return the job object
*/
public DrunkardJob addJob(String jobName, EdgeDirection edgeDirection,
WalkUpdateFunction<VertexDataType, EdgeDataType> callback,
RemoteDrunkardCompanion companion) {
/* Configure engine parameters */
switch(edgeDirection) {
case IN_AND_OUT_EDGES:
engine.setDisableInedges(false);
engine.setDisableOutEdges(false);
break;
case IN_EDGES:
engine.setDisableInedges(false);
break;
case OUT_EDGES:
engine.setDisableOutEdges(false);
break;
}
/**
* Create job object and the driver-object.
*/
DrunkardJob job = new DrunkardJob(jobName, companion, engine.numVertices(), factory);
drivers.add(factory.createDrunkardDriver(job, callback));
return job;
}
public void run(int numIterations) throws IOException, RemoteException {
engine.setEnableScheduler(true);
int memoryBudget = 1200;
if (System.getProperty("membudget") != null) memoryBudget = Integer.parseInt(System.getProperty("membudget"));
engine.setMemoryBudgetMb(memoryBudget);
engine.setEnableDeterministicExecution(false);
engine.setAutoLoadNext(false);
engine.setVertexDataConverter(null);
engine.setMaxWindow(10000000); // Handle maximum 10M vertices a time.
long t = System.currentTimeMillis();
for(DrunkardDriver driver : drivers) {
if (driver.getJob().getWalkManager() == null) {
throw new IllegalStateException("You need to configure walks by calling DrunkardJob.configureXXX()");
}
driver.initWalks();
}
long initTime = System.currentTimeMillis() - t;
/* Run GraphChi */
logger.info("Starting running drunkard jobs (" + drivers.size() + " jobs)");
engine.run(new GraphChiDrunkardWrapper(), numIterations);
/* Finish up */
for(DrunkardDriver driver: drivers) {
driver.spinUntilFinish();
}
}
public VertexIdTranslate getVertexIdTranslate() {
return engine.getVertexIdTranslate();
}
/**
* Multiplex for DrunkardDrivers.
*/
protected class GraphChiDrunkardWrapper implements GraphChiProgram<VertexDataType, EdgeDataType> {
long iterationStart;
public GraphChiDrunkardWrapper() {
}
@Override
public void update(ChiVertex<VertexDataType, EdgeDataType> vertex, GraphChiContext context) {
try {
/* Buffer management. TODO: think, this is too complex after adding the multiplex */
if (context.getThreadLocal() == null) {
ArrayList<LocalWalkBuffer> multiplexedLocalBuffers = new ArrayList<LocalWalkBuffer>(drivers.size());
for(DrunkardDriver driver: drivers) {
LocalWalkBuffer buf = factory.createLocalWalkBuffer();
driver.addLocalBuffer(buf);
multiplexedLocalBuffers.add(buf);
}
context.setThreadLocal(multiplexedLocalBuffers);
}
final ArrayList<LocalWalkBuffer> multiplexedLocalBuffers = (ArrayList<LocalWalkBuffer>) context.getThreadLocal();
int i = 0;
for(DrunkardDriver driver : drivers) {
driver.update(vertex, context, multiplexedLocalBuffers.get(i++));
}
} catch (Exception err) {
err.printStackTrace();
}
}
@Override
public void beginIteration(GraphChiContext ctx) {
iterationStart = System.currentTimeMillis();
for(DrunkardDriver driver : drivers) {
driver.beginIteration(ctx);
}
}
@Override
public void endIteration(GraphChiContext ctx) {
for(DrunkardDriver driver : drivers) {
driver.endIteration(ctx);
}
long iterationTime = System.currentTimeMillis() - iterationStart;
}
@Override
public void beginInterval(GraphChiContext ctx, VertexInterval interval) {
for(DrunkardDriver driver : drivers) {
driver.beginInterval(ctx, interval);
}
}
@Override
public void endInterval(GraphChiContext ctx, VertexInterval interval) {
for(DrunkardDriver driver : drivers) {
driver.endInterval(ctx, interval);
}
}
@Override
public void beginSubInterval(GraphChiContext ctx, VertexInterval interval) {
for(DrunkardDriver driver : drivers) {
driver.beginSubInterval(ctx, interval);
}
}
@Override
public void endSubInterval(GraphChiContext ctx, VertexInterval interval) {
for(DrunkardDriver driver : drivers) {
driver.endSubInterval(ctx, interval);
}
}
}
}