View Javadoc
1   /*
2    * Created on Aug 22, 2003
3    *
4    */
5   package edu.uci.ics.jung.algorithms.shortestpath;
6   
7   import java.util.HashMap;
8   import java.util.HashSet;
9   import java.util.Iterator;
10  import java.util.List;
11  import java.util.ListIterator;
12  import java.util.Map;
13  import java.util.Set;
14  
15  import junit.framework.TestCase;
16  
17  import com.google.common.base.Function;
18  import com.google.common.base.Functions;
19  import com.google.common.base.Supplier;
20  import com.google.common.collect.BiMap;
21  
22  import edu.uci.ics.jung.algorithms.util.Indexer;
23  import edu.uci.ics.jung.graph.DirectedGraph;
24  import edu.uci.ics.jung.graph.DirectedSparseMultigraph;
25  import edu.uci.ics.jung.graph.Graph;
26  import edu.uci.ics.jung.graph.UndirectedGraph;
27  import edu.uci.ics.jung.graph.UndirectedSparseMultigraph;
28  
29  
30  /**
31   * @author Joshua O'Madadhain
32   */
33  public class TestShortestPath extends TestCase
34  {
35      private DirectedGraph<String,Integer> dg;  
36      private UndirectedGraph<String,Integer> ug;
37      // graph based on Weiss, _Data Structures and Algorithm Analysis_,
38      // 1992, p. 292
39      private static int[][] edges = 
40          {{1,2,2}, {1,4,1}, // 0, 1
41              {2,4,3}, {2,5,10}, // 2, 3
42              {3,1,4}, {3,6,5},  // 4, 5
43              {4,3,2}, {4,5,2}, {4,6,8}, {4,7,4}, // 6,7,8,9
44              {5,7,6}, // 10
45              {7,6,1}, // 11
46              {8,9,4}, // (12) these three edges define a second connected component
47              {9,10,1}, // 13
48              {10,8,2}}; // 14
49  
50      private static Integer[][] ug_incomingEdges = 
51      {
52          {null, new Integer(0), new Integer(6), new Integer(1), new Integer(7), new Integer(11), new Integer(9), null, null, null},
53          {new Integer(0), null, new Integer(6), new Integer(2), new Integer(7), new Integer(11), new Integer(9), null, null, null},
54          {new Integer(1), new Integer(2), null, new Integer(6), new Integer(7), new Integer(5), new Integer(9), null, null, null},
55          {new Integer(1), new Integer(2), new Integer(6), null, new Integer(7), new Integer(11), new Integer(9), null, null, null},
56          {new Integer(1), new Integer(2), new Integer(6), new Integer(7), null, new Integer(11), new Integer(10), null, null, null},
57          {new Integer(1), new Integer(2), new Integer(5), new Integer(9), new Integer(10), null, new Integer(11), null, null, null},
58          {new Integer(1), new Integer(2), new Integer(5), new Integer(9), new Integer(10), new Integer(11), null, null, null, null},
59          {null, null, null, null, null, null, null, null, new Integer(13), new Integer(14)},
60          {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)},
61          {null, null, null, null, null, null, null, new Integer(14), new Integer(13), null},
62      };      
63      
64      private static Integer[][] dg_incomingEdges = 
65          {
66              {null, new Integer(0), new Integer(6), new Integer(1), new Integer(7), new Integer(11), new Integer(9), null, null, null},
67              {new Integer(4), null, new Integer(6), new Integer(2), new Integer(7), new Integer(11), new Integer(9), null, null, null},
68              {new Integer(4), new Integer(0), null, new Integer(1), new Integer(7), new Integer(5), new Integer(9), null, null, null},
69              {new Integer(4), new Integer(0), new Integer(6), null, new Integer(7), new Integer(11), new Integer(9), null, null, null},
70              {null, null, null, null, null, new Integer(11), new Integer(10), null, null, null},
71              {null, null, null, null, null, null, null, null, null, null},
72              {null, null, null, null, null, new Integer(11), null, null, null, null},
73              {null, null, null, null, null, null, null, null, new Integer(12), new Integer(13)},
74              {null, null, null, null, null, null, null, new Integer(14), null, new Integer(13)},
75              {null, null, null, null, null, null, null, new Integer(14), new Integer(12), null}
76      };      
77      
78      private static double[][] dg_distances = 
79      {
80          {0, 2, 3, 1, 3, 6, 5, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
81          {9, 0, 5, 3, 5, 8, 7, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
82          {4, 6, 0, 5, 7, 5, 9, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
83          {6, 8, 2, 0, 2, 5, 4, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
84          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 7, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
85          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
86          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 1, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
87          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 4, 5},
88          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 3, 0, 1},
89          {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2, 6, 0}
90      };
91  
92      private static double[][] ug_distances = 
93      {
94          {0, 2, 3, 1, 3, 6, 5, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
95          {2, 0, 5, 3, 5, 8, 7, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
96          {3, 5, 0, 2, 4, 5, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
97          {1, 3, 2, 0, 2, 5, 4, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
98          {3, 5, 4, 2, 0, 7, 6, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
99          {6, 8, 5, 5, 7, 0, 1, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
100         {5, 7, 6, 4, 6, 1, 0, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY},
101         {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 0, 3, 2},
102         {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 3, 0, 1},
103         {Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, 2, 1, 0}
104     };
105     
106 
107     private static Integer[][] shortestPaths1 = 
108     {
109     	null,
110         {new Integer(0)},
111         {new Integer(1), new Integer(6)},
112         {new Integer(1)},
113         {new Integer(1), new Integer(7)},
114         {new Integer(1), new Integer(9), new Integer(11)},
115         {new Integer(1), new Integer(9)},
116         null,
117         null,
118         null
119     };
120     
121     private Map<Graph<String,Integer>,Integer[]> edgeArrays;
122     
123     private Map<Integer,Number> edgeWeights;
124     
125     private Function<Integer,Number> nev;
126     
127     private Supplier<String> vertexFactoryDG =
128     	new Supplier<String>() {
129     	int count = 0;
130     	public String get() {
131     		return "V"+count++;
132     	}};
133     	private Supplier<String> vertexFactoryUG =
134     		new Supplier<String>() {
135     		int count = 0;
136     		public String get() {
137     			return "U"+count++;
138     		}};
139     		
140     BiMap<String,Integer> did;
141     BiMap<String,Integer> uid;
142 
143     @Override
144     protected void setUp() {
145     	edgeWeights = new HashMap<Integer,Number>();
146         nev = Functions.<Integer,Number>forMap(edgeWeights);
147 		dg = new DirectedSparseMultigraph<String,Integer>();
148 		for(int i=0; i<dg_distances.length; i++) {
149 			dg.addVertex(vertexFactoryDG.get());
150 		}
151 		did = Indexer.<String>create(dg.getVertices(), 1);
152         Integer[] dg_array = new Integer[edges.length];
153 		addEdges(dg, did, dg_array);
154         
155         ug = new UndirectedSparseMultigraph<String,Integer>();
156 		for(int i=0; i<ug_distances.length; i++) {
157 			ug.addVertex(vertexFactoryUG.get());
158 		}
159         uid = Indexer.<String>create(ug.getVertices(),1);
160 //        GraphUtils.addVertices(ug, ug_distances.length);
161 //        Indexer.newIndexer(ug, 1);
162         Integer[] ug_array = new Integer[edges.length];
163         addEdges(ug, uid, ug_array);
164         
165         edgeArrays = new HashMap<Graph<String,Integer>,Integer[]>();
166         edgeArrays.put(dg, dg_array);
167         edgeArrays.put(ug, ug_array);
168     }
169     
170     @Override
171     protected void tearDown() throws Exception {
172     }
173 
174     public void exceptionTest(Graph<String,Integer> g, BiMap<String,Integer> indexer, int index)
175     {
176         DijkstraShortestPath<String,Integer> dsp = 
177         	new DijkstraShortestPath<String,Integer>(g, nev);
178 //        Indexer id = Indexer.getIndexer(g);
179         String start = indexer.inverse().get(index);
180         Integer e = null;
181 
182         String v = "NOT IN GRAPH";
183         
184         try
185         {
186             dsp.getDistance(start, v);
187             fail("getDistance(): illegal destination vertex");
188         }
189         catch (IllegalArgumentException iae) {}
190         try
191         {
192             dsp.getDistance(v, start);
193             fail("getDistance(): illegal source vertex");
194         }
195         catch (IllegalArgumentException iae) {}
196         try
197         {
198             dsp.getDistanceMap(v, 1);
199             fail("getDistanceMap(): illegal source vertex");
200         }
201         catch (IllegalArgumentException iae) {}
202         try
203         {
204             dsp.getDistanceMap(start, 0);
205             fail("getDistanceMap(): too few vertices requested");
206         }
207         catch (IllegalArgumentException iae) {}
208         try
209         {
210             dsp.getDistanceMap(start, g.getVertexCount()+1);
211             fail("getDistanceMap(): too many vertices requested");
212         }
213         catch (IllegalArgumentException iae) {}
214 
215         try
216         {
217             dsp.getIncomingEdge(start, v);
218             fail("getIncomingEdge(): illegal destination vertex");
219         }
220         catch (IllegalArgumentException iae) {}
221         try
222         {
223             dsp.getIncomingEdge(v, start);
224             fail("getIncomingEdge(): illegal source vertex");
225         }
226         catch (IllegalArgumentException iae) {}
227         try
228         {
229             dsp.getIncomingEdgeMap(v, 1);
230             fail("getIncomingEdgeMap(): illegal source vertex");
231         }
232         catch (IllegalArgumentException iae) {}
233         try
234         {
235             dsp.getIncomingEdgeMap(start, 0);
236             fail("getIncomingEdgeMap(): too few vertices requested");
237         }
238         catch (IllegalArgumentException iae) {}
239         try
240         {
241             dsp.getDistanceMap(start, g.getVertexCount()+1);
242             fail("getIncomingEdgeMap(): too many vertices requested");
243         }
244         catch (IllegalArgumentException iae) {}
245 
246         try
247         {
248             // test negative edge weight exception
249             String v1 = indexer.inverse().get(1);
250             String v2 = indexer.inverse().get(7);
251             e = g.getEdgeCount()+1;
252          	g.addEdge(e, v1, v2);
253          	edgeWeights.put(e, -2);
254 //            e.addUserDatum("weight", new Double(-2), UserData.REMOVE);
255             dsp.reset();
256             dsp.getDistanceMap(start);
257 //            for (Iterator it = g.getEdges().iterator(); it.hasNext(); )
258 //            {
259 //                Edge edge = (Edge)it.next();
260 //                double weight = ((Number)edge.getUserDatum("weight")).doubleValue();
261 //                Pair p = edge.getEndpoints();
262 //                int i = id.getIndex((Vertex)p.getFirst());
263 //                int j = id.getIndex((Vertex)p.getSecond());
264 //                System.out.print("(" + i + "," + j + "): " + weight);
265 //                if (weight < 0)
266 //                    System.out.print(" *******");
267 //                System.out.println();
268 //            }
269             fail("DijkstraShortestPath should not accept negative edge weights");
270         }
271         catch (IllegalArgumentException iae) 
272         {
273             g.removeEdge(e);
274         }
275     }
276     
277     public void testDijkstra()
278     {
279         setUp();
280         exceptionTest(dg, did, 1);
281         
282         setUp();
283         exceptionTest(ug, uid, 1);
284         
285         setUp();
286         getPathTest(dg, did, 1);
287         
288         setUp();
289         getPathTest(ug, uid, 1);
290                 
291         for (int i = 1; i <= dg_distances.length; i++)
292         {
293             setUp();
294             weightedTest(dg, did, i, true);
295 
296             setUp();
297             weightedTest(dg, did, i, false);
298         }
299         
300         for (int i = 1; i <= ug_distances.length; i++)
301         {
302             setUp();
303             weightedTest(ug, uid, i, true);
304             
305             setUp();
306             weightedTest(ug, uid, i, false);
307         }
308         
309     }
310 
311     private void getPathTest(Graph<String,Integer> g, BiMap<String,Integer> indexer, int index)
312     {
313         DijkstraShortestPath<String,Integer> dsp = 
314         	new DijkstraShortestPath<String,Integer>(g, nev);
315 //        Indexer id = Indexer.getIndexer(g);
316         String start = indexer.inverse().get(index);
317         Integer[] edge_array = edgeArrays.get(g);
318         Integer[] incomingEdges1 = null;
319         if (g instanceof DirectedGraph)
320             incomingEdges1 = dg_incomingEdges[index-1];
321         if (g instanceof UndirectedGraph)
322             incomingEdges1 = ug_incomingEdges[index-1];
323         assertEquals(incomingEdges1.length, g.getVertexCount());
324         
325          // test getShortestPath(start, v)
326         dsp.reset();
327         for (int i = 1; i <= incomingEdges1.length; i++)
328         {
329           List<Integer> shortestPath = dsp.getPath(start, indexer.inverse().get(i));
330             Integer[] indices = shortestPaths1[i-1];
331             for (ListIterator<Integer> iter = shortestPath.listIterator(); iter.hasNext(); )
332             {
333                 int j = iter.nextIndex();
334                 Integer e = iter.next();
335                 if (e != null)
336                     assertEquals(edge_array[indices[j].intValue()], e);
337                 else
338                     assertNull(indices[j]);
339             }
340         }
341     }
342     
343     private void weightedTest(Graph<String,Integer> g, BiMap<String,Integer> indexer, int index, boolean cached) {
344 //        Indexer id = Indexer.getIndexer(g);
345         String start = indexer.inverse().get(index);
346         double[] distances1 = null;
347         Integer[] incomingEdges1 = null;
348         if (g instanceof DirectedGraph)
349         {
350             distances1 = dg_distances[index-1];
351             incomingEdges1 = dg_incomingEdges[index-1];
352         }
353         if (g instanceof UndirectedGraph)
354         {    
355             distances1 = ug_distances[index-1];
356             incomingEdges1 = ug_incomingEdges[index-1];
357         }
358         assertEquals(distances1.length, g.getVertexCount());
359         assertEquals(incomingEdges1.length, g.getVertexCount());
360         DijkstraShortestPath<String,Integer> dsp = 
361         	new DijkstraShortestPath<String,Integer>(g, nev, cached);
362         Integer[] edge_array = edgeArrays.get(g);
363         
364         // test getDistance(start, v)
365         for (int i = 1; i <= distances1.length; i++) {
366             String v = indexer.inverse().get(i);
367             Number n = dsp.getDistance(start, v);
368             double d = distances1[i-1];
369             double dist;
370             if (n == null)
371                 dist = Double.POSITIVE_INFINITY;
372             else
373                 dist = n.doubleValue();
374             
375             assertEquals(d, dist, .001);
376         }
377 
378         // test getIncomingEdge(start, v)
379         dsp.reset();
380         for (int i = 1; i <= incomingEdges1.length; i++)
381         {
382             String v = indexer.inverse().get(i);
383             Integer e = dsp.getIncomingEdge(start, v);
384             if (e != null)
385                 assertEquals(edge_array[incomingEdges1[i-1].intValue()], e);
386             else
387                 assertNull(incomingEdges1[i-1]);
388         }
389         
390         // test getDistanceMap(v)
391         dsp.reset();
392         Map<String,Number> distances = dsp.getDistanceMap(start);
393         assertTrue(distances.size() <= g.getVertexCount());
394         double d_prev = 0; // smallest possible distance
395         Set<String> reachable = new HashSet<String>();
396         for (Iterator<String> d_iter = distances.keySet().iterator(); d_iter.hasNext(); )
397         {
398             String cur = d_iter.next();
399             double d_cur = ((Double)distances.get(cur)).doubleValue();
400             assertTrue(d_cur >= d_prev);
401 
402             d_prev = d_cur;
403             int i = indexer.get(cur);
404             assertEquals(distances1[i-1], d_cur, .001);
405             reachable.add(cur);
406         }
407         // make sure that non-reachable vertices have no entries
408         for (Iterator<String> v_iter = g.getVertices().iterator(); v_iter.hasNext(); )
409         {
410             String v = v_iter.next();
411             assertEquals(reachable.contains(v), distances.keySet().contains(v));
412         }
413         
414         // test getIncomingEdgeMap(v)
415         dsp.reset();
416         Map<String,Integer> incomingEdgeMap = dsp.getIncomingEdgeMap(start);
417         assertTrue(incomingEdgeMap.size() <= g.getVertexCount());
418         for (Iterator<String> e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); )
419         {
420             String v = e_iter.next();
421             Integer e = incomingEdgeMap.get(v);
422             int i = indexer.get(v);
423 //            if (e != null)
424 //            {    
425 //                Pair endpoints = e.getEndpoints();
426 //                int j = id.getIndex((Vertex)endpoints.getFirst());
427 //                int k = id.getIndex((Vertex)endpoints.getSecond());
428 //                System.out.print(i + ": (" + j + "," + k + ");  ");
429 //            }
430 //            else
431 //                System.out.print(i + ": null;  ");
432             if (e != null)
433                 assertEquals(edge_array[incomingEdges1[i-1].intValue()], e);
434             else
435                 assertNull(incomingEdges1[i-1]);
436         }
437         
438         // test getDistanceMap(v, k)
439         dsp.reset();
440         for (int i = 1; i <= distances1.length; i++)
441         {
442             distances = dsp.getDistanceMap(start, i);
443             assertTrue(distances.size() <= i);
444             d_prev = 0; // smallest possible distance
445 
446             reachable.clear();
447             for (Iterator<String> d_iter = distances.keySet().iterator(); d_iter.hasNext(); )
448             {
449                 String cur = d_iter.next();
450                 double d_cur = ((Double)distances.get(cur)).doubleValue();
451                 assertTrue(d_cur >= d_prev);
452 
453                 d_prev = d_cur;
454                 int j = indexer.get(cur);
455 
456                 assertEquals(distances1[j-1], d_cur, .001);
457                 reachable.add(cur);
458             }
459             for (Iterator<String> v_iter = g.getVertices().iterator(); v_iter.hasNext(); )
460             {
461                 String v = v_iter.next();
462                 assertEquals(reachable.contains(v), distances.keySet().contains(v));
463             }
464         }
465                 
466         // test getIncomingEdgeMap(v, k)
467         dsp.reset();
468         for (int i = 1; i <= incomingEdges1.length; i++)
469         {
470             incomingEdgeMap = dsp.getIncomingEdgeMap(start, i);
471             assertTrue(incomingEdgeMap.size() <= i);
472             for (Iterator<String> e_iter = incomingEdgeMap.keySet().iterator(); e_iter.hasNext(); )
473             {
474                 String v = e_iter.next();
475                 Integer e = incomingEdgeMap.get(v);
476                 int j = indexer.get(v);
477                 if (e != null)
478                     assertEquals(edge_array[incomingEdges1[j-1].intValue()], e);
479                 else
480                     assertNull(incomingEdges1[j-1]);
481             }
482         }
483     }
484     
485     public void addEdges(Graph<String,Integer> g, BiMap<String,Integer> indexer, Integer[] edge_array)
486     {
487     	
488 //        Indexer id = Indexer.getIndexer(g);
489         for (int i = 0; i < edges.length; i++)
490         {
491             int[] edge = edges[i];
492             Integer e = i;
493             g.addEdge(i, indexer.inverse().get(edge[0]), indexer.inverse().get(edge[1]));
494             edge_array[i] = e;
495             if (edge.length > 2) {
496             	edgeWeights.put(e, edge[2]);
497 //                e.addUserDatum("weight", edge[2]);
498             }
499         }
500     }
501 
502     
503 //    private class UserDataEdgeWeight implements NumberEdgeValue
504 //    {
505 //        private Object ud_key;
506 //        
507 //        public UserDataEdgeWeight(Object key)
508 //        {
509 //            ud_key = key;
510 //        }
511 //        
512 //		/**
513 //		 * @see edu.uci.ics.jung.utils.NumberEdgeValue#getNumber(edu.uci.ics.jung.graph.ArchetypeEdge)
514 //		 */
515 //		public Number getNumber(ArchetypeEdge e)
516 //		{
517 //            return (Number)e.getUserDatum(ud_key);
518 //		}
519 //
520 //		/**
521 //		 * @see edu.uci.ics.jung.utils.NumberEdgeValue#setNumber(edu.uci.ics.jung.graph.ArchetypeEdge, java.lang.Number)
522 //		 */
523 //		public void setNumber(ArchetypeEdge e, Number n)
524 //		{
525 //            throw new UnsupportedOperationException();
526 //		}
527 //    }
528 }