From c370ddd89ace23437adc20f62ff02a9d5923e03b Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 14 Mar 2017 12:17:31 +0100 Subject: [PATCH 01/16] Add routing algorithm option --- features/lib/osrm_loader.js | 6 ++++-- features/support/env.js | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/features/lib/osrm_loader.js b/features/lib/osrm_loader.js index 27980cf4180..992eda433da 100644 --- a/features/lib/osrm_loader.js +++ b/features/lib/osrm_loader.js @@ -77,7 +77,8 @@ class OSRMDirectLoader extends OSRMBaseLoader { osrmUp (callback) { if (this.osrmIsRunning()) return callback(new Error("osrm-routed already running!")); - this.child = this.scope.runBin('osrm-routed', util.format("%s -p %d", this.inputFile, this.scope.OSRM_PORT), this.scope.environment, (err) => { + const command_arguments = util.format('%s -p %d -a %s', this.inputFile, this.scope.OSRM_PORT, this.scope.ROUTING_ALGORITHM); + this.child = this.scope.runBin('osrm-routed', command_arguments, this.scope.environment, (err) => { if (err && err.signal !== 'SIGINT') { this.child = null; throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd)); @@ -115,7 +116,8 @@ class OSRMDatastoreLoader extends OSRMBaseLoader { osrmUp (callback) { if (this.osrmIsRunning()) return callback(); - this.child = this.scope.runBin('osrm-routed', util.format('--shared-memory=1 -p %d', this.scope.OSRM_PORT), this.scope.environment, (err) => { + const command_arguments = util.format('--shared-memory=1 -p %d -a %s', this.scope.OSRM_PORT, this.scope.ROUTING_ALGORITHM); + this.child = this.scope.runBin('osrm-routed', command_arguments, this.scope.environment, (err) => { if (err && err.signal !== 'SIGINT') { this.child = null; throw new Error(util.format('osrm-routed %s: %s', errorReason(err), err.cmd)); diff --git a/features/support/env.js b/features/support/env.js index 7aa976f3039..79c8dd862f1 100644 --- a/features/support/env.js +++ b/features/support/env.js @@ -39,6 +39,7 @@ module.exports = function () { this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z'; this.WAY_SPACING = 100; this.DEFAULT_GRID_SIZE = 100; // meters + this.ROUTING_ALGORITHM = 'CH'; this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000; this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT; From 43a7e8e08ab85e585c2610f59e3b871b8fb88246 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Fri, 10 Mar 2017 10:57:07 +0100 Subject: [PATCH 02/16] Rename {id|edge_id} to turn_id --- include/contractor/graph_contractor.hpp | 6 ++--- .../contractor/graph_contractor_adaptors.hpp | 4 +-- include/contractor/query_edge.hpp | 8 +++--- .../routing_algorithms/routing_base.hpp | 27 +++++++++---------- include/extractor/edge_based_edge.hpp | 12 ++++----- include/partition/edge_based_graph_reader.hpp | 6 ++--- .../routing_algorithms/alternative_path.cpp | 4 +-- src/updater/updater.cpp | 16 +++++------ 8 files changed, 41 insertions(+), 42 deletions(-) diff --git a/include/contractor/graph_contractor.hpp b/include/contractor/graph_contractor.hpp index f2a2b12e0cf..06904af2055 100644 --- a/include/contractor/graph_contractor.hpp +++ b/include/contractor/graph_contractor.hpp @@ -139,13 +139,13 @@ class GraphContractor if (!data.is_original_via_node_ID && !orig_node_id_from_new_node_id_map.empty()) { // tranlate the _node id_ of the shortcutted node - new_edge.data.id = orig_node_id_from_new_node_id_map[data.id]; + new_edge.data.turn_id = orig_node_id_from_new_node_id_map[data.id]; } else { - new_edge.data.id = data.id; + new_edge.data.turn_id = data.id; } - BOOST_ASSERT_MSG(new_edge.data.id != INT_MAX, // 2^31 + BOOST_ASSERT_MSG(new_edge.data.turn_id != INT_MAX, // 2^31 "edge id invalid"); new_edge.data.forward = data.forward; new_edge.data.backward = data.backward; diff --git a/include/contractor/graph_contractor_adaptors.hpp b/include/contractor/graph_contractor_adaptors.hpp index 293ef0d7821..2bb123e2a20 100644 --- a/include/contractor/graph_contractor_adaptors.hpp +++ b/include/contractor/graph_contractor_adaptors.hpp @@ -38,7 +38,7 @@ std::vector adaptToContractorInput(InputEdgeContainer input_edge std::max(input_edge.data.weight, 1), input_edge.data.duration, 1, - input_edge.data.edge_id, + input_edge.data.turn_id, false, input_edge.data.forward ? true : false, input_edge.data.backward ? true : false); @@ -48,7 +48,7 @@ std::vector adaptToContractorInput(InputEdgeContainer input_edge std::max(input_edge.data.weight, 1), input_edge.data.duration, 1, - input_edge.data.edge_id, + input_edge.data.turn_id, false, input_edge.data.backward ? true : false, input_edge.data.forward ? true : false); diff --git a/include/contractor/query_edge.hpp b/include/contractor/query_edge.hpp index f34d0575cbd..06c1f0fbf61 100644 --- a/include/contractor/query_edge.hpp +++ b/include/contractor/query_edge.hpp @@ -17,7 +17,7 @@ struct QueryEdge struct EdgeData { explicit EdgeData() - : id(0), shortcut(false), weight(0), duration(0), forward(false), backward(false) + : turn_id(0), shortcut(false), weight(0), duration(0), forward(false), backward(false) { } @@ -26,14 +26,14 @@ struct QueryEdge weight = other.weight; duration = other.duration; shortcut = other.shortcut; - id = other.id; + turn_id = other.id; forward = other.forward; backward = other.backward; } // this ID is either the middle node of the shortcut, or the ID of the edge based node (node // based edge) storing the appropriate data. If `shortcut` is set to true, we get the middle // node. Otherwise we see the edge based node to access node data. - NodeID id : 31; + NodeID turn_id : 31; bool shortcut : 1; EdgeWeight weight; EdgeWeight duration : 30; @@ -58,7 +58,7 @@ struct QueryEdge return (source == right.source && target == right.target && data.weight == right.data.weight && data.duration == right.data.duration && data.shortcut == right.data.shortcut && data.forward == right.data.forward && - data.backward == right.data.backward && data.id == right.data.id); + data.backward == right.data.backward && data.turn_id == right.data.turn_id); } }; } diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index e1b0a630e09..946c506f443 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -292,7 +292,7 @@ void unpackPath(const datafacade::ContiguousInternalMemoryDataFacademiddle to get visited before middle->second recursion_stack.emplace(middle_node_id, edge.second); @@ -340,14 +340,15 @@ void unpackPath(const FacadeT &facade, const auto &edge_data) { BOOST_ASSERT_MSG(!edge_data.shortcut, "original edge flagged as shortcut"); - const auto name_index = facade.GetNameIndexFromEdgeID(edge_data.id); - const auto turn_instruction = facade.GetTurnInstructionForEdgeID(edge_data.id); + const auto turn_id = edge_data.turn_id; // edge-based node ID + const auto name_index = facade.GetNameIndexFromEdgeID(turn_id); + const auto turn_instruction = facade.GetTurnInstructionForEdgeID(turn_id); const extractor::TravelMode travel_mode = (unpacked_path.empty() && start_traversed_in_reverse) ? phantom_node_pair.source_phantom.backward_travel_mode - : facade.GetTravelModeForEdgeID(edge_data.id); + : facade.GetTravelModeForEdgeID(turn_id); - const auto geometry_index = facade.GetGeometryIndexForEdgeID(edge_data.id); + const auto geometry_index = facade.GetGeometryIndexForEdgeID(turn_id); std::vector id_vector; std::vector weight_vector; @@ -399,17 +400,15 @@ void unpackPath(const FacadeT &facade, util::guidance::TurnBearing(0)}); } BOOST_ASSERT(unpacked_path.size() > 0); - if (facade.hasLaneData(edge_data.id)) - unpacked_path.back().lane_data = facade.GetLaneData(edge_data.id); + if (facade.hasLaneData(turn_id)) + unpacked_path.back().lane_data = facade.GetLaneData(turn_id); - unpacked_path.back().entry_classid = facade.GetEntryClassID(edge_data.id); + unpacked_path.back().entry_classid = facade.GetEntryClassID(turn_id); unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().duration_until_turn += - facade.GetDurationPenaltyForEdgeID(edge_data.id); - unpacked_path.back().weight_until_turn += - facade.GetWeightPenaltyForEdgeID(edge_data.id); - unpacked_path.back().pre_turn_bearing = facade.PreTurnBearing(edge_data.id); - unpacked_path.back().post_turn_bearing = facade.PostTurnBearing(edge_data.id); + unpacked_path.back().duration_until_turn += facade.GetDurationPenaltyForEdgeID(turn_id); + unpacked_path.back().weight_until_turn += facade.GetWeightPenaltyForEdgeID(turn_id); + unpacked_path.back().pre_turn_bearing = facade.PreTurnBearing(turn_id); + unpacked_path.back().post_turn_bearing = facade.PostTurnBearing(turn_id); }); std::size_t start_index = 0, end_index = 0; diff --git a/include/extractor/edge_based_edge.hpp b/include/extractor/edge_based_edge.hpp index cbc10308d3b..a0611d3b6da 100644 --- a/include/extractor/edge_based_edge.hpp +++ b/include/extractor/edge_based_edge.hpp @@ -32,19 +32,19 @@ struct EdgeBasedEdge struct EdgeData { - EdgeData() : edge_id(0), weight(0), duration(0), forward(false), backward(false) {} + EdgeData() : turn_id(0), weight(0), duration(0), forward(false), backward(false) {} - EdgeData(const NodeID edge_id, + EdgeData(const NodeID turn_id, const EdgeWeight weight, const EdgeWeight duration, const bool forward, const bool backward) - : edge_id(edge_id), weight(weight), duration(duration), forward(forward), + : turn_id(turn_id), weight(weight), duration(duration), forward(forward), backward(backward) { } - NodeID edge_id; + NodeID turn_id; // ID of the edge based node (node based edge) EdgeWeight weight; EdgeWeight duration : 30; std::uint32_t forward : 1; @@ -64,12 +64,12 @@ inline EdgeBasedEdge::EdgeBasedEdge() : source(0), target(0) {} inline EdgeBasedEdge::EdgeBasedEdge(const NodeID source, const NodeID target, - const NodeID edge_id, + const NodeID turn_id, const EdgeWeight weight, const EdgeWeight duration, const bool forward, const bool backward) - : source(source), target(target), data{edge_id, weight, duration, forward, backward} + : source(source), target(target), data{turn_id, weight, duration, forward, backward} { } diff --git a/include/partition/edge_based_graph_reader.hpp b/include/partition/edge_based_graph_reader.hpp index 78e578629d3..aa544346cf8 100644 --- a/include/partition/edge_based_graph_reader.hpp +++ b/include/partition/edge_based_graph_reader.hpp @@ -35,7 +35,7 @@ splitBidirectionalEdges(const std::vector &edges) directed.emplace_back(edge.source, edge.target, - edge.data.edge_id, + edge.data.turn_id, std::max(edge.data.weight, 1), edge.data.duration, edge.data.forward, @@ -43,7 +43,7 @@ splitBidirectionalEdges(const std::vector &edges) directed.emplace_back(edge.target, edge.source, - edge.data.edge_id, + edge.data.turn_id, std::max(edge.data.weight, 1), edge.data.duration, edge.data.backward, @@ -77,7 +77,7 @@ std::vector prepareEdgesForUsageInGraph(std::vector edge.data.weight) { - util::Log(logWARNING) << geometry_id.id << " vs " << edge.data.edge_id << ":" + util::Log(logWARNING) << geometry_id.id << " vs " << edge.data.turn_id << ":" << weight << " > " << edge.data.weight; } } @@ -112,7 +112,7 @@ void checkWeightsConsistency( EdgeWeight weight = std::accumulate(range.begin(), range.end(), EdgeWeight{0}); if (weight > edge.data.weight) { - util::Log(logWARNING) << geometry_id.id << " vs " << edge.data.edge_id << ":" + util::Log(logWARNING) << geometry_id.id << " vs " << edge.data.turn_id << ":" << weight << " > " << edge.data.weight; } } @@ -583,7 +583,7 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &e }); const auto update_edge = [&](extractor::EdgeBasedEdge &edge) { - const auto geometry_id = edge_data[edge.data.edge_id].via_geometry; + const auto geometry_id = edge_data[edge.data.turn_id].via_geometry; auto updated_iter = std::lower_bound(updated_segments.begin(), updated_segments.end(), geometry_id, @@ -619,8 +619,8 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &e } // Get the turn penalty and update to the new value if required - auto turn_weight_penalty = turn_weight_penalties[edge.data.edge_id]; - auto turn_duration_penalty = turn_duration_penalties[edge.data.edge_id]; + auto turn_weight_penalty = turn_weight_penalties[edge.data.turn_id]; + auto turn_duration_penalty = turn_duration_penalties[edge.data.turn_id]; const auto num_nodes = segment_data.GetForwardGeometry(geometry_id.id).size(); const auto weight_min_value = static_cast(num_nodes); if (turn_weight_penalty + new_weight < weight_min_value) @@ -631,7 +631,7 @@ Updater::LoadAndUpdateEdgeExpandedGraph(std::vector &e << " is too negative: clamping turn weight to " << weight_min_value; turn_weight_penalty = weight_min_value - new_weight; - turn_weight_penalties[edge.data.edge_id] = turn_weight_penalty; + turn_weight_penalties[edge.data.turn_id] = turn_weight_penalty; } else { From 6829f46c31855d2321fd1768d9561bc0c667b581 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Fri, 10 Mar 2017 10:34:54 +0100 Subject: [PATCH 03/16] Split routing_base into CH and non-CH parts --- .../routing_algorithms/routing_base.hpp | 597 ++++-------------- .../routing_algorithms/routing_base_ch.hpp | 475 ++++++++++++++ .../routing_algorithms/alternative_path.cpp | 204 +++--- .../direct_shortest_path.cpp | 48 +- .../routing_algorithms/many_to_many.cpp | 42 +- .../routing_algorithms/map_matching.cpp | 18 +- .../{routing_base.cpp => routing_base_ch.cpp} | 31 +- .../routing_algorithms/shortest_path.cpp | 71 ++- 8 files changed, 746 insertions(+), 740 deletions(-) create mode 100644 include/engine/routing_algorithms/routing_base_ch.hpp rename src/engine/routing_algorithms/{routing_base.cpp => routing_base_ch.cpp} (94%) diff --git a/include/engine/routing_algorithms/routing_base.hpp b/include/engine/routing_algorithms/routing_base.hpp index 946c506f443..def1960e658 100644 --- a/include/engine/routing_algorithms/routing_base.hpp +++ b/include/engine/routing_algorithms/routing_base.hpp @@ -1,5 +1,5 @@ -#ifndef ROUTING_BASE_HPP -#define ROUTING_BASE_HPP +#ifndef OSRM_ENGINE_ROUTING_BASE_HPP +#define OSRM_ENGINE_ROUTING_BASE_HPP #include "extractor/guidance/turn_instruction.hpp" @@ -35,381 +35,150 @@ namespace routing_algorithms { static constexpr bool FORWARD_DIRECTION = true; static constexpr bool REVERSE_DIRECTION = false; +static constexpr bool DO_NOT_FORCE_LOOPS = false; -// Stalling -template -bool stallAtNode(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const NodeID node, - const EdgeWeight weight, - const HeapT &query_heap) +template +void insertNodesInHeap(Heap &heap, const PhantomNode &phantom_node) { - for (auto edge : facade.GetAdjacentEdgeRange(node)) + BOOST_ASSERT(phantom_node.IsValid()); + + const auto weight_sign = DIRECTION == FORWARD_DIRECTION ? -1 : 1; + if (phantom_node.forward_segment_id.enabled) { - const auto &data = facade.GetEdgeData(edge); - if (DIRECTION == REVERSE_DIRECTION ? data.forward : data.backward) - { - const NodeID to = facade.GetTarget(edge); - const EdgeWeight edge_weight = data.weight; - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - if (query_heap.WasInserted(to)) - { - if (query_heap.GetKey(to) + edge_weight < weight) - { - return true; - } - } - } + heap.Insert(phantom_node.forward_segment_id.id, + weight_sign * phantom_node.GetForwardWeightPlusOffset(), + phantom_node.forward_segment_id.id); } - return false; -} - -template -void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const NodeID node, - const EdgeWeight weight, - SearchEngineData::QueryHeap &heap) -{ - for (const auto edge : facade.GetAdjacentEdgeRange(node)) + if (phantom_node.reverse_segment_id.enabled) { - 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; - - BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); - const EdgeWeight to_weight = weight + edge_weight; - - // New Node discovered -> Add to Heap + Node Info Storage - if (!heap.WasInserted(to)) - { - heap.Insert(to, to_weight, node); - } - // Found a shorter Path -> Update weight - else if (to_weight < heap.GetKey(to)) - { - // new parent - heap.GetData(to).parent = node; - heap.DecreaseKey(to, to_weight); - } - } + heap.Insert(phantom_node.reverse_segment_id.id, + weight_sign * phantom_node.GetReverseWeightPlusOffset(), + phantom_node.reverse_segment_id.id); } } -/* -min_edge_offset is needed in case we use multiple -nodes as start/target nodes with different (even negative) offsets. -In that case the termination criterion is not correct -anymore. - -Example: -forward heap: a(-100), b(0), -reverse heap: c(0), d(100) - -a --- d - \ / - / \ -b --- c - -This is equivalent to running a bi-directional Dijkstra on the following graph: - - a --- d - / \ / \ - y x z - \ / \ / - b --- c - -The graph is constructed by inserting nodes y and z that are connected to the initial nodes -using edges (y, a) with weight -100, (y, b) with weight 0 and, -(d, z) with weight 100, (c, z) with weight 0 corresponding. -Since we are dealing with a graph that contains _negative_ edges, -we need to add an offset to the termination criterion. -*/ -static constexpr bool ENABLE_STALLING = true; -static constexpr bool DISABLE_STALLING = false; -static constexpr bool DO_NOT_FORCE_LOOPS = false; -template -void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - NodeID &middle_node_id, - EdgeWeight &upper_bound, - EdgeWeight min_edge_offset, - const bool force_loop_forward, - const bool force_loop_reverse) +template +void insertNodesInHeap(SearchEngineData::ManyToManyQueryHeap &heap, const PhantomNode &phantom_node) { - const NodeID node = forward_heap.DeleteMin(); - const EdgeWeight weight = forward_heap.GetKey(node); + BOOST_ASSERT(phantom_node.IsValid()); - if (reverse_heap.WasInserted(node)) + const auto weight_sign = DIRECTION == FORWARD_DIRECTION ? -1 : 1; + if (phantom_node.forward_segment_id.enabled) { - const EdgeWeight new_weight = reverse_heap.GetKey(node) + weight; - if (new_weight < upper_bound) - { - // if loops are forced, they are so at the source - if ((force_loop_forward && forward_heap.GetData(node).parent == node) || - (force_loop_reverse && reverse_heap.GetData(node).parent == node) || - // in this case we are looking at a bi-directional way where the source - // and target phantom are on the same edge based node - new_weight < 0) - { - // check whether there is a loop present at the node - for (const auto edge : facade.GetAdjacentEdgeRange(node)) - { - const auto &data = facade.GetEdgeData(edge); - if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) - { - const NodeID to = facade.GetTarget(edge); - if (to == node) - { - const EdgeWeight edge_weight = data.weight; - const EdgeWeight loop_weight = new_weight + edge_weight; - if (loop_weight >= 0 && loop_weight < upper_bound) - { - middle_node_id = node; - upper_bound = loop_weight; - } - } - } - } - } - else - { - BOOST_ASSERT(new_weight >= 0); - - middle_node_id = node; - upper_bound = new_weight; - } - } - } - - // make sure we don't terminate too early if we initialize the weight - // for the nodes in the forward heap with the forward/reverse offset - BOOST_ASSERT(min_edge_offset <= 0); - if (weight + min_edge_offset > upper_bound) - { - forward_heap.DeleteAll(); - return; + heap.Insert( + phantom_node.forward_segment_id.id, + weight_sign * phantom_node.GetForwardWeightPlusOffset(), + {phantom_node.forward_segment_id.id, weight_sign * phantom_node.GetForwardDuration()}); } - - // Stalling - if (STALLING && stallAtNode(facade, node, weight, forward_heap)) + if (phantom_node.reverse_segment_id.enabled) { - return; + heap.Insert( + phantom_node.reverse_segment_id.id, + weight_sign * phantom_node.GetReverseWeightPlusOffset(), + {phantom_node.reverse_segment_id.id, weight_sign * phantom_node.GetReverseDuration()}); } - - relaxOutgoingEdges(facade, node, weight, forward_heap); } -template -EdgeWeight -getLoopWeight(const datafacade::ContiguousInternalMemoryDataFacade &facade, - NodeID node) +template +void insertNodesInHeaps(Heap &forward_heap, Heap &reverse_heap, const PhantomNodes &nodes) { - EdgeWeight loop_weight = UseDuration ? MAXIMAL_EDGE_DURATION : INVALID_EDGE_WEIGHT; - for (auto edge : facade.GetAdjacentEdgeRange(node)) - { - const auto &data = facade.GetEdgeData(edge); - if (data.forward) - { - const NodeID to = facade.GetTarget(edge); - if (to == node) - { - const auto value = UseDuration ? data.duration : data.weight; - loop_weight = std::min(loop_weight, value); - } - } - } - return loop_weight; + insertNodesInHeap(forward_heap, nodes.source_phantom); + insertNodesInHeap(reverse_heap, nodes.target_phantom); } -/** - * Given a sequence of connected `NodeID`s in the CH graph, performs a depth-first unpacking of - * the shortcut - * edges. For every "original" edge found, it calls the `callback` with the two NodeIDs for the - * edge, and the EdgeData - * for that edge. - * - * The primary purpose of this unpacking is to expand a path through the CH into the original - * route through the - * pre-contracted graph. - * - * Because of the depth-first-search, the `callback` will effectively be called in sequence for - * the original route - * from beginning to end. - * - * @param packed_path_begin iterator pointing to the start of the NodeID list - * @param packed_path_end iterator pointing to the end of the NodeID list - * @param callback void(const std::pair, const EdgeData &) called for each - * original edge found. - */ -template -void unpackPath(const datafacade::ContiguousInternalMemoryDataFacade &facade, - BidirectionalIterator packed_path_begin, - BidirectionalIterator packed_path_end, - Callback &&callback) +template +void annotatePath(const FacadeT &facade, + const NodeID source_node, + const NodeID target_node, + const std::vector &unpacked_edges, + const PhantomNodes &phantom_node_pair, + std::vector &unpacked_path) { - // make sure we have at least something to unpack - if (packed_path_begin == packed_path_end) - return; + BOOST_ASSERT(source_node != SPECIAL_NODEID && target_node != SPECIAL_NODEID); + BOOST_ASSERT(!unpacked_edges.empty() || source_node == target_node); - std::stack> recursion_stack; + const bool start_traversed_in_reverse = + phantom_node_pair.source_phantom.forward_segment_id.id != source_node; + const bool target_traversed_in_reverse = + phantom_node_pair.target_phantom.forward_segment_id.id != target_node; - // We have to push the path in reverse order onto the stack because it's LIFO. - for (auto current = std::prev(packed_path_end); current != packed_path_begin; - current = std::prev(current)) - { - recursion_stack.emplace(*std::prev(current), *current); - } + BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id == source_node || + phantom_node_pair.source_phantom.reverse_segment_id.id == source_node); + BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id == target_node || + phantom_node_pair.target_phantom.reverse_segment_id.id == target_node); - std::pair edge; - while (!recursion_stack.empty()) + for (auto edge_id : unpacked_edges) { - edge = recursion_stack.top(); - recursion_stack.pop(); - - // Look for an edge on the forward CH graph (.forward) - EdgeID smaller_edge_id = facade.FindSmallestEdge( - edge.first, edge.second, [](const auto &data) { return data.forward; }); - - // If we didn't find one there, the we might be looking at a part of the path that - // was found using the backward search. Here, we flip the node order (.second, .first) - // and only consider edges with the `.backward` flag. - if (SPECIAL_EDGEID == smaller_edge_id) + const auto &edge_data = facade.GetEdgeData(edge_id); + const auto turn_id = edge_data.turn_id; // edge-based node ID + const auto name_index = facade.GetNameIndexFromEdgeID(turn_id); + const auto turn_instruction = facade.GetTurnInstructionForEdgeID(turn_id); + const extractor::TravelMode travel_mode = + (unpacked_path.empty() && start_traversed_in_reverse) + ? phantom_node_pair.source_phantom.backward_travel_mode + : facade.GetTravelModeForEdgeID(turn_id); + + const auto geometry_index = facade.GetGeometryIndexForEdgeID(turn_id); + std::vector id_vector; + + std::vector weight_vector; + std::vector duration_vector; + std::vector datasource_vector; + if (geometry_index.forward) { - smaller_edge_id = facade.FindSmallestEdge( - edge.second, edge.first, [](const auto &data) { return data.backward; }); - } - - // If we didn't find anything *still*, then something is broken and someone has - // called this function with bad values. - BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID"); - - const auto &data = facade.GetEdgeData(smaller_edge_id); - BOOST_ASSERT_MSG(data.weight != std::numeric_limits::max(), - "edge weight invalid"); - - // If the edge is a shortcut, we need to add the two halfs to the stack. - if (data.shortcut) - { // unpack - const NodeID middle_node_id = data.turn_id; - // Note the order here - we're adding these to a stack, so we - // want the first->middle to get visited before middle->second - recursion_stack.emplace(middle_node_id, edge.second); - recursion_stack.emplace(edge.first, middle_node_id); + id_vector = facade.GetUncompressedForwardGeometry(geometry_index.id); + weight_vector = facade.GetUncompressedForwardWeights(geometry_index.id); + duration_vector = facade.GetUncompressedForwardDurations(geometry_index.id); + datasource_vector = facade.GetUncompressedForwardDatasources(geometry_index.id); } else { - // We found an original edge, call our callback. - std::forward(callback)(edge, data); + id_vector = facade.GetUncompressedReverseGeometry(geometry_index.id); + weight_vector = facade.GetUncompressedReverseWeights(geometry_index.id); + duration_vector = facade.GetUncompressedReverseDurations(geometry_index.id); + datasource_vector = facade.GetUncompressedReverseDatasources(geometry_index.id); } + BOOST_ASSERT(id_vector.size() > 0); + BOOST_ASSERT(datasource_vector.size() > 0); + BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1); + BOOST_ASSERT(duration_vector.size() == id_vector.size() - 1); + const bool is_first_segment = unpacked_path.empty(); + + const std::size_t start_index = + (is_first_segment ? ((start_traversed_in_reverse) + ? weight_vector.size() - + phantom_node_pair.source_phantom.fwd_segment_position - 1 + : phantom_node_pair.source_phantom.fwd_segment_position) + : 0); + const std::size_t end_index = weight_vector.size(); + + BOOST_ASSERT(start_index >= 0); + BOOST_ASSERT(start_index < end_index); + for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx) + { + unpacked_path.push_back(PathData{id_vector[segment_idx + 1], + name_index, + weight_vector[segment_idx], + duration_vector[segment_idx], + extractor::guidance::TurnInstruction::NO_TURN(), + {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, + travel_mode, + INVALID_ENTRY_CLASSID, + datasource_vector[segment_idx], + util::guidance::TurnBearing(0), + util::guidance::TurnBearing(0)}); + } + BOOST_ASSERT(unpacked_path.size() > 0); + if (facade.hasLaneData(turn_id)) + unpacked_path.back().lane_data = facade.GetLaneData(turn_id); + + unpacked_path.back().entry_classid = facade.GetEntryClassID(turn_id); + unpacked_path.back().turn_instruction = turn_instruction; + unpacked_path.back().duration_until_turn += facade.GetDurationPenaltyForEdgeID(turn_id); + unpacked_path.back().weight_until_turn += facade.GetWeightPenaltyForEdgeID(turn_id); + unpacked_path.back().pre_turn_bearing = facade.PreTurnBearing(turn_id); + unpacked_path.back().post_turn_bearing = facade.PostTurnBearing(turn_id); } -} - -// Should work both for CH and not CH if the unpackPath function above is implemented a proper -// implementation. -template -void unpackPath(const FacadeT &facade, - RandomIter packed_path_begin, - RandomIter packed_path_end, - const PhantomNodes &phantom_node_pair, - std::vector &unpacked_path) -{ - BOOST_ASSERT(std::distance(packed_path_begin, packed_path_end) > 0); - - const bool start_traversed_in_reverse = - (*packed_path_begin != phantom_node_pair.source_phantom.forward_segment_id.id); - const bool target_traversed_in_reverse = - (*std::prev(packed_path_end) != phantom_node_pair.target_phantom.forward_segment_id.id); - - BOOST_ASSERT(*packed_path_begin == phantom_node_pair.source_phantom.forward_segment_id.id || - *packed_path_begin == phantom_node_pair.source_phantom.reverse_segment_id.id); - BOOST_ASSERT( - *std::prev(packed_path_end) == phantom_node_pair.target_phantom.forward_segment_id.id || - *std::prev(packed_path_end) == phantom_node_pair.target_phantom.reverse_segment_id.id); - - unpackPath( - facade, - packed_path_begin, - packed_path_end, - [&facade, - &unpacked_path, - &phantom_node_pair, - &start_traversed_in_reverse, - &target_traversed_in_reverse](std::pair & /* edge */, - const auto &edge_data) { - - BOOST_ASSERT_MSG(!edge_data.shortcut, "original edge flagged as shortcut"); - const auto turn_id = edge_data.turn_id; // edge-based node ID - const auto name_index = facade.GetNameIndexFromEdgeID(turn_id); - const auto turn_instruction = facade.GetTurnInstructionForEdgeID(turn_id); - const extractor::TravelMode travel_mode = - (unpacked_path.empty() && start_traversed_in_reverse) - ? phantom_node_pair.source_phantom.backward_travel_mode - : facade.GetTravelModeForEdgeID(turn_id); - - const auto geometry_index = facade.GetGeometryIndexForEdgeID(turn_id); - std::vector id_vector; - - std::vector weight_vector; - std::vector duration_vector; - std::vector datasource_vector; - if (geometry_index.forward) - { - id_vector = facade.GetUncompressedForwardGeometry(geometry_index.id); - weight_vector = facade.GetUncompressedForwardWeights(geometry_index.id); - duration_vector = facade.GetUncompressedForwardDurations(geometry_index.id); - datasource_vector = facade.GetUncompressedForwardDatasources(geometry_index.id); - } - else - { - id_vector = facade.GetUncompressedReverseGeometry(geometry_index.id); - weight_vector = facade.GetUncompressedReverseWeights(geometry_index.id); - duration_vector = facade.GetUncompressedReverseDurations(geometry_index.id); - datasource_vector = facade.GetUncompressedReverseDatasources(geometry_index.id); - } - BOOST_ASSERT(id_vector.size() > 0); - BOOST_ASSERT(datasource_vector.size() > 0); - BOOST_ASSERT(weight_vector.size() == id_vector.size() - 1); - BOOST_ASSERT(duration_vector.size() == id_vector.size() - 1); - const bool is_first_segment = unpacked_path.empty(); - - const std::size_t start_index = - (is_first_segment - ? ((start_traversed_in_reverse) - ? weight_vector.size() - - phantom_node_pair.source_phantom.fwd_segment_position - 1 - : phantom_node_pair.source_phantom.fwd_segment_position) - : 0); - const std::size_t end_index = weight_vector.size(); - - BOOST_ASSERT(start_index >= 0); - BOOST_ASSERT(start_index < end_index); - for (std::size_t segment_idx = start_index; segment_idx < end_index; ++segment_idx) - { - unpacked_path.push_back(PathData{id_vector[segment_idx + 1], - name_index, - weight_vector[segment_idx], - duration_vector[segment_idx], - extractor::guidance::TurnInstruction::NO_TURN(), - {{0, INVALID_LANEID}, INVALID_LANE_DESCRIPTIONID}, - travel_mode, - INVALID_ENTRY_CLASSID, - datasource_vector[segment_idx], - util::guidance::TurnBearing(0), - util::guidance::TurnBearing(0)}); - } - BOOST_ASSERT(unpacked_path.size() > 0); - if (facade.hasLaneData(turn_id)) - unpacked_path.back().lane_data = facade.GetLaneData(turn_id); - - unpacked_path.back().entry_classid = facade.GetEntryClassID(turn_id); - unpacked_path.back().turn_instruction = turn_instruction; - unpacked_path.back().duration_until_turn += facade.GetDurationPenaltyForEdgeID(turn_id); - unpacked_path.back().weight_until_turn += facade.GetWeightPenaltyForEdgeID(turn_id); - unpacked_path.back().pre_turn_bearing = facade.PreTurnBearing(turn_id); - unpacked_path.back().post_turn_bearing = facade.PostTurnBearing(turn_id); - }); std::size_t start_index = 0, end_index = 0; std::vector id_vector; @@ -536,140 +305,8 @@ void unpackPath(const FacadeT &facade, } } -/** - * Unpacks a single edge (NodeID->NodeID) from the CH graph down to it's original non-shortcut - * route. - * @param from the node the CH edge starts at - * @param to the node the CH edge finishes at - * @param unpacked_path the sequence of original NodeIDs that make up the expanded CH edge - */ -void unpackEdge(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const NodeID from, - const NodeID to, - std::vector &unpacked_path); - -void retrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, - const SearchEngineData::QueryHeap &reverse_heap, - const NodeID middle_node_id, - std::vector &packed_path); - -void retrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &search_heap, - const NodeID middle_node_id, - std::vector &packed_path); - -// assumes that heaps are already setup correctly. -// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node -// Offsets. -// In case additional offsets are supplied, you might have to force a loop first. -// A forced loop might be necessary, if source and target are on the same segment. -// If this is the case and the offsets of the respective direction are larger for the source -// than the target -// then a force loop is required (e.g. source_phantom.forward_segment_id == -// target_phantom.forward_segment_id -// && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) -// requires -// a force loop, if the heaps have been initialized with positive offsets. -void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - std::int32_t &weight, - std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - const int duration_upper_bound = INVALID_EDGE_WEIGHT); - -// Alias to be compatible with the overload for CoreCH that needs 4 heaps -inline void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - SearchEngineData::QueryHeap &, - SearchEngineData::QueryHeap &, - std::int32_t &weight, - std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - const int duration_upper_bound = INVALID_EDGE_WEIGHT) -{ - search(facade, - forward_heap, - reverse_heap, - weight, - packed_leg, - force_loop_forward, - force_loop_reverse, - duration_upper_bound); -} - -// assumes that heaps are already setup correctly. -// A forced loop might be necessary, if source and target are on the same segment. -// If this is the case and the offsets of the respective direction are larger for the source -// than the target -// then a force loop is required (e.g. source_phantom.forward_segment_id == -// target_phantom.forward_segment_id -// && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) -// requires -// a force loop, if the heaps have been initialized with positive offsets. -void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - SearchEngineData::QueryHeap &forward_core_heap, - SearchEngineData::QueryHeap &reverse_core_heap, - int &weight, - std::vector &packed_leg, - const bool force_loop_forward, - const bool force_loop_reverse, - int duration_upper_bound = INVALID_EDGE_WEIGHT); - -bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom); - -bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom); - -double getPathDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const std::vector &packed_path, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom); - -// Requires the heaps for be empty -// If heaps should be adjusted to be initialized outside of this function, -// the addition of force_loop parameters might be required -double -getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - SearchEngineData::QueryHeap &forward_core_heap, - SearchEngineData::QueryHeap &reverse_core_heap, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - int duration_upper_bound = INVALID_EDGE_WEIGHT); - -// Requires the heaps for be empty -// If heaps should be adjusted to be initialized outside of this function, -// the addition of force_loop parameters might be required -double -getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - int duration_upper_bound = INVALID_EDGE_WEIGHT); - -// Alias to be compatible with the overload for CoreCH that needs 4 heaps -inline double -getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - SearchEngineData::QueryHeap &, - SearchEngineData::QueryHeap &, - const PhantomNode &source_phantom, - const PhantomNode &target_phantom, - int duration_upper_bound = INVALID_EDGE_WEIGHT) -{ - return getNetworkDistance( - facade, forward_heap, reverse_heap, source_phantom, target_phantom, duration_upper_bound); -} - } // namespace routing_algorithms } // namespace engine } // namespace osrm -#endif // ROUTING_BASE_HPP +#endif // OSRM_ENGINE_ROUTING_BASE_HPP diff --git a/include/engine/routing_algorithms/routing_base_ch.hpp b/include/engine/routing_algorithms/routing_base_ch.hpp new file mode 100644 index 00000000000..994e5e5c901 --- /dev/null +++ b/include/engine/routing_algorithms/routing_base_ch.hpp @@ -0,0 +1,475 @@ +#ifndef OSRM_ENGINE_ROUTING_BASE_CH_HPP +#define OSRM_ENGINE_ROUTING_BASE_CH_HPP + +#include "extractor/guidance/turn_instruction.hpp" + +#include "engine/algorithm.hpp" +#include "engine/datafacade/contiguous_internalmem_datafacade.hpp" +#include "engine/internal_route_result.hpp" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" + +#include "util/coordinate_calculation.hpp" +#include "util/guidance/turn_bearing.hpp" +#include "util/typedefs.hpp" + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace osrm +{ +namespace engine +{ + +namespace routing_algorithms +{ + +namespace ch +{ + +// Stalling +template +bool stallAtNode(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const NodeID node, + const EdgeWeight weight, + const HeapT &query_heap) +{ + for (auto edge : facade.GetAdjacentEdgeRange(node)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == REVERSE_DIRECTION ? data.forward : data.backward) + { + const NodeID to = facade.GetTarget(edge); + const EdgeWeight edge_weight = data.weight; + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + if (query_heap.WasInserted(to)) + { + if (query_heap.GetKey(to) + edge_weight < weight) + { + return true; + } + } + } + } + return false; +} + +template +void relaxOutgoingEdges(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const NodeID node, + const EdgeWeight weight, + SearchEngineData::QueryHeap &heap) +{ + for (const auto edge : facade.GetAdjacentEdgeRange(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; + + BOOST_ASSERT_MSG(edge_weight > 0, "edge_weight invalid"); + const EdgeWeight to_weight = weight + edge_weight; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_weight, node); + } + // Found a shorter Path -> Update weight + else if (to_weight < heap.GetKey(to)) + { + // new parent + heap.GetData(to).parent = node; + heap.DecreaseKey(to, to_weight); + } + } + } +} + +/* +min_edge_offset is needed in case we use multiple +nodes as start/target nodes with different (even negative) offsets. +In that case the termination criterion is not correct +anymore. + +Example: +forward heap: a(-100), b(0), +reverse heap: c(0), d(100) + +a --- d + \ / + / \ +b --- c + +This is equivalent to running a bi-directional Dijkstra on the following graph: + + a --- d + / \ / \ + y x z + \ / \ / + b --- c + +The graph is constructed by inserting nodes y and z that are connected to the initial nodes +using edges (y, a) with weight -100, (y, b) with weight 0 and, +(d, z) with weight 100, (c, z) with weight 0 corresponding. +Since we are dealing with a graph that contains _negative_ edges, +we need to add an offset to the termination criterion. +*/ +static constexpr bool ENABLE_STALLING = true; +static constexpr bool DISABLE_STALLING = false; +template +void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + NodeID &middle_node_id, + EdgeWeight &upper_bound, + EdgeWeight min_edge_offset, + const bool force_loop_forward, + const bool force_loop_reverse) +{ + const NodeID node = forward_heap.DeleteMin(); + const EdgeWeight weight = forward_heap.GetKey(node); + + if (reverse_heap.WasInserted(node)) + { + const EdgeWeight new_weight = reverse_heap.GetKey(node) + weight; + if (new_weight < upper_bound) + { + // if loops are forced, they are so at the source + if ((force_loop_forward && forward_heap.GetData(node).parent == node) || + (force_loop_reverse && reverse_heap.GetData(node).parent == node) || + // in this case we are looking at a bi-directional way where the source + // and target phantom are on the same edge based node + new_weight < 0) + { + // check whether there is a loop present at the node + for (const auto edge : facade.GetAdjacentEdgeRange(node)) + { + const auto &data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? data.forward : data.backward) + { + const NodeID to = facade.GetTarget(edge); + if (to == node) + { + const EdgeWeight edge_weight = data.weight; + const EdgeWeight loop_weight = new_weight + edge_weight; + if (loop_weight >= 0 && loop_weight < upper_bound) + { + middle_node_id = node; + upper_bound = loop_weight; + } + } + } + } + } + else + { + BOOST_ASSERT(new_weight >= 0); + + middle_node_id = node; + upper_bound = new_weight; + } + } + } + + // make sure we don't terminate too early if we initialize the weight + // for the nodes in the forward heap with the forward/reverse offset + BOOST_ASSERT(min_edge_offset <= 0); + if (weight + min_edge_offset > upper_bound) + { + forward_heap.DeleteAll(); + return; + } + + // Stalling + if (STALLING && stallAtNode(facade, node, weight, forward_heap)) + { + return; + } + + relaxOutgoingEdges(facade, node, weight, forward_heap); +} + +template +EdgeWeight +getLoopWeight(const datafacade::ContiguousInternalMemoryDataFacade &facade, + NodeID node) +{ + EdgeWeight loop_weight = UseDuration ? MAXIMAL_EDGE_DURATION : INVALID_EDGE_WEIGHT; + for (auto edge : facade.GetAdjacentEdgeRange(node)) + { + const auto &data = facade.GetEdgeData(edge); + if (data.forward) + { + const NodeID to = facade.GetTarget(edge); + if (to == node) + { + const auto value = UseDuration ? data.duration : data.weight; + loop_weight = std::min(loop_weight, value); + } + } + } + return loop_weight; +} + +/** + * Given a sequence of connected `NodeID`s in the CH graph, performs a depth-first unpacking of + * the shortcut + * edges. For every "original" edge found, it calls the `callback` with the two NodeIDs for the + * edge, and the EdgeData + * for that edge. + * + * The primary purpose of this unpacking is to expand a path through the CH into the original + * route through the + * pre-contracted graph. + * + * Because of the depth-first-search, the `callback` will effectively be called in sequence for + * the original route + * from beginning to end. + * + * @param packed_path_begin iterator pointing to the start of the NodeID list + * @param packed_path_end iterator pointing to the end of the NodeID list + * @param callback void(const std::pair, const EdgeID &) called for each + * original edge found. + */ +template +void unpackPath(const datafacade::ContiguousInternalMemoryDataFacade &facade, + BidirectionalIterator packed_path_begin, + BidirectionalIterator packed_path_end, + Callback &&callback) +{ + // make sure we have at least something to unpack + if (packed_path_begin == packed_path_end) + return; + + std::stack> recursion_stack; + + // We have to push the path in reverse order onto the stack because it's LIFO. + for (auto current = std::prev(packed_path_end); current != packed_path_begin; + current = std::prev(current)) + { + recursion_stack.emplace(*std::prev(current), *current); + } + + std::pair edge; + while (!recursion_stack.empty()) + { + edge = recursion_stack.top(); + recursion_stack.pop(); + + // Look for an edge on the forward CH graph (.forward) + EdgeID smaller_edge_id = facade.FindSmallestEdge( + edge.first, edge.second, [](const auto &data) { return data.forward; }); + + // If we didn't find one there, the we might be looking at a part of the path that + // was found using the backward search. Here, we flip the node order (.second, .first) + // and only consider edges with the `.backward` flag. + if (SPECIAL_EDGEID == smaller_edge_id) + { + smaller_edge_id = facade.FindSmallestEdge( + edge.second, edge.first, [](const auto &data) { return data.backward; }); + } + + // If we didn't find anything *still*, then something is broken and someone has + // called this function with bad values. + BOOST_ASSERT_MSG(smaller_edge_id != SPECIAL_EDGEID, "Invalid smaller edge ID"); + + const auto &data = facade.GetEdgeData(smaller_edge_id); + BOOST_ASSERT_MSG(data.weight != std::numeric_limits::max(), + "edge weight invalid"); + + // If the edge is a shortcut, we need to add the two halfs to the stack. + if (data.shortcut) + { // unpack + const NodeID middle_node_id = data.turn_id; + // Note the order here - we're adding these to a stack, so we + // want the first->middle to get visited before middle->second + recursion_stack.emplace(middle_node_id, edge.second); + recursion_stack.emplace(edge.first, middle_node_id); + } + else + { + // We found an original edge, call our callback. + std::forward(callback)(edge, smaller_edge_id); + } + } +} + +template +void unpackPath(const FacadeT &facade, + RandomIter packed_path_begin, + RandomIter packed_path_end, + const PhantomNodes &phantom_nodes, + std::vector &unpacked_path) +{ + const auto nodes_number = std::distance(packed_path_begin, packed_path_end); + BOOST_ASSERT(nodes_number > 0); + + std::vector unpacked_edges; + + auto source_node = *packed_path_begin, target_node = *packed_path_begin; + if (nodes_number > 1) + { + target_node = *std::prev(packed_path_end); + unpacked_edges.reserve(std::distance(packed_path_begin, packed_path_end)); + unpackPath( + facade, + packed_path_begin, + packed_path_end, + [&facade, &unpacked_edges](std::pair & /* edge */, + const auto &edge_id) { unpacked_edges.push_back(edge_id); }); + } + + annotatePath(facade, source_node, target_node, unpacked_edges, phantom_nodes, unpacked_path); +} + +/** + * Unpacks a single edge (NodeID->NodeID) from the CH graph down to it's original non-shortcut + * route. + * @param from the node the CH edge starts at + * @param to the node the CH edge finishes at + * @param unpacked_path the sequence of original NodeIDs that make up the expanded CH edge + */ +void unpackEdge(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const NodeID from, + const NodeID to, + std::vector &unpacked_path); + +void retrievePackedPathFromHeap(const SearchEngineData::QueryHeap &forward_heap, + const SearchEngineData::QueryHeap &reverse_heap, + const NodeID middle_node_id, + std::vector &packed_path); + +void retrievePackedPathFromSingleHeap(const SearchEngineData::QueryHeap &search_heap, + const NodeID middle_node_id, + std::vector &packed_path); + +// assumes that heaps are already setup correctly. +// ATTENTION: This only works if no additional offset is supplied next to the Phantom Node +// Offsets. +// In case additional offsets are supplied, you might have to force a loop first. +// A forced loop might be necessary, if source and target are on the same segment. +// If this is the case and the offsets of the respective direction are larger for the source +// than the target +// then a force loop is required (e.g. source_phantom.forward_segment_id == +// target_phantom.forward_segment_id +// && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) +// requires +// a force loop, if the heaps have been initialized with positive offsets. +void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + std::int32_t &weight, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse, + const int duration_upper_bound = INVALID_EDGE_WEIGHT); + +// Alias to be compatible with the overload for CoreCH that needs 4 heaps +inline void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &, + SearchEngineData::QueryHeap &, + std::int32_t &weight, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse, + const int duration_upper_bound = INVALID_EDGE_WEIGHT) +{ + search(facade, + forward_heap, + reverse_heap, + weight, + packed_leg, + force_loop_forward, + force_loop_reverse, + duration_upper_bound); +} + +// assumes that heaps are already setup correctly. +// A forced loop might be necessary, if source and target are on the same segment. +// If this is the case and the offsets of the respective direction are larger for the source +// than the target +// then a force loop is required (e.g. source_phantom.forward_segment_id == +// target_phantom.forward_segment_id +// && source_phantom.GetForwardWeightPlusOffset() > target_phantom.GetForwardWeightPlusOffset()) +// requires +// a force loop, if the heaps have been initialized with positive offsets. +void search(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &forward_core_heap, + SearchEngineData::QueryHeap &reverse_core_heap, + int &weight, + std::vector &packed_leg, + const bool force_loop_forward, + const bool force_loop_reverse, + int duration_upper_bound = INVALID_EDGE_WEIGHT); + +bool needsLoopForward(const PhantomNode &source_phantom, const PhantomNode &target_phantom); + +bool needsLoopBackwards(const PhantomNode &source_phantom, const PhantomNode &target_phantom); + +double getPathDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const std::vector &packed_path, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom); + +// Requires the heaps for be empty +// If heaps should be adjusted to be initialized outside of this function, +// the addition of force_loop parameters might be required +double +getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &forward_core_heap, + SearchEngineData::QueryHeap &reverse_core_heap, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + int duration_upper_bound = INVALID_EDGE_WEIGHT); + +// Requires the heaps for be empty +// If heaps should be adjusted to be initialized outside of this function, +// the addition of force_loop parameters might be required +double +getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + int duration_upper_bound = INVALID_EDGE_WEIGHT); + +// Alias to be compatible with the overload for CoreCH that needs 4 heaps +inline double +getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::QueryHeap &forward_heap, + SearchEngineData::QueryHeap &reverse_heap, + SearchEngineData::QueryHeap &, + SearchEngineData::QueryHeap &, + const PhantomNode &source_phantom, + const PhantomNode &target_phantom, + int duration_upper_bound = INVALID_EDGE_WEIGHT) +{ + return getNetworkDistance( + facade, forward_heap, reverse_heap, source_phantom, target_phantom, duration_upper_bound); +} + +} // namespace ch +} // namespace routing_algorithms +} // namespace engine +} // namespace osrm + +#endif // OSRM_ENGINE_ROUTING_BASE_CH_HPP diff --git a/src/engine/routing_algorithms/alternative_path.cpp b/src/engine/routing_algorithms/alternative_path.cpp index a75e0772c4c..bd75721907e 100644 --- a/src/engine/routing_algorithms/alternative_path.cpp +++ b/src/engine/routing_algorithms/alternative_path.cpp @@ -1,5 +1,5 @@ #include "engine/routing_algorithms/alternative_path.hpp" -#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_ch.hpp" #include "util/integer_range.hpp" @@ -89,7 +89,7 @@ void alternativeRoutingStep( else { // check whether there is a loop present at the node - const auto loop_weight = getLoopWeight(facade, node); + const auto loop_weight = ch::getLoopWeight(facade, node); const EdgeWeight new_weight_with_loop = new_weight + loop_weight; if (loop_weight != INVALID_EDGE_WEIGHT && new_weight_with_loop <= *upper_bound_to_shortest_path_weight) @@ -139,11 +139,11 @@ void retrievePackedAlternatePath(const QueryHeap &forward_heap1, { // fetch packed path [s,v) std::vector packed_v_t_path; - retrievePackedPathFromHeap(forward_heap1, reverse_heap2, s_v_middle, packed_path); + ch::retrievePackedPathFromHeap(forward_heap1, reverse_heap2, s_v_middle, packed_path); packed_path.pop_back(); // remove middle node. It's in both half-paths // fetch patched path [v,t] - retrievePackedPathFromHeap(forward_heap2, reverse_heap1, v_t_middle, packed_v_t_path); + ch::retrievePackedPathFromHeap(forward_heap2, reverse_heap1, v_t_middle, packed_v_t_path); packed_path.insert(packed_path.end(), packed_v_t_path.begin(), packed_v_t_path.end()); } @@ -180,14 +180,14 @@ void computeLengthAndSharingOfViaPath( // compute path by reusing forward search from s while (!new_reverse_heap.Empty()) { - routingStep(facade, - new_reverse_heap, - existing_forward_heap, - s_v_middle, - upper_bound_s_v_path_length, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + new_reverse_heap, + existing_forward_heap, + s_v_middle, + upper_bound_s_v_path_length, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } // compute path by reusing backward search from node t NodeID v_t_middle = SPECIAL_NODEID; @@ -195,14 +195,14 @@ void computeLengthAndSharingOfViaPath( new_forward_heap.Insert(via_node, 0, via_node); while (!new_forward_heap.Empty()) { - routingStep(facade, - new_forward_heap, - existing_reverse_heap, - v_t_middle, - upper_bound_of_v_t_path_length, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + new_forward_heap, + existing_reverse_heap, + v_t_middle, + upper_bound_of_v_t_path_length, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } *real_length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length; @@ -212,9 +212,9 @@ void computeLengthAndSharingOfViaPath( } // retrieve packed paths - retrievePackedPathFromHeap( + ch::retrievePackedPathFromHeap( existing_forward_heap, new_reverse_heap, s_v_middle, packed_s_v_path); - retrievePackedPathFromHeap( + ch::retrievePackedPathFromHeap( new_forward_heap, existing_reverse_heap, v_t_middle, packed_v_t_path); // partial unpacking, compute sharing @@ -234,14 +234,14 @@ void computeLengthAndSharingOfViaPath( { if (packed_s_v_path[current_node] == packed_shortest_path[current_node]) { - unpackEdge(facade, - packed_s_v_path[current_node], - packed_s_v_path[current_node + 1], - partially_unpacked_via_path); - unpackEdge(facade, - packed_shortest_path[current_node], - packed_shortest_path[current_node + 1], - partially_unpacked_shortest_path); + ch::unpackEdge(facade, + packed_s_v_path[current_node], + packed_s_v_path[current_node + 1], + partially_unpacked_via_path); + ch::unpackEdge(facade, + packed_shortest_path[current_node], + packed_shortest_path[current_node + 1], + partially_unpacked_shortest_path); break; } } @@ -280,14 +280,14 @@ void computeLengthAndSharingOfViaPath( { if (packed_v_t_path[via_path_index] == packed_shortest_path[shortest_path_index]) { - unpackEdge(facade, - packed_v_t_path[via_path_index - 1], - packed_v_t_path[via_path_index], - partially_unpacked_via_path); - unpackEdge(facade, - packed_shortest_path[shortest_path_index - 1], - packed_shortest_path[shortest_path_index], - partially_unpacked_shortest_path); + ch::unpackEdge(facade, + packed_v_t_path[via_path_index - 1], + packed_v_t_path[via_path_index], + partially_unpacked_via_path); + ch::unpackEdge(facade, + packed_shortest_path[shortest_path_index - 1], + packed_shortest_path[shortest_path_index], + partially_unpacked_shortest_path); break; } } @@ -342,14 +342,14 @@ bool viaNodeCandidatePassesTTest( new_reverse_heap.Insert(candidate.node, 0, candidate.node); while (new_reverse_heap.Size() > 0) { - routingStep(facade, - new_reverse_heap, - existing_forward_heap, - *s_v_middle, - upper_bound_s_v_path_length, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + new_reverse_heap, + existing_forward_heap, + *s_v_middle, + upper_bound_s_v_path_length, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_s_v_path_length) @@ -363,14 +363,14 @@ bool viaNodeCandidatePassesTTest( new_forward_heap.Insert(candidate.node, 0, candidate.node); while (new_forward_heap.Size() > 0) { - routingStep(facade, - new_forward_heap, - existing_reverse_heap, - *v_t_middle, - upper_bound_of_v_t_path_length, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + new_forward_heap, + existing_reverse_heap, + *v_t_middle, + upper_bound_of_v_t_path_length, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } if (INVALID_EDGE_WEIGHT == upper_bound_of_v_t_path_length) @@ -381,10 +381,10 @@ bool viaNodeCandidatePassesTTest( *length_of_via_path = upper_bound_s_v_path_length + upper_bound_of_v_t_path_length; // retrieve packed paths - retrievePackedPathFromHeap( + ch::retrievePackedPathFromHeap( existing_forward_heap, new_reverse_heap, *s_v_middle, packed_s_v_path); - retrievePackedPathFromHeap( + ch::retrievePackedPathFromHeap( new_forward_heap, existing_reverse_heap, *v_t_middle, packed_v_t_path); NodeID s_P = *s_v_middle, t_P = *v_t_middle; @@ -536,25 +536,25 @@ bool viaNodeCandidatePassesTTest( { if (!forward_heap3.Empty()) { - routingStep(facade, - forward_heap3, - reverse_heap3, - middle, - upper_bound, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + forward_heap3, + reverse_heap3, + middle, + upper_bound, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } if (!reverse_heap3.Empty()) { - routingStep(facade, - reverse_heap3, - forward_heap3, - middle, - upper_bound, - min_edge_offset, - DO_NOT_FORCE_LOOPS, - DO_NOT_FORCE_LOOPS); + ch::routingStep(facade, + reverse_heap3, + forward_heap3, + middle, + upper_bound, + min_edge_offset, + DO_NOT_FORCE_LOOPS, + DO_NOT_FORCE_LOOPS); } } return (upper_bound <= t_test_path_length); @@ -593,35 +593,7 @@ alternativePathSearch(SearchEngineData &engine_working_data, ? -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset() : 0); - if (phantom_node_pair.source_phantom.forward_segment_id.enabled) - { - BOOST_ASSERT(phantom_node_pair.source_phantom.forward_segment_id.id != SPECIAL_SEGMENTID); - forward_heap1.Insert(phantom_node_pair.source_phantom.forward_segment_id.id, - -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.source_phantom.forward_segment_id.id); - } - if (phantom_node_pair.source_phantom.reverse_segment_id.enabled) - { - BOOST_ASSERT(phantom_node_pair.source_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID); - forward_heap1.Insert(phantom_node_pair.source_phantom.reverse_segment_id.id, - -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(), - phantom_node_pair.source_phantom.reverse_segment_id.id); - } - - if (phantom_node_pair.target_phantom.forward_segment_id.enabled) - { - BOOST_ASSERT(phantom_node_pair.target_phantom.forward_segment_id.id != SPECIAL_SEGMENTID); - reverse_heap1.Insert(phantom_node_pair.target_phantom.forward_segment_id.id, - phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(), - phantom_node_pair.target_phantom.forward_segment_id.id); - } - if (phantom_node_pair.target_phantom.reverse_segment_id.enabled) - { - BOOST_ASSERT(phantom_node_pair.target_phantom.reverse_segment_id.id != SPECIAL_SEGMENTID); - reverse_heap1.Insert(phantom_node_pair.target_phantom.reverse_segment_id.id, - phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(), - phantom_node_pair.target_phantom.reverse_segment_id.id); - } + insertNodesInHeaps(forward_heap1, reverse_heap1, phantom_node_pair); // search from s and t till new_min/(1+epsilon) > length_of_shortest_path while (0 < (forward_heap1.Size() + reverse_heap1.Size())) @@ -674,8 +646,8 @@ alternativePathSearch(SearchEngineData &engine_working_data, else { - retrievePackedPathFromSingleHeap(forward_heap1, middle_node, packed_forward_path); - retrievePackedPathFromSingleHeap(reverse_heap1, middle_node, packed_reverse_path); + ch::retrievePackedPathFromSingleHeap(forward_heap1, middle_node, packed_forward_path); + ch::retrievePackedPathFromSingleHeap(reverse_heap1, middle_node, packed_reverse_path); } // this set is is used as an indicator if a node is on the shortest path @@ -827,14 +799,14 @@ alternativePathSearch(SearchEngineData &engine_working_data, raw_route_data.target_traversed_in_reverse.push_back(( packed_shortest_path.back() != phantom_node_pair.target_phantom.forward_segment_id.id)); - unpackPath(facade, - // -- packed input - packed_shortest_path.begin(), - packed_shortest_path.end(), - // -- start of route - phantom_node_pair, - // -- unpacked output - raw_route_data.unpacked_path_segments.front()); + ch::unpackPath(facade, + // -- packed input + packed_shortest_path.begin(), + packed_shortest_path.end(), + // -- start of route + phantom_node_pair, + // -- unpacked output + raw_route_data.unpacked_path_segments.front()); raw_route_data.shortest_path_length = upper_bound_to_shortest_path_weight; } @@ -858,11 +830,11 @@ alternativePathSearch(SearchEngineData &engine_working_data, phantom_node_pair.target_phantom.forward_segment_id.id)); // unpack the alternate path - unpackPath(facade, - packed_alternate_path.begin(), - packed_alternate_path.end(), - phantom_node_pair, - raw_route_data.unpacked_alternative); + ch::unpackPath(facade, + packed_alternate_path.begin(), + packed_alternate_path.end(), + phantom_node_pair, + raw_route_data.unpacked_alternative); raw_route_data.alternative_path_length = length_of_via_path; } diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 93465b8b9c9..efcbd593117 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -1,6 +1,7 @@ #include "engine/routing_algorithms/direct_shortest_path.hpp" #include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_ch.hpp" namespace osrm { @@ -9,44 +10,8 @@ namespace engine namespace routing_algorithms { -namespace +namespace ch { -void insertInHeaps(SearchEngineData::QueryHeap &forward_heap, - SearchEngineData::QueryHeap &reverse_heap, - const PhantomNodes &nodes) -{ - const auto &source_phantom = nodes.source_phantom; - const auto &target_phantom = nodes.target_phantom; - BOOST_ASSERT(source_phantom.IsValid()); - BOOST_ASSERT(target_phantom.IsValid()); - - if (source_phantom.forward_segment_id.enabled) - { - forward_heap.Insert(source_phantom.forward_segment_id.id, - -source_phantom.GetForwardWeightPlusOffset(), - source_phantom.forward_segment_id.id); - } - if (source_phantom.reverse_segment_id.enabled) - { - forward_heap.Insert(source_phantom.reverse_segment_id.id, - -source_phantom.GetReverseWeightPlusOffset(), - source_phantom.reverse_segment_id.id); - } - - if (target_phantom.forward_segment_id.enabled) - { - reverse_heap.Insert(target_phantom.forward_segment_id.id, - target_phantom.GetForwardWeightPlusOffset(), - target_phantom.forward_segment_id.id); - } - - if (target_phantom.reverse_segment_id.enabled) - { - reverse_heap.Insert(target_phantom.reverse_segment_id.id, - target_phantom.GetReverseWeightPlusOffset(), - target_phantom.reverse_segment_id.id); - } -} template InternalRouteResult @@ -82,7 +47,6 @@ extractRoute(const datafacade::ContiguousInternalMemoryDataFacade &f return raw_route_data; } -} /// This is a striped down version of the general shortest path algorithm. /// The general algorithm always computes two queries for each leg. This is only @@ -109,7 +73,7 @@ InternalRouteResult directShortestPathSearchImpl( int weight = INVALID_EDGE_WEIGHT; std::vector packed_leg; - insertInHeaps(forward_heap, reverse_heap, phantom_nodes); + insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); search(facade, forward_heap, @@ -124,12 +88,14 @@ InternalRouteResult directShortestPathSearchImpl( return extractRoute(facade, weight, packed_leg, phantom_nodes); } +} // namespace ch + InternalRouteResult directShortestPathSearch( SearchEngineData &engine_working_data, const datafacade::ContiguousInternalMemoryDataFacade &facade, const PhantomNodes &phantom_nodes) { - return directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); + return ch::directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); } InternalRouteResult directShortestPathSearch( @@ -137,7 +103,7 @@ InternalRouteResult directShortestPathSearch( const datafacade::ContiguousInternalMemoryDataFacade &facade, const PhantomNodes &phantom_nodes) { - return directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); + return ch::directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); } } // namespace routing_algorithms diff --git a/src/engine/routing_algorithms/many_to_many.cpp b/src/engine/routing_algorithms/many_to_many.cpp index 6eef3b0d1db..64e30e9cbbc 100644 --- a/src/engine/routing_algorithms/many_to_many.cpp +++ b/src/engine/routing_algorithms/many_to_many.cpp @@ -1,5 +1,5 @@ #include "engine/routing_algorithms/many_to_many.hpp" -#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_ch.hpp" #include @@ -101,14 +101,14 @@ void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade(facade, node); + const EdgeWeight loop_weight = ch::getLoopWeight(facade, node); const EdgeWeight new_weight_with_loop = new_weight + loop_weight; if (loop_weight != INVALID_EDGE_WEIGHT && new_weight_with_loop >= 0) { current_weight = std::min(current_weight, new_weight_with_loop); current_duration = std::min(current_duration, source_duration + target_duration + - getLoopWeight(facade, node)); + ch::getLoopWeight(facade, node)); } } else if (new_weight < current_weight) @@ -118,7 +118,7 @@ void forwardRoutingStep(const datafacade::ContiguousInternalMemoryDataFacade(facade, node, source_weight, query_heap)) + if (ch::stallAtNode(facade, node, source_weight, query_heap)) { return; } @@ -139,7 +139,7 @@ void backwardRoutingStep( // store settled nodes in search space bucket search_space_with_buckets[node].emplace_back(column_idx, target_weight, target_duration); - if (stallAtNode(facade, node, target_weight, query_heap)) + if (ch::stallAtNode(facade, node, target_weight, query_heap)) { return; } @@ -172,21 +172,9 @@ manyToManySearch(SearchEngineData &engine_working_data, unsigned column_idx = 0; const auto search_target_phantom = [&](const PhantomNode &phantom) { + // clear heap and insert target nodes query_heap.Clear(); - // insert target(s) at weight 0 - - if (phantom.forward_segment_id.enabled) - { - query_heap.Insert(phantom.forward_segment_id.id, - phantom.GetForwardWeightPlusOffset(), - {phantom.forward_segment_id.id, phantom.GetForwardDuration()}); - } - if (phantom.reverse_segment_id.enabled) - { - query_heap.Insert(phantom.reverse_segment_id.id, - phantom.GetReverseWeightPlusOffset(), - {phantom.reverse_segment_id.id, phantom.GetReverseDuration()}); - } + insertNodesInHeap(query_heap, phantom); // explore search space while (!query_heap.Empty()) @@ -199,21 +187,9 @@ manyToManySearch(SearchEngineData &engine_working_data, // 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(); - // insert target(s) at weight 0 - - if (phantom.forward_segment_id.enabled) - { - query_heap.Insert(phantom.forward_segment_id.id, - -phantom.GetForwardWeightPlusOffset(), - {phantom.forward_segment_id.id, -phantom.GetForwardDuration()}); - } - if (phantom.reverse_segment_id.enabled) - { - query_heap.Insert(phantom.reverse_segment_id.id, - -phantom.GetReverseWeightPlusOffset(), - {phantom.reverse_segment_id.id, -phantom.GetReverseDuration()}); - } + insertNodesInHeap(query_heap, phantom); // explore search space while (!query_heap.Empty()) diff --git a/src/engine/routing_algorithms/map_matching.cpp b/src/engine/routing_algorithms/map_matching.cpp index 3975322ccd5..e5cbb54690c 100644 --- a/src/engine/routing_algorithms/map_matching.cpp +++ b/src/engine/routing_algorithms/map_matching.cpp @@ -1,5 +1,5 @@ #include "engine/routing_algorithms/map_matching.hpp" -#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_ch.hpp" #include "engine/map_matching/hidden_markov_model.hpp" #include "engine/map_matching/matching_confidence.hpp" @@ -210,14 +210,14 @@ mapMatchingImpl(SearchEngineData &engine_working_data, } double network_distance = - getNetworkDistance(facade, - forward_heap, - reverse_heap, - forward_core_heap, - reverse_core_heap, - prev_unbroken_timestamps_list[s].phantom_node, - current_timestamps_list[s_prime].phantom_node, - duration_upper_bound); + ch::getNetworkDistance(facade, + forward_heap, + reverse_heap, + forward_core_heap, + reverse_core_heap, + prev_unbroken_timestamps_list[s].phantom_node, + current_timestamps_list[s_prime].phantom_node, + duration_upper_bound); // get distance diff between loc1/2 and locs/s_prime const auto d_t = std::abs(network_distance - haversine_distance); diff --git a/src/engine/routing_algorithms/routing_base.cpp b/src/engine/routing_algorithms/routing_base_ch.cpp similarity index 94% rename from src/engine/routing_algorithms/routing_base.cpp rename to src/engine/routing_algorithms/routing_base_ch.cpp index 2d56e74b9d1..484e3b8e353 100644 --- a/src/engine/routing_algorithms/routing_base.cpp +++ b/src/engine/routing_algorithms/routing_base_ch.cpp @@ -1,4 +1,4 @@ -#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/routing_algorithms/routing_base_ch.hpp" namespace osrm { @@ -6,6 +6,8 @@ namespace engine { namespace routing_algorithms { +namespace ch +{ /** * Unpacks a single edge (NodeID->NodeID) from the CH graph down to it's original non-shortcut @@ -411,31 +413,7 @@ getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade packed_path; @@ -517,6 +495,7 @@ getNetworkDistance(const datafacade::ContiguousInternalMemoryDataFacade #include @@ -71,23 +71,24 @@ void searchWithUTurn(const datafacade::ContiguousInternalMemoryDataFacade &fa reverse_core_heap.Clear(); BOOST_ASSERT(forward_core_heap.Size() == 0); BOOST_ASSERT(reverse_core_heap.Size() == 0); - routing_algorithms::search(facade, - forward_heap, - reverse_heap, - forward_core_heap, - reverse_core_heap, - new_total_weight_to_forward, - leg_packed_path_forward, - needsLoopForward(source_phantom, target_phantom), - DO_NOT_FORCE_LOOP); + ch::search(facade, + forward_heap, + reverse_heap, + forward_core_heap, + reverse_core_heap, + new_total_weight_to_forward, + leg_packed_path_forward, + ch::needsLoopForward(source_phantom, target_phantom), + DO_NOT_FORCE_LOOP); } if (search_to_reverse_node) @@ -185,15 +186,15 @@ void search(const datafacade::ContiguousInternalMemoryDataFacade &fa reverse_core_heap.Clear(); BOOST_ASSERT(forward_core_heap.Size() == 0); BOOST_ASSERT(reverse_core_heap.Size() == 0); - routing_algorithms::search(facade, - forward_heap, - reverse_heap, - forward_core_heap, - reverse_core_heap, - new_total_weight_to_reverse, - leg_packed_path_reverse, - DO_NOT_FORCE_LOOP, - needsLoopBackwards(source_phantom, target_phantom)); + ch::search(facade, + forward_heap, + reverse_heap, + forward_core_heap, + reverse_core_heap, + new_total_weight_to_reverse, + leg_packed_path_reverse, + DO_NOT_FORCE_LOOP, + ch::needsLoopBackwards(source_phantom, target_phantom)); } } @@ -213,11 +214,11 @@ void unpackLegs(const datafacade::ContiguousInternalMemoryDataFacade Date: Mon, 6 Mar 2017 20:16:24 +0100 Subject: [PATCH 04/16] MLD direct shortest path plugin --- include/engine/algorithm.hpp | 2 +- include/engine/routing_algorithms.hpp | 7 - .../direct_shortest_path.hpp | 14 +- .../routing_algorithms/routing_base_ch.hpp | 17 -- .../routing_algorithms/routing_base_mld.hpp | 279 ++++++++++++++++++ include/engine/search_engine_data.hpp | 28 ++ .../direct_shortest_path.cpp | 93 +++++- src/engine/search_engine_data.cpp | 25 ++ 8 files changed, 416 insertions(+), 49 deletions(-) create mode 100644 include/engine/routing_algorithms/routing_base_mld.hpp diff --git a/include/engine/algorithm.hpp b/include/engine/algorithm.hpp index d6a8dd337e0..d106d9b2133 100644 --- a/include/engine/algorithm.hpp +++ b/include/engine/algorithm.hpp @@ -100,7 +100,7 @@ template <> struct HasManyToManySearch final : std::false_type template <> struct HasShortestPathSearch final : std::false_type { }; -template <> struct HasDirectShortestPathSearch final : std::false_type +template <> struct HasDirectShortestPathSearch final : std::true_type { }; template <> struct HasMapMatching final : std::false_type diff --git a/include/engine/routing_algorithms.hpp b/include/engine/routing_algorithms.hpp index 5b9fd553ed4..eefe068333f 100644 --- a/include/engine/routing_algorithms.hpp +++ b/include/engine/routing_algorithms.hpp @@ -193,13 +193,6 @@ RoutingAlgorithms::ShortestPathSearch(const std::vector -InternalRouteResult inline RoutingAlgorithms::DirectShortestPathSearch( - const PhantomNodes &) const -{ - throw util::exception("DirectShortestPathSearch is not implemented"); -} - template <> inline std::vector RoutingAlgorithms::ManyToManySearch(const std::vector &, diff --git a/include/engine/routing_algorithms/direct_shortest_path.hpp b/include/engine/routing_algorithms/direct_shortest_path.hpp index 5b1be5df72b..3a9472314c9 100644 --- a/include/engine/routing_algorithms/direct_shortest_path.hpp +++ b/include/engine/routing_algorithms/direct_shortest_path.hpp @@ -21,15 +21,11 @@ namespace routing_algorithms /// by the previous route. /// This variation is only an optimazation for graphs with slow queries, for example /// not fully contracted graphs. -InternalRouteResult directShortestPathSearch( - SearchEngineData &engine_working_data, - const datafacade::ContiguousInternalMemoryDataFacade &facade, - const PhantomNodes &phantom_nodes); - -InternalRouteResult directShortestPathSearch( - SearchEngineData &engine_working_data, - const datafacade::ContiguousInternalMemoryDataFacade &facade, - const PhantomNodes &phantom_nodes); +template +InternalRouteResult +directShortestPathSearch(SearchEngineData &engine_working_data, + const datafacade::ContiguousInternalMemoryDataFacade &facade, + const PhantomNodes &phantom_nodes); } // namespace routing_algorithms } // namespace engine diff --git a/include/engine/routing_algorithms/routing_base_ch.hpp b/include/engine/routing_algorithms/routing_base_ch.hpp index 994e5e5c901..e6bf5682c5d 100644 --- a/include/engine/routing_algorithms/routing_base_ch.hpp +++ b/include/engine/routing_algorithms/routing_base_ch.hpp @@ -1,32 +1,15 @@ #ifndef OSRM_ENGINE_ROUTING_BASE_CH_HPP #define OSRM_ENGINE_ROUTING_BASE_CH_HPP -#include "extractor/guidance/turn_instruction.hpp" - #include "engine/algorithm.hpp" #include "engine/datafacade/contiguous_internalmem_datafacade.hpp" -#include "engine/internal_route_result.hpp" #include "engine/routing_algorithms/routing_base.hpp" #include "engine/search_engine_data.hpp" -#include "util/coordinate_calculation.hpp" -#include "util/guidance/turn_bearing.hpp" #include "util/typedefs.hpp" #include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - namespace osrm { namespace engine diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp new file mode 100644 index 00000000000..47788f5b3de --- /dev/null +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -0,0 +1,279 @@ +#ifndef OSRM_ENGINE_ROUTING_BASE_MLD_HPP +#define OSRM_ENGINE_ROUTING_BASE_MLD_HPP + +#include "engine/algorithm.hpp" +#include "engine/datafacade/contiguous_internalmem_datafacade.hpp" +#include "engine/routing_algorithms/routing_base.hpp" +#include "engine/search_engine_data.hpp" + +#include "util/typedefs.hpp" + +#include + +namespace osrm +{ +namespace engine +{ +namespace routing_algorithms +{ +namespace mld +{ + +template +void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const partition::MultiLevelPartitionView &partition, + const partition::CellStorageView &cells, + SearchEngineData::MultiLayerDijkstraHeap &forward_heap, + SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, + const std::pair &parent_cell, + NodeID &middle_node, + EdgeWeight &path_upper_bound, + EdgeWeight &forward_upper_bound, + EdgeWeight &reverse_upper_bound) +{ + const auto node = forward_heap.DeleteMin(); + const auto weight = forward_heap.GetKey(node); + + auto update_upper_bounds = [&](NodeID to, EdgeWeight forward_weight, EdgeWeight edge_weight) { + if (reverse_heap.WasInserted(to) && reverse_heap.WasRemoved(to)) + { + auto reverse_weight = reverse_heap.GetKey(to); + auto path_weight = forward_weight + edge_weight + reverse_weight; + BOOST_ASSERT(path_weight >= 0); + if (path_weight < path_upper_bound) + { + middle_node = to; + path_upper_bound = path_weight; + forward_upper_bound = forward_weight + edge_weight; + reverse_upper_bound = reverse_weight + edge_weight; + } + } + }; + + const auto &node_data = forward_heap.GetData(node); + const auto level = node_data.level; + const auto check_overlay_edges = + (level >= 1) && // only if at least the first level and + (node_data.parent == node || // is the first point of the path + node_data.edge_id != SPECIAL_EDGEID); // or an overlay entreé point + + // Edge case: single node path + update_upper_bounds(node, weight, 0); + + if (check_overlay_edges) + { + if (DIRECTION == FORWARD_DIRECTION) + { + // Shortcuts in forward direction + const auto &cell = cells.GetCell(level, partition.GetCell(level, node)); + auto destination = cell.GetDestinationNodes().begin(); + for (auto shortcut_weight : cell.GetOutWeight(node)) + { + BOOST_ASSERT(destination != cell.GetDestinationNodes().end()); + const NodeID to = *destination; + if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to) + { + const EdgeWeight to_weight = weight + shortcut_weight; + if (!forward_heap.WasInserted(to)) + { + forward_heap.Insert(to, to_weight, {node, level}); + update_upper_bounds(to, weight, shortcut_weight); + } + else if (to_weight < forward_heap.GetKey(to)) + { + forward_heap.GetData(to) = {node, level}; + forward_heap.DecreaseKey(to, to_weight); + update_upper_bounds(to, weight, shortcut_weight); + } + } + ++destination; + } + } + else + { + // Shortcuts in backward direction + const auto &cell = cells.GetCell(level, partition.GetCell(level, node)); + auto source = cell.GetSourceNodes().begin(); + for (auto shortcut_weight : cell.GetInWeight(node)) + { + BOOST_ASSERT(source != cell.GetSourceNodes().end()); + const NodeID to = *source; + if (shortcut_weight != INVALID_EDGE_WEIGHT && node != to) + { + const EdgeWeight to_weight = weight + shortcut_weight; + if (!forward_heap.WasInserted(to)) + { + forward_heap.Insert(to, to_weight, {node, level}); + update_upper_bounds(to, weight, shortcut_weight); + } + else if (to_weight < forward_heap.GetKey(to)) + { + forward_heap.GetData(to) = {node, level}; + forward_heap.DecreaseKey(to, to_weight); + update_upper_bounds(to, weight, shortcut_weight); + } + } + ++source; + } + } + } + + // Boundary edges + for (const auto edge : facade.GetAdjacentEdgeRange(node)) + { + const auto &edge_data = facade.GetEdgeData(edge); + if (DIRECTION == FORWARD_DIRECTION ? edge_data.forward : edge_data.backward) + { + const NodeID to = facade.GetTarget(edge); + const auto to_level = + std::min(parent_cell.first, partition.GetHighestDifferentLevel(node, to)); + + if ( // Routing is unrestricted or restricted to the highest level cell + (parent_cell.second == INVALID_CELL_ID || + parent_cell.second == partition.GetCell(parent_cell.first + 1, to)) && + // "Never-go-down" at border edges + to_level >= level) + { + BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid"); + const EdgeWeight to_weight = weight + edge_data.weight; + + if (!forward_heap.WasInserted(to)) + { + forward_heap.Insert(to, to_weight, {node, to_level, edge}); + update_upper_bounds(to, weight, edge_data.weight); + } + else if (to_weight < forward_heap.GetKey(to)) + { + forward_heap.GetData(to) = {node, to_level, edge}; + forward_heap.DecreaseKey(to, to_weight); + update_upper_bounds(to, weight, edge_data.weight); + } + } + } + } +} + +auto search(const datafacade::ContiguousInternalMemoryDataFacade &facade, + const partition::MultiLevelPartitionView &partition, + const partition::CellStorageView &cells, + SearchEngineData::MultiLayerDijkstraHeap &forward_heap, + SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, + const std::pair &parent_cell) +{ + // run two-Target Dijkstra routing step. + NodeID middle = SPECIAL_NODEID; + EdgeWeight weight = INVALID_EDGE_WEIGHT; + EdgeWeight forward_search_radius = INVALID_EDGE_WEIGHT; + EdgeWeight reverse_search_radius = INVALID_EDGE_WEIGHT; + bool progress; + do + { + progress = false; + if (!forward_heap.Empty() && (forward_heap.MinKey() < forward_search_radius)) + { + progress = true; + routingStep(facade, + partition, + cells, + forward_heap, + reverse_heap, + parent_cell, + middle, + weight, + forward_search_radius, + reverse_search_radius); + } + if (!reverse_heap.Empty() && (reverse_heap.MinKey() < reverse_search_radius)) + { + progress = true; + routingStep(facade, + partition, + cells, + reverse_heap, + forward_heap, + parent_cell, + middle, + weight, + reverse_search_radius, + forward_search_radius); + } + } while (progress); + + // No path found for both target nodes? + if (weight == INVALID_EDGE_WEIGHT || SPECIAL_NODEID == middle) + { + return std::make_tuple( + INVALID_EDGE_WEIGHT, SPECIAL_NODEID, SPECIAL_NODEID, std::vector()); + } + + // Get packed path as edges {level, from node ID, to node ID, edge ID} + std::vector> packed_path; + NodeID current_node = middle, parent_node = forward_heap.GetData(middle).parent; + while (parent_node != current_node) + { + const auto &data = forward_heap.GetData(current_node); + packed_path.push_back(std::make_tuple(data.level, parent_node, current_node, data.edge_id)); + current_node = parent_node; + parent_node = forward_heap.GetData(parent_node).parent; + } + std::reverse(std::begin(packed_path), std::end(packed_path)); + const NodeID source_node = current_node; + + current_node = middle, parent_node = reverse_heap.GetData(middle).parent; + while (parent_node != current_node) + { + const auto &data = reverse_heap.GetData(current_node); + packed_path.push_back(std::make_tuple(data.level, current_node, parent_node, data.edge_id)); + current_node = parent_node; + parent_node = reverse_heap.GetData(parent_node).parent; + } + const NodeID target_node = current_node; + + // Unpack path + std::vector unpacked_path; + unpacked_path.reserve(packed_path.size()); + for (auto &packed_edge : packed_path) + { + LevelID level; + NodeID source, target; + EdgeID edge_id; + std::tie(level, source, target, edge_id) = packed_edge; + if (edge_id != SPECIAL_EDGEID) + { // a base graph edge + unpacked_path.push_back(edge_id); + } + else + { // an overlay graph edge + LevelID sublevel = level - 1; + CellID parent_cell_id = partition.GetCell(level, source); + BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); + + // Here heaps can be reused, let's go deeper! + forward_heap.Clear(); + reverse_heap.Clear(); + forward_heap.Insert(source, 0, {source, sublevel}); + reverse_heap.Insert(target, 0, {target, sublevel}); + + // TODO: when structured bindings will be allowed change to + // auto [subpath_weight, subpath_source, subpath_target, subpath] = ... + EdgeWeight subpath_weight; + NodeID subpath_source, subpath_target; + std::vector subpath; + std::tie(subpath_weight, subpath_source, subpath_target, subpath) = search( + facade, partition, cells, forward_heap, reverse_heap, {sublevel, parent_cell_id}); + BOOST_ASSERT(!subpath.empty()); + BOOST_ASSERT(subpath_source == source); + BOOST_ASSERT(subpath_target == target); + unpacked_path.insert(unpacked_path.end(), subpath.begin(), subpath.end()); + } + } + + return std::make_tuple(weight, source_node, target_node, std::move(unpacked_path)); +} + +} // namespace mld +} // namespace routing_algorithms +} // namespace engine +} // namespace osrm + +#endif // OSRM_ENGINE_ROUTING_BASE_MLD_HPP diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index ba57487b3cc..3ab5245a7fe 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -3,6 +3,7 @@ #include +#include "partition/multi_level_partition.hpp" #include "util/binary_heap.hpp" #include "util/typedefs.hpp" @@ -23,6 +24,21 @@ struct ManyToManyHeapData : HeapData ManyToManyHeapData(NodeID p, EdgeWeight duration) : HeapData(p), duration(duration) {} }; +struct MultiLayerDijkstraHeapData : HeapData +{ + LevelID level; // node level: always increasing along the path starting from 0 + EdgeID edge_id; // edge id if parent -> node is a boundary edge + MultiLayerDijkstraHeapData(NodeID p) : HeapData(p), level(0), edge_id(SPECIAL_EDGEID) {} + MultiLayerDijkstraHeapData(NodeID p, LevelID level) + : HeapData(p), level(level), edge_id(SPECIAL_EDGEID) + { + } + MultiLayerDijkstraHeapData(NodeID p, LevelID level, EdgeID edge_id) + : HeapData(p), level(level), edge_id(edge_id) + { + } +}; + struct SearchEngineData { using QueryHeap = util:: @@ -37,6 +53,14 @@ struct SearchEngineData using ManyToManyHeapPtr = boost::thread_specific_ptr; + using MultiLayerDijkstraHeap = util::BinaryHeap>; + + using MultiLayerDijkstraHeapPtr = boost::thread_specific_ptr; + static SearchEngineHeapPtr forward_heap_1; static SearchEngineHeapPtr reverse_heap_1; static SearchEngineHeapPtr forward_heap_2; @@ -44,6 +68,8 @@ struct SearchEngineData static SearchEngineHeapPtr forward_heap_3; static SearchEngineHeapPtr reverse_heap_3; static ManyToManyHeapPtr many_to_many_heap; + static MultiLayerDijkstraHeapPtr mld_forward_heap; + static MultiLayerDijkstraHeapPtr mld_reverse_heap; void InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes); @@ -52,6 +78,8 @@ struct SearchEngineData void InitializeOrClearThirdThreadLocalStorage(const unsigned number_of_nodes); void InitializeOrClearManyToManyThreadLocalStorage(const unsigned number_of_nodes); + + void InitializeOrClearMultiLayerDijkstraThreadLocalStorage(const unsigned number_of_nodes); }; } } diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index efcbd593117..887905469ef 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -2,6 +2,7 @@ #include "engine/routing_algorithms/routing_base.hpp" #include "engine/routing_algorithms/routing_base_ch.hpp" +#include "engine/routing_algorithms/routing_base_mld.hpp" namespace osrm { @@ -10,14 +11,13 @@ namespace engine namespace routing_algorithms { -namespace ch -{ - template InternalRouteResult extractRoute(const datafacade::ContiguousInternalMemoryDataFacade &facade, const EdgeWeight weight, - const std::vector &packed_leg, + const NodeID source_node, + const NodeID target_node, + const std::vector &edges, const PhantomNodes &nodes) { InternalRouteResult raw_route_data; @@ -30,24 +30,25 @@ extractRoute(const datafacade::ContiguousInternalMemoryDataFacade &f return raw_route_data; } - BOOST_ASSERT_MSG(!packed_leg.empty(), "packed path empty"); - raw_route_data.shortest_path_length = weight; raw_route_data.unpacked_path_segments.resize(1); raw_route_data.source_traversed_in_reverse.push_back( - (packed_leg.front() != nodes.source_phantom.forward_segment_id.id)); + (source_node != nodes.source_phantom.forward_segment_id.id)); raw_route_data.target_traversed_in_reverse.push_back( - (packed_leg.back() != nodes.target_phantom.forward_segment_id.id)); + (target_node != nodes.target_phantom.forward_segment_id.id)); - unpackPath(facade, - packed_leg.begin(), - packed_leg.end(), - nodes, - raw_route_data.unpacked_path_segments.front()); + annotatePath(facade, + source_node, + target_node, + edges, + nodes, + raw_route_data.unpacked_path_segments.front()); return raw_route_data; } +namespace ch +{ /// This is a striped down version of the general shortest path algorithm. /// The general algorithm always computes two queries for each leg. This is only /// necessary in case of vias, where the directions of the start node is constrainted @@ -71,7 +72,7 @@ InternalRouteResult directShortestPathSearchImpl( forward_core_heap.Clear(); reverse_core_heap.Clear(); - int weight = INVALID_EDGE_WEIGHT; + EdgeWeight weight = INVALID_EDGE_WEIGHT; std::vector packed_leg; insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); @@ -85,11 +86,27 @@ InternalRouteResult directShortestPathSearchImpl( DO_NOT_FORCE_LOOPS, DO_NOT_FORCE_LOOPS); - return extractRoute(facade, weight, packed_leg, phantom_nodes); + std::vector unpacked_edges; + auto source_node = SPECIAL_NODEID, target_node = SPECIAL_NODEID; + if (!packed_leg.empty()) + { + source_node = packed_leg.front(); + target_node = packed_leg.back(); + unpacked_edges.reserve(packed_leg.size()); + unpackPath( + facade, + packed_leg.begin(), + packed_leg.end(), + [&facade, &unpacked_edges](std::pair & /* edge */, + const auto &edge_id) { unpacked_edges.push_back(edge_id); }); + } + + return extractRoute(facade, weight, source_node, target_node, unpacked_edges, phantom_nodes); } } // namespace ch +template <> InternalRouteResult directShortestPathSearch( SearchEngineData &engine_working_data, const datafacade::ContiguousInternalMemoryDataFacade &facade, @@ -98,6 +115,7 @@ InternalRouteResult directShortestPathSearch( return ch::directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); } +template <> InternalRouteResult directShortestPathSearch( SearchEngineData &engine_working_data, const datafacade::ContiguousInternalMemoryDataFacade &facade, @@ -106,6 +124,51 @@ InternalRouteResult directShortestPathSearch( return ch::directShortestPathSearchImpl(engine_working_data, facade, phantom_nodes); } +template <> +InternalRouteResult directShortestPathSearch( + SearchEngineData &engine_working_data, + const datafacade::ContiguousInternalMemoryDataFacade &facade, + const PhantomNodes &phantom_nodes) +{ + engine_working_data.InitializeOrClearMultiLayerDijkstraThreadLocalStorage( + facade.GetNumberOfNodes()); + auto &forward_heap = *(engine_working_data.mld_forward_heap); + auto &reverse_heap = *(engine_working_data.mld_reverse_heap); + forward_heap.Clear(); + reverse_heap.Clear(); + insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); + + const auto &partition = facade.GetMultiLevelPartition(); + const auto &cells = facade.GetCellStorage(); + + auto get_highest_level = [&partition](const SegmentID &source, const SegmentID &target) { + if (source.enabled && target.enabled) + return partition.GetHighestDifferentLevel(source.id, target.id); + return INVALID_LEVEL_ID; + }; + + const auto &source_phantom = phantom_nodes.source_phantom; + const auto &target_phantom = phantom_nodes.target_phantom; + const auto highest_level = + std::min(std::min(get_highest_level(source_phantom.forward_segment_id, + target_phantom.forward_segment_id), + get_highest_level(source_phantom.forward_segment_id, + target_phantom.reverse_segment_id)), + std::min(get_highest_level(source_phantom.reverse_segment_id, + target_phantom.forward_segment_id), + get_highest_level(source_phantom.reverse_segment_id, + target_phantom.reverse_segment_id))); + // TODO: when structured bindings will be allowed change to + // auto [weight, source_node, target_node, unpacked_edges] = ... + EdgeWeight weight; + NodeID source_node, target_node; + std::vector unpacked_edges; + std::tie(weight, source_node, target_node, unpacked_edges) = mld::search( + facade, partition, cells, forward_heap, reverse_heap, {highest_level, INVALID_CELL_ID}); + + return extractRoute(facade, weight, source_node, target_node, unpacked_edges, phantom_nodes); +} + } // namespace routing_algorithms } // namespace engine } // namespace osrm diff --git a/src/engine/search_engine_data.cpp b/src/engine/search_engine_data.cpp index 644e5ba68ad..9e299fc997d 100644 --- a/src/engine/search_engine_data.cpp +++ b/src/engine/search_engine_data.cpp @@ -15,6 +15,9 @@ SearchEngineData::SearchEngineHeapPtr SearchEngineData::forward_heap_3; SearchEngineData::SearchEngineHeapPtr SearchEngineData::reverse_heap_3; SearchEngineData::ManyToManyHeapPtr SearchEngineData::many_to_many_heap; +SearchEngineData::MultiLayerDijkstraHeapPtr SearchEngineData::mld_forward_heap; +SearchEngineData::MultiLayerDijkstraHeapPtr SearchEngineData::mld_reverse_heap; + void SearchEngineData::InitializeOrClearFirstThreadLocalStorage(const unsigned number_of_nodes) { if (forward_heap_1.get()) @@ -89,5 +92,27 @@ void SearchEngineData::InitializeOrClearManyToManyThreadLocalStorage(const unsig many_to_many_heap.reset(new ManyToManyQueryHeap(number_of_nodes)); } } + +void SearchEngineData::InitializeOrClearMultiLayerDijkstraThreadLocalStorage( + const unsigned number_of_nodes) +{ + if (mld_forward_heap.get()) + { + mld_forward_heap->Clear(); + } + else + { + mld_forward_heap.reset(new MultiLayerDijkstraHeap(number_of_nodes)); + } + + if (mld_reverse_heap.get()) + { + mld_reverse_heap->Clear(); + } + else + { + mld_reverse_heap.reset(new MultiLayerDijkstraHeap(number_of_nodes)); + } +} } } From 81771a3bfd88f4e02fe3d1e4fb5ca27e10733588 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Sat, 11 Mar 2017 09:25:13 +0100 Subject: [PATCH 05/16] Fix cell storage for tiny maps --- .../datafacade/contiguous_internalmem_datafacade.hpp | 4 ---- include/partition/cell_storage.hpp | 10 ++++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index 91117101f08..dac9906ccb9 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -939,10 +939,6 @@ class ContiguousInternalMemoryAlgorithmDataFacade if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_WEIGHTS) > 0) { - BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_SOURCE_BOUNDARY) > - 0); - BOOST_ASSERT( - data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_DESTINATION_BOUNDARY) > 0); BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELLS) > 0); BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_LEVEL_OFFSETS) > 0); diff --git a/include/partition/cell_storage.hpp b/include/partition/cell_storage.hpp index 6c5fdde0536..99ccc02a726 100644 --- a/include/partition/cell_storage.hpp +++ b/include/partition/cell_storage.hpp @@ -172,8 +172,8 @@ template class CellStorageImpl destination_boundary{all_destinations + data.destination_boundary_offset} { BOOST_ASSERT(all_weight != nullptr); - BOOST_ASSERT(all_sources != nullptr); - BOOST_ASSERT(all_destinations != nullptr); + BOOST_ASSERT(num_source_nodes == 0 || all_sources != nullptr); + BOOST_ASSERT(num_destination_nodes == 0 || all_destinations != nullptr); } }; @@ -333,8 +333,10 @@ template class CellStorageImpl const auto offset = level_to_cell_offset[level_index]; const auto cell_index = offset + id; BOOST_ASSERT(cell_index < cells.size()); - return ConstCell{ - cells[cell_index], weights.data(), source_boundary.data(), destination_boundary.data()}; + return ConstCell{cells[cell_index], + weights.data(), + source_boundary.empty() ? nullptr : source_boundary.data(), + destination_boundary.empty() ? nullptr : destination_boundary.data()}; } template > Cell GetCell(LevelID level, CellID id) From fe5d1a6e74a01f5c4559aa471c3c52ac4298316d Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Sat, 11 Mar 2017 09:26:12 +0100 Subject: [PATCH 06/16] Add MLD testing to cucumber tests --- cucumber.js | 6 +-- features/car/summaries.feature | 2 +- features/options/contract/version.feature | 2 +- features/options/customize/files.feature | 23 +++++++++ features/options/customize/help.feature | 35 +++++++++++++ features/options/customize/invalid.feature | 20 ++++++++ features/options/customize/version.feature | 19 ++++++++ features/options/partition/files.feature | 22 +++++++++ features/options/partition/help.feature | 50 +++++++++++++++++++ features/options/partition/invalid.feature | 20 ++++++++ features/options/partition/version.feature | 19 ++++++++ features/step_definitions/data.js | 16 ++++-- features/step_definitions/options.js | 26 ++-------- features/support/data.js | 57 ++++++++++++++++++++-- features/support/env.js | 4 +- features/support/hooks.js | 2 + features/testbot/loop.feature | 3 +- features/testbot/weight.feature | 2 + package.json | 2 +- src/tools/customize.cpp | 2 +- src/tools/partition.cpp | 2 +- 21 files changed, 295 insertions(+), 39 deletions(-) create mode 100644 features/options/customize/files.feature create mode 100644 features/options/customize/help.feature create mode 100644 features/options/customize/invalid.feature create mode 100644 features/options/customize/version.feature create mode 100644 features/options/partition/files.feature create mode 100644 features/options/partition/help.feature create mode 100644 features/options/partition/invalid.feature create mode 100644 features/options/partition/version.feature diff --git a/cucumber.js b/cucumber.js index 0bf6f570f95..5390a9215aa 100644 --- a/cucumber.js +++ b/cucumber.js @@ -2,8 +2,6 @@ module.exports = { default: '--strict --tags ~@stress --tags ~@todo --require features/support --require features/step_definitions', verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions', todo: '--strict --tags @todo --require features/support --require features/step_definitions', - all: '--strict --require features/support --require features/step_definitions' + all: '--strict --require features/support --require features/step_definitions', + mld: '--strict --tags ~@stress --tags ~@todo --tags ~@match --tags ~@alternative --tags ~@traffic --tags ~@weight --tags ~@matrix --tags ~@trip --tags ~@via --require features/support --require features/step_definitions -f progress' } - - - diff --git a/features/car/summaries.feature b/features/car/summaries.feature index 5c14f7b0ed2..27cb406e787 100644 --- a/features/car/summaries.feature +++ b/features/car/summaries.feature @@ -5,7 +5,7 @@ Feature: Basic Routing Given the profile "car" Given a grid size of 500 meters - @smallest + @smallest @via Scenario: Summaries when routing on a simple network Given the node map """ diff --git a/features/options/contract/version.feature b/features/options/contract/version.feature index 88ec8b734e4..0c2360890c1 100644 --- a/features/options/contract/version.feature +++ b/features/options/contract/version.feature @@ -1,4 +1,4 @@ -@prepare @options @version +@contract @options @version Feature: osrm-contract command line options: version # the regex will match these two formats: # v0.3.7.0 # this is the normal format when you build from a git clone diff --git a/features/options/customize/files.feature b/features/options/customize/files.feature new file mode 100644 index 00000000000..4c74e26c562 --- /dev/null +++ b/features/options/customize/files.feature @@ -0,0 +1,23 @@ +@customize @options @files +Feature: osrm-contract command line options: files + Background: + Given the profile "testbot" + And the node map + """ + a b + """ + And the ways + | nodes | + | ab | + And the data has been extracted + And the data has been partitioned + + Scenario: osrm-customize - Passing base file + When I run "osrm-customize {processed_file}" + Then it should exit successfully + + Scenario: osrm-customize - Missing input file + When I try to run "osrm-customize over-the-rainbow.osrm" + And stderr should contain "over-the-rainbow.osrm" + And stderr should contain "not found" + And it should exit with an error diff --git a/features/options/customize/help.feature b/features/options/customize/help.feature new file mode 100644 index 00000000000..504a5199d65 --- /dev/null +++ b/features/options/customize/help.feature @@ -0,0 +1,35 @@ +@contract @options @help +Feature: osrm-customize command line options: help + + Scenario: osrm-customize - Help should be shown when no options are passed + When I try to run "osrm-customize" + Then stderr should be empty + And stdout should contain /osrm-customize(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And it should exit with an error + + Scenario: osrm-customize - Help, short + When I run "osrm-customize -h" + Then stderr should be empty + And stdout should contain /osrm-customize(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And it should exit successfully + + Scenario: osrm-customize - Help, long + When I run "osrm-customize --help" + Then stderr should be empty + And stdout should contain /osrm-customize(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And it should exit successfully diff --git a/features/options/customize/invalid.feature b/features/options/customize/invalid.feature new file mode 100644 index 00000000000..ec03c4d7a1a --- /dev/null +++ b/features/options/customize/invalid.feature @@ -0,0 +1,20 @@ +@prepare @options @invalid +Feature: osrm-customize command line options: invalid options + + Background: + Given the profile "testbot" + And the node map + """ + a b + """ + And the ways + | nodes | + | ab | + And the data has been extracted + + Scenario: osrm-customize - Non-existing option + When I try to run "osrm-customize --fly-me-to-the-moon" + Then stdout should be empty + And stderr should contain "option" + And stderr should contain "fly-me-to-the-moon" + And it should exit with an error diff --git a/features/options/customize/version.feature b/features/options/customize/version.feature new file mode 100644 index 00000000000..54b5bd53153 --- /dev/null +++ b/features/options/customize/version.feature @@ -0,0 +1,19 @@ +@prepare @options @version +Feature: osrm-customize command line options: version + + Background: + Given the profile "testbot" + + Scenario: osrm-customize - Version, short + When I run "osrm-customize --v" + Then stderr should be empty + And stdout should contain 1 line + And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ + And it should exit successfully + + Scenario: osrm-customize - Version, long + When I run "osrm-customize --version" + Then stderr should be empty + And stdout should contain 1 line + And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ + And it should exit successfully diff --git a/features/options/partition/files.feature b/features/options/partition/files.feature new file mode 100644 index 00000000000..b9fd151b30a --- /dev/null +++ b/features/options/partition/files.feature @@ -0,0 +1,22 @@ +@partition @options @files +Feature: osrm-partition command line options: files + Background: + Given the profile "testbot" + And the node map + """ + a b + """ + And the ways + | nodes | + | ab | + And the data has been extracted + + Scenario: osrm-partition - Passing base file + When I run "osrm-partition {processed_file}" + Then it should exit successfully + + Scenario: osrm-partition - Missing input file + When I try to run "osrm-partition over-the-rainbow.osrm" + And stderr should contain "over-the-rainbow.osrm" + And stderr should contain "not found" + And it should exit with an error diff --git a/features/options/partition/help.feature b/features/options/partition/help.feature new file mode 100644 index 00000000000..e757628f93b --- /dev/null +++ b/features/options/partition/help.feature @@ -0,0 +1,50 @@ +@partition @options @help +Feature: osrm-partition command line options: help + + Scenario: osrm-partition - Help should be shown when no options are passed + When I try to run "osrm-partition" + Then stderr should be empty + And stdout should contain /osrm-partition(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And stdout should contain "--min-cell-size" + And stdout should contain "--balance" + And stdout should contain "--boundary" + And stdout should contain "--optimizing-cuts" + And stdout should contain "--small-component-size" + And it should exit with an error + + Scenario: osrm-partition - Help, short + When I run "osrm-partition -h" + Then stderr should be empty + And stdout should contain /osrm-partition(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And stdout should contain "--min-cell-size" + And stdout should contain "--balance" + And stdout should contain "--boundary" + And stdout should contain "--optimizing-cuts" + And stdout should contain "--small-component-size" + And it should exit successfully + + Scenario: osrm-partition - Help, long + When I run "osrm-partition --help" + Then stderr should be empty + And stdout should contain /osrm-partition(.exe)? \[options\]:/ + And stdout should contain "Options:" + And stdout should contain "--version" + And stdout should contain "--help" + And stdout should contain "Configuration:" + And stdout should contain "--threads" + And stdout should contain "--min-cell-size" + And stdout should contain "--balance" + And stdout should contain "--boundary" + And stdout should contain "--optimizing-cuts" + And stdout should contain "--small-component-size" + And it should exit successfully diff --git a/features/options/partition/invalid.feature b/features/options/partition/invalid.feature new file mode 100644 index 00000000000..fc832d93f45 --- /dev/null +++ b/features/options/partition/invalid.feature @@ -0,0 +1,20 @@ +@partition @options @invalid +Feature: osrm-partition command line options: invalid options + + Background: + Given the profile "testbot" + And the node map + """ + a b + """ + And the ways + | nodes | + | ab | + And the data has been extracted + + Scenario: osrm-partition - Non-existing option + When I try to run "osrm-partition --fly-me-to-the-moon" + Then stdout should be empty + And stderr should contain "option" + And stderr should contain "fly-me-to-the-moon" + And it should exit with an error diff --git a/features/options/partition/version.feature b/features/options/partition/version.feature new file mode 100644 index 00000000000..df99c06e14c --- /dev/null +++ b/features/options/partition/version.feature @@ -0,0 +1,19 @@ +@partition @options @version +Feature: osrm-partition command line options: version + + Background: + Given the profile "testbot" + + Scenario: osrm-partition - Version, short + When I run "osrm-partition --v" + Then stderr should be empty + And stdout should contain 1 line + And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ + And it should exit successfully + + Scenario: osrm-partition - Version, long + When I run "osrm-partition --version" + Then stderr should be empty + And stdout should contain 1 line + And stdout should contain /(v\d{1,2}\.\d{1,2}\.\d{1,2}|\w*-\d+-\w+)/ + And it should exit successfully diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js index 49ca6d7f17b..fe954335e02 100644 --- a/features/step_definitions/data.js +++ b/features/step_definitions/data.js @@ -23,6 +23,16 @@ module.exports = function () { callback(); }); + this.Given(/^the partition extra arguments "(.*?)"$/, (args, callback) => { + this.partitionArgs = this.expandOptions(args); + callback(); + }); + + this.Given(/^the customize extra arguments "(.*?)"$/, (args, callback) => { + this.customizeArgs = this.expandOptions(args); + callback(); + }); + this.Given(/^a grid size of ([0-9.]+) meters$/, (meters, callback) => { this.setGridSize(meters); callback(); @@ -259,12 +269,12 @@ module.exports = function () { this.writeAndLinkOSM(callback); }); - this.Given(/^the data has been extracted$/, (callback) => { + this.Given(/^the data has been (extract|contract)ed$/, (step, callback) => { this.reprocess(callback); }); - this.Given(/^the data has been contracted$/, (callback) => { - this.reprocess(callback); + this.Given(/^the data has been (partition|customiz)ed$/, (step, callback) => { + this.reprocessMLD(callback); }); this.Given(/^osrm\-routed is stopped$/, (callback) => { diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js index 948f8920862..abf3b6cdfc8 100644 --- a/features/step_definitions/options.js +++ b/features/step_definitions/options.js @@ -25,32 +25,16 @@ module.exports = function () { this.runAndSafeOutput('osrm-routed', options, callback); }); - this.When(/^I run "osrm\-extract\s?(.*?)"$/, (options, callback) => { - const stamp = this.processedCacheFile + '.extract'; - this.runAndSafeOutput('osrm-extract', options, (err) => { + this.When(/^I run "osrm\-(extract|contract|partition|customize)\s?(.*?)"$/, (binary, options, callback) => { + const stamp = this.processedCacheFile + '.stamp_' + binary; + this.runAndSafeOutput('osrm-' + binary, options, (err) => { if (err) return callback(err); fs.writeFile(stamp, 'ok', callback); }); }); - this.When(/^I run "osrm\-contract\s?(.*?)"$/, (options, callback) => { - const stamp = this.processedCacheFile + '.contract'; - this.runAndSafeOutput('osrm-contract', options, (err) => { - if (err) return callback(err); - fs.writeFile(stamp, 'ok', callback); - }); - }); - - this.When(/^I try to run "osrm\-routed\s?(.*?)"$/, (options, callback) => { - this.runAndSafeOutput('osrm-routed', options, () => { callback(); }); - }); - - this.When(/^I try to run "osrm\-extract\s?(.*?)"$/, (options, callback) => { - this.runAndSafeOutput('osrm-extract', options, () => { callback(); }); - }); - - this.When(/^I try to run "osrm\-contract\s?(.*?)"$/, (options, callback) => { - this.runAndSafeOutput('osrm-contract', options, () => { callback(); }); + this.When(/^I try to run "(osrm\-[a-z]+)\s?(.*?)"$/, (binary, options, callback) => { + this.runAndSafeOutput(binary, options, () => { callback(); }); }); this.When(/^I run "osrm\-datastore\s?(.*?)"(?: with input "([^"]*)")?$/, (options, input, callback) => { diff --git a/features/support/data.js b/features/support/data.js index c01cda3b7dd..13cdebbc0f6 100644 --- a/features/support/data.js +++ b/features/support/data.js @@ -179,7 +179,7 @@ module.exports = function () { }; this.extractData = (p, callback) => { - let stamp = p.processedCacheFile + '.extract'; + let stamp = p.processedCacheFile + '.stamp_extract'; fs.exists(stamp, (exists) => { if (exists) return callback(); @@ -193,7 +193,7 @@ module.exports = function () { }; this.contractData = (p, callback) => { - let stamp = p.processedCacheFile + '.contract'; + let stamp = p.processedCacheFile + '.stamp_contract'; fs.exists(stamp, (exists) => { if (exists) return callback(); @@ -206,10 +206,39 @@ module.exports = function () { }); }; + this.partitionData = (p, callback) => { + let stamp = p.processedCacheFile + '.stamp_partition'; + fs.exists(stamp, (exists) => { + if (exists) return callback(); + + this.runBin('osrm-partition', util.format('%s %s', p.partitionArgs, p.processedCacheFile), p.environment, (err) => { + if (err) { + return callback(new Error(util.format('osrm-partition %s: %s', errorReason(err), err.cmd))); + } + fs.writeFile(stamp, 'ok', callback); + }); + }); + }; + + this.customizeData = (p, callback) => { + let stamp = p.processedCacheFile + '.stamp_customize'; + fs.exists(stamp, (exists) => { + if (exists) return callback(); + + this.runBin('osrm-customize', util.format('%s %s', p.customizeArgs, p.processedCacheFile), p.environment, (err) => { + if (err) { + return callback(new Error(util.format('osrm-customize %s: %s', errorReason(err), err))); + } + fs.writeFile(stamp, 'ok', callback); + }); + }); + }; + this.extractAndContract = (callback) => { // a shallow copy of scenario parameters to avoid data inconsistency // if a cucumber timeout occurs during deferred jobs let p = {extractArgs: this.extractArgs, contractArgs: this.contractArgs, + partitionArgs: this.partitionArgs, customizeArgs: this.customizeArgs, profileFile: this.profileFile, inputCacheFile: this.inputCacheFile, processedCacheFile: this.processedCacheFile, environment: this.environment}; let queue = d3.queue(1); @@ -218,6 +247,19 @@ module.exports = function () { queue.awaitAll(callback); }; + this.extractPartitionAndCustomize = (callback) => { + // a shallow copy of scenario parameters to avoid data inconsistency + // if a cucumber timeout occurs during deferred jobs + let p = {extractArgs: this.extractArgs, partitionArgs: this.partitionArgs, customizeArgs: this.customizeArgs, + profileFile: this.profileFile, inputCacheFile: this.inputCacheFile, + processedCacheFile: this.processedCacheFile, environment: this.environment}; + let queue = d3.queue(1); + queue.defer(this.extractData.bind(this), p); + queue.defer(this.partitionData.bind(this), p); + queue.defer(this.customizeData.bind(this), p); + queue.awaitAll(callback); + }; + this.writeAndLinkOSM = (callback) => { let queue = d3.queue(1); queue.defer(this.writeOSM.bind(this)); @@ -232,10 +274,19 @@ module.exports = function () { queue.awaitAll(callback); }; + this.reprocessMLD = (callback) => { + let queue = d3.queue(1); + queue.defer(this.writeAndLinkOSM.bind(this)); + queue.defer(this.extractPartitionAndCustomize.bind(this)); + queue.awaitAll(callback); + }; + this.reprocessAndLoadData = (callback) => { let queue = d3.queue(1); queue.defer(this.writeAndLinkOSM.bind(this)); - queue.defer(this.extractAndContract.bind(this)); + queue.defer((this.ROUTING_ALGORITHM === 'MLD' ? + this.extractPartitionAndCustomize : + this.extractAndContract).bind(this)); queue.defer(this.osrmLoader.load.bind(this.osrmLoader), this.processedCacheFile); queue.awaitAll(callback); }; diff --git a/features/support/env.js b/features/support/env.js index 79c8dd862f1..8e1f8d4740e 100644 --- a/features/support/env.js +++ b/features/support/env.js @@ -34,12 +34,12 @@ module.exports = function () { this.DEFAULT_LOAD_METHOD = 'datastore'; this.DEFAULT_ORIGIN = [1,1]; this.OSM_USER = 'osrm'; - this.OSM_GENERATOR = 'osrm-test'; this.OSM_UID = 1; this.OSM_TIMESTAMP = '2000-01-01T00:00:00Z'; this.WAY_SPACING = 100; this.DEFAULT_GRID_SIZE = 100; // meters - this.ROUTING_ALGORITHM = 'CH'; + // get algorithm name from the command line profile argument + this.ROUTING_ALGORITHM = process.argv[process.argv.indexOf('-p') + 1] === 'mld' ? 'MLD' : 'CH'; this.OSRM_PORT = process.env.OSRM_PORT && parseInt(process.env.OSRM_PORT) || 5000; this.HOST = 'http://127.0.0.1:' + this.OSRM_PORT; diff --git a/features/support/hooks.js b/features/support/hooks.js index b0f6b0b77f8..f3c7fe10df6 100644 --- a/features/support/hooks.js +++ b/features/support/hooks.js @@ -35,6 +35,8 @@ module.exports = function () { this.queryParams = {}; this.extractArgs = ''; this.contractArgs = ''; + this.partitionArgs = ''; + this.customizeArgs = ''; this.environment = Object.assign(this.DEFAULT_ENVIRONMENT); this.resetOSM(); diff --git a/features/testbot/loop.feature b/features/testbot/loop.feature index 41a6bb4c018..985c8ba6a0e 100644 --- a/features/testbot/loop.feature +++ b/features/testbot/loop.feature @@ -4,6 +4,7 @@ Feature: Avoid weird loops caused by rounding errors Background: Given the profile "testbot" + @via Scenario: Weird sidestreet loops Given the node map """ @@ -72,7 +73,7 @@ Feature: Avoid weird loops caused by rounding errors | from | to | route | | x | y | abc,abc | - @412 + @412 @via Scenario: Avoid weird loops 3 And the node map """ diff --git a/features/testbot/weight.feature b/features/testbot/weight.feature index 7e9c113eaab..afe3f5e152b 100644 --- a/features/testbot/weight.feature +++ b/features/testbot/weight.feature @@ -240,6 +240,7 @@ Feature: Weight tests | e,d | ,, | 40m +-.1 | 4.009,1.11,0 | 189.9s,100s,0s | | d,e | ,, | 40m +-.1 | 2.21,1.11,0 | 10.1s,100s,0s | + @traffic @speed Scenario: Step weights -- segment_function with speed and turn updates Given the profile file "testbot" extended with """ @@ -286,6 +287,7 @@ Feature: Weight tests | a,e | ,, | 60.1m | 68.7,10,0 | 38.5s,11s,0s | | d,e | ,, | 39.9m | 10,10,0 | 11s,11s,0s | + @traffic @speed Scenario: Step weights -- segment_function with speed and turn updates with fallback to durations Given the profile file "testbot" extended with """ diff --git a/package.json b/package.json index 363a4dfe58b..84547068dfa 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ }, "scripts": { "lint": "eslint -c ./.eslintrc features/step_definitions/ features/support/", - "test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify", + "test": "npm run lint && node ./node_modules/cucumber/bin/cucumber.js features/ -p verify && node ./node_modules/cucumber/bin/cucumber.js features/ -p mld", "clean-test": "rm -rf test/cache", "cucumber": "./node_modules/cucumber/bin/cucumber.js", "build-api-docs": "./scripts/build_api_docs.sh" diff --git a/src/tools/customize.cpp b/src/tools/customize.cpp index ee3befca469..f675f309f5a 100644 --- a/src/tools/customize.cpp +++ b/src/tools/customize.cpp @@ -107,7 +107,7 @@ parseArguments(int argc, char *argv[], customizer::CustomizationConfig &customiz if (!option_variables.count("input")) { std::cout << visible_options; - return return_code::exit; + return return_code::fail; } return return_code::ok; diff --git a/src/tools/partition.cpp b/src/tools/partition.cpp index 5f3fa74fd2c..b3c80d05fe6 100644 --- a/src/tools/partition.cpp +++ b/src/tools/partition.cpp @@ -116,7 +116,7 @@ return_code parseArguments(int argc, char *argv[], partition::PartitionConfig &p if (!option_variables.count("input")) { std::cout << visible_options; - return return_code::exit; + return return_code::fail; } return return_code::ok; From 658673783567f650acce249508f659ada8bf48f5 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Sat, 11 Mar 2017 14:46:22 +0100 Subject: [PATCH 07/16] Fix weights and annotations for phantom points on one segment --- include/engine/guidance/assemble_steps.hpp | 17 +++++++++++++---- .../routing_algorithms/routing_base_mld.hpp | 10 +++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/include/engine/guidance/assemble_steps.hpp b/include/engine/guidance/assemble_steps.hpp index 2bef099adaf..0f52995d30d 100644 --- a/include/engine/guidance/assemble_steps.hpp +++ b/include/engine/guidance/assemble_steps.hpp @@ -209,13 +209,22 @@ inline std::vector assembleSteps(const datafacade::BaseDataFacade &fa else { BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position); - // s t + BOOST_ASSERT(source_traversed_in_reverse == target_traversed_in_reverse); + + // The difference (target-source) should handle + // all variants for similar directions u-v and s-t (and opposite) + // s(t) t(s) source_traversed_in_reverse = target_traversed_in_reverse = false // u-------------v - // |---| source_duration - // |---------| target_duration + // |---| source_weight + // |---------| target_weight - const EdgeWeight duration = target_duration - source_duration; + // s(t) t(s) source_traversed_in_reverse = target_traversed_in_reverse = true + // u-------------v + // | |---------| source_weight + // | |---| target_weight const EdgeWeight weight = target_weight - source_weight; + const EdgeWeight duration = target_duration - source_duration; + BOOST_ASSERT(weight >= 0); BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{source_node.name_id, diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 47788f5b3de..2af6ab7475e 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -35,12 +35,16 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade target with + // weight(source -> node) = forward_weight, weight(node -> to) = edge_weight and + // weight(to -> target) ≤ reverse_weight is forward_weight + edge_weight + reverse_weight + // More tighter upper bound requires additional condition reverse_heap.WasRemoved(to) + // with weight(to -> target) = reverse_weight and all weights ≥ 0 + if (reverse_heap.WasInserted(to)) { auto reverse_weight = reverse_heap.GetKey(to); auto path_weight = forward_weight + edge_weight + reverse_weight; - BOOST_ASSERT(path_weight >= 0); - if (path_weight < path_upper_bound) + if (path_weight >= 0 && path_weight < path_upper_bound) { middle_node = to; path_upper_bound = path_weight; From 97c442482dea424b94ca36eda1fb76b9eee003da Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 14 Mar 2017 14:45:44 +0100 Subject: [PATCH 08/16] Prepare test data for both CH and MLD algorithms Leaving log files opened was intentional to avoid missing output that can appear `child.on('exit',...)`. With this approach cucumber tests hit locally maximum number of opened files, because node.js keeps all log files opened. --- features/step_definitions/data.js | 6 +----- features/support/data.js | 26 +++----------------------- features/support/run.js | 4 ++++ 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/features/step_definitions/data.js b/features/step_definitions/data.js index fe954335e02..a46bb889b4c 100644 --- a/features/step_definitions/data.js +++ b/features/step_definitions/data.js @@ -269,14 +269,10 @@ module.exports = function () { this.writeAndLinkOSM(callback); }); - this.Given(/^the data has been (extract|contract)ed$/, (step, callback) => { + this.Given(/^the data has been (extract|contract|partition|customiz)ed$/, (step, callback) => { this.reprocess(callback); }); - this.Given(/^the data has been (partition|customiz)ed$/, (step, callback) => { - this.reprocessMLD(callback); - }); - this.Given(/^osrm\-routed is stopped$/, (callback) => { this.OSRMLoader.shutdown(callback); }); diff --git a/features/support/data.js b/features/support/data.js index 13cdebbc0f6..9dc9856d9c8 100644 --- a/features/support/data.js +++ b/features/support/data.js @@ -234,7 +234,7 @@ module.exports = function () { }); }; - this.extractAndContract = (callback) => { + this.extractContractPartitionAndCustomize = (callback) => { // a shallow copy of scenario parameters to avoid data inconsistency // if a cucumber timeout occurs during deferred jobs let p = {extractArgs: this.extractArgs, contractArgs: this.contractArgs, @@ -244,17 +244,6 @@ module.exports = function () { let queue = d3.queue(1); queue.defer(this.extractData.bind(this), p); queue.defer(this.contractData.bind(this), p); - queue.awaitAll(callback); - }; - - this.extractPartitionAndCustomize = (callback) => { - // a shallow copy of scenario parameters to avoid data inconsistency - // if a cucumber timeout occurs during deferred jobs - let p = {extractArgs: this.extractArgs, partitionArgs: this.partitionArgs, customizeArgs: this.customizeArgs, - profileFile: this.profileFile, inputCacheFile: this.inputCacheFile, - processedCacheFile: this.processedCacheFile, environment: this.environment}; - let queue = d3.queue(1); - queue.defer(this.extractData.bind(this), p); queue.defer(this.partitionData.bind(this), p); queue.defer(this.customizeData.bind(this), p); queue.awaitAll(callback); @@ -270,23 +259,14 @@ module.exports = function () { this.reprocess = (callback) => { let queue = d3.queue(1); queue.defer(this.writeAndLinkOSM.bind(this)); - queue.defer(this.extractAndContract.bind(this)); - queue.awaitAll(callback); - }; - - this.reprocessMLD = (callback) => { - let queue = d3.queue(1); - queue.defer(this.writeAndLinkOSM.bind(this)); - queue.defer(this.extractPartitionAndCustomize.bind(this)); + queue.defer(this.extractContractPartitionAndCustomize.bind(this)); queue.awaitAll(callback); }; this.reprocessAndLoadData = (callback) => { let queue = d3.queue(1); queue.defer(this.writeAndLinkOSM.bind(this)); - queue.defer((this.ROUTING_ALGORITHM === 'MLD' ? - this.extractPartitionAndCustomize : - this.extractAndContract).bind(this)); + queue.defer(this.extractContractPartitionAndCustomize.bind(this)); queue.defer(this.osrmLoader.load.bind(this.osrmLoader), this.processedCacheFile); queue.awaitAll(callback); }; diff --git a/features/support/run.js b/features/support/run.js index 4427814ad78..2b4408f7510 100644 --- a/features/support/run.js +++ b/features/support/run.js @@ -46,6 +46,10 @@ module.exports = function () { let child = child_process.execFile(cmd, opts, {maxBuffer: 1024 * 1024 * 1000, env: env}, callback); child.on('exit', function(code) { log.write(util.format('*** %s exited with code %d\n', bin, code)); + // remove listeners and close log file -> some tail messages can be lost + child.stdout.removeListener('data', child.logFunc); + child.stderr.removeListener('data', child.logFunc); + log.end(); }.bind(this)); this.setupOutputLog(child, log); return child; From 20c6b4682f6b8a60d154b46843c93cc80f8ba863 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Sun, 12 Mar 2017 19:19:57 +0100 Subject: [PATCH 09/16] MLD regression tests --- features/step_definitions/options.js | 5 +- features/testbot/multi_level_routing.feature | 131 +++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) create mode 100644 features/testbot/multi_level_routing.feature diff --git a/features/step_definitions/options.js b/features/step_definitions/options.js index abf3b6cdfc8..d16ef8d0c97 100644 --- a/features/step_definitions/options.js +++ b/features/step_definitions/options.js @@ -52,8 +52,9 @@ module.exports = function () { assert.ok(this.exitCode !== 0 || this.termSignal); }); - this.Then(/^stdout should contain "(.*?)"$/, (str) => { - assert.ok(this.stdout.indexOf(str) > -1); + this.Then(/^stdout should( not)? contain "(.*?)"$/, (not, str) => { + const contains = this.stdout.indexOf(str) > -1; + assert.ok(typeof not === 'undefined' ? contains : !contains); }); this.Then(/^stderr should( not)? contain "(.*?)"$/, (not, str) => { diff --git a/features/testbot/multi_level_routing.feature b/features/testbot/multi_level_routing.feature new file mode 100644 index 00000000000..30a643f812e --- /dev/null +++ b/features/testbot/multi_level_routing.feature @@ -0,0 +1,131 @@ +@routing @testbot @mld +Feature: Multi level routing + + Background: + Given the profile "testbot" + And the partition extra arguments "--min-cell-size 4 --small-component-size 1" + + Scenario: Testbot - Multi level routing check partition + Given the node map + """ + a───b───e───f + │ │ │ │ + d───c h───g + ╲ ╱ + ╳ + ╱ ╲ + i───j m───n + │ │ │ │ + l───k───p───o + """ + + And the ways + | nodes | highway | + | abcda | primary | + | efghe | primary | + | ijkli | primary | + | nmop | primary | + | cm | primary | + | hj | primary | + | kp | primary | + | be | primary | + + And the data has been extracted + When I run "osrm-partition --min-cell-size 4 --small-component-size 1 {processed_file}" + Then it should exit successfully + And stdout should not contain "level 1 #cells 1 bit size 1" + + Scenario: Testbot - Multi level routing + Given the node map + """ + a───b e───f + │ │ │ │ + d───c h───g + ╲ ╱ + ╳ + ╱ ╲ + i───j m───n + │ │ │ │ + l───k───p───o + """ + And the ways + | nodes | highway | + | abcda | primary | + | efghe | primary | + | ijkli | primary | + | nmop | primary | + | cm | primary | + | hj | primary | + | kp | primary | + And the partition extra arguments "--min-cell-size 4 --small-component-size 1" + + When I route I should get + | from | to | route | time | + | a | b | abcda,abcda | 20s | + | a | f | abcda,cm,nmop,kp,ijkli,hj,efghe,efghe | 257.7s | + | c | m | cm,cm | 44.7s | + + Scenario: Testbot - Multi level routing: horizontal road + Given the node map + """ + a───b e───f + │ │ │ │ + d───c h───g + │ │ + i═══j═══k═══l + │ │ + m───n q───r + │ │ │ │ + p───o───t───s + """ + And the ways + | nodes | highway | + | abcda | primary | + | efghe | primary | + | mnopm | primary | + | qrstq | primary | + | ijkl | primary | + | dim | primary | + | glr | primary | + | ot | secondary | + And the partition extra arguments "--min-cell-size 4 --small-component-size 1" + + When I route I should get + | from | to | route | time | + | a | b | abcda,abcda | 20s | + | a | d | abcda,abcda | 20s | + | a | l | abcda,dim,ijkl,ijkl | 100s | + | a | p | abcda,dim,mnopm,mnopm | 80s | + | a | o | abcda,dim,mnopm,mnopm | 100s | + | a | t | abcda,dim,mnopm,ot,ot | 140s | + | a | s | abcda,dim,ijkl,glr,qrstq,qrstq | 140s | + | a | f | abcda,dim,ijkl,glr,efghe,efghe | 140s | + + + Scenario: Testbot - Multi level routing: route over internal cell edge hf + Given the node map + """ + a───b + │ │ + d───c──e───f + ╲ │ ╳ │ ╲ + h───g──i───j + │ │ + l───k + """ + And the ways + | nodes | maxspeed | + | abcda | 5 | + | efghe | 5 | + | ijkli | 5 | + | eg | 10 | + | ce | 15 | + | ch | 15 | + | fi | 15 | + | gi | 15 | + | hf | 100 | + And the partition extra arguments "--min-cell-size 4 --small-component-size 1" + + When I route I should get + | from | to | route | time | + | a | k | abcda,ch,hf,fi,ijkli,ijkli | 724.3s | From c39690195e06a69f02160a715d5c3a6a89c53397 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 14 Mar 2017 15:39:59 +0100 Subject: [PATCH 10/16] Added traffic tests to MLD testing --- cucumber.js | 2 +- features/car/traffic_turn_penalties.feature | 6 ++++-- features/car/weight.feature | 1 + features/testbot/matching.feature | 2 ++ features/testbot/traffic_speeds.feature | 12 ++++++++---- features/testbot/traffic_turn_penalties.feature | 4 ++++ features/testbot/weight.feature | 2 ++ 7 files changed, 22 insertions(+), 7 deletions(-) diff --git a/cucumber.js b/cucumber.js index 5390a9215aa..10dcd79b40d 100644 --- a/cucumber.js +++ b/cucumber.js @@ -3,5 +3,5 @@ module.exports = { verify: '--strict --tags ~@stress --tags ~@todo -f progress --require features/support --require features/step_definitions', todo: '--strict --tags @todo --require features/support --require features/step_definitions', all: '--strict --require features/support --require features/step_definitions', - mld: '--strict --tags ~@stress --tags ~@todo --tags ~@match --tags ~@alternative --tags ~@traffic --tags ~@weight --tags ~@matrix --tags ~@trip --tags ~@via --require features/support --require features/step_definitions -f progress' + mld: '--strict --tags ~@stress --tags ~@todo --tags ~@match --tags ~@alternative --tags ~@matrix --tags ~@trip --tags ~@via --require features/support --require features/step_definitions -f progress' } diff --git a/features/car/traffic_turn_penalties.feature b/features/car/traffic_turn_penalties.feature index 7fcbe197e2d..21644a2ea1f 100644 --- a/features/car/traffic_turn_penalties.feature +++ b/features/car/traffic_turn_penalties.feature @@ -87,6 +87,7 @@ Feature: Traffic - turn penalties # hkl left turn # ade left turn And the contract extra arguments "--turn-penalty-file {penalties_file}" + And the customize extra arguments "--turn-penalty-file {penalties_file}" When I route I should get | from | to | route | speed | weight | time | | a | h | ad,dhk | 65 km/h | 11 | 11s +-1 | @@ -109,8 +110,9 @@ Feature: Traffic - turn penalties # double left - hdc penalty ever so slightly higher than imn; forces all the way around Scenario: Too-negative penalty clamps, but does not fail - Given the contract extra arguments "--turn-penalty-file {penalties_file}" - And the profile "testbot" + Given the profile "testbot" + And the contract extra arguments "--turn-penalty-file {penalties_file}" + And the customize extra arguments "--turn-penalty-file {penalties_file}" And the turn penalty file """ 1,4,5,-10 diff --git a/features/car/weight.feature b/features/car/weight.feature index 5ca4e205e0a..d47569d7ec9 100644 --- a/features/car/weight.feature +++ b/features/car/weight.feature @@ -53,6 +53,7 @@ Feature: Car - weights | ec | service | yes | And the extract extra arguments "--generate-edge-lookup" And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" And the speed file """ 2,5,8 diff --git a/features/testbot/matching.feature b/features/testbot/matching.feature index 8d25be2756c..2fc4f8fa227 100644 --- a/features/testbot/matching.feature +++ b/features/testbot/matching.feature @@ -139,6 +139,7 @@ Feature: Basic Map Matching """ And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" When I match I should get | trace | matchings | a:duration | @@ -165,6 +166,7 @@ Feature: Basic Map Matching """ And the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" When I match I should get | trace | matchings | a:duration | diff --git a/features/testbot/traffic_speeds.feature b/features/testbot/traffic_speeds.feature index 16f2b3b6a5c..02ceb179706 100644 --- a/features/testbot/traffic_speeds.feature +++ b/features/testbot/traffic_speeds.feature @@ -27,7 +27,8 @@ Feature: Traffic - speeds Scenario: Weighting based on speed file Given the contract extra arguments "--segment-speed-file {speeds_file}" - Given the speed file + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file """ 1,2,0 2,1,0 @@ -52,7 +53,8 @@ Feature: Traffic - speeds Scenario: Weighting based on speed file weights, ETA based on file durations Given the contract extra arguments "--segment-speed-file {speeds_file}" - Given the speed file + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file """ 1,2,1,20020.7 2,1,1,20020.7 @@ -84,7 +86,8 @@ Feature: Traffic - speeds properties.weight_precision = 3 """ And the contract extra arguments "--segment-speed-file {speeds_file}" - Given the speed file + And the customize extra arguments "--segment-speed-file {speeds_file}" + And the speed file """ 1,2,1,20020.789 2,1,1,20020.123 @@ -110,10 +113,11 @@ Feature: Traffic - speeds Scenario: Speeds that isolate a single node (a) Given the contract extra arguments "--segment-speed-file {speeds_file}" + And the customize extra arguments "--segment-speed-file {speeds_file}" And the node locations | node | lat | lon | | h | 2.075 | 19.1 | - Given the speed file + And the speed file """ 1,2,0 2,1,0 diff --git a/features/testbot/traffic_turn_penalties.feature b/features/testbot/traffic_turn_penalties.feature index a86d5323ea8..f8654b649d9 100644 --- a/features/testbot/traffic_turn_penalties.feature +++ b/features/testbot/traffic_turn_penalties.feature @@ -40,6 +40,8 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap 3,4,7,-30 """ And the contract extra arguments "--turn-penalty-file {penalties_file}" + And the customize extra arguments "--turn-penalty-file {penalties_file}" + When I route I should get | from | to | route | speed | time | | a | e | ab,be,be | 36 km/h | 40s +-1 | @@ -56,6 +58,8 @@ Feature: Traffic - turn penalties applied to turn onto which a phantom node snap 3,4,7,-30,100.75 """ And the contract extra arguments "--turn-penalty-file {penalties_file}" + And the customize extra arguments "--turn-penalty-file {penalties_file}" + When I route I should get | from | to | route | speed | time | weights | | a | e | ab,be,be | 36 km/h | 40s +-1 | 16.7,20,0 | diff --git a/features/testbot/weight.feature b/features/testbot/weight.feature index afe3f5e152b..de20a61931c 100644 --- a/features/testbot/weight.feature +++ b/features/testbot/weight.feature @@ -280,6 +280,7 @@ Feature: Weight tests 2,3,5,25.5,16.7 """ And the contract extra arguments "--segment-speed-file {speeds_file} --turn-penalty-file {penalties_file}" + And the customize extra arguments "--segment-speed-file {speeds_file} --turn-penalty-file {penalties_file}" When I route I should get | waypoints | route | distance | weights | times | @@ -315,6 +316,7 @@ Feature: Weight tests 2,3,5,1 """ And the contract extra arguments "--segment-speed-file {speeds_file} --turn-penalty-file {penalties_file}" + And the customize extra arguments "--segment-speed-file {speeds_file} --turn-penalty-file {penalties_file}" When I route I should get | waypoints | route | distance | weights | times | From 152f77b6654d3c8ece449cc0516453c081563662 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Wed, 15 Mar 2017 13:33:43 +0100 Subject: [PATCH 11/16] Remove level field from heap node data --- .../routing_algorithms/routing_base_mld.hpp | 54 ++++++++++--------- include/engine/search_engine_data.hpp | 12 +---- .../direct_shortest_path.cpp | 37 +++++++------ 3 files changed, 50 insertions(+), 53 deletions(-) diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 2af6ab7475e..ff0ddf6a42d 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -21,16 +21,18 @@ namespace mld template void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const partition::MultiLevelPartitionView &partition, - const partition::CellStorageView &cells, SearchEngineData::MultiLayerDijkstraHeap &forward_heap, SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, const std::pair &parent_cell, + const std::function &get_query_level, NodeID &middle_node, EdgeWeight &path_upper_bound, EdgeWeight &forward_upper_bound, EdgeWeight &reverse_upper_bound) { + const auto &partition = facade.GetMultiLevelPartition(); + const auto &cells = facade.GetCellStorage(); + const auto node = forward_heap.DeleteMin(); const auto weight = forward_heap.GetKey(node); @@ -55,7 +57,7 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade= 1) && // only if at least the first level and (node_data.parent == node || // is the first point of the path @@ -80,12 +82,12 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, - const partition::MultiLevelPartitionView &partition, - const partition::CellStorageView &cells, SearchEngineData::MultiLayerDijkstraHeap &forward_heap, SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, - const std::pair &parent_cell) + const std::pair &parent_cell, + const std::function &get_query_level) { + const auto &partition = facade.GetMultiLevelPartition(); + // run two-Target Dijkstra routing step. NodeID middle = SPECIAL_NODEID; EdgeWeight weight = INVALID_EDGE_WEIGHT; @@ -177,11 +180,10 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade { progress = true; routingStep(facade, - partition, - cells, forward_heap, reverse_heap, parent_cell, + get_query_level, middle, weight, forward_search_radius, @@ -191,11 +193,10 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade { progress = true; routingStep(facade, - partition, - cells, reverse_heap, forward_heap, parent_cell, + get_query_level, middle, weight, reverse_search_radius, @@ -210,13 +211,13 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade INVALID_EDGE_WEIGHT, SPECIAL_NODEID, SPECIAL_NODEID, std::vector()); } - // Get packed path as edges {level, from node ID, to node ID, edge ID} - std::vector> packed_path; + // Get packed path as edges {from node ID, to node ID, edge ID} + std::vector> packed_path; NodeID current_node = middle, parent_node = forward_heap.GetData(middle).parent; while (parent_node != current_node) { const auto &data = forward_heap.GetData(current_node); - packed_path.push_back(std::make_tuple(data.level, parent_node, current_node, data.edge_id)); + packed_path.push_back(std::make_tuple(parent_node, current_node, data.edge_id)); current_node = parent_node; parent_node = forward_heap.GetData(parent_node).parent; } @@ -227,7 +228,7 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade while (parent_node != current_node) { const auto &data = reverse_heap.GetData(current_node); - packed_path.push_back(std::make_tuple(data.level, current_node, parent_node, data.edge_id)); + packed_path.push_back(std::make_tuple(current_node, parent_node, data.edge_id)); current_node = parent_node; parent_node = reverse_heap.GetData(parent_node).parent; } @@ -238,25 +239,26 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade unpacked_path.reserve(packed_path.size()); for (auto &packed_edge : packed_path) { - LevelID level; NodeID source, target; EdgeID edge_id; - std::tie(level, source, target, edge_id) = packed_edge; + std::tie(source, target, edge_id) = packed_edge; if (edge_id != SPECIAL_EDGEID) { // a base graph edge unpacked_path.push_back(edge_id); } else { // an overlay graph edge - LevelID sublevel = level - 1; + LevelID level = std::min(parent_cell.first, get_query_level(source)); CellID parent_cell_id = partition.GetCell(level, source); BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); + LevelID sublevel = level - 1; + // Here heaps can be reused, let's go deeper! forward_heap.Clear(); reverse_heap.Clear(); - forward_heap.Insert(source, 0, {source, sublevel}); - reverse_heap.Insert(target, 0, {target, sublevel}); + forward_heap.Insert(source, 0, {source}); + reverse_heap.Insert(target, 0, {target}); // TODO: when structured bindings will be allowed change to // auto [subpath_weight, subpath_source, subpath_target, subpath] = ... @@ -264,7 +266,7 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade NodeID subpath_source, subpath_target; std::vector subpath; std::tie(subpath_weight, subpath_source, subpath_target, subpath) = search( - facade, partition, cells, forward_heap, reverse_heap, {sublevel, parent_cell_id}); + facade, forward_heap, reverse_heap, {sublevel, parent_cell_id}, get_query_level); BOOST_ASSERT(!subpath.empty()); BOOST_ASSERT(subpath_source == source); BOOST_ASSERT(subpath_target == target); diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index 3ab5245a7fe..f84b921919b 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -26,17 +26,9 @@ struct ManyToManyHeapData : HeapData struct MultiLayerDijkstraHeapData : HeapData { - LevelID level; // node level: always increasing along the path starting from 0 EdgeID edge_id; // edge id if parent -> node is a boundary edge - MultiLayerDijkstraHeapData(NodeID p) : HeapData(p), level(0), edge_id(SPECIAL_EDGEID) {} - MultiLayerDijkstraHeapData(NodeID p, LevelID level) - : HeapData(p), level(level), edge_id(SPECIAL_EDGEID) - { - } - MultiLayerDijkstraHeapData(NodeID p, LevelID level, EdgeID edge_id) - : HeapData(p), level(level), edge_id(edge_id) - { - } + MultiLayerDijkstraHeapData(NodeID p) : HeapData(p), edge_id(SPECIAL_EDGEID) {} + MultiLayerDijkstraHeapData(NodeID p, EdgeID edge_id) : HeapData(p), edge_id(edge_id) {} }; struct SearchEngineData diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 887905469ef..b307903ee6e 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -139,32 +139,35 @@ InternalRouteResult directShortestPathSearch( insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); const auto &partition = facade.GetMultiLevelPartition(); - const auto &cells = facade.GetCellStorage(); - auto get_highest_level = [&partition](const SegmentID &source, const SegmentID &target) { - if (source.enabled && target.enabled) - return partition.GetHighestDifferentLevel(source.id, target.id); - return INVALID_LEVEL_ID; + auto get_query_level = [&partition, &phantom_nodes](const NodeID node) -> LevelID { + auto level = + [&partition](const SegmentID &source, const SegmentID &target, const NodeID node) { + if (source.enabled && target.enabled) + return partition.GetQueryLevel(source.id, target.id, node); + return INVALID_LEVEL_ID; + }; + return std::min(std::min(level(phantom_nodes.source_phantom.forward_segment_id, + phantom_nodes.target_phantom.forward_segment_id, + node), + level(phantom_nodes.source_phantom.forward_segment_id, + phantom_nodes.target_phantom.reverse_segment_id, + node)), + std::min(level(phantom_nodes.source_phantom.reverse_segment_id, + phantom_nodes.target_phantom.forward_segment_id, + node), + level(phantom_nodes.source_phantom.reverse_segment_id, + phantom_nodes.target_phantom.reverse_segment_id, + node))); }; - const auto &source_phantom = phantom_nodes.source_phantom; - const auto &target_phantom = phantom_nodes.target_phantom; - const auto highest_level = - std::min(std::min(get_highest_level(source_phantom.forward_segment_id, - target_phantom.forward_segment_id), - get_highest_level(source_phantom.forward_segment_id, - target_phantom.reverse_segment_id)), - std::min(get_highest_level(source_phantom.reverse_segment_id, - target_phantom.forward_segment_id), - get_highest_level(source_phantom.reverse_segment_id, - target_phantom.reverse_segment_id))); // TODO: when structured bindings will be allowed change to // auto [weight, source_node, target_node, unpacked_edges] = ... EdgeWeight weight; NodeID source_node, target_node; std::vector unpacked_edges; std::tie(weight, source_node, target_node, unpacked_edges) = mld::search( - facade, partition, cells, forward_heap, reverse_heap, {highest_level, INVALID_CELL_ID}); + facade, forward_heap, reverse_heap, {INVALID_LEVEL_ID, INVALID_CELL_ID}, get_query_level); return extractRoute(facade, weight, source_node, target_node, unpacked_edges, phantom_nodes); } From a75fd560d7d794f6d95d2348f8ee77bf80f25e00 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Tue, 14 Mar 2017 16:04:15 +0100 Subject: [PATCH 12/16] Use correct upper bound condition for MLD routing --- .../routing_algorithms/routing_base_mld.hpp | 90 +++++++------------ 1 file changed, 33 insertions(+), 57 deletions(-) diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index ff0ddf6a42d..4268d88c483 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -26,9 +26,7 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &parent_cell, const std::function &get_query_level, NodeID &middle_node, - EdgeWeight &path_upper_bound, - EdgeWeight &forward_upper_bound, - EdgeWeight &reverse_upper_bound) + EdgeWeight &path_upper_bound) { const auto &partition = facade.GetMultiLevelPartition(); const auto &cells = facade.GetCellStorage(); @@ -36,25 +34,21 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade target with - // weight(source -> node) = forward_weight, weight(node -> to) = edge_weight and - // weight(to -> target) ≤ reverse_weight is forward_weight + edge_weight + reverse_weight - // More tighter upper bound requires additional condition reverse_heap.WasRemoved(to) - // with weight(to -> target) = reverse_weight and all weights ≥ 0 - if (reverse_heap.WasInserted(to)) + // Upper bound for the path source -> target with + // weight(source -> node) = weight weight(to -> target) ≤ reverse_weight + // is weight + reverse_weight + // More tighter upper bound requires additional condition reverse_heap.WasRemoved(to) + // with weight(to -> target) = reverse_weight and all weights ≥ 0 + if (reverse_heap.WasInserted(node)) + { + auto reverse_weight = reverse_heap.GetKey(node); + auto path_weight = weight + reverse_weight; + if (path_weight >= 0 && path_weight < path_upper_bound) { - auto reverse_weight = reverse_heap.GetKey(to); - auto path_weight = forward_weight + edge_weight + reverse_weight; - if (path_weight >= 0 && path_weight < path_upper_bound) - { - middle_node = to; - path_upper_bound = path_weight; - forward_upper_bound = forward_weight + edge_weight; - reverse_upper_bound = reverse_weight + edge_weight; - } + middle_node = node; + path_upper_bound = path_weight; } - }; + } const auto &node_data = forward_heap.GetData(node); const auto level = get_query_level(node); @@ -63,9 +57,6 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade const std::pair &parent_cell, const std::function &get_query_level) { + const auto &partition = facade.GetMultiLevelPartition(); + BOOST_ASSERT(!forward_heap.Empty() && forward_heap.MinKey() < INVALID_EDGE_WEIGHT); + BOOST_ASSERT(!reverse_heap.Empty() && reverse_heap.MinKey() < INVALID_EDGE_WEIGHT); + // run two-Target Dijkstra routing step. NodeID middle = SPECIAL_NODEID; EdgeWeight weight = INVALID_EDGE_WEIGHT; - EdgeWeight forward_search_radius = INVALID_EDGE_WEIGHT; - EdgeWeight reverse_search_radius = INVALID_EDGE_WEIGHT; - bool progress; - do + EdgeWeight forward_heap_min = forward_heap.MinKey(); + EdgeWeight reverse_heap_min = reverse_heap.MinKey(); + while (forward_heap.Size() + reverse_heap.Size() > 0 && + forward_heap_min + reverse_heap_min < weight) { - progress = false; - if (!forward_heap.Empty() && (forward_heap.MinKey() < forward_search_radius)) + if (!forward_heap.Empty()) { - progress = true; - routingStep(facade, - forward_heap, - reverse_heap, - parent_cell, - get_query_level, - middle, - weight, - forward_search_radius, - reverse_search_radius); + routingStep( + facade, forward_heap, reverse_heap, parent_cell, get_query_level, middle, weight); + if (!forward_heap.Empty()) + forward_heap_min = forward_heap.MinKey(); } - if (!reverse_heap.Empty() && (reverse_heap.MinKey() < reverse_search_radius)) + if (!reverse_heap.Empty()) { - progress = true; - routingStep(facade, - reverse_heap, - forward_heap, - parent_cell, - get_query_level, - middle, - weight, - reverse_search_radius, - forward_search_radius); + routingStep( + facade, reverse_heap, forward_heap, parent_cell, get_query_level, middle, weight); + if (!reverse_heap.Empty()) + reverse_heap_min = reverse_heap.MinKey(); } - } while (progress); + }; // No path found for both target nodes? if (weight == INVALID_EDGE_WEIGHT || SPECIAL_NODEID == middle) From 5e40b124504840af6ce3f79d3d972ddba71f7f4c Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Wed, 15 Mar 2017 20:22:59 +0100 Subject: [PATCH 13/16] Add data names to benchmark output --- test/data/Makefile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/data/Makefile b/test/data/Makefile index da3c5f3ca40..31f4b0fc5bf 100755 --- a/test/data/Makefile +++ b/test/data/Makefile @@ -35,21 +35,21 @@ $(DATA_NAME)_%.osrm: $(DATA_NAME)_%.osm.pbf $(DATA_NAME).poly $(PROFILE) $(OSRM_ @echo "Verifiyng data file integrity..." $(MD5SUM) -c data.md5sum @echo "Running osrm-extract..." - $(TIMER) "osrm-extract" $(OSRM_EXTRACT) $< -p $(PROFILE) + $(TIMER) "osrm-extract\t$@" $(OSRM_EXTRACT) $< -p $(PROFILE) $(DATA_NAME)_CH.osrm.hsgr: $(DATA_NAME)_CH.osrm $(PROFILE) $(OSRM_CONTRACT) @echo "Running osrm-contract..." - $(TIMER) "osrm-contract" $(OSRM_CONTRACT) $< + $(TIMER) "osrm-contract\t$@" $(OSRM_CONTRACT) $< $(DATA_NAME)_CoreCH.osrm.hsgr: $(DATA_NAME)_CoreCH.osrm $(PROFILE) $(OSRM_CONTRACT) @echo "Running osrm-contract..." - $(TIMER) "osrm-contract" $(OSRM_CONTRACT) --core=0.95 $< + $(TIMER) "osrm-contract\t$@" $(OSRM_CONTRACT) --core=0.95 $< $(DATA_NAME)_MLD.osrm.partition: $(DATA_NAME)_MLD.osrm $(PROFILE) $(OSRM_PARTITION) @echo "Running osrm-partition..." - $(TIMER) "osrm-contract" $(OSRM_CONTRACT) $< - $(TIMER) "osrm-partition" $(OSRM_PARTITION) $< - $(TIMER) "osrm-customize" $(OSRM_CUSTOMIZE) $< + $(TIMER) "osrm-contract\t$@" $(OSRM_CONTRACT) $< + $(TIMER) "osrm-partition\t$@" $(OSRM_PARTITION) $< + $(TIMER) "osrm-customize\t$@" $(OSRM_CUSTOMIZE) $< $(DATA_NAME).requests: $(DATA_NAME).poly $(POLY2REQ) $(DATA_NAME).poly > $(DATA_NAME).requests @@ -58,17 +58,17 @@ benchmark: data $(DATA_NAME).requests @echo "Running benchmark..." @/bin/sh -c '$(OSRM_ROUTED) --algorithm=CH $(DATA_NAME)_CH.osrm > /dev/null & echo "$$!" > osrm-routed.pid' @sleep 1 - $(TIMER) "queries" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" + $(TIMER) "queries\tCH" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" @cat osrm-routed.pid | xargs kill @rm osrm-routed.pid @/bin/sh -c '$(OSRM_ROUTED) --algorithm=CoreCH $(DATA_NAME)_CoreCH.osrm > /dev/null & echo "$$!" > osrm-routed.pid' @sleep 1 - $(TIMER) "queries" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" + $(TIMER) "queries\tCoreCH" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" @cat osrm-routed.pid | xargs kill @rm osrm-routed.pid @/bin/sh -c '$(OSRM_ROUTED) --algorithm=MLD $(DATA_NAME)_MLD.osrm > /dev/null & echo "$$!" > osrm-routed.pid' @sleep 1 - $(TIMER) "queries" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" + $(TIMER) "queries\tMLD" "cat $(DATA_NAME).requests | xargs curl &> /dev/null" @cat osrm-routed.pid | xargs kill @rm osrm-routed.pid @echo "**** timings ***" From 0c6ce6ce085e66a50b8f12f0f37bb1d30e500735 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Wed, 15 Mar 2017 23:16:02 +0100 Subject: [PATCH 14/16] Implement MLD routingStep with cell restriction and without --- .../routing_algorithms/routing_base_mld.hpp | 50 +++++++++++-------- .../direct_shortest_path.cpp | 4 +- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 4268d88c483..4d9759f6a14 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -19,14 +19,29 @@ namespace routing_algorithms namespace mld { -template +namespace +{ +// Unrestricted search (Args is std::function): +// * use `query_level` closure of partition.GetQueryLevel to find the query level +// * allow to traverse all cells +using QueryLevelFunction = std::function; +LevelID getNodeQureyLevel(NodeID node, const QueryLevelFunction &functor) { return functor(node); } +bool checkParentCellRestriction(CellID, const QueryLevelFunction &) { return true; } + +// Restricted search (Args is LevelID, CellID): +// * use the fixed level for queries +// * check if the node cell is the same as the specified parent onr +LevelID getNodeQureyLevel(NodeID, LevelID level, CellID) { return level; } +bool checkParentCellRestriction(CellID cell, LevelID, CellID parent) { return cell == parent; } +} + +template void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &facade, SearchEngineData::MultiLayerDijkstraHeap &forward_heap, SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, - const std::pair &parent_cell, - const std::function &get_query_level, NodeID &middle_node, - EdgeWeight &path_upper_bound) + EdgeWeight &path_upper_bound, + Args... args) { const auto &partition = facade.GetMultiLevelPartition(); const auto &cells = facade.GetCellStorage(); @@ -50,8 +65,8 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade= 1) && // only if at least the first level and (node_data.parent == node || // is the first point of the path @@ -118,14 +133,9 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade= level) + if (checkParentCellRestriction(partition.GetCell(level + 1, to), args...) && + partition.GetHighestDifferentLevel(node, to) >= level) { BOOST_ASSERT_MSG(edge_data.weight > 0, "edge_weight invalid"); const EdgeWeight to_weight = weight + edge_data.weight; @@ -144,11 +154,11 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade auto search(const datafacade::ContiguousInternalMemoryDataFacade &facade, SearchEngineData::MultiLayerDijkstraHeap &forward_heap, SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, - const std::pair &parent_cell, - const std::function &get_query_level) + Args... args) { const auto &partition = facade.GetMultiLevelPartition(); @@ -167,14 +177,14 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade if (!forward_heap.Empty()) { routingStep( - facade, forward_heap, reverse_heap, parent_cell, get_query_level, middle, weight); + facade, forward_heap, reverse_heap, middle, weight, args...); if (!forward_heap.Empty()) forward_heap_min = forward_heap.MinKey(); } if (!reverse_heap.Empty()) { routingStep( - facade, reverse_heap, forward_heap, parent_cell, get_query_level, middle, weight); + facade, reverse_heap, forward_heap, middle, weight, args...); if (!reverse_heap.Empty()) reverse_heap_min = reverse_heap.MinKey(); } @@ -213,7 +223,7 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade // Unpack path std::vector unpacked_path; unpacked_path.reserve(packed_path.size()); - for (auto &packed_edge : packed_path) + for (auto const &packed_edge : packed_path) { NodeID source, target; EdgeID edge_id; @@ -224,7 +234,7 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade } else { // an overlay graph edge - LevelID level = std::min(parent_cell.first, get_query_level(source)); + LevelID level = getNodeQureyLevel(source, args...); CellID parent_cell_id = partition.GetCell(level, source); BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); @@ -241,8 +251,8 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade EdgeWeight subpath_weight; NodeID subpath_source, subpath_target; std::vector subpath; - std::tie(subpath_weight, subpath_source, subpath_target, subpath) = search( - facade, forward_heap, reverse_heap, {sublevel, parent_cell_id}, get_query_level); + std::tie(subpath_weight, subpath_source, subpath_target, subpath) = + search(facade, forward_heap, reverse_heap, sublevel, parent_cell_id); BOOST_ASSERT(!subpath.empty()); BOOST_ASSERT(subpath_source == source); BOOST_ASSERT(subpath_target == target); diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index b307903ee6e..284d9277852 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -166,8 +166,8 @@ InternalRouteResult directShortestPathSearch( EdgeWeight weight; NodeID source_node, target_node; std::vector unpacked_edges; - std::tie(weight, source_node, target_node, unpacked_edges) = mld::search( - facade, forward_heap, reverse_heap, {INVALID_LEVEL_ID, INVALID_CELL_ID}, get_query_level); + std::tie(weight, source_node, target_node, unpacked_edges) = + mld::search(facade, forward_heap, reverse_heap, get_query_level); return extractRoute(facade, weight, source_node, target_node, unpacked_edges, phantom_nodes); } From f1b88adebe0aa573134bc3707dbe03ffe33349a6 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Thu, 16 Mar 2017 07:52:17 +0100 Subject: [PATCH 15/16] Avoid dynamic binding in getNodeQureyLevel --- .../routing_algorithms/routing_base_mld.hpp | 46 ++++++++++++++----- .../direct_shortest_path.cpp | 25 +--------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 4d9759f6a14..7dad4f20165 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -21,17 +21,38 @@ namespace mld namespace { -// Unrestricted search (Args is std::function): -// * use `query_level` closure of partition.GetQueryLevel to find the query level +// Unrestricted search (Args is const PhantomNodes &): +// * use partition.GetQueryLevel to find the node query level based on source and target phantoms // * allow to traverse all cells -using QueryLevelFunction = std::function; -LevelID getNodeQureyLevel(NodeID node, const QueryLevelFunction &functor) { return functor(node); } -bool checkParentCellRestriction(CellID, const QueryLevelFunction &) { return true; } +LevelID getNodeQureyLevel(const partition::MultiLevelPartitionView &partition, + NodeID node, + const PhantomNodes &phantom_nodes) +{ + 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; + }; + return std::min(std::min(level(phantom_nodes.source_phantom.forward_segment_id, + phantom_nodes.target_phantom.forward_segment_id), + level(phantom_nodes.source_phantom.forward_segment_id, + phantom_nodes.target_phantom.reverse_segment_id)), + std::min(level(phantom_nodes.source_phantom.reverse_segment_id, + phantom_nodes.target_phantom.forward_segment_id), + level(phantom_nodes.source_phantom.reverse_segment_id, + phantom_nodes.target_phantom.reverse_segment_id))); +} + +bool checkParentCellRestriction(CellID, const PhantomNodes &) { return true; } // Restricted search (Args is LevelID, CellID): // * use the fixed level for queries // * check if the node cell is the same as the specified parent onr -LevelID getNodeQureyLevel(NodeID, LevelID level, CellID) { return level; } +LevelID getNodeQureyLevel(const partition::MultiLevelPartitionView &, NodeID, LevelID level, CellID) +{ + return level; +} + bool checkParentCellRestriction(CellID cell, LevelID, CellID parent) { return cell == parent; } } @@ -65,7 +86,7 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade= 1) && // only if at least the first level and @@ -155,10 +176,11 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade -auto search(const datafacade::ContiguousInternalMemoryDataFacade &facade, - SearchEngineData::MultiLayerDijkstraHeap &forward_heap, - SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, - Args... args) +std::tuple> +search(const datafacade::ContiguousInternalMemoryDataFacade &facade, + SearchEngineData::MultiLayerDijkstraHeap &forward_heap, + SearchEngineData::MultiLayerDijkstraHeap &reverse_heap, + Args... args) { const auto &partition = facade.GetMultiLevelPartition(); @@ -234,7 +256,7 @@ auto search(const datafacade::ContiguousInternalMemoryDataFacade } else { // an overlay graph edge - LevelID level = getNodeQureyLevel(source, args...); + LevelID level = getNodeQureyLevel(partition, source, args...); CellID parent_cell_id = partition.GetCell(level, source); BOOST_ASSERT(parent_cell_id == partition.GetCell(level, target)); diff --git a/src/engine/routing_algorithms/direct_shortest_path.cpp b/src/engine/routing_algorithms/direct_shortest_path.cpp index 284d9277852..a7af4a16c80 100644 --- a/src/engine/routing_algorithms/direct_shortest_path.cpp +++ b/src/engine/routing_algorithms/direct_shortest_path.cpp @@ -138,36 +138,13 @@ InternalRouteResult directShortestPathSearch( reverse_heap.Clear(); insertNodesInHeaps(forward_heap, reverse_heap, phantom_nodes); - const auto &partition = facade.GetMultiLevelPartition(); - - auto get_query_level = [&partition, &phantom_nodes](const NodeID node) -> LevelID { - auto level = - [&partition](const SegmentID &source, const SegmentID &target, const NodeID node) { - if (source.enabled && target.enabled) - return partition.GetQueryLevel(source.id, target.id, node); - return INVALID_LEVEL_ID; - }; - return std::min(std::min(level(phantom_nodes.source_phantom.forward_segment_id, - phantom_nodes.target_phantom.forward_segment_id, - node), - level(phantom_nodes.source_phantom.forward_segment_id, - phantom_nodes.target_phantom.reverse_segment_id, - node)), - std::min(level(phantom_nodes.source_phantom.reverse_segment_id, - phantom_nodes.target_phantom.forward_segment_id, - node), - level(phantom_nodes.source_phantom.reverse_segment_id, - phantom_nodes.target_phantom.reverse_segment_id, - node))); - }; - // TODO: when structured bindings will be allowed change to // auto [weight, source_node, target_node, unpacked_edges] = ... EdgeWeight weight; NodeID source_node, target_node; std::vector unpacked_edges; std::tie(weight, source_node, target_node, unpacked_edges) = - mld::search(facade, forward_heap, reverse_heap, get_query_level); + mld::search(facade, forward_heap, reverse_heap, phantom_nodes); return extractRoute(facade, weight, source_node, target_node, unpacked_edges, phantom_nodes); } From 79ef204e1f8feb8ad89faabc12585ab7d3f2b5b7 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Wed, 15 Mar 2017 14:36:41 +0100 Subject: [PATCH 16/16] Change edge_id to from_clique_arc in MultiLayerDijkstraHeapData --- .../datafacade/algorithm_datafacade.hpp | 3 ++ .../contiguous_internalmem_datafacade.hpp | 6 ++++ .../routing_algorithms/routing_base_mld.hpp | 33 ++++++++----------- include/engine/search_engine_data.hpp | 6 ++-- 4 files changed, 26 insertions(+), 22 deletions(-) diff --git a/include/engine/datafacade/algorithm_datafacade.hpp b/include/engine/datafacade/algorithm_datafacade.hpp index 5608e925152..7a78779b4b7 100644 --- a/include/engine/datafacade/algorithm_datafacade.hpp +++ b/include/engine/datafacade/algorithm_datafacade.hpp @@ -89,6 +89,9 @@ template <> class AlgorithmDataFacade virtual const partition::MultiLevelPartitionView &GetMultiLevelPartition() const = 0; virtual const partition::CellStorageView &GetCellStorage() const = 0; + + // searches for a specific edge + virtual EdgeID FindEdge(const NodeID from, const NodeID to) const = 0; }; } } diff --git a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp index dac9906ccb9..8185af8e87a 100644 --- a/include/engine/datafacade/contiguous_internalmem_datafacade.hpp +++ b/include/engine/datafacade/contiguous_internalmem_datafacade.hpp @@ -1067,6 +1067,12 @@ class ContiguousInternalMemoryDataFacade { return m_query_graph->GetAdjacentEdgeRange(node); } + + // searches for a specific edge + EdgeID FindEdge(const NodeID from, const NodeID to) const override final + { + return m_query_graph->FindEdge(from, to); + } }; } } diff --git a/include/engine/routing_algorithms/routing_base_mld.hpp b/include/engine/routing_algorithms/routing_base_mld.hpp index 7dad4f20165..8e38d502307 100644 --- a/include/engine/routing_algorithms/routing_base_mld.hpp +++ b/include/engine/routing_algorithms/routing_base_mld.hpp @@ -87,13 +87,8 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade= 1) && // only if at least the first level and - (node_data.parent == node || // is the first point of the path - node_data.edge_id != SPECIAL_EDGEID); // or an overlay entreé point - if (check_overlay_edges) + if (level >= 1 && !forward_heap.GetData(node).from_clique_arc) { if (DIRECTION == FORWARD_DIRECTION) { @@ -109,11 +104,11 @@ void routingStep(const datafacade::ContiguousInternalMemoryDataFacade &fac } // Get packed path as edges {from node ID, to node ID, edge ID} - std::vector> packed_path; + std::vector> packed_path; NodeID current_node = middle, parent_node = forward_heap.GetData(middle).parent; while (parent_node != current_node) { const auto &data = forward_heap.GetData(current_node); - packed_path.push_back(std::make_tuple(parent_node, current_node, data.edge_id)); + packed_path.push_back(std::make_tuple(parent_node, current_node, data.from_clique_arc)); current_node = parent_node; parent_node = forward_heap.GetData(parent_node).parent; } @@ -236,7 +231,7 @@ search(const datafacade::ContiguousInternalMemoryDataFacade &fac while (parent_node != current_node) { const auto &data = reverse_heap.GetData(current_node); - packed_path.push_back(std::make_tuple(current_node, parent_node, data.edge_id)); + packed_path.push_back(std::make_tuple(current_node, parent_node, data.from_clique_arc)); current_node = parent_node; parent_node = reverse_heap.GetData(parent_node).parent; } @@ -248,11 +243,11 @@ search(const datafacade::ContiguousInternalMemoryDataFacade &fac for (auto const &packed_edge : packed_path) { NodeID source, target; - EdgeID edge_id; - std::tie(source, target, edge_id) = packed_edge; - if (edge_id != SPECIAL_EDGEID) + bool overlay_edge; + std::tie(source, target, overlay_edge) = packed_edge; + if (!overlay_edge) { // a base graph edge - unpacked_path.push_back(edge_id); + unpacked_path.push_back(facade.FindEdge(source, target)); } else { // an overlay graph edge diff --git a/include/engine/search_engine_data.hpp b/include/engine/search_engine_data.hpp index f84b921919b..57edf242764 100644 --- a/include/engine/search_engine_data.hpp +++ b/include/engine/search_engine_data.hpp @@ -26,9 +26,9 @@ struct ManyToManyHeapData : HeapData struct MultiLayerDijkstraHeapData : HeapData { - EdgeID edge_id; // edge id if parent -> node is a boundary edge - MultiLayerDijkstraHeapData(NodeID p) : HeapData(p), edge_id(SPECIAL_EDGEID) {} - MultiLayerDijkstraHeapData(NodeID p, EdgeID edge_id) : HeapData(p), edge_id(edge_id) {} + bool from_clique_arc; + MultiLayerDijkstraHeapData(NodeID p) : HeapData(p), from_clique_arc(false) {} + MultiLayerDijkstraHeapData(NodeID p, bool from) : HeapData(p), from_clique_arc(from) {} }; struct SearchEngineData