Skip to content

Commit

Permalink
gephi#1706 Clarify how pagerank works in the code and improve perform…
Browse files Browse the repository at this point in the history
…ance. Also adapt code to correctly take into account parallel edges so each neighbor is not processed several times (+ ignore self loops too).
  • Loading branch information
eduramiba committed Aug 20, 2017
1 parent 57d481c commit 8cad2f2
Showing 1 changed file with 129 additions and 40 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,13 @@ Development and Distribution License("CDDL") (collectively, the
*/
package org.gephi.statistics.plugin;

import it.unimi.dsi.fastutil.objects.Object2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.gephi.graph.api.Column;
import org.gephi.graph.api.DirectedGraph;
import org.gephi.graph.api.Edge;
Expand Down Expand Up @@ -166,11 +171,11 @@ private void saveCalculatedValues(Graph graph, Column attributeColumn, HashMap<N
}
}

private void setInitialValues(Graph graph, double[] pagerankValues, double[] weights, boolean directed, boolean useWeights) {
int N = graph.getNodeCount();
int index = 0;
private void setInitialValues(Graph graph, Map<Node, Integer> indicies, double[] pagerankValues, double[] weights, boolean directed, boolean useWeights) {
final int N = graph.getNodeCount();
for (Node s : graph.getNodes()) {
pagerankValues[index] = 1.0f / N;
final int index = indicies.get(s);
pagerankValues[index] = 1.0 / N;
if (useWeights) {
double sum = 0;
EdgeIterable eIter;
Expand All @@ -180,66 +185,148 @@ private void setInitialValues(Graph graph, double[] pagerankValues, double[] wei
eIter = ((UndirectedGraph) graph).getEdges(s);
}
for (Edge edge : eIter) {
sum += edge.getWeight();
if(!edge.isSelfLoop()){
sum += edge.getWeight();
}
}
weights[index] = sum;
}
index++;
}
}

private double calculateR(Graph graph, double[] pagerankValues, HashMap<Node, Integer> indicies, boolean directed, double prob) {
int N = graph.getNodeCount();
double r = 0;
double r = (1.0 - prob) / N;//Initialize to damping factor

//Calculate dangling nodes (nodes without out edges) contribution to all other nodes.
//Necessary for all nodes page rank values sum to be 1
NodeIterable nodesIterable = graph.getNodes();
for (Node s : nodesIterable) {
double danglingNodesRankContrib = 0;
for (Node s : graph.getNodes()) {
int s_index = indicies.get(s);
boolean out;
int outDegree;
if (directed) {
out = ((DirectedGraph) graph).getOutDegree(s) > 0;
outDegree = ((DirectedGraph) graph).getOutDegree(s);
} else {
out = graph.getDegree(s) > 0;
outDegree = graph.getDegree(s);
}

if (out) {
r += (1.0 - prob) * (pagerankValues[s_index] / N);
} else {
r += (pagerankValues[s_index] / N);
if (outDegree == 0) {
danglingNodesRankContrib += pagerankValues[s_index];
}

if (isCanceled) {
nodesIterable.doBreak();
return r;
break;
}
}
danglingNodesRankContrib *= prob / N;
r += danglingNodesRankContrib;

return r;
}

private double updateValueForNode(Graph graph, Node s, double[] pagerankValues, double[] weights,
HashMap<Node, Integer> indicies, boolean directed, boolean useWeights, double r, double prob) {
double res = r;
EdgeIterable eIter;
if (directed) {
eIter = ((DirectedGraph) graph).getInEdges(s);
} else {
eIter = graph.getEdges(s);
}
private Map<Node, Set<Node>> calculateInNeighborsPerNode(Graph graph, boolean directed) {
Map<Node, Set<Node>> inNeighborsPerNode = new Object2ObjectOpenHashMap<>();

for (Edge edge : eIter) {
Node neighbor = graph.getOpposite(s, edge);
int neigh_index = indicies.get(neighbor);
int normalize;
NodeIterable nodesIterable = graph.getNodes();
for (Node node : nodesIterable) {
Set<Node> nodeInNeighbors = new ObjectOpenHashSet<>();

EdgeIterable edgesIterable;
if (directed) {
normalize = ((DirectedGraph) graph).getOutDegree(neighbor);
edgesIterable = ((DirectedGraph) graph).getInEdges(node);
} else {
normalize = graph.getDegree(neighbor);
edgesIterable = graph.getEdges(node);
}

for (Edge edge : edgesIterable) {
if (!edge.isSelfLoop()) {
Node neighbor = graph.getOpposite(node, edge);
nodeInNeighbors.add(neighbor);
}

if (isCanceled) {
edgesIterable.doBreak();
break;
}
}

inNeighborsPerNode.put(node, nodeInNeighbors);

if (isCanceled) {
nodesIterable.doBreak();
break;
}
}

return inNeighborsPerNode;
}

private Map<Node, Object2DoubleOpenHashMap<Node>> calculateInWeightPerNodeAndNeighbor(Graph graph, boolean directed, boolean useWeights) {
Object2ObjectOpenHashMap<Node, Object2DoubleOpenHashMap<Node>> inWeightPerNodeAndNeighbor = new Object2ObjectOpenHashMap<>();

if (useWeights) {
NodeIterable nodesIterable = graph.getNodes();
for (Node node : nodesIterable) {
Object2DoubleOpenHashMap<Node> inWeightPerNeighbor = new Object2DoubleOpenHashMap<>();
inWeightPerNeighbor.defaultReturnValue(0);

EdgeIterable edgesIterable;
if (directed) {
edgesIterable = ((DirectedGraph) graph).getInEdges(node);
} else {
edgesIterable = graph.getEdges(node);
}

for (Edge edge : edgesIterable) {
if (!edge.isSelfLoop()) {
Node neighbor = graph.getOpposite(node, edge);
inWeightPerNeighbor.addTo(neighbor, edge.getWeight());
}

if (isCanceled) {
edgesIterable.doBreak();
break;
}
}

if (isCanceled) {
nodesIterable.doBreak();
break;
}

inWeightPerNodeAndNeighbor.put(node, inWeightPerNeighbor);
}
}

return inWeightPerNodeAndNeighbor;
}

private double updateValueForNode(Graph graph, Node node, double[] pagerankValues, double[] weights,
HashMap<Node, Integer> indicies, boolean directed, boolean useWeights, double r, double prob,
Map<Node, Set<Node>> inNeighborsPerNode, final Object2DoubleOpenHashMap<Node> inWeightPerNeighbor) {
double res = r;

double sumNeighbors = 0;
for (Node neighbor : inNeighborsPerNode.get(node)) {
int neigh_index = indicies.get(neighbor);

if (useWeights) {
double weight = edge.getWeight() / weights[neigh_index];
res += prob * pagerankValues[neigh_index] * weight;
double weight = inWeightPerNeighbor.getDouble(neighbor) / weights[neigh_index];
sumNeighbors += pagerankValues[neigh_index] * weight;
} else {
res += prob * (pagerankValues[neigh_index] / normalize);
int outDegree;
if (directed) {
outDegree = ((DirectedGraph) graph).getOutDegree(neighbor);
} else {
outDegree = graph.getDegree(neighbor);
}
sumNeighbors += (pagerankValues[neigh_index] / outDegree);
}
}

res += prob * sumNeighbors;

return res;
}

Expand All @@ -250,18 +337,20 @@ private double updateValueForNode(Graph graph, Node s, double[] pagerankValues,
double[] temp = new double[N];

Progress.start(progress);
double[] weights = new double[N];
final double[] weights = useWeights ? new double[N] : null;
final Map<Node, Set<Node>> inNeighborsPerNode = calculateInNeighborsPerNode(graph, directed);
final Map<Node, Object2DoubleOpenHashMap<Node>> inWeightPerNodeAndNeighbor = calculateInWeightPerNodeAndNeighbor(graph, directed, useWeights);

setInitialValues(graph, pagerankValues, weights, directed, useWeights);
setInitialValues(graph, indicies, pagerankValues, weights, directed, useWeights);

while (true) {
double r = calculateR(graph, pagerankValues, indicies, directed, prob);

boolean done = true;

double r = calculateR(graph, pagerankValues, indicies, directed, prob);
NodeIterable nodesIterable = graph.getNodes();
for (Node s : nodesIterable) {
int s_index = indicies.get(s);
temp[s_index] = updateValueForNode(graph, s, pagerankValues, weights, indicies, directed, useWeights, r, prob);
temp[s_index] = updateValueForNode(graph, s, pagerankValues, weights, indicies, directed, useWeights, r, prob, inNeighborsPerNode, inWeightPerNodeAndNeighbor.get(s));

if ((temp[s_index] - pagerankValues[s_index]) / pagerankValues[s_index] >= eps) {
done = false;
Expand Down

0 comments on commit 8cad2f2

Please sign in to comment.