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
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46 public class FloydWarshall<NODE> {
47
48
49
50
51
52
53
54
55
56
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
66
67 public PathMatrixNode() {
68 }
69
70 public PathMatrixNode(int cost) {
71 this.cost = cost;
72 }
73
74
75
76
77
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
86
87
88
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
97
98
99 public int getPathCost() {
100 return cost;
101 }
102
103
104
105
106
107 public void setPathCost(int cost) {
108 this.cost = cost;
109 }
110
111
112
113
114
115 public Integer getViaNode() {
116 return viaNode;
117 }
118
119
120
121
122
123 public void setViaNode(Integer indice) {
124 this.viaNode = indice;
125 }
126
127
128
129
130
131
132
133
134 public List<N> getPath() {
135 return path == null ? null : path.get();
136 }
137
138
139
140
141
142
143 public void setPath(List<N> path) {
144 this.path = new SoftReference<List<N>>(path);
145 }
146
147 }
148
149
150
151
152 private IPFKnownMap<NODE> map;
153
154
155
156
157 private IPFKnownMapView<NODE> view;
158
159
160
161
162 private Logger log = null;
163
164
165
166
167 private Map<NODE, Integer> nodesIndices;
168
169
170
171
172 private Map<Integer, NODE> indicesNodes;
173
174
175
176
177 private PathMatrixNode<NODE>[][] pathMatrix;
178
179
180
181
182
183
184
185
186
187
188
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
199
200
201
202
203
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
217
218
219
220
221
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
233
234
235
236
237
238
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
254
255
256 public Logger getLog() {
257 return log;
258 }
259
260
261
262
263
264 public void setLog(Logger log) {
265 this.log = log;
266 }
267
268
269
270
271
272 public IPFKnownMap<NODE> getMap() {
273 return map;
274 }
275
276
277
278
279
280 public synchronized void setMap(IPFKnownMap<NODE> map) {
281 this.map = map;
282 }
283
284
285
286
287
288 public IPFKnownMapView<NODE> getMapView() {
289 return view;
290 }
291
292
293
294
295
296 public synchronized void setMapView(IPFKnownMapView<NODE> mapView) {
297 this.view = mapView;
298 }
299
300
301
302
303
304
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
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
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
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
349 if (log != null && log.isLoggable(Level.FINER)) log.warning("Unreachable path from " + indicesNodes.get(i) + " -> " + indicesNodes.get(j));
350 count++;
351 } else {
352
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
362
363
364
365
366
367
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
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
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
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
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;
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;
411 if (!view.isArcOpened(node1, node2)) continue;
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
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
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
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
475
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
490
491
492
493
494
495
496
497
498
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
520
521
522
523
524
525
526
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
538
539
540
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
551
552
553
554
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
565
566
567
568
569
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
581
582
583
584
585
586
587
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
599 path = retrievePathInner(nodesIndices.get(from), nodesIndices.get(to));
600
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
609
610
611 public PathMatrixNode<NODE>[][] getMatrix() {
612 return pathMatrix;
613 }
614
615
616
617
618
619
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 }