View Javadoc

1   package cz.cuni.amis.pathfinding.alg.floydwarshall;
2   
3   import java.lang.ref.SoftReference;
4   import java.util.ArrayList;
5   import java.util.Collection;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.logging.Level;
11  import java.util.logging.Logger;
12  
13  import cz.cuni.amis.pathfinding.map.IPFKnownMap;
14  import cz.cuni.amis.pathfinding.map.IPFKnownMapView;
15  import cz.cuni.amis.pathfinding.map.IPFMap;
16  import cz.cuni.amis.pathfinding.map.IPFMapView;
17  import cz.cuni.amis.pathfinding.map.IPFMapView.DefaultView;
18  import cz.cuni.amis.utils.Iterators;
19  import cz.cuni.amis.utils.NullCheck;
20  
21  /**
22   * Floyd-Warshall algorithm for precomputing all-possible paths within the {@link IPFKnownMap}.
23   * <p><p>
24   * It precomputes all the paths inside the environment using Floyd-Warshall
25   * algorithm (time: O(n^3)) in the form of matrix.
26   * <p><p> 
27   * Use {@link FloydWarshall#isReachable(Object, Object)}, {@link FloydWarshall#getPathCost(Object, Object)}
28   * and {@link FloydWarshall#getPath(Object, Object)} to obtain information about computed paths.
29   * the info about the path.
30   * <p><p>
31   * {@link FloydWarshall#getPath(Object, Object)} is caching retrieved paths using {@link SoftReference}s.
32   * <p><p>
33   * If needed you may call {@link FloydWarshall#compute()} to recompute paths, this call is needed if you set new map / agent view
34   * using {@link FloydWarshall#setMap(IPFKnownMap)} or {@link FloydWarshall#setMapView(IPFMapView)}.
35   * <p><p>
36   * Based upon the implementation from Martin Krulis with his kind consent, thank you! Even though it was heavily recreated by now ;-)
37   * <p><p>
38   * NOTE: requires O(map.nodes.size^2) of memory! So be careful.
39   * <p><p>
40   * NOTE: you should read javadocs for {@link IPFMap}, {@link IPFKnownMap} and {@link IPFMapView} to understand how you can separate 
41   * your map representation from various agent "views on the map" (i.e., how to provide customized path finding 
42   * for different agents, e.g., fish vs. human)
43   * 
44   * @author Jimmy
45   */
46  public class FloydWarshall<NODE>  {
47  	
48  	/**
49  	 * Class describing cell inside the FloydWarshall matrix holding additional informations relating to the path between two
50  	 * nodes.
51  	 * <p><p>
52  	 * These nodes are stored under "indices" inside {@link FloydWarshall#pathMatrix}.
53  	 * 
54  	 * @author Jimmy
55  	 *
56  	 * @param <N>
57  	 */
58  	public static class PathMatrixNode<N> {
59  
60  		private int cost = Integer.MAX_VALUE;
61  		private Integer viaNode = null;
62  		private SoftReference<List<N>> path = null;
63  
64  		/**
65  		 * Doesn't leading anywhere
66  		 */
67  		public PathMatrixNode() {
68  		}
69  
70  		public PathMatrixNode(int cost) {
71  			this.cost = cost;
72  		}
73  		
74  		/**
75  		 * Returns aprox. number of bytes used by this class (for 32-bit Java, might be as twice as much for 64-bit!) including
76  		 * currently cached path.
77  		 * @return
78  		 */
79  		public int getBytesAprox() {
80  			List<N> path = (this.path != null ? this.path.get() : null);
81  			return 4 + 8 + (path == null ? 4 : 4 + path.size() * 4); 
82  		}
83  		
84  		/**
85  		 * Returns aprox. number of bytes used by this class (for 32-bit Java, might be as twice as much for 64-bit!) EXCLUDING
86  		 * currently cached path.
87  		 * 
88  		 * @return
89  		 */
90  		public int getBytesAproxWithoutPath() {
91  			List<N> path = (this.path != null ? this.path.get() : null);
92  			return 4 + 8 + 4; 
93  		}
94  
95  		/**
96  		 * Returns the cost of the path between nodes, if the path does not exist, returns {@link Integer#MAX_VALUE}.
97  		 * @return
98  		 */
99  		public int getPathCost() {
100 			return cost;
101 		}
102 
103 		/**
104 		 * Sets the cost of the path between nodes.
105 		 * @param cost
106 		 */
107 		public void setPathCost(int cost) {
108 			this.cost = cost;
109 		}
110 
111 		/**
112 		 * Returns the node you have to travel through.
113 		 * @return
114 		 */
115 		public Integer getViaNode() {
116 			return viaNode;
117 		}
118 
119 		/**
120 		 * Sets the node you have to travel through.
121 		 * @param indice
122 		 */
123 		public void setViaNode(Integer indice) {
124 			this.viaNode = indice;
125 		}
126 
127 		/**
128 		 * Returns the full path between nodes.
129 		 * <p><p>
130 		 * WARNING: this is cached path! Might return null even though such path exists! Use {@link FloydWarshall#getPath(Object, Object)}
131 		 * to obtain the result in every case.
132 		 * @return
133 		 */
134 		public List<N> getPath() {
135 			return path == null ? null : path.get();
136 		}
137 
138 		/**
139 		 * Sets the full path between nodes, computed as the last step of {@link FloydWarshall#performFloydWarshall(List)}. Such path
140 		 * is going to be stored using {@link SoftReference} (cached) and might be freed by GC if heap runs dry.
141 		 * @param path
142 		 */
143 		public void setPath(List<N> path) {
144 			this.path = new SoftReference<List<N>>(path);
145 		}
146 
147 	}
148 	
149 	/**
150 	 * Map used for the computation of paths.
151 	 */
152 	private IPFKnownMap<NODE> map;
153 
154 	/**
155 	 * Agent's custom view of the map.
156 	 */
157 	private IPFKnownMapView<NODE> view;
158 
159 	/**
160 	 * Logger used by this object.
161 	 */
162 	private Logger log = null;
163 	
164 	/**
165 	 * Map converting nodes to their corresponding indices inside {@link FloydWarshall#pathMatrix}.
166 	 */
167 	private Map<NODE, Integer> nodesIndices;
168 
169 	/**
170 	 * Mapping indices (inside {@link FloydWarshall#pathMatrix}) to nodes.
171 	 */
172 	private Map<Integer, NODE> indicesNodes;
173 
174 	/**
175 	 * FloydWarshall's matrix of distances & paths.
176 	 */
177 	private PathMatrixNode<NODE>[][] pathMatrix;
178 	
179 	// ===========
180 	// CONSTRUCTOR
181 	// ===========
182 
183 	/**
184 	 * FloydWarshall configured with "map" with no agent-specific view on the map, {@link DefaultView} is used.
185 	 * <p><p>
186 	 * {@link FloydWarshall#compute()} method is immediately called from within this constructor.
187 	 *  
188 	 * @param map
189 	 */
190 	public FloydWarshall(IPFKnownMap<NODE> map) {
191 		this.map = map;
192 		this.view = new IPFKnownMapView.DefaultView();
193 		NullCheck.check(this.map, "map");
194 		compute();
195 	}
196 	
197 	/**
198 	 * FloydWarshall configured with "map" and agent-specific view on the map, if "view" is null, {@link DefaultView} is going to be used.
199 	 * <p><p>
200 	 * {@link FloydWarshall#compute()} method is immediately called from within this constructor.
201 	 *  
202 	 * @param map
203 	 * @param view may be null
204 	 */
205 	public FloydWarshall(IPFKnownMap<NODE> map, IPFKnownMapView<NODE> view) {
206 		this.map = map;
207 		this.view = view;
208 		NullCheck.check(this.map, "map");
209 		if (this.view == null) {
210 			this.view = new IPFKnownMapView.DefaultView();
211 		}
212 		compute();
213 	}
214 	
215 	/**
216 	 * FloydWarshall configured with "map" with no agent-specific view on the map, {@link DefaultView} is used.
217 	 * <p><p>
218 	 * {@link FloydWarshall#compute()} method is immediately called from within this constructor.
219 	 *  
220 	 * @param map
221 	 * @param log may be null
222 	 */
223 	public FloydWarshall(IPFKnownMap<NODE> map, Logger log) {
224 		this.map = map;
225 		this.view = new IPFKnownMapView.DefaultView();
226 		NullCheck.check(this.map, "map");
227 		this.log = log;
228 		compute();
229 	}
230 	
231 	/**
232 	 * FloydWarshall configured with "map" and agent-specific view on the map, if "view" is null, {@link DefaultView} is going to be used.
233 	 * <p><p>
234 	 * {@link FloydWarshall#compute()} method is immediately called from within this constructor.
235 	 *  
236 	 * @param map
237 	 * @param view may be null
238 	 * @param log may be null
239 	 */
240 	public FloydWarshall(IPFKnownMap<NODE> map, IPFKnownMapView<NODE> view, Logger log) {
241 		this.map = map;
242 		this.view = view;
243 		NullCheck.check(this.map, "map");
244 		this.log = log;
245 		if (this.view == null) {
246 			if (log != null && log.isLoggable(Level.WARNING)) log.warning("No view specified! IPFKnownMapView.DefaultView is going to be used.");
247 			this.view = new IPFKnownMapView.DefaultView();
248 		}		
249 		compute();
250 	}
251 	
252 	/**
253 	 * Returns logger used by this object.
254 	 * @return
255 	 */
256 	public Logger getLog() {
257 		return log;
258 	}
259 
260 	/**
261 	 * Sets logger used by this object.
262 	 * @param log
263 	 */
264 	public void setLog(Logger log) {
265 		this.log = log;
266 	}
267 
268 	/**
269 	 * Map abstraction the FloydWarshall is working with.
270 	 * @return
271 	 */
272 	public IPFKnownMap<NODE> getMap() {
273 		return map;
274 	}
275 
276 	/**
277 	 * Sets map abstraction into the FloydWarshall.
278 	 * @param map
279 	 */
280 	public synchronized void setMap(IPFKnownMap<NODE> map) {
281 		this.map = map;
282 	}
283 
284 	/**
285 	 * Returns agent-specific map view for the map.
286 	 * @return
287 	 */
288 	public IPFKnownMapView<NODE> getMapView() {
289 		return view;
290 	}
291 
292 	/**
293 	 * Sets agent-specific map view for the map. 
294 	 * @param mapView
295 	 */
296 	public synchronized void setMapView(IPFKnownMapView<NODE> mapView) {
297 		this.view = mapView;
298 	}
299 	
300 	/**
301 	 * Force FloydWarshall to refresh its path matrix, useful if you modify the map or view using {@link FloydWarshall#setMap(IPFKnownMap)}
302 	 * or {@link FloydWarshall#setMapView(IPFKnownMapView)}.
303 	 * <p><p>
304 	 * Called automatically from constructors!
305 	 */
306 	public synchronized void compute() {
307 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Computing paths...");
308 		
309 		List<NODE> nodes = new ArrayList<NODE>();
310 		
311 		Collection<NODE> mapNodes = map.getNodes();
312 		if (mapNodes != null) {
313 			Iterator<NODE> iter = mapNodes.iterator();
314 			while (iter.hasNext()) {
315 				NODE node = iter.next();
316 				if (view.isNodeOpened(node)) {
317 					// we can use it
318 					nodes.add(node);
319 				}
320 			}
321 		}
322 		
323 		Collection<NODE> extraNodes = view.getExtraNodes(mapNodes);
324 		if (extraNodes != null) {
325 			Iterator<NODE> iter = extraNodes.iterator();
326 			while (iter.hasNext()) {
327 				NODE node = iter.next();
328 				if (view.isNodeOpened(node)) {
329 					// we can use it
330 					nodes.add(node);
331 				}
332 			}
333 		}
334 		
335 		performFloydWarshall(nodes);
336 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Paths computed!");
337 	}
338 	
339 	public synchronized void cacheAllPaths() {
340 		int size = pathMatrix.length;
341 		// Construct paths + log unreachable paths.		
342 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Caching all paths O(" + size + "^3)...");
343 		long time = System.currentTimeMillis();
344 		int count = 0;
345 		for (int i = 0; i < size; i++) {
346 			for (int j = 0; j < size; j++) {
347 				if (pathMatrix[i][j].getPathCost() == Integer.MAX_VALUE) {
348 					// WE'RE PURPOSEFULLY TESTING "FINER" LEVEL HERE!
349 					if (log != null && log.isLoggable(Level.FINER)) log.warning("Unreachable path from " + indicesNodes.get(i) + " -> " + indicesNodes.get(j));
350 					count++;
351 				} else {
352 					// path exists ... retrieve it
353 					pathMatrix[i][j].setPath(retrievePath(i, j));
354 				}
355 			}
356 		}
357 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Paths cached (" + (System.currentTimeMillis() - time) + " ms).");
358 	}
359 	
360 	//////////////////////////////////////
361 	// FloydWarshall algorithms & variable 
362 	//////////////////////////////////////
363 
364 	
365 	/**
366 	 * Perform FloydWarshall over the list of nodes initializing {@link FloydWarshall#nodesIndices}, {@link FloydWarshall#pathMatrix}.
367 	 * @param nodes
368 	 */
369 	@SuppressWarnings("unchecked")
370 	private void performFloydWarshall(List<NODE> nodes) {
371 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Running Floyd-Warshall algorithm...");
372 		long start = System.currentTimeMillis();
373 
374 		// prepares data structures
375 		int size = nodes.size();
376 		nodesIndices = new HashMap<NODE, Integer>(size);
377 		indicesNodes = new HashMap<Integer, NODE>(size);
378 		pathMatrix = new PathMatrixNode[size][size];
379 
380 		// Initialize navPoint indices mapping.
381 		for (int i = 0; i < nodes.size(); ++i) {
382 			nodesIndices.put(nodes.get(i), i);
383 			indicesNodes.put(i, nodes.get(i));
384 		}
385 
386 		// Initialize distance matrix.
387 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Initializing matrix...");
388 		for (int i = 0; i < size; i++) {
389 			for (int j = 0; j < size; j++) {
390 				pathMatrix[i][j] = new PathMatrixNode<NODE>((i == j) ? 0 : Integer.MAX_VALUE);
391 			}
392 		}
393 
394 		// Set initial arc costs into distance matrix.
395 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Setting initial arc costs...");
396 		for (int i = 0; i < size; i++) {
397 			NODE node1 = nodes.get(i);
398 			if (!view.isNodeOpened(node1)) continue; // forbidden node
399 			
400 			Collection<NODE> neighbors = map.getNeighbors(node1);
401 			Collection<NODE> extraNeighbors = view.getExtraNeighbors(node1, neighbors);
402 			Iterator<NODE> iterator = 
403 				new Iterators<NODE>(
404 					neighbors == null ? null : neighbors.iterator(), 
405 					extraNeighbors == null ? null : extraNeighbors.iterator()
406 				);
407 			
408 			while(iterator.hasNext()) {
409 				NODE node2 = iterator.next();
410 				if (!view.isNodeOpened(node2)) continue;       // forbidden node
411 				if (!view.isArcOpened(node1, node2)) continue; // forbidden arc
412 				int j = nodesIndices.get(node2);
413 				
414 				int arcCost = map.getArcCost(node1, node2);
415 				arcCost += view.getArcExtraCost(node1, node2, arcCost);
416 				int nodeCost = map.getNodeCost(node2);
417 				nodeCost += view.getNodeExtraCost(node2, nodeCost);
418 				
419 				pathMatrix[i][j].setPathCost(arcCost+nodeCost);
420 			}
421 		}
422 
423 		// Perform Floyd-Warshall.
424 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Computing path-matrix O(" + size + "^3)...");
425 		for (int k = 0; k < size; k++) {
426 			for (int i = 0; i < size; i++) {
427 				NODE node1 = nodes.get(i);
428 				NODE node2 = nodes.get(k);
429 				for (int j = 0; j < size; j++) {
430 					NODE node3 = nodes.get(j);
431 					int newLen =
432 						pathMatrix[i][k].getPathCost() == Integer.MAX_VALUE ?
433 								Integer.MAX_VALUE
434 							:	(pathMatrix[k][j].getPathCost() == Integer.MAX_VALUE) ? 
435 									Integer.MAX_VALUE
436 								:	pathMatrix[i][k].getPathCost() + pathMatrix[k][j].getPathCost();
437 					if (pathMatrix[i][j].getPathCost() > newLen) {
438 						pathMatrix[i][j].setPathCost(newLen);
439 						pathMatrix[i][j].setViaNode(k);
440 					}
441 				}
442 			}
443 		}
444 
445 		// Check reachability...
446 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Checking reachability...");
447 		int count = 0;
448 		for (int i = 0; i < size; i++) {
449 			for (int j = 0; j < size; j++) {
450 				if (pathMatrix[i][j].getPathCost() == Integer.MAX_VALUE) {
451 					// WE'RE PURPOSEFULLY TESTING "FINER" LEVEL HERE!
452 					if (log != null && log.isLoggable(Level.FINER)) log.warning("Unreachable path from " + nodes.get(i) + " -> " + nodes.get(j));
453 					count++;
454 				}
455 			}
456 		}
457 
458 		if (count > 0) {
459 			if (log != null && log.isLoggable(Level.WARNING)) log.warning(this + ": There are " + count + " unreachable nodes pairs (if you wish to see their list, set logging to Level.FINER).");
460 		} else {
461 			if (log != null && log.isLoggable(Level.INFO)) log.info(this + ": All nodes are connected, there are NO unreachable pairs of nodes.");
462 		}
463 
464 		if (log != null && log.isLoggable(Level.INFO)) log.info(this + ": Computation for " + size + " navpoints took " + (System.currentTimeMillis() - start) + " millis.");
465 		
466 		if (log != null && log.isLoggable(Level.INFO)) {		
467 			log.info(this + ": Memory consumption (no paths cached) is aprox. " + getAproxMemory() + " bytes, might be as twice as much for 64-bit system.");
468 		}
469 				
470 		if (log != null && log.isLoggable(Level.FINE)) log.fine(this + ": Floyd-Warshall algorithm finished!");
471 	}
472 
473 	/**
474 	 * Returns approximation of memory consumption of this object in bytes for 32-bit JVM, might be as twice for 64-bit JVM!
475 	 * @return
476 	 */
477 	public long getAproxMemory() {
478 		long bytes = 0;
479 		for (int i = 0; i < pathMatrix.length; ++i) {
480 			for (int j = 0; j < pathMatrix.length; ++j) {
481 				bytes += pathMatrix[i][j].getBytesAprox();
482 			}
483 		}
484 		bytes += pathMatrix.length * 8 * 2; 
485 		return bytes;
486 	}
487 
488 	/**
489 	 * Sub-routine of {@link FloydWarshall#retrievePath(Integer, Integer)} - do not use! ... Well you may, it returns
490 	 * path without 'from', 'to' or null if such path dosn't exist.
491 	 * <p><p>
492 	 * DO NOT USE OUTSIDE {@link FloydWarshall#performFloydWarshall(List)} (relies on indicesNavPoints).
493 	 * <p><p>
494 	 * Uses recursion.
495 	 * 
496 	 * @param from
497 	 * @param to
498 	 * @return
499 	 */
500 	private List<NODE> retrievePathInner(Integer from, Integer to) {
501 		PathMatrixNode node = pathMatrix[from][to];
502 		if (node.getPathCost() == Integer.MAX_VALUE)
503 			return null;
504 		if (node.getViaNode() == null) {
505 			return new ArrayList<NODE>(0);
506 		}
507 		if (node.getViaNode() == null)
508 			return new ArrayList<NODE>(0);
509 
510 		List<NODE> path = new ArrayList<NODE>();
511 		path.addAll(retrievePathInner(from, node.getViaNode()));
512 		path.add(indicesNodes.get(node.getViaNode()));
513 		path.addAll(retrievePathInner(node.getViaNode(), to));
514 
515 		return path;
516 	}
517 
518 	/**
519 	 * Returns path between from-to or null if path doesn't exist. Path begins
520 	 * with 'from' and ends with 'to'.
521 	 * <p><p>
522 	 * DO NOT USE OUTSIDE CONSTRUCTOR (relies on indicesNavPoints).
523 	 * 
524 	 * @param from
525 	 * @param to
526 	 * @return
527 	 */
528 	private List<NODE> retrievePath(Integer from, Integer to) {
529 		List<NODE> path = new ArrayList<NODE>();
530 		path.add(indicesNodes.get(from));
531 		path.addAll(retrievePathInner(from, to));
532 		path.add(indicesNodes.get(to));
533 		return path;
534 	}
535 
536 	/**
537 	 * Returns {@link PathMatrixNode<NODE>} describing path from "nodeFrom" to "nodeTo". 
538 	 * @param nodeFrom
539 	 * @param nodeTo
540 	 * @return
541 	 */
542 	public PathMatrixNode<NODE> getPathMatrixNode(NODE nodeFrom, NODE nodeTo) {
543 		Integer from = nodesIndices.get(nodeFrom);
544 		Integer to = nodesIndices.get(nodeTo);
545 		if (from == null || to == null) return null;
546 		return pathMatrix[from][to];
547 	}
548 
549 	/**
550 	 * Whether node 'to' is reachable (path exists) from the node 'from'.
551 	 * 
552 	 * @param from
553 	 * @param to
554 	 * @return
555 	 */
556 	public boolean isReachable(NODE from, NODE to) {
557 		if ((from == null) || (to == null)) return false;
558 		PathMatrixNode matrixNode = getPathMatrixNode(from, to);
559 		if (matrixNode == null) return false;
560 		return matrixNode.getPathCost() != Integer.MAX_VALUE;
561 	}
562 
563 	/**
564 	 * Calculate's distance between two nav points (using pathfinding).
565 	 * <p><p>
566 	 * Throws exception if object is disabled, see {@link FloydWarshallMap#setEnabled(boolean)}. Note that the object
567 	 * is enabled by default.
568 	 * 
569 	 * @return Distance or {@link Integer#MAX_VALUE} if there's no path.
570 	 */
571 	public int getPathCost(NODE from, NODE to) {
572 		if ((from == null) || (to == null))
573 			return Integer.MAX_VALUE;
574 		PathMatrixNode matrixNode = getPathMatrixNode(from, to);
575 		if (matrixNode == null) return Integer.MAX_VALUE;
576 		return matrixNode.getPathCost();
577 	}
578 
579 	/**
580 	 * Returns path between navpoints 'from' -> 'to'. The path begins with
581 	 * 'from' and ends with 'to'. If such path doesn't exist, returns null.
582 	 * <p><p>
583 	 * Path is automatically cached using {@link SoftReference}.
584 	 * 
585 	 * @param from
586 	 * @param to
587 	 * @return
588 	 */
589 	public List<NODE> getPath(NODE from, NODE to) {
590 		if ((from == null) || (to == null))
591 			return null;
592 		if (log != null && log.isLoggable(Level.FINE)) log.fine("Retrieving path: " + from + " -> " + to);
593 		PathMatrixNode matrixNode = getPathMatrixNode(from, to);
594 		if (matrixNode == null) return null;
595 		if (matrixNode.getPathCost() == Integer.MAX_VALUE) return null;
596 		List<NODE> path = matrixNode.getPath();
597 		if (path == null) {
598 			// was not cached or JVM has GC()ed it
599 			path = retrievePathInner(nodesIndices.get(from), nodesIndices.get(to));
600 			// cache the path
601 			matrixNode.setPath(path);
602 		}
603 		if (log != null && log.isLoggable(Level.FINE)) log.fine("Path " + from + " -> " + to + " exists, " + path.size() + " nodes long.");
604 		return path;
605 	}
606 	
607 	/**
608 	 * Returns matrix of nodes as computed by FloydWarshall algorithm. You should not alter it by hand!
609 	 * @return
610 	 */
611 	public PathMatrixNode<NODE>[][] getMatrix() {
612 		return pathMatrix;
613 	}
614 	
615 	/**
616 	 * Returns index of the node inside {@link FloydWarshall#getMatrix()}. Note that if node that is not inside the matrix is passed,
617 	 * this will return null! 
618 	 * @param node
619 	 * @return
620 	 */
621 	public Integer getNodeIndex(NODE node) {
622 		return nodesIndices.get(node);
623 	}
624 
625 	@Override
626 	public String toString() {
627 		return "FloydWarshall";
628 	}
629 
630 }