Skip to content

Commit c0a6c24

Browse files
authored
Merge pull request matsim-org#3726 from moia-oss/turnRestrictionsLeastCostPathTree
Use path iterators for constructing paths outside of least cost path tree
2 parents 64838f1 + 98ec498 commit c0a6c24

File tree

2 files changed

+83
-51
lines changed

2 files changed

+83
-51
lines changed

contribs/dvrp/src/main/java/org/matsim/contrib/dvrp/path/OneToManyPathCalculator.java

Lines changed: 11 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,6 @@ class OneToManyPathCalculator {
6767
this.forwardSearch = forwardSearch;
6868
this.fromLink = fromLink;
6969
this.startTime = startTime;
70-
71-
verifyParallelLinks();
7270
}
7371

7472
void calculateDijkstraTree(Collection<Link> toLinks) {
@@ -127,7 +125,7 @@ Path createPath(Node toNode) {
127125
return null;
128126
}
129127
var nodes = constructNodeSequence(dijkstraTree, toNode, forwardSearch);
130-
var links = constructLinkSequence(nodes);
128+
var links = constructLinkSequence(dijkstraTree, toNode, forwardSearch);
131129
double cost = dijkstraTree.getCost(toNodeIndex);
132130
return new Path(nodes, links, travelTime, cost);
133131
}
@@ -145,32 +143,23 @@ private List<Node> constructNodeSequence(LeastCostPathTree dijkstraTree, Node to
145143
ArrayList<Node> nodes = new ArrayList<>();
146144
nodes.add(toNode);
147145

148-
LeastCostPathTree.PathIterator pathIterator = dijkstraTree.getComingFromIterator(toNode);
149-
while (pathIterator.hasNext()) {
150-
nodes.add(pathIterator.next());
151-
}
146+
LeastCostPathTree.PathIterator pathIterator = dijkstraTree.getNodePathIterator(toNode);
147+
pathIterator.forEachRemaining(nodes::add);
152148

153149
if (forward) {
154150
Collections.reverse(nodes);
155151
}
156152
return nodes;
157153
}
158154

159-
private List<Link> constructLinkSequence(List<Node> nodes) {
160-
List<Link> links = new ArrayList<>(nodes.size() - 1);
161-
Node prevNode = nodes.get(0);
162-
for (int i = 1; i < nodes.size(); i++) {
163-
Node nextNode = nodes.get(i);
164-
for (Link link : prevNode.getOutLinks().values()) {
165-
//FIXME this method will not work properly if there are many prevNode -> nextNode links
166-
//TODO save link idx in tree OR pre-check: at most 1 arc per each node pair OR choose faster/better link
167-
// sh, 26/07/2023, added a check further below to increase awareness
168-
if (link.getToNode() == nextNode) {
169-
links.add(link);
170-
break;
171-
}
172-
}
173-
prevNode = nextNode;
155+
private List<Link> constructLinkSequence(LeastCostPathTree dijkstraTree, Node toNode, boolean forward) {
156+
ArrayList<Link> links = new ArrayList<>();
157+
158+
LeastCostPathTree.LinkPathIterator pathIterator = dijkstraTree.getLinkPathIterator(toNode);
159+
pathIterator.forEachRemaining(links::add);
160+
161+
if (forward) {
162+
Collections.reverse(links);
174163
}
175164
return links;
176165
}
@@ -189,31 +178,4 @@ private double getFirstAndLastLinkTT(Link fromLink, Link toLink, double pathTrav
189178
VrpPaths.getLastLinkTT(travelTime, fromLink, time);
190179
return FIRST_LINK_TT + lastLinkTT;
191180
}
192-
193-
private final static Logger logger = LogManager.getLogger(OneToManyPathCalculator.class);
194-
private static int parallelLinksWarningCount = 0;
195-
196-
private void verifyParallelLinks() {
197-
if (parallelLinksWarningCount < 20) {
198-
for (Node prevNode : nodeMap.values()) {
199-
Set<Integer> candidates = new HashSet<>();
200-
201-
for (Link link : prevNode.getOutLinks().values()) {
202-
if (!candidates.add(link.getToNode().getId().index())) {
203-
logger.warn(
204-
"Found parallel links between nodes {} and {}. This may lead to problems in path calculation.",
205-
prevNode.getId().toString(), link.getToNode().getId().toString());
206-
207-
if (parallelLinksWarningCount > 20) {
208-
logger.warn("Consider using NetworkSegmentDoubleLinks.run on your network");
209-
logger.warn("Only showing 20 of these warnings ...");
210-
return;
211-
}
212-
213-
parallelLinksWarningCount++;
214-
}
215-
}
216-
}
217-
}
218-
}
219181
}

matsim/src/main/java/org/matsim/core/router/speedy/LeastCostPathTree.java

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
* implementation NOT thread-safe.
2424
*
2525
* @author mrieser / Simunto, sponsored by SBB Swiss Federal Railways
26+
* @author hrewald, nkuehnel / MOIA turn restriction adjustments
2627
*/
2728
public class LeastCostPathTree {
2829

@@ -31,6 +32,8 @@ public class LeastCostPathTree {
3132
private final TravelDisutility td;
3233
private final double[] data; // 3 entries per node: time, cost, distance
3334
private final int[] comingFrom;
35+
private final int[] fromLink;
36+
private final int[] comingFromLink;
3437
private final SpeedyGraph.LinkIterator outLI;
3538
private final SpeedyGraph.LinkIterator inLI;
3639
private final NodeMinHeap pq;
@@ -41,6 +44,8 @@ public LeastCostPathTree(SpeedyGraph graph, TravelTime tt, TravelDisutility td)
4144
this.td = td;
4245
this.data = new double[graph.nodeCount * 3];
4346
this.comingFrom = new int[graph.nodeCount];
47+
this.fromLink = new int[graph.nodeCount];
48+
this.comingFromLink = new int[graph.linkCount];
4449
this.pq = new NodeMinHeap(graph.nodeCount, this::getCost, this::setCost);
4550
this.outLI = graph.getOutLinkIterator();
4651
this.inLI = graph.getInLinkIterator();
@@ -53,6 +58,7 @@ public void calculate(int startNode, double startTime, Person person, Vehicle ve
5358
public void calculate(int startNode, double startTime, Person person, Vehicle vehicle, StopCriterion stopCriterion) {
5459
Arrays.fill(this.data, Double.POSITIVE_INFINITY);
5560
Arrays.fill(this.comingFrom, -1);
61+
Arrays.fill(this.fromLink, -1);
5662

5763
setData(startNode, 0, startTime, 0);
5864

@@ -86,18 +92,32 @@ public void calculate(int startNode, double startTime, Person person, Vehicle ve
8692
this.pq.decreaseKey(toNode, newCost);
8793
setData(toNode, newCost, newTime, currDistance + link.getLength());
8894
this.comingFrom[toNode] = nodeIdx;
95+
this.fromLink[toNode] = linkIdx;
8996
}
9097
} else {
9198
setData(toNode, newCost, newTime, currDistance + link.getLength());
9299
this.pq.insert(toNode);
93100
this.comingFrom[toNode] = nodeIdx;
101+
this.fromLink[toNode] = linkIdx;
94102
}
95103
}
96104
}
97105

98106
if (graph.hasTurnRestrictions()) {
99107
consolidateColoredNodes();
100108
}
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+
}
101121
}
102122

103123
public void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle) {
@@ -107,6 +127,7 @@ public void calculateBackwards(int arrivalNode, double arrivalTime, Person perso
107127
public void calculateBackwards(int arrivalNode, double arrivalTime, Person person, Vehicle vehicle, StopCriterion stopCriterion) {
108128
Arrays.fill(this.data, Double.POSITIVE_INFINITY);
109129
Arrays.fill(this.comingFrom, -1);
130+
Arrays.fill(this.fromLink, -1);
110131

111132
setData(arrivalNode, 0, arrivalTime, 0);
112133

@@ -140,18 +161,32 @@ public void calculateBackwards(int arrivalNode, double arrivalTime, Person perso
140161
this.pq.decreaseKey(fromNode, newCost);
141162
setData(fromNode, newCost, newTime, currDistance + link.getLength());
142163
this.comingFrom[fromNode] = nodeIdx;
164+
this.fromLink[fromNode] = linkIdx;
143165
}
144166
} else {
145167
setData(fromNode, newCost, newTime, currDistance + link.getLength());
146168
this.pq.insert(fromNode);
147169
this.comingFrom[fromNode] = nodeIdx;
170+
this.fromLink[fromNode] = linkIdx;
148171
}
149172
}
150173
}
151174

152175
if (graph.hasTurnRestrictions()) {
153176
consolidateColoredNodes();
154177
}
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+
}
155190
}
156191

157192
private void consolidateColoredNodes() {
@@ -169,6 +204,7 @@ private void consolidateColoredNodes() {
169204
if (coloredCost < uncoloredCost) {
170205
setData(uncoloredIndex, coloredCost, getTimeRaw(i), getDistance(i));
171206
this.comingFrom[uncoloredIndex] = this.comingFrom[i];
207+
this.fromLink[uncoloredIndex] = this.fromLink[i];
172208
}
173209
}
174210
}
@@ -206,10 +242,14 @@ private void setData(int nodeIndex, double cost, double time, double distance) {
206242
this.data[index + 2] = distance;
207243
}
208244

209-
public PathIterator getComingFromIterator(Node node) {
245+
public PathIterator getNodePathIterator(Node node) {
210246
return new PathIterator(node);
211247
}
212248

249+
public LinkPathIterator getLinkPathIterator(Node node) {
250+
return new LinkPathIterator(node);
251+
}
252+
213253
public interface StopCriterion {
214254

215255
boolean stop(int nodeIndex, double arrivalTime, double travelCost, double distance, double departureTime);
@@ -253,7 +293,7 @@ public PathIterator(Node startNode) {
253293
}
254294

255295
@Override
256-
public Node next() throws NoSuchElementException {
296+
public Node next() {
257297
current = comingFrom[current];
258298
if (current < 0) {
259299
throw new NoSuchElementException();
@@ -266,4 +306,34 @@ public boolean hasNext() {
266306
return comingFrom[current] >= 0;
267307
}
268308
}
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+
}
269339
}

0 commit comments

Comments
 (0)