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 | ||||
|     - 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 | ||||
|  | ||||
| @ -135,6 +135,35 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition, | ||||
|                     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> | ||||
| void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade, | ||||
|                         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::vector<EdgeWeight> weights(phantom_nodes.size(), INVALID_EDGE_WEIGHT); | ||||
|     std::vector<EdgeDuration> durations(phantom_nodes.size(), MAXIMAL_EDGE_DURATION); | ||||
|     std::vector<EdgeWeight> weights(phantom_indices.size(), INVALID_EDGE_WEIGHT); | ||||
|     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; | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user