diff --git a/.pkg b/.pkg index 67b285264..16c0b3965 100644 --- a/.pkg +++ b/.pkg @@ -5,7 +5,7 @@ [cista] url=git@github.com:felixguendling/cista.git branch=master - commit=1fb6f1d9bb8ac9fdb4ca648df69695dd819dc07e + commit=f51e3e9251ce9df6e8c2617e3c5ed21a0ba18221 [geo] url=git@github.com:motis-project/geo.git branch=master diff --git a/.pkg.lock b/.pkg.lock index 03f4c85ac..3251c340d 100644 --- a/.pkg.lock +++ b/.pkg.lock @@ -1,5 +1,5 @@ -11896755751842712608 -cista 1fb6f1d9bb8ac9fdb4ca648df69695dd819dc07e +16540055415631783859 +cista f51e3e9251ce9df6e8c2617e3c5ed21a0ba18221 PEGTL 0d37dcf8f02c12a84fdf521973801a5baab78e8f res b759b93316afeb529b6cb5b2548b24c41e382fb0 date ce88cc33b5551f66655614eeebb7c5b7189025fb diff --git a/include/nigiri/loader/permutate_locations.h b/include/nigiri/loader/permutate_locations.h new file mode 100644 index 000000000..41ea7b0aa --- /dev/null +++ b/include/nigiri/loader/permutate_locations.h @@ -0,0 +1,9 @@ +#pragma once + +namespace nigiri { + +struct timetable; + +void permutate_locations(timetable&); + +} // namespace nigiri \ No newline at end of file diff --git a/include/nigiri/logging.h b/include/nigiri/logging.h index 67e28ee10..e8d8edfca 100644 --- a/include/nigiri/logging.h +++ b/include/nigiri/logging.h @@ -27,7 +27,7 @@ static log_lvl s_verbosity; inline std::string now() { using clock = std::chrono::system_clock; auto const now = clock::to_time_t(clock::now()); - struct tm tmp{}; + struct tm tmp {}; #if _MSC_VER >= 1400 gmtime_s(&tmp, &now); #else diff --git a/include/nigiri/special_stations.h b/include/nigiri/special_stations.h index 739b45f7d..376df447f 100644 --- a/include/nigiri/special_stations.h +++ b/include/nigiri/special_stations.h @@ -17,17 +17,16 @@ enum class special_station : location_idx_t::value_t { kSpecialStationsSize }; +constexpr auto const kNSpecialStations = + static_cast>( + special_station::kSpecialStationsSize); + constexpr bool is_special(location_idx_t const l) { - constexpr auto const max = - static_cast>( - special_station::kSpecialStationsSize); - return to_idx(l) < max; + return to_idx(l) < kNSpecialStations; } constexpr auto const special_stations_names = - cista::array>( - special_station::kSpecialStationsSize)>{ + cista::array{ "START", "END", "VIA0", "VIA1", "VIA2", "VIA3", "VIA4", "VIA5", "VIA6"}; constexpr location_idx_t get_special_station(special_station const x) { diff --git a/include/nigiri/timetable.h b/include/nigiri/timetable.h index 910f07252..e8210f74d 100644 --- a/include/nigiri/timetable.h +++ b/include/nigiri/timetable.h @@ -27,6 +27,14 @@ namespace nigiri { +// Warning: for better data locality, locations will be reordered in a way that +// locations with no traffic (not served by any route) are at the end (high +// index) while locations with traffic will be at the start (low index). +// This means that every added data field that (directly or indirectly) +// references location_idx_t has to be handled in permutate_locations.cc +// Don't forget to add handling to permutate_locations.cc when adding new fields +// that either use location_idx_t as index (permutate) or as value +// (re-reference). struct timetable { struct locations { timezone_idx_t register_timezone(timezone tz) { @@ -121,10 +129,10 @@ struct timetable { vector_map types_; vector_map parents_; vector_map location_timezones_; - mutable_fws_multimap equivalences_; - mutable_fws_multimap children_; - mutable_fws_multimap preprocessing_footpaths_out_; - mutable_fws_multimap preprocessing_footpaths_in_; + paged_vecvec equivalences_; + paged_vecvec children_; + paged_vecvec preprocessing_footpaths_out_; + paged_vecvec preprocessing_footpaths_in_; array, kNProfiles> footpaths_out_; array, kNProfiles> footpaths_in_; vector_map timezones_; diff --git a/src/loader/build_footpaths.cc b/src/loader/build_footpaths.cc index cb47f4b1e..17e076f8c 100644 --- a/src/loader/build_footpaths.cc +++ b/src/loader/build_footpaths.cc @@ -207,11 +207,9 @@ void connect_components(timetable& tt, utl::sort(assignments); tt.locations_.preprocessing_footpaths_out_.clear(); - tt.locations_.preprocessing_footpaths_out_[location_idx_t{ - tt.locations_.src_.size() - 1}]; + tt.locations_.preprocessing_footpaths_out_.resize(tt.n_locations()); tt.locations_.preprocessing_footpaths_in_.clear(); - tt.locations_.preprocessing_footpaths_in_[location_idx_t{ - tt.locations_.src_.size() - 1}]; + tt.locations_.preprocessing_footpaths_in_.resize(tt.n_locations()); auto tmp_graph = cista::raw::mutable_fws_multimap{}; auto components = std::vector{}; diff --git a/src/loader/hrd/service/service.cc b/src/loader/hrd/service/service.cc index ee5eb47e0..b329a0cf5 100644 --- a/src/loader/hrd/service/service.cc +++ b/src/loader/hrd/service/service.cc @@ -139,7 +139,7 @@ void parse_range(std::vector const& spec_lines, std::vector const& stops, std::vector& sections, service::section& begin_to_end, - TargetInformationType service::section::* member, + TargetInformationType service::section::*member, TargetInformationParserFun parse_target_info) { compute_ranges(spec_lines, stops, parse_info, [&](auto const& r) { TargetInformationType target_info = parse_target_info(r.first, r.second); @@ -156,14 +156,14 @@ void parse_range(std::vector const& spec_lines, } template -void parse_range(std::vector const& spec_lines, - range_parse_information const& parse_info, - std::vector const& stops, - std::vector& sections, - service::section& begin_to_end, - std::optional> - service::section::* member, - TargetInformationParserFun parse_target_info) { +void parse_range( + std::vector const& spec_lines, + range_parse_information const& parse_info, + std::vector const& stops, + std::vector& sections, + service::section& begin_to_end, + std::optional> service::section::*member, + TargetInformationParserFun parse_target_info) { compute_ranges(spec_lines, stops, parse_info, [&](auto const& r) { TargetInformationType target_info = parse_target_info(r.first, r.second); diff --git a/src/loader/init_finish.cc b/src/loader/init_finish.cc index 7ded1ad60..6f86d283d 100644 --- a/src/loader/init_finish.cc +++ b/src/loader/init_finish.cc @@ -8,6 +8,7 @@ #include "nigiri/loader/build_footpaths.h" #include "nigiri/loader/build_lb_graph.h" +#include "nigiri/loader/permutate_locations.h" #include "nigiri/flex.h" #include "nigiri/special_stations.h" #include "nigiri/timetable.h" @@ -129,6 +130,8 @@ void finalize(timetable& tt, finalize_options const opt) { tt.strings_.get(tt.providers_[b].short_name_); }); } + + permutate_locations(tt); build_footpaths(tt, opt); build_lb_graph(tt, kDefaultProfile); build_lb_graph(tt, kDefaultProfile); diff --git a/src/loader/permutate_locations.cc b/src/loader/permutate_locations.cc new file mode 100644 index 000000000..0ec1a217b --- /dev/null +++ b/src/loader/permutate_locations.cc @@ -0,0 +1,137 @@ +#include "nigiri/loader/permutate_locations.h" + +#include "nigiri/special_stations.h" +#include "nigiri/timetable.h" + +namespace nigiri { + +template +concept Collection = requires(T& x) { + x.begin(); + x.end(); +}; + +using permutation_t = vector_map; + +void update_refs(permutation_t const& p, location_idx_t& l) { + l = l == location_idx_t::invalid() ? l : p[l]; +} + +void update_refs(permutation_t const& p, + vecvec::bucket& seq) { + for (auto& s : seq) { + auto old = stop{s}; + s = stop{p[old.location_idx()], old.in_allowed(), old.out_allowed(), + old.in_allowed_wheelchair(), old.out_allowed_wheelchair()} + .value(); + } +} + +void update_refs(permutation_t const& p, footpath& fp) { + fp = footpath{p[fp.target()], fp.duration()}; +} + +template +void update_refs(permutation_t const& p, T& c) { + if (c.empty()) { + return; + } + + for (auto it = begin(c); it != end(c); ++it) { + if constexpr (std::is_reference_v) { + update_refs(p, *it); + } else { + update_refs(p, it); + } + } +} + +void update_refs(permutation_t const& p, + hash_map& map) { + for (auto& it : map) { + update_refs(p, it.second); + } +} + +template +void permutate(permutation_t const& p, Vec& vec) { + auto copy = Vec{}; + for (auto i = location_idx_t{0U}; i != vec.size(); ++i) { + copy.emplace_back(vec[p[i]]); + } + vec = copy; +} + +std::pair get_permutation(timetable const& tt) { + if (tt.n_locations() == 0U) { + return {}; + } + + auto ret = std::pair{}; + auto& [p, r] = ret; + + p.resize(tt.n_locations()); + std::generate(begin(p), end(p), + [l = location_idx_t{0U}]() mutable { return l++; }); + auto const first_idx = + tt.n_locations() >= kNSpecialStations && + tt.locations_.names_.at(location_idx_t{0U}).view() == + special_stations_names[0] + ? kNSpecialStations + : 0U; + std::stable_sort(begin(p) + first_idx, end(p), + [&](location_idx_t const a, location_idx_t const b) { + return !tt.location_routes_[a].empty() && + tt.location_routes_[b].empty(); + }); + + r.resize(tt.n_locations()); + for (auto i = location_idx_t{0U}; i != tt.n_locations(); ++i) { + r[p[i]] = i; + } + + return ret; +} + +void permutate_locations(timetable& tt) { + for (auto i = 0U; i != kNProfiles; ++i) { + assert(tt.locations_.footpaths_out_[i].empty()); + assert(tt.locations_.footpaths_in_[i].empty()); + assert(tt.fwd_search_lb_graph_[i].empty()); + assert(tt.bwd_search_lb_graph_[i].empty()); + } + assert(tt.locations_.rtree_.nodes_.empty()); + + auto const [p, r] = get_permutation(tt); + + auto& locations = tt.locations_; + permutate(p, tt.location_areas_); + permutate(p, tt.location_routes_); + permutate(p, tt.location_location_groups_); + permutate(p, locations.names_); + permutate(p, locations.descriptions_); + permutate(p, locations.ids_); + permutate(p, locations.coordinates_); + permutate(p, locations.src_); + permutate(p, locations.transfer_time_); + permutate(p, locations.types_); + permutate(p, locations.parents_); + permutate(p, locations.location_timezones_); + permutate(p, locations.equivalences_); + permutate(p, locations.children_); + permutate(p, locations.preprocessing_footpaths_out_); + permutate(p, locations.preprocessing_footpaths_in_); + permutate(p, locations.location_importance_); + + update_refs(r, tt.route_location_seq_); + update_refs(r, tt.location_group_locations_); + update_refs(r, tt.flex_area_locations_); + update_refs(r, locations.preprocessing_footpaths_out_); + update_refs(r, locations.preprocessing_footpaths_in_); + update_refs(r, locations.equivalences_); + update_refs(r, locations.parents_); + update_refs(r, locations.children_); + update_refs(r, locations.location_id_to_idx_); +} + +} // namespace nigiri \ No newline at end of file diff --git a/test/routing/dijkstra_test.cc b/test/routing/dijkstra_test.cc index 16379bc32..59c9068ac 100644 --- a/test/routing/dijkstra_test.cc +++ b/test/routing/dijkstra_test.cc @@ -106,6 +106,7 @@ TEST(routing, dijkstra) { finalize(tt); auto const d1_l = tt.locations_.location_id_to_idx_.at({"D1", src}); + EXPECT_EQ("D1", (location{tt, d1_l}.name_)); auto const q_d1_c2 = query{ .start_time_ = unixtime_t{sys_days{2024_y / June / 8} + 7_hours}, .start_match_mode_ = location_match_mode::kExact,