From 561178e77536164a28450b05908827436d1f560c Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Thu, 19 Mar 2026 16:16:32 +0100 Subject: [PATCH 1/8] implementation of a_star --- include/nigiri/routing/tb/tb_a_star/a_star.h | 115 ++ include/nigiri/string_store.h | 2 + src/routing/tb/tb_a_star/a_star.cc | 390 +++++ test/routing/tb_a_star_pareto_verification.cc | 513 ++++++ test/routing/tb_a_star_test.cc | 1427 +++++++++++++++++ 5 files changed, 2447 insertions(+) create mode 100644 include/nigiri/routing/tb/tb_a_star/a_star.h create mode 100644 src/routing/tb/tb_a_star/a_star.cc create mode 100644 test/routing/tb_a_star_pareto_verification.cc create mode 100644 test/routing/tb_a_star_test.cc diff --git a/include/nigiri/routing/tb/tb_a_star/a_star.h b/include/nigiri/routing/tb/tb_a_star/a_star.h new file mode 100644 index 000000000..55e2ddd6a --- /dev/null +++ b/include/nigiri/routing/tb/tb_a_star/a_star.h @@ -0,0 +1,115 @@ +#pragma once +#include "nigiri/common/dial.h" +#include "nigiri/routing/tb/tb_data.h" +#include "nigiri/rt/rt_timetable.h" +#include "nigiri/routing/pareto_set.h" + +namespace nigiri::routing::tb::a_star{ + +inline int transfer_factor = 5; +unixtime_t get_time(segment_idx_t const& idx,timetable const& tt,event_type const& event,tb_data const& tbd,day_idx_t const& day_idx); +day_idx_t get_day(unixtime_t const& before,segment_idx_t const& s_idx, timetable const& tt,tb_data const& tbd); +std::pair,bool> get_neighbours(segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day) ; + +struct cost_func_t { + explicit cost_func_t() = default; + std::size_t operator ()(std::pair const& element) const { + return element.second.count(); + } +}; + +struct a_star_stats { + std::map to_map() const { + return { + {"max_transfers_reached_", max_transfers_reached_}, + {"queued_segments_", queued_segments_}, + {"segments_taken_out_",segments_taken_out_}, + {"segments_ignored_", segments_ignored_}, + {"ever_requeued_",ever_requeued_} + }; + } + + bool max_transfers_reached_ = false; + int queued_segments_ = 0; + int segments_taken_out_ = 0; + int segments_ignored_ = 0; + bool ever_requeued_ = false; +}; + +struct a_star_state { + explicit a_star_state(tb_data const& tbd) : tbd_{tbd}{ + end_reachable_.resize(tbd_.segment_transfers_.size()); + } + tb_data tbd_; + bitvec_map end_reachable_; + hash_map dist_to_dest_; +}; + +struct tb_a_star{ + + using algo_state_t = a_star_state; + using algo_stats_t = a_star_stats; + static constexpr bool kUseLowerBounds = true; + static constexpr auto kUnreachable = + std::numeric_limits::max(); + + explicit tb_a_star(std::size_t const& init_cap) : + state_(tb_data()), + queue_(1440+(init_cap-1)*transfer_factor,cost_func_t()){ + pred_ = vector_map(init_cap,segment_idx_t::invalid()); + day_idx_ = vector_map(init_cap); + transfers_ = vector_map(init_cap,std::numeric_limits::max()); + is_start_segment_ = bitvec_map(init_cap); + is_start_segment_.zero_out(); + } + + tb_a_star(timetable const& tt, + rt_timetable const*, + a_star_state& state, + bitvec const& is_dest, + std::array const&, + std::vector const& dist_to_dest, + hash_map> const&, + std::vector const& lb, + std::vector const&, + day_idx_t base, + clasz_mask_t, + bool, + bool, + bool, + transfer_time_settings); + + algo_stats_t get_stats() const { + return algo_stats_; + } + void reset_arrivals() {} + void next_start_time() { + queue_.clear(); + is_start_segment_.zero_out(); + utl::fill(pred_,segment_idx_t::invalid()); + utl::fill(transfers_,std::numeric_limits::max()); + } + void add_start(location_idx_t const l,unixtime_t const t); + segment_idx_t get_departure_segment(segment_idx_t const& s); + void execute(unixtime_t const start_time, + std::uint8_t const max_transfers, + unixtime_t const worst_time_at_dest, + profile_idx_t const profile_idx, + pareto_set& results); + void reconstruct(query const& q, journey& j); + + duration_t heuristic(segment_idx_t const& s); + + vector_map pred_; + vector_map day_idx_; + timetable tt_; + a_star_state state_; + dial, cost_func_t> queue_; + segment_idx_t end_segment_ = segment_idx_t::invalid(); + vector_map transfers_; + std::vector travel_time_lower_bound_; + bitvec_map is_start_segment_; + algo_stats_t algo_stats_; +}; + +}// namespace nigiri::routing::tb::a_star \ No newline at end of file diff --git a/include/nigiri/string_store.h b/include/nigiri/string_store.h index fa79c40c0..63b6921b3 100644 --- a/include/nigiri/string_store.h +++ b/include/nigiri/string_store.h @@ -53,6 +53,7 @@ struct string_store { cache_ = o.cache_; resolve(); } + return *this; } string_store& operator=(string_store&& o) { if (&o != this) { @@ -60,6 +61,7 @@ struct string_store { cache_ = std::move(o.cache_); resolve(); } + return *this; } template diff --git a/src/routing/tb/tb_a_star/a_star.cc b/src/routing/tb/tb_a_star/a_star.cc new file mode 100644 index 000000000..1f6d8db82 --- /dev/null +++ b/src/routing/tb/tb_a_star/a_star.cc @@ -0,0 +1,390 @@ +#pragma once + +#include "nigiri/routing/tb/tb_a_star/a_star.h" + +#include +#include + +#include "nigiri/routing/get_earliest_transport.h" +#include "nigiri/timetable.h" + +#include "nigiri/for_each_meta.h" + +namespace nigiri::routing::tb::a_star { + +tb_a_star::tb_a_star(timetable const& tt, + rt_timetable const*, + a_star_state& state, + bitvec const& is_dest, + std::array const&, + std::vector const& dist_to_dest, + hash_map> const&, + std::vector const& lb, + std::vector const&, + day_idx_t, + clasz_mask_t, + bool, + bool, + bool, + transfer_time_settings) + : pred_(state.tbd_.segment_transfers_.size(), segment_idx_t::invalid()), + day_idx_(state.tbd_.segment_transfers_.size()), + tt_{tt}, + state_{state}, + queue_{ + 1440 + (state.tbd_.segment_transfers_.size() - 1) * transfer_factor, + cost_func_t()}, + transfers_(state.tbd_.segment_transfers_.size(), + std::numeric_limits::max()), + travel_time_lower_bound_{lb}, + is_start_segment_(state.tbd_.segment_transfers_.size()){ + state_.end_reachable_.zero_out(); + is_start_segment_.zero_out(); + auto const mark_dest_segments = [&](location_idx_t const l, + duration_t const d) { + for (auto const r : tt_.location_routes_[l]) { + auto const stop_seq = tt_.route_location_seq_[r]; + for (auto i = stop_idx_t{1U}; i != stop_seq.size(); ++i) { + if (auto const stp = stop{stop_seq[i]}; + stp.location_idx() != l || !stp.out_allowed()) { + continue; + } + + for (auto const t : tt_.route_transport_ranges_[r]) { + auto const segment = state_.tbd_.transport_first_segment_[t] + i - 1; + state_.end_reachable_.set(segment, true); + + auto const it = state_.dist_to_dest_.find(segment); + if (it == end(state_.dist_to_dest_)) { + state_.dist_to_dest_.emplace_hint(it, segment, d); + } else { + it->second = std::min(it->second, d); + } + } + } + } + }; + + if (dist_to_dest.empty()) /* Destination is stop. */ { + is_dest.for_each_set_bit([&](std::size_t const i) { + auto const l = location_idx_t{i}; + mark_dest_segments(l, duration_t{0U}); + for (auto const fp : + tt_.locations_.footpaths_in_[state_.tbd_.prf_idx_][l]) { + mark_dest_segments(fp.target(), fp.duration()); + } + }); + } else /* Destination is coordinate. */ { + for (auto const [l_idx, dist] : utl::enumerate(dist_to_dest)) { + if (dist != kUnreachable) { + mark_dest_segments(location_idx_t{l_idx}, duration_t{dist}); + } + } + } +} + +std::pair,bool> get_neighbours(segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day) { + std::vector neighbours; + for(auto const transfer : tbd.segment_transfers_[s_idx]) { + if(tbd.bitfields_[transfer.traffic_days_].test(day.v_)) neighbours.push_back(transfer.to_segment_); + } + auto next_exists = s_idx+1( + (idx - tbd.transport_first_segment_[transport]+(event==event_type::kArr?1:0)).v_); + return tt.event_time({transport, day_idx}, stop_idx,event); +} + +day_idx_t get_day(unixtime_t const& before,segment_idx_t const& s_idx, timetable const& tt,tb_data const& tbd) { + for(auto day : tt.date_range_) { + auto day_idx = tt.day_idx_mam(day).first; + auto dep_time = get_time(s_idx,tt,event_type::kDep,tbd,day_idx); + if(dep_time >= before) return day_idx; + } + return day_idx_t::invalid(); + +} + +location_idx_t get_location(segment_idx_t const& s,timetable const& tt,tb_data const& tbd,event_type const& event) { + auto transport = tbd.segment_transports_[s]; + auto stop_idx = s-tbd.transport_first_segment_[transport] + (event==event_type::kArr?1:0); + auto route_idx = tt.transport_route_[transport]; + auto stop_value = tt.route_location_seq_[route_idx][stop_idx.v_]; + stop stop_place(stop_value); + return stop_place.location_idx(); +} + +duration_t tb_a_star::heuristic(segment_idx_t const& s) { + return duration_t(travel_time_lower_bound_[get_location(s,tt_,state_.tbd_,event_type::kArr).v_]); +} + +template +bool is_invalid(T v) { + auto invalid_value = T::invalid(); + return v==invalid_value; +} + +void tb_a_star::execute(unixtime_t const start_time, + std::uint8_t const max_transfers, + unixtime_t const worst_time_at_dest, + profile_idx_t const, + pareto_set& results) { + vector_map seen(state_.tbd_.segment_transfers_.size(),false); + duration_t best_cost_at_dest = duration_t::max(); + unixtime_t arrival_time_limit = std::min(worst_time_at_dest,start_time+duration_t(1441)); + is_start_segment_.for_each_set_bit([&](segment_idx_t const& s) { + day_idx_[s] = get_day(start_time,s,tt_,state_.tbd_); + if(is_invalid(day_idx_[s])) return; + auto time = get_time(s,tt_,event_type::kArr,state_.tbd_,day_idx_[s]); + if(time >= arrival_time_limit) return; + queue_.push(std::pair(s,time-start_time+heuristic(s))); + ++algo_stats_.queued_segments_; + }); + + while (true) { + if(queue_.empty()) { + if(best_cost_at_dest!=duration_t::max()) break; + return; + } + auto [p,costs] = queue_.top(); + queue_.pop(); + ++algo_stats_.segments_taken_out_; + if(costs >= best_cost_at_dest) break; + if(seen[p]) continue; + seen[p] = true; + if(state_.end_reachable_.test(p)) { + auto costs_at_dest = costs + state_.dist_to_dest_[p] - heuristic(p); + if(costs_at_dest(day_idx_[neighbour])) continue; + auto neighbour_arrival = get_time(neighbour,tt_,event_type::kArr,state_.tbd_,day_idx_[neighbour]); + if(transfer_count=max_transfers; + results.add({.legs_ = {}, + .start_time_ = start_time, + .dest_time_ = get_time(end_segment_,tt_,event_type::kArr,state_.tbd_,day_idx_[end_segment_])+state_.dist_to_dest_[end_segment_], + .dest_ = location_idx_t::invalid(), + .transfers_ = transfers_[end_segment_]}); +} + +void tb_a_star::add_start(location_idx_t const l,unixtime_t const t){ + auto const [day, mam] = tt_.day_idx_mam(t); + for (auto const r : tt_.location_routes_[l]) { + // iterate stop sequence of route, skip last stop + auto const stop_seq = tt_.route_location_seq_[r]; + for (auto i = stop_idx_t{0U}; i < stop_seq.size() - 1; ++i) { + auto const stp = stop{stop_seq[i]}; + if (!stp.in_allowed() || stp.location_idx() != l) { + continue; + } + + auto const et = get_earliest_transport( + tt_, tt_, 0U, r, i, day, mam, stp.location_idx(), + [](day_idx_t, std::int16_t) { return false; }); + if (!et.is_valid()) { + continue; + } + + auto const transport_first_segment = + state_.tbd_.transport_first_segment_[et.t_idx_]; + is_start_segment_.set(transport_first_segment+i); + transfers_[transport_first_segment+i] = 0; + } + } +} + +segment_idx_t tb_a_star::get_departure_segment(segment_idx_t const& s) { + auto p = pred_[s]; + auto prev = s; + auto invalid_segment = segment_idx_t::invalid(); + while(p!=invalid_segment && state_.tbd_.segment_transports_[p]==state_.tbd_.segment_transports_[prev]) { + prev = p; + p = pred_[p]; + } + return prev; +} + +void tb_a_star::reconstruct(query const& q, journey& j) { + if(is_invalid(end_segment_)) throw std::runtime_error("reconstruction demanded with no route found"); + auto const offset = state_.dist_to_dest_.at(end_segment_); + auto arrival_location = get_location(end_segment_,tt_,state_.tbd_,event_type::kArr); + segment_idx_t departure_segment = get_departure_segment(end_segment_); + + auto const has_offset = [&](std::vector const& offsets, + location_match_mode const match_mode, + location_idx_t const l) { + return utl::any_of(offsets, [&](routing::offset const& o) { + return matches(tt_, match_mode, o.target(), l); + }); + }; + + auto const get_fp = [&](location_idx_t const from, location_idx_t const to) { + if (from == to) { + return footpath{to, tt_.locations_.transfer_time_[from]}; + } + auto const from_fps = + tt_.locations_.footpaths_out_[state_.tbd_.prf_idx_][from]; + auto const it = utl::find_if( + from_fps, [&](footpath const& fp) { return fp.target() == to; }); + utl::verify(it != end(from_fps), + "tb reconstruct: footpath from {} to {} not found", + loc{tt_, from}, loc{tt_, to}); + return *it; + }; + + auto const get_transport_info = [&](segment_idx_t const s, + event_type const ev_type) + -> std::tuple { + auto const d = day_idx_[s]; + auto const t = state_.tbd_.segment_transports_[s]; + auto const i = static_cast( + to_idx(s - state_.tbd_.transport_first_segment_[t] + + (ev_type == event_type::kArr ? 1 : 0))); + auto const loc_seq = tt_.route_location_seq_[tt_.transport_route_[t]]; + return {{t, d}, + i, + stop{loc_seq[i]}.location_idx(), + tt_.event_time({t, d}, i, ev_type)}; + }; + + auto const get_run_leg = + [&](segment_idx_t const arrival_segment) -> journey::leg { + auto const [transport, arr_stop_idx, arr_l, arr_time] = + get_transport_info(arrival_segment, event_type::kArr); + auto const [_, dep_stop_idx, dep_l, dep_time] = + get_transport_info(departure_segment, event_type::kDep); + return { + direction::kForward, + dep_l, + arr_l, + dep_time, + arr_time, + journey::run_enter_exit{ + rt::run{ + .t_ = transport, + .stop_range_ = {static_cast(0U), + static_cast( + tt_.route_location_seq_ + [tt_.transport_route_[transport.t_idx_]] + .size())}}, + dep_stop_idx, arr_stop_idx}}; + }; + + //Last leg + if(q.dest_match_mode_ == location_match_mode::kIntermodal) { + auto const offset_it = + utl::find_if(q.destination_, [&](routing::offset const& o) { + return o.target() == arrival_location && o.duration() == offset; + }); + utl::verify(offset_it!=q.destination_.end(),"Reconstruction failed because end_segment is invalid"); + j.legs_.emplace_back(direction::kForward, arrival_location, + get_special_station(special_station::kEnd), + get_time(end_segment_, tt_, event_type::kArr, state_.tbd_, + day_idx_[end_segment_]), + j.arrival_time(), *offset_it); + j.legs_.push_back(get_run_leg(end_segment_)); + } + else { + auto arr_time = get_time(end_segment_,tt_,event_type::kArr,state_.tbd_,day_idx_[end_segment_]); + auto const handle_fp = [&](footpath const& fp) { + if (arr_time + fp.duration() != j.arrival_time() || + !has_offset(q.destination_, q.dest_match_mode_, fp.target())) { + return false; + } + j.legs_.emplace_back(direction::kForward, arrival_location, fp.target(), arr_time, + j.arrival_time(), fp); + j.legs_.push_back(get_run_leg(end_segment_)); + return true; + }; + bool found_path = handle_fp(footpath{arrival_location, duration_t{0}}); + if(!found_path) { + for (auto const fp : + tt_.locations_.footpaths_out_[state_.tbd_.prf_idx_][arrival_location]) { + if (handle_fp(fp)) { + found_path = true; + break; + } + } + } + utl::verify(found_path,"Reconstruction found no footpath to destination"); + } + j.dest_ = j.legs_.back().to_; + + //intermediary legs + segment_idx_t p = pred_[departure_segment]; + while(!is_invalid(p)) { + auto const [transport, arr_stop_idx, arr_l, arr_time] = + get_transport_info(p, event_type::kArr); + auto const fp = get_fp(arr_l, j.legs_.back().from_); + j.legs_.emplace_back(direction::kForward, arr_l, + j.legs_.back().from_, arr_time, + arr_time + fp.duration(), fp); + departure_segment = get_departure_segment(p); + j.legs_.push_back(get_run_leg(p)); + p = pred_[departure_segment]; + } + + //First leg + auto const start_time = j.start_time_; + auto const first_dep_l = j.legs_.back().from_; + auto const first_dep_time = j.legs_.back().dep_time_; + if (q.start_match_mode_ == location_match_mode::kIntermodal) { + auto const offset_it = + utl::find_if(q.start_, [&](routing::offset const& o) { + return o.target() == first_dep_l && + start_time + o.duration() <= first_dep_time; + }); + utl::verify( + offset_it != end(q.start_), + "Reconstruction failed because no start offset was found"); + j.legs_.emplace_back( + direction::kForward, get_special_station(special_station::kStart), + first_dep_l, first_dep_time - offset_it->duration(), first_dep_time, + *offset_it); + } + else { + for (auto const fp : + tt_.locations_.footpaths_in_[state_.tbd_.prf_idx_][first_dep_l]) { + if (start_time + fp.duration() <= first_dep_time && + has_offset(q.start_, q.start_match_mode_, fp.target())) { + j.legs_.emplace_back(direction::kForward, fp.target(), first_dep_l, + first_dep_time - fp.duration(), first_dep_time, fp); + break; + } + } + } + + std::ranges::reverse(j.legs_); + +} + +} // namespace nigiri::routing::tb::a_star diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc new file mode 100644 index 000000000..c65da824c --- /dev/null +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -0,0 +1,513 @@ +#pragma once +#include +#include +#include +#include "nigiri/routing/tb/preprocess.h" +#include "nigiri/routing/tb/tb_a_star/a_star.h" + +#include "nigiri/loader/gtfs/load_timetable.h" +#include "nigiri/loader/init_finish.h" +#include "gtest/gtest.h" +#include "../loader/hrd/hrd_timetable.h" +#include "nigiri/loader/hrd/load_timetable.h" +namespace nigiri::routing::tb{ + +using namespace date; +using namespace nigiri; +using namespace nigiri::routing; +using namespace nigiri::loader; +using namespace std::chrono_literals; +using namespace nigiri::test_data::hrd_timetable; + +timetable load_gtfs(auto const& files) { + timetable tt; + tt.date_range_ = interval{sys_days{2021_y / March / 1},sys_days{2021_y / March / 8}}; + loader::register_special_stations(tt); + loader::gtfs::load_timetable({}, source_idx_t{0}, files(), tt); + loader::finalize(tt); + return tt; +} + +timetable load_hrd(auto const& files) { + timetable tt; + tt.date_range_ = full_period(); + register_special_stations(tt); + hrd::load_timetable(source_idx_t{0U}, loader::hrd::hrd_5_20_26, files(), tt); + finalize(tt); + return tt; +} + +int calculate_transfer_factor(std::vector const& journeys,int index) { + double higher_bound = std::numeric_limits::max(); + double lower_bound = std::numeric_limits::min(); + for(int j=0;j((journeys[j].arrival_time() - journeys[index].arrival_time()).count())/(journeys[index].transfers_-journeys[j].transfers_); + if(journeys[index].transfers_ < journeys[j].transfers_ && factor > lower_bound) lower_bound = factor; + if(journeys[index].transfers_ > journeys[j].transfers_ && factor < higher_bound) higher_bound = factor; + } + if(higher_bound==std::numeric_limits::max()) return static_cast(lower_bound)==lower_bound? lower_bound+1 : std::ceil(lower_bound); + int higher_bound_floor = static_cast(higher_bound); + utl::verify((higher_bound_floor==higher_bound?higher_bound-1:higher_bound_floor) > lower_bound,"No Integer exists between bounds"); + return higher_bound_floor == higher_bound ? higher_bound-1 : higher_bound_floor; +} + + +void verify_pareto_set(auto const& files, std::string_view const& start,std::string_view const& stop,unixtime_t const& start_time,int expectedNumberOfJourneys) { + auto const tt = load_gtfs(files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + + query q{ + .start_time_ =start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({start, source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({stop, source_idx_t{0}}), 0_minutes, 0U}}}; + + static auto search_state = routing::search_state{}; + auto algo_state = query_state{tt, tbd}; + + search> tb_searcher{ + tt, nullptr, search_state, algo_state, q}; + + auto results = tb_searcher.execute().journeys_->els_; + + EXPECT_EQ(results.size(),expectedNumberOfJourneys); + for(int i=0;i1)a_star::transfer_factor = calculate_transfer_factor(results,i); + static auto a_star_search_state = routing::search_state{}; + auto a_star_algo_state = a_star::a_star_state{tbd}; + search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + + auto a_star = a_star_searcher.execute().journeys_->els_.front(); + EXPECT_EQ(results[i],a_star_searcher.execute().journeys_->els_.front()); + } + + search_state = routing::search_state{}; + auto raptor_algo_state = raptor_state{}; + + search> raptor_searcher{ + tt, nullptr, search_state, raptor_algo_state, q,std::nullopt}; + + results = raptor_searcher.execute().journeys_->els_; + + EXPECT_EQ(results.size(),expectedNumberOfJourneys); + for(int i=0;i1) a_star::transfer_factor = calculate_transfer_factor(results,i); + static auto a_star_search_state = routing::search_state{}; + auto a_star_algo_state = a_star::a_star_state{tbd}; + + search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + + auto found_journey = a_star_searcher.execute().journeys_->els_.front(); + found_journey.legs_.pop_back(); + EXPECT_EQ(results[i],found_journey); + } + +} + +loader::mem_dir simple_pareto_verification_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S3",2 +R1,DTA,R1,R1,"S0 -> S4",2 +R2,DTA,R2,R2,"S2 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 +R2,MON,R2_MON,R2_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,03:00:00,03:00:00,S3,3,0,0 +R1_MON,00:00:00,00:00:00,S0,0,0,0 +R1_MON,01:00:00,01:00:00,S2,1,0,0 +R1_MON,02:00:00,02:00:00,S4,2,0,0 +R2_MON,01:10:00,01:10:00,S2,0,0,0 +R2_MON,01:30:00,01:30:00,S3,1,0,0 +)"); +} +TEST(tb_a_star,pareto_verification) { + verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},2); +} + +TEST(tb_a_star,pareto_verification_with_early_start_time) { + verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/February/day{28}}+23h,2); +} + +loader::mem_dir later_pareto_verification_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S3",2 +R1,DTA,R1,R1,"S0 -> S4",2 +R2,DTA,R2,R2,"S2 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 +R2,MON,R2_MON,R2_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,01:00:00,01:00:00,S0,0,0,0 +R0_MON,02:00:00,02:00:00,S1,1,0,0 +R0_MON,04:00:00,04:00:00,S3,3,0,0 +R1_MON,01:00:00,01:00:00,S0,0,0,0 +R1_MON,02:00:00,02:00:00,S2,1,0,0 +R1_MON,03:00:00,03:00:00,S4,2,0,0 +R2_MON,02:10:00,02:10:00,S2,0,0,0 +R2_MON,02:30:00,02:30:00,S3,1,0,0 +)"); +} +TEST(tb_a_star,pareto_verification_early_start_time_same_day) { + verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},2); +} +loader::mem_dir overnight_pareto_verification_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S3",2 +R1,DTA,R1,R1,"S0 -> S4",2 +R2,DTA,R2,R2,"S2 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 +R2,MON,R2_MON,R2_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,10:00:00,10:00:00,S0,0,0,0 +R0_MON,11:00:00,11:00:00,S1,1,0,0 +R0_MON,26:00:00,26:00:00,S3,3,0,0 +R1_MON,10:00:00,10:00:00,S0,0,0,0 +R1_MON,11:00:00,11:00:00,S2,1,0,0 +R1_MON,12:00:00,12:00:00,S4,2,0,0 +R2_MON,11:10:00,11:10:00,S2,0,0,0 +R2_MON,24:30:00,24:30:00,S3,1,0,0 +)"); +} +TEST(tb_a_star,overnight_pareto_verification) { + verify_pareto_set(overnight_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}}+10h,2); +} + + +loader::mem_dir three_journeys_pareto_verification_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, +S5,S5,,,,,, +S6,S6,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S3",2 +R1,DTA,R1,R1,"S0 -> S4",2 +R2,DTA,R2,R2,"S2 -> S3",2 +R3,DTA,R2,R2,"S2 -> S3",2 +R4,DTA,R2,R2,"S2 -> S3",2 +R5,DTA,R2,R2,"S2 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 +R2,MON,R2_MON,R2_MON,1 +R3,MON,R3_MON,R3_MON,1 +R4,MON,R4_MON,R4_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,03:00:00,03:00:00,S3,3,0,0 +R1_MON,00:00:00,00:00:00,S0,0,0,0 +R1_MON,01:00:00,01:00:00,S2,1,0,0 +R1_MON,02:00:00,02:00:00,S4,2,0,0 +R2_MON,01:10:00,01:10:00,S2,0,0,0 +R2_MON,02:30:00,02:30:00,S3,1,0,0 +R3_MON,01:10:00,01:10:00,S2,0,0,0 +R3_MON,01:30:00,01:30:00,S5,1,0,0 +R3_MON,02:00:00,02:00:00,S6,2,0,0 +R4_MON,01:40:00,01:40:00,S5,0,0,0 +R4_MON,02:10:00,02:10:00,S3,0,0,0 +)"); +} +TEST(tb_a_star,three_journeys_pareto_verification) { + verify_pareto_set(three_journeys_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},3); +} + + +TEST(tb_a_star,abc_pareto_verification) { + auto const tt = load_hrd(files_abc); + auto const tbd = tb::preprocess(tt, profile_idx_t{0}); + + query q{ + .start_time_ =interval{unixtime_t{sys_days{March / 30 / 2020}} + 5h, + unixtime_t{sys_days{March / 30 / 2020}} + 6h}, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"0000001", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"0000003", source_idx_t{0}}), 0_minutes, 0U}}}; + + static auto search_state = routing::search_state{}; + auto algo_state = query_state{tt, tbd}; + + search> tb_searcher{ + tt, nullptr, search_state, algo_state, q}; + + auto results = tb_searcher.execute().journeys_->els_; + + static auto a_star_search_state = routing::search_state{}; + auto a_star_algo_state = a_star::a_star_state{tbd}; + + search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + + auto result_a_star = a_star_searcher.execute().journeys_->els_; + EXPECT_TRUE(result_a_star.front() == results.front() || result_a_star.front() == results.back()); + EXPECT_TRUE(result_a_star.back() == results.front() || result_a_star.back() == results.back()); + EXPECT_NE(result_a_star.back(),result_a_star.front()); + + search_state = routing::search_state{}; + auto raptor_algo_state = raptor_state{}; + + search> raptor_searcher{ + tt, nullptr, search_state, raptor_algo_state, q,std::nullopt}; + + results = raptor_searcher.execute().journeys_->els_; + result_a_star.front().legs_.pop_back(); + result_a_star.back().legs_.pop_back(); + EXPECT_TRUE(result_a_star.front() == results.front() || result_a_star.front() == results.back()); + EXPECT_TRUE(result_a_star.back() == results.front() || result_a_star.back() == results.back()); + EXPECT_NE(result_a_star.back(),result_a_star.front()); +} + +mem_dir earlier_stop_transfer_files() { + return mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S4",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON0,R0_MON0,0 +R0,MON,R0_MON1,R0_MON1,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON0,00:00:00,00:00:00,S0,0,0,0 +R0_MON0,01:00:00,01:00:00,S1,1,0,0 +R0_MON0,02:00:00,02:00:00,S2,2,0,0 +R0_MON0,03:00:00,03:00:00,S3,3,0,0 +R0_MON0,04:00:00,04:00:00,S1,4,0,0 +R0_MON0,05:00:00,05:00:00,S4,5,0,0 +R0_MON1,04:00:00,04:00:00,S0,0,0,0 +R0_MON1,05:00:00,05:00:00,S1,1,0,0 +R0_MON1,06:00:00,06:00:00,S2,2,0,0 +R0_MON1,07:00:00,07:00:00,S3,3,0,0 +R0_MON1,08:00:00,08:00:00,S1,4,0,0 +R0_MON1,09:00:00,09:00:00,S4,5,0,0 +)"); +} +TEST(tb_a_star,earlier_stop_transfer_verification) { + verify_pareto_set(earlier_stop_transfer_files,"S3","S2",sys_days{year{2021}/March/day{1}},1); +} + +mem_dir another_longer_files() { + return mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, +S5,S5,,,,,, +S6,S6,,,,,, +S7,S7,,,,,, +S8,S8,,,,,, +S9,S9,,,,,, +S10,S10,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S1 -> S3",2 +R1,DTA,R1,R1,"S3 -> S7",2 +R2,DTA,R2,R2,"S7 -> S4",2 +R3,DTA,R3,R3,"S4 -> S10",2 +R4,DTA,R4,R4,"S1 -> S7",2 +R5,DTA,R5,R5,"S7 -> S9",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,0 +R1,MON,R1_MON,R1_MON,0 +R2,MON,R2_MON,R2_MON,0 +R3,MON,R3_MON,R3_MON,0 +R4,MON,R4_MON,R4_MON,0 +R5,MON,R5_MON,R5_MON,0 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S1,0,0,0 +R0_MON,01:00:00,01:00:00,S2,1,0,0 +R0_MON,02:00:00,02:00:00,S3,2,0,0 +R1_MON,02:10:00,02:10:00,S3,0,0,0 +R1_MON,03:00:00,03:00:00,S7,1,0,0 +R2_MON,03:10:00,03:10:00,S7,0,0,0 +R2_MON,04:00:00,04:00:00,S8,1,0,0 +R2_MON,05:00:00,05:00:00,S5,2,0,0 +R2_MON,06:00:00,06:00:00,S4,3,0,0 +R3_MON,06:10:00,06:10:00,S4,0,0,0 +R3_MON,07:00:00,07:00:00,S10,1,0,0 +R4_MON,00:00:00,00:00:00,S1,0,0,0 +R4_MON,01:00:00,01:00:00,S6,1,0,0 +R4_MON,03:00:00,03:00:00,S7,2,0,0 +R5_MON,03:10:00,03:10:00,S7,0,0,0 +R5_MON,04:00:00,04:00:00,S9,1,0,0 +)"); +} +TEST(tb_a_star,another_longer_gtfs) { +verify_pareto_set(another_longer_files,"S1","S10",sys_days{year{2021}/March/day{1}},1); +} + +mem_dir overnight_files() { + return mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 +TUE,0,1,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S2",2 +R1,DTA,R1,R1,"S1 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,0 +R1,TUE,R1_TUE,R1_TUE,0 + + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,23:30:00,23:30:00,S0,0,0,0 +R0_MON,23:55:00,23:55:00,S1,1,0,0 +R0_MON,25:30:00,25:30:00,S2,2,0,0 +R1_TUE,00:05:00,00:05:00,S1,0,0,0 +R1_TUE,01:00:00,01:00:00,S2,1,0,0 +)"); +} +TEST(tb_a_star,overnight_pareto_verification_files) { + verify_pareto_set(overnight_files,"S0","S2",sys_days{year{2021}/March/day{1}}+23h+30min,2); +} +}// namespace nigiri::routing::tb diff --git a/test/routing/tb_a_star_test.cc b/test/routing/tb_a_star_test.cc new file mode 100644 index 000000000..ae13cd95d --- /dev/null +++ b/test/routing/tb_a_star_test.cc @@ -0,0 +1,1427 @@ +#pragma once +#include "nigiri/routing/tb/tb_a_star/a_star.h" + +#include "nigiri/routing/tb/preprocess.h" + +#include +#include + +#include "gtest/gtest.h" + +#include "nigiri/loader/gtfs/load_timetable.h" +#include "nigiri/loader/init_finish.h" + +namespace nigiri::routing::tb::a_star { + +using namespace date; +using namespace std::chrono_literals; + +timetable load_gtfs(auto const& files) { + timetable tt; + tt.date_range_ = {sys_days{2021_y / March / 1}, + sys_days{2021_y / March / 8}}; + loader::register_special_stations(tt); + loader::gtfs::load_timetable({}, source_idx_t{0}, files(), tt); + loader::finalize(tt); + return tt; +} + +void check_neighbours(tb_data const& tbd) { + for(segment_idx_t i(0);i get_segments(tb_a_star const& a_star_exec) { + std::vector result; + auto p = a_star_exec.end_segment_; + auto invalid_segment = segment_idx_t::invalid(); + while(p!=invalid_segment) { + result.push_back(p); + p = a_star_exec.pred_[p]; + } + std::ranges::reverse(result); + return result; +} + +loader::mem_dir no_transfer_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, +S4,S4,,,,,, +S5,S5,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S4",2 +R1,DTA,R1,R1,"S2 -> S5 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,02:00:00,02:00:00,S2,2,0,0 +R0_MON,03:00:00,03:00:00,S3,3,0,0 +R0_MON,04:00:00,04:00:00,S4,4,0,0 +R1_MON,02:00:10,02:10:00,S2,0,0,0 +R1_MON,02:30:00,02:30:00,S5,1,0,0 +R1_MON,02:50:00,02:50:00,S3,2,0,0 +)"); +} + +TEST(tb_a_star,a_star_no_transfer) { + auto const tt = load_gtfs(no_transfer_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; + auto end_segment = tbd.transport_first_segment_[transport_idx_t(1)]-1; + auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.transfers_[start_segment] = 0; + a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + + auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); + if (it == end(a_star_exec.state_.dist_to_dest_)) { + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + } else { + it->second = duration_t::zero(); + } + + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + std::vector expected; + expected.emplace_back(0); + expected.emplace_back(1); + expected.emplace_back(2); + expected.emplace_back(3); + EXPECT_EQ(result,expected); + EXPECT_EQ(a_star_exec.end_segment_,end_segment); + + for(auto const& segment: result) { + EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); + EXPECT_EQ(a_star_exec.transfers_[segment],0); + } + + EXPECT_EQ(journeys.els_.size(),1); + journey journey = journeys.els_[0]; + EXPECT_TRUE(journey.legs_.empty()); + EXPECT_EQ(journey.start_time_,start_time); + EXPECT_EQ(journey.transfers_,0); + EXPECT_EQ(journey.dest_,location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_,start_time+4h); + + a_star_exec.reconstruct(query{ + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S4", source_idx_t{0}}), 0_minutes, 0U}}},journey); + EXPECT_EQ(journey.legs_.size(),2); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + + for(int i=0;i S2",2 +R1,DTA,R1,R1,"S1 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,02:00:00,02:00:00,S2,2,0,0 +R1_MON,01:10:00,01:10:00,S1,0,0,0 +R1_MON,02:00:00,02:00:00,S3,1,0,0 +)"); +} + +TEST(tb_a_star,a_star_with_transfer) { + auto const tt = load_gtfs(with_transfer_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; + auto end_segment = segment_idx_t(tbd.segment_transports_.size()-1); + auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.transfers_[start_segment] = 0; + a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + + auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); + if (it == end(a_star_exec.state_.dist_to_dest_)) { + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + } else { + it->second = duration_t::zero(); + } + + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + + EXPECT_EQ(result.size(),2); + EXPECT_EQ(result[0],start_segment); + EXPECT_EQ(result[1],end_segment); + + for(auto const& segment: result) { + EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); + } + EXPECT_EQ(a_star_exec.transfers_[start_segment],0); + EXPECT_EQ(a_star_exec.transfers_[end_segment],1); + EXPECT_EQ(a_star_exec.end_segment_,end_segment); + + EXPECT_EQ(journeys.els_.size(),1); + journey journey = journeys.els_[0]; + EXPECT_TRUE(journey.legs_.empty()); + EXPECT_EQ(journey.start_time_,start_time); + EXPECT_EQ(journey.transfers_,1); + EXPECT_EQ(journey.dest_,location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_,start_time+2h); + + a_star_exec.reconstruct(query{ + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S3", source_idx_t{0}}), 0_minutes, 0U}}},journey); + EXPECT_EQ(journey.legs_.size(),4); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i S4",2 +R1,DTA,R1,R1,"S3 -> S6 -> S8",2 +R2,DTA,R2,R2,"S6 -> S7",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 +R2,MON,R2_MON,R2_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,02:00:00,02:00:00,S2,2,0,0 +R0_MON,03:00:00,03:00:00,S3,3,0,0 +R0_MON,04:00:00,04:00:00,S4,4,0,0 +R1_MON,03:10:00,03:10:00,S3,0,0,0 +R1_MON,04:00:00,04:00:00,S5,1,0,0 +R1_MON,05:00:00,05:00:00,S6,2,0,0 +R1_MON,06:00:00,06:00:00,S8,3,0,0 +R2_MON,05:10:00,05:10:00,S6,0,0,0 +R2_MON,06:00:00,06:00:00,S7,1,0,0 +)"); +} + + +TEST(tb_a_star,a_star_longer_routes) { + auto const tt = load_gtfs(longer_trips_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; + auto end_segment = segment_idx_t(tbd.segment_transports_.size()-1); + auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.transfers_[start_segment] = 0; + a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + + auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); + if (it == end(a_star_exec.state_.dist_to_dest_)) { + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + } else { + it->second = duration_t::zero(); + } + + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + std::vector expected(6); + expected[0] = start_segment; // S0 to S1 + expected[1] = start_segment+1; // S1 to S2 + expected[2] = start_segment+2; // S2 to S3 + expected[3] = tbd.transport_first_segment_[transport_idx_t(1)]; // S3 to S5 in the next transport + expected[4] = expected[3]+1; //S5 to S6 + expected[5] = end_segment; //S6 to S7 + EXPECT_EQ(expected,result); + EXPECT_EQ(a_star_exec.end_segment_,end_segment); + + for(auto const& segment: result) { + EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); + } + EXPECT_EQ(a_star_exec.transfers_[start_segment],0); + EXPECT_EQ(a_star_exec.transfers_[start_segment+1],0); + EXPECT_EQ(a_star_exec.transfers_[start_segment+2],0); + EXPECT_EQ(a_star_exec.transfers_[expected[3]],1); + EXPECT_EQ(a_star_exec.transfers_[expected[4]],1); + EXPECT_EQ(a_star_exec.transfers_[end_segment],2); + + EXPECT_EQ(journeys.els_.size(),1); + journey journey = journeys.els_[0]; + EXPECT_TRUE(journey.legs_.empty()); + EXPECT_EQ(journey.start_time_,start_time); + EXPECT_EQ(journey.transfers_,2); + EXPECT_EQ(journey.dest_,location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_,start_time+6h); + + a_star_exec.reconstruct(query{ + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S7", source_idx_t{0}}), 0_minutes, 0U}}},journey); + EXPECT_EQ(journey.legs_.size(),6); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i S1",2 +R1,DTA,R1,R1,"S1 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,TUE,R1_TUE,R1_TUE,2 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,10:00:00,10:00:00,S0,0,0,0 +R0_MON,12:00:00,12:00:00,S1,1,0,0 +R1_TUE,00:00:00,00:00:00,S1,0,0,0 +R1_TUE,08:00:00,08:00:00,S2,1,0,0 +)"); +} + +TEST(tb_a_star,overnight_travel) { + auto const tt = load_gtfs(overnight_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; + auto end_segment = segment_idx_t(tbd.segment_transports_.size()-1); + auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); + unixtime_t start_time = sys_days{year{2021}/March/day{1}}+10h; + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.transfers_[start_segment] = 0; + a_star_exec.day_idx_[start_segment] = day_idx; + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + + auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); + if (it == end(a_star_exec.state_.dist_to_dest_)) { + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + } else { + it->second = duration_t::zero(); + } + + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + + EXPECT_EQ(result.size(),2); + EXPECT_EQ(result[0],start_segment); + EXPECT_EQ(result[1],end_segment); + + EXPECT_EQ(a_star_exec.day_idx_[start_segment],day_idx); + EXPECT_EQ(a_star_exec.day_idx_[end_segment],day_idx+1); + + EXPECT_EQ(a_star_exec.transfers_[start_segment],0); + EXPECT_EQ(a_star_exec.transfers_[end_segment],1); + EXPECT_EQ(a_star_exec.end_segment_,end_segment); + + EXPECT_EQ(journeys.els_.size(),1); + journey journey = journeys.els_[0]; + EXPECT_TRUE(journey.legs_.empty()); + EXPECT_EQ(journey.start_time_,start_time); + EXPECT_EQ(journey.transfers_,1); + EXPECT_EQ(journey.dest_,location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_,start_time+22h); + + a_star_exec.reconstruct(query{ + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", source_idx_t{0}}), 0_minutes, 0U}}},journey); + EXPECT_EQ(journey.legs_.size(),4); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,10:00:00,10:00:00,S0,0,0,0 +R0_MON,12:00:00,12:00:00,S1,1,0,0 +R0_MON,24:00:00,24:00:00,S2,2,0,0 +)"); +} +TEST(tb_a_star,overnight_travel_without_transfer) { + auto const tt = load_gtfs(overnight_files_without_transfer); + auto const tbd = preprocess(tt, profile_idx_t{0}); + auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; + auto end_segment = segment_idx_t(tbd.segment_transports_.size()-1); + auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); + unixtime_t start_time = sys_days{year{2021}/March/day{1}}+10h; + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.transfers_[start_segment] = 0; + a_star_exec.day_idx_[start_segment] = day_idx; + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + + auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); + if (it == end(a_star_exec.state_.dist_to_dest_)) { + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + } else { + it->second = duration_t::zero(); + } + + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + + EXPECT_EQ(result.size(),2); + EXPECT_EQ(result[0],start_segment); + EXPECT_EQ(result[1],end_segment); + + EXPECT_EQ(a_star_exec.day_idx_[start_segment],day_idx); + EXPECT_EQ(a_star_exec.day_idx_[end_segment],day_idx); + + EXPECT_EQ(a_star_exec.transfers_[start_segment],0); + EXPECT_EQ(a_star_exec.transfers_[end_segment],0); + EXPECT_EQ(a_star_exec.end_segment_,end_segment); + + EXPECT_EQ(journeys.els_.size(),1); + journey journey = journeys.els_[0]; + EXPECT_TRUE(journey.legs_.empty()); + EXPECT_EQ(journey.start_time_,start_time); + EXPECT_EQ(journey.transfers_,0); + EXPECT_EQ(journey.dest_,location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_,start_time+14h); + + a_star_exec.reconstruct(query{ + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = { + {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", source_idx_t{0}}), 0_minutes, 0U}}},journey); + EXPECT_EQ(journey.legs_.size(),2); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i S1",2 +R1,DTA,R1,R1,"S0 -> S3",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,2 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,10:00:00,10:00:00,S0,0,0,0 +R0_MON,12:00:00,12:00:00,S1,1,0,0 +R1_MON,11:00:00,11:00:00,S0,0,0,0 +R1_MON,12:00:00,12:00:00,S2,1,0,0 +R1_MON,13:00:00,13:00:00,S3,2,0,0 +)"); +} + +TEST(tb_a_star,add_starts_two_segment) { + auto const tt = load_gtfs(two_start_segment_files); + auto const tbd = tb::preprocess(tt, profile_idx_t{0}); + unixtime_t start_time = date::sys_days{date::year{2021}/date::March/date::day{1}}+10h; + auto stop_value = tt.route_location_seq_[route_idx_t(0)][0]; + stop start_stop(stop_value); + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + + a_star_exec.add_start(start_stop.location_idx(),start_time); + + EXPECT_TRUE(a_star_exec.is_start_segment_.test(segment_idx_t{0})); + EXPECT_TRUE(a_star_exec.is_start_segment_.test(segment_idx_t{1})); + EXPECT_FALSE(a_star_exec.is_start_segment_.test(segment_idx_t{2})); + + EXPECT_EQ(a_star_exec.transfers_[segment_idx_t{0}],0); + EXPECT_EQ(a_star_exec.transfers_[segment_idx_t{1}],0); +} + +TEST(tb_a_star,a_star_two_start_segments) { + auto const tt = load_gtfs(two_start_segment_files); + auto const tbd = tb::preprocess(tt, profile_idx_t{0}); + auto end_segment = segment_idx_t(0); + unixtime_t start_time = date::sys_days{date::year{2021}/date::March/date::day{1}}+10h; + auto day_idx = tt.day_idx(date::year_month_day{date::year{2021},date::month{3},date::day{1}}); + tb_a_star a_star_exec(tbd.segment_transfers_.size()); + a_star_exec.state_.tbd_ = tbd; + a_star_exec.tt_ = tt; + a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); + a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment); + a_star_exec.state_.end_reachable_.set(end_segment+1); + a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.state_.dist_to_dest_[segment_idx_t(1)] = duration_t::zero(); + a_star_exec.state_.dist_to_dest_[segment_idx_t(0)] = duration_t(1); + auto stop_value = tt.route_location_seq_[route_idx_t(0)][0]; + stop start_stop(stop_value); + a_star_exec.add_start(start_stop.location_idx(),start_time); + pareto_set journeys; + a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + auto result = get_segments(a_star_exec); + EXPECT_EQ(result.size(),1); + EXPECT_EQ(result.front(),segment_idx_t(1)); + EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(1)],day_idx); + EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(0)],day_idx); + for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.els_.size(),1); + journey journey = result.els_[0]; + + EXPECT_EQ(journey.legs_.size(),6); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.els_.size(),1); + journey journey = result.els_[0]; + + EXPECT_EQ(journey.legs_.size(),4); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.els_.size(),1); + journey journey = result.els_[0]; + + EXPECT_EQ(journey.legs_.size(),7); + EXPECT_FALSE(journey.error_); + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); + EXPECT_EQ(journey.dest_,dest); + auto start = get_special_station(special_station::kStart); + EXPECT_EQ(journey.legs_.front().from_,start); + for(int i=0;i S1",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +)"); +} +TEST(tb_a_star,search_no_solution) { + auto const tt = load_gtfs(no_journey_segment_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}}; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_TRUE(result.els_.empty()); +} + +loader::mem_dir different_termination_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S1",2 +R1,DTA,R1,R1,"S0 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R1_MON,00:00:00,00:00:00,S0,0,0,0 +R1_MON,02:00:00,02:00:00,S2,1,0,0 +)"); +} +TEST(tb_a_star,search_different_termination) { + auto const tt = load_gtfs(different_termination_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}}; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.els_.size(),1); + EXPECT_EQ(result.els_.front().legs_.size(),2); + + EXPECT_EQ(result.els_.front().legs_.front().dep_time_,start_time); + EXPECT_EQ(result.els_.front().legs_.front().arr_time_,start_time+1h); + EXPECT_EQ(result.els_.front().legs_.back().dep_time_,start_time+1h); + EXPECT_EQ(result.els_.front().legs_.back().arr_time_,start_time+1h); + + EXPECT_EQ(result.els_.front().transfers_,0); + EXPECT_FALSE(result.els_.front().error_); + EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + EXPECT_EQ(result.els_.front().legs_.front().to_,dest); +} + + +loader::mem_dir max_cost_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, +S3,S3,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S3",2 +R1,DTA,R1,R1,"S1 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,01:00:00,01:00:00,S1,1,0,0 +R0_MON,02:00:00,02:00:00,S3,2,0,0 +R1_MON,01:10:00,01:10:00,S1,0,0,0 +R1_MON,24:00:00,24:00:00,S2,1,0,0 +)"); +} +TEST(tb_a_star,search_max_cost) { + auto const tt = load_gtfs(max_cost_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}}; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.els_.size(),1); + EXPECT_EQ(result.els_.front().legs_.size(),4); + + EXPECT_EQ(result.els_.front().legs_.front().dep_time_,start_time); + EXPECT_EQ(result.els_.front().legs_.front().arr_time_,start_time+1h); + EXPECT_EQ(result.els_.front().legs_[1].dep_time_,start_time+1h); + EXPECT_EQ(result.els_.front().legs_[1].arr_time_,start_time+1h+2min); + EXPECT_EQ(result.els_.front().legs_[2].dep_time_,start_time+1h+10min); + EXPECT_EQ(result.els_.front().legs_[2].arr_time_,start_time+24h); + EXPECT_EQ(result.els_.front().legs_.back().dep_time_,start_time+24h); + EXPECT_EQ(result.els_.front().legs_.back().arr_time_,start_time+24h); + + EXPECT_EQ(result.els_.front().transfers_,1); + EXPECT_FALSE(result.els_.front().error_); + for(int i=0;i<3;++i) { + EXPECT_EQ(result.els_.front().legs_[i].to_,result.els_.front().legs_[i+1].from_); + } + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + auto s1 = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().to_,s1); + EXPECT_EQ(result.els_.front().legs_[1].to_,s1); +} + +loader::mem_dir over_24h_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S1",2 +R1,DTA,R1,R1,"S0 -> S2 -> S1",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 +R1,MON,R1_MON,R1_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,00:00:00,00:00:00,S0,0,0,0 +R0_MON,25:00:00,25:00:00,S1,1,0,0 +R1_MON,00:00:00,00:00:00,S0,0,0,0 +R1_MON,00:10:00,00:10:00,S2,1,0,0 +R1_MON,00:20:00,00:20:00,S1,2,0,0 +)"); +} + +TEST(tb_a_star,longer_trip_then_24h) { + auto const tt = load_gtfs(over_24h_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}, + }; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.size(),1); + auto journey = result.els_.front(); + EXPECT_EQ(journey.legs_.size(),2); + + EXPECT_EQ(journey.legs_.front().dep_time_,start_time); + EXPECT_EQ(journey.legs_.front().arr_time_,start_time+20min); + EXPECT_EQ(journey.legs_.back().arr_time_,start_time+20min); + EXPECT_EQ(journey.legs_.back().dep_time_,start_time+20min); + + EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); +} + +TEST(tb_a_star,start_time_different_date_to_first_segment) { + auto const tt = load_gtfs(over_24h_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/February/day{28}}+23h; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}, + }; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.size(),1); + auto journey = result.els_.front(); + EXPECT_EQ(journey.legs_.size(),2); + + EXPECT_EQ(journey.legs_.front().dep_time_,sys_days{year{2021}/March/day{1}}); + EXPECT_EQ(journey.legs_.front().arr_time_,sys_days{year{2021}/March/day{1}}+20min); + EXPECT_EQ(journey.legs_.back().arr_time_,sys_days{year{2021}/March/day{1}}+20min); + EXPECT_EQ(journey.legs_.back().dep_time_,sys_days{year{2021}/March/day{1}}+20min); + + EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); +} + +loader::mem_dir segment_passing_midnight_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,23:30:00,23:30:00,S0,0,0,0 +R0_MON,24:30:00,24:30:00,S1,1,0,0 +R0_MON,25:00:00,25:00:00,S2,2,0,0 +)"); +} +TEST(tb_a_star,segment_passing_midnight) { + auto const tt = load_gtfs(segment_passing_midnight_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{1}}+23h+30min; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}, + .max_travel_time_ = duration_t{24h}}; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.size(),1); + auto journey = result.els_.front(); + EXPECT_EQ(journey.legs_.size(),2); + + EXPECT_EQ(journey.legs_.front().dep_time_,start_time); + EXPECT_EQ(journey.legs_.front().arr_time_,start_time+1h+30min); + EXPECT_EQ(journey.legs_.back().arr_time_,start_time+1h+30min); + EXPECT_EQ(journey.legs_.back().dep_time_,start_time+1h+30min); + + EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); +} + +loader::mem_dir transport_starts_before_start_time_files() { + return loader::mem_dir::read(R"( +# agency.txt +agency_id,agency_name,agency_url,agency_timezone +DTA,Demo Transit Authority,,Europe/London + +# stops.txt +stop_id,stop_name,stop_desc,stop_lat,stop_lon,stop_url,location_type,parent_station +S0,S0,,,,,, +S1,S1,,,,,, +S2,S2,,,,,, + +# calendar.txt +service_id,monday,tuesday,wednesday,thursday,friday,saturday,sunday,start_date,end_date +MON,1,0,0,0,0,0,0,20210301,20210307 + +# routes.txt +route_id,agency_id,route_short_name,route_long_name,route_desc,route_type +R0,DTA,R0,R0,"S0 -> S2",2 + +# trips.txt +route_id,service_id,trip_id,trip_headsign,block_id +R0,MON,R0_MON,R0_MON,1 + +# stop_times.txt +trip_id,arrival_time,departure_time,stop_id,stop_sequence,pickup_type,drop_off_type +R0_MON,23:30:00,23:30:00,S0,0,0,0 +R0_MON,24:30:00,24:30:00,S1,1,0,0 +R0_MON,25:00:00,25:00:00,S2,2,0,0 +)"); +} +TEST(tb_a_star,tranpsport_starts_before_start_time) { + auto const tt = load_gtfs(transport_starts_before_start_time_files); + auto const tbd = preprocess(tt, profile_idx_t{0}); + static auto search_state = routing::search_state{}; + auto algo_state = a_star_state{tbd}; + unixtime_t start_time = sys_days{year{2021}/March/day{2}}; + + auto constexpr src = source_idx_t{0}; + auto q = routing::query{ + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}, + .max_travel_time_ = duration_t{24h}}; + + routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + auto result = *searcher.execute().journeys_; + EXPECT_EQ(result.size(),1); + auto journey = result.els_.front(); + EXPECT_EQ(journey.legs_.size(),2); + + EXPECT_EQ(journey.legs_.front().dep_time_,start_time+30min); + EXPECT_EQ(journey.legs_.front().arr_time_,start_time+1h); + EXPECT_EQ(journey.legs_.back().arr_time_,start_time+1h); + EXPECT_EQ(journey.legs_.back().dep_time_,start_time+1h); + + EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); + + auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); + EXPECT_EQ(result.els_.front().dest_,dest); + EXPECT_EQ(result.els_.front().legs_.back().to_,dest); + EXPECT_EQ(result.els_.front().legs_.back().from_,dest); + auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]}.location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_,start); + EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); +} +} // namespace nigiri::routing::tb::a_star From 8e82fc26e4e78efffc98d3630818aa875d4b8725 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Thu, 19 Mar 2026 17:49:59 +0100 Subject: [PATCH 2/8] Formatting + changes to compile --- include/nigiri/routing/tb/tb_a_star/a_star.h | 99 +- src/routing/tb/tb_a_star/a_star.cc | 301 ++-- test/routing/tb_a_star_pareto_verification.cc | 194 ++- test/routing/tb_a_star_test.cc | 1393 +++++++++-------- 4 files changed, 1112 insertions(+), 875 deletions(-) diff --git a/include/nigiri/routing/tb/tb_a_star/a_star.h b/include/nigiri/routing/tb/tb_a_star/a_star.h index 55e2ddd6a..21e998495 100644 --- a/include/nigiri/routing/tb/tb_a_star/a_star.h +++ b/include/nigiri/routing/tb/tb_a_star/a_star.h @@ -1,32 +1,39 @@ #pragma once #include "nigiri/common/dial.h" +#include "nigiri/routing/pareto_set.h" #include "nigiri/routing/tb/tb_data.h" #include "nigiri/rt/rt_timetable.h" -#include "nigiri/routing/pareto_set.h" -namespace nigiri::routing::tb::a_star{ +namespace nigiri::routing::tb::a_star { inline int transfer_factor = 5; -unixtime_t get_time(segment_idx_t const& idx,timetable const& tt,event_type const& event,tb_data const& tbd,day_idx_t const& day_idx); -day_idx_t get_day(unixtime_t const& before,segment_idx_t const& s_idx, timetable const& tt,tb_data const& tbd); -std::pair,bool> get_neighbours(segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day) ; +unixtime_t get_time(segment_idx_t const& idx, + timetable const& tt, + event_type const& event, + tb_data const& tbd, + day_idx_t const& day_idx); +day_idx_t get_day(unixtime_t const& before, + segment_idx_t const& s_idx, + timetable const& tt, + tb_data const& tbd); +std::pair, bool> get_neighbours( + segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day); struct cost_func_t { explicit cost_func_t() = default; - std::size_t operator ()(std::pair const& element) const { + std::size_t operator()( + std::pair const& element) const { return element.second.count(); } }; struct a_star_stats { std::map to_map() const { - return { - {"max_transfers_reached_", max_transfers_reached_}, - {"queued_segments_", queued_segments_}, - {"segments_taken_out_",segments_taken_out_}, - {"segments_ignored_", segments_ignored_}, - {"ever_requeued_",ever_requeued_} - }; + return {{"max_transfers_reached_", max_transfers_reached_}, + {"queued_segments_", queued_segments_}, + {"segments_taken_out_", segments_taken_out_}, + {"segments_ignored_", segments_ignored_}, + {"ever_requeued_", ever_requeued_}}; } bool max_transfers_reached_ = false; @@ -37,7 +44,7 @@ struct a_star_stats { }; struct a_star_state { - explicit a_star_state(tb_data const& tbd) : tbd_{tbd}{ + explicit a_star_state(tb_data const& tbd) : tbd_{tbd} { end_reachable_.resize(tbd_.segment_transfers_.size()); } tb_data tbd_; @@ -45,7 +52,7 @@ struct a_star_state { hash_map dist_to_dest_; }; -struct tb_a_star{ +struct tb_a_star { using algo_state_t = a_star_state; using algo_stats_t = a_star_stats; @@ -53,43 +60,43 @@ struct tb_a_star{ static constexpr auto kUnreachable = std::numeric_limits::max(); - explicit tb_a_star(std::size_t const& init_cap) : - state_(tb_data()), - queue_(1440+(init_cap-1)*transfer_factor,cost_func_t()){ - pred_ = vector_map(init_cap,segment_idx_t::invalid()); - day_idx_ = vector_map(init_cap); - transfers_ = vector_map(init_cap,std::numeric_limits::max()); + explicit tb_a_star(std::size_t const& init_cap) + : state_(tb_data()), + queue_(1440 + (init_cap - 1) * transfer_factor, cost_func_t()) { + pred_ = vector_map(init_cap, + segment_idx_t::invalid()); + day_idx_ = vector_map(init_cap); + transfers_ = vector_map( + init_cap, std::numeric_limits::max()); is_start_segment_ = bitvec_map(init_cap); is_start_segment_.zero_out(); } tb_a_star(timetable const& tt, - rt_timetable const*, - a_star_state& state, - bitvec const& is_dest, - std::array const&, - std::vector const& dist_to_dest, - hash_map> const&, - std::vector const& lb, - std::vector const&, - day_idx_t base, - clasz_mask_t, - bool, - bool, - bool, - transfer_time_settings); + rt_timetable const*, + a_star_state& state, + bitvec const& is_dest, + std::array const&, + std::vector const& dist_to_dest, + hash_map> const&, + std::vector const& lb, + std::vector const&, + day_idx_t base, + clasz_mask_t, + bool, + bool, + bool, + transfer_time_settings); - algo_stats_t get_stats() const { - return algo_stats_; - } + algo_stats_t get_stats() const { return algo_stats_; } void reset_arrivals() {} void next_start_time() { queue_.clear(); is_start_segment_.zero_out(); - utl::fill(pred_,segment_idx_t::invalid()); - utl::fill(transfers_,std::numeric_limits::max()); + utl::fill(pred_, segment_idx_t::invalid()); + utl::fill(transfers_, std::numeric_limits::max()); } - void add_start(location_idx_t const l,unixtime_t const t); + void add_start(location_idx_t const l, unixtime_t const t); segment_idx_t get_departure_segment(segment_idx_t const& s); void execute(unixtime_t const start_time, std::uint8_t const max_transfers, @@ -100,16 +107,16 @@ struct tb_a_star{ duration_t heuristic(segment_idx_t const& s); - vector_map pred_; - vector_map day_idx_; + vector_map pred_; + vector_map day_idx_; timetable tt_; a_star_state state_; - dial, cost_func_t> queue_; + dial, cost_func_t> queue_; segment_idx_t end_segment_ = segment_idx_t::invalid(); - vector_map transfers_; + vector_map transfers_; std::vector travel_time_lower_bound_; bitvec_map is_start_segment_; algo_stats_t algo_stats_; }; -}// namespace nigiri::routing::tb::a_star \ No newline at end of file +} // namespace nigiri::routing::tb::a_star \ No newline at end of file diff --git a/src/routing/tb/tb_a_star/a_star.cc b/src/routing/tb/tb_a_star/a_star.cc index 1f6d8db82..639e12566 100644 --- a/src/routing/tb/tb_a_star/a_star.cc +++ b/src/routing/tb/tb_a_star/a_star.cc @@ -1,5 +1,3 @@ -#pragma once - #include "nigiri/routing/tb/tb_a_star/a_star.h" #include @@ -13,20 +11,20 @@ namespace nigiri::routing::tb::a_star { tb_a_star::tb_a_star(timetable const& tt, - rt_timetable const*, - a_star_state& state, - bitvec const& is_dest, - std::array const&, - std::vector const& dist_to_dest, - hash_map> const&, - std::vector const& lb, - std::vector const&, - day_idx_t, - clasz_mask_t, - bool, - bool, - bool, - transfer_time_settings) + rt_timetable const*, + a_star_state& state, + bitvec const& is_dest, + std::array const&, + std::vector const& dist_to_dest, + hash_map> const&, + std::vector const& lb, + std::vector const&, + day_idx_t, + clasz_mask_t, + bool, + bool, + bool, + transfer_time_settings) : pred_(state.tbd_.segment_transfers_.size(), segment_idx_t::invalid()), day_idx_(state.tbd_.segment_transfers_.size()), tt_{tt}, @@ -37,11 +35,11 @@ tb_a_star::tb_a_star(timetable const& tt, transfers_(state.tbd_.segment_transfers_.size(), std::numeric_limits::max()), travel_time_lower_bound_{lb}, - is_start_segment_(state.tbd_.segment_transfers_.size()){ + is_start_segment_(state.tbd_.segment_transfers_.size()) { state_.end_reachable_.zero_out(); is_start_segment_.zero_out(); auto const mark_dest_segments = [&](location_idx_t const l, - duration_t const d) { + duration_t const d) { for (auto const r : tt_.location_routes_[l]) { auto const stop_seq = tt_.route_location_seq_[r]; for (auto i = stop_idx_t{1U}; i != stop_seq.size(); ++i) { @@ -83,36 +81,54 @@ tb_a_star::tb_a_star(timetable const& tt, } } -std::pair,bool> get_neighbours(segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day) { +std::pair, bool> get_neighbours( + segment_idx_t const& s_idx, tb_data const& tbd, day_idx_t const& day) { std::vector neighbours; - for(auto const transfer : tbd.segment_transfers_[s_idx]) { - if(tbd.bitfields_[transfer.traffic_days_].test(day.v_)) neighbours.push_back(transfer.to_segment_); + for (auto const transfer : tbd.segment_transfers_[s_idx]) { + if (tbd.bitfields_[transfer.traffic_days_].test(day.v_)) + neighbours.push_back(transfer.to_segment_); } - auto next_exists = s_idx+1( - (idx - tbd.transport_first_segment_[transport]+(event==event_type::kArr?1:0)).v_); - return tt.event_time({transport, day_idx}, stop_idx,event); + auto const stop_idx = + static_cast((idx - tbd.transport_first_segment_[transport] + + (event == event_type::kArr ? 1 : 0)) + .v_); + return tt.event_time({transport, day_idx}, stop_idx, event); } -day_idx_t get_day(unixtime_t const& before,segment_idx_t const& s_idx, timetable const& tt,tb_data const& tbd) { - for(auto day : tt.date_range_) { - auto day_idx = tt.day_idx_mam(day).first; - auto dep_time = get_time(s_idx,tt,event_type::kDep,tbd,day_idx); - if(dep_time >= before) return day_idx; +day_idx_t get_day(unixtime_t const& before, + segment_idx_t const& s_idx, + timetable const& tt, + tb_data const& tbd) { + auto size = (tt.date_range_.to_ - tt.date_range_.from_).count(); + auto const starting_day_idx = tt.day_idx_mam(tt.date_range_.from_).first; + for (int i = 0; i < size; ++i) { + auto day_idx = starting_day_idx + i; + auto dep_time = get_time(s_idx, tt, event_type::kDep, tbd, day_idx); + if (dep_time >= before) return day_idx; } return day_idx_t::invalid(); - } -location_idx_t get_location(segment_idx_t const& s,timetable const& tt,tb_data const& tbd,event_type const& event) { +location_idx_t get_location(segment_idx_t const& s, + timetable const& tt, + tb_data const& tbd, + event_type const& event) { auto transport = tbd.segment_transports_[s]; - auto stop_idx = s-tbd.transport_first_segment_[transport] + (event==event_type::kArr?1:0); + auto stop_idx = s - tbd.transport_first_segment_[transport] + + (event == event_type::kArr ? 1 : 0); auto route_idx = tt.transport_route_[transport]; auto stop_value = tt.route_location_seq_[route_idx][stop_idx.v_]; stop stop_place(stop_value); @@ -120,83 +136,95 @@ location_idx_t get_location(segment_idx_t const& s,timetable const& tt,tb_data c } duration_t tb_a_star::heuristic(segment_idx_t const& s) { - return duration_t(travel_time_lower_bound_[get_location(s,tt_,state_.tbd_,event_type::kArr).v_]); + return duration_t( + travel_time_lower_bound_ + [get_location(s, tt_, state_.tbd_, event_type::kArr).v_]); } template bool is_invalid(T v) { auto invalid_value = T::invalid(); - return v==invalid_value; + return v == invalid_value; } void tb_a_star::execute(unixtime_t const start_time, - std::uint8_t const max_transfers, - unixtime_t const worst_time_at_dest, - profile_idx_t const, - pareto_set& results) { - vector_map seen(state_.tbd_.segment_transfers_.size(),false); + std::uint8_t const max_transfers, + unixtime_t const worst_time_at_dest, + profile_idx_t const, + pareto_set& results) { + vector_map seen(state_.tbd_.segment_transfers_.size(), + false); duration_t best_cost_at_dest = duration_t::max(); - unixtime_t arrival_time_limit = std::min(worst_time_at_dest,start_time+duration_t(1441)); + unixtime_t arrival_time_limit = + std::min(worst_time_at_dest, start_time + duration_t(1441)); is_start_segment_.for_each_set_bit([&](segment_idx_t const& s) { - day_idx_[s] = get_day(start_time,s,tt_,state_.tbd_); - if(is_invalid(day_idx_[s])) return; - auto time = get_time(s,tt_,event_type::kArr,state_.tbd_,day_idx_[s]); - if(time >= arrival_time_limit) return; - queue_.push(std::pair(s,time-start_time+heuristic(s))); + day_idx_[s] = get_day(start_time, s, tt_, state_.tbd_); + if (is_invalid(day_idx_[s])) return; + auto time = get_time(s, tt_, event_type::kArr, state_.tbd_, day_idx_[s]); + if (time >= arrival_time_limit) return; + queue_.push(std::pair(s, time - start_time + heuristic(s))); ++algo_stats_.queued_segments_; }); while (true) { - if(queue_.empty()) { - if(best_cost_at_dest!=duration_t::max()) break; + if (queue_.empty()) { + if (best_cost_at_dest != duration_t::max()) break; return; } - auto [p,costs] = queue_.top(); + auto [p, costs] = queue_.top(); queue_.pop(); ++algo_stats_.segments_taken_out_; - if(costs >= best_cost_at_dest) break; - if(seen[p]) continue; + if (costs >= best_cost_at_dest) break; + if (seen[p]) continue; seen[p] = true; - if(state_.end_reachable_.test(p)) { + if (state_.end_reachable_.test(p)) { auto costs_at_dest = costs + state_.dist_to_dest_[p] - heuristic(p); - if(costs_at_dest(day_idx_[neighbour])) continue; - auto neighbour_arrival = get_time(neighbour,tt_,event_type::kArr,state_.tbd_,day_idx_[neighbour]); - if(transfer_count(day_idx_[neighbour])) continue; + auto neighbour_arrival = get_time(neighbour, tt_, event_type::kArr, + state_.tbd_, day_idx_[neighbour]); + if (transfer_count < transfers_[neighbour] && + neighbour_arrival < arrival_time_limit) { transfers_[neighbour] = transfer_count; pred_[neighbour] = p; - duration_t new_costs = neighbour_arrival-start_time + duration_t(transfer_factor*transfer_count) + heuristic(neighbour); - queue_.push(std::pair(neighbour,new_costs)); - if(seen[neighbour]) algo_stats_.ever_requeued_ = true; + duration_t new_costs = neighbour_arrival - start_time + + duration_t(transfer_factor * transfer_count) + + heuristic(neighbour); + queue_.push(std::pair(neighbour, new_costs)); + if (seen[neighbour]) algo_stats_.ever_requeued_ = true; seen[neighbour] = false; ++algo_stats_.queued_segments_; - } - else { + } else { ++algo_stats_.segments_ignored_; } } } - algo_stats_.max_transfers_reached_ = transfers_[end_segment_]>=max_transfers; + algo_stats_.max_transfers_reached_ = + transfers_[end_segment_] >= max_transfers; results.add({.legs_ = {}, - .start_time_ = start_time, - .dest_time_ = get_time(end_segment_,tt_,event_type::kArr,state_.tbd_,day_idx_[end_segment_])+state_.dist_to_dest_[end_segment_], - .dest_ = location_idx_t::invalid(), - .transfers_ = transfers_[end_segment_]}); + .start_time_ = start_time, + .dest_time_ = get_time(end_segment_, tt_, event_type::kArr, + state_.tbd_, day_idx_[end_segment_]) + + state_.dist_to_dest_[end_segment_], + .dest_ = location_idx_t::invalid(), + .transfers_ = transfers_[end_segment_]}); } -void tb_a_star::add_start(location_idx_t const l,unixtime_t const t){ +void tb_a_star::add_start(location_idx_t const l, unixtime_t const t) { auto const [day, mam] = tt_.day_idx_mam(t); for (auto const r : tt_.location_routes_[l]) { // iterate stop sequence of route, skip last stop @@ -216,8 +244,8 @@ void tb_a_star::add_start(location_idx_t const l,unixtime_t const t){ auto const transport_first_segment = state_.tbd_.transport_first_segment_[et.t_idx_]; - is_start_segment_.set(transport_first_segment+i); - transfers_[transport_first_segment+i] = 0; + is_start_segment_.set(transport_first_segment + i); + transfers_[transport_first_segment + i] = 0; } } } @@ -226,7 +254,8 @@ segment_idx_t tb_a_star::get_departure_segment(segment_idx_t const& s) { auto p = pred_[s]; auto prev = s; auto invalid_segment = segment_idx_t::invalid(); - while(p!=invalid_segment && state_.tbd_.segment_transports_[p]==state_.tbd_.segment_transports_[prev]) { + while (p != invalid_segment && state_.tbd_.segment_transports_[p] == + state_.tbd_.segment_transports_[prev]) { prev = p; p = pred_[p]; } @@ -234,9 +263,11 @@ segment_idx_t tb_a_star::get_departure_segment(segment_idx_t const& s) { } void tb_a_star::reconstruct(query const& q, journey& j) { - if(is_invalid(end_segment_)) throw std::runtime_error("reconstruction demanded with no route found"); + if (is_invalid(end_segment_)) + throw std::runtime_error("reconstruction demanded with no route found"); auto const offset = state_.dist_to_dest_.at(end_segment_); - auto arrival_location = get_location(end_segment_,tt_,state_.tbd_,event_type::kArr); + auto arrival_location = + get_location(end_segment_, tt_, state_.tbd_, event_type::kArr); segment_idx_t departure_segment = get_departure_segment(end_segment_); auto const has_offset = [&](std::vector const& offsets, @@ -278,82 +309,82 @@ void tb_a_star::reconstruct(query const& q, journey& j) { auto const get_run_leg = [&](segment_idx_t const arrival_segment) -> journey::leg { - auto const [transport, arr_stop_idx, arr_l, arr_time] = - get_transport_info(arrival_segment, event_type::kArr); - auto const [_, dep_stop_idx, dep_l, dep_time] = - get_transport_info(departure_segment, event_type::kDep); - return { - direction::kForward, - dep_l, - arr_l, - dep_time, - arr_time, - journey::run_enter_exit{ + auto const [transport, arr_stop_idx, arr_l, arr_time] = + get_transport_info(arrival_segment, event_type::kArr); + auto const [_, dep_stop_idx, dep_l, dep_time] = + get_transport_info(departure_segment, event_type::kDep); + return { + direction::kForward, + dep_l, + arr_l, + dep_time, + arr_time, + journey::run_enter_exit{ rt::run{ - .t_ = transport, - .stop_range_ = {static_cast(0U), - static_cast( - tt_.route_location_seq_ - [tt_.transport_route_[transport.t_idx_]] - .size())}}, - dep_stop_idx, arr_stop_idx}}; + .t_ = transport, + .stop_range_ = {static_cast(0U), + static_cast( + tt_.route_location_seq_ + [tt_.transport_route_[transport.t_idx_]] + .size())}}, + dep_stop_idx, arr_stop_idx}}; }; - //Last leg - if(q.dest_match_mode_ == location_match_mode::kIntermodal) { + // Last leg + if (q.dest_match_mode_ == location_match_mode::kIntermodal) { auto const offset_it = - utl::find_if(q.destination_, [&](routing::offset const& o) { - return o.target() == arrival_location && o.duration() == offset; - }); - utl::verify(offset_it!=q.destination_.end(),"Reconstruction failed because end_segment is invalid"); + utl::find_if(q.destination_, [&](routing::offset const& o) { + return o.target() == arrival_location && o.duration() == offset; + }); + utl::verify(offset_it != q.destination_.end(), + "Reconstruction failed because end_segment is invalid"); j.legs_.emplace_back(direction::kForward, arrival_location, - get_special_station(special_station::kEnd), - get_time(end_segment_, tt_, event_type::kArr, state_.tbd_, - day_idx_[end_segment_]), - j.arrival_time(), *offset_it); + get_special_station(special_station::kEnd), + get_time(end_segment_, tt_, event_type::kArr, + state_.tbd_, day_idx_[end_segment_]), + j.arrival_time(), *offset_it); j.legs_.push_back(get_run_leg(end_segment_)); - } - else { - auto arr_time = get_time(end_segment_,tt_,event_type::kArr,state_.tbd_,day_idx_[end_segment_]); + } else { + auto arr_time = get_time(end_segment_, tt_, event_type::kArr, state_.tbd_, + day_idx_[end_segment_]); auto const handle_fp = [&](footpath const& fp) { if (arr_time + fp.duration() != j.arrival_time() || !has_offset(q.destination_, q.dest_match_mode_, fp.target())) { return false; - } - j.legs_.emplace_back(direction::kForward, arrival_location, fp.target(), arr_time, - j.arrival_time(), fp); + } + j.legs_.emplace_back(direction::kForward, arrival_location, fp.target(), + arr_time, j.arrival_time(), fp); j.legs_.push_back(get_run_leg(end_segment_)); return true; }; bool found_path = handle_fp(footpath{arrival_location, duration_t{0}}); - if(!found_path) { - for (auto const fp : - tt_.locations_.footpaths_out_[state_.tbd_.prf_idx_][arrival_location]) { + if (!found_path) { + for (auto const fp : tt_.locations_.footpaths_out_[state_.tbd_.prf_idx_] + [arrival_location]) { if (handle_fp(fp)) { found_path = true; break; } } } - utl::verify(found_path,"Reconstruction found no footpath to destination"); + utl::verify(found_path, "Reconstruction found no footpath to destination"); } j.dest_ = j.legs_.back().to_; - //intermediary legs + // intermediary legs segment_idx_t p = pred_[departure_segment]; - while(!is_invalid(p)) { + while (!is_invalid(p)) { auto const [transport, arr_stop_idx, arr_l, arr_time] = get_transport_info(p, event_type::kArr); auto const fp = get_fp(arr_l, j.legs_.back().from_); - j.legs_.emplace_back(direction::kForward, arr_l, - j.legs_.back().from_, arr_time, - arr_time + fp.duration(), fp); + j.legs_.emplace_back(direction::kForward, arr_l, j.legs_.back().from_, + arr_time, arr_time + fp.duration(), fp); departure_segment = get_departure_segment(p); j.legs_.push_back(get_run_leg(p)); p = pred_[departure_segment]; } - //First leg + // First leg auto const start_time = j.start_time_; auto const first_dep_l = j.legs_.back().from_; auto const first_dep_time = j.legs_.back().dep_time_; @@ -363,28 +394,26 @@ void tb_a_star::reconstruct(query const& q, journey& j) { return o.target() == first_dep_l && start_time + o.duration() <= first_dep_time; }); - utl::verify( - offset_it != end(q.start_), - "Reconstruction failed because no start offset was found"); - j.legs_.emplace_back( - direction::kForward, get_special_station(special_station::kStart), - first_dep_l, first_dep_time - offset_it->duration(), first_dep_time, - *offset_it); - } - else { + utl::verify(offset_it != end(q.start_), + "Reconstruction failed because no start offset was found"); + j.legs_.emplace_back(direction::kForward, + get_special_station(special_station::kStart), + first_dep_l, first_dep_time - offset_it->duration(), + first_dep_time, *offset_it); + } else { for (auto const fp : tt_.locations_.footpaths_in_[state_.tbd_.prf_idx_][first_dep_l]) { if (start_time + fp.duration() <= first_dep_time && has_offset(q.start_, q.start_match_mode_, fp.target())) { j.legs_.emplace_back(direction::kForward, fp.target(), first_dep_l, - first_dep_time - fp.duration(), first_dep_time, fp); + first_dep_time - fp.duration(), first_dep_time, + fp); break; } } } std::ranges::reverse(j.legs_); - } } // namespace nigiri::routing::tb::a_star diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc index c65da824c..edf0b3211 100644 --- a/test/routing/tb_a_star_pareto_verification.cc +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -1,4 +1,3 @@ -#pragma once #include #include #include @@ -6,11 +5,11 @@ #include "nigiri/routing/tb/tb_a_star/a_star.h" #include "nigiri/loader/gtfs/load_timetable.h" +#include "nigiri/loader/hrd/load_timetable.h" #include "nigiri/loader/init_finish.h" -#include "gtest/gtest.h" #include "../loader/hrd/hrd_timetable.h" -#include "nigiri/loader/hrd/load_timetable.h" -namespace nigiri::routing::tb{ +#include "gtest/gtest.h" +namespace nigiri::routing::tb { using namespace date; using namespace nigiri; @@ -21,7 +20,8 @@ using namespace nigiri::test_data::hrd_timetable; timetable load_gtfs(auto const& files) { timetable tt; - tt.date_range_ = interval{sys_days{2021_y / March / 1},sys_days{2021_y / March / 8}}; + tt.date_range_ = interval{sys_days{2021_y / March / 1}, + sys_days{2021_y / March / 8}}; loader::register_special_stations(tt); loader::gtfs::load_timetable({}, source_idx_t{0}, files(), tt); loader::finalize(tt); @@ -37,76 +37,100 @@ timetable load_hrd(auto const& files) { return tt; } -int calculate_transfer_factor(std::vector const& journeys,int index) { +int calculate_transfer_factor(std::vector const& journeys, int index) { double higher_bound = std::numeric_limits::max(); double lower_bound = std::numeric_limits::min(); - for(int j=0;j((journeys[j].arrival_time() - journeys[index].arrival_time()).count())/(journeys[index].transfers_-journeys[j].transfers_); - if(journeys[index].transfers_ < journeys[j].transfers_ && factor > lower_bound) lower_bound = factor; - if(journeys[index].transfers_ > journeys[j].transfers_ && factor < higher_bound) higher_bound = factor; + for (int j = 0; j < journeys.size(); ++j) { + if (j == index) continue; + utl::verify(journeys[index].transfers_ != journeys[j].transfers_, + "Two travels with same amount of transfers"); + auto factor = static_cast((journeys[j].arrival_time() - + journeys[index].arrival_time()) + .count()) / + (journeys[index].transfers_ - journeys[j].transfers_); + if (journeys[index].transfers_ < journeys[j].transfers_ && + factor > lower_bound) + lower_bound = factor; + if (journeys[index].transfers_ > journeys[j].transfers_ && + factor < higher_bound) + higher_bound = factor; } - if(higher_bound==std::numeric_limits::max()) return static_cast(lower_bound)==lower_bound? lower_bound+1 : std::ceil(lower_bound); + if (higher_bound == std::numeric_limits::max()) + return static_cast(lower_bound) == lower_bound + ? lower_bound + 1 + : std::ceil(lower_bound); int higher_bound_floor = static_cast(higher_bound); - utl::verify((higher_bound_floor==higher_bound?higher_bound-1:higher_bound_floor) > lower_bound,"No Integer exists between bounds"); - return higher_bound_floor == higher_bound ? higher_bound-1 : higher_bound_floor; + utl::verify( + (higher_bound_floor == higher_bound ? higher_bound - 1 + : higher_bound_floor) > lower_bound, + "No Integer exists between bounds"); + return higher_bound_floor == higher_bound ? higher_bound - 1 + : higher_bound_floor; } - -void verify_pareto_set(auto const& files, std::string_view const& start,std::string_view const& stop,unixtime_t const& start_time,int expectedNumberOfJourneys) { +void verify_pareto_set(auto const& files, + std::string_view const& start, + std::string_view const& stop, + unixtime_t const& start_time, + int expectedNumberOfJourneys) { auto const tt = load_gtfs(files); auto const tbd = preprocess(tt, profile_idx_t{0}); - query q{ - .start_time_ =start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({start, source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({stop, source_idx_t{0}}), 0_minutes, 0U}}}; + query q{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {start, source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({stop, source_idx_t{0}}), + 0_minutes, 0U}}}; static auto search_state = routing::search_state{}; auto algo_state = query_state{tt, tbd}; search> tb_searcher{ - tt, nullptr, search_state, algo_state, q}; + tt, nullptr, search_state, algo_state, q}; auto results = tb_searcher.execute().journeys_->els_; - EXPECT_EQ(results.size(),expectedNumberOfJourneys); - for(int i=0;i1)a_star::transfer_factor = calculate_transfer_factor(results,i); + EXPECT_EQ(results.size(), expectedNumberOfJourneys); + for (int i = 0; i < results.size(); ++i) { + if (results.size() > 1) + a_star::transfer_factor = calculate_transfer_factor(results, i); static auto a_star_search_state = routing::search_state{}; auto a_star_algo_state = a_star::a_star_state{tbd}; - search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + search a_star_searcher{ + tt, nullptr, a_star_search_state, a_star_algo_state, q}; auto a_star = a_star_searcher.execute().journeys_->els_.front(); - EXPECT_EQ(results[i],a_star_searcher.execute().journeys_->els_.front()); + EXPECT_EQ(results[i], a_star_searcher.execute().journeys_->els_.front()); } search_state = routing::search_state{}; auto raptor_algo_state = raptor_state{}; - search> raptor_searcher{ - tt, nullptr, search_state, raptor_algo_state, q,std::nullopt}; + search> + raptor_searcher{tt, nullptr, search_state, raptor_algo_state, + q, std::nullopt}; results = raptor_searcher.execute().journeys_->els_; - EXPECT_EQ(results.size(),expectedNumberOfJourneys); - for(int i=0;i1) a_star::transfer_factor = calculate_transfer_factor(results,i); + EXPECT_EQ(results.size(), expectedNumberOfJourneys); + for (int i = 0; i < results.size(); ++i) { + if (results.size() > 1) + a_star::transfer_factor = calculate_transfer_factor(results, i); static auto a_star_search_state = routing::search_state{}; auto a_star_algo_state = a_star::a_star_state{tbd}; - search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + search a_star_searcher{ + tt, nullptr, a_star_search_state, a_star_algo_state, q}; auto found_journey = a_star_searcher.execute().journeys_->els_.front(); found_journey.legs_.pop_back(); - EXPECT_EQ(results[i],found_journey); + EXPECT_EQ(results[i], found_journey); } - } loader::mem_dir simple_pareto_verification_files() { @@ -151,12 +175,14 @@ R2_MON,01:10:00,01:10:00,S2,0,0,0 R2_MON,01:30:00,01:30:00,S3,1,0,0 )"); } -TEST(tb_a_star,pareto_verification) { - verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},2); +TEST(tb_a_star, pareto_verification) { + verify_pareto_set(simple_pareto_verification_files, "S0", "S3", + sys_days{year{2021} / March / day{1}}, 2); } -TEST(tb_a_star,pareto_verification_with_early_start_time) { - verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/February/day{28}}+23h,2); +TEST(tb_a_star, pareto_verification_with_early_start_time) { + verify_pareto_set(simple_pareto_verification_files, "S0", "S3", + sys_days{year{2021} / February / day{28}} + 23h, 2); } loader::mem_dir later_pareto_verification_files() { @@ -201,8 +227,9 @@ R2_MON,02:10:00,02:10:00,S2,0,0,0 R2_MON,02:30:00,02:30:00,S3,1,0,0 )"); } -TEST(tb_a_star,pareto_verification_early_start_time_same_day) { - verify_pareto_set(simple_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},2); +TEST(tb_a_star, pareto_verification_early_start_time_same_day) { + verify_pareto_set(simple_pareto_verification_files, "S0", "S3", + sys_days{year{2021} / March / day{1}}, 2); } loader::mem_dir overnight_pareto_verification_files() { return loader::mem_dir::read(R"( @@ -247,11 +274,11 @@ R2_MON,11:10:00,11:10:00,S2,0,0,0 R2_MON,24:30:00,24:30:00,S3,1,0,0 )"); } -TEST(tb_a_star,overnight_pareto_verification) { - verify_pareto_set(overnight_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}}+10h,2); +TEST(tb_a_star, overnight_pareto_verification) { + verify_pareto_set(overnight_pareto_verification_files, "S0", "S3", + sys_days{year{2021} / March / day{1}} + 10h, 2); } - loader::mem_dir three_journeys_pareto_verification_files() { return loader::mem_dir::read(R"( # agency.txt @@ -307,55 +334,63 @@ R4_MON,01:40:00,01:40:00,S5,0,0,0 R4_MON,02:10:00,02:10:00,S3,0,0,0 )"); } -TEST(tb_a_star,three_journeys_pareto_verification) { - verify_pareto_set(three_journeys_pareto_verification_files,"S0","S3",sys_days{year{2021}/March/day{1}},3); +TEST(tb_a_star, three_journeys_pareto_verification) { + verify_pareto_set(three_journeys_pareto_verification_files, "S0", "S3", + sys_days{year{2021} / March / day{1}}, 3); } - -TEST(tb_a_star,abc_pareto_verification) { +TEST(tb_a_star, abc_pareto_verification) { auto const tt = load_hrd(files_abc); auto const tbd = tb::preprocess(tt, profile_idx_t{0}); - query q{ - .start_time_ =interval{unixtime_t{sys_days{March / 30 / 2020}} + 5h, - unixtime_t{sys_days{March / 30 / 2020}} + 6h}, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"0000001", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"0000003", source_idx_t{0}}), 0_minutes, 0U}}}; + query q{.start_time_ = interval{unixtime_t{sys_days{March / 30 / 2020}} + 5h, + unixtime_t{sys_days{March / 30 / 2020}} + 6h}, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"0000001", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"0000003", source_idx_t{0}}), + 0_minutes, 0U}}}; static auto search_state = routing::search_state{}; auto algo_state = query_state{tt, tbd}; search> tb_searcher{ - tt, nullptr, search_state, algo_state, q}; + tt, nullptr, search_state, algo_state, q}; auto results = tb_searcher.execute().journeys_->els_; static auto a_star_search_state = routing::search_state{}; auto a_star_algo_state = a_star::a_star_state{tbd}; - search a_star_searcher{tt,nullptr,a_star_search_state,a_star_algo_state,q}; + search a_star_searcher{ + tt, nullptr, a_star_search_state, a_star_algo_state, q}; auto result_a_star = a_star_searcher.execute().journeys_->els_; - EXPECT_TRUE(result_a_star.front() == results.front() || result_a_star.front() == results.back()); - EXPECT_TRUE(result_a_star.back() == results.front() || result_a_star.back() == results.back()); - EXPECT_NE(result_a_star.back(),result_a_star.front()); + EXPECT_TRUE(result_a_star.front() == results.front() || + result_a_star.front() == results.back()); + EXPECT_TRUE(result_a_star.back() == results.front() || + result_a_star.back() == results.back()); + EXPECT_NE(result_a_star.back(), result_a_star.front()); search_state = routing::search_state{}; auto raptor_algo_state = raptor_state{}; - search> raptor_searcher{ - tt, nullptr, search_state, raptor_algo_state, q,std::nullopt}; + search> + raptor_searcher{tt, nullptr, search_state, raptor_algo_state, + q, std::nullopt}; results = raptor_searcher.execute().journeys_->els_; result_a_star.front().legs_.pop_back(); result_a_star.back().legs_.pop_back(); - EXPECT_TRUE(result_a_star.front() == results.front() || result_a_star.front() == results.back()); - EXPECT_TRUE(result_a_star.back() == results.front() || result_a_star.back() == results.back()); - EXPECT_NE(result_a_star.back(),result_a_star.front()); + EXPECT_TRUE(result_a_star.front() == results.front() || + result_a_star.front() == results.back()); + EXPECT_TRUE(result_a_star.back() == results.front() || + result_a_star.back() == results.back()); + EXPECT_NE(result_a_star.back(), result_a_star.front()); } mem_dir earlier_stop_transfer_files() { @@ -401,8 +436,9 @@ R0_MON1,08:00:00,08:00:00,S1,4,0,0 R0_MON1,09:00:00,09:00:00,S4,5,0,0 )"); } -TEST(tb_a_star,earlier_stop_transfer_verification) { - verify_pareto_set(earlier_stop_transfer_files,"S3","S2",sys_days{year{2021}/March/day{1}},1); +TEST(tb_a_star, earlier_stop_transfer_verification) { + verify_pareto_set(earlier_stop_transfer_files, "S3", "S2", + sys_days{year{2021} / March / day{1}}, 1); } mem_dir another_longer_files() { @@ -466,8 +502,9 @@ R5_MON,03:10:00,03:10:00,S7,0,0,0 R5_MON,04:00:00,04:00:00,S9,1,0,0 )"); } -TEST(tb_a_star,another_longer_gtfs) { -verify_pareto_set(another_longer_files,"S1","S10",sys_days{year{2021}/March/day{1}},1); +TEST(tb_a_star, another_longer_gtfs) { + verify_pareto_set(another_longer_files, "S1", "S10", + sys_days{year{2021} / March / day{1}}, 1); } mem_dir overnight_files() { @@ -507,7 +544,8 @@ R1_TUE,00:05:00,00:05:00,S1,0,0,0 R1_TUE,01:00:00,01:00:00,S2,1,0,0 )"); } -TEST(tb_a_star,overnight_pareto_verification_files) { - verify_pareto_set(overnight_files,"S0","S2",sys_days{year{2021}/March/day{1}}+23h+30min,2); +TEST(tb_a_star, overnight_pareto_verification_files) { + verify_pareto_set(overnight_files, "S0", "S2", + sys_days{year{2021} / March / day{1}} + 23h + 30min, 2); } -}// namespace nigiri::routing::tb +} // namespace nigiri::routing::tb diff --git a/test/routing/tb_a_star_test.cc b/test/routing/tb_a_star_test.cc index ae13cd95d..ddd0207b7 100644 --- a/test/routing/tb_a_star_test.cc +++ b/test/routing/tb_a_star_test.cc @@ -1,4 +1,3 @@ -#pragma once #include "nigiri/routing/tb/tb_a_star/a_star.h" #include "nigiri/routing/tb/preprocess.h" @@ -18,8 +17,7 @@ using namespace std::chrono_literals; timetable load_gtfs(auto const& files) { timetable tt; - tt.date_range_ = {sys_days{2021_y / March / 1}, - sys_days{2021_y / March / 8}}; + tt.date_range_ = {sys_days{2021_y / March / 1}, sys_days{2021_y / March / 8}}; loader::register_special_stations(tt); loader::gtfs::load_timetable({}, source_idx_t{0}, files(), tt); loader::finalize(tt); @@ -27,19 +25,22 @@ timetable load_gtfs(auto const& files) { } void check_neighbours(tb_data const& tbd) { - for(segment_idx_t i(0);i get_segments(tb_a_star const& a_star_exec) { std::vector result; auto p = a_star_exec.end_segment_; auto invalid_segment = segment_idx_t::invalid(); - while(p!=invalid_segment) { + while (p != invalid_segment) { result.push_back(p); p = a_star_exec.pred_[p]; } @@ -98,85 +99,98 @@ R1_MON,02:50:00,02:50:00,S3,2,0,0 )"); } -TEST(tb_a_star,a_star_no_transfer) { +TEST(tb_a_star, a_star_no_transfer) { auto const tt = load_gtfs(no_transfer_files); auto const tbd = preprocess(tt, profile_idx_t{0}); auto start_segment = tbd.transport_first_segment_[transport_idx_t(0)]; - auto end_segment = tbd.transport_first_segment_[transport_idx_t(1)]-1; - auto day_idx = tt.day_idx(year_month_day{year{2021},March,day{1}}); - unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + auto end_segment = tbd.transport_first_segment_[transport_idx_t(1)] - 1; + auto day_idx = tt.day_idx(year_month_day{year{2021}, March, day{1}}); + unixtime_t start_time = sys_days{year{2021} / March / day{1}}; tb_a_star a_star_exec(tbd.segment_transfers_.size()); a_star_exec.state_.tbd_ = tbd; a_star_exec.tt_ = tt; - a_star_exec.travel_time_lower_bound_ = std::vector(tt.n_locations(),0); - a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); + a_star_exec.queue_.push(std::pair(start_segment, duration_t(0))); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); a_star_exec.transfers_[start_segment] = 0; - a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.day_idx_[start_segment] = + day_idx; // get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); if (it == end(a_star_exec.state_.dist_to_dest_)) { - a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, + duration_t::zero()); } else { it->second = duration_t::zero(); } pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); std::vector expected; expected.emplace_back(0); expected.emplace_back(1); expected.emplace_back(2); expected.emplace_back(3); - EXPECT_EQ(result,expected); - EXPECT_EQ(a_star_exec.end_segment_,end_segment); + EXPECT_EQ(result, expected); + EXPECT_EQ(a_star_exec.end_segment_, end_segment); - for(auto const& segment: result) { - EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); - EXPECT_EQ(a_star_exec.transfers_[segment],0); + for (auto const& segment : result) { + EXPECT_EQ(a_star_exec.day_idx_[segment], day_idx); + EXPECT_EQ(a_star_exec.transfers_[segment], 0); } - EXPECT_EQ(journeys.els_.size(),1); + EXPECT_EQ(journeys.els_.size(), 1); journey journey = journeys.els_[0]; EXPECT_TRUE(journey.legs_.empty()); - EXPECT_EQ(journey.start_time_,start_time); - EXPECT_EQ(journey.transfers_,0); - EXPECT_EQ(journey.dest_,location_idx_t::invalid()); - EXPECT_EQ(journey.dest_time_,start_time+4h); - - a_star_exec.reconstruct(query{ - .start_time_ = start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S4", source_idx_t{0}}), 0_minutes, 0U}}},journey); - EXPECT_EQ(journey.legs_.size(),2); + EXPECT_EQ(journey.start_time_, start_time); + EXPECT_EQ(journey.transfers_, 0); + EXPECT_EQ(journey.dest_, location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_, start_time + 4h); + + a_star_exec.reconstruct( + query{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"S0", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"S4", source_idx_t{0}}), + 0_minutes, 0U}}}, + journey); + EXPECT_EQ(journey.legs_.size(), 2); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - - for(int i=0;i(tt.n_locations(),0); - a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); + a_star_exec.queue_.push(std::pair(start_segment, duration_t(0))); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); a_star_exec.transfers_[start_segment] = 0; - a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.day_idx_[start_segment] = + day_idx; // get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); if (it == end(a_star_exec.state_.dist_to_dest_)) { - a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, + duration_t::zero()); } else { it->second = duration_t::zero(); } pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); - EXPECT_EQ(result.size(),2); - EXPECT_EQ(result[0],start_segment); - EXPECT_EQ(result[1],end_segment); + EXPECT_EQ(result.size(), 2); + EXPECT_EQ(result[0], start_segment); + EXPECT_EQ(result[1], end_segment); - for(auto const& segment: result) { - EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); + for (auto const& segment : result) { + EXPECT_EQ(a_star_exec.day_idx_[segment], day_idx); } - EXPECT_EQ(a_star_exec.transfers_[start_segment],0); - EXPECT_EQ(a_star_exec.transfers_[end_segment],1); - EXPECT_EQ(a_star_exec.end_segment_,end_segment); + EXPECT_EQ(a_star_exec.transfers_[start_segment], 0); + EXPECT_EQ(a_star_exec.transfers_[end_segment], 1); + EXPECT_EQ(a_star_exec.end_segment_, end_segment); - EXPECT_EQ(journeys.els_.size(),1); + EXPECT_EQ(journeys.els_.size(), 1); journey journey = journeys.els_[0]; EXPECT_TRUE(journey.legs_.empty()); - EXPECT_EQ(journey.start_time_,start_time); - EXPECT_EQ(journey.transfers_,1); - EXPECT_EQ(journey.dest_,location_idx_t::invalid()); - EXPECT_EQ(journey.dest_time_,start_time+2h); - - a_star_exec.reconstruct(query{ - .start_time_ = start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S3", source_idx_t{0}}), 0_minutes, 0U}}},journey); - EXPECT_EQ(journey.legs_.size(),4); + EXPECT_EQ(journey.start_time_, start_time); + EXPECT_EQ(journey.transfers_, 1); + EXPECT_EQ(journey.dest_, location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_, start_time + 2h); + + a_star_exec.reconstruct( + query{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"S0", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"S3", source_idx_t{0}}), + 0_minutes, 0U}}}, + journey); + EXPECT_EQ(journey.legs_.size(), 4); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i(tt.n_locations(),0); - a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); + a_star_exec.queue_.push(std::pair(start_segment, duration_t(0))); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); a_star_exec.transfers_[start_segment] = 0; - a_star_exec.day_idx_[start_segment] = day_idx;//get_arrival_day(start_time,start_segment,tt,tbd); - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.day_idx_[start_segment] = + day_idx; // get_arrival_day(start_time,start_segment,tt,tbd); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); if (it == end(a_star_exec.state_.dist_to_dest_)) { - a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, + duration_t::zero()); } else { it->second = duration_t::zero(); } pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); std::vector expected(6); - expected[0] = start_segment; // S0 to S1 - expected[1] = start_segment+1; // S1 to S2 - expected[2] = start_segment+2; // S2 to S3 - expected[3] = tbd.transport_first_segment_[transport_idx_t(1)]; // S3 to S5 in the next transport - expected[4] = expected[3]+1; //S5 to S6 - expected[5] = end_segment; //S6 to S7 - EXPECT_EQ(expected,result); - EXPECT_EQ(a_star_exec.end_segment_,end_segment); - - for(auto const& segment: result) { - EXPECT_EQ(a_star_exec.day_idx_[segment],day_idx); + expected[0] = start_segment; // S0 to S1 + expected[1] = start_segment + 1; // S1 to S2 + expected[2] = start_segment + 2; // S2 to S3 + expected[3] = tbd.transport_first_segment_[transport_idx_t( + 1)]; // S3 to S5 in the next transport + expected[4] = expected[3] + 1; // S5 to S6 + expected[5] = end_segment; // S6 to S7 + EXPECT_EQ(expected, result); + EXPECT_EQ(a_star_exec.end_segment_, end_segment); + + for (auto const& segment : result) { + EXPECT_EQ(a_star_exec.day_idx_[segment], day_idx); } - EXPECT_EQ(a_star_exec.transfers_[start_segment],0); - EXPECT_EQ(a_star_exec.transfers_[start_segment+1],0); - EXPECT_EQ(a_star_exec.transfers_[start_segment+2],0); - EXPECT_EQ(a_star_exec.transfers_[expected[3]],1); - EXPECT_EQ(a_star_exec.transfers_[expected[4]],1); - EXPECT_EQ(a_star_exec.transfers_[end_segment],2); - - EXPECT_EQ(journeys.els_.size(),1); + EXPECT_EQ(a_star_exec.transfers_[start_segment], 0); + EXPECT_EQ(a_star_exec.transfers_[start_segment + 1], 0); + EXPECT_EQ(a_star_exec.transfers_[start_segment + 2], 0); + EXPECT_EQ(a_star_exec.transfers_[expected[3]], 1); + EXPECT_EQ(a_star_exec.transfers_[expected[4]], 1); + EXPECT_EQ(a_star_exec.transfers_[end_segment], 2); + + EXPECT_EQ(journeys.els_.size(), 1); journey journey = journeys.els_[0]; EXPECT_TRUE(journey.legs_.empty()); - EXPECT_EQ(journey.start_time_,start_time); - EXPECT_EQ(journey.transfers_,2); - EXPECT_EQ(journey.dest_,location_idx_t::invalid()); - EXPECT_EQ(journey.dest_time_,start_time+6h); - - a_star_exec.reconstruct(query{ - .start_time_ = start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S7", source_idx_t{0}}), 0_minutes, 0U}}},journey); - EXPECT_EQ(journey.legs_.size(),6); + EXPECT_EQ(journey.start_time_, start_time); + EXPECT_EQ(journey.transfers_, 2); + EXPECT_EQ(journey.dest_, location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_, start_time + 6h); + + a_star_exec.reconstruct( + query{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"S0", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"S7", source_idx_t{0}}), + 0_minutes, 0U}}}, + journey); + EXPECT_EQ(journey.legs_.size(), 6); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i(tt.n_locations(),0); - a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); + a_star_exec.queue_.push(std::pair(start_segment, duration_t(0))); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); a_star_exec.transfers_[start_segment] = 0; a_star_exec.day_idx_[start_segment] = day_idx; - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); if (it == end(a_star_exec.state_.dist_to_dest_)) { - a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, + duration_t::zero()); } else { it->second = duration_t::zero(); } pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); - EXPECT_EQ(result.size(),2); - EXPECT_EQ(result[0],start_segment); - EXPECT_EQ(result[1],end_segment); + EXPECT_EQ(result.size(), 2); + EXPECT_EQ(result[0], start_segment); + EXPECT_EQ(result[1], end_segment); - EXPECT_EQ(a_star_exec.day_idx_[start_segment],day_idx); - EXPECT_EQ(a_star_exec.day_idx_[end_segment],day_idx+1); + EXPECT_EQ(a_star_exec.day_idx_[start_segment], day_idx); + EXPECT_EQ(a_star_exec.day_idx_[end_segment], day_idx + 1); - EXPECT_EQ(a_star_exec.transfers_[start_segment],0); - EXPECT_EQ(a_star_exec.transfers_[end_segment],1); - EXPECT_EQ(a_star_exec.end_segment_,end_segment); + EXPECT_EQ(a_star_exec.transfers_[start_segment], 0); + EXPECT_EQ(a_star_exec.transfers_[end_segment], 1); + EXPECT_EQ(a_star_exec.end_segment_, end_segment); - EXPECT_EQ(journeys.els_.size(),1); + EXPECT_EQ(journeys.els_.size(), 1); journey journey = journeys.els_[0]; EXPECT_TRUE(journey.legs_.empty()); - EXPECT_EQ(journey.start_time_,start_time); - EXPECT_EQ(journey.transfers_,1); - EXPECT_EQ(journey.dest_,location_idx_t::invalid()); - EXPECT_EQ(journey.dest_time_,start_time+22h); - - a_star_exec.reconstruct(query{ - .start_time_ = start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S2", source_idx_t{0}}), 0_minutes, 0U}}},journey); - EXPECT_EQ(journey.legs_.size(),4); + EXPECT_EQ(journey.start_time_, start_time); + EXPECT_EQ(journey.transfers_, 1); + EXPECT_EQ(journey.dest_, location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_, start_time + 22h); + + a_star_exec.reconstruct( + query{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"S0", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"S2", source_idx_t{0}}), + 0_minutes, 0U}}}, + journey); + EXPECT_EQ(journey.legs_.size(), 4); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i(tt.n_locations(),0); - a_star_exec.queue_.push(std::pair(start_segment,duration_t(0))); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); + a_star_exec.queue_.push(std::pair(start_segment, duration_t(0))); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); a_star_exec.transfers_[start_segment] = 0; a_star_exec.day_idx_[start_segment] = day_idx; - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); auto const it = a_star_exec.state_.dist_to_dest_.find(end_segment); if (it == end(a_star_exec.state_.dist_to_dest_)) { - a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, duration_t::zero()); + a_star_exec.state_.dist_to_dest_.emplace_hint(it, end_segment, + duration_t::zero()); } else { it->second = duration_t::zero(); } pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); - EXPECT_EQ(result.size(),2); - EXPECT_EQ(result[0],start_segment); - EXPECT_EQ(result[1],end_segment); + EXPECT_EQ(result.size(), 2); + EXPECT_EQ(result[0], start_segment); + EXPECT_EQ(result[1], end_segment); - EXPECT_EQ(a_star_exec.day_idx_[start_segment],day_idx); - EXPECT_EQ(a_star_exec.day_idx_[end_segment],day_idx); + EXPECT_EQ(a_star_exec.day_idx_[start_segment], day_idx); + EXPECT_EQ(a_star_exec.day_idx_[end_segment], day_idx); - EXPECT_EQ(a_star_exec.transfers_[start_segment],0); - EXPECT_EQ(a_star_exec.transfers_[end_segment],0); - EXPECT_EQ(a_star_exec.end_segment_,end_segment); + EXPECT_EQ(a_star_exec.transfers_[start_segment], 0); + EXPECT_EQ(a_star_exec.transfers_[end_segment], 0); + EXPECT_EQ(a_star_exec.end_segment_, end_segment); - EXPECT_EQ(journeys.els_.size(),1); + EXPECT_EQ(journeys.els_.size(), 1); journey journey = journeys.els_[0]; EXPECT_TRUE(journey.legs_.empty()); - EXPECT_EQ(journey.start_time_,start_time); - EXPECT_EQ(journey.transfers_,0); - EXPECT_EQ(journey.dest_,location_idx_t::invalid()); - EXPECT_EQ(journey.dest_time_,start_time+14h); - - a_star_exec.reconstruct(query{ - .start_time_ = start_time, - .start_match_mode_ = location_match_mode::kEquivalent, - .dest_match_mode_ = location_match_mode::kEquivalent, - .start_ = { - {tt.locations_.location_id_to_idx_.at({"S0", source_idx_t{0}}), 0_minutes, 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S2", source_idx_t{0}}), 0_minutes, 0U}}},journey); - EXPECT_EQ(journey.legs_.size(),2); + EXPECT_EQ(journey.start_time_, start_time); + EXPECT_EQ(journey.transfers_, 0); + EXPECT_EQ(journey.dest_, location_idx_t::invalid()); + EXPECT_EQ(journey.dest_time_, start_time + 14h); + + a_star_exec.reconstruct( + query{.start_time_ = start_time, + .start_match_mode_ = location_match_mode::kEquivalent, + .dest_match_mode_ = location_match_mode::kEquivalent, + .start_ = {{tt.locations_.location_id_to_idx_.at( + {"S0", source_idx_t{0}}), + 0_minutes, 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at( + {"S2", source_idx_t{0}}), + 0_minutes, 0U}}}, + journey); + EXPECT_EQ(journey.legs_.size(), 2); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i(tt.n_locations(),0); + a_star_exec.travel_time_lower_bound_ = + std::vector(tt.n_locations(), 0); a_star_exec.state_.end_reachable_.resize(tbd.segment_transports_.size()); a_star_exec.state_.end_reachable_.set(end_segment); - a_star_exec.state_.end_reachable_.set(end_segment+1); - a_star_exec.state_.dist_to_dest_ = hash_map(tbd.segment_transports_.size()); + a_star_exec.state_.end_reachable_.set(end_segment + 1); + a_star_exec.state_.dist_to_dest_ = + hash_map(tbd.segment_transports_.size()); a_star_exec.state_.dist_to_dest_[segment_idx_t(1)] = duration_t::zero(); a_star_exec.state_.dist_to_dest_[segment_idx_t(0)] = duration_t(1); auto stop_value = tt.route_location_seq_[route_idx_t(0)][0]; stop start_stop(stop_value); - a_star_exec.add_start(start_stop.location_idx(),start_time); + a_star_exec.add_start(start_stop.location_idx(), start_time); pareto_set journeys; - a_star_exec.execute(start_time,std::numeric_limits::max(),unixtime_t::max(),profile_idx_t{0},journeys); + a_star_exec.execute(start_time, std::numeric_limits::max(), + unixtime_t::max(), profile_idx_t{0}, journeys); auto result = get_segments(a_star_exec); - EXPECT_EQ(result.size(),1); - EXPECT_EQ(result.front(),segment_idx_t(1)); - EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(1)],day_idx); - EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(0)],day_idx); - for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S7", src}), 0_minutes, 0U}}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.els_.size(),1); + EXPECT_EQ(result.els_.size(), 1); journey journey = result.els_[0]; - EXPECT_EQ(journey.legs_.size(),6); + EXPECT_EQ(journey.legs_.size(), 6); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.els_.size(),1); + EXPECT_EQ(result.els_.size(), 1); journey journey = result.els_[0]; - EXPECT_EQ(journey.legs_.size(),4); + EXPECT_EQ(journey.legs_.size(), 4); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_match_mode_ = location_match_mode::kIntermodal, + .dest_match_mode_ = location_match_mode::kIntermodal, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S7", src}), 0_minutes, 0U}}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.els_.size(),1); + EXPECT_EQ(result.els_.size(), 1); journey journey = result.els_[0]; - EXPECT_EQ(journey.legs_.size(),7); + EXPECT_EQ(journey.legs_.size(), 7); EXPECT_FALSE(journey.error_); - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]].back()}.location_idx(); - EXPECT_EQ(journey.dest_,dest); + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{2}]] + .back()} + .location_idx(); + EXPECT_EQ(journey.dest_, dest); auto start = get_special_station(special_station::kStart); - EXPECT_EQ(journey.legs_.front().from_,start); - for(int i=0;i searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; EXPECT_TRUE(result.els_.empty()); } @@ -1054,45 +1165,52 @@ R1_MON,00:00:00,00:00:00,S0,0,0,0 R1_MON,02:00:00,02:00:00,S2,1,0,0 )"); } -TEST(tb_a_star,search_different_termination) { +TEST(tb_a_star, search_different_termination) { auto const tt = load_gtfs(different_termination_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + unixtime_t start_time = sys_days{year{2021} / March / day{1}}; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}}; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.els_.size(),1); - EXPECT_EQ(result.els_.front().legs_.size(),2); + EXPECT_EQ(result.els_.size(), 1); + EXPECT_EQ(result.els_.front().legs_.size(), 2); - EXPECT_EQ(result.els_.front().legs_.front().dep_time_,start_time); - EXPECT_EQ(result.els_.front().legs_.front().arr_time_,start_time+1h); - EXPECT_EQ(result.els_.front().legs_.back().dep_time_,start_time+1h); - EXPECT_EQ(result.els_.front().legs_.back().arr_time_,start_time+1h); + EXPECT_EQ(result.els_.front().legs_.front().dep_time_, start_time); + EXPECT_EQ(result.els_.front().legs_.front().arr_time_, start_time + 1h); + EXPECT_EQ(result.els_.front().legs_.back().dep_time_, start_time + 1h); + EXPECT_EQ(result.els_.front().legs_.back().arr_time_, start_time + 1h); - EXPECT_EQ(result.els_.front().transfers_,0); + EXPECT_EQ(result.els_.front().transfers_, 0); EXPECT_FALSE(result.els_.front().error_); - EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); - - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - EXPECT_EQ(result.els_.front().legs_.front().to_,dest); + EXPECT_EQ(result.els_.front().legs_.front().to_, + result.els_.front().legs_.back().from_); + + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .front()} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + EXPECT_EQ(result.els_.front().legs_.front().to_, dest); } - loader::mem_dir max_cost_files() { return loader::mem_dir::read(R"( # agency.txt @@ -1129,50 +1247,60 @@ R1_MON,01:10:00,01:10:00,S1,0,0,0 R1_MON,24:00:00,24:00:00,S2,1,0,0 )"); } -TEST(tb_a_star,search_max_cost) { +TEST(tb_a_star, search_max_cost) { auto const tt = load_gtfs(max_cost_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + unixtime_t start_time = sys_days{year{2021} / March / day{1}}; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}}; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = { + {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.els_.size(),1); - EXPECT_EQ(result.els_.front().legs_.size(),4); - - EXPECT_EQ(result.els_.front().legs_.front().dep_time_,start_time); - EXPECT_EQ(result.els_.front().legs_.front().arr_time_,start_time+1h); - EXPECT_EQ(result.els_.front().legs_[1].dep_time_,start_time+1h); - EXPECT_EQ(result.els_.front().legs_[1].arr_time_,start_time+1h+2min); - EXPECT_EQ(result.els_.front().legs_[2].dep_time_,start_time+1h+10min); - EXPECT_EQ(result.els_.front().legs_[2].arr_time_,start_time+24h); - EXPECT_EQ(result.els_.front().legs_.back().dep_time_,start_time+24h); - EXPECT_EQ(result.els_.front().legs_.back().arr_time_,start_time+24h); - - EXPECT_EQ(result.els_.front().transfers_,1); + EXPECT_EQ(result.els_.size(), 1); + EXPECT_EQ(result.els_.front().legs_.size(), 4); + + EXPECT_EQ(result.els_.front().legs_.front().dep_time_, start_time); + EXPECT_EQ(result.els_.front().legs_.front().arr_time_, start_time + 1h); + EXPECT_EQ(result.els_.front().legs_[1].dep_time_, start_time + 1h); + EXPECT_EQ(result.els_.front().legs_[1].arr_time_, start_time + 1h + 2min); + EXPECT_EQ(result.els_.front().legs_[2].dep_time_, start_time + 1h + 10min); + EXPECT_EQ(result.els_.front().legs_[2].arr_time_, start_time + 24h); + EXPECT_EQ(result.els_.front().legs_.back().dep_time_, start_time + 24h); + EXPECT_EQ(result.els_.front().legs_.back().arr_time_, start_time + 24h); + + EXPECT_EQ(result.els_.front().transfers_, 1); EXPECT_FALSE(result.els_.front().error_); - for(int i=0;i<3;++i) { - EXPECT_EQ(result.els_.front().legs_[i].to_,result.els_.front().legs_[i+1].from_); + for (int i = 0; i < 3; ++i) { + EXPECT_EQ(result.els_.front().legs_[i].to_, + result.els_.front().legs_[i + 1].from_); } - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - auto s1 = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().to_,s1); - EXPECT_EQ(result.els_.front().legs_[1].to_,s1); + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .front()} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + auto s1 = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().to_, s1); + EXPECT_EQ(result.els_.front().legs_[1].to_, s1); } loader::mem_dir over_24h_files() { @@ -1211,80 +1339,100 @@ R1_MON,00:20:00,00:20:00,S1,2,0,0 )"); } -TEST(tb_a_star,longer_trip_then_24h) { +TEST(tb_a_star, longer_trip_then_24h) { auto const tt = load_gtfs(over_24h_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/March/day{1}}; + unixtime_t start_time = sys_days{year{2021} / March / day{1}}; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}, - }; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at({"S1", src}), + 0_minutes, 0U}}, + }; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.size(),1); + EXPECT_EQ(result.size(), 1); auto journey = result.els_.front(); - EXPECT_EQ(journey.legs_.size(),2); - - EXPECT_EQ(journey.legs_.front().dep_time_,start_time); - EXPECT_EQ(journey.legs_.front().arr_time_,start_time+20min); - EXPECT_EQ(journey.legs_.back().arr_time_,start_time+20min); - EXPECT_EQ(journey.legs_.back().dep_time_,start_time+20min); - - EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); - - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); + EXPECT_EQ(journey.legs_.size(), 2); + + EXPECT_EQ(journey.legs_.front().dep_time_, start_time); + EXPECT_EQ(journey.legs_.front().arr_time_, start_time + 20min); + EXPECT_EQ(journey.legs_.back().arr_time_, start_time + 20min); + EXPECT_EQ(journey.legs_.back().dep_time_, start_time + 20min); + + EXPECT_EQ(journey.legs_.front().to_, journey.legs_.back().from_); + + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .front()} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + EXPECT_EQ(result.els_.front().legs_.front().to_, + result.els_.front().legs_.back().from_); } -TEST(tb_a_star,start_time_different_date_to_first_segment) { +TEST(tb_a_star, start_time_different_date_to_first_segment) { auto const tt = load_gtfs(over_24h_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/February/day{28}}+23h; + unixtime_t start_time = sys_days{year{2021} / February / day{28}} + 23h; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, 0U}}, - }; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at({"S1", src}), + 0_minutes, 0U}}, + }; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.size(),1); + EXPECT_EQ(result.size(), 1); auto journey = result.els_.front(); - EXPECT_EQ(journey.legs_.size(),2); - - EXPECT_EQ(journey.legs_.front().dep_time_,sys_days{year{2021}/March/day{1}}); - EXPECT_EQ(journey.legs_.front().arr_time_,sys_days{year{2021}/March/day{1}}+20min); - EXPECT_EQ(journey.legs_.back().arr_time_,sys_days{year{2021}/March/day{1}}+20min); - EXPECT_EQ(journey.legs_.back().dep_time_,sys_days{year{2021}/March/day{1}}+20min); - - EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); - - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); + EXPECT_EQ(journey.legs_.size(), 2); + + EXPECT_EQ(journey.legs_.front().dep_time_, + sys_days{year{2021} / March / day{1}}); + EXPECT_EQ(journey.legs_.front().arr_time_, + sys_days{year{2021} / March / day{1}} + 20min); + EXPECT_EQ(journey.legs_.back().arr_time_, + sys_days{year{2021} / March / day{1}} + 20min); + EXPECT_EQ(journey.legs_.back().dep_time_, + sys_days{year{2021} / March / day{1}} + 20min); + + EXPECT_EQ(journey.legs_.front().to_, journey.legs_.back().from_); + + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{1}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .front()} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + EXPECT_EQ(result.els_.front().legs_.front().to_, + result.els_.front().legs_.back().from_); } loader::mem_dir segment_passing_midnight_files() { @@ -1318,42 +1466,50 @@ R0_MON,24:30:00,24:30:00,S1,1,0,0 R0_MON,25:00:00,25:00:00,S2,2,0,0 )"); } -TEST(tb_a_star,segment_passing_midnight) { +TEST(tb_a_star, segment_passing_midnight) { auto const tt = load_gtfs(segment_passing_midnight_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/March/day{1}}+23h+30min; + unixtime_t start_time = sys_days{year{2021} / March / day{1}} + 23h + 30min; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}, - .max_travel_time_ = duration_t{24h}}; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S0", src}), 0_minutes, + 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at({"S2", src}), + 0_minutes, 0U}}, + .max_travel_time_ = duration_t{24h}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.size(),1); + EXPECT_EQ(result.size(), 1); auto journey = result.els_.front(); - EXPECT_EQ(journey.legs_.size(),2); - - EXPECT_EQ(journey.legs_.front().dep_time_,start_time); - EXPECT_EQ(journey.legs_.front().arr_time_,start_time+1h+30min); - EXPECT_EQ(journey.legs_.back().arr_time_,start_time+1h+30min); - EXPECT_EQ(journey.legs_.back().dep_time_,start_time+1h+30min); - - EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); - - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].front()}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); + EXPECT_EQ(journey.legs_.size(), 2); + + EXPECT_EQ(journey.legs_.front().dep_time_, start_time); + EXPECT_EQ(journey.legs_.front().arr_time_, start_time + 1h + 30min); + EXPECT_EQ(journey.legs_.back().arr_time_, start_time + 1h + 30min); + EXPECT_EQ(journey.legs_.back().dep_time_, start_time + 1h + 30min); + + EXPECT_EQ(journey.legs_.front().to_, journey.legs_.back().from_); + + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .front()} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + EXPECT_EQ(result.els_.front().legs_.front().to_, + result.els_.front().legs_.back().from_); } loader::mem_dir transport_starts_before_start_time_files() { @@ -1387,41 +1543,48 @@ R0_MON,24:30:00,24:30:00,S1,1,0,0 R0_MON,25:00:00,25:00:00,S2,2,0,0 )"); } -TEST(tb_a_star,tranpsport_starts_before_start_time) { +TEST(tb_a_star, tranpsport_starts_before_start_time) { auto const tt = load_gtfs(transport_starts_before_start_time_files); auto const tbd = preprocess(tt, profile_idx_t{0}); static auto search_state = routing::search_state{}; auto algo_state = a_star_state{tbd}; - unixtime_t start_time = sys_days{year{2021}/March/day{2}}; + unixtime_t start_time = sys_days{year{2021} / March / day{2}}; auto constexpr src = source_idx_t{0}; auto q = routing::query{ - .start_time_ = start_time, - .start_ = {{tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, - 0U}}, - .destination_ = { - {tt.locations_.location_id_to_idx_.at({"S2", src}), 0_minutes, 0U}}, - .max_travel_time_ = duration_t{24h}}; - - routing::search searcher{tt,nullptr,search_state,algo_state,std::move(q)}; + .start_time_ = start_time, + .start_ = {{tt.locations_.location_id_to_idx_.at({"S1", src}), 0_minutes, + 0U}}, + .destination_ = {{tt.locations_.location_id_to_idx_.at({"S2", src}), + 0_minutes, 0U}}, + .max_travel_time_ = duration_t{24h}}; + + routing::search searcher{ + tt, nullptr, search_state, algo_state, std::move(q)}; auto result = *searcher.execute().journeys_; - EXPECT_EQ(result.size(),1); + EXPECT_EQ(result.size(), 1); auto journey = result.els_.front(); - EXPECT_EQ(journey.legs_.size(),2); - - EXPECT_EQ(journey.legs_.front().dep_time_,start_time+30min); - EXPECT_EQ(journey.legs_.front().arr_time_,start_time+1h); - EXPECT_EQ(journey.legs_.back().arr_time_,start_time+1h); - EXPECT_EQ(journey.legs_.back().dep_time_,start_time+1h); - - EXPECT_EQ(journey.legs_.front().to_,journey.legs_.back().from_); - - auto dest = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]].back()}.location_idx(); - EXPECT_EQ(result.els_.front().dest_,dest); - EXPECT_EQ(result.els_.front().legs_.back().to_,dest); - EXPECT_EQ(result.els_.front().legs_.back().from_,dest); - auto start = stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]}.location_idx(); - EXPECT_EQ(result.els_.front().legs_.front().from_,start); - EXPECT_EQ(result.els_.front().legs_.front().to_,result.els_.front().legs_.back().from_); + EXPECT_EQ(journey.legs_.size(), 2); + + EXPECT_EQ(journey.legs_.front().dep_time_, start_time + 30min); + EXPECT_EQ(journey.legs_.front().arr_time_, start_time + 1h); + EXPECT_EQ(journey.legs_.back().arr_time_, start_time + 1h); + EXPECT_EQ(journey.legs_.back().dep_time_, start_time + 1h); + + EXPECT_EQ(journey.legs_.front().to_, journey.legs_.back().from_); + + auto dest = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]] + .back()} + .location_idx(); + EXPECT_EQ(result.els_.front().dest_, dest); + EXPECT_EQ(result.els_.front().legs_.back().to_, dest); + EXPECT_EQ(result.els_.front().legs_.back().from_, dest); + auto start = + stop{tt.route_location_seq_[tt.transport_route_[transport_idx_t{0}]][1]} + .location_idx(); + EXPECT_EQ(result.els_.front().legs_.front().from_, start); + EXPECT_EQ(result.els_.front().legs_.front().to_, + result.els_.front().legs_.back().from_); } -} // namespace nigiri::routing::tb::a_star +} // namespace nigiri::routing::tb::a_star From 0ee75e02f3eb1716893c48d864a7533b71d93c5c Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Thu, 19 Mar 2026 18:02:06 +0100 Subject: [PATCH 3/8] Additional include in pareto set --- include/nigiri/routing/pareto_set.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/nigiri/routing/pareto_set.h b/include/nigiri/routing/pareto_set.h index 13190b710..93c3eff00 100644 --- a/include/nigiri/routing/pareto_set.h +++ b/include/nigiri/routing/pareto_set.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include From d5d8a8800e1f5990e16807ee985fe114beb434b6 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Sun, 22 Mar 2026 17:14:41 +0100 Subject: [PATCH 4/8] Changes made for ci pipeline --- include/nigiri/routing/tb/tb_a_star/a_star.h | 8 +++--- src/routing/tb/tb_a_star/a_star.cc | 4 +-- test/routing/tb_a_star_pareto_verification.cc | 15 ++++++----- test/routing/tb_a_star_test.cc | 26 +++++++++---------- 4 files changed, 29 insertions(+), 24 deletions(-) diff --git a/include/nigiri/routing/tb/tb_a_star/a_star.h b/include/nigiri/routing/tb/tb_a_star/a_star.h index 21e998495..c070a995c 100644 --- a/include/nigiri/routing/tb/tb_a_star/a_star.h +++ b/include/nigiri/routing/tb/tb_a_star/a_star.h @@ -6,7 +6,7 @@ namespace nigiri::routing::tb::a_star { -inline int transfer_factor = 5; +inline unsigned int transfer_factor = 5; unixtime_t get_time(segment_idx_t const& idx, timetable const& tt, event_type const& event, @@ -23,7 +23,9 @@ struct cost_func_t { explicit cost_func_t() = default; std::size_t operator()( std::pair const& element) const { - return element.second.count(); + if (element.second.count() < 0) + throw std::runtime_error("Got negative costs"); + return static_cast(element.second.count()); } }; @@ -60,7 +62,7 @@ struct tb_a_star { static constexpr auto kUnreachable = std::numeric_limits::max(); - explicit tb_a_star(std::size_t const& init_cap) + explicit tb_a_star(unsigned int const& init_cap) : state_(tb_data()), queue_(1440 + (init_cap - 1) * transfer_factor, cost_func_t()) { pred_ = vector_map(init_cap, diff --git a/src/routing/tb/tb_a_star/a_star.cc b/src/routing/tb/tb_a_star/a_star.cc index 639e12566..0edc55e42 100644 --- a/src/routing/tb/tb_a_star/a_star.cc +++ b/src/routing/tb/tb_a_star/a_star.cc @@ -114,7 +114,7 @@ day_idx_t get_day(unixtime_t const& before, tb_data const& tbd) { auto size = (tt.date_range_.to_ - tt.date_range_.from_).count(); auto const starting_day_idx = tt.day_idx_mam(tt.date_range_.from_).first; - for (int i = 0; i < size; ++i) { + for (unsigned short i = 0; i < size; ++i) { auto day_idx = starting_day_idx + i; auto dep_time = get_time(s_idx, tt, event_type::kDep, tbd, day_idx); if (dep_time >= before) return day_idx; @@ -199,7 +199,7 @@ void tb_a_star::execute(unixtime_t const start_time, state_.tbd_, day_idx_[neighbour]); if (transfer_count < transfers_[neighbour] && neighbour_arrival < arrival_time_limit) { - transfers_[neighbour] = transfer_count; + transfers_[neighbour] = static_cast(transfer_count); pred_[neighbour] = p; duration_t new_costs = neighbour_arrival - start_time + duration_t(transfer_factor * transfer_count) + diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc index edf0b3211..7edb24f3f 100644 --- a/test/routing/tb_a_star_pareto_verification.cc +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -37,10 +37,11 @@ timetable load_hrd(auto const& files) { return tt; } -int calculate_transfer_factor(std::vector const& journeys, int index) { +unsigned int calculate_transfer_factor(std::vector const& journeys, + int index) { double higher_bound = std::numeric_limits::max(); double lower_bound = std::numeric_limits::min(); - for (int j = 0; j < journeys.size(); ++j) { + for (std::size_t j = 0; j < journeys.size(); ++j) { if (j == index) continue; utl::verify(journeys[index].transfers_ != journeys[j].transfers_, "Two travels with same amount of transfers"); @@ -64,8 +65,10 @@ int calculate_transfer_factor(std::vector const& journeys, int index) { (higher_bound_floor == higher_bound ? higher_bound - 1 : higher_bound_floor) > lower_bound, "No Integer exists between bounds"); - return higher_bound_floor == higher_bound ? higher_bound - 1 - : higher_bound_floor; + auto result = higher_bound_floor == higher_bound ? higher_bound - 1 + : higher_bound_floor; + if (result < 0) throw std::runtime_error("Negative factor"); + return static_cast(result); } void verify_pareto_set(auto const& files, @@ -95,7 +98,7 @@ void verify_pareto_set(auto const& files, auto results = tb_searcher.execute().journeys_->els_; EXPECT_EQ(results.size(), expectedNumberOfJourneys); - for (int i = 0; i < results.size(); ++i) { + for (std::size_t i = 0; i < results.size(); ++i) { if (results.size() > 1) a_star::transfer_factor = calculate_transfer_factor(results, i); static auto a_star_search_state = routing::search_state{}; @@ -118,7 +121,7 @@ void verify_pareto_set(auto const& files, results = raptor_searcher.execute().journeys_->els_; EXPECT_EQ(results.size(), expectedNumberOfJourneys); - for (int i = 0; i < results.size(); ++i) { + for (std::size_t i = 0; i < results.size(); ++i) { if (results.size() > 1) a_star::transfer_factor = calculate_transfer_factor(results, i); static auto a_star_search_state = routing::search_state{}; diff --git a/test/routing/tb_a_star_test.cc b/test/routing/tb_a_star_test.cc index ddd0207b7..4d7e63d9f 100644 --- a/test/routing/tb_a_star_test.cc +++ b/test/routing/tb_a_star_test.cc @@ -29,7 +29,7 @@ void check_neighbours(tb_data const& tbd) { auto [neighbours, next_exists] = get_neighbours(i, tbd, day_idx_t{0}); auto end = next_exists ? neighbours.size() - 1 : neighbours.size(); std::vector seen(tbd.segment_transports_.size(), false); - for (int j = 0; j < end; ++j) { + for (std::size_t j = 0; j < end; ++j) { auto neighbour = neighbours[j]; EXPECT_TRUE(utl::any_of(tbd.segment_transfers_[i], [neighbour](transfer const& t) { @@ -177,7 +177,7 @@ TEST(tb_a_star, a_star_no_transfer) { .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -304,7 +304,7 @@ TEST(tb_a_star, a_star_with_transfer) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -457,7 +457,7 @@ TEST(tb_a_star, a_star_longer_routes) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -590,7 +590,7 @@ TEST(tb_a_star, overnight_travel) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -714,7 +714,7 @@ TEST(tb_a_star, overnight_travel_without_transfer) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -742,7 +742,7 @@ TEST(tb_a_star, add_starts_one_segment) { a_star_exec.add_start(start_stop.location_idx(), start_time); EXPECT_TRUE(a_star_exec.is_start_segment_.test(start_segment)); - for (int i = 0; i < tbd.segment_transfers_.size(); ++i) { + for (std::size_t i = 0; i < tbd.segment_transfers_.size(); ++i) { segment_idx_t segment{i}; if (segment != start_segment) { EXPECT_FALSE(a_star_exec.is_start_segment_.test(segment)); @@ -840,7 +840,7 @@ TEST(tb_a_star, a_star_two_start_segments) { EXPECT_EQ(result.front(), segment_idx_t(1)); EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(1)], day_idx); EXPECT_EQ(a_star_exec.day_idx_[segment_idx_t(0)], day_idx); - for (int i = 0; i < tbd.segment_transfers_.size(); ++i) { + for (std::size_t i = 0; i < tbd.segment_transfers_.size(); ++i) { EXPECT_EQ(a_star_exec.transfers_[segment_idx_t{i}], 0); } EXPECT_EQ(a_star_exec.end_segment_, segment_idx_t(1)); @@ -875,7 +875,7 @@ TEST(tb_a_star, a_star_two_start_segments) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -922,7 +922,7 @@ TEST(tb_a_star, search_execute) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -988,7 +988,7 @@ TEST(tb_a_star, search_overnight) { .front()} .location_idx(); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, journey.dest_); @@ -1041,7 +1041,7 @@ TEST(tb_a_star, search_intermodal) { EXPECT_EQ(journey.dest_, dest); auto start = get_special_station(special_station::kStart); EXPECT_EQ(journey.legs_.front().from_, start); - for (int i = 0; i < journey.legs_.size() - 1; ++i) { + for (std::size_t i = 0; i < journey.legs_.size() - 1; ++i) { EXPECT_EQ(journey.legs_[i].to_, journey.legs_[i + 1].from_); } EXPECT_EQ(journey.legs_.back().to_, @@ -1279,7 +1279,7 @@ TEST(tb_a_star, search_max_cost) { EXPECT_EQ(result.els_.front().transfers_, 1); EXPECT_FALSE(result.els_.front().error_); - for (int i = 0; i < 3; ++i) { + for (auto i = 0; i < 3; ++i) { EXPECT_EQ(result.els_.front().legs_[i].to_, result.els_.front().legs_[i + 1].from_); } From 0fabd6f603d7ef12b39228a9d996e848f7f55dd8 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Sun, 22 Mar 2026 17:31:02 +0100 Subject: [PATCH 5/8] Additional pipeline change --- test/routing/tb_a_star_pareto_verification.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc index 7edb24f3f..0e5d5e9d2 100644 --- a/test/routing/tb_a_star_pareto_verification.cc +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -38,7 +38,7 @@ timetable load_hrd(auto const& files) { } unsigned int calculate_transfer_factor(std::vector const& journeys, - int index) { + std::size_t index) { double higher_bound = std::numeric_limits::max(); double lower_bound = std::numeric_limits::min(); for (std::size_t j = 0; j < journeys.size(); ++j) { From 7867ff0596edc5b5002bf19e38be16a869a4ddc6 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Sun, 22 Mar 2026 17:54:46 +0100 Subject: [PATCH 6/8] Another change to hopefully please linux sanitization --- test/routing/tb_a_star_pareto_verification.cc | 14 ++++++-------- test/routing/tb_a_star_test.cc | 2 +- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc index 0e5d5e9d2..1bf94bf4f 100644 --- a/test/routing/tb_a_star_pareto_verification.cc +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -36,7 +36,7 @@ timetable load_hrd(auto const& files) { finalize(tt); return tt; } - +constexpr double EPSILON = 0.00001; unsigned int calculate_transfer_factor(std::vector const& journeys, std::size_t index) { double higher_bound = std::numeric_limits::max(); @@ -56,17 +56,15 @@ unsigned int calculate_transfer_factor(std::vector const& journeys, factor < higher_bound) higher_bound = factor; } - if (higher_bound == std::numeric_limits::max()) + if (std::numeric_limits::max() - higher_bound < EPSILON) return static_cast(lower_bound) == lower_bound ? lower_bound + 1 : std::ceil(lower_bound); int higher_bound_floor = static_cast(higher_bound); - utl::verify( - (higher_bound_floor == higher_bound ? higher_bound - 1 - : higher_bound_floor) > lower_bound, - "No Integer exists between bounds"); - auto result = higher_bound_floor == higher_bound ? higher_bound - 1 - : higher_bound_floor; + auto result = (higher_bound - higher_bound_floor < EPSILON) + ? higher_bound - 1 + : higher_bound_floor; + utl::verify(result > lower_bound, "No Integer exists between bounds"); if (result < 0) throw std::runtime_error("Negative factor"); return static_cast(result); } diff --git a/test/routing/tb_a_star_test.cc b/test/routing/tb_a_star_test.cc index 4d7e63d9f..7f8ef71ed 100644 --- a/test/routing/tb_a_star_test.cc +++ b/test/routing/tb_a_star_test.cc @@ -1279,7 +1279,7 @@ TEST(tb_a_star, search_max_cost) { EXPECT_EQ(result.els_.front().transfers_, 1); EXPECT_FALSE(result.els_.front().error_); - for (auto i = 0; i < 3; ++i) { + for (unsigned long i = 0; i < 3; ++i) { EXPECT_EQ(result.els_.front().legs_[i].to_, result.els_.front().legs_[i + 1].from_); } From e84d55675b3b69e09c8b560f1529c9f178456531 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Sun, 22 Mar 2026 18:12:27 +0100 Subject: [PATCH 7/8] additional fix --- test/routing/tb_a_star_pareto_verification.cc | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/test/routing/tb_a_star_pareto_verification.cc b/test/routing/tb_a_star_pareto_verification.cc index 1bf94bf4f..71a7f7bd9 100644 --- a/test/routing/tb_a_star_pareto_verification.cc +++ b/test/routing/tb_a_star_pareto_verification.cc @@ -57,9 +57,14 @@ unsigned int calculate_transfer_factor(std::vector const& journeys, higher_bound = factor; } if (std::numeric_limits::max() - higher_bound < EPSILON) - return static_cast(lower_bound) == lower_bound - ? lower_bound + 1 - : std::ceil(lower_bound); + return std::abs(lower_bound - + static_cast( + lower_bound))(lower_bound)> - + 2 + ? static_cast(lower_bound + 1) + : (std::ceil(lower_bound) > 0 + ? static_cast(std::ceil(lower_bound)) + : 0); int higher_bound_floor = static_cast(higher_bound); auto result = (higher_bound - higher_bound_floor < EPSILON) ? higher_bound - 1 From 73a606ae7cb2a06b6d83f46b387dadb3a09a0384 Mon Sep 17 00:00:00 2001 From: omarBenZaied Date: Sun, 22 Mar 2026 18:27:49 +0100 Subject: [PATCH 8/8] additional change --- src/routing/tb/tb_a_star/a_star.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/routing/tb/tb_a_star/a_star.cc b/src/routing/tb/tb_a_star/a_star.cc index 0edc55e42..503c30413 100644 --- a/src/routing/tb/tb_a_star/a_star.cc +++ b/src/routing/tb/tb_a_star/a_star.cc @@ -191,7 +191,7 @@ void tb_a_star::execute(unixtime_t const start_time, for (std::size_t i = 0; i < neighbours.size(); ++i) { auto neighbour = neighbours[i]; auto is_next = next_exists && i == neighbours.size() - 1; - auto transfer_count = transfers_[p] + (is_next ? 0 : 1); + uint8_t transfer_count = transfers_[p] + (is_next ? 0 : 1); day_idx_[neighbour] = is_next ? day_idx_[p] : get_day(arrival, neighbour, tt_, state_.tbd_); if (is_invalid(day_idx_[neighbour])) continue; @@ -199,7 +199,7 @@ void tb_a_star::execute(unixtime_t const start_time, state_.tbd_, day_idx_[neighbour]); if (transfer_count < transfers_[neighbour] && neighbour_arrival < arrival_time_limit) { - transfers_[neighbour] = static_cast(transfer_count); + transfers_[neighbour] = transfer_count; pred_[neighbour] = p; duration_t new_costs = neighbour_arrival - start_time + duration_t(transfer_factor * transfer_count) +