apoc.generate.relationship.SimpleGraphRelationshipGenerator Maven / Gradle / Ivy
package apoc.generate.relationship;
import apoc.generate.config.DistributionBasedConfig;
import apoc.generate.distribution.DegreeDistribution;
import apoc.generate.distribution.MutableDegreeDistribution;
import apoc.generate.distribution.MutableSimpleDegreeDistribution;
import apoc.generate.utils.WeightedReservoirSampler;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.List;
/**
* A simple minded {@link RelationshipGenerator} based on a {@link DegreeDistribution}
*
* Please note that the distribution of randomly generated graphs isn't exactly uniform (see the paper below)
*
* Uses Blitzstein-Diaconis algorithm Ref:
*
* A SEQUENTIAL IMPORTANCE SAMPLING ALGORITHM FOR GENERATING RANDOM GRAPHS WITH PRESCRIBED DEGREES
* By Joseph Blitzstein and Persi Diaconis (Stanford University). (Harvard, June 2006)
*/
public class SimpleGraphRelationshipGenerator extends BaseRelationshipGenerator {
public SimpleGraphRelationshipGenerator(DistributionBasedConfig configuration) {
super(configuration);
}
/**
* {@inheritDoc}
*
* Returns an edge-set corresponding to a randomly chosen simple graph.
*/
@Override
protected List> doGenerateEdges() {
List> edges = new ArrayList<>();
MutableDegreeDistribution distribution = new MutableSimpleDegreeDistribution(getConfiguration().getDegrees());
while (!distribution.isZeroList()) {
// int length = distribution.size();
int index = 0;
long min = Long.MAX_VALUE;
// find minimal nonzero element
for (int i = 0; i < distribution.size(); ++i) {
long elem = distribution.get(i);
if (elem != 0 && elem < min) {
min = elem;
index = i;
}
}
WeightedReservoirSampler sampler = new WeightedReservoirSampler();
// Obtain a candidate list:
while (true) {
MutableDegreeDistribution temp = new MutableSimpleDegreeDistribution(distribution.getDegrees());
int candidateIndex = sampler.randomIndexChoice(temp.getDegrees(), index);
Pair edgeCandidate = Pair.of(candidateIndex, index);
// Checks if edge has already been added.
boolean skip = false;
for (Pair edge : edges) {
if (edge.equals(edgeCandidate)) {
skip = true;
break;
}
}
if (skip) {
continue;
}
// Prepare the candidate set and test if it is graphical
temp.decrease(index);
temp.decrease(candidateIndex);
if (temp.isValid()) {
distribution = temp;
edges.add(edgeCandidate); // edge is allowed, add it.
break;
}
}
}
return edges;
}
}