Commit ad69736a authored by Antoine's avatar Antoine

Added edge-betweeness centrality and edge direction.

Thanks to the contribution of Thibaut Démare.
parent 86d05e5c
......@@ -222,6 +222,12 @@ public class TestBetweenessCentrality {
assertEquals(0, (Double) graph.getNode("C").getAttribute("Cb"), 0);
assertEquals(2, (Double) graph.getNode("D").getAttribute("Cb"), 0);
assertEquals(4, (Double) graph.getNode("E").getAttribute("Cb"), 0);
assertEquals(8, (Double) graph.getEdge("AB").getAttribute("Cb"), 0);
assertEquals(8, (Double) graph.getEdge("AE").getAttribute("Cb"), 0);
assertEquals(0, (Double) graph.getEdge("BE").getAttribute("Cb"), 0);
assertEquals(4, (Double) graph.getEdge("BC").getAttribute("Cb"), 0);
assertEquals(4, (Double) graph.getEdge("CD").getAttribute("Cb"), 0);
assertEquals(8, (Double) graph.getEdge("ED").getAttribute("Cb"), 0);
}
@Test
......
......@@ -45,8 +45,10 @@ import org.graphstream.graph.Path;
* An implementation of the A* algorithm.
*
* <p>
* A* computes the shortest path from a node to another in a graph. It can
* eventually fail if the two nodes are in two distinct connected components.
* A* computes the shortest path from a node to another in a graph. It guarantees
* that the path found is the shortest one, given its heuristic is admissible,
* and a path exists between the two nodes. It will fail if the two nodes are in
* two distinct connected components.
* </p>
*
* <p>
......@@ -63,15 +65,22 @@ import org.graphstream.graph.Path;
*
* <p>
* By default the {@link org.graphstream.algorithm.AStar.Costs} implementation
* used uses a heuristic that returns 0 for any heuristic. This makes A* an
* equivalent of the Dijkstra algorithm, but also makes it far less efficient.
* used uses a heuristic that always returns 0. This makes A* an * equivalent of
* the Dijkstra algorithm, but also makes it less efficient.
* </p>
*
* <p>
* If there are several equivalent shortest paths between the two nodes, the returned
* one is arbitrary. Therefore this AStar algorithm works with multi-graphs but if two
* edges between two nodes have the same properties, the one that will be chosen will
* be arbitrary.
* </p>
*
* <h2>Usage</h2>
*
* <p>The basic usage is to create an instance of A*, then to ask it to compute
* from a shortest path from one target to one destination, and finally to ask
* for that path:
* <p>The basic usage is to create an instance of A* (optionally specify a {@link Costs}
* object), then to ask it to compute from a shortest path from one target to one
* destination, and finally to ask for that path:
* </p>
* <pre>
* AStart astar = new AStar(graph);
......@@ -80,11 +89,11 @@ import org.graphstream.graph.Path;
* </pre>
* <p>
* The advantage of A* is that it can consider any cost function to drive the
* search. You can create your own cost functions implementing the
* search. You can (and should) create your own cost functions implementing the
* {@link org.graphstream.algorithm.AStar.Costs} interface.
* </p>
* <p>
* You can also test the default "distance" cost function on a graph that has
* You can also test the default euclidean "distance" cost function on a graph that has
* "x" and "y" values. You specify the {@link Costs} function before calling the
* {@link #compute(String,String)} method:
* </p>
......@@ -152,7 +161,6 @@ import org.graphstream.graph.Path;
* }
* </pre>
*
*
* @complexity The complexity of A* depends on the heuristic.
*/
public class AStar implements Algorithm {
......@@ -482,7 +490,6 @@ public class AStar implements Algorithm {
// Nested classes
/**
* The definition of an heuristic. The heuristic is in charge of evaluating
* the distance between the current position and the target.
*/
public interface Costs {
......@@ -496,7 +503,7 @@ public class AStar implements Algorithm {
* @return The estimated cost between a node and a target node.
*/
double heuristic(Node node, Node target);
/**
* Cost of displacement from parent to next. The next node must be
* directly connected to parent, or -1 is returned.
......@@ -504,6 +511,7 @@ public class AStar implements Algorithm {
* @param parent
* The node we come from.
* @param from
* The definition of an heuristic. The heuristic is in charge of evaluating
* The edge used between the two nodes (in case this is a
* multi-graph).
* @param next
......
......@@ -37,6 +37,7 @@ import java.util.PriorityQueue;
import java.util.Set;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Element;
import org.graphstream.graph.Graph;
import org.graphstream.graph.Node;
......@@ -52,12 +53,12 @@ import org.graphstream.graph.Node;
* <h2>Usage</h2>
*
* <p>
* This algorithm, by default, stores the centrality values for each edge inside
* This algorithm, by default, stores the centrality values for each node inside
* the "Cb" attribute. You can change this attribute name at construction time.
* </p>
*
* <p>
* This algorithm does not accept multi-graphs (p-graphs with p>1) yet.
* <b>This algorithm does not accept multi-graphs (p-graphs with p>1) yet.</b>
* </p>
*
* <p>
......@@ -159,30 +160,39 @@ import org.graphstream.graph.Node;
* issn 0378-8733, "DOI: 10.1016/j.socnet.2007.11.001".
*/
public class BetweennessCentrality implements Algorithm {
// Attribute
protected static double INFINITY = 1000000.0;
/** Store the centrality value in this attribute on nodes and edges. */
protected String centralityAttributeName = "Cb";
/** The predecessors. */
protected String predAttributeName = "brandes.P";
/** The sigma value. */
protected String sigmaAttributeName = "brandes.sigma";
/** The distance value. */
protected String distAttributeName = "brandes.d";
/** The delta value. */
protected String deltaAttributeName = "brandes.delta";
/** Name of the attribute used to retrieve weights on edges. */
protected String weightAttributeName = "weight";
/** Do not use weights ? */
protected boolean unweighted = true;
/** The graph to modify. */
protected Graph graph;
/** The progress call-back method. */
protected Progress progress = null;
// Construction
/** Compute the centrality of edges. */
protected boolean doEdges = true;
/**
* New centrality algorithm that will perform as if the graph was
* unweighted. By default the centrality will be stored in a "Cb" attribute
......@@ -226,8 +236,6 @@ public class BetweennessCentrality implements Algorithm {
this.unweighted = false;
}
// Access
/**
* Name of the attribute used to retrieve weights on edges.
*/
......@@ -242,8 +250,6 @@ public class BetweennessCentrality implements Algorithm {
return centralityAttributeName;
}
// Command
/**
* Specify the name of the weight attribute to retrieve edge attributes.
* This automatically set the algorithm to perform on the graph as if it was
......@@ -270,6 +276,17 @@ public class BetweennessCentrality implements Algorithm {
public void setUnweighted() {
unweighted = true;
}
/**
* Activate or deactivate the centrality computation on edges. By default it is
* activated. Notice that this does not change the complexity of the algorithm.
* Only one more access on the edges is done to store the centrality in addition
* to the node access.
* @param on If true, the edges centrality is also computed.
*/
public void computeEdgeCentrality(boolean on) {
doEdges = on;
}
/**
* Specify the name of the attribute used to store the computed centrality
......@@ -306,13 +323,14 @@ public class BetweennessCentrality implements Algorithm {
}
/**
* Compute the betweenness centrality on the given graph for each node. This
* method is equivalent to a call in sequence to the two methods
* {@link #init(Graph)} then {@link #compute()}.
* Compute the betweenness centrality on the given graph for each node and
* eventually edges. This method is equivalent to a call in sequence to the
* two methods {@link #init(Graph)} then {@link #compute()}.
*/
public void betweennessCentrality(Graph graph) {
init(graph);
initAllNodes(graph);
initAllEdges(graph);
float n = graph.getNodeCount();
float i = 0;
......@@ -326,13 +344,18 @@ public class BetweennessCentrality implements Algorithm {
S = dijkstraExplore2(s, graph);
// The really new things in the Brandes algorithm are here:
// Accumulation phase:
while (!S.isEmpty()) {
Node w = S.poll();
for (Node v : predecessorsOf(w)) {
setDelta(v, delta(v)
+ ((sigma(v) / sigma(w)) * (1.0 + delta(w))));
double c = ((sigma(v) / sigma(w)) * (1.0 + delta(w)));
if(doEdges) {
Edge e = w.getEdgeBetween(v);
setCentrality(e, centrality(e) + c);
}
setDelta(v, delta(v) + c);
}
if (w != s) {
setCentrality(w, centrality(w) + delta(w));
......@@ -371,10 +394,11 @@ public class BetweennessCentrality implements Algorithm {
Node v = Q.removeFirst();
S.add(v);
Iterator<? extends Node> ww = v.getNeighborNodeIterator();
Iterator<? extends Edge> ww = v.getLeavingEdgeIterator();
while (ww.hasNext()) {
Node w = ww.next();
Edge l = ww.next();
Node w = l.getOpposite(v);//ww.next();
if (distance(w) == INFINITY) {
setDistance(w, distance(v) + 1);
......@@ -422,10 +446,14 @@ public class BetweennessCentrality implements Algorithm {
} else {
S.add(u);
Iterator<? extends Node> k = u.getNeighborNodeIterator();
// Iterator<? extends Node> k = u.getNeighborNodeIterator();
Iterator<? extends Edge> k = u.getLeavingEdgeIterator();
while (k.hasNext()) {
Node v = k.next();
// Node v = k.next();
Edge l = k.next();
Node v = l.getOpposite(u);
double alt = distance(u) + weight(u, v);
if (alt < distance(v)) {
......@@ -488,10 +516,14 @@ public class BetweennessCentrality implements Algorithm {
S.add(v);
Iterator<? extends Node> k = v.getNeighborNodeIterator();
//Iterator<? extends Node> k = v.getNeighborNodeIterator();
Iterator<? extends Edge> k = v.getLeavingEdgeIterator();
while (k.hasNext()) {
Node w = k.next();
//Node w = k.next();
Edge l = k.next();
Node w = l.getOpposite(v);
double alt = distance(v) + weight(v, w);
double dw = distance(w);
......@@ -565,16 +597,16 @@ public class BetweennessCentrality implements Algorithm {
}
/**
* The centrality value of the given node.
* The centrality value of the given node or edge.
*
* @param node
* Extract the centrality of this node.
* @param elt
* Extract the centrality of this node or edge.
* @return The centrality value.
*/
public double centrality(Node node) {
return node.getNumber(centralityAttributeName);
public double centrality(Element elt) {
return elt.getNumber(centralityAttributeName);
}
/**
* List of predecessors of the given node.
*
......@@ -624,17 +656,17 @@ public class BetweennessCentrality implements Algorithm {
}
/**
* Set the centrality of the given node.
* Set the centrality of the given node or edge.
*
* @param node
* The node to modify.
* @param elt
* The node or edge to modify.
* @param centrality
* The centrality to store on the node.
*/
public void setCentrality(Node node, double centrality) {
node.setAttribute(centralityAttributeName, centrality);
public void setCentrality(Element elt, double centrality) {
elt.setAttribute(centralityAttributeName, centrality);
}
/**
* Set the weight of the edge between 'from' and 'to'.
*
......@@ -727,6 +759,20 @@ public class BetweennessCentrality implements Algorithm {
setCentrality(node, 0.0);
}
}
/**
* Set a default centrality of 0 to all edges.
*
* @param graph
* The graph to modify.
*/
protected void initAllEdges(Graph graph) {
if(doEdges) {
for(Edge edge : graph.getEachEdge()) {
setCentrality(edge, 0.0);
}
}
}
/**
* Add a default value for attributes used during computation.
......
......@@ -266,8 +266,8 @@ public class Eades84Layout extends PipeBase implements Layout {
}
public void particleMoved(Object id, double x, double y, double z) {
for (LayoutListener listener : listeners)
listener.nodeMoved((String) id, x, y, z);
//for (LayoutListener listener : listeners)
// listener.nodeMoved((String) id, x, y, z);
Object xyz[] = new Object[3];
xyz[0] = x;
......
......@@ -207,8 +207,8 @@ public class HierarchicalLayout extends PipeBase implements Layout {
sendNodeAttributeChanged(sourceId, n.getId(), "xyz", null,
new double[] { p.x, p.y, 0 });
for (int i = 0; i < listeners.size(); i++)
listeners.get(i).nodeMoved(n.getId(), p.x, p.y, 0);
// for (int i = 0; i < listeners.size(); i++)
// listeners.get(i).nodeMoved(n.getId(), p.x, p.y, 0);
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment