From 61c430c0983cdc0f4b679925f7d4f069acca6a69 Mon Sep 17 00:00:00 2001 From: Patrick Niklaus Date: Sun, 20 Aug 2017 23:24:05 +0000 Subject: [PATCH] Implement exclude flags on CH using shared core The core is fully contracted for each exclude flag and stored in a merged graph data structure. --- .travis.yml | 1 + features/testbot/exclude.feature | 15 -- .../contractor/contract_excludable_graph.hpp | 87 ++++++ .../contractor/contracted_edge_container.hpp | 153 +++++++++++ include/contractor/contractor_config.hpp | 9 +- include/contractor/contractor_dijkstra.hpp | 50 ---- include/contractor/contractor_search.hpp | 26 ++ include/contractor/files.hpp | 53 +++- include/contractor/graph_contractor.hpp | 32 ++- .../contractor/graph_contractor_adaptors.hpp | 5 +- include/contractor/query_edge.hpp | 11 + include/engine/algorithm.hpp | 6 + .../datafacade/algorithm_datafacade.hpp | 13 +- .../contiguous_internalmem_datafacade.hpp | 66 +++-- include/engine/engine.hpp | 7 +- include/partition/bisection_graph_view.hpp | 8 +- include/partition/dinic_max_flow.hpp | 5 +- include/partition/inertial_flow.hpp | 2 +- include/storage/shared_datatype.hpp | 36 ++- include/util/dynamic_graph.hpp | 78 ++++-- include/util/exclude_flag.hpp | 35 +++ include/util/filtered_graph.hpp | 165 ++++++++++++ include/util/filtered_integer_range.hpp | 101 +++++++ include/util/query_heap.hpp | 1 + src/contractor/contractor.cpp | 59 +++-- ...tor_dijkstra.cpp => contractor_search.cpp} | 109 ++++---- src/contractor/graph_contractor.cpp | 249 +++++++++--------- src/customize/customizer.cpp | 24 +- src/partition/bisection_graph_view.cpp | 13 +- src/partition/dinic_max_flow.cpp | 3 +- src/partition/inertial_flow.cpp | 9 +- src/partition/recursive_bisection_state.cpp | 3 +- src/storage/storage.cpp | 79 +++++- unit_tests/CMakeLists.txt | 14 +- .../contractor/contracted_edge_container.cpp | 109 ++++++++ unit_tests/contractor/graph_contractor.cpp | 156 +++++++++++ unit_tests/contractor_tests.cpp | 7 + unit_tests/mocks/mock_datafacade.hpp | 6 +- unit_tests/partition/dinic.cpp | 2 +- unit_tests/partition/scc_integration.cpp | 2 +- unit_tests/util/dynamic_graph.cpp | 39 +++ unit_tests/util/filtered_integer_range.cpp | 38 +++ 42 files changed, 1468 insertions(+), 418 deletions(-) create mode 100644 include/contractor/contract_excludable_graph.hpp create mode 100644 include/contractor/contracted_edge_container.hpp delete mode 100644 include/contractor/contractor_dijkstra.hpp create mode 100644 include/contractor/contractor_search.hpp create mode 100644 include/util/exclude_flag.hpp create mode 100644 include/util/filtered_graph.hpp create mode 100644 include/util/filtered_integer_range.hpp rename src/contractor/{contractor_dijkstra.cpp => contractor_search.cpp} (50%) create mode 100644 unit_tests/contractor/contracted_edge_container.cpp create mode 100644 unit_tests/contractor/graph_contractor.cpp create mode 100644 unit_tests/contractor_tests.cpp create mode 100644 unit_tests/util/filtered_integer_range.cpp diff --git a/.travis.yml b/.travis.yml index cf705b4a2..437aa75ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -365,6 +365,7 @@ script: - pushd ${OSRM_BUILD_DIR} - ./unit_tests/library-tests - ./unit_tests/extractor-tests + - ./unit_tests/contractor-tests - ./unit_tests/engine-tests - ./unit_tests/util-tests - ./unit_tests/server-tests diff --git a/features/testbot/exclude.feature b/features/testbot/exclude.feature index b483ed7d7..e7ed2d7c6 100644 --- a/features/testbot/exclude.feature +++ b/features/testbot/exclude.feature @@ -20,7 +20,6 @@ Feature: Testbot - Exclude flags | fg | primary | | always drivable | | gd | primary | | always drivable | - @mld Scenario: Testbot - exclude nothing When I route I should get | from | to | route | @@ -38,7 +37,6 @@ Feature: Testbot - Exclude flags | a | 0 | 115 | | d | 115 | 0 | - @mld Scenario: Testbot - exclude motorway Given the query options | exclude | motorway | @@ -59,7 +57,6 @@ Feature: Testbot - Exclude flags | a | 0 | 125 | | d | 125 | 0 | - @mld Scenario: Testbot - exclude toll Given the query options | exclude | toll | @@ -72,7 +69,6 @@ Feature: Testbot - Exclude flags | a | f | | | f | d | fg,gd,gd | - @mld Scenario: Testbot - exclude motorway and toll Given the query options | exclude | motorway,toll | @@ -85,7 +81,6 @@ Feature: Testbot - Exclude flags | a | f | | | f | d | fg,gd,gd | - @mld Scenario: Testbot - exclude with unsupported exclude combination Given the query options | exclude | TwoWords2 | @@ -94,7 +89,6 @@ Feature: Testbot - Exclude flags | from | to | status | message | | a | d | 400 | Exclude flag combination is not supported. | - @mld Scenario: Testbot - exclude with invalid exclude class name Given the query options | exclude | foo | @@ -103,12 +97,3 @@ Feature: Testbot - Exclude flags | from | to | status | message | | a | d | 400 | Exclude flag combination is not supported. | - @ch - Scenario: Testbot - Check error message for exclude on non-MLD - Given the query options - | exclude | motorway | - - When I route I should get - | from | to | status | message | - | a | d | 400 | This algorithm does not support exclude flags. | - diff --git a/include/contractor/contract_excludable_graph.hpp b/include/contractor/contract_excludable_graph.hpp new file mode 100644 index 000000000..a83fd0a93 --- /dev/null +++ b/include/contractor/contract_excludable_graph.hpp @@ -0,0 +1,87 @@ +#ifndef OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP +#define OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP + +#include "contractor/contracted_edge_container.hpp" +#include "contractor/contractor_graph.hpp" +#include "contractor/graph_contractor.hpp" +#include "contractor/graph_contractor_adaptors.hpp" +#include "contractor/query_graph.hpp" + +namespace osrm +{ +namespace contractor +{ + +using GraphFilterAndCore = + std::tuple>, std::vector>>; + +inline auto contractExcludableGraph(ContractorGraph contractor_graph_, + std::vector node_weights, + const std::vector> &filters, + const float core_factor = 1.0) +{ + auto num_nodes = contractor_graph_.GetNumberOfNodes(); + ContractedEdgeContainer edge_container; + ContractorGraph shared_core_graph; + std::vector is_shared_core; + { + ContractorGraph contractor_graph = std::move(contractor_graph_); + std::vector always_allowed(num_nodes, true); + for (const auto &filter : filters) + { + for (const auto node : util::irange(0, num_nodes)) + { + always_allowed[node] = always_allowed[node] && filter[node]; + } + } + + // By not contracting all contractable nodes we avoid creating + // a very dense core. This increases the overall graph sizes a little bit + // but increases the final CH quality and contraction speed. + constexpr float BASE_CORE = 0.9; + is_shared_core = contractGraph(contractor_graph, + std::move(always_allowed), + node_weights, + std::min(BASE_CORE, core_factor)); + + // Add all non-core edges to container + { + auto non_core_edges = toEdges(contractor_graph); + auto new_end = + std::remove_if(non_core_edges.begin(), non_core_edges.end(), [&](const auto &edge) { + return is_shared_core[edge.source] && is_shared_core[edge.target]; + }); + non_core_edges.resize(new_end - non_core_edges.begin()); + edge_container.Insert(std::move(non_core_edges)); + } + + // Extract core graph for further contraction + shared_core_graph = contractor_graph.Filter( + [&is_shared_core](const NodeID node) { return is_shared_core[node]; }); + } + + std::vector> cores; + for (const auto &filter : filters) + { + auto filtered_core_graph = + shared_core_graph.Filter([&filter](const NodeID node) { return filter[node]; }); + + auto core = contractGraph( + filtered_core_graph, is_shared_core, is_shared_core, node_weights, core_factor); + if (core_factor == 1.0) + { + core.clear(); + } + cores.push_back(std::move(core)); + + edge_container.Merge(toEdges(std::move(filtered_core_graph))); + } + + return GraphFilterAndCore{QueryGraph{num_nodes, std::move(edge_container.edges)}, + edge_container.MakeEdgeFilters(), + std::move(cores)}; +} +} +} + +#endif diff --git a/include/contractor/contracted_edge_container.hpp b/include/contractor/contracted_edge_container.hpp new file mode 100644 index 000000000..9ea9cb003 --- /dev/null +++ b/include/contractor/contracted_edge_container.hpp @@ -0,0 +1,153 @@ +#ifndef OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP +#define OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP + +#include "contractor/query_edge.hpp" +#include "util/deallocating_vector.hpp" + +#include + +namespace osrm +{ +namespace contractor +{ + +struct ContractedEdgeContainer +{ + private: + using MergedFlags = std::uint8_t; + static constexpr auto ALL_FLAGS = 0xFF; + + static bool mergeCompare(const QueryEdge &lhs, const QueryEdge &rhs) + { + return std::tie(lhs.source, + lhs.target, + lhs.data.shortcut, + lhs.data.turn_id, + lhs.data.weight, + lhs.data.duration, + lhs.data.forward, + lhs.data.backward) < std::tie(rhs.source, + rhs.target, + rhs.data.shortcut, + rhs.data.turn_id, + rhs.data.weight, + rhs.data.duration, + rhs.data.forward, + rhs.data.backward); + } + + static bool mergable(const QueryEdge &lhs, const QueryEdge &rhs) + { + // only true if both are equal + return !mergeCompare(lhs, rhs) && !mergeCompare(rhs, lhs); + } + + public: + void Insert(util::DeallocatingVector new_edges) + { + BOOST_ASSERT(edges.size() == 0); + BOOST_ASSERT(flags.empty()); + + edges = std::move(new_edges); + flags.resize(edges.size(), ALL_FLAGS); + } + + void Merge(util::DeallocatingVector new_edges) + { + BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT); + + const MergedFlags flag = 1 << index++; + + std::vector merged_flags; + merged_flags.reserve(flags.size() * 1.1); + util::DeallocatingVector merged_edges; + merged_edges.reserve(edges.size() * 1.1); + + auto flags_iter = flags.begin(); + // destructive iterators, this is single-pass only + // FIXME using dbegin() dend() will result in segfaults. + auto edges_iter = edges.dbegin(); + auto edges_end = edges.dend(); + auto new_edges_iter = new_edges.dbegin(); + auto new_edges_end = new_edges.dend(); + + while (edges_iter != edges_end && new_edges_iter != new_edges_end) + { + while (edges_iter != edges_end && mergeCompare(*edges_iter, *new_edges_iter)) + { + merged_edges.push_back(*edges_iter); + merged_flags.push_back(*flags_iter); + edges_iter++; + flags_iter++; + } + + if (edges_iter == edges_end) + { + break; + } + + while (new_edges_iter != new_edges_end && mergeCompare(*new_edges_iter, *edges_iter)) + { + merged_edges.push_back(*new_edges_iter); + merged_flags.push_back(flag); + new_edges_iter++; + } + + if (new_edges_iter == new_edges_end) + { + break; + } + + while (edges_iter != edges_end && new_edges_iter != new_edges_end && + mergable(*edges_iter, *new_edges_iter)) + { + merged_edges.push_back(*edges_iter); + merged_flags.push_back(*flags_iter | flag); + + edges_iter++; + flags_iter++; + new_edges_iter++; + } + } + + while (edges_iter != edges_end) + { + BOOST_ASSERT(new_edges_iter == new_edges_end); + merged_edges.push_back(*edges_iter++); + merged_flags.push_back(*flags_iter++); + } + + while (new_edges_iter != new_edges_end) + { + BOOST_ASSERT(edges_iter == edges_end); + merged_edges.push_back(*new_edges_iter++); + merged_flags.push_back(flag); + } + + flags = std::move(merged_flags); + edges = std::move(merged_edges); + } + + auto MakeEdgeFilters() const + { + std::vector> filters(index); + for (const auto flag_index : util::irange(0, index)) + { + MergedFlags mask = 1 << flag_index; + for (const auto flag : flags) + { + filters[flag_index].push_back(flag & mask); + } + } + + return filters; + } + + std::size_t index = 0; + std::vector flags; + util::DeallocatingVector edges; +}; +} +} + +#endif diff --git a/include/contractor/contractor_config.hpp b/include/contractor/contractor_config.hpp index d4b1818cc..23dc32959 100644 --- a/include/contractor/contractor_config.hpp +++ b/include/contractor/contractor_config.hpp @@ -43,12 +43,9 @@ namespace contractor struct ContractorConfig final : storage::IOConfig { ContractorConfig() - : IOConfig( - { - ".osrm.ebg", - }, - {}, - {".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}), + : IOConfig({".osrm.ebg", ".osrm.ebg_nodes", ".osrm.properties"}, + {}, + {".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}), requested_num_threads(0) { } diff --git a/include/contractor/contractor_dijkstra.hpp b/include/contractor/contractor_dijkstra.hpp deleted file mode 100644 index a9d449053..000000000 --- a/include/contractor/contractor_dijkstra.hpp +++ /dev/null @@ -1,50 +0,0 @@ -#ifndef OSRM_CONTRACTOR_DIJKSTRA_HPP -#define OSRM_CONTRACTOR_DIJKSTRA_HPP - -#include "contractor/contractor_graph.hpp" -#include "contractor/contractor_heap.hpp" -#include "util/typedefs.hpp" - -#include - -namespace osrm -{ -namespace contractor -{ - -// allow access to the heap itself, add Dijkstra functionality on top -class ContractorDijkstra -{ - public: - ContractorDijkstra(std::size_t heap_size); - - // search the graph up - void Run(const unsigned number_of_targets, - const int node_limit, - const int weight_limit, - const NodeID forbidden_node, - const ContractorGraph &graph); - - // adaption of the heap interface - void Clear(); - bool WasInserted(const NodeID node) const; - void Insert(const NodeID node, - const ContractorHeap::WeightType weight, - const ContractorHeap::DataType &data); - - // cannot be const due to node-hash access in the binary heap :( - ContractorHeap::WeightType GetKey(const NodeID node); - - private: - void RelaxNode(const NodeID node, - const int node_weight, - const NodeID forbidden_node, - const ContractorGraph &graph); - - ContractorHeap heap; -}; - -} // namespace contractor -} // namespace osrm - -#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP diff --git a/include/contractor/contractor_search.hpp b/include/contractor/contractor_search.hpp new file mode 100644 index 000000000..246228980 --- /dev/null +++ b/include/contractor/contractor_search.hpp @@ -0,0 +1,26 @@ +#ifndef OSRM_CONTRACTOR_SEARCH_HPP +#define OSRM_CONTRACTOR_SEARCH_HPP + +#include "contractor/contractor_graph.hpp" +#include "contractor/contractor_heap.hpp" + +#include "util/typedefs.hpp" + +#include + +namespace osrm +{ +namespace contractor +{ + +void search(ContractorHeap &heap, + const ContractorGraph &graph, + const unsigned number_of_targets, + const int node_limit, + const EdgeWeight weight_limit, + const NodeID forbidden_node); + +} // namespace contractor +} // namespace osrm + +#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP diff --git a/include/contractor/files.hpp b/include/contractor/files.hpp index 88e8dda76..3fbad54c1 100644 --- a/include/contractor/files.hpp +++ b/include/contractor/files.hpp @@ -16,54 +16,85 @@ namespace files { // reads .osrm.core template -void readCoreMarker(const boost::filesystem::path &path, CoreVectorT &is_core_node) +void readCoreMarker(const boost::filesystem::path &path, std::vector &cores) { static_assert(util::is_view_or_vector::value, - "is_core_node must be a vector"); + "cores must be a vector of boolean vectors"); storage::io::FileReader reader(path, storage::io::FileReader::VerifyFingerprint); - storage::serialization::read(reader, is_core_node); + auto num_cores = reader.ReadElementCount64(); + cores.resize(num_cores); + for (const auto index : util::irange(0, num_cores)) + { + storage::serialization::read(reader, cores[index]); + } } // writes .osrm.core template -void writeCoreMarker(const boost::filesystem::path &path, const CoreVectorT &is_core_node) +void writeCoreMarker(const boost::filesystem::path &path, const std::vector &cores) { static_assert(util::is_view_or_vector::value, - "is_core_node must be a vector"); + "cores must be a vector of boolean vectors"); storage::io::FileWriter writer(path, storage::io::FileWriter::GenerateFingerprint); - storage::serialization::write(writer, is_core_node); + writer.WriteElementCount64(cores.size()); + for (const auto &core : cores) + { + storage::serialization::write(writer, core); + } } // reads .osrm.hsgr file -template -inline void readGraph(const boost::filesystem::path &path, unsigned &checksum, QueryGraphT &graph) +template +inline void readGraph(const boost::filesystem::path &path, + unsigned &checksum, + QueryGraphT &graph, + std::vector &edge_filter) { static_assert(std::is_same::value || std::is_same::value, "graph must be of type QueryGraph<>"); + static_assert(std::is_same>::value || + std::is_same>::value, + "edge_filter must be a container of vector or vector_view"); const auto fingerprint = storage::io::FileReader::VerifyFingerprint; storage::io::FileReader reader{path, fingerprint}; reader.ReadInto(checksum); util::serialization::read(reader, graph); + auto count = reader.ReadElementCount64(); + edge_filter.resize(count); + for (const auto index : util::irange(0, count)) + { + storage::serialization::read(reader, edge_filter[index]); + } } // writes .osrm.hsgr file -template -inline void -writeGraph(const boost::filesystem::path &path, unsigned checksum, const QueryGraphT &graph) +template +inline void writeGraph(const boost::filesystem::path &path, + unsigned checksum, + const QueryGraphT &graph, + const std::vector &edge_filter) { static_assert(std::is_same::value || std::is_same::value, "graph must be of type QueryGraph<>"); + static_assert(std::is_same>::value || + std::is_same>::value, + "edge_filter must be a container of vector or vector_view"); const auto fingerprint = storage::io::FileWriter::GenerateFingerprint; storage::io::FileWriter writer{path, fingerprint}; writer.WriteOne(checksum); util::serialization::write(writer, graph); + writer.WriteElementCount64(edge_filter.size()); + for (const auto &filter : edge_filter) + { + storage::serialization::write(writer, filter); + } } // reads .levels file diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp index 649955011..a6a3891c2 100644 --- a/include/contractor/graph_contractor.hpp +++ b/include/contractor/graph_contractor.hpp @@ -3,6 +3,8 @@ #include "contractor/contractor_graph.hpp" +#include "util/filtered_graph.hpp" + #include #include @@ -11,21 +13,29 @@ namespace osrm namespace contractor { -using LevelAndCore = std::tuple, std::vector>; +std::vector contractGraph(ContractorGraph &graph, + std::vector node_is_uncontracted, + std::vector node_is_contractable, + std::vector node_weights, + double core_factor = 1.0); -LevelAndCore contractGraph(ContractorGraph &graph, - std::vector cached_node_levels, - std::vector node_weights, - double core_factor = 1.0); - -// Overload for contracting withcout cache -inline LevelAndCore contractGraph(ContractorGraph &graph, - std::vector node_weights, - double core_factor = 1.0) +// Overload for contracting all nodes +inline auto contractGraph(ContractorGraph &graph, + std::vector node_weights, + double core_factor = 1.0) { - return contractGraph(graph, {}, std::move(node_weights), core_factor); + return contractGraph(graph, {}, {}, std::move(node_weights), core_factor); } +// Overload no contracted nodes +inline auto contractGraph(ContractorGraph &graph, + std::vector node_is_contractable, + std::vector node_weights, + double core_factor = 1.0) +{ + return contractGraph( + graph, {}, std::move(node_is_contractable), std::move(node_weights), core_factor); +} } // namespace contractor } // namespace osrm diff --git a/include/contractor/graph_contractor_adaptors.hpp b/include/contractor/graph_contractor_adaptors.hpp index d1ddefb48..f509a5d9f 100644 --- a/include/contractor/graph_contractor_adaptors.hpp +++ b/include/contractor/graph_contractor_adaptors.hpp @@ -23,7 +23,8 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp for (const auto &input_edge : input_edge_list) { - BOOST_ASSERT(input_edge.data.weight < INVALID_EDGE_WEIGHT); + if (input_edge.data.weight == INVALID_EDGE_WEIGHT) + continue; #ifndef NDEBUG const unsigned int constexpr DAY_IN_DECI_SECONDS = 24 * 60 * 60 * 10; @@ -124,7 +125,7 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp return ContractorGraph{number_of_nodes, edges}; } -template inline util::DeallocatingVector toEdges(ContractorGraph graph) +template inline util::DeallocatingVector toEdges(GraphT graph) { util::DeallocatingVector edges; diff --git a/include/contractor/query_edge.hpp b/include/contractor/query_edge.hpp index 06c1f0fbf..a1d24f861 100644 --- a/include/contractor/query_edge.hpp +++ b/include/contractor/query_edge.hpp @@ -21,6 +21,17 @@ struct QueryEdge { } + EdgeData(const NodeID turn_id, + const bool shortcut, + const EdgeWeight weight, + const EdgeWeight duration, + const bool forward, + const bool backward) + : turn_id(turn_id), shortcut(shortcut), weight(weight), duration(duration), + forward(forward), backward(backward) + { + } + template EdgeData(const OtherT &other) { weight = other.weight; diff --git a/include/engine/algorithm.hpp b/include/engine/algorithm.hpp index f6a4bc65a..a8da0236f 100644 --- a/include/engine/algorithm.hpp +++ b/include/engine/algorithm.hpp @@ -79,6 +79,9 @@ template <> struct HasManyToManySearch final : std::true_type template <> struct HasGetTileTurns final : std::true_type { }; +template <> struct HasExcludeFlags final : std::true_type +{ +}; // Algorithms supported by Contraction Hierarchies with core // the rest is disabled because of performance reasons @@ -94,6 +97,9 @@ template <> struct HasMapMatching final : std::true_type template <> struct HasGetTileTurns final : std::true_type { }; +template <> struct HasExcludeFlags final : std::true_type +{ +}; // Algorithms supported by Multi-Level Dijkstra template <> struct HasAlternativePathSearch final : std::true_type diff --git a/include/engine/datafacade/algorithm_datafacade.hpp b/include/engine/datafacade/algorithm_datafacade.hpp index 046639d92..6036bb331 100644 --- a/include/engine/datafacade/algorithm_datafacade.hpp +++ b/include/engine/datafacade/algorithm_datafacade.hpp @@ -8,6 +8,7 @@ #include "partition/cell_storage.hpp" #include "partition/multi_level_partition.hpp" +#include "util/filtered_graph.hpp" #include "util/integer_range.hpp" namespace osrm @@ -22,14 +23,13 @@ using CH = routing_algorithms::ch::Algorithm; using CoreCH = routing_algorithms::corech::Algorithm; using MLD = routing_algorithms::mld::Algorithm; -using EdgeRange = util::range; - template class AlgorithmDataFacade; template <> class AlgorithmDataFacade { public: using EdgeData = contractor::QueryEdge::EdgeData; + using EdgeRange = util::filtered_range>; // search graph access virtual unsigned GetNumberOfNodes() const = 0; @@ -42,10 +42,6 @@ template <> class AlgorithmDataFacade virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0; - virtual EdgeID BeginEdges(const NodeID n) const = 0; - - virtual EdgeID EndEdges(const NodeID n) const = 0; - virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; // searches for a specific edge @@ -73,6 +69,7 @@ template <> class AlgorithmDataFacade { public: using EdgeData = extractor::EdgeBasedEdge::EdgeData; + using EdgeRange = util::range; // search graph access virtual unsigned GetNumberOfNodes() const = 0; @@ -85,10 +82,6 @@ template <> class AlgorithmDataFacade virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0; - virtual EdgeID BeginEdges(const NodeID n) const = 0; - - virtual EdgeID EndEdges(const NodeID n) const = 0; - virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0; virtual const partition::MultiLevelPartitionView &GetMultiLevelPartition() const = 0; diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 8ab1226bc..5824d7fa7 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -31,6 +31,7 @@ #include "util/exception.hpp" #include "util/exception_utils.hpp" +#include "util/filtered_graph.hpp" #include "util/guidance/bearing_class.hpp" #include "util/guidance/entry_class.hpp" #include "util/guidance/turn_bearing.hpp" @@ -68,7 +69,7 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade : public datafacade::AlgorithmDataFacade { private: - using QueryGraph = contractor::QueryGraphView; + using QueryGraph = util::FilteredGraphView; using GraphNode = QueryGraph::NodeArrayEntry; using GraphEdge = QueryGraph::EdgeArrayEntry; @@ -77,7 +78,9 @@ class ContiguousInternalMemoryAlgorithmDataFacade : public datafacade::Algor // allocator that keeps the allocation data std::shared_ptr allocator; - void InitializeGraphPointer(storage::DataLayout &data_layout, char *memory_block) + void InitializeGraphPointer(storage::DataLayout &data_layout, + char *memory_block, + const std::size_t exclude_index) { auto graph_nodes_ptr = data_layout.GetBlockPtr( memory_block, storage::DataLayout::CH_GRAPH_NODE_LIST); @@ -85,24 +88,34 @@ class ContiguousInternalMemoryAlgorithmDataFacade : public datafacade::Algor auto graph_edges_ptr = data_layout.GetBlockPtr( memory_block, storage::DataLayout::CH_GRAPH_EDGE_LIST); + auto filter_block_id = static_cast( + storage::DataLayout::CH_EDGE_FILTER_0 + exclude_index); + + auto edge_filter_ptr = data_layout.GetBlockPtr(memory_block, filter_block_id); + util::vector_view node_list( graph_nodes_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_NODE_LIST]); util::vector_view edge_list( graph_edges_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_EDGE_LIST]); - m_query_graph = QueryGraph(node_list, edge_list); + + util::vector_view edge_filter(edge_filter_ptr, + data_layout.num_entries[filter_block_id]); + m_query_graph = QueryGraph({node_list, edge_list}, edge_filter); } public: ContiguousInternalMemoryAlgorithmDataFacade( - std::shared_ptr allocator_) + std::shared_ptr allocator_, std::size_t exclude_index) : allocator(std::move(allocator_)) { - InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory()); + InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index); } - void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block) + void InitializeInternalPointers(storage::DataLayout &data_layout, + char *memory_block, + const std::size_t exclude_index) { - InitializeGraphPointer(data_layout, memory_block); + InitializeGraphPointer(data_layout, memory_block, exclude_index); } // search graph access @@ -122,10 +135,6 @@ class ContiguousInternalMemoryAlgorithmDataFacade : public datafacade::Algor return m_query_graph.GetEdgeData(e); } - EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph.BeginEdges(n); } - - EdgeID EndEdges(const NodeID n) const override final { return m_query_graph.EndEdges(n); } - EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final { return m_query_graph.GetAdjacentEdgeRange(node); @@ -166,32 +175,37 @@ class ContiguousInternalMemoryAlgorithmDataFacade // allocator that keeps the allocation data std::shared_ptr allocator; - void InitializeCoreInformationPointer(storage::DataLayout &data_layout, char *memory_block) + void InitializeCoreInformationPointer(storage::DataLayout &data_layout, + char *memory_block, + const std::size_t exclude_index) { - auto core_marker_ptr = - data_layout.GetBlockPtr(memory_block, storage::DataLayout::CH_CORE_MARKER); - util::vector_view is_core_node( - core_marker_ptr, data_layout.num_entries[storage::DataLayout::CH_CORE_MARKER]); + auto core_block_id = static_cast( + storage::DataLayout::CH_CORE_MARKER_0 + exclude_index); + auto core_marker_ptr = data_layout.GetBlockPtr(memory_block, core_block_id); + util::vector_view is_core_node(core_marker_ptr, + data_layout.num_entries[core_block_id]); m_is_core_node = std::move(is_core_node); } public: ContiguousInternalMemoryAlgorithmDataFacade( - std::shared_ptr allocator_) + std::shared_ptr allocator_, const std::size_t exclude_index) : allocator(std::move(allocator_)) { - InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory()); + InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index); } - void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block) + void InitializeInternalPointers(storage::DataLayout &data_layout, + char *memory_block, + const std::size_t exclude_index) { - InitializeCoreInformationPointer(data_layout, memory_block); + InitializeCoreInformationPointer(data_layout, memory_block, exclude_index); } bool IsCoreNode(const NodeID id) const override final { - BOOST_ASSERT(id < m_is_core_node.size()); - return m_is_core_node[id]; + BOOST_ASSERT(m_is_core_node.empty() || id < m_is_core_node.size()); + return !m_is_core_node.empty() || m_is_core_node[id]; } }; @@ -942,7 +956,7 @@ class ContiguousInternalMemoryDataFacade ContiguousInternalMemoryDataFacade(std::shared_ptr allocator, const std::size_t exclude_index) : ContiguousInternalMemoryDataFacadeBase(allocator, exclude_index), - ContiguousInternalMemoryAlgorithmDataFacade(allocator) + ContiguousInternalMemoryAlgorithmDataFacade(allocator, exclude_index) { } @@ -957,7 +971,7 @@ class ContiguousInternalMemoryDataFacade final ContiguousInternalMemoryDataFacade(std::shared_ptr allocator, const std::size_t exclude_index) : ContiguousInternalMemoryDataFacade(allocator, exclude_index), - ContiguousInternalMemoryAlgorithmDataFacade(allocator) + ContiguousInternalMemoryAlgorithmDataFacade(allocator, exclude_index) { } @@ -1130,10 +1144,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade : public Algo return query_graph.GetEdgeData(e); } - EdgeID BeginEdges(const NodeID n) const override final { return query_graph.BeginEdges(n); } - - EdgeID EndEdges(const NodeID n) const override final { return query_graph.EndEdges(n); } - EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final { return query_graph.GetAdjacentEdgeRange(node); diff --git a/include/engine/engine.hpp b/include/engine/engine.hpp index 2031e4e89..d21baff5c 100644 --- a/include/engine/engine.hpp +++ b/include/engine/engine.hpp @@ -176,7 +176,7 @@ bool Engine::CheckCompability(const Engin auto mem = storage::makeSharedMemory(barrier.data().region); auto layout = reinterpret_cast(mem->Ptr()); - return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER) > + return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER_0) > sizeof(std::uint64_t) + sizeof(util::FingerPrint); } else @@ -185,9 +185,10 @@ bool Engine::CheckCompability(const Engin return false; storage::io::FileReader in(config.storage_config.GetPath(".osrm.core"), storage::io::FileReader::VerifyFingerprint); + in.ReadElementCount64(); // number of core markers + const auto number_of_core_markers = in.ReadElementCount64(); - auto size = in.GetSize(); - return size > sizeof(std::uint64_t); + return number_of_core_markers > 0; } } diff --git a/include/partition/bisection_graph_view.hpp b/include/partition/bisection_graph_view.hpp index 056141720..ae4a430c9 100644 --- a/include/partition/bisection_graph_view.hpp +++ b/include/partition/bisection_graph_view.hpp @@ -28,11 +28,13 @@ class BisectionGraphView // Construction either for a subrange, or for a full range BisectionGraphView(const BisectionGraph &graph); BisectionGraphView(const BisectionGraph &graph, - const ConstNodeIterator begin, - const ConstNodeIterator end); + const ConstNodeIterator begin, + const ConstNodeIterator end); // construction from a different view, no need to keep the graph around - BisectionGraphView(const BisectionGraphView &view, const ConstNodeIterator begin, const ConstNodeIterator end); + BisectionGraphView(const BisectionGraphView &view, + const ConstNodeIterator begin, + const ConstNodeIterator end); // Number of nodes _in this sub-graph. std::size_t NumberOfNodes() const; diff --git a/include/partition/dinic_max_flow.hpp b/include/partition/dinic_max_flow.hpp index 8882c9890..5d99f0e1a 100644 --- a/include/partition/dinic_max_flow.hpp +++ b/include/partition/dinic_max_flow.hpp @@ -83,8 +83,9 @@ class DinicMaxFlow const SourceSinkNodes &source_nodes) const; // Builds an actual cut result from a level graph - MinCut - MakeCut(const BisectionGraphView &view, const LevelGraph &levels, const std::size_t flow_value) const; + MinCut MakeCut(const BisectionGraphView &view, + const LevelGraph &levels, + const std::size_t flow_value) const; }; } // namespace partition diff --git a/include/partition/inertial_flow.hpp b/include/partition/inertial_flow.hpp index 96eb2a548..b1fae6ef2 100644 --- a/include/partition/inertial_flow.hpp +++ b/include/partition/inertial_flow.hpp @@ -1,8 +1,8 @@ #ifndef OSRM_PARTITION_INERTIAL_FLOW_HPP_ #define OSRM_PARTITION_INERTIAL_FLOW_HPP_ -#include "partition/dinic_max_flow.hpp" #include "partition/bisection_graph_view.hpp" +#include "partition/dinic_max_flow.hpp" namespace osrm { diff --git a/include/storage/shared_datatype.hpp b/include/storage/shared_datatype.hpp index aef3f6639..3851a115e 100644 --- a/include/storage/shared_datatype.hpp +++ b/include/storage/shared_datatype.hpp @@ -26,6 +26,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA", "CLASSES_LIST", "CH_GRAPH_NODE_LIST", "CH_GRAPH_EDGE_LIST", + "CH_EDGE_FILTER_0", + "CH_EDGE_FILTER_1", + "CH_EDGE_FILTER_2", + "CH_EDGE_FILTER_3", + "CH_EDGE_FILTER_4", + "CH_EDGE_FILTER_5", + "CH_EDGE_FILTER_6", + "CH_EDGE_FILTER_7", "COORDINATE_LIST", "OSM_NODE_ID_LIST", "TURN_INSTRUCTION", @@ -43,7 +51,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA", "HSGR_CHECKSUM", "TIMESTAMP", "FILE_INDEX_PATH", - "CH_CORE_MARKER", + "CH_CORE_MARKER_0", + "CH_CORE_MARKER_1", + "CH_CORE_MARKER_2", + "CH_CORE_MARKER_3", + "CH_CORE_MARKER_4", + "CH_CORE_MARKER_5", + "CH_CORE_MARKER_6", + "CH_CORE_MARKER_7", "DATASOURCES_NAMES", "PROPERTIES", "BEARING_CLASSID", @@ -98,6 +113,14 @@ struct DataLayout CLASSES_LIST, CH_GRAPH_NODE_LIST, CH_GRAPH_EDGE_LIST, + CH_EDGE_FILTER_0, + CH_EDGE_FILTER_1, + CH_EDGE_FILTER_2, + CH_EDGE_FILTER_3, + CH_EDGE_FILTER_4, + CH_EDGE_FILTER_5, + CH_EDGE_FILTER_6, + CH_EDGE_FILTER_7, COORDINATE_LIST, OSM_NODE_ID_LIST, TURN_INSTRUCTION, @@ -115,7 +138,14 @@ struct DataLayout HSGR_CHECKSUM, TIMESTAMP, FILE_INDEX_PATH, - CH_CORE_MARKER, + CH_CORE_MARKER_0, + CH_CORE_MARKER_1, + CH_CORE_MARKER_2, + CH_CORE_MARKER_3, + CH_CORE_MARKER_4, + CH_CORE_MARKER_5, + CH_CORE_MARKER_6, + CH_CORE_MARKER_7, DATASOURCES_NAMES, PROPERTIES, BEARING_CLASSID, @@ -179,7 +209,7 @@ struct DataLayout inline uint64_t GetBlockSize(BlockID bid) const { // special bit encoding - if (bid == CH_CORE_MARKER) + if (bid >= CH_CORE_MARKER_0 && bid <= CH_CORE_MARKER_7) { return (num_entries[bid] / 32 + 1) * entry_size[bid]; } diff --git a/include/util/dynamic_graph.hpp b/include/util/dynamic_graph.hpp index f2eaa211d..522d2c12d 100644 --- a/include/util/dynamic_graph.hpp +++ b/include/util/dynamic_graph.hpp @@ -33,6 +33,27 @@ template void write(storage::io::FileWriter &writer, const DynamicGraph &graph); } +namespace detail +{ +// These types need to live outside of DynamicGraph +// to be not dependable. We need this for transforming graphs +// with different data. + +template struct DynamicNode +{ + // index of the first edge + EdgeIterator first_edge; + // amount of edges + unsigned edges; +}; + +template struct DynamicEdge +{ + NodeIterator target; + EdgeDataT data; +}; +} + template class DynamicGraph { public: @@ -41,6 +62,11 @@ template class DynamicGraph using EdgeIterator = std::uint32_t; using EdgeRange = range; + using Node = detail::DynamicNode; + using Edge = detail::DynamicEdge; + + template friend class DynamicGraph; + class InputEdge { public: @@ -120,6 +146,9 @@ template class DynamicGraph } } + // Copy&move for the same data + // + DynamicGraph(const DynamicGraph &other) { number_of_nodes = other.number_of_nodes; @@ -130,7 +159,7 @@ template class DynamicGraph edge_list = other.edge_list; } - DynamicGraph&operator=(const DynamicGraph &other) + DynamicGraph &operator=(const DynamicGraph &other) { auto copy_other = other; *this = std::move(other); @@ -159,6 +188,38 @@ template class DynamicGraph return *this; } + // Removes all edges to and from nodes for which filter(node_id) returns false + template auto Filter(Pred filter) const & + { + DynamicGraph other; + + other.number_of_nodes = number_of_nodes; + other.number_of_edges = static_cast(number_of_edges); + other.edge_list.reserve(edge_list.size()); + other.node_array.resize(node_array.size()); + + NodeID node_id = 0; + std::transform( + node_array.begin(), node_array.end(), other.node_array.begin(), [&](const Node &node) { + const EdgeIterator first_edge = other.edge_list.size(); + if (filter(node_id++)) + { + std::copy_if(edge_list.begin() + node.first_edge, + edge_list.begin() + node.first_edge + node.edges, + std::back_inserter(other.edge_list), + [&](const auto &edge) { return filter(edge.target); }); + const unsigned num_edges = other.edge_list.size() - first_edge; + return Node{first_edge, num_edges}; + } + else + { + return Node{first_edge, 0}; + } + }); + + return other; + } + unsigned GetNumberOfNodes() const { return number_of_nodes; } unsigned GetNumberOfEdges() const { return number_of_edges; } @@ -366,6 +427,7 @@ template class DynamicGraph for (auto edge : GetAdjacentEdgeRange(node)) { edge_list[edge].target = old_to_new_node[edge_list[edge].target]; + BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID); old_to_new_edge[edge] = new_edge_index++; } node_array[node].first_edge = new_first_edge; @@ -400,20 +462,6 @@ template class DynamicGraph edge_list[edge].target = (std::numeric_limits::max)(); } - struct Node - { - // index of the first edge - EdgeIterator first_edge; - // amount of edges - unsigned edges; - }; - - struct Edge - { - NodeIterator target; - EdgeDataT data; - }; - NodeIterator number_of_nodes; std::atomic_uint number_of_edges; diff --git a/include/util/exclude_flag.hpp b/include/util/exclude_flag.hpp new file mode 100644 index 000000000..ffec32c80 --- /dev/null +++ b/include/util/exclude_flag.hpp @@ -0,0 +1,35 @@ +#ifndef OSRM_UTIL_EXCLUDE_FLAG_HPP +#define OSRM_UTIL_EXCLUDE_FLAG_HPP + +#include "extractor/node_data_container.hpp" +#include "extractor/profile_properties.hpp" + +namespace osrm +{ +namespace util +{ + +inline std::vector> +excludeFlagsToNodeFilter(const NodeID number_of_nodes, + const extractor::EdgeBasedNodeDataContainer &node_data, + const extractor::ProfileProperties &properties) +{ + std::vector> filters; + for (auto mask : properties.excludable_classes) + { + if (mask != extractor::INAVLID_CLASS_DATA) + { + std::vector allowed_nodes(number_of_nodes); + for (const auto node : util::irange(0, number_of_nodes)) + { + allowed_nodes[node] = (node_data.GetClassData(node) & mask) == 0; + } + filters.push_back(std::move(allowed_nodes)); + } + } + return filters; +} +} +} + +#endif diff --git a/include/util/filtered_graph.hpp b/include/util/filtered_graph.hpp new file mode 100644 index 000000000..c4df051d0 --- /dev/null +++ b/include/util/filtered_graph.hpp @@ -0,0 +1,165 @@ +#ifndef OSRM_UTIL_FILTERED_GRAPH_HPP +#define OSRM_UTIL_FILTERED_GRAPH_HPP + +#include "storage/shared_memory_ownership.hpp" + +#include "util/dynamic_graph.hpp" +#include "util/filtered_integer_range.hpp" +#include "util/static_graph.hpp" +#include "util/vector_view.hpp" + +namespace osrm +{ +namespace util +{ +namespace detail +{ +template class FilteredGraphImpl; + +// For static graphs we can save the filters as a static vector since +// we don't modify the structure of the graph. This also makes it easy to +// swap out the filter. +template +class FilteredGraphImpl, Ownership> +{ + template using Vector = util::ViewOrVector; + + public: + using Graph = util::StaticGraph; + using EdgeIterator = typename Graph::EdgeIterator; + using NodeIterator = typename Graph::NodeIterator; + using NodeArrayEntry = typename Graph::NodeArrayEntry; + using EdgeArrayEntry = typename Graph::EdgeArrayEntry; + using EdgeRange = util::filtered_range>; + + unsigned GetNumberOfNodes() const { return graph.GetNumberOfNodes(); } + + unsigned GetNumberOfEdges() const { return graph.GetNumberOfEdges(); } + + unsigned GetOutDegree(const NodeIterator n) const + { + auto range = graph.GetAdjacentEdgeRange(n); + return std::count_if(range.begin(), range.end(), [this](const EdgeIterator edge) { + return edge_filter[edge]; + }); + } + + inline NodeIterator GetTarget(const EdgeIterator e) const + { + BOOST_ASSERT(edge_filter[e]); + return graph.GetTarget(e); + } + + auto &GetEdgeData(const EdgeIterator e) + { + BOOST_ASSERT(edge_filter[e]); + return graph.GetEdgeData(e); + } + + const auto &GetEdgeData(const EdgeIterator e) const + { + BOOST_ASSERT(edge_filter[e]); + return graph.GetEdgeData(e); + } + + auto GetAdjacentEdgeRange(const NodeIterator n) const + { + return EdgeRange{graph.BeginEdges(n), graph.EndEdges(n), edge_filter}; + } + + // searches for a specific edge + EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const + { + for (const auto edge : GetAdjacentEdgeRange(from)) + { + if (to == GetTarget(edge)) + { + return edge; + } + } + return SPECIAL_EDGEID; + } + + template + EdgeIterator + FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const + { + static_assert(traits::HasDataMember::value, + "Filtering on .data not possible without .data member attribute"); + + EdgeIterator smallest_edge = SPECIAL_EDGEID; + EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT; + for (auto edge : GetAdjacentEdgeRange(from)) + { + const NodeID target = GetTarget(edge); + const auto &data = GetEdgeData(edge); + if (target == to && data.weight < smallest_weight && + std::forward(filter)(data)) + { + smallest_edge = edge; + smallest_weight = data.weight; + } + } + return smallest_edge; + } + + EdgeIterator FindEdgeInEitherDirection(const NodeIterator from, const NodeIterator to) const + { + EdgeIterator tmp = FindEdge(from, to); + return (SPECIAL_NODEID != tmp ? tmp : FindEdge(to, from)); + } + + EdgeIterator + FindEdgeIndicateIfReverse(const NodeIterator from, const NodeIterator to, bool &result) const + { + EdgeIterator current_iterator = FindEdge(from, to); + if (SPECIAL_NODEID == current_iterator) + { + current_iterator = FindEdge(to, from); + if (SPECIAL_NODEID != current_iterator) + { + result = true; + } + } + return current_iterator; + } + + FilteredGraphImpl() = default; + + FilteredGraphImpl(Graph graph, Vector edge_filter_) + : graph(std::move(graph)), edge_filter(std::move(edge_filter_)) + { + BOOST_ASSERT(edge_filter.empty() || edge_filter.size() == graph.GetNumberOfEdges()); + } + + // Takes a graph and a function that maps EdgeID to true + // if the edge should be included in the graph. + template + FilteredGraphImpl(Graph graph, Pred filter) + : graph(std::move(graph)), edge_filter(graph.GetNumberOfEdges()) + { + auto edge_ids = util::irange(0, graph.GetNumberOfEdges()); + std::transform(edge_ids.begin(), edge_ids.end(), edge_filter.begin(), filter); + } + + void Renumber(const std::vector &old_to_new_node) + { + graph.Renumber(old_to_new_node); + // FIXME the edge filter needs to be renumbered with a different permutation + // util::inplacePermutation(edge_filter.begin(), edge_filter.end(), old_to_new_node); + } + + private: + Graph graph; + Vector edge_filter; +}; +} + +template +using FilteredGraphContainer = detail::FilteredGraphImpl; +template +using FilteredGraphView = detail::FilteredGraphImpl; +} +} + +#endif diff --git a/include/util/filtered_integer_range.hpp b/include/util/filtered_integer_range.hpp new file mode 100644 index 000000000..dd3f61b3d --- /dev/null +++ b/include/util/filtered_integer_range.hpp @@ -0,0 +1,101 @@ +#ifndef FILTERED_INTEGER_RANGE_HPP +#define FILTERED_INTEGER_RANGE_HPP + +#include +#include + +#include + +#include + +namespace osrm +{ +namespace util +{ + +// This implements a single-pass integer range. +// We need our own implementation here because using boost::adaptor::filtered() has +// the problem that the return-type depends on the lambda-type you pass into the function. +// That makes it unsuitable to use in interface where we would expect all filtered ranges +// to be off the same type. + +template +class filtered_integer_iterator + : public boost::iterator_facade, + Integer, + boost::single_pass_traversal_tag, + Integer> +{ + typedef boost::iterator_facade, + Integer, + boost::single_pass_traversal_tag, + Integer> + base_t; + + public: + typedef typename base_t::value_type value_type; + typedef typename base_t::difference_type difference_type; + typedef typename base_t::reference reference; + typedef std::random_access_iterator_tag iterator_category; + + filtered_integer_iterator() : value(), filter(nullptr) {} + explicit filtered_integer_iterator(value_type x, value_type end_value, const Filter *filter) + : value(x), end_value(end_value), filter(filter) + { + } + + private: + void increment() + { + do + { + ++value; + } while (value < end_value && !(*filter)[value]); + } + bool equal(const filtered_integer_iterator &other) const { return value == other.value; } + reference dereference() const { return value; } + + friend class ::boost::iterator_core_access; + value_type value; + value_type end_value; + const Filter *filter; +}; + +template class filtered_range +{ + public: + typedef filtered_integer_iterator const_iterator; + typedef filtered_integer_iterator iterator; + + filtered_range(Integer begin, Integer end, const Filter &filter) : last(end, end, &filter) + { + while (begin < end && !filter[begin]) + { + begin++; + } + + iter = iterator(begin, end, &filter); + } + + iterator begin() const noexcept { return iter; } + iterator end() const noexcept { return last; } + + private: + iterator iter; + iterator last; +}; + +// convenience function to construct an integer range with type deduction +template +filtered_range +filtered_irange(const Integer first, + const Integer last, + const Filter &filter, + typename std::enable_if::value>::type * = 0) noexcept +{ + return filtered_range(first, last, filter); +} +} +} + +#endif // INTEGER_RANGE_HPP diff --git a/include/util/query_heap.hpp b/include/util/query_heap.hpp index 5f83502ba..779f1f446 100644 --- a/include/util/query_heap.hpp +++ b/include/util/query_heap.hpp @@ -152,6 +152,7 @@ class QueryHeap void Insert(NodeID node, Weight weight, const Data &data) { + BOOST_ASSERT(node < std::numeric_limits::max()); const auto index = static_cast(inserted_nodes.size()); const auto handle = heap.push(std::make_pair(weight, index)); inserted_nodes.emplace_back(HeapNode{handle, node, weight, data}); diff --git a/src/contractor/contractor.cpp b/src/contractor/contractor.cpp index a1b24c5a4..d958c498d 100644 --- a/src/contractor/contractor.cpp +++ b/src/contractor/contractor.cpp @@ -1,4 +1,6 @@ #include "contractor/contractor.hpp" +#include "contractor/contract_excludable_graph.hpp" +#include "contractor/contracted_edge_container.hpp" #include "contractor/crc32_processor.hpp" #include "contractor/files.hpp" #include "contractor/graph_contractor.hpp" @@ -6,6 +8,7 @@ #include "extractor/compressed_edge_container.hpp" #include "extractor/edge_based_graph_factory.hpp" +#include "extractor/files.hpp" #include "extractor/node_based_edge.hpp" #include "storage/io.hpp" @@ -14,6 +17,8 @@ #include "util/exception.hpp" #include "util/exception_utils.hpp" +#include "util/exclude_flag.hpp" +#include "util/filtered_graph.hpp" #include "util/graph_loader.hpp" #include "util/integer_range.hpp" #include "util/log.hpp" @@ -42,6 +47,11 @@ int Contractor::Run() throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)" + SOURCE_REF); } + if (config.use_cached_priority) + { + util::Log(logWARNING) << "Using cached priorities is deprecated and they will be ignored."; + } + TIMER_START(preparing); util::Log() << "Reading node weights."; @@ -63,41 +73,36 @@ int Contractor::Run() // Contracting the edge-expanded graph TIMER_START(contraction); - std::vector is_core_node; - std::vector node_levels; - if (config.use_cached_priority) + + std::vector> node_filters; { - files::readLevels(config.GetPath(".osrm.level"), node_levels); + extractor::EdgeBasedNodeDataContainer node_data; + extractor::files::readNodeData(config.GetPath(".osrm.ebg_nodes"), node_data); + + extractor::ProfileProperties properties; + extractor::files::readProfileProperties(config.GetPath(".osrm.properties"), properties); + + node_filters = util::excludeFlagsToNodeFilter(max_edge_id + 1, node_data, properties); } - util::DeallocatingVector contracted_edge_list; - { // own scope to not keep the contractor around - auto contractor_graph = toContractorGraph(max_edge_id+1, std::move(edge_based_edge_list)); - std::tie(node_levels, is_core_node) = contractGraph(contractor_graph, - std::move(node_levels), - std::move(node_weights), - config.core_factor); + RangebasedCRC32 crc32_calculator; + const unsigned checksum = crc32_calculator(edge_based_edge_list); - contracted_edge_list = toEdges(std::move(contractor_graph)); - } + QueryGraph query_graph; + std::vector> edge_filters; + std::vector> cores; + std::tie(query_graph, edge_filters, cores) = + contractExcludableGraph(toContractorGraph(max_edge_id + 1, std::move(edge_based_edge_list)), + std::move(node_weights), + std::move(node_filters), + config.core_factor); TIMER_STOP(contraction); - + util::Log() << "Contracted graph has " << query_graph.GetNumberOfEdges() << " edges."; util::Log() << "Contraction took " << TIMER_SEC(contraction) << " sec"; - { - RangebasedCRC32 crc32_calculator; - const unsigned checksum = crc32_calculator(contracted_edge_list); + files::writeGraph(config.GetPath(".osrm.hsgr"), checksum, query_graph, edge_filters); - files::writeGraph(config.GetPath(".osrm.hsgr"), - checksum, - QueryGraph{max_edge_id + 1, std::move(contracted_edge_list)}); - } - - files::writeCoreMarker(config.GetPath(".osrm.core"), is_core_node); - if (!config.use_cached_priority) - { - files::writeLevels(config.GetPath(".osrm.level"), node_levels); - } + files::writeCoreMarker(config.GetPath(".osrm.core"), cores); TIMER_STOP(preparing); diff --git a/src/contractor/contractor_dijkstra.cpp b/src/contractor/contractor_search.cpp similarity index 50% rename from src/contractor/contractor_dijkstra.cpp rename to src/contractor/contractor_search.cpp index c638e7ef2..e010723b8 100644 --- a/src/contractor/contractor_dijkstra.cpp +++ b/src/contractor/contractor_search.cpp @@ -1,23 +1,65 @@ -#include "contractor/contractor_dijkstra.hpp" +#include "contractor/contractor_search.hpp" + +#include "contractor/contractor_graph.hpp" +#include "contractor/contractor_heap.hpp" namespace osrm { namespace contractor { -ContractorDijkstra::ContractorDijkstra(const std::size_t heap_size) : heap(heap_size) {} +namespace +{ +void relaxNode(ContractorHeap &heap, + const ContractorGraph &graph, + const NodeID node, + const EdgeWeight node_weight, + const NodeID forbidden_node) +{ + const short current_hop = heap.GetData(node).hop + 1; + for (auto edge : graph.GetAdjacentEdgeRange(node)) + { + const auto &data = graph.GetEdgeData(edge); + if (!data.forward) + { + continue; + } + const NodeID to = graph.GetTarget(edge); + BOOST_ASSERT(to != SPECIAL_NODEID); + if (forbidden_node == to) + { + continue; + } + const EdgeWeight to_weight = node_weight + data.weight; -void ContractorDijkstra::Run(const unsigned number_of_targets, - const int node_limit, - const EdgeWeight weight_limit, - const NodeID forbidden_node, - const ContractorGraph &graph) + // New Node discovered -> Add to Heap + Node Info Storage + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_weight, ContractorHeapData{current_hop, false}); + } + // Found a shorter Path -> Update weight + else if (to_weight < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_weight); + heap.GetData(to).hop = current_hop; + } + } +} +} + +void search(ContractorHeap &heap, + const ContractorGraph &graph, + const unsigned number_of_targets, + const int node_limit, + const EdgeWeight weight_limit, + const NodeID forbidden_node) { int nodes = 0; unsigned number_of_targets_found = 0; while (!heap.Empty()) { const NodeID node = heap.DeleteMin(); + BOOST_ASSERT(node != SPECIAL_NODEID); const auto node_weight = heap.GetKey(node); if (++nodes > node_limit) { @@ -38,59 +80,8 @@ void ContractorDijkstra::Run(const unsigned number_of_targets, } } - RelaxNode(node, node_weight, forbidden_node, graph); + relaxNode(heap, graph, node, node_weight, forbidden_node); } } - -void ContractorDijkstra::RelaxNode(const NodeID node, - const EdgeWeight node_weight, - const NodeID forbidden_node, - const ContractorGraph &graph) -{ - const short current_hop = heap.GetData(node).hop + 1; - for (auto edge : graph.GetAdjacentEdgeRange(node)) - { - const ContractorEdgeData &data = graph.GetEdgeData(edge); - if (!data.forward) - { - continue; - } - const NodeID to = graph.GetTarget(edge); - if (forbidden_node == to) - { - continue; - } - const EdgeWeight to_weight = node_weight + data.weight; - - // New Node discovered -> Add to Heap + Node Info Storage - if (!heap.WasInserted(to)) - { - heap.Insert(to, to_weight, ContractorHeapData{current_hop, false}); - } - // Found a shorter Path -> Update weight - else if (to_weight < GetKey(to)) - { - heap.DecreaseKey(to, to_weight); - heap.GetData(to).hop = current_hop; - } - } } - -void ContractorDijkstra::Clear() { heap.Clear(); } - -bool ContractorDijkstra::WasInserted(const NodeID node) const { return heap.WasInserted(node); } - -void ContractorDijkstra::Insert(const NodeID node, - const ContractorHeap::WeightType weight, - const ContractorHeap::DataType &data) -{ - heap.Insert(node, weight, data); } - -ContractorHeap::WeightType ContractorDijkstra::GetKey(const NodeID node) -{ - return heap.GetKey(node); -} - -} // namespace contractor -} // namespace osrm diff --git a/src/contractor/graph_contractor.cpp b/src/contractor/graph_contractor.cpp index 3174757ad..93b33704d 100644 --- a/src/contractor/graph_contractor.cpp +++ b/src/contractor/graph_contractor.cpp @@ -1,6 +1,6 @@ #include "contractor/graph_contractor.hpp" -#include "contractor/contractor_dijkstra.hpp" #include "contractor/contractor_graph.hpp" +#include "contractor/contractor_search.hpp" #include "contractor/query_edge.hpp" #include "util/deallocating_vector.hpp" #include "util/integer_range.hpp" @@ -14,6 +14,7 @@ #include #include +#include #include #include @@ -29,10 +30,10 @@ namespace { struct ContractorThreadData { - ContractorDijkstra dijkstra; + ContractorHeap heap; std::vector inserted_edges; std::vector neighbours; - explicit ContractorThreadData(NodeID nodes) : dijkstra(nodes) {} + explicit ContractorThreadData(NodeID nodes) : heap(nodes) {} }; struct ContractorNodeData @@ -42,34 +43,37 @@ struct ContractorNodeData using NodeLevel = float; ContractorNodeData(std::size_t number_of_nodes, - std::vector priorities_, + std::vector uncontracted_nodes_, + std::vector contractable_, std::vector weights_) - : is_core(number_of_nodes, true), priorities(std::move(priorities_)), - weights(std::move(weights_)) + : is_core(std::move(uncontracted_nodes_)), contractable(std::move(contractable_)), + priorities(number_of_nodes), weights(std::move(weights_)), depths(number_of_nodes, 0) { - // no cached priorities - if (priorities.empty()) + if (contractable.empty()) { - depths.resize(number_of_nodes, 0); - levels.resize(number_of_nodes); - priorities.resize(number_of_nodes); + contractable.resize(number_of_nodes, true); + } + if (is_core.empty()) + { + is_core.resize(number_of_nodes, true); } } void Renumber(const std::vector &old_to_new) { - util::inplacePermutation(priorities.begin(), priorities.end(), old_to_new); - util::inplacePermutation(weights.begin(), weights.end(), old_to_new); - util::inplacePermutation(levels.begin(), levels.end(), old_to_new); - util::inplacePermutation(is_core.begin(), is_core.end(), old_to_new); - util::inplacePermutation(depths.begin(), depths.end(), old_to_new); + tbb::parallel_invoke( + [&] { util::inplacePermutation(priorities.begin(), priorities.end(), old_to_new); }, + [&] { util::inplacePermutation(weights.begin(), weights.end(), old_to_new); }, + [&] { util::inplacePermutation(is_core.begin(), is_core.end(), old_to_new); }, + [&] { util::inplacePermutation(contractable.begin(), contractable.end(), old_to_new); }, + [&] { util::inplacePermutation(depths.begin(), depths.end(), old_to_new); }); } std::vector is_core; + std::vector contractable; std::vector priorities; std::vector weights; std::vector depths; - std::vector levels; }; struct ContractionStats @@ -87,7 +91,8 @@ struct ContractionStats struct RemainingNodeData { - RemainingNodeData() : id(0), is_independent(false) {} + RemainingNodeData() = default; + RemainingNodeData(NodeID id, bool is_independent) : id(id), is_independent(is_independent) {} NodeID id : 31; bool is_independent : 1; }; @@ -102,8 +107,7 @@ struct ThreadDataContainer auto &ref = data.local(exists); if (!exists) { - // ref = std::make_shared(number_of_nodes); - ref = std::make_shared(4000); + ref = std::make_shared(number_of_nodes); } return ref.get(); @@ -130,14 +134,14 @@ inline bool Bias(const util::XORFastHash<> &fast_hash, const NodeID a, const Nod return a < b; } -template +template void ContractNode(ContractorThreadData *data, const ContractorGraph &graph, const NodeID node, std::vector &node_weights, ContractionStats *stats = nullptr) { - auto &dijkstra = data->dijkstra; + auto &heap = data->heap; std::size_t inserted_edges_size = data->inserted_edges.size(); std::vector &inserted_edges = data->inserted_edges; constexpr bool SHORTCUT_ARC = true; @@ -164,8 +168,8 @@ void ContractNode(ContractorThreadData *data, continue; } - dijkstra.Clear(); - dijkstra.Insert(source, 0, ContractorHeapData{}); + heap.Clear(); + heap.Insert(source, 0, ContractorHeapData{}); EdgeWeight max_weight = 0; unsigned number_of_targets = 0; @@ -230,9 +234,9 @@ void ContractNode(ContractorThreadData *data, continue; } max_weight = std::max(max_weight, path_weight); - if (!dijkstra.WasInserted(target)) + if (!heap.WasInserted(target)) { - dijkstra.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true}); + heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true}); ++number_of_targets; } } @@ -240,12 +244,12 @@ void ContractNode(ContractorThreadData *data, if (RUNSIMULATION) { const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000; - dijkstra.Run(number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, max_weight, node, graph); + search(heap, graph, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, max_weight, node); } else { const int constexpr FULL_SEARCH_SPACE_SIZE = 2000; - dijkstra.Run(number_of_targets, FULL_SEARCH_SPACE_SIZE, max_weight, node, graph); + search(heap, graph, number_of_targets, FULL_SEARCH_SPACE_SIZE, max_weight, node); } for (auto out_edge : graph.GetAdjacentEdgeRange(node)) { @@ -259,7 +263,7 @@ void ContractNode(ContractorThreadData *data, continue; const EdgeWeight path_weight = in_data.weight + out_data.weight; - const EdgeWeight weight = dijkstra.GetKey(target); + const EdgeWeight weight = heap.GetKey(target); if (path_weight < weight) { if (RUNSIMULATION) @@ -371,9 +375,9 @@ void RenumberGraph(ContractorGraph &graph, const std::vector &old_to_new /* Reorder nodes for better locality during contraction */ void RenumberData(std::vector &remaining_nodes, - std::vector &new_to_old_node_id, - ContractorNodeData &node_data, - ContractorGraph &graph) + std::vector &new_to_old_node_id, + ContractorNodeData &node_data, + ContractorGraph &graph) { std::vector current_to_new_node_id(graph.GetNumberOfNodes(), SPECIAL_NODEID); @@ -407,7 +411,8 @@ void RenumberData(std::vector &remaining_nodes, RenumberGraph(graph, current_to_new_node_id); } -float EvaluateNodePriority(const ContractionStats &stats, const ContractorNodeData::NodeDepth node_depth) +float EvaluateNodePriority(const ContractionStats &stats, + const ContractorNodeData::NodeDepth node_depth) { // Result will contain the priority float result; @@ -476,14 +481,18 @@ bool UpdateNodeNeighbours(ContractorNodeData &node_data, // re-evaluate priorities of neighboring nodes for (const NodeID u : neighbours) { - node_data.priorities[u] = EvaluateNodePriority( - SimulateNodeContraction(data, graph, u, node_data.weights), node_data.depths[u]); + if (node_data.contractable[u]) + { + node_data.priorities[u] = EvaluateNodePriority( + SimulateNodeContraction(data, graph, u, node_data.weights), node_data.depths[u]); + } } return true; } bool IsNodeIndependent(const util::XORFastHash<> &hash, const std::vector &priorities, + const std::vector &new_to_old_node_id, const ContractorGraph &graph, ContractorThreadData *const data, const NodeID node) @@ -509,7 +518,7 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash, } // tie breaking if (std::abs(priority - target_priority) < std::numeric_limits::epsilon() && - Bias(hash, node, target)) + Bias(hash, new_to_old_node_id[node], new_to_old_node_id[target])) { return false; } @@ -538,7 +547,7 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash, } // tie breaking if (std::abs(priority - target_priority) < std::numeric_limits::epsilon() && - Bias(hash, node, target)) + Bias(hash, new_to_old_node_id[node], new_to_old_node_id[target])) { return false; } @@ -548,15 +557,16 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash, } } -LevelAndCore contractGraph(ContractorGraph &graph, - std::vector cached_node_levels_, - std::vector node_weights_, - double core_factor) +std::vector contractGraph(ContractorGraph &graph, + std::vector node_is_uncontracted_, + std::vector node_is_contractable_, + std::vector node_weights_, + double core_factor) { + BOOST_ASSERT(node_weights_.size() == graph.GetNumberOfNodes()); util::XORFastHash<> fast_hash; // for the preperation we can use a big grain size, which is much faster (probably cache) - const constexpr size_t InitGrainSize = 100000; const constexpr size_t PQGrainSize = 100000; // auto_partitioner will automatically increase the blocksize if we have // a lot of data. It is *important* for the last loop iterations @@ -575,56 +585,71 @@ LevelAndCore contractGraph(ContractorGraph &graph, // Fill the map with an identiy mapping std::iota(new_to_old_node_id.begin(), new_to_old_node_id.end(), 0); - bool use_cached_node_priorities = !cached_node_levels_.empty(); - ContractorNodeData node_data{ - graph.GetNumberOfNodes(), std::move(cached_node_levels_), std::move(node_weights_)}; + ContractorNodeData node_data{graph.GetNumberOfNodes(), + std::move(node_is_uncontracted_), + std::move(node_is_contractable_), + std::move(node_weights_)}; - std::vector remaining_nodes(number_of_nodes); - // initialize priorities in parallel - tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, InitGrainSize), - [&remaining_nodes](const tbb::blocked_range &range) { - for (auto x = range.begin(), end = range.end(); x != end; ++x) - { - remaining_nodes[x].id = x; - } - }); + std::vector remaining_nodes; + remaining_nodes.reserve(number_of_nodes); + for (auto node : util::irange(0, number_of_nodes)) + { + if (node_data.is_core[node]) + { + if (node_data.contractable[node]) + { + remaining_nodes.emplace_back(node, false); + } + else + { + node_data.priorities[node] = + std::numeric_limits::max(); + } + } + else + { + node_data.priorities[node] = 0; + } + } - if (!use_cached_node_priorities) { util::UnbufferedLog log; log << "initializing node priorities..."; - tbb::parallel_for( - tbb::blocked_range(0, number_of_nodes, PQGrainSize), - [&node_data, &graph, &thread_data_list](const tbb::blocked_range &range) { - ContractorThreadData *data = thread_data_list.GetThreadData(); - for (auto x = range.begin(), end = range.end(); x != end; ++x) - { - node_data.priorities[x] = EvaluateNodePriority( - SimulateNodeContraction(data, graph, x, node_data.weights), - node_data.depths[x]); - } - }); + tbb::parallel_for(tbb::blocked_range(0, remaining_nodes.size(), PQGrainSize), + [&](const auto &range) { + ContractorThreadData *data = thread_data_list.GetThreadData(); + for (auto x = range.begin(), end = range.end(); x != end; ++x) + { + auto node = remaining_nodes[x].id; + BOOST_ASSERT(node_data.contractable[node]); + node_data.priorities[node] = EvaluateNodePriority( + SimulateNodeContraction(data, graph, node, node_data.weights), + node_data.depths[node]); + } + }); log << " ok."; } - util::Log() << "preprocessing " << number_of_nodes << " nodes..."; + auto number_of_core_nodes = std::max(0, (1 - core_factor) * number_of_nodes); + auto number_of_nodes_to_contract = remaining_nodes.size() - number_of_core_nodes; + util::Log() << "preprocessing " << number_of_nodes_to_contract << " (" + << (number_of_nodes_to_contract / (float)number_of_nodes * 100.) << "%) nodes..."; util::UnbufferedLog log; - util::Percent p(log, number_of_nodes); + util::Percent p(log, remaining_nodes.size()); const util::XORFastHash<> hash; unsigned current_level = 0; - std::size_t next_renumbering = number_of_nodes * 0.65 * core_factor; - while (remaining_nodes.size() > 1 && - number_of_contracted_nodes < static_cast(number_of_nodes * core_factor)) + std::size_t next_renumbering = number_of_nodes * 0.35; + while (remaining_nodes.size() > number_of_core_nodes) { - if (number_of_contracted_nodes > next_renumbering) + if (remaining_nodes.size() < next_renumbering) { RenumberData(remaining_nodes, new_to_old_node_id, node_data, graph); log << "[renumbered]"; // only one renumbering for now - next_renumbering = number_of_nodes; + next_renumbering = 0; } tbb::parallel_for( @@ -635,8 +660,8 @@ LevelAndCore contractGraph(ContractorGraph &graph, for (auto i = range.begin(), end = range.end(); i != end; ++i) { const NodeID node = remaining_nodes[i].id; - remaining_nodes[i].is_independent = - IsNodeIndependent(hash, node_data.priorities, graph, data, node); + remaining_nodes[i].is_independent = IsNodeIndependent( + hash, node_data.priorities, new_to_old_node_id, graph, data, node); } }); @@ -649,21 +674,6 @@ LevelAndCore contractGraph(ContractorGraph &graph, std::distance(remaining_nodes.begin(), begin_independent_nodes); auto end_independent_nodes_idx = remaining_nodes.size(); - if (!use_cached_node_priorities) - { - // write out contraction level - tbb::parallel_for( - tbb::blocked_range( - begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize), - [&](const auto &range) { - for (auto position = range.begin(), end = range.end(); position != end; - ++position) - { - node_data.levels[remaining_nodes[position].id] = current_level; - } - }); - } - // contract independent nodes tbb::parallel_for( tbb::blocked_range( @@ -673,11 +683,17 @@ LevelAndCore contractGraph(ContractorGraph &graph, for (auto position = range.begin(), end = range.end(); position != end; ++position) { const NodeID node = remaining_nodes[position].id; - node_data.is_core[node] = false; ContractNode(data, graph, node, node_data.weights); } }); + // core flags need to be set in serial since vector is not thread safe + for (auto position : + util::irange(begin_independent_nodes_idx, end_independent_nodes_idx)) + { + node_data.is_core[remaining_nodes[position].id] = false; + } + tbb::parallel_for( tbb::blocked_range( begin_independent_nodes_idx, end_independent_nodes_idx, DeleteGrainSize), @@ -691,12 +707,10 @@ LevelAndCore contractGraph(ContractorGraph &graph, }); // make sure we really sort each block - tbb::parallel_for(thread_data_list.data.range(), - [&](const auto &range) { - for (auto &data : range) - tbb::parallel_sort(data->inserted_edges.begin(), - data->inserted_edges.end()); - }); + tbb::parallel_for(thread_data_list.data.range(), [&](const auto &range) { + for (auto &data : range) + tbb::parallel_sort(data->inserted_edges.begin(), data->inserted_edges.end()); + }); // insert new edges for (auto &data : thread_data_list.data) @@ -706,7 +720,7 @@ LevelAndCore contractGraph(ContractorGraph &graph, const EdgeID current_edge_ID = graph.FindEdge(edge.source, edge.target); if (current_edge_ID != SPECIAL_EDGEID) { - ContractorGraph::EdgeData ¤t_data = graph.GetEdgeData(current_edge_ID); + auto ¤t_data = graph.GetEdgeData(current_edge_ID); if (current_data.shortcut && edge.data.forward == current_data.forward && edge.data.backward == current_data.backward) { @@ -724,22 +738,17 @@ LevelAndCore contractGraph(ContractorGraph &graph, data->inserted_edges.clear(); } - if (!use_cached_node_priorities) - { - tbb::parallel_for( - tbb::blocked_range( - begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize), - [&]( - const auto &range) { - ContractorThreadData *data = thread_data_list.GetThreadData(); - for (auto position = range.begin(), end = range.end(); position != end; - ++position) - { - NodeID node = remaining_nodes[position].id; - UpdateNodeNeighbours(node_data, data, graph, node); - } - }); - } + tbb::parallel_for( + tbb::blocked_range( + begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize), + [&](const auto &range) { + ContractorThreadData *data = thread_data_list.GetThreadData(); + for (auto position = range.begin(), end = range.end(); position != end; ++position) + { + NodeID node = remaining_nodes[position].id; + UpdateNodeNeighbours(node_data, data, graph, node); + } + }); // remove contracted nodes from the pool BOOST_ASSERT(end_independent_nodes_idx - begin_independent_nodes_idx > 0); @@ -749,21 +758,11 @@ LevelAndCore contractGraph(ContractorGraph &graph, p.PrintStatus(number_of_contracted_nodes); ++current_level; } - log << "\n"; - - util::Log() << "[core] " << remaining_nodes.size() << " nodes " << graph.GetNumberOfEdges() - << " edges."; node_data.Renumber(new_to_old_node_id); RenumberGraph(graph, new_to_old_node_id); - if (remaining_nodes.size() <= 2) - { - // in this case we don't need core markers since we fully contracted the graph - node_data.is_core.clear(); - } - - return LevelAndCore {std::move(node_data.levels), std::move(node_data.is_core)}; + return std::move(node_data.is_core); } } // namespace contractor diff --git a/src/customize/customizer.cpp b/src/customize/customizer.cpp index 311cf580b..2b926c244 100644 --- a/src/customize/customizer.cpp +++ b/src/customize/customizer.cpp @@ -14,6 +14,7 @@ #include "updater/updater.hpp" +#include "util/exclude_flag.hpp" #include "util/log.hpp" #include "util/timing_util.hpp" @@ -94,27 +95,6 @@ auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config, return edge_based_graph; } -std::vector> -excludeFlagsToNodeFilter(const MultiLevelEdgeBasedGraph &graph, - const extractor::EdgeBasedNodeDataContainer &node_data, - const extractor::ProfileProperties &properties) -{ - std::vector> filters; - for (auto mask : properties.excludable_classes) - { - if (mask != extractor::INAVLID_CLASS_DATA) - { - std::vector allowed_nodes(graph.GetNumberOfNodes(), true); - for (const auto node : util::irange(0, graph.GetNumberOfNodes())) - { - allowed_nodes[node] = (node_data.GetClassData(node) & mask) == 0; - } - filters.push_back(std::move(allowed_nodes)); - } - } - return filters; -} - std::vector customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph, const partition::CellStorage &storage, const CellCustomizer &customizer, @@ -157,7 +137,7 @@ int Customizer::Run(const CustomizationConfig &config) util::Log() << "Loading partition data took " << TIMER_SEC(loading_data) << " seconds"; TIMER_START(cell_customize); - auto filter = excludeFlagsToNodeFilter(graph, node_data, properties); + auto filter = util::excludeFlagsToNodeFilter(graph.GetNumberOfNodes(), node_data, properties); auto metrics = customizeFilteredMetrics(graph, storage, CellCustomizer{mlp}, filter); TIMER_STOP(cell_customize); util::Log() << "Cells customization took " << TIMER_SEC(cell_customize) << " seconds"; diff --git a/src/partition/bisection_graph_view.cpp b/src/partition/bisection_graph_view.cpp index 4c3299fa5..859db0605 100644 --- a/src/partition/bisection_graph_view.cpp +++ b/src/partition/bisection_graph_view.cpp @@ -16,15 +16,15 @@ BisectionGraphView::BisectionGraphView(const BisectionGraph &bisection_graph_) } BisectionGraphView::BisectionGraphView(const BisectionGraph &bisection_graph_, - const BisectionGraph::ConstNodeIterator begin_, - const BisectionGraph::ConstNodeIterator end_) + const BisectionGraph::ConstNodeIterator begin_, + const BisectionGraph::ConstNodeIterator end_) : bisection_graph(bisection_graph_), begin(begin_), end(end_) { } BisectionGraphView::BisectionGraphView(const BisectionGraphView &other_view, - const BisectionGraph::ConstNodeIterator begin_, - const BisectionGraph::ConstNodeIterator end_) + const BisectionGraph::ConstNodeIterator begin_, + const BisectionGraph::ConstNodeIterator end_) : BisectionGraphView(other_view.bisection_graph, begin_, end_) { } @@ -42,7 +42,10 @@ BisectionGraph::ConstNodeIterator BisectionGraphView::Begin() const { return beg BisectionGraph::ConstNodeIterator BisectionGraphView::End() const { return end; } -const BisectionGraphView::NodeT &BisectionGraphView::Node(const NodeID nid) const { return *(begin + nid); } +const BisectionGraphView::NodeT &BisectionGraphView::Node(const NodeID nid) const +{ + return *(begin + nid); +} const BisectionGraphView::EdgeT &BisectionGraphView::Edge(const EdgeID eid) const { diff --git a/src/partition/dinic_max_flow.cpp b/src/partition/dinic_max_flow.cpp index c71de204d..754843bac 100644 --- a/src/partition/dinic_max_flow.cpp +++ b/src/partition/dinic_max_flow.cpp @@ -18,7 +18,8 @@ namespace const auto constexpr INVALID_LEVEL = std::numeric_limits::max(); -auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set, const BisectionGraphView &view) +auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set, + const BisectionGraphView &view) { return [&](const NodeID nid) { const auto is_not_contained = [&set](const BisectionEdge &edge) { diff --git a/src/partition/inertial_flow.cpp b/src/partition/inertial_flow.cpp index 8dc3133f6..c0f02bc46 100644 --- a/src/partition/inertial_flow.cpp +++ b/src/partition/inertial_flow.cpp @@ -33,7 +33,8 @@ struct SpatialOrder // Creates a spatial order of n * sources "first" and n * sink "last" node ids. // The slope determines the spatial order for sorting node coordinates. -SpatialOrder makeSpatialOrder(const BisectionGraphView &view, const double ratio, const double slope) +SpatialOrder +makeSpatialOrder(const BisectionGraphView &view, const double ratio, const double slope) { struct NodeWithCoordinate { @@ -89,8 +90,10 @@ SpatialOrder makeSpatialOrder(const BisectionGraphView &view, const double ratio } // Makes n cuts with different spatial orders and returns the best. -DinicMaxFlow::MinCut -bestMinCut(const BisectionGraphView &view, const std::size_t n, const double ratio, const double balance) +DinicMaxFlow::MinCut bestMinCut(const BisectionGraphView &view, + const std::size_t n, + const double ratio, + const double balance) { DinicMaxFlow::MinCut best; best.num_edges = -1; diff --git a/src/partition/recursive_bisection_state.cpp b/src/partition/recursive_bisection_state.cpp index e1baf828a..42c6961c5 100644 --- a/src/partition/recursive_bisection_state.cpp +++ b/src/partition/recursive_bisection_state.cpp @@ -149,7 +149,8 @@ RecursiveBisectionState::PrePartitionWithSCC(const std::size_t small_component_s }(); if (!has_small_component) - views.push_back(BisectionGraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd())); + views.push_back( + BisectionGraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd())); // apply scc as bisections, we need scc_level bits for this with scc_levels = // ceil(log_2(components)) diff --git a/src/storage/storage.cpp b/src/storage/storage.cpp index 512b91cf3..a8d52b639 100644 --- a/src/storage/storage.cpp +++ b/src/storage/storage.cpp @@ -268,12 +268,30 @@ void Storage::PopulateLayout(DataLayout &layout) reader.Skip(1); // checksum auto num_nodes = reader.ReadVectorSize(); auto num_edges = reader.ReadVectorSize(); + auto num_metrics = reader.ReadElementCount64(); + + if (num_metrics > NUM_METRICS) + { + throw util::exception("Only " + std::to_string(NUM_METRICS) + + " metrics are supported at the same time."); + } layout.SetBlockSize(DataLayout::HSGR_CHECKSUM, 1); layout.SetBlockSize(DataLayout::CH_GRAPH_NODE_LIST, num_nodes); layout.SetBlockSize(DataLayout::CH_GRAPH_EDGE_LIST, num_edges); + + for (const auto index : util::irange(0, num_metrics)) + { + layout.SetBlockSize( + static_cast(DataLayout::CH_EDGE_FILTER_0 + index), num_edges); + } + for (const auto index : util::irange(num_metrics, NUM_METRICS)) + { + layout.SetBlockSize( + static_cast(DataLayout::CH_EDGE_FILTER_0 + index), 0); + } } else { @@ -282,6 +300,11 @@ void Storage::PopulateLayout(DataLayout &layout) 0); layout.SetBlockSize(DataLayout::CH_GRAPH_EDGE_LIST, 0); + for (const auto index : util::irange(0, NUM_METRICS)) + { + layout.SetBlockSize( + static_cast(DataLayout::CH_EDGE_FILTER_0 + index), 0); + } } // load rsearch tree size @@ -313,12 +336,36 @@ void Storage::PopulateLayout(DataLayout &layout) { io::FileReader core_marker_file(config.GetPath(".osrm.core"), io::FileReader::VerifyFingerprint); + const auto num_metrics = core_marker_file.ReadElementCount64(); + if (num_metrics > NUM_METRICS) + { + throw util::exception("Only " + std::to_string(NUM_METRICS) + + " metrics are supported at the same time."); + } + const auto number_of_core_markers = core_marker_file.ReadElementCount64(); - layout.SetBlockSize(DataLayout::CH_CORE_MARKER, number_of_core_markers); + for (const auto index : util::irange(0, num_metrics)) + { + layout.SetBlockSize( + static_cast(DataLayout::CH_CORE_MARKER_0 + index), + number_of_core_markers); + } + for (const auto index : util::irange(num_metrics, NUM_METRICS)) + { + layout.SetBlockSize( + static_cast(DataLayout::CH_CORE_MARKER_0 + index), 0); + } } else { - layout.SetBlockSize(DataLayout::CH_CORE_MARKER, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_0, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_1, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_2, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_3, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_4, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_5, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_6, 0); + layout.SetBlockSize(DataLayout::CH_CORE_MARKER_7, 0); } // load turn weight penalties @@ -577,8 +624,19 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr) util::vector_view edge_list( graph_edges_ptr, layout.num_entries[storage::DataLayout::CH_GRAPH_EDGE_LIST]); + std::vector> edge_filter; + for (auto index : util::irange(0, NUM_METRICS)) + { + auto block_id = + static_cast(storage::DataLayout::CH_EDGE_FILTER_0 + index); + auto data_ptr = layout.GetBlockPtr(memory_ptr, block_id); + auto num_entries = layout.num_entries[block_id]; + edge_filter.emplace_back(data_ptr, num_entries); + } + contractor::QueryGraphView graph_view(std::move(node_list), std::move(edge_list)); - contractor::files::readGraph(config.GetPath(".osrm.hsgr"), *checksum, graph_view); + contractor::files::readGraph( + config.GetPath(".osrm.hsgr"), *checksum, graph_view, edge_filter); } else { @@ -872,12 +930,17 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr) if (boost::filesystem::exists(config.GetPath(".osrm.core"))) { - auto core_marker_ptr = - layout.GetBlockPtr(memory_ptr, storage::DataLayout::CH_CORE_MARKER); - util::vector_view is_core_node( - core_marker_ptr, layout.num_entries[storage::DataLayout::CH_CORE_MARKER]); + std::vector> cores; + for (auto index : util::irange(0, NUM_METRICS)) + { + auto block_id = + static_cast(storage::DataLayout::CH_CORE_MARKER_0 + index); + auto data_ptr = layout.GetBlockPtr(memory_ptr, block_id); + auto num_entries = layout.num_entries[block_id]; + cores.emplace_back(data_ptr, num_entries); + } - contractor::files::readCoreMarker(config.GetPath(".osrm.core"), is_core_node); + contractor::files::readCoreMarker(config.GetPath(".osrm.core"), cores); } // load profile properties diff --git a/unit_tests/CMakeLists.txt b/unit_tests/CMakeLists.txt index 2aa7a9b77..ded629142 100644 --- a/unit_tests/CMakeLists.txt +++ b/unit_tests/CMakeLists.txt @@ -2,6 +2,10 @@ file(GLOB EngineTestsSources engine_tests.cpp engine/*.cpp) +file(GLOB ContractorTestsSources + contractor_tests.cpp + contractor/*.cpp) + file(GLOB ExtractorTestsSources extractor_tests.cpp extractor/*.cpp) @@ -44,6 +48,11 @@ add_executable(engine-tests ${EngineTestsSources} $ $ $) +add_executable(contractor-tests + EXCLUDE_FROM_ALL + ${ContractorTestsSources} + $ $ $) + add_executable(extractor-tests EXCLUDE_FROM_ALL ${ExtractorTestsSources} @@ -104,6 +113,7 @@ target_compile_definitions(library-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_D target_compile_definitions(library-extract-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_DATA_DIR="${TEST_DATA_DIR}") target_compile_definitions(library-contract-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_DATA_DIR="${TEST_DATA_DIR}") target_compile_definitions(updater-tests PRIVATE COMPILE_DEFINITIONS TEST_DATA_DIR="${UPDATER_TEST_DATA_DIR}") +target_compile_definitions(contractor-tests PRIVATE COMPILE_DEFINITIONS TEST_DATA_DIR="${TEST_DATA_DIR}") target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(library-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) @@ -113,6 +123,7 @@ target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(partition-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(customizer-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_include_directories(updater-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) +target_include_directories(contractor-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) @@ -124,6 +135,7 @@ target_link_libraries(library-extract-tests osrm_extract ${Boost_UNIT_TEST_FRAME target_link_libraries(library-contract-tests osrm_contract ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) target_link_libraries(server-tests osrm ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) target_link_libraries(util-tests ${UTIL_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) +target_link_libraries(contractor-tests ${CONTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}) add_custom_target(tests - DEPENDS engine-tests extractor-tests partition-tests updater-tests customizer-tests library-tests library-extract-tests server-tests util-tests) + DEPENDS engine-tests extractor-tests contractor-tests partition-tests updater-tests customizer-tests library-tests library-extract-tests server-tests util-tests) diff --git a/unit_tests/contractor/contracted_edge_container.cpp b/unit_tests/contractor/contracted_edge_container.cpp new file mode 100644 index 000000000..831159bc0 --- /dev/null +++ b/unit_tests/contractor/contracted_edge_container.cpp @@ -0,0 +1,109 @@ +#include "contractor/contracted_edge_container.hpp" + +#include "../common/range_tools.hpp" + +#include + +using namespace osrm; +using namespace osrm::contractor; + +namespace osrm +{ +namespace contractor +{ + +bool operator!=(const QueryEdge &lhs, const QueryEdge &rhs) { return !(lhs == rhs); } + +std::ostream &operator<<(std::ostream &out, const QueryEdge::EdgeData &data) +{ + out << "{" << data.turn_id << ", " << data.shortcut << ", " << data.duration << ", " + << data.weight << ", " << data.forward << ", " << data.backward << "}"; + return out; +} + +std::ostream &operator<<(std::ostream &out, const QueryEdge &edge) +{ + out << "{" << edge.source << ", " << edge.target << ", " << edge.data << "}"; + return out; +} +} +} + +BOOST_AUTO_TEST_SUITE(contracted_edge_container) + +BOOST_AUTO_TEST_CASE(merge_edge_of_multiple_graph) +{ + ContractedEdgeContainer container; + + util::DeallocatingVector edges; + edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{2, 0, {3, false, 3, 6, false, true}}); + edges.push_back(QueryEdge{2, 1, {4, false, 3, 6, false, true}}); + container.Insert(edges); + + edges.clear(); + edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}}); + edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}}); + container.Merge(edges); + + edges.clear(); + edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}}); + container.Merge(edges); + + util::DeallocatingVector reference_edges; + reference_edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{2, 0, {3, false, 3, 6, false, true}}); + reference_edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}}); + reference_edges.push_back(QueryEdge{2, 1, {4, false, 3, 6, false, true}}); + reference_edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}}); + CHECK_EQUAL_COLLECTIONS(container.edges, reference_edges); + + auto filters = container.MakeEdgeFilters(); + BOOST_CHECK_EQUAL(filters.size(), 2); + + REQUIRE_SIZE_RANGE(filters[0], 7); + CHECK_EQUAL_RANGE(filters[0], true, true, false, true, true, true, true); + + REQUIRE_SIZE_RANGE(filters[1], 7); + CHECK_EQUAL_RANGE(filters[1], true, true, true, true, false, true, false); +} + +BOOST_AUTO_TEST_CASE(merge_edge_of_multiple_disjoint_graph) +{ + ContractedEdgeContainer container; + + util::DeallocatingVector edges; + edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}}); + edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}}); + edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}}); + container.Merge(edges); + + edges.clear(); + edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}}); + container.Merge(edges); + + util::DeallocatingVector reference_edges; + reference_edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}}); + reference_edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}}); + reference_edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}}); + CHECK_EQUAL_COLLECTIONS(container.edges, reference_edges); + + auto filters = container.MakeEdgeFilters(); + BOOST_CHECK_EQUAL(filters.size(), 2); + + REQUIRE_SIZE_RANGE(filters[0], 5); + CHECK_EQUAL_RANGE(filters[0], true, true, false, true, true); + + REQUIRE_SIZE_RANGE(filters[1], 5); + CHECK_EQUAL_RANGE(filters[1], false, false, true, false, false); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/contractor/graph_contractor.cpp b/unit_tests/contractor/graph_contractor.cpp new file mode 100644 index 000000000..4222de76f --- /dev/null +++ b/unit_tests/contractor/graph_contractor.cpp @@ -0,0 +1,156 @@ +#include "contractor/graph_contractor.hpp" + +#include "../common/range_tools.hpp" + +#include +#include + +#include + +using namespace osrm; +using namespace osrm::contractor; + +BOOST_AUTO_TEST_SUITE(graph_contractor) + +using TestEdge = std::tuple; +ContractorGraph makeGraph(const std::vector &edges) +{ + std::vector input_edges; + auto id = 0u; + auto max_id = 0u; + for (const auto &edge : edges) + { + unsigned start; + unsigned target; + int weight; + std::tie(start, target, weight) = edge; + max_id = std::max(std::max(start, target), max_id); + input_edges.push_back(ContractorEdge{ + start, target, ContractorEdgeData{weight, weight * 2, id++, 0, false, true, false}}); + input_edges.push_back(ContractorEdge{ + target, start, ContractorEdgeData{weight, weight * 2, id++, 0, false, false, true}}); + } + std::sort(input_edges.begin(), input_edges.end()); + + return ContractorGraph{max_id + 1, std::move(input_edges)}; +} + +BOOST_AUTO_TEST_CASE(contract_graph) +{ + tbb::task_scheduler_init scheduler(1); + /* + * <--1--< + * (0) >--3--> (1) >--3--> (3) + * v ^ v ^ + * \ / \ | + * 1 1 1 1 + * \ ^ \ / + * >(5) > (4) > + */ + std::vector edges = {TestEdge{0, 1, 3}, + TestEdge{0, 5, 1}, + TestEdge{1, 3, 3}, + TestEdge{1, 4, 1}, + TestEdge{3, 1, 1}, + TestEdge{4, 3, 1}, + TestEdge{5, 1, 1}}; + auto reference_graph = makeGraph(edges); + + auto contracted_graph = reference_graph; + std::vector core = contractGraph(contracted_graph, {1, 1, 1, 1, 1, 1}); + + // This contraction order is dependent on the priority caculation in the contractor + // but deterministic for the same graph. + CHECK_EQUAL_RANGE(core, false, false, false, false, false, false); + + /* After contracting 0 and 2: + * + * Deltes edges 5 -> 0, 1 -> 0 + * + * <--1--< + * (0) ---3--> (1) >--3--> (3) + * \ ^ v ^ + * \ / \ | + * 1 1 1 1 + * \ ^ \ / + * >(5) > (4) > + */ + reference_graph.DeleteEdgesTo(5, 0); + reference_graph.DeleteEdgesTo(1, 0); + + /* After contracting 5: + * + * Deletes edges 1 -> 5 + * + * <--1--< + * (0) ---3--> (1) >--3--> (3) + * \ ^ v ^ + * \ / \ | + * 1 1 1 1 + * \ / \ / + * >(5) > (4) > + */ + reference_graph.DeleteEdgesTo(5, 0); + reference_graph.DeleteEdgesTo(1, 0); + + /* After contracting 3: + * + * Deletes edges 1 -> 3 + * Deletes edges 4 -> 3 + * Insert edge 4 -> 1 + * + * <--1--- + * (0) ---3--> (1) >--3--- (3) + * \ ^ v ^ | + * \ / \ \ | + * 1 1 1 2 1 + * \ / \ \ / + * >(5) > (4) > + */ + reference_graph.DeleteEdgesTo(1, 3); + reference_graph.DeleteEdgesTo(4, 3); + // Insert shortcut + reference_graph.InsertEdge(4, 1, {2, 4, 3, 0, true, true, false}); + + /* After contracting 4: + * + * Delete edges 1 -> 4 + * + * <--1--- + * (0) ---3--> (1) >--3--- (3) + * \ ^ v ^ | + * \ / \ \ | + * 1 1 1 2 1 + * \ / \ \ / + * >(5) \ (4) > + */ + reference_graph.DeleteEdgesTo(1, 4); + + /* After contracting 1: + * + * Delete no edges. + * + * <--1--- + * (0) ---3--> (1) >--3--- (3) + * \ ^ v ^ | + * \ / \ \ | + * 1 1 1 2 1 + * \ / \ \ / + * >(5) \ (4) > + */ + + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(0), 2); + BOOST_CHECK(contracted_graph.FindEdge(0, 1) != SPECIAL_EDGEID); + BOOST_CHECK(contracted_graph.FindEdge(0, 5) != SPECIAL_EDGEID); + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(1), 0); + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(2), 0); + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(3), 3); + BOOST_CHECK(contracted_graph.FindEdge(3, 1) != SPECIAL_EDGEID); + BOOST_CHECK(contracted_graph.FindEdge(3, 4) != SPECIAL_EDGEID); + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(4), 2); + BOOST_CHECK(contracted_graph.FindEdge(4, 1) != SPECIAL_EDGEID); + REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(5), 1); + BOOST_CHECK(contracted_graph.FindEdge(5, 1) != SPECIAL_EDGEID); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/contractor_tests.cpp b/unit_tests/contractor_tests.cpp new file mode 100644 index 000000000..c144d4d8a --- /dev/null +++ b/unit_tests/contractor_tests.cpp @@ -0,0 +1,7 @@ +#define BOOST_TEST_MODULE contractor tests + +#include + +/* + * This file will contain an automatically generated main function. + */ diff --git a/unit_tests/mocks/mock_datafacade.hpp b/unit_tests/mocks/mock_datafacade.hpp index 270af1e7a..0a09a537b 100644 --- a/unit_tests/mocks/mock_datafacade.hpp +++ b/unit_tests/mocks/mock_datafacade.hpp @@ -280,11 +280,9 @@ class MockAlgorithmDataFacade unsigned GetOutDegree(const NodeID /* n */) const override { return 0; } NodeID GetTarget(const EdgeID /* e */) const override { return SPECIAL_NODEID; } const EdgeData &GetEdgeData(const EdgeID /* e */) const override { return foo; } - EdgeID BeginEdges(const NodeID /* n */) const override { return SPECIAL_EDGEID; } - EdgeID EndEdges(const NodeID /* n */) const override { return SPECIAL_EDGEID; } - osrm::engine::datafacade::EdgeRange GetAdjacentEdgeRange(const NodeID /* node */) const override + EdgeRange GetAdjacentEdgeRange(const NodeID /* node */) const override { - return util::irange(static_cast(0), static_cast(0)); + return EdgeRange(static_cast(0), static_cast(0), {}); } EdgeID FindEdge(const NodeID /* from */, const NodeID /* to */) const override { diff --git a/unit_tests/partition/dinic.cpp b/unit_tests/partition/dinic.cpp index 87e0a17e2..ca77ce1f7 100644 --- a/unit_tests/partition/dinic.cpp +++ b/unit_tests/partition/dinic.cpp @@ -1,6 +1,6 @@ +#include "partition/bisection_graph_view.hpp" #include "partition/dinic_max_flow.hpp" #include "partition/graph_generator.hpp" -#include "partition/bisection_graph_view.hpp" #include "partition/recursive_bisection_state.hpp" #include diff --git a/unit_tests/partition/scc_integration.cpp b/unit_tests/partition/scc_integration.cpp index 46c70eca7..f0aeb025b 100644 --- a/unit_tests/partition/scc_integration.cpp +++ b/unit_tests/partition/scc_integration.cpp @@ -1,5 +1,5 @@ -#include "partition/graph_generator.hpp" #include "partition/bisection_graph_view.hpp" +#include "partition/graph_generator.hpp" #include "partition/recursive_bisection_state.hpp" #include diff --git a/unit_tests/util/dynamic_graph.cpp b/unit_tests/util/dynamic_graph.cpp index 1104f208c..c5b608fed 100644 --- a/unit_tests/util/dynamic_graph.cpp +++ b/unit_tests/util/dynamic_graph.cpp @@ -1,6 +1,8 @@ #include "util/dynamic_graph.hpp" #include "util/typedefs.hpp" +#include "../common/range_tools.hpp" + #include #include @@ -120,4 +122,41 @@ BOOST_AUTO_TEST_CASE(renumber_test) BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 2); } +BOOST_AUTO_TEST_CASE(filter_test) +{ + /* + * (0) -1-> (1) + * ^ ^ ^ + * 2 5 1 + * | | | + * (3) -3-> (4) + * <-4- + */ + std::vector input_edges = {TestInputEdge{0, 1, TestData{1}}, + TestInputEdge{3, 0, TestData{2}}, + TestInputEdge{3, 0, TestData{5}}, + TestInputEdge{3, 4, TestData{3}}, + TestInputEdge{4, 1, TestData{1}}, + TestInputEdge{4, 3, TestData{4}}}; + TestDynamicGraph simple_graph(5, input_edges); + + // only keep 0, 1, 4 -> filter out all edges from/to 3 + auto filtered_simple_graph = + simple_graph.Filter([](const NodeID node) { return node == 0 || node == 1 || node == 4; }); + + REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(0), 1); + CHECK_EQUAL_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(0), + filtered_simple_graph.FindEdge(0, 1)); + + REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(1), 0); + + REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(2), 0); + + REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(3), 0); + + REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(4), 1); + CHECK_EQUAL_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(4), + filtered_simple_graph.FindEdge(4, 1)); +} + BOOST_AUTO_TEST_SUITE_END() diff --git a/unit_tests/util/filtered_integer_range.cpp b/unit_tests/util/filtered_integer_range.cpp new file mode 100644 index 000000000..810bcf3fc --- /dev/null +++ b/unit_tests/util/filtered_integer_range.cpp @@ -0,0 +1,38 @@ +#include "util/filtered_integer_range.hpp" + +#include "../common/range_tools.hpp" + +#include +#include + +BOOST_AUTO_TEST_SUITE(bit_range_test) + +using namespace osrm; +using namespace osrm::util; + +BOOST_AUTO_TEST_CASE(filtered_irange_test) +{ + std::vector filter = {// 0 1 2 3 4 5 + true, + false, + false, + true, + true, + false}; + + CHECK_EQUAL_RANGE(filtered_irange(0, 2, filter), 0); + CHECK_EQUAL_RANGE(filtered_irange(1, 4, filter), 3); + CHECK_EQUAL_RANGE(filtered_irange(1, 5, filter), 3, 4); + CHECK_EQUAL_RANGE(filtered_irange(0, 6, filter), 0, 3, 4); + + auto empty_1 = filtered_irange(1, 3, filter); + auto empty_2 = filtered_irange(3, 3, filter); + auto empty_3 = filtered_irange(4, 4, filter); + auto empty_4 = filtered_irange(5, 5, filter); + BOOST_CHECK(empty_1.begin() == empty_1.end()); + BOOST_CHECK(empty_2.begin() == empty_2.end()); + BOOST_CHECK(empty_3.begin() == empty_3.end()); + BOOST_CHECK(empty_4.begin() == empty_4.end()); +} + +BOOST_AUTO_TEST_SUITE_END()