package ldbc.snb.datagen.generator;
import javafx.util.Pair;
import ldbc.snb.datagen.objects.Knows;
import ldbc.snb.datagen.objects.Person;
import org.apache.hadoop.conf.Configuration;
import org.roaringbitmap.RoaringBitmap;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.*;
/**
* Created by aprat on 12/07/16.
*/
public class BTERKnowsGenerator implements KnowsGenerator{
private int graphSize = 0;
private Random random;
private Configuration conf;
private long [] expectedDegree;
private double [] p ;
private HashMap<Long,RoaringBitmap> openCommunities = new HashMap<Long,RoaringBitmap>();
private ArrayList<RoaringBitmap> closedCommunities = new ArrayList<RoaringBitmap>();
private RoaringBitmap smallDegreeNodes = new RoaringBitmap();
private RoaringBitmap [] adjacencyMatrix;
private int count = 0;
public int BinarySearch(ArrayList<Pair<Long,Double>> array, Long degree) {
int min = 0;
int max = array.size();
while(min <= max) {
int midPoint = (max - min) / 2 + min;
if(midPoint >= array.size()) return array.size()-1;
if(midPoint < 0) return 0;
if(array.get(midPoint).getKey() > degree ) {
max = midPoint - 1;
} else if(array.get(midPoint).getKey() < degree) {
min = midPoint + 1;
} else {
return midPoint;
}
}
return max;
}
void generateCommunities(RoaringBitmap block) {
Iterator<Integer> iter = block.iterator();
while(iter.hasNext()) {
int node = iter.next();
RoaringBitmap community = openCommunities.get(expectedDegree[node]+1);
if(community != null) {
community.add(node);
if(community.getCardinality() >= (expectedDegree[node]+1)) {
openCommunities.remove(expectedDegree[node]+1);
closedCommunities.add(community);
}
} else {
community = new RoaringBitmap();
community.add(node);
openCommunities.put(expectedDegree[node]+1,community);
}
}
}
void generateEdgesInCommunity(RoaringBitmap community) {
Iterator<Integer> iter = community.iterator();
while(iter.hasNext()) {
int nodeA = iter.next();
Iterator<Integer> iter2 = community.iterator();
while(iter2.hasNext()) {
int nodeB = iter2.next();
if(nodeA < nodeB) {
double prob = random.nextDouble();
if(prob < p[community.getCardinality()-1]) {
adjacencyMatrix[nodeA].add(nodeB);
adjacencyMatrix[nodeB].add(nodeA);
}
}
}
}
}
void generateRemainingEdges() {
LinkedList<Integer> stubs = new LinkedList<Integer>();
for(int i = 0; i < graphSize; ++i) {
long difference = expectedDegree[i]-adjacencyMatrix[i].getCardinality();
if( difference > 0) {
for(int j = 0; j < difference; ++j) {
stubs.add(i);
}
}
}
Collections.shuffle(stubs,random);
while(!stubs.isEmpty()) {
int node1 = stubs.get(0);
stubs.remove(0);
if(!stubs.isEmpty()) {
int node2 = stubs.get(0);
stubs.remove(0);
if(node1 != node2) {
adjacencyMatrix[node1].add(node2);
adjacencyMatrix[node2].add(node1);
}
}
}
}
@Override
public void generateKnows(ArrayList<Person> persons, int seed, ArrayList<Float> percentages, int step_index) {
graphSize = persons.size();
expectedDegree = new long[graphSize];
adjacencyMatrix = new RoaringBitmap[graphSize];
p = new double[graphSize];
for(int i = 0; i < graphSize; ++i) {
adjacencyMatrix[i] = new RoaringBitmap();
}
random = new Random();
random.setSeed(seed);
openCommunities.clear();
closedCommunities.clear();
smallDegreeNodes.clear();
int maxExpectedDegree = 0;
for(int i = 0; i < graphSize; ++i) {
adjacencyMatrix[i].clear();
expectedDegree[i] = Knows.target_edges(persons.get(i),percentages,step_index);
maxExpectedDegree = maxExpectedDegree < expectedDegree[i] ? (int)expectedDegree[i] : maxExpectedDegree;
}
p = new double[maxExpectedDegree+1];
/** Initializing the array of triangles **/
ArrayList<Pair<Long,Double>> ccDistribution = new ArrayList<Pair<Long,Double>>();
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(getClass().getResourceAsStream(conf.get("ldbc.snb.datagen.generator.BTERKnowsGenerator.ccDistribution")), "UTF-8"));
String line;
while ((line = reader.readLine()) != null) {
String data[] = line.split(" ");
ccDistribution.add(new Pair<Long, Double>(Long.parseLong(data[0]), Double.parseDouble(data[1])));
}
reader.close();
} catch( IOException e) {
e.printStackTrace();
}
p[0] = 0.0;
p[1] = 0.0;
for(int i = 2; i < maxExpectedDegree+1; ++i) {
int degree = i;
int pos = BinarySearch(ccDistribution,(long)degree);
if(ccDistribution.get(pos).getKey() == degree || pos == (ccDistribution.size() - 1)) {
p[degree] = ccDistribution.get(pos).getValue();
} else if( pos < ccDistribution.size() - 1 ){
long minDegree = ccDistribution.get(pos).getKey();
long maxDegree = ccDistribution.get(pos+1).getKey();
double ratio = (degree - minDegree) / (maxDegree - minDegree);
double minCC = ccDistribution.get(pos).getValue();
double maxCC = ccDistribution.get(pos+1).getValue();
double cc_current = ratio * (maxCC - minCC ) + minCC;
p[degree] = Math.pow(cc_current,1/3.0);
}
}
RoaringBitmap block = new RoaringBitmap();
for(int i = 0; i < graphSize; ++i) {
if(expectedDegree[i] > 1 ) {
block.add(i);
} else {
smallDegreeNodes.add(i);
}
}
generateCommunities(block);
TreeMap<Long,RoaringBitmap> sortedMap = new TreeMap<Long,RoaringBitmap>(openCommunities);
RoaringBitmap currentCommunity = null;
long currentCommunitySize = 0;
for(HashMap.Entry<Long,RoaringBitmap> community : sortedMap.entrySet()) {
RoaringBitmap nextCommunity = community.getValue();
if(currentCommunity == null) {
currentCommunity = nextCommunity;
currentCommunitySize = community.getKey();
} else {
while(currentCommunity.getCardinality()<=currentCommunitySize && nextCommunity.getCardinality() > 0) {
int nextNode = nextCommunity.select(0);
currentCommunity.add(nextNode);
nextCommunity.remove(nextNode);
}
if(currentCommunity.getCardinality()>=currentCommunitySize) {
closedCommunities.add(currentCommunity);
currentCommunity=null;
currentCommunitySize = 0;
if(nextCommunity.getCardinality() > 0) {
currentCommunity=nextCommunity;
currentCommunitySize = community.getKey();
}
}
}
}
openCommunities.clear();
for(RoaringBitmap community : closedCommunities) {
generateEdgesInCommunity(community);
}
generateRemainingEdges();
for (int i = 0; i < graphSize; ++i) {
Iterator<Integer> it = adjacencyMatrix[i].iterator();
while (it.hasNext()) {
int next = it.next();
Knows.createKnow(random, persons.get(i), persons.get(next));
}
}
count++;
}
@Override
public void initialize(Configuration conf) {
this.conf = conf;
}
}