MATSIM
LeastCostPathTree.java
Go to the documentation of this file.
1 package org.matsim.core.router.speedy;
2 
3 import com.google.common.base.Preconditions;
10 import org.matsim.vehicles.Vehicle;
11 
12 import java.util.Arrays;
13 import java.util.Iterator;
14 import java.util.NoSuchElementException;
15 
28 public class LeastCostPathTree {
29 
30  private final SpeedyGraph graph;
31  private final TravelTime tt;
32  private final TravelDisutility td;
33  private final double[] data; // 3 entries per node: time, cost, distance
34  private final int[] comingFrom;
35  private final int[] fromLink;
36  private final int[] comingFromLink;
38  private final SpeedyGraph.LinkIterator inLI;
39  private final NodeMinHeap pq;
40 
42  this.graph = graph;
43  this.tt = tt;
44  this.td = td;
45  this.data = new double[graph.nodeCount * 3];
46  this.comingFrom = new int[graph.nodeCount];
47  this.fromLink = new int[graph.nodeCount];
48  this.comingFromLink = new int[graph.linkCount];
49  this.pq = new NodeMinHeap(graph.nodeCount, this::getCost, this::setCost);
50  this.outLI = graph.getOutLinkIterator();
51  this.inLI = graph.getInLinkIterator();
52  }
53 
54  public void calculate(int startNode, double startTime, Person person, Vehicle vehicle) {
55  this.calculate(startNode, startTime, person, vehicle, (node, arrTime, cost, distance, depTime) -> false);
56  }
57 
58  public void calculate(int startNode, double startTime, Person person, Vehicle vehicle, StopCriterion stopCriterion) {
59  Arrays.fill(this.data, Double.POSITIVE_INFINITY);
60  Arrays.fill(this.comingFrom, -1);
61  Arrays.fill(this.fromLink, -1);
62 
63  setData(startNode, 0, startTime, 0);
64 
65  this.pq.clear();
66  this.pq.insert(startNode);
67 
68  while (!this.pq.isEmpty()) {
69  final int nodeIdx = this.pq.poll();
70  double currTime = getTimeRaw(nodeIdx);
71  Preconditions.checkState(currTime != Double.POSITIVE_INFINITY, "Undefined Time");
72  double currCost = getCost(nodeIdx);
73  double currDistance = getDistance(nodeIdx);
74 
75  if (stopCriterion.stop(nodeIdx, currTime, currCost, currDistance, startTime)) {
76  break;
77  }
78 
79  this.outLI.reset(nodeIdx);
80  while (this.outLI.next()) {
81  int linkIdx = this.outLI.getLinkIndex();
82  Link link = this.graph.getLink(linkIdx);
83  int toNode = this.outLI.getToNodeIndex();
84 
85  double travelTime = this.tt.getLinkTravelTime(link, currTime, person, vehicle);
86  double newTime = currTime + travelTime;
87  double newCost = currCost + this.td.getLinkTravelDisutility(link, currTime, person, vehicle);
88 
89  double oldCost = getCost(toNode);
90  if (Double.isFinite(oldCost)) {
91  if (newCost < oldCost) {
92  this.pq.decreaseKey(toNode, newCost);
93  setData(toNode, newCost, newTime, currDistance + link.getLength());
94  this.comingFrom[toNode] = nodeIdx;
95  this.fromLink[toNode] = linkIdx;
96  }
97  } else {
98  setData(toNode, newCost, newTime, currDistance + link.getLength());
99  this.pq.insert(toNode);
100  this.comingFrom[toNode] = nodeIdx;
101  this.fromLink[toNode] = linkIdx;
102  }
103  }
104  }
105 
106  if (graph.hasTurnRestrictions()) {
108  }
109 
110  Arrays.fill(this.comingFromLink, -1);
111  for (int i = 0; i < graph.nodeCount; i++) {
112  Node node = graph.getNode(i);
113  if(node != null) {
114  this.outLI.reset(i);
115  while (this.outLI.next()) {
116  int previousLinkIdx = fromLink[i];
117  this.comingFromLink[outLI.getLinkIndex()] = previousLinkIdx;
118  }
119  }
120  }
121  }
122 
123  public void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle) {
124  this.calculate(arrivalNode, arrivalTime, person, vehicle, (node, arrTime, cost, distance, depTime) -> false);
125  }
126 
127  public void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle, StopCriterion stopCriterion) {
128  Arrays.fill(this.data, Double.POSITIVE_INFINITY);
129  Arrays.fill(this.comingFrom, -1);
130  Arrays.fill(this.fromLink, -1);
131 
132  setData(arrivalNode, 0, arrivalTime, 0);
133 
134  this.pq.clear();
135  this.pq.insert(arrivalNode);
136 
137  while (!this.pq.isEmpty()) {
138  final int nodeIdx = this.pq.poll();
139  double currTime = getTimeRaw(nodeIdx);
140  Preconditions.checkState(currTime != Double.POSITIVE_INFINITY, "Undefined Time");
141  double currCost = getCost(nodeIdx);
142  double currDistance = getDistance(nodeIdx);
143 
144  if (stopCriterion.stop(nodeIdx, arrivalTime, currCost, currDistance, currTime)) {
145  break;
146  }
147 
148  this.inLI.reset(nodeIdx);
149  while (this.inLI.next()) {
150  int linkIdx = this.inLI.getLinkIndex();
151  Link link = this.graph.getLink(linkIdx);
152  int fromNode = this.inLI.getFromNodeIndex();
153 
154  double travelTime = this.tt.getLinkTravelTime(link, currTime, person, vehicle);
155  double newTime = currTime - travelTime;
156  double newCost = currCost + this.td.getLinkTravelDisutility(link, currTime, person, vehicle);
157 
158  double oldCost = getCost(fromNode);
159  if (Double.isFinite(oldCost)) {
160  if (newCost < oldCost) {
161  this.pq.decreaseKey(fromNode, newCost);
162  setData(fromNode, newCost, newTime, currDistance + link.getLength());
163  this.comingFrom[fromNode] = nodeIdx;
164  this.fromLink[fromNode] = linkIdx;
165  }
166  } else {
167  setData(fromNode, newCost, newTime, currDistance + link.getLength());
168  this.pq.insert(fromNode);
169  this.comingFrom[fromNode] = nodeIdx;
170  this.fromLink[fromNode] = linkIdx;
171  }
172  }
173  }
174 
175  if (graph.hasTurnRestrictions()) {
177  }
178 
179  Arrays.fill(this.comingFromLink, -1);
180  for (int i = 0; i < graph.nodeCount; i++) {
181  Node node = graph.getNode(i);
182  if(node != null) {
183  this.inLI.reset(i);
184  while (this.inLI.next()) {
185  int previousLinkIdx = fromLink[i];
186  this.comingFromLink[inLI.getLinkIndex()] = previousLinkIdx;
187  }
188  }
189  }
190  }
191 
192  private void consolidateColoredNodes() {
193  // update node values with the minimum of their colored copies, if any
194  for (int i = 0; i < data.length / 3; i++) {
195  Node uncoloredNode = graph.getNode(i);
196  if (uncoloredNode != null) {
197 
198  // the index points to a node with a different index -> colored copy
199  if (uncoloredNode.getId().index() != i) {
200  int uncoloredIndex = uncoloredNode.getId().index();
201  double uncoloredCost = getCost(uncoloredIndex);
202  double coloredCost = getCost(i);
203 
204  if (coloredCost < uncoloredCost) {
205  setData(uncoloredIndex, coloredCost, getTimeRaw(i), getDistance(i));
206  this.comingFrom[uncoloredIndex] = this.comingFrom[i];
207  this.fromLink[uncoloredIndex] = this.fromLink[i];
208  }
209  }
210  }
211  }
212  }
213 
214  public double getCost(int nodeIndex) {
215  return this.data[nodeIndex * 3];
216  }
217 
218  private double getTimeRaw(int nodeIndex) {
219  return this.data[nodeIndex * 3 + 1];
220  }
221 
222  public OptionalTime getTime(int nodeIndex) {
223  double time = getTimeRaw(nodeIndex);
224  if (Double.isInfinite(time)) {
225  return OptionalTime.undefined();
226  }
227  return OptionalTime.defined(time);
228  }
229 
230  public double getDistance(int nodeIndex) {
231  return this.data[nodeIndex * 3 + 2];
232  }
233 
234  private void setCost(int nodeIndex, double cost) {
235  this.data[nodeIndex * 3] = cost;
236  }
237 
238  private void setData(int nodeIndex, double cost, double time, double distance) {
239  int index = nodeIndex * 3;
240  this.data[index] = cost;
241  this.data[index + 1] = time;
242  this.data[index + 2] = distance;
243  }
244 
246  return new PathIterator(node);
247  }
248 
250  return new LinkPathIterator(node);
251  }
252 
253  public interface StopCriterion {
254 
255  boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime);
256  }
257 
258  public static final class TravelTimeStopCriterion implements StopCriterion {
259 
260  private final double limit;
261 
262  public TravelTimeStopCriterion(double limit) {
263  this.limit = limit;
264  }
265 
266  @Override
267  public boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime) {
268  return Math.abs(arrivalTime - departureTime) >= this.limit; // use Math.abs() so it also works in backwards search
269  }
270  }
271 
272  public static final class TravelDistanceStopCriterion implements StopCriterion {
273 
274  private final double limit;
275 
276  public TravelDistanceStopCriterion(double limit) {
277  this.limit = limit;
278  }
279 
280  @Override
281  public boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime) {
282  return distance >= this.limit;
283  }
284  }
285 
286  // by not exposing internal indices to the outside we ensure that only uncolored nodes are returned. nkuehnel Feb'25
287  public final class PathIterator implements Iterator<Node> {
288 
289  private int current;
290 
291  public PathIterator(Node startNode) {
292  current = startNode.getId().index();
293  }
294 
295  @Override
296  public Node next() {
297  current = comingFrom[current];
298  if (current < 0) {
299  throw new NoSuchElementException();
300  }
301  return graph.getNode(current);
302  }
303 
304  @Override
305  public boolean hasNext() {
306  return comingFrom[current] >= 0;
307  }
308  }
309 
310  // by not exposing internal indices to the outside we ensure that only uncolored nodes are returned. nkuehnel Feb'25
311  public final class LinkPathIterator implements Iterator<Link> {
312 
313  private boolean firstStep = true;
314 
315  private int current;
316 
317  public LinkPathIterator(Node startNode) {
318  current = fromLink[startNode.getId().index()];
319  }
320 
321  @Override
322  public Link next() {
323  if(firstStep) {
324  firstStep = false;
325  return graph.getLink(current);
326  }
327  current = comingFromLink[current];
328  if (current < 0) {
329  throw new NoSuchElementException();
330  }
331  return graph.getLink(current);
332  }
333 
334  @Override
335  public boolean hasNext() {
336  return current >= 0 && (comingFromLink[current] >= 0 || firstStep);
337  }
338  }
339 }
LeastCostPathTree(SpeedyGraph graph, TravelTime tt, TravelDisutility td)
boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime)
double getLinkTravelTime(Link link, double time, Person person, Vehicle vehicle)
boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime)
double getLinkTravelDisutility(final Link link, final double time, final Person person, final Vehicle vehicle)
void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle)
void calculate(int startNode, double startTime, Person person, Vehicle vehicle)
void setData(int nodeIndex, double cost, double time, double distance)
static OptionalTime defined(double seconds)
void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle, StopCriterion stopCriterion)
void calculate(int startNode, double startTime, Person person, Vehicle vehicle, StopCriterion stopCriterion)
boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime)