From a862e5fb3ae6d8240eca7fbaa2b17b9ccdade6d6 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 26 Sep 2017 14:42:56 +0200 Subject: [PATCH] Implement one-to-many unidirectional MLD search --- CHANGELOG.md | 2 + .../routing_algorithms/many_to_many.cpp | 218 +++++++++++++++++- 2 files changed, 218 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 31ec1d632..9a355013e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ - Lua 5.1 support is removed due to lack of support in sol2 https://github.com/ThePhD/sol2/issues/302 - Node.js Bindings: - Exposes `use_threads_number=Number` parameter of `EngineConfig` to limit a number of threads in a TBB internal pool + - Internals + - MLD uses a unidirectional Dijkstra for 1-to-N and N-to-1 matrices # 5.12.0 - Guidance diff --git a/src/engine/routing_algorithms/many_to_many.cpp b/src/engine/routing_algorithms/many_to_many.cpp index d413ec7f9..adc162fe4 100644 --- a/src/engine/routing_algorithms/many_to_many.cpp +++ b/src/engine/routing_algorithms/many_to_many.cpp @@ -135,6 +135,35 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, highest_diffrent_level(phantom_node.reverse_segment_id)); } +template +inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, + NodeID node, + const std::vector &phantom_nodes, + const std::size_t phantom_index, + const std::vector &phantom_indices) +{ + auto level = [&partition, node](const SegmentID &source, const SegmentID &target) { + if (source.enabled && target.enabled) + return partition.GetQueryLevel(source.id, target.id, node); + return INVALID_LEVEL_ID; + }; + + const auto &source_phantom = phantom_nodes[phantom_index]; + + auto result = INVALID_LEVEL_ID; + for (const auto &index : phantom_indices) + { + const auto &target_phantom = phantom_nodes[index]; + auto min_level = std::min( + std::min(level(source_phantom.forward_segment_id, target_phantom.forward_segment_id), + level(source_phantom.forward_segment_id, target_phantom.reverse_segment_id)), + std::min(level(source_phantom.reverse_segment_id, target_phantom.forward_segment_id), + level(source_phantom.reverse_segment_id, target_phantom.reverse_segment_id))); + result = std::min(result, min_level); + } + return result; +} + template void relaxOutgoingEdges(const DataFacade &facade, const NodeID node, @@ -445,8 +474,193 @@ std::vector oneToManySearch(SearchEngineData &engine_wo std::iota(phantom_indices.begin(), phantom_indices.end(), 0); } - std::vector weights(phantom_nodes.size(), INVALID_EDGE_WEIGHT); - std::vector durations(phantom_nodes.size(), MAXIMAL_EDGE_DURATION); + std::vector weights(phantom_indices.size(), INVALID_EDGE_WEIGHT); + std::vector durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION); + + // Collect destination (source) nodes into a map + std::unordered_multimap> + target_nodes_index; + target_nodes_index.reserve(phantom_indices.size()); + for (std::size_t index = 0; index < phantom_indices.size(); ++index) + { + const auto &phantom_index = phantom_indices[index]; + const auto &phantom_node = phantom_nodes[phantom_index]; + + if (DIRECTION == FORWARD_DIRECTION) + { + if (phantom_node.IsValidForwardTarget()) + target_nodes_index.insert({phantom_node.forward_segment_id.id, + {index, + phantom_node.GetForwardWeightPlusOffset(), + phantom_node.GetForwardDuration()}}); + if (phantom_node.IsValidReverseTarget()) + target_nodes_index.insert({phantom_node.reverse_segment_id.id, + {index, + phantom_node.GetReverseWeightPlusOffset(), + phantom_node.GetReverseDuration()}}); + } + else if (DIRECTION == REVERSE_DIRECTION) + { + if (phantom_node.IsValidForwardSource()) + target_nodes_index.insert({phantom_node.forward_segment_id.id, + {index, + -phantom_node.GetForwardWeightPlusOffset(), + -phantom_node.GetForwardDuration()}}); + if (phantom_node.IsValidReverseSource()) + target_nodes_index.insert({phantom_node.reverse_segment_id.id, + {index, + -phantom_node.GetReverseWeightPlusOffset(), + -phantom_node.GetReverseDuration()}}); + } + } + + // Handler if node is in the destinations list and update weights/durations + auto update_values = [&](NodeID node, EdgeWeight weight, EdgeDuration duration) { + auto candidates = target_nodes_index.equal_range(node); + for (auto it = candidates.first; it != candidates.second;) + { + std::size_t index; + EdgeWeight target_weight; + EdgeDuration target_duration; + std::tie(index, target_weight, target_duration) = it->second; + + const auto path_weight = weight + target_weight; + if (path_weight >= 0 && + path_weight < weights[index]) // TODO: check if path_weight < weights[index] needed + { + weights[index] = path_weight; + durations[index] = duration + target_duration; + } + + if (path_weight >= 0) + { // Remove node from destinations list + it = target_nodes_index.erase(it); + } + else + { + ++it; + } + } + }; + + // Place source (destination) adjacent nodes into the heap + engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(facade.GetNumberOfNodes()); + auto &query_heap = *(engine_working_data.many_to_many_heap); + + { // Update single node paths + const auto &phantom_node = phantom_nodes[phantom_index]; + + if (DIRECTION == FORWARD_DIRECTION) + { + if (phantom_node.IsValidForwardSource()) + update_values(phantom_node.forward_segment_id.id, + -phantom_node.GetForwardWeightPlusOffset(), + -phantom_node.GetForwardDuration()); + if (phantom_node.IsValidReverseSource()) + update_values(phantom_node.reverse_segment_id.id, + -phantom_node.GetReverseWeightPlusOffset(), + -phantom_node.GetReverseDuration()); + } + else if (DIRECTION == REVERSE_DIRECTION) + { + if (phantom_node.IsValidForwardTarget()) + update_values(phantom_node.forward_segment_id.id, + phantom_node.GetForwardWeightPlusOffset(), + phantom_node.GetForwardDuration()); + if (phantom_node.IsValidReverseTarget()) + update_values(phantom_node.reverse_segment_id.id, + phantom_node.GetReverseWeightPlusOffset(), + phantom_node.GetReverseDuration()); + } + + if (DIRECTION == FORWARD_DIRECTION) + { + if (phantom_node.IsValidForwardSource()) + { + const auto parent = phantom_node.forward_segment_id.id; + for (auto edge : facade.GetAdjacentEdgeRange(parent)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + query_heap.Insert( + facade.GetTarget(edge), + data.weight - phantom_node.GetForwardWeightPlusOffset(), + {parent, data.duration - phantom_node.GetForwardDuration()}); + } + } + } + if (phantom_node.IsValidReverseSource()) + { + const auto parent = phantom_node.reverse_segment_id.id; + for (auto edge : facade.GetAdjacentEdgeRange(parent)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + query_heap.Insert( + facade.GetTarget(edge), + data.weight - phantom_node.GetReverseWeightPlusOffset(), + {parent, data.duration - phantom_node.GetReverseDuration()}); + } + } + } + } + else if (DIRECTION == REVERSE_DIRECTION) + { + if (phantom_node.IsValidForwardTarget()) + { + const auto parent = phantom_node.forward_segment_id.id; + for (auto edge : facade.GetAdjacentEdgeRange(parent)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + query_heap.Insert( + facade.GetTarget(edge), + data.weight + phantom_node.GetForwardWeightPlusOffset(), + {parent, data.duration + phantom_node.GetForwardDuration()}); + } + } + } + if (phantom_node.IsValidReverseTarget()) + { + const auto parent = phantom_node.reverse_segment_id.id; + for (auto edge : facade.GetAdjacentEdgeRange(parent)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + query_heap.Insert( + facade.GetTarget(edge), + data.weight + phantom_node.GetReverseWeightPlusOffset(), + {parent, data.duration + phantom_node.GetReverseDuration()}); + } + } + } + } + } + + while (!query_heap.Empty() && !target_nodes_index.empty()) + { + // Extract node from the heap + const auto node = query_heap.DeleteMin(); + const auto weight = query_heap.GetKey(node); + const auto duration = query_heap.GetData(node).duration; + + // Update values + update_values(node, weight, duration); + + // Relax outgoing edges + relaxOutgoingEdges(facade, + node, + weight, + duration, + query_heap, + phantom_nodes, + phantom_index, + phantom_indices); + } return durations; }