package i5.las2peer.services.ocd.benchmarks;
import i5.las2peer.services.ocd.adapters.coverInput.CoverInputAdapter;
import i5.las2peer.services.ocd.adapters.coverInput.NodeCommunityListsCoverInputAdapter;
import i5.las2peer.services.ocd.adapters.graphInput.GraphInputAdapter;
import i5.las2peer.services.ocd.adapters.graphInput.WeightedEdgeListGraphInputAdapter;
import i5.las2peer.services.ocd.graphs.Cover;
import i5.las2peer.services.ocd.graphs.CoverCreationLog;
import i5.las2peer.services.ocd.graphs.CoverCreationType;
import i5.las2peer.services.ocd.graphs.CustomGraph;
import i5.las2peer.services.ocd.graphs.GraphType;
import java.io.File;
import java.io.FileReader;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import org.apache.commons.exec.CommandLine;
import org.apache.commons.exec.DefaultExecuteResultHandler;
import org.apache.commons.exec.DefaultExecutor;
import org.apache.commons.lang3.SystemUtils;
/**
* The LFR benchmark model for weighted and directed graphs.
* Makes use of an LFR benchmark application written by Lancichinetti.
* @author Sebastian
*
*/
public class LfrBenchmark implements GroundTruthBenchmark {
/*
* Path of the directory reserved for the lfr benchmark application.
*/
private static final String lfrDirectoryPath = "ocd/lfr/";
/*
* Used for synchronization purposes. Executes the benchmark graph calculation.
*/
private static DefaultExecutor executor = new DefaultExecutor();
/* Path of the file holding an application developed by Lancichinetti that calculates
* LFR benchmark graphs. For Windows.
*/
private static String windowsBenchmarkGeneratorPath = lfrDirectoryPath + "LfrBenchmarkWindows.exe";
/* Path of the file holding an application developed by Lancichinetti that calculates
* LFR benchmark graphs. For Linux.
*/
private static String linuxBenchmarkGeneratorPath = "./LfrBenchmarkLinux";
/*
* Path of the file containing the calculated benchmark graph.
*/
private static final String graphPath = lfrDirectoryPath + "network.dat";
/*
* Path of the file containing the ground truth cover of the benchmark graph.
*/
private static final String coverPath = lfrDirectoryPath + "community.dat";
/**
* The node count of the benchmark graphs.
* The default value is 1000. Must be greater than 0.
*/
private int n = 1000;
/**
* The average node degree of the benchmark graphs.
* The default value is 20. Must be greater than 0.
*/
private int k = 20;
/**
* The maximum node degree of the benchmark graphs.
* The default value is 50. Must be greater or equal k.
*/
private int maxk = 50;
/**
* The topological mixing parameter which determines how many edges a node shares
* with nodes of other communities.
* The default value is 0.2. Must be in [0, 1].
*/
private double mut = 0.2;
/**
* The weight mixing parameter for the community-internal strengths of nodes.
* The default value is 0.1. Must be in [0, 1].
*/
private double muw = 0.1;
/**
* The exponent for the probability distribution of the total strengths of nodes.
* The default value is 1.5.
*/
private double beta = 1.5;
/**
* The exponent for the probability distribution of the node degrees.
* The default value is -2.
*/
private double t1 = -2;
/**
* The exponent for the probability distribution of community sizes.
* The default value is -1.
*/
private double t2 = -1;
/**
* The minimum community size of the benchmark graphs.
* The default value is 10. Must be greater than 0.
*/
private int minc = 10;
/**
* The maximum community size of the benchmark graphs.
* The default value is 50. Must be greater or equal minc.
*/
private int maxc = 50;
/**
* The number of overlapping nodes in the benchmark graphs.
* The default value is 100. Must be greater or equal 0.
*/
private int on = 100;
/**
* The number of communities an overlapping node belongs to.
* The default value is 2. Must be greater or equal 2.
*/
private int om = 2;
@Override
public Map<String, String> getParameters() {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put(N_NAME, Integer.toString(n));
parameters.put(K_NAME, Integer.toString(k));
parameters.put(MAXK_NAME, Integer.toString(maxk));
parameters.put(MUT_NAME, Double.toString(mut));
parameters.put(MUW_NAME, Double.toString(muw));
parameters.put(BETA_NAME, Double.toString(beta));
parameters.put(T1_NAME, Double.toString(t1));
parameters.put(T2_NAME, Double.toString(t2));
parameters.put(MINC_NAME, Integer.toString(minc));
parameters.put(MAXC_NAME, Integer.toString(maxc));
parameters.put(ON_NAME, Integer.toString(on));
parameters.put(OM_NAME, Integer.toString(om));
return parameters;
}
@Override
public void setParameters(Map<String, String> parameters) {
if(parameters.containsKey(N_NAME)) {
n = Integer.parseInt(parameters.get(N_NAME));
parameters.remove(N_NAME);
if(n <= 0) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(K_NAME)) {
k = Integer.parseInt(parameters.get(K_NAME));
parameters.remove(K_NAME);
if(k <= 0) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(MAXK_NAME)) {
maxk = Integer.parseInt(parameters.get(MAXK_NAME));
parameters.remove(MAXK_NAME);
if(maxk < k) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(MUT_NAME)) {
mut = Double.parseDouble(parameters.get(MUT_NAME));
parameters.remove(MUT_NAME);
if(mut < 0 || mut > 1) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(MUW_NAME)) {
muw = Double.parseDouble(parameters.get(MUW_NAME));
parameters.remove(MUW_NAME);
if(muw < 0 || muw > 1) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(BETA_NAME)) {
beta = Double.parseDouble(parameters.get(BETA_NAME));
parameters.remove(BETA_NAME);
}
if(parameters.containsKey(T1_NAME)) {
t1 = Double.parseDouble(parameters.get(T1_NAME));
parameters.remove(T1_NAME);
}
if(parameters.containsKey(T2_NAME)) {
t2 = Double.parseDouble(parameters.get(T2_NAME));
parameters.remove(T2_NAME);
}
if(parameters.containsKey(MINC_NAME)) {
minc = Integer.parseInt(parameters.get(MINC_NAME));
parameters.remove(MINC_NAME);
if(minc < 1) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(MAXC_NAME)) {
maxc = Integer.parseInt(parameters.get(MAXC_NAME));
parameters.remove(MAXC_NAME);
if(maxc < minc) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(ON_NAME)) {
on = Integer.parseInt(parameters.get(ON_NAME));
parameters.remove(ON_NAME);
if(on < 0 || on > n) {
throw new IllegalArgumentException();
}
}
if(parameters.containsKey(OM_NAME)) {
om = Integer.parseInt(parameters.get(OM_NAME));
parameters.remove(OM_NAME);
if(om < 2) {
throw new IllegalArgumentException();
}
}
if(parameters.size() > 0) {
throw new IllegalArgumentException();
}
}
/**
* Creates a standardized instance of the benchmark model.
*/
public LfrBenchmark() {
}
/*
* PARAMETER NAMES
*/
protected final String N_NAME = "n";
protected final String K_NAME = "k";
protected final String MAXK_NAME = "maxk";
protected final String MUT_NAME = "mut";
protected final String MUW_NAME = "muw";
protected final String BETA_NAME = "beta";
protected final String T1_NAME = "t1";
protected final String T2_NAME = "t2";
protected final String MINC_NAME = "minc";
protected final String MAXC_NAME = "maxc";
protected final String ON_NAME = "on";
protected final String OM_NAME = "om";
/**
* Creates a partially standardized instance of the benchmark model.
* The constructor is designed mainly for the creation of four sets of standard benchmark graphs
* Each set is obtained by setting mu and k to a combination of the suggested standard values and
* continuously increasing the fraction of overlapping nodes.
* The unspecified values are set to n=1000, maxk=50, minc=10, maxc=50, om=2, t1=-2, t2=-1 and beta=1.5.
* @param k Sets k.
* Is typically either 12 or 24.
* @param mu Sets mut and muw.
* Is typically either 0.1 or 0.3.
* @param overlappingNodeFraction Determines the percentage of overlapping nodes and hence on.
* Is typically increased from 0 by intervals of 0.05 or 0.1 up to 0.5 or 1.
*/
public LfrBenchmark(int k, double mu, double overlappingNodeFraction) {
this.minc = 10;
this.mut = mu;
this.muw = mu;
this.n = 1000;
this.k = k;
this.maxk = 50;
this.on = (int) Math.round((n * overlappingNodeFraction));
this.maxc = 50;
this.om = 2;
this.t1 = -2;
this.t2 = -1;
this.beta = 1.5;
}
/**
* Creates a customized instance of the benchmark model.
* The parameters must be values which are valid for the LFR model.
* @param n Sets n.
* @param k Sets k.
* @param maxk Sets maxk.
* @param mut Sets mut.
* @param muw Sets muw.
* @param beta Sets beta.
* @param t1 Sets t1.
* @param t2 Sets t2.
* @param minc Sets minc.
* @param maxc Sets maxc.
* @param on Sets on.
* @param om Sets om.
*/
public LfrBenchmark(int n, int k, int maxk, double mut, double muw,
double beta, double t1, double t2, int minc, int maxc, int on, int om) {
this.minc = minc;
this.mut = mut;
this.muw = muw;
this.n = n;
this.k = k;
this.maxk = maxk;
this.on = on;
this.maxc = maxc;
this.om = om;
this.t1 = t1;
this.t2 = t2;
this.beta = beta;
}
@Override
public Cover createGroundTruthCover() throws OcdBenchmarkException, InterruptedException {
synchronized (executor) {
try {
String executorFilename;
if(SystemUtils.IS_OS_WINDOWS) {
executorFilename = windowsBenchmarkGeneratorPath;
}
else if(SystemUtils.IS_OS_LINUX) {
executorFilename = linuxBenchmarkGeneratorPath;
}
/*
* Benchmark not implemented for this operating system.
*/
else {
throw new OcdBenchmarkException();
}
CommandLine cmdLine = new CommandLine(executorFilename);
cmdLine.addArgument("-N");
cmdLine.addArgument(Integer.toString(this.n));
cmdLine.addArgument("-k");
cmdLine.addArgument(Integer.toString(this.k));
cmdLine.addArgument("-maxk");
cmdLine.addArgument(Integer.toString(this.maxk));
cmdLine.addArgument("-muw");
cmdLine.addArgument(Double.toString(this.muw));
cmdLine.addArgument("-mut");
cmdLine.addArgument(Double.toString(this.mut));
cmdLine.addArgument("-beta");
cmdLine.addArgument(Double.toString(this.beta));
cmdLine.addArgument("-minc");
cmdLine.addArgument(Integer.toString(this.minc));
cmdLine.addArgument("-maxc");
cmdLine.addArgument(Integer.toString(this.maxc));
cmdLine.addArgument("-on");
cmdLine.addArgument(Integer.toString(this.on));
cmdLine.addArgument("-om");
cmdLine.addArgument(Integer.toString(this.om));
cmdLine.addArgument("-t1");
cmdLine.addArgument(Double.toString( - this.t1));
cmdLine.addArgument("-t2");
cmdLine.addArgument(Double.toString( - this.t2));
File workingDirectory = new File(lfrDirectoryPath);
executor.setWorkingDirectory(workingDirectory);
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
executor.execute(cmdLine, resultHandler);
resultHandler.waitFor();
if(resultHandler.getExitValue() != 0) {
System.out.println(resultHandler.getException());
throw new OcdBenchmarkException("LFR Process exit value: " + resultHandler.getExitValue());
}
GraphInputAdapter graphAdapter = new WeightedEdgeListGraphInputAdapter(new FileReader(graphPath));
CustomGraph graph = graphAdapter.readGraph();
graph.addType(GraphType.DIRECTED);
graph.addType(GraphType.WEIGHTED);
CoverInputAdapter coverAdapter = new NodeCommunityListsCoverInputAdapter(new FileReader(coverPath));
Cover cover = coverAdapter.readCover(graph);
cover.setCreationMethod(new CoverCreationLog(CoverCreationType.GROUND_TRUTH, new HashMap<String, String>(), new HashSet<GraphType>()));
return cover;
}
catch(InterruptedException e) {
throw e;
}
catch (Exception e) {
e.printStackTrace();
if(Thread.interrupted()) {
throw new InterruptedException();
}
throw new OcdBenchmarkException(e);
}
}
}
}