1 package org.matsim.core.router.speedy;
3 import org.apache.logging.log4j.LogManager;
4 import org.apache.logging.log4j.Logger;
10 import java.util.Arrays;
11 import java.util.HashMap;
13 import java.util.concurrent.ExecutionException;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16 import java.util.concurrent.Future;
27 private final static Logger LOG = LogManager.getLogger(SpeedyALTData.class);
29 final SpeedyGraph graph;
30 private final int landmarksCount;
31 private final TravelDisutility travelCosts;
32 private final int[] landmarksNodeIndices;
33 private final double[] nodesData;
34 private final int[] deadendData;
35 private final double minTravelCostPerLength;
37 public SpeedyALTData(SpeedyGraph graph,
int landmarksCount, TravelDisutility travelCosts) {
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];
47 this.minTravelCostPerLength = this.calcMinTravelCostPerLength();
50 private void findDeadEnds() {
51 LOG.info(
"find dead ends...");
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<>();
58 for (
int nodeIdx = 0; nodeIdx < this.graph.nodeCount; nodeIdx++) {
59 Node node = this.graph.getNode(nodeIdx);
60 if (node == null)
continue;
62 if (this.deadendData[nodeIdx] >= 0)
continue;
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);
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);
80 mergers.put(fromIdx, finalToIdx);
82 for (
int nodeIdx = 0; nodeIdx < this.graph.nodeCount; nodeIdx++) {
83 int deadend = this.deadendData[nodeIdx];
85 this.deadendData[nodeIdx] = mergers.getOrDefault(deadend, deadend);
90 private int checkNodeForDeadend(
int[] deadends, Map<Integer, Integer> mergedDeadends,
int nodeIdx,
int currentDeadend, LinkIterator outLI, LinkIterator inLI) {
91 int otherNodeIndex = -1;
94 while (outLI.next()) {
95 int toNodeIdx = outLI.getToNodeIndex();
96 if (deadends[toNodeIdx] >= 0)
continue;
98 if (toNodeIdx != otherNodeIndex) {
99 if (otherNodeIndex == -1) otherNodeIndex = toNodeIdx;
105 while (inLI.next()) {
106 int fromNodeIdx = inLI.getFromNodeIndex();
107 if (deadends[fromNodeIdx] >= 0)
continue;
109 if (fromNodeIdx != otherNodeIndex) {
110 if (otherNodeIndex == -1) otherNodeIndex = fromNodeIdx;
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);
122 while (inLI.next()) {
123 int fromNodeIdx = inLI.getFromNodeIndex();
124 int deadend = deadends[fromNodeIdx];
125 if (deadend >= 0) mergedDeadends.put(deadend, currentDeadend);
128 return otherNodeIndex;
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) {
140 if (firstNode == null) {
141 LOG.warn(
"Network does not contain any nodes!");
145 Future<double[]>[] trees =
new Future[this.landmarksCount * 2];
146 ExecutorService executor = Executors.newFixedThreadPool(4);
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));
153 for (
int i = 1; i < this.landmarksCount; i++) {
154 int nextLandmark = calculateNextLandmark(i);
155 this.landmarksNodeIndices[i] = nextLandmark;
157 trees[i * 2] = executor.submit(() -> calculateTreeForward(nextLandmark));
158 trees[i * 2 + 1] = executor.submit(() -> calculateTreeBackward(nextLandmark));
161 for (
int i = 0; i < trees.length; i++) {
163 double[] data = trees[i].get();
164 setNodeData(data, i);
165 }
catch (InterruptedException | ExecutionException e) {
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);
178 double cost = this.travelCosts.getLinkMinimumTravelDisutility(link) / link.getLength();
179 if (cost < minCost) {
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];
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();
199 for (
int i = 0; i < existingCount; i++) {
200 data[this.landmarksNodeIndices[i]] = 0;
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]);
208 int lastNodeIdx = -1;
209 while (!pq.isEmpty()) {
210 final int nodeIdx = pq.poll();
211 lastNodeIdx = nodeIdx;
212 double currCost = data[nodeIdx];
214 outLI.reset(nodeIdx);
215 while (outLI.next()) {
216 int toNode = outLI.getToNodeIndex();
218 double newCost = currCost + 1;
220 double oldCost = data[toNode];
221 if (Double.isFinite(oldCost)) {
222 if (newCost < oldCost) {
223 pq.decreaseKey(toNode, newCost);
226 data[toNode] = newCost;
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();
241 NodeMinHeap pq =
new NodeMinHeap(this.graph.nodeCount, i -> data[i], (i, c) -> data[i] = c);
244 while (!pq.isEmpty()) {
245 final int nodeIdx = pq.poll();
246 double currCost = data[nodeIdx];
248 outLI.reset(nodeIdx);
249 while (outLI.next()) {
250 int toNode = outLI.getToNodeIndex();
252 double newCost = currCost + this.travelCosts.getLinkMinimumTravelDisutility(this.graph.getLink(outLI.getLinkIndex()));
254 double oldCost = data[toNode];
255 if (Double.isFinite(oldCost)) {
256 if (newCost < oldCost) {
257 pq.decreaseKey(toNode, newCost);
260 data[toNode] = newCost;
266 if(graph.hasTurnRestrictions()) {
267 consolidateColoredNodes(data);
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();
280 NodeMinHeap pq =
new NodeMinHeap(this.graph.nodeCount, i -> data[i], (i, c) -> data[i] = c);
283 while (!pq.isEmpty()) {
284 final int nodeIdx = pq.poll();
285 double currCost = data[nodeIdx];
288 while (inLI.next()) {
289 int fromNode = inLI.getFromNodeIndex();
291 double newCost = currCost + this.travelCosts.getLinkMinimumTravelDisutility(this.graph.getLink(inLI.getLinkIndex()));
293 double oldCost = data[fromNode];
294 if (Double.isFinite(oldCost)) {
295 if (newCost < oldCost) {
296 pq.decreaseKey(fromNode, newCost);
299 data[fromNode] = newCost;
305 if(graph.hasTurnRestrictions()) {
306 consolidateColoredNodes(data);
312 private void consolidateColoredNodes(
double[] data) {
314 for (
int i = 0; i < graph.nodeCount; i++) {
315 Node uncoloredNode = graph.getNode(i);
316 if (uncoloredNode != null) {
319 if (uncoloredNode.getId().index() != i) {
320 int uncoloredIndex = uncoloredNode.getId().index();
321 double uncoloredCost = data[uncoloredIndex];
322 double coloredCost = data[i];
324 if (Double.isFinite(uncoloredCost)) {
325 if (coloredCost < uncoloredCost) {
326 data[uncoloredIndex] = coloredCost;
329 data[uncoloredIndex] = coloredCost;
336 int getNodeDeadend(
int nodeIndex) {
337 return this.deadendData[nodeIndex];
340 int getLandmarksCount() {
341 return this.landmarksCount;
344 double getTravelCostFromLandmark(
int nodeIndex,
int landmarkIndex) {
345 return this.nodesData[nodeIndex * (this.landmarksCount * 2) + 2 * landmarkIndex];
348 double getTravelCostToLandmark(
int nodeIndex,
int landmarkIndex) {
349 return this.nodesData[nodeIndex * (this.landmarksCount * 2) + 2 * landmarkIndex + 1];
352 public double getMinTravelCostPerLength() {
353 return this.minTravelCostPerLength;