MATSIM
SpeedyALTData.java
Go to the documentation of this file.
1 package org.matsim.core.router.speedy;
2 
3 import org.apache.logging.log4j.LogManager;
4 import org.apache.logging.log4j.Logger;
9 
10 import java.util.Arrays;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.Future;
17 
25 class SpeedyALTData {
26 
27  private final static Logger LOG = LogManager.getLogger(SpeedyALTData.class);
28 
29  final SpeedyGraph graph;
30  private final int landmarksCount;
31  private final TravelDisutility travelCosts;
32  private final int[] landmarksNodeIndices;
33  private final double[] nodesData; // for each node: 2 values per landmark
34  private final int[] deadendData;
35  private final double minTravelCostPerLength;
36 
37  public SpeedyALTData(SpeedyGraph graph, int landmarksCount, TravelDisutility travelCosts) {
38  this.graph = graph;
39  this.landmarksCount = landmarksCount;
40  this.travelCosts = travelCosts;
41  this.landmarksNodeIndices = new int[landmarksCount];
42  this.nodesData = new double[graph.nodeCount * (landmarksCount * 2)];
43  this.deadendData = new int[graph.nodeCount];
44 
45  this.findDeadEnds();
46  this.calcLandmarks();
47  this.minTravelCostPerLength = this.calcMinTravelCostPerLength();
48  }
49 
50  private void findDeadEnds() {
51  LOG.info("find dead ends...");
52 
53  LinkIterator outLI = this.graph.getOutLinkIterator();
54  LinkIterator inLI = this.graph.getInLinkIterator();
55  Arrays.fill(this.deadendData, -1);
56  Map<Integer, Integer> mergedDeadends = new HashMap<>();
57 
58  for (int nodeIdx = 0; nodeIdx < this.graph.nodeCount; nodeIdx++) {
59  Node node = this.graph.getNode(nodeIdx);
60  if (node == null) continue; // not all indices might be in use
61 
62  if (this.deadendData[nodeIdx] >= 0) continue; // already detected as part of dead-end
63 
64  int nIdx = nodeIdx;
65  int otherNodeIndex = checkNodeForDeadend(this.deadendData, mergedDeadends, nIdx, nodeIdx, outLI, inLI);
66  while (otherNodeIndex >= 0) {
67  this.deadendData[nIdx] = nodeIdx;
68  nIdx = otherNodeIndex;
69  otherNodeIndex = checkNodeForDeadend(this.deadendData, mergedDeadends, nIdx, nodeIdx, outLI, inLI);
70  }
71  }
72  Map<Integer, Integer> mergers = new HashMap<>();
73  mergedDeadends.forEach((fromIdx, toIdx) -> {
74  int finalToIdx = toIdx;
75  Integer newToIdx = mergedDeadends.get(toIdx);
76  while (newToIdx != null && newToIdx != finalToIdx) {
77  finalToIdx = newToIdx;
78  newToIdx = mergedDeadends.get(finalToIdx);
79  }
80  mergers.put(fromIdx, finalToIdx);
81  });
82  for (int nodeIdx = 0; nodeIdx < this.graph.nodeCount; nodeIdx++) {
83  int deadend = this.deadendData[nodeIdx];
84  if (deadend >= 0) {
85  this.deadendData[nodeIdx] = mergers.getOrDefault(deadend, deadend);
86  }
87  }
88  }
89 
90  private int checkNodeForDeadend(int[] deadends, Map<Integer, Integer> mergedDeadends, int nodeIdx, int currentDeadend, LinkIterator outLI, LinkIterator inLI) {
91  int otherNodeIndex = -1;
92 
93  outLI.reset(nodeIdx);
94  while (outLI.next()) {
95  int toNodeIdx = outLI.getToNodeIndex();
96  if (deadends[toNodeIdx] >= 0) continue;
97 
98  if (toNodeIdx != otherNodeIndex) {
99  if (otherNodeIndex == -1) otherNodeIndex = toNodeIdx;
100  else return -1; // there are more than one non-dead-end incident nodes
101  }
102  }
103 
104  inLI.reset(nodeIdx);
105  while (inLI.next()) {
106  int fromNodeIdx = inLI.getFromNodeIndex();
107  if (deadends[fromNodeIdx] >= 0) continue;
108 
109  if (fromNodeIdx != otherNodeIndex) {
110  if (otherNodeIndex == -1) otherNodeIndex = fromNodeIdx;
111  else return -1; // there are more than one non-dead-end incident nodes
112  }
113  }
114 
115  outLI.reset(nodeIdx);
116  while (outLI.next()) {
117  int toNodeIdx = outLI.getToNodeIndex();
118  int deadend = deadends[toNodeIdx];
119  if (deadend >= 0) mergedDeadends.put(deadend, currentDeadend);
120  }
121  inLI.reset(nodeIdx);
122  while (inLI.next()) {
123  int fromNodeIdx = inLI.getFromNodeIndex();
124  int deadend = deadends[fromNodeIdx];
125  if (deadend >= 0) mergedDeadends.put(deadend, currentDeadend);
126  }
127 
128  return otherNodeIndex;
129  }
130 
131  private void calcLandmarks() {
132  LOG.info("calculate landmarks...");
133  Node firstNode = null;
134  for (int i = 0; i < this.graph.nodeCount; i++) {
135  firstNode = this.graph.getNode(i);
136  if (firstNode != null) {
137  break;
138  }
139  }
140  if (firstNode == null) {
141  LOG.warn("Network does not contain any nodes!");
142  return;
143  }
144 
145  Future<double[]>[] trees = new Future[this.landmarksCount * 2];
146  ExecutorService executor = Executors.newFixedThreadPool(4);
147 
148  int firstLandmarkIndex = firstNode.getId().index();
149  this.landmarksNodeIndices[0] = firstLandmarkIndex;
150  trees[0] = executor.submit(() -> calculateTreeForward(firstLandmarkIndex));
151  trees[1] = executor.submit(() -> calculateTreeBackward(firstLandmarkIndex));
152 
153  for (int i = 1; i < this.landmarksCount; i++) {
154  int nextLandmark = calculateNextLandmark(i);
155  this.landmarksNodeIndices[i] = nextLandmark;
156 
157  trees[i * 2] = executor.submit(() -> calculateTreeForward(nextLandmark));
158  trees[i * 2 + 1] = executor.submit(() -> calculateTreeBackward(nextLandmark));
159  }
160 
161  for (int i = 0; i < trees.length; i++) {
162  try {
163  double[] data = trees[i].get();
164  setNodeData(data, i);
165  } catch (InterruptedException | ExecutionException e) {
166  LOG.error(e);
167  }
168  }
169  executor.shutdown();
170  }
171 
172  private double calcMinTravelCostPerLength() {
173  LOG.info("calculate min travelcost...");
174  double minCost = Double.POSITIVE_INFINITY;
175  for (int linkIdx = 0; linkIdx < graph.linkCount; linkIdx++) {
176  Link link = this.graph.getLink(linkIdx);
177  if (link != null) {
178  double cost = this.travelCosts.getLinkMinimumTravelDisutility(link) / link.getLength();
179  if (cost < minCost) {
180  minCost = cost;
181  }
182  }
183  }
184  return minCost;
185  }
186 
187  private void setNodeData(double[] data, int offset) {
188  int multiplier = this.landmarksCount * 2;
189  for (int i = 0; i < this.graph.nodeCount; i++) {
190  this.nodesData[i * multiplier + offset] = data[i];
191  }
192  }
193 
194  private int calculateNextLandmark(int existingCount) {
195  double[] data = new double[this.graph.nodeCount];
196  Arrays.fill(data, Double.POSITIVE_INFINITY);
197  LinkIterator outLI = this.graph.getOutLinkIterator();
198 
199  for (int i = 0; i < existingCount; i++) {
200  data[this.landmarksNodeIndices[i]] = 0;
201  }
202 
203  NodeMinHeap pq = new NodeMinHeap(this.graph.nodeCount, i -> data[i], (i, c) -> data[i] = c);
204  for (int i = 0; i < existingCount; i++) {
205  pq.insert(this.landmarksNodeIndices[i]);
206  }
207 
208  int lastNodeIdx = -1;
209  while (!pq.isEmpty()) {
210  final int nodeIdx = pq.poll();
211  lastNodeIdx = nodeIdx;
212  double currCost = data[nodeIdx];
213 
214  outLI.reset(nodeIdx);
215  while (outLI.next()) {
216  int toNode = outLI.getToNodeIndex();
217 
218  double newCost = currCost + 1;
219 
220  double oldCost = data[toNode];
221  if (Double.isFinite(oldCost)) {
222  if (newCost < oldCost) {
223  pq.decreaseKey(toNode, newCost);
224  }
225  } else {
226  data[toNode] = newCost;
227  pq.insert(toNode);
228  }
229  }
230  }
231  return lastNodeIdx;
232  }
233 
234  private double[] calculateTreeForward(int node) {
235  double[] data = new double[this.graph.nodeCount];
236  Arrays.fill(data, Double.POSITIVE_INFINITY);
237  LinkIterator outLI = this.graph.getOutLinkIterator();
238 
239  data[node] = 0;
240 
241  NodeMinHeap pq = new NodeMinHeap(this.graph.nodeCount, i -> data[i], (i, c) -> data[i] = c);
242  pq.insert(node);
243 
244  while (!pq.isEmpty()) {
245  final int nodeIdx = pq.poll();
246  double currCost = data[nodeIdx];
247 
248  outLI.reset(nodeIdx);
249  while (outLI.next()) {
250  int toNode = outLI.getToNodeIndex();
251 
252  double newCost = currCost + this.travelCosts.getLinkMinimumTravelDisutility(this.graph.getLink(outLI.getLinkIndex()));
253 
254  double oldCost = data[toNode];
255  if (Double.isFinite(oldCost)) {
256  if (newCost < oldCost) {
257  pq.decreaseKey(toNode, newCost);
258  }
259  } else {
260  data[toNode] = newCost;
261  pq.insert(toNode);
262  }
263  }
264  }
265 
266  if(graph.hasTurnRestrictions()) {
267  consolidateColoredNodes(data);
268  }
269 
270  return data;
271  }
272 
273  private double[] calculateTreeBackward(int node) {
274  double[] data = new double[this.graph.nodeCount];
275  Arrays.fill(data, Double.POSITIVE_INFINITY);
276  LinkIterator inLI = this.graph.getInLinkIterator();
277 
278  data[node] = 0;
279 
280  NodeMinHeap pq = new NodeMinHeap(this.graph.nodeCount, i -> data[i], (i, c) -> data[i] = c);
281  pq.insert(node);
282 
283  while (!pq.isEmpty()) {
284  final int nodeIdx = pq.poll();
285  double currCost = data[nodeIdx];
286 
287  inLI.reset(nodeIdx);
288  while (inLI.next()) {
289  int fromNode = inLI.getFromNodeIndex();
290 
291  double newCost = currCost + this.travelCosts.getLinkMinimumTravelDisutility(this.graph.getLink(inLI.getLinkIndex()));
292 
293  double oldCost = data[fromNode];
294  if (Double.isFinite(oldCost)) {
295  if (newCost < oldCost) {
296  pq.decreaseKey(fromNode, newCost);
297  }
298  } else {
299  data[fromNode] = newCost;
300  pq.insert(fromNode);
301  }
302  }
303  }
304 
305  if(graph.hasTurnRestrictions()) {
306  consolidateColoredNodes(data);
307  }
308 
309  return data;
310  }
311 
312  private void consolidateColoredNodes(double[] data) {
313  // update node values with the minimum of their colored copies, if any
314  for (int i = 0; i < graph.nodeCount; i++) {
315  Node uncoloredNode = graph.getNode(i);
316  if (uncoloredNode != null) {
317 
318  // the index points to a node with a different index -> colored copy
319  if (uncoloredNode.getId().index() != i) {
320  int uncoloredIndex = uncoloredNode.getId().index();
321  double uncoloredCost = data[uncoloredIndex];
322  double coloredCost = data[i];
323 
324  if (Double.isFinite(uncoloredCost)) {
325  if (coloredCost < uncoloredCost) {
326  data[uncoloredIndex] = coloredCost;
327  }
328  } else {
329  data[uncoloredIndex] = coloredCost;
330  }
331  }
332  }
333  }
334  }
335 
336  int getNodeDeadend(int nodeIndex) {
337  return this.deadendData[nodeIndex];
338  }
339 
340  int getLandmarksCount() {
341  return this.landmarksCount;
342  }
343 
344  double getTravelCostFromLandmark(int nodeIndex, int landmarkIndex) {
345  return this.nodesData[nodeIndex * (this.landmarksCount * 2) + 2 * landmarkIndex];
346  }
347 
348  double getTravelCostToLandmark(int nodeIndex, int landmarkIndex) {
349  return this.nodesData[nodeIndex * (this.landmarksCount * 2) + 2 * landmarkIndex + 1];
350  }
351 
352  public double getMinTravelCostPerLength() {
353  return this.minTravelCostPerLength;
354  }
355 }