Skip to content

Commit

Permalink
Via search fixes (#158)
Browse files Browse the repository at this point in the history
* fix via reconstruction for trips with in/out allowed = false

* fix via search if last via stop = destination and stay > 0

* fixes for gcc + clang
  • Loading branch information
pablohoch authored Dec 9, 2024
1 parent 815bd0c commit 0568714
Show file tree
Hide file tree
Showing 3 changed files with 244 additions and 25 deletions.
98 changes: 88 additions & 10 deletions include/nigiri/routing/raptor/raptor.h
Original file line number Diff line number Diff line change
Expand Up @@ -336,9 +336,9 @@ struct raptor {
continue;
}

auto const is_dest = v == Vias && is_dest_[i];
auto const is_via = v != Vias && is_via_[v][i];
auto const target_v = is_via ? v + 1 : v;
auto const is_dest = target_v == Vias && is_dest_[i];
auto const stay = is_via ? via_stops_[v].stay_ : 0_minutes;

trace(
Expand Down Expand Up @@ -592,7 +592,11 @@ struct raptor {
if (best_time == kInvalid) {
return;
}
auto const end_time = clamp(best_time + dir(dist_to_end_[i]));
auto const stay = Vias != 0 && is_via_[Vias - 1U][i]
? via_stops_[Vias - 1U].stay_
: 0_minutes;
auto const end_time =
clamp(best_time + stay.count() + dir(dist_to_end_[i]));

if (is_better(end_time, best_[kIntermodalTarget][Vias])) {
round_times_[k][kIntermodalTarget][Vias] = end_time;
Expand Down Expand Up @@ -662,9 +666,17 @@ struct raptor {
auto const v = Vias - j;
auto target_v = v + v_offset[v];
if (et[v] && stp.can_finish<SearchDir>(is_wheelchair_)) {
auto const is_via = target_v != Vias && is_via_[target_v][l_idx] &&
via_stops_[target_v].stay_ == 0_minutes;
if (is_via) {
auto const is_via = target_v != Vias && is_via_[target_v][l_idx];
auto const is_no_stay_via =
is_via && via_stops_[target_v].stay_ == 0_minutes;

// special case: stop is via with stay > 0m + destination
auto const is_via_and_dest =
is_via && !is_no_stay_via &&
(is_dest_[l_idx] ||
(is_intermodal_dest() && state_.end_reachable_[l_idx]));

if (is_no_stay_via) {
++v_offset[v];
++target_v;
}
Expand Down Expand Up @@ -701,6 +713,36 @@ struct raptor {
current_best = by_transport;
any_marked = true;
}

if (is_via_and_dest) {
auto const dest_v = target_v + 1;
assert(dest_v == Vias);
auto const best_dest =
get_best(round_times_[k - 1][l_idx][dest_v],
tmp_[l_idx][dest_v], best_[l_idx][dest_v]);

if (is_better(by_transport, best_dest) &&
is_better(by_transport, time_at_dest_[k]) &&
lb_[l_idx] != kUnreachable &&
is_better(by_transport + dir(lb_[l_idx]), time_at_dest_[k])) {
trace_upd(
"┊ │k={} v={}->{} RT name={}, dbg={}, "
"time_by_transport={}, "
"BETTER THAN dest_best={} => update, {} marking station "
"{} (destination)!\n",
k, v, dest_v, tt_.transport_name(et[v].t_idx_),
tt_.dbg(et[v].t_idx_), to_unix(by_transport),
to_unix(best_dest),
!is_better(by_transport, best_dest) ? "NOT" : "",
location{tt_, stp.location_idx()});

++stats_.n_earliest_arrival_updated_by_route_;
tmp_[l_idx][dest_v] =
get_best(by_transport, tmp_[l_idx][dest_v]);
state_.station_mark_.set(l_idx, true);
any_marked = true;
}
}
}
}
}
Expand Down Expand Up @@ -783,19 +825,27 @@ struct raptor {
auto const by_transport = time_at_stop(
r, et[v], stop_idx, kFwd ? event_type::kArr : event_type::kDep);

auto const is_via = target_v != Vias && is_via_[target_v][l_idx] &&
via_stops_[target_v].stay_ == 0_minutes;
auto const is_via = target_v != Vias && is_via_[target_v][l_idx];
auto const is_no_stay_via =
is_via && via_stops_[target_v].stay_ == 0_minutes;

// special case: stop is via with stay > 0m + destination
auto const is_via_and_dest =
is_via && !is_no_stay_via &&
(is_dest_[l_idx] ||
(is_intermodal_dest() && state_.end_reachable_[l_idx]));

if (Vias != 0) {
trace_upd(
"┊ │k={} v={}(+{})={} via_count={} is_via_dest={} stay={} "
"is_via={}\n",
"is_via={} is_dest={} is_via_and_dest={}\n",
k, v, v_offset[v], target_v, Vias,
target_v != Vias ? is_via_[target_v][l_idx] : is_dest_[l_idx],
via_stops_[target_v].stay_, is_via);
via_stops_[target_v].stay_, is_no_stay_via, is_dest_[l_idx],
is_via_and_dest);
}

if (is_via) {
if (is_no_stay_via) {
++v_offset[v];
++target_v;
}
Expand Down Expand Up @@ -864,6 +914,34 @@ struct raptor {
is_better(clamp(by_transport + dir(lb_[l_idx])),
time_at_dest_[k]));
}

if (is_via_and_dest) {
auto const dest_v = target_v + 1;
assert(dest_v == Vias);
auto const best_dest =
get_best(round_times_[k - 1][l_idx][dest_v],
tmp_[l_idx][dest_v], best_[l_idx][dest_v]);

if (is_better(by_transport, best_dest) &&
is_better(by_transport, time_at_dest_[k]) &&
lb_[l_idx] != kUnreachable &&
is_better(by_transport + dir(lb_[l_idx]), time_at_dest_[k])) {
trace_upd(
"┊ │k={} v={}->{} name={}, dbg={}, time_by_transport={}, "
"BETTER THAN dest_best={} => update, {} marking station "
"{} (destination)!\n",
k, v, dest_v, tt_.transport_name(et[v].t_idx_),
tt_.dbg(et[v].t_idx_), to_unix(by_transport),
to_unix(best_dest),
!is_better(by_transport, best_dest) ? "NOT" : "",
location{tt_, stp.location_idx()});

++stats_.n_earliest_arrival_updated_by_route_;
tmp_[l_idx][dest_v] = get_best(by_transport, tmp_[l_idx][dest_v]);
state_.station_mark_.set(l_idx, true);
any_marked = true;
}
}
} else {
trace(
"┊ │k={} v={}->{} *** NO UPD: no_trip={}, in_allowed={}, "
Expand Down
44 changes: 29 additions & 15 deletions src/routing/raptor/reconstruct.cc
Original file line number Diff line number Diff line change
Expand Up @@ -182,20 +182,30 @@ void reconstruct_journey_with_vias(timetable const& tt,
break;
}

auto const stop_matches_via =
new_v != 0 && q.via_stops_[new_v - 1].stay_ == 0_minutes &&
matches(tt, location_match_mode::kEquivalent,
q.via_stops_[new_v - 1].location_, l);

auto const check_via = [&]() {
if (stop_matches_via) {
trace_reconstruct(
" [find_entry_in_prev_round] new_v={}->{} (stop matches via)\n",
v, new_v, new_v - 1);
--new_v;
}
};

if ((kFwd && !stp.in_allowed(is_wheelchair)) ||
(!kFwd && !stp.out_allowed(is_wheelchair))) {
check_via();
continue;
}

auto const event_time = unix_to_delta(
base, stp.time(kFwd ? event_type::kDep : event_type::kArr));
auto const round_time = round_times[k - 1][to_idx(l)][new_v];

auto const stop_matches_via =
new_v != 0 && q.via_stops_[new_v - 1].stay_ == 0_minutes &&
matches(tt, location_match_mode::kEquivalent,
q.via_stops_[new_v - 1].location_, l);

if (is_better_or_eq(round_time, event_time) ||
// special case: first stop with meta stations
(k == 1 && q.start_match_mode_ == location_match_mode::kEquivalent &&
Expand All @@ -212,12 +222,7 @@ void reconstruct_journey_with_vias(timetable const& tt,
journey::run_enter_exit{r, stop_idx, from_stop_idx}};
} else {
trace_rc_transport_entry_not_possible;
if (stop_matches_via) {
trace_reconstruct(
" [find_entry_in_prev_round] new_v={}->{} (stop matches via)\n",
v, new_v, new_v - 1);
--new_v;
}
check_via();
}
}

Expand Down Expand Up @@ -391,15 +396,22 @@ void reconstruct_journey_with_vias(timetable const& tt,

auto const backup_v = v;

auto const is_final_leg = k == j.transfers_ + 1U;
auto const is_intermodal =
q.dest_match_mode_ == location_match_mode::kIntermodal;
auto stay_l = 0_minutes;
auto stay_fp_target = 0_minutes;
trace_reconstruct(" [check_fp] v={}, l={}, fp.target={}\n", v,
location{tt, l}, location{tt, fp.target()});
trace_reconstruct(
" [check_fp] v={}, l={}, fp.target={}, final_leg={}, intermodal={}\n",
v, location{tt, l}, location{tt, fp.target()}, is_final_leg,
is_intermodal);
if (v != 0 && matches(tt, location_match_mode::kEquivalent,
q.via_stops_[v - 1].location_, l)) {
--v;
if (matches(tt, location_match_mode::kEquivalent, l, fp.target())) {
stay_fp_target = q.via_stops_[v].stay_;
if (!is_final_leg) {
stay_fp_target = q.via_stops_[v].stay_;
}
trace_reconstruct(
" [check_fp]: fp start+target matches current via: v={}->{}, "
"stay_target={}\n",
Expand All @@ -415,7 +427,9 @@ void reconstruct_journey_with_vias(timetable const& tt,
q.via_stops_[v - 1].location_, fp.target())) {
--v;
assert(stay_fp_target == 0_minutes);
stay_fp_target = q.via_stops_[v].stay_;
if (!is_final_leg || is_intermodal) {
stay_fp_target = q.via_stops_[v].stay_;
}
trace_reconstruct(
" [check_fp]: fp target matches current via: v={}->{}, "
"stay_fp_target={}\n",
Expand Down
127 changes: 127 additions & 0 deletions test/routing/via_search_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1240,3 +1240,130 @@ leg 5: (P, P) [2019-05-01 09:43] -> (END, END) [2019-05-01 10:03]

EXPECT_EQ(expected_A_intermodal_LP_via_O_0m, results_to_str(results, tt));
}

TEST(routing, via_test_31_M_Q_via_Q_10m) {
// test: last via = destination, with stay duration (ignored)
// M -> Q, via Q (10 min)
auto tt = load_timetable(test_files_1);

constexpr auto const expected_M_Q_via_O_10min =
R"(
[2019-05-01 09:00, 2019-05-01 10:00]
TRANSFERS: 0
FROM: (M, M) [2019-05-01 09:00]
TO: (Q, Q) [2019-05-01 10:00]
leg 0: (M, M) [2019-05-01 09:00] -> (Q, Q) [2019-05-01 10:00]
0: M M............................................... d: 01.05 09:00 [01.05 11:00] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
1: N N............................................... a: 01.05 09:13 [01.05 11:13] d: 01.05 09:15 [01.05 11:15] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
2: O O............................................... a: 01.05 09:28 [01.05 11:28] d: 01.05 09:30 [01.05 11:30] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
3: P P............................................... a: 01.05 09:43 [01.05 11:43] d: 01.05 09:45 [01.05 11:45] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
4: Q Q............................................... a: 01.05 10:00 [01.05 12:00]
)"sv;

for (auto const& [dir, start_time] :
{std::pair{direction::kForward, time("2019-05-01 11:00 Europe/Berlin")},
std::pair{direction::kBackward,
time("2019-05-01 12:00 Europe/Berlin")}}) {
auto const results =
search(tt, nullptr,
routing::query{.start_time_ = start_time,
.start_ = {{loc(tt, "M"), 0_minutes, 0U}},
.destination_ = {{loc(tt, "Q"), 0_minutes, 0U}},
.via_stops_ = {{loc(tt, "Q"), 10_minutes}}},
dir);

EXPECT_EQ(expected_M_Q_via_O_10min, results_to_str(results, tt));
}
}

TEST(routing, via_test_32_M_intermodal_Q_via_Q_10m) {
// test: last via = destination, with stay duration (before intermodal fp)
// M -> Q, via Q (10 min)
auto tt = load_timetable(test_files_1);

constexpr auto const expected_M_intermodal_Q_via_O_10min =
R"(
[2019-05-01 09:00, 2019-05-01 10:25]
TRANSFERS: 0
FROM: (M, M) [2019-05-01 09:00]
TO: (END, END) [2019-05-01 10:25]
leg 0: (M, M) [2019-05-01 09:00] -> (Q, Q) [2019-05-01 10:00]
0: M M............................................... d: 01.05 09:00 [01.05 11:00] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
1: N N............................................... a: 01.05 09:13 [01.05 11:13] d: 01.05 09:15 [01.05 11:15] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
2: O O............................................... a: 01.05 09:28 [01.05 11:28] d: 01.05 09:30 [01.05 11:30] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
3: P P............................................... a: 01.05 09:43 [01.05 11:43] d: 01.05 09:45 [01.05 11:45] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
4: Q Q............................................... a: 01.05 10:00 [01.05 12:00]
leg 1: (Q, Q) [2019-05-01 10:10] -> (END, END) [2019-05-01 10:25]
MUMO (id=0, duration=15)
)"sv;

for (auto const& [dir, start_time] :
{std::pair{direction::kForward, time("2019-05-01 11:00 Europe/Berlin")},
std::pair{direction::kBackward,
time("2019-05-01 12:25 Europe/Berlin")}}) {
auto const results =
search(tt, nullptr,
routing::query{.start_time_ = start_time,
.dest_match_mode_ =
routing::location_match_mode::kIntermodal,
.start_ = {{loc(tt, "M"), 0_minutes, 0U}},
.destination_ = {{loc(tt, "Q"), 15_minutes, 0U}},
.via_stops_ = {{loc(tt, "Q"), 10_minutes}}},
dir);

auto results_str = results_to_str(results, tt);
if (dir == direction::kBackward) {
results_str = std::regex_replace(results_str, std::regex("START"), "END");
}
EXPECT_EQ(expected_M_intermodal_Q_via_O_10min, results_str);
}
}

TEST(routing, via_test_33_M_intermodal_Q_via_Q_0m) {
// test: last via = destination
// M -> Q, via Q (0 min)
auto tt = load_timetable(test_files_1);

constexpr auto const expected_M_intermodal_Q_via_O_0min =
R"(
[2019-05-01 09:00, 2019-05-01 10:15]
TRANSFERS: 0
FROM: (M, M) [2019-05-01 09:00]
TO: (END, END) [2019-05-01 10:15]
leg 0: (M, M) [2019-05-01 09:00] -> (Q, Q) [2019-05-01 10:00]
0: M M............................................... d: 01.05 09:00 [01.05 11:00] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
1: N N............................................... a: 01.05 09:13 [01.05 11:13] d: 01.05 09:15 [01.05 11:15] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
2: O O............................................... a: 01.05 09:28 [01.05 11:28] d: 01.05 09:30 [01.05 11:30] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
3: P P............................................... a: 01.05 09:43 [01.05 11:43] d: 01.05 09:45 [01.05 11:45] [{name=Bus 7, day=2019-05-01, id=T9, src=0}]
4: Q Q............................................... a: 01.05 10:00 [01.05 12:00]
leg 1: (Q, Q) [2019-05-01 10:00] -> (END, END) [2019-05-01 10:15]
MUMO (id=0, duration=15)
)"sv;

for (auto const& [dir, start_time] :
{std::pair{direction::kForward, time("2019-05-01 11:00 Europe/Berlin")},
std::pair{direction::kBackward,
time("2019-05-01 12:15 Europe/Berlin")}}) {
auto const results =
search(tt, nullptr,
routing::query{.start_time_ = start_time,
.dest_match_mode_ =
routing::location_match_mode::kIntermodal,
.start_ = {{loc(tt, "M"), 0_minutes, 0U}},
.destination_ = {{loc(tt, "Q"), 15_minutes, 0U}},
.via_stops_ = {{loc(tt, "Q"), 0_minutes}}},
dir);

auto results_str = results_to_str(results, tt);
if (dir == direction::kBackward) {
results_str = std::regex_replace(results_str, std::regex("START"), "END");
}
EXPECT_EQ(expected_M_intermodal_Q_via_O_0min, results_str);
}
}

0 comments on commit 0568714

Please sign in to comment.