Implement one-to-many unidirectional MLD search
This commit is contained in:
		
							parent
							
								
									2715e5758b
								
							
						
					
					
						commit
						a862e5fb3a
					
				| @ -6,6 +6,8 @@ | |||||||
|       - Lua 5.1 support is removed due to lack of support in sol2 https://github.com/ThePhD/sol2/issues/302 |       - Lua 5.1 support is removed due to lack of support in sol2 https://github.com/ThePhD/sol2/issues/302 | ||||||
|     - Node.js Bindings: |     - Node.js Bindings: | ||||||
|       - Exposes `use_threads_number=Number` parameter of `EngineConfig` to limit a number of threads in a TBB internal pool |       - 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 | # 5.12.0 | ||||||
|     - Guidance |     - Guidance | ||||||
|  | |||||||
| @ -135,6 +135,35 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, | |||||||
|                     highest_diffrent_level(phantom_node.reverse_segment_id)); |                     highest_diffrent_level(phantom_node.reverse_segment_id)); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | template <typename MultiLevelPartition> | ||||||
|  | inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, | ||||||
|  |                                  NodeID node, | ||||||
|  |                                  const std::vector<PhantomNode> &phantom_nodes, | ||||||
|  |                                  const std::size_t phantom_index, | ||||||
|  |                                  const std::vector<std::size_t> &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 <bool DIRECTION, typename... Args> | template <bool DIRECTION, typename... Args> | ||||||
| void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade, | void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade, | ||||||
|                         const NodeID node, |                         const NodeID node, | ||||||
| @ -445,8 +474,193 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo | |||||||
|         std::iota(phantom_indices.begin(), phantom_indices.end(), 0); |         std::iota(phantom_indices.begin(), phantom_indices.end(), 0); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::vector<EdgeWeight> weights(phantom_nodes.size(), INVALID_EDGE_WEIGHT); |     std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT); | ||||||
|     std::vector<EdgeDuration> durations(phantom_nodes.size(), MAXIMAL_EDGE_DURATION); |     std::vector<EdgeDuration> durations(phantom_indices.size(), MAXIMAL_EDGE_DURATION); | ||||||
|  | 
 | ||||||
|  |     // Collect destination (source) nodes into a map
 | ||||||
|  |     std::unordered_multimap<NodeID, std::tuple<std::size_t, EdgeWeight, EdgeDuration>> | ||||||
|  |         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<DIRECTION>(facade, | ||||||
|  |                                       node, | ||||||
|  |                                       weight, | ||||||
|  |                                       duration, | ||||||
|  |                                       query_heap, | ||||||
|  |                                       phantom_nodes, | ||||||
|  |                                       phantom_index, | ||||||
|  |                                       phantom_indices); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
|     return durations; |     return durations; | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user