Skip to content

Commit 99e52be

Browse files
Fix negative cost in filter
1 parent c6cec69 commit 99e52be

3 files changed

Lines changed: 94 additions & 15 deletions

File tree

application/src/main/java/org/opentripplanner/routing/algorithm/filterchain/filters/transit/group/RemoveOtherThanSameLegsMaxGeneralizedCost.java

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -59,35 +59,39 @@ public List<Itinerary> flagForRemoval(List<Itinerary> itineraries) {
5959
})
6060
.get();
6161

62+
if (commonTrips.isEmpty()) {
63+
return List.of();
64+
}
65+
6266
// Find the lowest cost of the common legs
63-
OptionalInt commonCost = itineraries
67+
int commonLegsCost = itineraries
6468
.stream()
6569
.mapToInt(itinerary ->
6670
itinerary
6771
.legs()
6872
.stream()
6973
.filter(Leg::isTransitLeg)
7074
.filter(leg -> commonTrips.contains(leg.trip()))
71-
.mapToInt(leg -> leg.generalizedCost())
75+
.mapToInt(leg -> Integer.max(0, leg.generalizedCost()))
7276
.sum()
7377
)
74-
.min();
75-
76-
if (commonCost.isEmpty()) {
77-
return List.of();
78-
}
78+
.min()
79+
.orElseThrow();
7980

8081
// Find the lowest cost for any itinerary
81-
int minimumCost = itineraries
82+
int minimumItineraryCost = itineraries
8283
.stream()
8384
.mapToInt(it -> it.generalizedCostIncludingPenalty().toSeconds())
8485
.min()
8586
.orElseThrow();
8687

88+
// Leg costs in otp are not guaranteed to be accurate and it is possible that the sum of the
89+
// commonLegCosts are higher than the minimumItineraryCost. So we need to make sure this does
90+
// not go negative.
91+
int otherLegsCost = Integer.max(0, minimumItineraryCost - commonLegsCost);
92+
8793
// Calculate the maximum limit allowed for itinerary cost
88-
Cost maxLimit = Cost.costOfSeconds(
89-
((minimumCost - commonCost.getAsInt()) * maxCostOtherLegsFactor) + commonCost.getAsInt()
90-
);
94+
Cost maxLimit = Cost.costOfSeconds(otherLegsCost * maxCostOtherLegsFactor + commonLegsCost);
9195

9296
return itineraries
9397
.stream()

application/src/test/java/org/opentripplanner/model/plan/TestItineraryBuilder.java

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,13 @@ public TestItineraryBuilder bus(
303303
/**
304304
* Add a rail/train leg to the itinerary
305305
*/
306-
public TestItineraryBuilder rail(int tripId, int startTime, int endTime, Place to) {
306+
public TestItineraryBuilder rail(
307+
int tripId,
308+
int startTime,
309+
int endTime,
310+
Place to,
311+
@Nullable Integer cost
312+
) {
307313
return transit(
308314
RAIL_ROUTE,
309315
Integer.toString(tripId),
@@ -314,10 +320,15 @@ public TestItineraryBuilder rail(int tripId, int startTime, int endTime, Place t
314320
to,
315321
null,
316322
null,
317-
null
323+
null,
324+
cost
318325
);
319326
}
320327

328+
public TestItineraryBuilder rail(int tripId, int startTime, int endTime, Place to) {
329+
return rail(tripId, startTime, endTime, to, null);
330+
}
331+
321332
public TestItineraryBuilder faresV2Rail(
322333
int tripId,
323334
int startTime,
@@ -464,14 +475,46 @@ public TestItineraryBuilder transit(
464475
LocalDate serviceDate,
465476
Integer headwaySecs,
466477
ConstrainedTransfer transferFromPreviousLeg
478+
) {
479+
return transit(
480+
route,
481+
tripId,
482+
start,
483+
end,
484+
fromStopIndex,
485+
toStopIndex,
486+
to,
487+
serviceDate,
488+
headwaySecs,
489+
transferFromPreviousLeg,
490+
null
491+
);
492+
}
493+
494+
public TestItineraryBuilder transit(
495+
Route route,
496+
String tripId,
497+
int start,
498+
int end,
499+
int fromStopIndex,
500+
int toStopIndex,
501+
Place to,
502+
LocalDate serviceDate,
503+
Integer headwaySecs,
504+
ConstrainedTransfer transferFromPreviousLeg,
505+
@Nullable Integer cost
467506
) {
468507
if (lastPlace == null) {
469508
throw new IllegalStateException("Trip from place is unknown!");
470509
}
471510
int waitTime = start - lastEndTime(start);
472511
int legCost = 0;
473-
legCost += cost(WAIT_RELUCTANCE_FACTOR, waitTime);
474-
legCost += cost(1.0f, end - start) + BOARD_COST;
512+
if (cost != null) {
513+
legCost = cost;
514+
} else {
515+
legCost += cost(WAIT_RELUCTANCE_FACTOR, waitTime);
516+
legCost += cost(1.0f, end - start) + BOARD_COST;
517+
}
475518

476519
Trip trip = trip(tripId, route);
477520

application/src/test/java/org/opentripplanner/routing/algorithm/filterchain/filters/transit/group/RemoveOtherThanSameLegsMaxGeneralizedCostTest.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,36 @@ public void testFilter() {
2222
var subject = new RemoveOtherThanSameLegsMaxGeneralizedCost(2.0);
2323
assertEquals(List.of(second), subject.flagForRemoval(List.of(first, second)));
2424
}
25+
26+
@Test
27+
public void testFilterNothingInCommon() {
28+
Itinerary first = newItinerary(A)
29+
.rail(20, T11_05, T11_14, B)
30+
.bus(30, T11_16, T11_20, C)
31+
.build();
32+
33+
Itinerary second = newItinerary(A).rail(40, T11_05, T11_14, B).walk(D10m, C).build();
34+
35+
var subject = new RemoveOtherThanSameLegsMaxGeneralizedCost(2.0);
36+
assertEquals(List.of(), subject.flagForRemoval(List.of(first, second)));
37+
}
38+
39+
@Test
40+
public void testFilterInaccurateLegCosts() {
41+
// Regression test that verifies that we don't crash in the case where the sum of the leg costs
42+
// is larger than the itinerary cost.
43+
int itineraryCost = 100;
44+
Itinerary first = newItinerary(A)
45+
.rail(20, T11_05, T11_14, B, 400)
46+
.walk(60 * 4, C)
47+
.build(itineraryCost);
48+
49+
Itinerary second = newItinerary(A)
50+
.rail(20, T11_05, T11_14, B, 400)
51+
.walk(D10m, C)
52+
.build(itineraryCost);
53+
54+
var subject = new RemoveOtherThanSameLegsMaxGeneralizedCost(2.0);
55+
assertEquals(List.of(), subject.flagForRemoval(List.of(first, second)));
56+
}
2557
}

0 commit comments

Comments
 (0)