From 517cb5f0948c270c911f970d608e3831b8547e0f Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Wed, 28 Jun 2017 00:48:18 +0200 Subject: [PATCH] Matrix plugin with MLD overlay --- CHANGELOG.md | 6 + .../routing_algorithms/routing_base.hpp | 40 ++- include/engine/search_engine_data.hpp | 25 ++ .../routing_algorithms/many_to_many.cpp | 250 +++++++++++++++++- .../routing_algorithms/routing_base.cpp | 34 --- src/engine/search_engine_data.cpp | 12 + unit_tests/customizer/cell_customization.cpp | 11 +- 7 files changed, 327 insertions(+), 51 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f3374424..7bb909a0a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 5.9.0 + - Changes from 5.8 + - Algorithm: + - Multi-Level Dijkstra: + - Plugins supported: `table` + # 5.8.0 - Changes from 5.7 - API: diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index e6891e99e..d0c8a5fe2 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -41,12 +41,6 @@ bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &targ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom); -void insertSourceInHeap(SearchEngineData::ManyToManyQueryHeap &heap, - const PhantomNode &phantom_node); - -void insertTargetInHeap(SearchEngineData::ManyToManyQueryHeap &heap, - const PhantomNode &phantom_node); - template void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes) { @@ -81,6 +75,40 @@ void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNod } } +template +void insertSourceInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) +{ + if (phantom_node.IsValidForwardSource()) + { + heap.Insert(phantom_node.forward_segment_id.id, + -phantom_node.GetForwardWeightPlusOffset(), + {phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()}); + } + if (phantom_node.IsValidReverseSource()) + { + heap.Insert(phantom_node.reverse_segment_id.id, + -phantom_node.GetReverseWeightPlusOffset(), + {phantom_node.reverse_segment_id.id, -phantom_node.GetReverseDuration()}); + } +} + +template +void insertTargetInHeap(ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) +{ + if (phantom_node.IsValidForwardTarget()) + { + heap.Insert(phantom_node.forward_segment_id.id, + phantom_node.GetForwardWeightPlusOffset(), + {phantom_node.forward_segment_id.id, phantom_node.GetForwardDuration()}); + } + if (phantom_node.IsValidReverseTarget()) + { + heap.Insert(phantom_node.reverse_segment_id.id, + phantom_node.GetReverseWeightPlusOffset(), + {phantom_node.reverse_segment_id.id, phantom_node.GetReverseDuration()}); + } +} + template void annotatePath(const FacadeT &facade, const PhantomNodes &phantom_node_pair, diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index 0852d02c5..af2bbf053 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -78,6 +78,20 @@ struct MultiLayerDijkstraHeapData MultiLayerDijkstraHeapData(NodeID p, bool from) : parent(p), from_clique_arc(from) {} }; +struct ManyToManyMultiLayerDijkstraHeapData : MultiLayerDijkstraHeapData +{ + LevelID level; + EdgeWeight duration; + ManyToManyMultiLayerDijkstraHeapData(NodeID p, EdgeWeight duration) + : MultiLayerDijkstraHeapData(p), level(0), duration(duration) + { + } + ManyToManyMultiLayerDijkstraHeapData(NodeID p, bool from, LevelID level, EdgeWeight duration) + : MultiLayerDijkstraHeapData(p, from), level(level), duration(duration) + { + } +}; + template <> struct SearchEngineData { using QueryHeap = util::QueryHeap struct SearchEngineData MultiLayerDijkstraHeapData, util::UnorderedMapStorage>; + using ManyToManyQueryHeap = util::QueryHeap>; + using SearchEngineHeapPtr = boost::thread_specific_ptr; + using ManyToManyHeapPtr = boost::thread_specific_ptr; + static SearchEngineHeapPtr forward_heap_1; static SearchEngineHeapPtr reverse_heap_1; + static ManyToManyHeapPtr many_to_many_heap; void InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes); + + void InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes); }; } } diff --git a/src/engine/routing_algorithms/many_to_many.cpp b/src/engine/routing_algorithms/many_to_many.cpp index b3fcd8187..b0b6b9281 100644 --- a/src/engine/routing_algorithms/many_to_many.cpp +++ b/src/engine/routing_algorithms/many_to_many.cpp @@ -15,11 +15,6 @@ namespace engine namespace routing_algorithms { -namespace ch -{ - -using ManyToManyQueryHeap = SearchEngineData::ManyToManyQueryHeap; - namespace { struct NodeBucket @@ -32,16 +27,19 @@ struct NodeBucket { } }; +} // FIXME This should be replaced by an std::unordered_multimap, though this needs benchmarking using SearchSpaceWithBuckets = std::unordered_map>; +namespace ch +{ template void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade &facade, const NodeID node, const EdgeWeight weight, const EdgeWeight duration, - ManyToManyQueryHeap &query_heap) + typename SearchEngineData::ManyToManyQueryHeap &query_heap) { for (auto edge : facade.GetAdjacentEdgeRange(node)) { @@ -75,7 +73,7 @@ void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade &facade, const unsigned row_idx, const unsigned number_of_targets, - ManyToManyQueryHeap &query_heap, + typename SearchEngineData::ManyToManyQueryHeap &query_heap, const SearchSpaceWithBuckets &search_space_with_buckets, std::vector &weights_table, std::vector &durations_table) @@ -131,7 +129,7 @@ void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, const unsigned column_idx, - ManyToManyQueryHeap &query_heap, + typename SearchEngineData::ManyToManyQueryHeap &query_heap, SearchSpaceWithBuckets &search_space_with_buckets) { const NodeID node = query_heap.DeleteMin(); @@ -148,7 +146,6 @@ void backwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade(facade, node, target_weight, target_duration, query_heap); } -} std::vector manyToManySearch(SearchEngineData &engine_working_data, @@ -247,6 +244,168 @@ manyToManySearch(SearchEngineData &engine_working_data, // TODO: generalize with CH version namespace mld { +template +void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const NodeID node, + const EdgeWeight weight, + const EdgeWeight duration, + typename SearchEngineData::ManyToManyQueryHeap &query_heap) +{ + const auto &partition = facade.GetMultiLevelPartition(); + const auto &cells = facade.GetCellStorage(); + + const auto &node_data = query_heap.GetData(node); + const auto level = + std::max(node_data.level, partition.GetHighestDifferentLevel(node_data.parent, node)); + + if (level >= 1 && !node_data.from_clique_arc) + { + const auto &cell = cells.GetCell(level, partition.GetCell(level, node)); + if (DIRECTION == FORWARD_DIRECTION) + { + // Shortcuts in forward direction + auto destination = cell.GetDestinationNodes().begin(); + auto shortcut_durations = cell.GetOutDuration(node); + for (auto shortcut_weight : cell.GetOutWeight(node)) + { + BOOST_ASSERT(destination != cell.GetDestinationNodes().end()); + BOOST_ASSERT(!shortcut_durations.empty()); + const NodeID to = *destination; + if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to) + { + const auto to_weight = weight + shortcut_weight; + const auto to_duration = duration + shortcut_durations.front(); + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_weight, {node, true, level, to_duration}); + } + else if (to_weight < query_heap.GetKey(to)) + { + query_heap.GetData(to) = {node, true, level, to_duration}; + query_heap.DecreaseKey(to, to_weight); + } + } + ++destination; + shortcut_durations.advance_begin(1); + } + BOOST_ASSERT(shortcut_durations.empty()); + } + else + { + // Shortcuts in backward direction + auto source = cell.GetSourceNodes().begin(); + auto shortcut_durations = cell.GetInDuration(node); + for (auto shortcut_weight : cell.GetInWeight(node)) + { + BOOST_ASSERT(source != cell.GetSourceNodes().end()); + BOOST_ASSERT(!shortcut_durations.empty()); + const NodeID to = *source; + if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to) + { + const auto to_weight = weight + shortcut_weight; + const auto to_duration = duration + shortcut_durations.front(); + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_weight, {node, true, level, to_duration}); + } + else if (to_weight < query_heap.GetKey(to)) + { + query_heap.GetData(to) = {node, true, level, to_duration}; + query_heap.DecreaseKey(to, to_weight); + } + } + ++source; + shortcut_durations.advance_begin(1); + } + BOOST_ASSERT(shortcut_durations.empty()); + } + } + + for (const auto edge : facade.GetBorderEdgeRange(level, node)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + const NodeID to = facade.GetTarget(edge); + const EdgeWeight edge_weight = data.weight; + const EdgeWeight edge_duration = data.duration; + + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + const EdgeWeight to_weight = weight + edge_weight; + const EdgeWeight to_duration = duration + edge_duration; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!query_heap.WasInserted(to)) + { + query_heap.Insert(to, to_weight, {node, false, level, to_duration}); + } + // Found a shorter Path -> Update weight + else if (to_weight < query_heap.GetKey(to)) + { + // new parent + query_heap.GetData(to) = {node, false, level, to_duration}; + query_heap.DecreaseKey(to, to_weight); + } + } + } +} + +void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const unsigned row_idx, + const unsigned number_of_targets, + typename SearchEngineData::ManyToManyQueryHeap &query_heap, + const SearchSpaceWithBuckets &search_space_with_buckets, + std::vector &weights_table, + std::vector &durations_table) +{ + const NodeID node = query_heap.DeleteMin(); + const EdgeWeight source_weight = query_heap.GetKey(node); + const EdgeWeight source_duration = query_heap.GetData(node).duration; + + // check if each encountered node has an entry + const auto bucket_iterator = search_space_with_buckets.find(node); + // iterate bucket if there exists one + if (bucket_iterator != search_space_with_buckets.end()) + { + const std::vector &bucket_list = bucket_iterator->second; + for (const NodeBucket ¤t_bucket : bucket_list) + { + // get target id from bucket entry + const unsigned column_idx = current_bucket.target_id; + const EdgeWeight target_weight = current_bucket.weight; + const EdgeWeight target_duration = current_bucket.duration; + + auto ¤t_weight = weights_table[row_idx * number_of_targets + column_idx]; + auto ¤t_duration = durations_table[row_idx * number_of_targets + column_idx]; + + // check if new weight is better + const EdgeWeight new_weight = source_weight + target_weight; + if (new_weight >= 0 && new_weight < current_weight) + { + current_weight = new_weight; + current_duration = source_duration + target_duration; + } + } + } + + relaxOutgoingEdges(facade, node, source_weight, source_duration, query_heap); +} + +void backwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const unsigned column_idx, + typename SearchEngineData::ManyToManyQueryHeap &query_heap, + SearchSpaceWithBuckets &search_space_with_buckets) +{ + const NodeID node = query_heap.DeleteMin(); + const EdgeWeight target_weight = query_heap.GetKey(node); + const EdgeWeight target_duration = query_heap.GetData(node).duration; + + // store settled nodes in search space bucket + search_space_with_buckets[node].emplace_back(column_idx, target_weight, target_duration); + + relaxOutgoingEdges(facade, node, target_weight, target_duration, query_heap); +} + std::vector manyToManySearch(SearchEngineData &engine_working_data, const datafacade::ContiguousInternalMemoryDataFacade &facade, @@ -263,6 +422,79 @@ manyToManySearch(SearchEngineData &engine_working_data, std::vector weights_table(number_of_entries, INVALID_EDGE_WEIGHT); std::vector durations_table(number_of_entries, MAXIMAL_EDGE_DURATION); + engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(facade.GetNumberOfNodes()); + + auto &query_heap = *(engine_working_data.many_to_many_heap); + + SearchSpaceWithBuckets search_space_with_buckets; + + unsigned column_idx = 0; + const auto search_target_phantom = [&](const PhantomNode &phantom) { + // clear heap and insert target nodes + query_heap.Clear(); + insertTargetInHeap(query_heap, phantom); + + // explore search space + while (!query_heap.Empty()) + { + backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets); + } + ++column_idx; + }; + + // for each source do forward search + unsigned row_idx = 0; + const auto search_source_phantom = [&](const PhantomNode &phantom) { + // clear heap and insert source nodes + query_heap.Clear(); + insertSourceInHeap(query_heap, phantom); + + // explore search space + while (!query_heap.Empty()) + { + forwardRoutingStep(facade, + row_idx, + number_of_targets, + query_heap, + search_space_with_buckets, + weights_table, + durations_table); + } + ++row_idx; + }; + + if (target_indices.empty()) + { + for (const auto &phantom : phantom_nodes) + { + search_target_phantom(phantom); + } + } + else + { + for (const auto index : target_indices) + { + const auto &phantom = phantom_nodes[index]; + search_target_phantom(phantom); + } + } + + if (source_indices.empty()) + { + for (const auto &phantom : phantom_nodes) + { + search_source_phantom(phantom); + } + } + else + { + for (const auto index : source_indices) + { + const auto &phantom = phantom_nodes[index]; + search_source_phantom(phantom); + } + } + return durations_table; } diff --git a/src/engine/routing_algorithms/routing_base.cpp b/src/engine/routing_algorithms/routing_base.cpp index 8f834847f..4a1140e9e 100644 --- a/src/engine/routing_algorithms/routing_base.cpp +++ b/src/engine/routing_algorithms/routing_base.cpp @@ -23,40 +23,6 @@ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &ta target_phantom.GetReverseWeightPlusOffset(); } -void insertSourceInHeap(SearchEngineData::ManyToManyQueryHeap &heap, - const PhantomNode &phantom_node) -{ - if (phantom_node.IsValidForwardSource()) - { - heap.Insert(phantom_node.forward_segment_id.id, - -phantom_node.GetForwardWeightPlusOffset(), - {phantom_node.forward_segment_id.id, -phantom_node.GetForwardDuration()}); - } - if (phantom_node.IsValidReverseSource()) - { - heap.Insert(phantom_node.reverse_segment_id.id, - -phantom_node.GetReverseWeightPlusOffset(), - {phantom_node.reverse_segment_id.id, -phantom_node.GetReverseDuration()}); - } -} - -void insertTargetInHeap(SearchEngineData::ManyToManyQueryHeap &heap, - const PhantomNode &phantom_node) -{ - if (phantom_node.IsValidForwardTarget()) - { - heap.Insert(phantom_node.forward_segment_id.id, - phantom_node.GetForwardWeightPlusOffset(), - {phantom_node.forward_segment_id.id, phantom_node.GetForwardDuration()}); - } - if (phantom_node.IsValidReverseTarget()) - { - heap.Insert(phantom_node.reverse_segment_id.id, - phantom_node.GetReverseWeightPlusOffset(), - {phantom_node.reverse_segment_id.id, phantom_node.GetReverseDuration()}); - } -} - } // namespace routing_algorithms } // namespace engine } // namespace osrm diff --git a/src/engine/search_engine_data.cpp b/src/engine/search_engine_data.cpp index a51741521..0069db56b 100644 --- a/src/engine/search_engine_data.cpp +++ b/src/engine/search_engine_data.cpp @@ -94,6 +94,7 @@ void SearchEngineData::InitializeOrClearManyToManyThreadLocalStorage(unsigne using MLD = routing_algorithms::mld::Algorithm; SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_1; SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_1; +SearchEngineData::ManyToManyHeapPtr SearchEngineData::many_to_many_heap; void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes) { @@ -116,5 +117,16 @@ void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(unsigned nu } } +void SearchEngineData::InitializeOrClearManyToManyThreadLocalStorage(unsigned number_of_nodes) +{ + if (many_to_many_heap.get()) + { + many_to_many_heap->Clear(); + } + else + { + many_to_many_heap.reset(new ManyToManyQueryHeap(number_of_nodes)); + } +} } } diff --git a/unit_tests/customizer/cell_customization.cpp b/unit_tests/customizer/cell_customization.cpp index 38c23db85..cac4daac7 100644 --- a/unit_tests/customizer/cell_customization.cpp +++ b/unit_tests/customizer/cell_customization.cpp @@ -25,6 +25,7 @@ auto makeGraph(const MultiLevelPartition &mlp, const std::vector &mock struct EdgeData { EdgeWeight weight; + EdgeDuration duration; bool forward; bool backward; }; @@ -34,8 +35,8 @@ auto makeGraph(const MultiLevelPartition &mlp, const std::vector &mock for (const auto &m : mock_edges) { max_id = std::max(max_id, std::max(m.start, m.target)); - edges.push_back(Edge{m.start, m.target, m.weight, true, false}); - edges.push_back(Edge{m.target, m.start, m.weight, false, true}); + edges.push_back(Edge{m.start, m.target, m.weight, 2 * m.weight, true, false}); + edges.push_back(Edge{m.target, m.start, m.weight, 2 * m.weight, false, true}); } std::sort(edges.begin(), edges.end()); return partition::MultiLevelGraph( @@ -253,6 +254,12 @@ BOOST_AUTO_TEST_CASE(four_levels_test) CHECK_EQUAL_RANGE(cell_2_1.GetInWeight(9), 0, INVALID_EDGE_WEIGHT); CHECK_EQUAL_RANGE(cell_2_1.GetInWeight(12), INVALID_EDGE_WEIGHT, 10); + CHECK_EQUAL_RANGE(cell_2_1.GetOutDuration(9), 6, 0, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetOutDuration(13), INVALID_EDGE_WEIGHT, INVALID_EDGE_WEIGHT, 20); + CHECK_EQUAL_RANGE(cell_2_1.GetInDuration(8), 6, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetInDuration(9), 0, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetInDuration(12), INVALID_EDGE_WEIGHT, 20); + CellStorage storage_rec(mlp, graph); customizer.Customize(graph, storage_rec);