Matrix plugin with MLD overlay
This commit is contained in:
parent
b910ab9bcb
commit
517cb5f094
@ -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:
|
||||
|
@ -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<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node);
|
||||
|
||||
void insertTargetInHeap(SearchEngineData<ch::Algorithm>::ManyToManyQueryHeap &heap,
|
||||
const PhantomNode &phantom_node);
|
||||
|
||||
template <typename Heap>
|
||||
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 <typename ManyToManyQueryHeap>
|
||||
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 <typename ManyToManyQueryHeap>
|
||||
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 <typename FacadeT>
|
||||
void annotatePath(const FacadeT &facade,
|
||||
const PhantomNodes &phantom_node_pair,
|
||||
|
@ -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<routing_algorithms::mld::Algorithm>
|
||||
{
|
||||
using QueryHeap = util::QueryHeap<NodeID,
|
||||
@ -86,12 +100,23 @@ template <> struct SearchEngineData<routing_algorithms::mld::Algorithm>
|
||||
MultiLayerDijkstraHeapData,
|
||||
util::UnorderedMapStorage<NodeID, int>>;
|
||||
|
||||
using ManyToManyQueryHeap = util::QueryHeap<NodeID,
|
||||
NodeID,
|
||||
EdgeWeight,
|
||||
ManyToManyMultiLayerDijkstraHeapData,
|
||||
util::UnorderedMapStorage<NodeID, int>>;
|
||||
|
||||
using SearchEngineHeapPtr = boost::thread_specific_ptr<QueryHeap>;
|
||||
|
||||
using ManyToManyHeapPtr = boost::thread_specific_ptr<ManyToManyQueryHeap>;
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,6 @@ namespace engine
|
||||
namespace routing_algorithms
|
||||
{
|
||||
|
||||
namespace ch
|
||||
{
|
||||
|
||||
using ManyToManyQueryHeap = SearchEngineData<Algorithm>::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<NodeID, std::vector<NodeBucket>>;
|
||||
|
||||
namespace ch
|
||||
{
|
||||
template <bool DIRECTION>
|
||||
void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
const NodeID node,
|
||||
const EdgeWeight weight,
|
||||
const EdgeWeight duration,
|
||||
ManyToManyQueryHeap &query_heap)
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap)
|
||||
{
|
||||
for (auto edge : facade.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
@ -75,7 +73,7 @@ void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade<Alg
|
||||
void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
const unsigned row_idx,
|
||||
const unsigned number_of_targets,
|
||||
ManyToManyQueryHeap &query_heap,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const SearchSpaceWithBuckets &search_space_with_buckets,
|
||||
std::vector<EdgeWeight> &weights_table,
|
||||
std::vector<EdgeWeight> &durations_table)
|
||||
@ -131,7 +129,7 @@ void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade<Alg
|
||||
|
||||
void backwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
const unsigned column_idx,
|
||||
ManyToManyQueryHeap &query_heap,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
SearchSpaceWithBuckets &search_space_with_buckets)
|
||||
{
|
||||
const NodeID node = query_heap.DeleteMin();
|
||||
@ -148,7 +146,6 @@ void backwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade<Al
|
||||
|
||||
relaxOutgoingEdges<REVERSE_DIRECTION>(facade, node, target_weight, target_duration, query_heap);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<EdgeWeight>
|
||||
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
@ -247,6 +244,168 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
// TODO: generalize with CH version
|
||||
namespace mld
|
||||
{
|
||||
template <bool DIRECTION>
|
||||
void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
const NodeID node,
|
||||
const EdgeWeight weight,
|
||||
const EdgeWeight duration,
|
||||
typename SearchEngineData<Algorithm>::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<Algorithm> &facade,
|
||||
const unsigned row_idx,
|
||||
const unsigned number_of_targets,
|
||||
typename SearchEngineData<Algorithm>::ManyToManyQueryHeap &query_heap,
|
||||
const SearchSpaceWithBuckets &search_space_with_buckets,
|
||||
std::vector<EdgeWeight> &weights_table,
|
||||
std::vector<EdgeWeight> &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<NodeBucket> &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<FORWARD_DIRECTION>(facade, node, source_weight, source_duration, query_heap);
|
||||
}
|
||||
|
||||
void backwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
const unsigned column_idx,
|
||||
typename SearchEngineData<Algorithm>::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<REVERSE_DIRECTION>(facade, node, target_weight, target_duration, query_heap);
|
||||
}
|
||||
|
||||
std::vector<EdgeWeight>
|
||||
manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
const datafacade::ContiguousInternalMemoryDataFacade<Algorithm> &facade,
|
||||
@ -263,6 +422,79 @@ manyToManySearch(SearchEngineData<Algorithm> &engine_working_data,
|
||||
std::vector<EdgeWeight> weights_table(number_of_entries, INVALID_EDGE_WEIGHT);
|
||||
std::vector<EdgeWeight> 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;
|
||||
}
|
||||
|
||||
|
@ -23,40 +23,6 @@ bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &ta
|
||||
target_phantom.GetReverseWeightPlusOffset();
|
||||
}
|
||||
|
||||
void insertSourceInHeap(SearchEngineData<ch::Algorithm>::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<ch::Algorithm>::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
|
||||
|
@ -94,6 +94,7 @@ void SearchEngineData<CH>::InitializeOrClearManyToManyThreadLocalStorage(unsigne
|
||||
using MLD = routing_algorithms::mld::Algorithm;
|
||||
SearchEngineData<MLD>::SearchEngineHeapPtr SearchEngineData<MLD>::forward_heap_1;
|
||||
SearchEngineData<MLD>::SearchEngineHeapPtr SearchEngineData<MLD>::reverse_heap_1;
|
||||
SearchEngineData<MLD>::ManyToManyHeapPtr SearchEngineData<MLD>::many_to_many_heap;
|
||||
|
||||
void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(unsigned number_of_nodes)
|
||||
{
|
||||
@ -116,5 +117,16 @@ void SearchEngineData<MLD>::InitializeOrClearFirstThreadLocalStorage(unsigned nu
|
||||
}
|
||||
}
|
||||
|
||||
void SearchEngineData<MLD>::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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@ auto makeGraph(const MultiLevelPartition &mlp, const std::vector<MockEdge> &mock
|
||||
struct EdgeData
|
||||
{
|
||||
EdgeWeight weight;
|
||||
EdgeDuration duration;
|
||||
bool forward;
|
||||
bool backward;
|
||||
};
|
||||
@ -34,8 +35,8 @@ auto makeGraph(const MultiLevelPartition &mlp, const std::vector<MockEdge> &mock
|
||||
for (const auto &m : mock_edges)
|
||||
{
|
||||
max_id = std::max<std::size_t>(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<EdgeData, osrm::storage::Ownership::Container>(
|
||||
@ -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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user