MATSIM
Dijkstra.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * Dijkstra.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2007 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.router;
22 
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.List;
26 import java.util.Set;
27 
28 import org.apache.logging.log4j.LogManager;
29 import org.apache.logging.log4j.Logger;
30 import org.matsim.api.core.v01.Id;
42 import org.matsim.vehicles.Vehicle;
43 
84  public class Dijkstra implements LeastCostPathCalculator {
85  // yyyyyy I don't think that we should make this class publicly inheritable; as we know, will eventually lead
86  // to problems. kai, feb'18
87 
88  private final static Logger log = LogManager.getLogger(Dijkstra.class);
89 
93  protected Network network;
94 
98  protected final TravelDisutility costFunction;
99 
103  protected final TravelTime timeFunction;
104 
105  final HashMap<Id<Node>, DijkstraNodeData> nodeData;
106 
112  private int iterationID = Integer.MIN_VALUE + 1;
113 
119 
124  /*package*/ final boolean pruneDeadEnds;
125 
126 
128 
130 
131  private String[] modeRestriction = null;
132 
133  /*package*/ Person person = null;
134  /*package*/ Vehicle vehicle = null;
135 
146  // please use DijkstraFactory when you want to create an instance of this
147  protected Dijkstra(final Network network, final TravelDisutility costFunction, final TravelTime timeFunction) {
148  this(network, costFunction, timeFunction, null);
149  }
150 
163  // please use DijkstraFactory when you want to create an instance of this
164  protected Dijkstra(final Network network, final TravelDisutility costFunction, final TravelTime timeFunction,
165  final PreProcessDijkstra preProcessData) {
166 
167  this.network = network;
168  this.costFunction = costFunction;
169  this.timeFunction = timeFunction;
170  this.preProcessData = preProcessData;
171 
172  this.nodeData = new HashMap<>((int)(network.getNodes().size() * 1.1), 0.95f);
173 
174  if (preProcessData != null) {
175  if (!preProcessData.containsData()) {
176  this.pruneDeadEnds = false;
177  log.warn("The preprocessing data provided to router class Dijkstra contains no data! Please execute its run(...) method first!");
178  log.warn("Running without dead-end pruning.");
179  } else {
180  this.pruneDeadEnds = true;
181  }
182  } else {
183  this.pruneDeadEnds = false;
184  }
185  }
186 
190  @Deprecated
191  public void setModeRestriction(final Set<String> modeRestriction) {
192  if (modeRestriction == null) {
193  this.modeRestriction = null;
194  } else {
195  this.modeRestriction = modeRestriction.toArray(new String[modeRestriction.size()]);
196  }
197  }
198 
211  @Override
212  public Path calcLeastCostPath(final Node fromNode, final Node toNode, final double startTime, final Person person2, final Vehicle vehicle2) {
213 
214  /*
215  * Ensure that the given nodes are part of the network used by the router. Otherwise, the router would
216  * route within the network of the nodes and NOT the one provided when it was created. Previously, this
217  * caused problems when sub-networks where used.
218  * cdobler, jun'14
219  */
220  checkNodeBelongToNetwork(fromNode);
221  checkNodeBelongToNetwork(toNode);
222 
223  augmentIterationId(); // this call makes the class not thread-safe
224  this.person = person2;
225  this.vehicle = vehicle2;
226 
227  if (this.pruneDeadEnds) {
228  this.deadEndEntryNode = getPreProcessData(toNode).getDeadEndEntryNode();
229  }
230 
231  RouterPriorityQueue<Node> pendingNodes = (RouterPriorityQueue<Node>) createRouterPriorityQueue();
232  initFromNode(fromNode, toNode, startTime, pendingNodes);
233 
234  Node foundToNode = searchLogic(fromNode, toNode, pendingNodes, person2, vehicle2 );
235 
236  if (foundToNode == null) return null;
237  else {
238  DijkstraNodeData outData = getData(foundToNode);
239  double arrivalTime = outData.getTime();
240 
241  // now construct and return the path
242  return constructPath(fromNode, foundToNode, startTime, arrivalTime);
243  }
244  }
245 
246  /*
247  * Move this code to a separate method since it needs to be extended by the MultiNodeDijkstra.
248  * cdobler, jun'14
249  */
250  /*package*/ void checkNodeBelongToNetwork(Node node) {
251  if (this.network.getNodes().get(node.getId()) != node) {
252  throw new IllegalArgumentException("The nodes passed as parameters are not part of the network stored by "+
253  getClass().getSimpleName() + ": the validity of the results cannot be guaranteed. Aborting!");
254  }
255  }
256 
260  /*package*/ RouterPriorityQueue<? extends Node> createRouterPriorityQueue() {
261  /*
262  * The (Wrapped)BinaryMinHeap replaces the PseudoRemovePriorityQueue as default
263  * PriorityQueue. It offers a decreaseKey method and uses less memory. As a result,
264  * the routing performance is increased by ~20%.
265  *
266  * When an ArrayRoutingNetwork is used, an BinaryMinHeap can be used which uses
267  * the getArrayIndex() method of the ArrayRoutingNetworkNodes which further reduces
268  * the memory consumption and increases the performance by another ~10%.
269  */
270  /*
271  * Create a WrappedBinaryMinHeap and add all nodes initially once in the same order as
272  * they are in the network. Internally, the heap assigns all elements an index. By adding
273  * all elements initially, we ensure that the indices are in the same order as the nodes
274  * are in the network. In case the router finds two connections with the same costs,
275  * the selected connection depends on the indices.
276  * Moreover, re-use the heap instead of creating it from scratch for each calculated route.
277  * According to findings from the FastDijkstra, this should be faster.
278  * cdobler, sep'17
279  */
280  if (this.heap == null) {
281  this.heap = new WrappedBinaryMinHeap<>(this.network.getNodes().size());
282  for (Node node : this.network.getNodes().values()) this.heap.add(node, 0.);
283  }
284  this.heap.reset();
285  return this.heap;
286 // return new WrappedBinaryMinHeap<>(this.network.getNodes().size());
287 // return new PseudoRemovePriorityQueue<>(this.network.getNodes().size());
288  }
289 
296  /*package*/ Node searchLogic( final Node fromNode, final Node toNode, final RouterPriorityQueue<Node> pendingNodes, Person person, Vehicle vehicle ) {
297  // It is a bit overkill to pass Person and Vehicle. However, since the method is package-private, I think that it is acceptable. For
298  // a more public method, presumably only an "info" string should be passed. kai, oct'21
299 
300  boolean stillSearching = true;
301 
302  while (stillSearching) {
303  Node outNode = pendingNodes.poll();
304 
305  if (outNode == null) {
306  log.warn("No route was found from node " + fromNode.getId() + " to node " + toNode.getId() + ". " + createInfoMessage( person, vehicle ) + "Some possible reasons:" );
307  log.warn(" * Network is not connected. Run NetworkCleaner().") ;
308  log.warn(" * Network for considered mode does not even exist. Modes need to be entered for each link in network.xml.");
309  log.warn(" * Network for considered mode is not connected to starting or ending point of route. Setting insertingAccessEgressWalk to true may help.");
310  log.warn("This will now return null, but it may fail later with a null pointer exception.");
311  return null;
312  }
313 
314  if (outNode == toNode) {
315  stillSearching = false;
316  } else {
317  relaxNode(outNode, toNode, pendingNodes);
318  }
319  }
320  return toNode;
321  }
322  static StringBuilder createInfoMessage( Person person, Vehicle vehicle ){
323  StringBuilder strb = new StringBuilder();
324  boolean flag = false ;
325  if ( person != null ) {
326  strb.append( person.getId() );
327  flag = true ;
328  }
329  if ( vehicle !=null ) {
330  strb.append( vehicle.getId() );
331  flag = true;
332  }
333  if ( flag ) {
334  strb.append( ". " );
335  }
336  return strb;
337  }
338 
349  protected Path constructPath(Node fromNode, Node toNode, double startTime, double arrivalTime) {
350  List<Node> nodes = new ArrayList<>();
351  List<Link> links = new ArrayList<>();
352 
353  nodes.add(0, toNode);
354  Link tmpLink = getData(toNode).getPrevLink();
355  if (tmpLink != null) {
356  while (tmpLink.getFromNode() != fromNode) {
357  links.add(0, tmpLink);
358  nodes.add(0, tmpLink.getFromNode());
359  tmpLink = getData(tmpLink.getFromNode()).getPrevLink();
360  }
361  links.add(0, tmpLink);
362  nodes.add(0, tmpLink.getFromNode());
363 
364  }
365 
366  DijkstraNodeData toNodeData = getData(toNode);
367  Path path = new Path(nodes, links, arrivalTime - startTime, toNodeData.getCost());
368  return path;
369  }
370 
383  /*package*/ void initFromNode(final Node fromNode, final Node toNode, final double startTime,
384  final RouterPriorityQueue<Node> pendingNodes) {
385  DijkstraNodeData data = getData(fromNode);
386  visitNode(fromNode, data, pendingNodes, startTime, 0, null);
387  }
388 
400  protected void relaxNode(final Node outNode, final Node toNode, final RouterPriorityQueue<Node> pendingNodes) {
401 
402  DijkstraNodeData outData = getData(outNode);
403  double currTime = outData.getTime();
404  double currCost = outData.getCost();
405  if (this.pruneDeadEnds) {
406  PreProcessDijkstra.DeadEndData ddOutData = getPreProcessData(outNode);
407 
408  for (Link l : outNode.getOutLinks().values()) {
409  relaxNodeLogic(l, pendingNodes, currTime, currCost, toNode, ddOutData);
410  }
411  } else { // this.pruneDeadEnds == false
412  for (Link l : outNode.getOutLinks().values()) {
413  relaxNodeLogic(l, pendingNodes, currTime, currCost, toNode, null);
414  }
415  }
416  }
417 
422  /*package*/ void relaxNodeLogic(final Link l, final RouterPriorityQueue<Node> pendingNodes, final double currTime,
423  final double currCost, final Node toNode, final PreProcessDijkstra.DeadEndData ddOutData) {
424  if (this.pruneDeadEnds) {
425  if (canPassLink(l)) {
426  Node n = l.getToNode();
428 
429  /* IF the current node n is not in a dead end
430  * OR it is in the same dead end as the fromNode
431  * OR it is in the same dead end as the toNode
432  * THEN we add the current node to the pending nodes */
433  if ((ddData.getDeadEndEntryNode() == null)
434  || (ddOutData.getDeadEndEntryNode() != null)
435  || ((this.deadEndEntryNode != null)
436  && (this.deadEndEntryNode.getId() == ddData.getDeadEndEntryNode().getId()))) {
437 
438  addToPendingNodes(l, n, pendingNodes, currTime, currCost, toNode);
439  }
440  }
441  } else {
442  if (canPassLink(l)) {
443  addToPendingNodes(l, l.getToNode(), pendingNodes, currTime, currCost, toNode);
444  }
445  }
446  }
447 
467  protected boolean addToPendingNodes(final Link l, final Node n,
468  final RouterPriorityQueue<Node> pendingNodes, final double currTime,
469  final double currCost, final Node toNode) {
470 
471  final double travelTime = this.timeFunction.getLinkTravelTime(l, currTime, this.person, this.vehicle);
472  final double travelCost = this.costFunction.getLinkTravelDisutility(l, currTime, this.person, this.vehicle);
473  final DijkstraNodeData data = getData(n);
474  if (!data.isVisited(getIterationId())) {
475  visitNode(n, data, pendingNodes, currTime + travelTime, currCost + travelCost, l);
476  return true;
477  }
478 
479  final double nCost = data.getCost();
480  final double totalCost = currCost + travelCost;
481  if (totalCost < nCost) {
482  revisitNode(n, data, pendingNodes, currTime + travelTime, totalCost, l);
483  return true;
484  } else if (totalCost == nCost) {
485  // Special case: a node can be reached from two links with exactly the same costs.
486  // Decide based on the linkId which one to take... just have to common criteria to be deterministic.
487  Link prevLink = data.getPrevLink();
488  if (prevLink != null && prevLink.getId().compareTo(l.getId()) > 0) {
489  revisitNode(n, data, pendingNodes, currTime + travelTime, totalCost, l);
490  return true;
491  }
492  }
493  return false;
494  }
495 
501  protected boolean canPassLink(final Link link) {
502  if (this.modeRestriction == null) {
503  return true;
504  }
505  for (String mode : this.modeRestriction) {
506  if (link.getAllowedModes().contains(mode)) {
507  return true;
508  }
509  }
510  return false;
511  }
512 
530  protected void revisitNode(final Node n, final DijkstraNodeData data, final RouterPriorityQueue<Node> pendingNodes,
531  final double time, final double cost, final Link outLink) {
532  data.visit(outLink, cost, time, getIterationId());
533  pendingNodes.decreaseKey(n, getPriority(data));
534  }
535 
553  protected void visitNode(final Node n, final DijkstraNodeData data, final RouterPriorityQueue<Node> pendingNodes,
554  final double time, final double cost, final Link outLink) {
555  data.visit(outLink, cost, time, getIterationId());
556  pendingNodes.add(n, getPriority(data));
557  }
558 
563  protected void augmentIterationId() {
564  if (getIterationId() == Integer.MAX_VALUE) {
565  this.iterationID = Integer.MIN_VALUE + 1;
567  } else {
568  this.iterationID++;
569  }
570  }
571 
575  protected int getIterationId() {
576  return this.iterationID;
577  }
578 
582  private void resetNetworkVisited() {
583  for (Node node : this.network.getNodes().values()) {
584  DijkstraNodeData data = getData(node);
585  data.resetVisited();
586  }
587  }
588 
594  protected double getPriority(final DijkstraNodeData data) {
595  return data.getCost();
596  }
597 
606  protected DijkstraNodeData getData(final Node n) {
607  DijkstraNodeData r = this.nodeData.get(n.getId());
608  if (null == r) {
609  r = createNodeData();
610  this.nodeData.put(n.getId(), r);
611  }
612  return r;
613  }
614 
616  return new DijkstraNodeData();
617  }
618 
620  return this.preProcessData.getNodeData(n);
621  }
622 
623  protected final Person getPerson() {
624  return this.person;
625  }
626 
627  protected final Vehicle getVehicle() {
628  return this.vehicle;
629  }
630 }
void visitNode(final Node n, final DijkstraNodeData data, final RouterPriorityQueue< Node > pendingNodes, final double time, final double cost, final Link outLink)
Definition: Dijkstra.java:553
Map< Id< Node >, ? extends Node > getNodes()
DijkstraNodeData getData(final Node n)
Definition: Dijkstra.java:606
double getPriority(final DijkstraNodeData data)
Definition: Dijkstra.java:594
boolean canPassLink(final Link link)
Definition: Dijkstra.java:501
boolean addToPendingNodes(final Link l, final Node n, final RouterPriorityQueue< Node > pendingNodes, final double currTime, final double currCost, final Node toNode)
Definition: Dijkstra.java:467
RouterPriorityQueue< Node > heap
Definition: Dijkstra.java:129
Path constructPath(Node fromNode, Node toNode, double startTime, double arrivalTime)
Definition: Dijkstra.java:349
double getLinkTravelTime(Link link, double time, Person person, Vehicle vehicle)
void setModeRestriction(final Set< String > modeRestriction)
Definition: Dijkstra.java:191
void revisitNode(final Node n, final DijkstraNodeData data, final RouterPriorityQueue< Node > pendingNodes, final double time, final double cost, final Link outLink)
Definition: Dijkstra.java:530
double getLinkTravelDisutility(final Link link, final double time, final Person person, final Vehicle vehicle)
boolean decreaseKey(E value, double priority)
PreProcessDijkstra.DeadEndData getPreProcessData(final Node n)
Definition: Dijkstra.java:619
void visit(final Link comingFrom, final double cost, final double time, final int iterID)
Dijkstra(final Network network, final TravelDisutility costFunction, final TravelTime timeFunction, final PreProcessDijkstra preProcessData)
Definition: Dijkstra.java:164
final PreProcessDijkstra preProcessData
Definition: Dijkstra.java:127
final TravelDisutility costFunction
Definition: Dijkstra.java:98
final TravelTime timeFunction
Definition: Dijkstra.java:103
Path calcLeastCostPath(final Node fromNode, final Node toNode, final double startTime, final Person person2, final Vehicle vehicle2)
Definition: Dijkstra.java:212
Dijkstra(final Network network, final TravelDisutility costFunction, final TravelTime timeFunction)
Definition: Dijkstra.java:147
Map< Id< Link >, ? extends Link > getOutLinks()
static final Logger log
Definition: Dijkstra.java:88
void relaxNode(final Node outNode, final Node toNode, final RouterPriorityQueue< Node > pendingNodes)
Definition: Dijkstra.java:400
boolean add(final E o, final double priority)
DijkstraNodeData createNodeData()
Definition: Dijkstra.java:615