package edu.cmu.graphchi.walks;
import edu.cmu.graphchi.ChiVertex;
import edu.cmu.graphchi.EdgeDirection;
import java.util.Arrays;
import java.util.Random;
/**
* This class takes a vertex with <b>weighted</b> edges and
* generates an array of random hops. Note: the hops are edge-indices,
* not the edges themselves.
*/
public class WeightedHopper {
public static <VT> int[] generateRandomHopsOut(Random r, ChiVertex<VT, Float> vertex, int n) {
int l = vertex.numOutEdges();
float[] cumDist = new float[l];
float prefix = 0.0f;
for(int i=0; i < l; i++) {
float x = vertex.getOutEdgeValue(i);
cumDist[i] = prefix + x;
prefix += x;
}
int[] hops = new int[n];
for(int i=0; i < n; i++) {
float x = prefix * r.nextFloat();
if (l > 32) {
int h = Arrays.binarySearch(cumDist, x);
if (h < 0) h = -(h + 1);
hops[i] = h;
} else {
// linear scan
for(int j=0; j < l; j++) {
if (cumDist[j] > x) {
hops[i] = j;
break;
}
}
}
}
return hops;
}
public static <VT> int[] generateRandomHopsAliasMethodOut(Random r, ChiVertex<VT, Float> vertex, int n) {
return generateRandomHopsAliasMethod(r, vertex, n, EdgeDirection.OUT_EDGES, null);
}
public static <VT> int[] generateRandomHopsAliasMethod(Random r, ChiVertex<VT, Float> vertex, int n, EdgeDirection edgeDir,
EdgeWeightMap weightMap) {
int l = 0;
switch (edgeDir) {
case IN_AND_OUT_EDGES: l = vertex.numEdges(); break;
case OUT_EDGES: l = vertex.numOutEdges(); break;
case IN_EDGES: l = vertex.numInEdges(); break;
}
float[] values = new float[l];
int[] aliases = new int[l];
// Compute average
float sum = 0;
for(int i=0; i < l; i++) {
float x = 0.0f;
switch (edgeDir) {
case IN_AND_OUT_EDGES: x = vertex.edge(i).getValue(); break;
case OUT_EDGES: x= vertex.getOutEdgeValue(i); break;
case IN_EDGES: x = vertex.inEdge(i).getValue(); break;
}
if (weightMap != null) {
x = weightMap.map(x);
}
sum += x;
values[i] = x;
}
int[] aboveAverages = new int[l];
int[] belowAverages = new int[l];
int aboveIdx = 0;
int belowIdx = 0;
// Init stacks
for(int i=0; i < l; i++) {
values[i] = values[i] / sum * l;
if (values[i] < 1.0f) {
belowAverages[belowIdx++] = i;
} else {
aboveAverages[aboveIdx++] = i;
}
aliases[i] = -1;
}
// Start shoveling
while(aboveIdx > 0 && belowIdx > 0) {
int small = belowAverages[--belowIdx];
int large = aboveAverages[--aboveIdx];
aliases[small] = large;
values[large] = (values[large] - (1.0f - values[small]));
if (values[large] < 1) {
belowAverages[belowIdx++] = large;
} else {
aboveAverages[aboveIdx++] = large;
}
}
while(aboveIdx > 0) {
values[aboveAverages[--aboveIdx]] = 1.0f;
}
while(belowIdx > 0) { // might happen for numerical instability
values[belowAverages[--belowIdx]] = 1.0f;
}
int[] hops = new int[n];
// Hops
for(int i=0; i < n; i++) {
int bucket = r.nextInt(l);
float val = r.nextFloat();
if (val < values[bucket]) {
hops[i] = bucket;
} else {
hops[i] = aliases[bucket];
if (hops[i] < 0) hops[i] = bucket;
}
}
return hops;
}
public static interface EdgeWeightMap {
public float map(float x);
}
}