Use for M*n (m*N) tables queries forward (backward) MLD search
This commit is contained in:
parent
5af05631c2
commit
f2fbe16979
@ -6,6 +6,7 @@ Feature: Basic Distance Matrix
|
|||||||
|
|
||||||
Background:
|
Background:
|
||||||
Given the profile "testbot"
|
Given the profile "testbot"
|
||||||
|
And the partition extra arguments "--small-component-size 1 --max-cell-sizes 2,4,8,16"
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix of minimal network
|
Scenario: Testbot - Travel time matrix of minimal network
|
||||||
Given the node map
|
Given the node map
|
||||||
@ -124,7 +125,7 @@ Feature: Basic Distance Matrix
|
|||||||
| d | 20 | 30 | 0 | 30 |
|
| d | 20 | 30 | 0 | 30 |
|
||||||
| e | 30 | 40 | 10 | 0 |
|
| e | 30 | 40 | 10 | 0 |
|
||||||
|
|
||||||
Scenario: Testbot - Travel time matrix and with only one source
|
Scenario: Testbot - Rectangular travel time matrix
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
a b c
|
a b c
|
||||||
@ -150,6 +151,39 @@ Feature: Basic Distance Matrix
|
|||||||
| e | 20 |
|
| e | 20 |
|
||||||
| f | 30 |
|
| f | 30 |
|
||||||
|
|
||||||
|
When I request a travel time matrix I should get
|
||||||
|
| | a | b | e | f |
|
||||||
|
| a | 0 | 10 | 20 | 30 |
|
||||||
|
| b | 10 | 0 | 10 | 20 |
|
||||||
|
|
||||||
|
When I request a travel time matrix I should get
|
||||||
|
| | a | b |
|
||||||
|
| a | 0 | 10 |
|
||||||
|
| b | 10 | 0 |
|
||||||
|
| e | 20 | 10 |
|
||||||
|
| f | 30 | 20 |
|
||||||
|
|
||||||
|
When I request a travel time matrix I should get
|
||||||
|
| | a | b | e | f |
|
||||||
|
| a | 0 | 10 | 20 | 30 |
|
||||||
|
| b | 10 | 0 | 10 | 20 |
|
||||||
|
| e | 20 | 10 | 0 | 10 |
|
||||||
|
|
||||||
|
When I request a travel time matrix I should get
|
||||||
|
| | a | b | e |
|
||||||
|
| a | 0 | 10 | 20 |
|
||||||
|
| b | 10 | 0 | 10 |
|
||||||
|
| e | 20 | 10 | 0 |
|
||||||
|
| f | 30 | 20 | 10 |
|
||||||
|
|
||||||
|
When I request a travel time matrix I should get
|
||||||
|
| | a | b | e | f |
|
||||||
|
| a | 0 | 10 | 20 | 30 |
|
||||||
|
| b | 10 | 0 | 10 | 20 |
|
||||||
|
| e | 20 | 10 | 0 | 10 |
|
||||||
|
| f | 30 | 20 | 10 | 0 |
|
||||||
|
|
||||||
|
|
||||||
Scenario: Testbot - Travel time 3x2 matrix
|
Scenario: Testbot - Travel time 3x2 matrix
|
||||||
Given the node map
|
Given the node map
|
||||||
"""
|
"""
|
||||||
|
@ -19,7 +19,7 @@ namespace routing_algorithms
|
|||||||
namespace mld
|
namespace mld
|
||||||
{
|
{
|
||||||
|
|
||||||
template <bool DIRECTION, typename MultiLevelPartition>
|
template <typename MultiLevelPartition>
|
||||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||||
const NodeID node,
|
const NodeID node,
|
||||||
const PhantomNode &phantom_node)
|
const PhantomNode &phantom_node)
|
||||||
@ -33,13 +33,24 @@ inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
|||||||
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
|
const auto node_level = std::min(highest_diffrent_level(phantom_node.forward_segment_id),
|
||||||
highest_diffrent_level(phantom_node.reverse_segment_id));
|
highest_diffrent_level(phantom_node.reverse_segment_id));
|
||||||
|
|
||||||
if (DIRECTION == REVERSE_DIRECTION && node_level >= partition.GetNumberOfLevels() - 1)
|
return node_level;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename MultiLevelPartition>
|
||||||
|
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||||
|
const NodeID node,
|
||||||
|
const PhantomNode &phantom_node,
|
||||||
|
const LevelID maximal_level)
|
||||||
|
{
|
||||||
|
const auto node_level = getNodeQueryLevel(partition, node, phantom_node);
|
||||||
|
|
||||||
|
if (node_level >= maximal_level)
|
||||||
return INVALID_LEVEL_ID;
|
return INVALID_LEVEL_ID;
|
||||||
|
|
||||||
return node_level;
|
return node_level;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <bool DIRECTION, typename MultiLevelPartition>
|
template <typename MultiLevelPartition>
|
||||||
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
inline LevelID getNodeQueryLevel(const MultiLevelPartition &partition,
|
||||||
NodeID node,
|
NodeID node,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const std::vector<PhantomNode> &phantom_nodes,
|
||||||
@ -83,7 +94,7 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
|
|
||||||
const auto &partition = facade.GetMultiLevelPartition();
|
const auto &partition = facade.GetMultiLevelPartition();
|
||||||
|
|
||||||
const auto level = getNodeQueryLevel<DIRECTION>(partition, node, args...);
|
const auto level = getNodeQueryLevel(partition, node, args...);
|
||||||
|
|
||||||
// Break outgoing edges relaxation if node at the restricted level
|
// Break outgoing edges relaxation if node at the restricted level
|
||||||
if (level == INVALID_LEVEL_ID)
|
if (level == INVALID_LEVEL_ID)
|
||||||
@ -192,68 +203,9 @@ void relaxOutgoingEdges(const DataFacade<mld::Algorithm> &facade,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
//
|
||||||
const unsigned row_idx,
|
|
||||||
const unsigned number_of_targets,
|
|
||||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
|
||||||
const std::vector<NodeBucket> &search_space_with_buckets,
|
|
||||||
std::vector<EdgeWeight> &weights_table,
|
|
||||||
std::vector<EdgeDuration> &durations_table,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
const auto node = query_heap.DeleteMin();
|
|
||||||
const auto source_weight = query_heap.GetKey(node);
|
|
||||||
const auto source_duration = query_heap.GetData(node).duration;
|
|
||||||
|
|
||||||
// Check if each encountered node has an entry
|
|
||||||
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
|
||||||
search_space_with_buckets.end(),
|
|
||||||
node,
|
|
||||||
NodeBucket::Compare());
|
|
||||||
for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list))
|
|
||||||
{
|
|
||||||
// Get target id from bucket entry
|
|
||||||
const auto column_idx = current_bucket.column_index;
|
|
||||||
const auto target_weight = current_bucket.weight;
|
|
||||||
const auto 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
|
|
||||||
auto new_weight = source_weight + target_weight;
|
|
||||||
auto new_duration = source_duration + target_duration;
|
|
||||||
|
|
||||||
if (new_weight >= 0 &&
|
|
||||||
std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
|
|
||||||
{
|
|
||||||
current_weight = new_weight;
|
|
||||||
current_duration = new_duration;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
relaxOutgoingEdges<FORWARD_DIRECTION>(
|
|
||||||
facade, node, source_weight, source_duration, query_heap, phantom_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
|
||||||
const unsigned column_idx,
|
|
||||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
|
||||||
std::vector<NodeBucket> &search_space_with_buckets,
|
|
||||||
const PhantomNode &phantom_node)
|
|
||||||
{
|
|
||||||
const auto node = query_heap.DeleteMin();
|
|
||||||
const auto target_weight = query_heap.GetKey(node);
|
|
||||||
const auto target_duration = query_heap.GetData(node).duration;
|
|
||||||
|
|
||||||
// Store settled nodes in search space bucket
|
|
||||||
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
|
||||||
|
|
||||||
relaxOutgoingEdges<REVERSE_DIRECTION>(
|
|
||||||
facade, node, target_weight, target_duration, query_heap, phantom_node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
|
// Unidirectional multi-layer Dijkstra search for 1-to-N and N-to-1 matrices
|
||||||
|
//
|
||||||
template <bool DIRECTION>
|
template <bool DIRECTION>
|
||||||
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
const DataFacade<Algorithm> &facade,
|
const DataFacade<Algorithm> &facade,
|
||||||
@ -414,6 +366,83 @@ std::vector<EdgeDuration> oneToManySearch(SearchEngineData<Algorithm> &engine_wo
|
|||||||
return durations;
|
return durations;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Bidirectional multi-layer Dijkstra search for M-to-N matrices
|
||||||
|
//
|
||||||
|
template <bool DIRECTION>
|
||||||
|
void forwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||||
|
const unsigned row_idx,
|
||||||
|
const unsigned number_of_sources,
|
||||||
|
const unsigned number_of_targets,
|
||||||
|
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
|
const std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
|
std::vector<EdgeWeight> &weights_table,
|
||||||
|
std::vector<EdgeDuration> &durations_table,
|
||||||
|
const PhantomNode &phantom_node)
|
||||||
|
{
|
||||||
|
const auto node = query_heap.DeleteMin();
|
||||||
|
const auto source_weight = query_heap.GetKey(node);
|
||||||
|
const auto source_duration = query_heap.GetData(node).duration;
|
||||||
|
|
||||||
|
// Check if each encountered node has an entry
|
||||||
|
const auto &bucket_list = std::equal_range(search_space_with_buckets.begin(),
|
||||||
|
search_space_with_buckets.end(),
|
||||||
|
node,
|
||||||
|
NodeBucket::Compare());
|
||||||
|
for (const auto ¤t_bucket : boost::make_iterator_range(bucket_list))
|
||||||
|
{
|
||||||
|
// Get target id from bucket entry
|
||||||
|
const auto column_idx = current_bucket.column_index;
|
||||||
|
const auto target_weight = current_bucket.weight;
|
||||||
|
const auto target_duration = current_bucket.duration;
|
||||||
|
|
||||||
|
// Get the value location in the results tables:
|
||||||
|
// * row-major direct (row_idx, column_idx) index for forward direction
|
||||||
|
// * row-major transposed (column_idx, row_idx) for reversed direction
|
||||||
|
const auto location = DIRECTION == FORWARD_DIRECTION
|
||||||
|
? row_idx * number_of_targets + column_idx
|
||||||
|
: row_idx + column_idx * number_of_sources;
|
||||||
|
auto ¤t_weight = weights_table[location];
|
||||||
|
auto ¤t_duration = durations_table[location];
|
||||||
|
|
||||||
|
// Check if new weight is better
|
||||||
|
auto new_weight = source_weight + target_weight;
|
||||||
|
auto new_duration = source_duration + target_duration;
|
||||||
|
|
||||||
|
if (new_weight >= 0 &&
|
||||||
|
std::tie(new_weight, new_duration) < std::tie(current_weight, current_duration))
|
||||||
|
{
|
||||||
|
current_weight = new_weight;
|
||||||
|
current_duration = new_duration;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
relaxOutgoingEdges<DIRECTION>(
|
||||||
|
facade, node, source_weight, source_duration, query_heap, phantom_node);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool DIRECTION>
|
||||||
|
void backwardRoutingStep(const DataFacade<Algorithm> &facade,
|
||||||
|
const unsigned column_idx,
|
||||||
|
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||||
|
std::vector<NodeBucket> &search_space_with_buckets,
|
||||||
|
const PhantomNode &phantom_node)
|
||||||
|
{
|
||||||
|
const auto node = query_heap.DeleteMin();
|
||||||
|
const auto target_weight = query_heap.GetKey(node);
|
||||||
|
const auto target_duration = query_heap.GetData(node).duration;
|
||||||
|
|
||||||
|
// Store settled nodes in search space bucket
|
||||||
|
search_space_with_buckets.emplace_back(node, column_idx, target_weight, target_duration);
|
||||||
|
|
||||||
|
const auto &partition = facade.GetMultiLevelPartition();
|
||||||
|
const auto maximal_level = partition.GetNumberOfLevels() - 1;
|
||||||
|
|
||||||
|
relaxOutgoingEdges<!DIRECTION>(
|
||||||
|
facade, node, target_weight, target_duration, query_heap, phantom_node, maximal_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <bool DIRECTION>
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||||
const DataFacade<Algorithm> &facade,
|
const DataFacade<Algorithm> &facade,
|
||||||
const std::vector<PhantomNode> &phantom_nodes,
|
const std::vector<PhantomNode> &phantom_nodes,
|
||||||
@ -438,12 +467,17 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes());
|
facade.GetNumberOfNodes());
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
insertTargetInHeap(query_heap, phantom);
|
|
||||||
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
|
insertTargetInHeap(query_heap, phantom);
|
||||||
|
else
|
||||||
|
insertSourceInHeap(query_heap, phantom);
|
||||||
|
|
||||||
// explore search space
|
// explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
{
|
{
|
||||||
backwardRoutingStep(facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
backwardRoutingStep<DIRECTION>(
|
||||||
|
facade, column_idx, query_heap, search_space_with_buckets, phantom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -460,19 +494,24 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
engine_working_data.InitializeOrClearManyToManyThreadLocalStorage(
|
||||||
facade.GetNumberOfNodes());
|
facade.GetNumberOfNodes());
|
||||||
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
auto &query_heap = *(engine_working_data.many_to_many_heap);
|
||||||
insertSourceInHeap(query_heap, phantom);
|
|
||||||
|
if (DIRECTION == FORWARD_DIRECTION)
|
||||||
|
insertSourceInHeap(query_heap, phantom);
|
||||||
|
else
|
||||||
|
insertTargetInHeap(query_heap, phantom);
|
||||||
|
|
||||||
// Explore search space
|
// Explore search space
|
||||||
while (!query_heap.Empty())
|
while (!query_heap.Empty())
|
||||||
{
|
{
|
||||||
forwardRoutingStep(facade,
|
forwardRoutingStep<DIRECTION>(facade,
|
||||||
row_idx,
|
row_idx,
|
||||||
number_of_targets,
|
number_of_sources,
|
||||||
query_heap,
|
number_of_targets,
|
||||||
search_space_with_buckets,
|
query_heap,
|
||||||
weights_table,
|
search_space_with_buckets,
|
||||||
durations_table,
|
weights_table,
|
||||||
phantom);
|
durations_table,
|
||||||
|
phantom);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -489,6 +528,10 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<Algorithm> &engine_w
|
|||||||
//
|
//
|
||||||
// * many-to-many search tasks use a bidirectional Dijkstra search
|
// * many-to-many search tasks use a bidirectional Dijkstra search
|
||||||
// with the candidate node level `min(GetHighestDifferentLevel(phantom_node, node))`
|
// with the candidate node level `min(GetHighestDifferentLevel(phantom_node, node))`
|
||||||
|
// Due to pruned backward search space it is always better to compute the durations matrix
|
||||||
|
// when number of sources is less than targets. If number of targets is less than sources
|
||||||
|
// then search is performed on a reversed graph with phantom nodes with flipped roles and
|
||||||
|
// returning a transposed matrix.
|
||||||
template <>
|
template <>
|
||||||
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &engine_working_data,
|
||||||
const DataFacade<mld::Algorithm> &facade,
|
const DataFacade<mld::Algorithm> &facade,
|
||||||
@ -508,7 +551,13 @@ std::vector<EdgeDuration> manyToManySearch(SearchEngineData<mld::Algorithm> &eng
|
|||||||
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
|
engine_working_data, facade, phantom_nodes, target_indices.front(), source_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
return mld::manyToManySearch(
|
if (target_indices.size() < source_indices.size())
|
||||||
|
{
|
||||||
|
return mld::manyToManySearch<REVERSE_DIRECTION>(
|
||||||
|
engine_working_data, facade, phantom_nodes, target_indices, source_indices);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mld::manyToManySearch<FORWARD_DIRECTION>(
|
||||||
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
|
engine_working_data, facade, phantom_nodes, source_indices, target_indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user