Skip to content

Commit f6b1e32

Browse files
authored
Merge pull request #118 from naviqore/feature/NAV-181-initialize-hashset-size-of-marked-stops
Feature/nav 181 initialize hashset size of marked stops
2 parents e200499 + 8962884 commit f6b1e32

File tree

5 files changed

+211
-118
lines changed

5 files changed

+211
-118
lines changed

src/main/java/ch/naviqore/raptor/router/FootpathRelaxer.java

Lines changed: 15 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
import ch.naviqore.raptor.TimeType;
44
import lombok.extern.slf4j.Slf4j;
55

6-
import java.util.Collection;
7-
import java.util.HashSet;
8-
import java.util.Set;
9-
106
import static ch.naviqore.raptor.router.QueryState.NO_INDEX;
117

128
@Slf4j
@@ -45,50 +41,39 @@ class FootpathRelaxer {
4541

4642
/**
4743
* Relax all footpaths from all initial source stops.
48-
*
49-
* @param stopIndices the indices of the stops to be relaxed.
50-
* @return returns the newly marked stops due to the relaxation.
5144
*/
52-
Set<Integer> relaxInitial(int[] stopIndices) {
45+
void relaxInitial() {
5346
log.debug("Initial relaxing of footpaths for source stops");
54-
Set<Integer> newlyMarkedStops = new HashSet<>();
55-
56-
for (int sourceStopIdx : stopIndices) {
57-
expandFootpathsFromStop(sourceStopIdx, 0, newlyMarkedStops);
58-
}
59-
60-
return newlyMarkedStops;
47+
relax(0);
6148
}
6249

6350
/**
6451
* Relax all footpaths from marked stops.
6552
*
66-
* @param round the current round.
67-
* @param stopIndices the indices of the stops to be relaxed.
68-
* @return returns the newly marked stops due to the relaxation.
53+
* @param round the current round.
6954
*/
70-
Set<Integer> relax(int round, Collection<Integer> stopIndices) {
55+
void relax(int round) {
7156
log.debug("Relaxing footpaths for round {}", round);
72-
Set<Integer> newlyMarkedStops = new HashSet<>();
57+
// to prevent extending transfers from stops that were only reached by footpath in the same round
58+
boolean[] routeMarkedStops = queryState.cloneMarkedStopsMaskNextRound();
7359

74-
for (int sourceStopIdx : stopIndices) {
75-
expandFootpathsFromStop(sourceStopIdx, round, newlyMarkedStops);
60+
for (int sourceStopIdx = 0; sourceStopIdx < routeMarkedStops.length; sourceStopIdx++) {
61+
if (!routeMarkedStops[sourceStopIdx]) {
62+
continue;
63+
}
64+
expandFootpathsFromStop(sourceStopIdx, round);
7665
}
77-
78-
return newlyMarkedStops;
7966
}
8067

8168
/**
8269
* Expands all transfers between stops from a given stop. If a transfer improves the target time at the target stop,
8370
* then the target stop is marked for the next round. And the improved target time is stored in the bestTimes array
8471
* and the bestLabelPerRound list (including the new transfer label).
8572
*
86-
* @param stopIdx the index of the stop to expand transfers from.
87-
* @param round the current round to relax footpaths for.
88-
* @param markedStops a set of stop indices that have been marked for scanning in the next round, which will be
89-
* extended if new stops improve due to relaxation.
73+
* @param stopIdx the index of the stop to expand transfers from.
74+
* @param round the current round to relax footpaths for.
9075
*/
91-
private void expandFootpathsFromStop(int stopIdx, int round, Set<Integer> markedStops) {
76+
private void expandFootpathsFromStop(int stopIdx, int round) {
9277
// if stop has no transfers, then no footpaths can be expanded
9378
if (stops[stopIdx].numberOfTransfers() == 0) {
9479
return;
@@ -133,8 +118,7 @@ private void expandFootpathsFromStop(int stopIdx, int round, Set<Integer> marked
133118
QueryState.Label label = new QueryState.Label(sourceTime, targetTime, QueryState.LabelType.TRANSFER, i,
134119
NO_INDEX, transfer.targetStopIdx(), queryState.getLabel(round, stopIdx));
135120
queryState.setLabel(round, transfer.targetStopIdx(), label);
136-
// mark stop as improved
137-
markedStops.add(transfer.targetStopIdx());
121+
queryState.mark(transfer.targetStopIdx());
138122
}
139123
}
140124
}

src/main/java/ch/naviqore/raptor/router/Query.java

Lines changed: 61 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
import lombok.extern.slf4j.Slf4j;
66

77
import java.time.LocalDateTime;
8-
import java.util.*;
8+
import java.util.ArrayList;
9+
import java.util.Arrays;
10+
import java.util.HashMap;
11+
import java.util.List;
912

1013
import static ch.naviqore.raptor.router.QueryState.INFINITY;
1114

@@ -30,6 +33,8 @@ class Query {
3033
private final FootpathRelaxer footpathRelaxer;
3134
private final RouteScanner routeScanner;
3235

36+
private final int numStops;
37+
3338
private final int raptorRange;
3439

3540
/**
@@ -65,6 +70,7 @@ class Query {
6570

6671
targetStops = new int[targetStopIndices.length * 2];
6772
cutoffTime = determineCutoffTime();
73+
numStops = raptorData.getStopContext().stops().length;
6874
queryState = new QueryState(raptorData.getStopContext().stops().length, timeType);
6975

7076
// set up footpath relaxer and route scanner and inject stop labels and times
@@ -74,6 +80,21 @@ class Query {
7480
raptorConfig.getDaysToScan());
7581
}
7682

83+
/**
84+
* Check if there are any marked stops in the marked stops mask.
85+
*
86+
* @param markedStopsMask the marked stops mask to check.
87+
* @return true if there are any marked stops, false otherwise.
88+
*/
89+
private static boolean hasMarkedStops(boolean[] markedStopsMask) {
90+
for (boolean b : markedStopsMask) {
91+
if (b) {
92+
return true;
93+
}
94+
}
95+
return false;
96+
}
97+
7798
/**
7899
* Main control flow of the routing algorithm. Spawns from source stops, coordinates route scanning, footpath
79100
* relaxation, and time/label updates in the correct order.
@@ -93,40 +114,47 @@ class Query {
93114
List<QueryState.Label[]> run() {
94115

95116
// initially relax all source stops and add the newly improved stops by relaxation to the marked stops
96-
Set<Integer> markedStops = initialize();
97-
markedStops.addAll(footpathRelaxer.relaxInitial(sourceStopIndices));
98-
markedStops = removeSuboptimalLabelsForRound(0, markedStops);
117+
initialize();
118+
footpathRelaxer.relaxInitial();
119+
removeSuboptimalLabelsForRound(0);
99120

100121
// if range is 0 or smaller there is no range, and we don't need to rerun rounds with different start offsets
101122
if (raptorRange <= 0) {
102-
doRounds(markedStops);
123+
doRounds();
103124
} else {
104-
doRangeRaptor(markedStops);
125+
doRangeRaptor();
105126
}
106127
return queryState.getBestLabelsPerRound();
107128
}
108129

109-
void doRangeRaptor(Set<Integer> markedStops) {
130+
void doRangeRaptor() {
110131
// prepare range offsets
111-
List<Integer> rangeOffsets = getRangeOffsets(markedStops, routeScanner);
132+
// get initial marked stops to reset after each range offset
133+
List<Integer> initialMarkedStops = new ArrayList<>();
112134
HashMap<Integer, Integer> stopIdxSourceTimes = new HashMap<>();
113-
for (int stopIdx : markedStops) {
135+
for (int stopIdx = 0; stopIdx < numStops; stopIdx++) {
136+
if (!queryState.isMarkedNextRound(stopIdx)) {
137+
continue;
138+
}
139+
initialMarkedStops.add(stopIdx);
114140
stopIdxSourceTimes.put(stopIdx, queryState.getLabel(0, stopIdx).targetTime());
115141
}
116-
142+
List<Integer> rangeOffsets = getRangeOffsets(initialMarkedStops, routeScanner);
117143
// scan all range offsets in reverse order (earliest arrival / latest departure first)
118144
for (int offsetIdx = rangeOffsets.size() - 1; offsetIdx >= 0; offsetIdx--) {
145+
queryState.resetRounds();
119146
int rangeOffset = rangeOffsets.get(offsetIdx);
120147
int timeFactor = timeType == TimeType.DEPARTURE ? 1 : -1;
121148
log.debug("Running rounds with range offset {}", rangeOffset);
122149

123150
// set source times to the source times of the previous round
124-
for (int stopIdx : markedStops) {
151+
for (int stopIdx : initialMarkedStops) {
125152
QueryState.Label label = queryState.getLabel(0, stopIdx);
126153
int targetTime = stopIdxSourceTimes.get(stopIdx) + timeFactor * rangeOffset;
127154
queryState.setLabel(0, stopIdx, copyLabelWithNewTargetTime(label, targetTime));
155+
queryState.mark(stopIdx);
128156
}
129-
doRounds(markedStops);
157+
doRounds();
130158
}
131159
}
132160

@@ -146,26 +174,22 @@ QueryState.Label copyLabelWithNewTargetTime(QueryState.Label label, int targetTi
146174

147175
/**
148176
* Method to perform the rounds of the routing algorithm (see {@link #run()}).
149-
*
150-
* @param markedStops the initially marked stops.
151177
*/
152-
private void doRounds(Set<Integer> markedStops) {
178+
private void doRounds() {
153179

154-
// continue with further rounds as long as there are new marked stops
155-
int round = 1;
156-
while (!markedStops.isEmpty() && (round - 1) <= config.getMaximumTransferNumber()) {
180+
// check if marked stops has any true values
181+
while (queryState.hasMarkedStops() && (queryState.getRound()) <= config.getMaximumTransferNumber()) {
157182
// add label layer for new round
158183
queryState.addNewRound();
159184

160185
// scan all routs and mark stops that have improved
161-
Set<Integer> markedStopsNext = routeScanner.scan(round, markedStops);
186+
routeScanner.scan(queryState.getRound());
162187

163188
// relax footpaths for all newly marked stops
164-
markedStopsNext.addAll(footpathRelaxer.relax(round, markedStopsNext));
189+
footpathRelaxer.relax(queryState.getRound());
165190

166191
// prepare next round
167-
markedStops = removeSuboptimalLabelsForRound(round, markedStopsNext);
168-
round++;
192+
removeSuboptimalLabelsForRound(queryState.getRound());
169193
}
170194
}
171195

@@ -180,13 +204,13 @@ private void doRounds(Set<Integer> markedStops) {
180204
* 10:10, 10:20, and Route B has departures at 10:05, 10:15, 10:25, the range offsets are be 0, 10, 20 and not 0, 5,
181205
* 10, 15, 20, 25 (note real values are in seconds and not minutes --> *60).
182206
*
183-
* @param markedStops the marked stops to get the range offsets for.
184-
* @param routeScanner the route scanner to get the trip offsets for the stops.
207+
* @param initialMarkedStops the initial marked stops to get the range offsets for.
208+
* @param routeScanner the route scanner to get the trip offsets for the stops.
185209
* @return the range offsets (in seconds) applicable for all marked stops.
186210
*/
187-
private List<Integer> getRangeOffsets(Set<Integer> markedStops, RouteScanner routeScanner) {
211+
private List<Integer> getRangeOffsets(List<Integer> initialMarkedStops, RouteScanner routeScanner) {
188212
ArrayList<Integer> rangeOffsets = new ArrayList<>();
189-
for (int stopIdx : markedStops) {
213+
for (int stopIdx : initialMarkedStops) {
190214
List<Integer> stopRangeOffsets = routeScanner.getTripOffsetsForStop(stopIdx, raptorRange);
191215
for (int i = 0; i < stopRangeOffsets.size(); i++) {
192216
// if the rangeOffsets list is not long enough, add the offset
@@ -210,10 +234,8 @@ private List<Integer> getRangeOffsets(Set<Integer> markedStops, RouteScanner rou
210234

211235
/**
212236
* Set up the best times per stop and best labels per round for a new query.
213-
*
214-
* @return the initially marked stops.
215237
*/
216-
Set<Integer> initialize() {
238+
void initialize() {
217239
log.debug("Initializing global best times per stop and best labels per round");
218240

219241
// fill target stops
@@ -224,7 +246,6 @@ Set<Integer> initialize() {
224246
}
225247

226248
// set initial labels, best time and mark source stops
227-
Set<Integer> markedStops = new HashSet<>();
228249
for (int i = 0; i < sourceStopIndices.length; i++) {
229250
int currentStopIdx = sourceStopIndices[i];
230251
int targetTime = sourceTimes[i];
@@ -233,42 +254,35 @@ Set<Integer> initialize() {
233254
QueryState.NO_INDEX, QueryState.NO_INDEX, currentStopIdx, null);
234255
queryState.setLabel(0, currentStopIdx, label);
235256
queryState.setBestTime(currentStopIdx, targetTime);
236-
237-
markedStops.add(currentStopIdx);
257+
queryState.mark(currentStopIdx);
238258
}
239-
240-
return markedStops;
241259
}
242260

243261
/**
244262
* Nullify labels that are suboptimal for the current round. This method checks if the label time is worse than the
245263
* optimal time mark and removes the mark for the next round and nullifies the label in this case.
246264
*
247-
* @param round the round to remove suboptimal labels for.
248-
* @param markedStops the marked stops to check for suboptimal labels.
265+
* @param round the round to remove suboptimal labels for.
249266
*/
250-
Set<Integer> removeSuboptimalLabelsForRound(int round, Set<Integer> markedStops) {
267+
void removeSuboptimalLabelsForRound(int round) {
251268
int bestTime = getBestTimeForAllTargetStops();
252269

253270
if (bestTime == INFINITY || bestTime == -INFINITY) {
254-
return markedStops;
271+
return;
255272
}
256273

257-
Set<Integer> markedStopsClean = new HashSet<>();
258-
for (int stopIdx : markedStops) {
274+
for (int stopIdx = 0; stopIdx < numStops; stopIdx++) {
275+
if (!queryState.isMarkedNextRound(stopIdx)) {
276+
continue;
277+
}
259278
QueryState.Label label = queryState.getLabel(round, stopIdx);
260279
if (label != null) {
261-
if (timeType == TimeType.DEPARTURE && label.targetTime() > bestTime) {
262-
queryState.setLabel(round, stopIdx, null);
263-
} else if (timeType == TimeType.ARRIVAL && label.targetTime() < bestTime) {
280+
if ((timeType == TimeType.DEPARTURE && label.targetTime() > bestTime) || (timeType == TimeType.ARRIVAL && label.targetTime() < bestTime)) {
264281
queryState.setLabel(round, stopIdx, null);
265-
} else {
266-
markedStopsClean.add(stopIdx);
282+
queryState.unmark(stopIdx);
267283
}
268284
}
269285
}
270-
271-
return markedStopsClean;
272286
}
273287

274288
/**

0 commit comments

Comments
 (0)