Commit abbde18e authored by sbalev's avatar sbalev

Merge branch 'spanningtree'

parents 08a84a74 26765577
package org.graphstream.algorithm.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.graphstream.algorithm.Kruskal;
import org.graphstream.algorithm.Prim;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Graph;
import org.graphstream.graph.implementations.SingleGraph;
import org.junit.Test;
public class TestKruskalPrim {
// B-----8-----C-----7-----D
// /| / \ |\
// / | / \ | \
// 4 | 2 \ | 9
// / | / \ | \
// / | / \ | \
// A 11 I 4 14 E
// \ | / \ \ | /
// \ | / \ \ | /
// 8 | 7 6 \ | 10
// \ | / \ \ | /
// \|/ \ \|/
// H-----1-----G-----2-----F
public static Graph toyGraph() {
String[] eIds = {"AB", "AH", "BH", "BC", "HI", "HG", "IC", "IG", "CD", "CF", "GF", "DF", "DE", "FE"};
int[] weights = {4, 8, 11, 8, 7, 1, 2, 6, 7, 4, 2, 14, 9, 10};
Graph g = new SingleGraph("test", false, true);
for (int i = 0; i < eIds.length; i++) {
String eId = eIds[i];
Edge e = g.addEdge(eId, eId.substring(0, 1), eId.substring(1, 2));
e.addAttribute("weight", weights[i]);
}
return g;
}
@Test
public void toyTest() {
Graph g = toyGraph();
Kruskal k = new Kruskal("weight", "kruskal");
k.init(g);
k.compute();
helper(k, g, 37.0, 8);
Prim p = new Prim("weight", "prim");
p.init(g);
p.compute();
helper(p, g, 37.0, 8);
// remove the lightest edge
g.removeEdge("HG");
k.compute();
helper(k, g, 43.0, 8);
p.compute();
helper(p, g, 43.0, 8);
// now cut the graph in 2 CC
g.removeEdge("BC");
g.removeEdge("HI");
k.compute();
helper(k, g, 36.0, 7);
p.compute();
helper(p, g, 36.0, 7);
}
public void helper(Kruskal k, Graph g, double expectedWeight, int expectedCount) {
assertEquals(expectedWeight, k.getTreeWeight(), 0);
int edgeCount = 0;
for (Edge e : k.getTreeEdges()) {
boolean b = e.getAttribute(k.getFlagAttribute());
assertTrue(b);
edgeCount++;
}
assertEquals(expectedCount, edgeCount);
edgeCount = 0;
double treeWeight = 0;
for (Edge e : g.getEachEdge()) {
boolean b = e.getAttribute(k.getFlagAttribute());
if (b) {
edgeCount++;
treeWeight += e.getNumber("weight");
}
}
assertEquals(expectedWeight, treeWeight, 0);
assertEquals(expectedCount, edgeCount);
}
}
......@@ -40,20 +40,25 @@ import org.graphstream.graph.Graph;
* <p>
* The result is stored in an edge attribute which name is defined by
* {@link #flagAttribute} and value is {@link #flagOn} if the edge is in the
* tree or {@link #flagOff} if not.
* tree or {@link #flagOff} if not. If {@link #flagAttribute} is {@code null}
* nothing is stored in the edges. If {@link #flagOn} is {@code null} edges in
* the tree are not tagged. If {@link #flagOff} is {@code null} edges out of the
* tree are not tagged.
* </p>
*
*
* <h2>Creating a spanning tree algorithm</h2>
*
* <p>
* Spanning tree algorithms have to extend this class and to implements the
* {@link #makeTree()} method. {@link #edgeOn(Edge)} and {@link #edgeOff(Edge)}
* methods have to be used to properly tag edge.
* {@link #makeTree()} and {@link #getTreeEdgesIterator()} methods.
* {@link #edgeOn(Edge)} and {@link #edgeOff(Edge)} methods have to be used to
* properly tag edges.
* </p>
*
* <p>
* A call to compute reset the values of edges attribute. Then a call to
* {@link #makeTree()} is done.
* {@link #makeTree()} is made.
* </p>
*
* <h2>Highlight the spanning tree in viewer</h2>
......@@ -106,7 +111,11 @@ import org.graphstream.graph.Graph;
* ..
* </pre>
*/
public abstract class AbstractSpanningTree implements Algorithm {
/**
* @author stefan
*
*/
public abstract class AbstractSpanningTree implements SpanningTree {
/**
* The graph on which algorithm try to extract a spanning tree.
*/
......@@ -130,14 +139,16 @@ public abstract class AbstractSpanningTree implements Algorithm {
protected Object flagOff;
/**
* Create a new SpanningTree algorithm.
* Create a new SpanningTree algorithm. By default edges are not tagged.
*/
public AbstractSpanningTree() {
this("SpanningTree.flag");
this(null, null, null);
}
/**
* Create a new SpanningTree algorithm.
* Create a new SpanningTree algorithm. Default flag attribute values are
* {@code true} for edges in the tree and {@code false} for the remaining
* edges.
*
* @param flagAttribute
* attribute used to compare edges
......@@ -160,71 +171,62 @@ public abstract class AbstractSpanningTree implements Algorithm {
*/
public AbstractSpanningTree(String flagAttribute, Object flagOn,
Object flagOff) {
graph = null;
this.flagAttribute = flagAttribute;
this.flagOn = flagOn;
this.flagOff = flagOff;
}
/**
* Get key attribute which will be used to set if edges are in the spanning
* tree, or not.
*
* @return flag attribute
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#getFlagAttribute()
*/
public String getFlagAttribute() {
return this.flagAttribute;
return flagAttribute;
}
/**
* Set the flag attribute.
*
* @param newFlagAttribute
* new attribute used
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#setFlagAttribute(java.lang.String)
*/
public void setFlagAttribute(String newFlagAttribute) {
this.flagAttribute = newFlagAttribute;
public void setFlagAttribute(String flagAttribute) {
if (graph != null)
throw new IllegalStateException(
"Flag attribute can be set only before the algorithm is initialized");
this.flagAttribute = flagAttribute;
}
/**
* Get value used to set that an edge is in the spanning tree.
*
* @return on value
*/
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#getFlagOn()
*/
public Object getFlagOn() {
return this.flagOn;
return flagOn;
}
/**
* Set value used to set that an edge is in the spanning tree.
*
* @param newFlagOn
* on value
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#setFlagOn(java.lang.Object)
*/
public void setFlagOn(Object newFlagOn) {
if (!this.flagOff.equals(newFlagOn))
this.flagOn = newFlagOn;
public void setFlagOn(Object flagOn) {
if (graph != null)
throw new IllegalStateException(
"Flag values can be set only before the algorithm is initialized");
this.flagOn = flagOn;
}
/**
* Get value used to set that an edge is not in the spanning tree.
*
* @return off value
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#getFlagOff()
*/
public Object getFlagOff() {
return this.flagOff;
return flagOff;
}
/**
* Set value used to set that an edge is not in the spanning tree.
*
* @param newFlagOff
* off value
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#setFlagOff(java.lang.Object)
*/
public void setFlagOff(Object newFlagOff) {
if (!this.flagOn.equals(newFlagOff))
this.flagOff = newFlagOff;
public void setFlagOff(Object flagOff) {
if (graph != null)
throw new IllegalStateException(
"Flag values can be set only before the algorithm is initialized");
this.flagOff = flagOff;
}
// Protected Access
......@@ -236,7 +238,12 @@ public abstract class AbstractSpanningTree implements Algorithm {
* edge to add
*/
protected void edgeOn(Edge e) {
e.changeAttribute(flagAttribute, flagOn);
if (flagAttribute != null) {
if (flagOn != null)
e.changeAttribute(flagAttribute, flagOn);
else
e.removeAttribute(flagAttribute);
}
}
/**
......@@ -246,27 +253,56 @@ public abstract class AbstractSpanningTree implements Algorithm {
* edge to remove
*/
protected void edgeOff(Edge e) {
e.changeAttribute(flagAttribute, flagOff);
if (flagAttribute != null) {
if (flagOff != null)
e.changeAttribute(flagAttribute, flagOff);
else
e.removeAttribute(flagAttribute);
}
}
/**
* Reset cluster and flag attribute values.
* Reset flag attribute values. All edges are tagged as being out of the
* tree.
*/
protected void resetFlags() {
Iterator<? extends Edge> iteE;
iteE = graph.getEdgeIterator();
while (iteE.hasNext())
edgeOff(iteE.next());
for (Edge edge : graph.getEachEdge())
edgeOff(edge);
}
// Abstract methods to be implemented by subclasses
/**
* Method that will be implemented by spanning tree's algorithms to build
* the tree.
*/
protected abstract void makeTree();
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#getTreeEdgesIterator()
*/
public abstract <T extends Edge> Iterator<T> getTreeEdgesIterator();
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#getTreeEdges()
*/
public <T extends Edge> Iterable<T> getTreeEdges() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return getTreeEdgesIterator();
}
};
}
/* (non-Javadoc)
* @see org.graphstream.algorithm.SpanningTree#clear()
*/
public void clear() {
if (flagAttribute != null)
for (Edge edge : graph.getEachEdge())
edge.removeAttribute(flagAttribute);
}
// Algorithm interface
/*
......
......@@ -149,7 +149,7 @@ import org.graphstream.graph.Path;
*
* @author Stefan Balev
*/
public class Dijkstra implements Algorithm {
public class Dijkstra extends AbstractSpanningTree {
protected static class Data {
FibonacciHeap<Double, Node>.Node fn;
Edge edgeFromParent;
......@@ -180,7 +180,6 @@ public class Dijkstra implements Algorithm {
protected Element element;
protected String resultAttribute;
protected String lengthAttribute;
protected Graph graph;
protected Node source;
// *** Helpers ***
......@@ -208,7 +207,7 @@ public class Dijkstra implements Algorithm {
// *** Constructors ***
/**
* Constructs an instance with the specified parameters.
* Constructs an instance with the specified parameters. The edges of the shortest path tree are not tagged.
*
* @param element
* Graph elements (edges or/and nodes) used to compute the path
......@@ -225,20 +224,49 @@ public class Dijkstra implements Algorithm {
*/
public Dijkstra(Element element, String resultAttribute,
String lengthAttribute) {
this.element = element == null ? Element.EDGE : element;
this.resultAttribute = resultAttribute == null ? toString()
+ "_result_" : resultAttribute;
this.lengthAttribute = lengthAttribute;
graph = null;
source = null;
this(element, resultAttribute, lengthAttribute, null, null, null);
}
/**
* Constructs an instance in which the length of the path is considered to
* be the number of edges. Unique result attribute is chosen automatically.
* be the number of edges. Unique result attribute is chosen automatically. The edges of the shortest path tree are not tagged.
*/
public Dijkstra() {
this(null, null, null);
this(null, null, null, null, null, null);
}
/**
* Constructs an instance with the specified parameters.
*
* @param element
* Graph elements (edges or/and nodes) used to compute the path
* lengths. If {@code null}, the length of the path is computed
* using edges.
* @param resultAttribute
* Attribute name used to store internal solution data in the
* nodes of the graph. If {@code null}, a unique name is chosen
* automatically.
* @param lengthAttribute
* Attribute name used to define individual element lengths. If
* {@code null} the length of the elements is considered to be
* one.
* @param flagAttribute
* attribute used to set if an edge is in the spanning tree
* @param flagOn
* value of the <i>flagAttribute</i> if edge is in the spanning
* tree
* @param flagOff
* value of the <i>flagAttribute</i> if edge is not in the
* spanning tree
*/
public Dijkstra(Element element, String resultAttribute, String lengthAttribute, String flagAttribute, Object flagOn, Object flagOff) {
super(flagAttribute, flagOn, flagOff);
this.element = element == null ? Element.EDGE : element;
this.resultAttribute = resultAttribute == null ? toString()
+ "_result_" : resultAttribute;
this.lengthAttribute = lengthAttribute;
graph = null;
source = null;
}
// *** Some basic methods ***
......@@ -272,7 +300,9 @@ public class Dijkstra implements Algorithm {
* of the graph. Use this method to free memory. Solution access methods
* must not be used after calling this method.
*/
@Override
public void clear() {
super.clear();
for (Node node : graph) {
Data data = node.getAttribute(resultAttribute);
if (data != null) {
......@@ -285,15 +315,6 @@ public class Dijkstra implements Algorithm {
// *** Methods of Algorithm interface ***
/*
* (non-Javadoc)
*
* @see
* org.graphstream.algorithm.Algorithm#init(org.graphstream.graph.Graph)
*/
public void init(Graph graph) {
this.graph = graph;
}
/**
* Computes the shortest paths from the source node to all nodes in the
......@@ -308,6 +329,7 @@ public class Dijkstra implements Algorithm {
* the number of edges and <em>n</em> is the number of nodes in
* the graph.
*/
@Override
public void compute() {
// check if computation can start
if (graph == null)
......@@ -316,7 +338,12 @@ public class Dijkstra implements Algorithm {
if (source == null)
throw new IllegalStateException(
"No source specified. Call setSource() first.");
resetFlags();
makeTree();
}
@Override
protected void makeTree() {
// initialization
FibonacciHeap<Double, Node> heap = new FibonacciHeap<Double, Node>();
for (Node node : graph) {
......@@ -334,6 +361,8 @@ public class Dijkstra implements Algorithm {
Data dataU = u.getAttribute(resultAttribute);
dataU.distance = dataU.fn.getKey();
dataU.fn = null;
if (dataU.edgeFromParent != null)
edgeOn(dataU.edgeFromParent);
for (Edge e : u.getEachLeavingEdge()) {
Node v = e.getOpposite(u);
Data dataV = v.getAttribute(resultAttribute);
......@@ -345,7 +374,7 @@ public class Dijkstra implements Algorithm {
heap.decreaseKey(dataV.fn, tryDist);
}
}
}
}
}
// *** Iterators ***
......@@ -714,25 +743,11 @@ public class Dijkstra implements Algorithm {
* @complexity Each call of {@link java.util.Iterator#next()} of this
* iterator takes O(1) time
*/
@Override
public <T extends Edge> Iterator<T> getTreeEdgesIterator() {
return new TreeIterator<T>();
}
/**
* Dijkstra's algorithm produces a shortest path tree rooted in the source
* node. This method provides an iterable view of the edges of this tree.
* Uses {@link #getTreeEdgesIterator()}
*
* @return an iterable view of the edges of the shortest path tree
* @see #getTreeEdgesIterator()
*/
public <T extends Edge> Iterable<T> getTreeEdges() {
return new Iterable<T>() {
public Iterator<T> iterator() {
return getTreeEdgesIterator();
}
};
}
/**
* Returns the shortest path from the source node to a given target node. If
......
......@@ -29,13 +29,16 @@
*/
package org.graphstream.algorithm;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Node;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.graphstream.algorithm.util.DisjointSets;
import org.graphstream.graph.Edge;
import org.graphstream.graph.Node;
/**
* Compute a spanning tree using the Kruskal algorithm.
......@@ -49,7 +52,7 @@ import java.util.Iterator;
* <h2>Example</h2>
*
* The following example generates a graph with the Dorogovtsev-Mendes generator
* and then compute a spanning-tree using the Kruskal algorithm. The generator
* and then computes a spanning-tree using the Kruskal algorithm. The generator
* creates random weights for edges that will be used by the Kruskal algorithm.
*
* If no weight is present, algorithm considers that all weights are set to 1.
......@@ -95,7 +98,8 @@ import java.util.Iterator;
* }
* </pre>
*
* @complexity m*(log(m)+3)+n+n<sup>2</sup>, m = |E|, n = |V|
* @complexity O(m log n) where m is the number of edges and n is the number of
* nodes of the graph
* @reference Joseph. B. Kruskal: On the Shortest Spanning Subtree of a Graph
* and the Traveling Salesman Problem. In: Proceedings of the
* American Mathematical Society, Vol 7, No. 1 (Feb, 1956), pp. 48–50
......@@ -104,29 +108,36 @@ import java.util.Iterator;
*/
public class Kruskal extends AbstractSpanningTree {
/**
* Attribute which will be used to compare edges.
* Default weight attribute
*/
public static final String DEFAULT_WEIGHT_ATTRIBUTE = "weight";
/**
* Attribute where the weights of the edges are stored
*/
protected String weightAttribute;
/**
* Attribute used to clusterize the graph.
* List of the tree edges. Used by the iterator.
*/
protected String clusterAttribute = "Kruskal.cluster";
protected List<Edge> treeEdges;
/**
* List of edges that will be added to the tree.
* The weight of the spanning tree
*/
protected LinkedList<Edge> edgesToTreat;
protected double treeWeight;
/**
* Create a new Kruskal's algorithm.
* Create a new Kruskal's algorithm. Uses the default weight attribute and
* does not tag the edges.
*/
public Kruskal() {
this("weight", "Kruskal.flag");
this(DEFAULT_WEIGHT_ATTRIBUTE, null);
}
/**
* Create a new Kruskal's algorithm.
* Create a new Kruskal's algorithm. The value of the flag attribute is
* {@code true} for the tree edges and false for the non-tree edges.
*
* @param weightAttribute
* attribute used to compare edges
......@@ -138,7 +149,7 @@ public class Kruskal extends AbstractSpanningTree {
}
/**
* Create a new Kruskal's algorithm.
* Create a new Kruskal's algorithm. Uses the default weight attribute.
*
* @param flagAttribute
* attribute used to set if an edge is in the spanning tree
......@@ -150,7 +161,7 @@ public class Kruskal extends AbstractSpanningTree {
* spanning tree
*/
public Kruskal(String flagAttribute, Object flagOn, Object flagOff) {
this("weight", flagAttribute, flagOn, flagOff);
this(DEFAULT_WEIGHT_ATTRIBUTE, flagAttribute, flagOn, flagOff);
}
/**
......@@ -172,16 +183,15 @@ public class Kruskal extends AbstractSpanningTree {
super(flagAttribute, flagOn, flagOff);
this.weightAttribute = weightAttribute;
this.edgesToTreat = new LinkedList<Edge>();
}
/**
* Get key attribute used to compare edges.
* Get weight attribute used to compare edges.
*
* @return weight attribute
*/
public String getWeightAttribute() {
return this.weightAttribute;
return weightAttribute;
}
/**
......@@ -194,168 +204,93 @@ public class Kruskal extends AbstractSpanningTree {
this.weightAttribute = newWeightAttribute;
}
// Protected Access
/**
* Sort edges using <i>weightAttribute</i> to compare.
*/
protected void sortEdgesByWeight() {
Collections.sort(edgesToTreat, new WeightEdgeComparator());
}
/**
* Create the <i>edgesToTreat</i> list. Also check if all edges as a
* <i>weightAttribute</i> which is an instance of Comparable.
*
* @see java.lang.Comparable
*/
protected void buildAndCheck() {
Iterator<? extends Edge> iteE;
boolean error = false;
edgesToTreat.clear();
iteE = this.graph.getEdgeIterator();
while (iteE.hasNext()) {
edgesToTreat.addLast(iteE.next());
if (!edgesToTreat.getLast().hasAttribute(weightAttribute,
Comparable.class)) {
error = true;
@Override
protected void makeTree() {
if (treeEdges == null)
treeEdges = new LinkedList<Edge>();
else
treeEdges.clear();
List<Edge> sortedEdges = new ArrayList<Edge>(graph.getEdgeSet());
Collections.sort(sortedEdges, new EdgeComparator());
DisjointSets<Node> components = new DisjointSets<Node>(
graph.getNodeCount());
for (Node node : graph)
components.add(node);
treeWeight = 0;
for (Edge edge : sortedEdges)
if (components.union(edge.getNode0(), edge.getNode1())) {
treeEdges.add(edge);
edgeOn(edge);
treeWeight += getWeight(edge);
if (treeEdges.size() == graph.getNodeCount() - 1)
break;
}
}
if (error) {
System.err
.printf("*** error *** Kruskal's algorithm: some weight are not comparable%n");
}
sortedEdges.clear();
components.clear();
}
/**
* Reset cluster and flag attribute values.
*/
@Override
protected void resetFlags() {
super.resetFlags();
Iterator<? extends Node> iteN;
int cluster = 0;
iteN = this.graph.getNodeIterator();
while (iteN.hasNext()) {
iteN.next().setAttribute(clusterAttribute, cluster++);
}
public <T extends Edge> Iterator<T> getTreeEdgesIterator() {
return new TreeIterator<T>();
}
/**
* Get weight of an edge.
*
* @param e
* an edge
* @return weight of <i>e</i>
*/
protected Double getWeight(Edge e) {
if (!e.hasNumber(weightAttribute))
return Double.valueOf(1);
return e.getNumber(weightAttribute);
@Override
public void clear() {
super.clear();
treeEdges.clear();