diff --git a/contractor/contractor.hpp b/contractor/contractor.hpp index 4468f5e4969..cd5f8eecb0f 100644 --- a/contractor/contractor.hpp +++ b/contractor/contractor.hpp @@ -284,7 +284,7 @@ class Contractor ~Contractor() {} - void Run() + void Run( double core_factor = 1.0 ) { // for the preperation we can use a big grain size, which is much faster (probably cache) constexpr size_t InitGrainSize = 100000; @@ -333,9 +333,9 @@ class Contractor << std::flush; bool flushed_contractor = false; - while (number_of_nodes > 2 && number_of_contracted_nodes < number_of_nodes) + while (number_of_nodes > 2 && number_of_contracted_nodes < static_cast(number_of_nodes * core_factor) ) { - if (!flushed_contractor && (number_of_contracted_nodes > (number_of_nodes * 0.65))) + if (!flushed_contractor && (number_of_contracted_nodes > static_cast(number_of_nodes * 0.65 * core_factor))) { DeallocatingVector new_edge_set; // this one is not explicitely // cleared since it goes out of @@ -524,7 +524,7 @@ class Contractor // unsigned quaddegree = 0; // // for(unsigned i = 0; i < remaining_nodes.size(); ++i) { - // unsigned degree = contractor_graph->EndEdges(remaining_nodes[i].first) + // unsigned degree = contractor_graph->EndEdges(remaining_nodes[i].id) // - // contractor_graph->BeginEdges(remaining_nodes[i].first); // if(degree > maxdegree) @@ -546,6 +546,8 @@ class Contractor p.printStatus(number_of_contracted_nodes); } + SimpleLogger().Write() << "[core] " << remaining_nodes.size() << " nodes " << contractor_graph->GetNumberOfEdges() << " edges." << std::endl; + thread_data_list.data.clear(); } diff --git a/contractor/contractor_options.cpp b/contractor/contractor_options.cpp index eaa1ba997f8..cd304bc3c77 100644 --- a/contractor/contractor_options.cpp +++ b/contractor/contractor_options.cpp @@ -56,7 +56,11 @@ ContractorOptions::ParseArguments(int argc, char *argv[], ContractorConfig &cont "Path to LUA routing profile")( "threads,t", boost::program_options::value(&contractor_config.requested_num_threads) ->default_value(tbb::task_scheduler_init::default_num_threads()), - "Number of threads to use"); + "Number of threads to use")( + "core,k", boost::program_options::value(&contractor_config.core_factor) + ->default_value(1.0),"Percentage of the graph (in vertices) to contract [0.1]"); + + // hidden options, will be allowed both on command line and in config file, but will not be // shown to the user diff --git a/contractor/contractor_options.hpp b/contractor/contractor_options.hpp index 87541af9d88..248659527c8 100644 --- a/contractor/contractor_options.hpp +++ b/contractor/contractor_options.hpp @@ -56,6 +56,12 @@ struct ContractorConfig std::string rtree_leafs_output_path; unsigned requested_num_threads; + + //A percentage of vertices that will be contracted for the hierarchy. + //Offers a trade-off between preprocessing and query time. + //The remaining vertices form the core of the hierarchy + //(e.g. 0.8 contracts 80 percent of the hierarchy, leaving a core of 20%) + double core_factor; }; struct ContractorOptions diff --git a/contractor/processing_chain.cpp b/contractor/processing_chain.cpp index 7c6db539025..39005462d6a 100644 --- a/contractor/processing_chain.cpp +++ b/contractor/processing_chain.cpp @@ -414,7 +414,7 @@ void Prepare::ContractGraph(const unsigned max_edge_id, DeallocatingVector& contracted_edge_list) { Contractor contractor(max_edge_id + 1, edge_based_edge_list); - contractor.Run(); + contractor.Run(config.core_factor); contractor.GetEdges(contracted_edge_list); } diff --git a/data_structures/binary_heap.hpp b/data_structures/binary_heap.hpp index a23a6b0f516..b237486aa7a 100644 --- a/data_structures/binary_heap.hpp +++ b/data_structures/binary_heap.hpp @@ -189,6 +189,11 @@ class BinaryHeap return inserted_nodes[heap[1].index].node; } + Weight MinKey() const { + BOOST_ASSERT(heap.size() > 1); + return heap[1].weight; + } + NodeID DeleteMin() { BOOST_ASSERT(heap.size() > 1); diff --git a/data_structures/search_engine.hpp b/data_structures/search_engine.hpp index 7e47b1f5df1..5af734e77f9 100644 --- a/data_structures/search_engine.hpp +++ b/data_structures/search_engine.hpp @@ -33,6 +33,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../routing_algorithms/many_to_many.hpp" #include "../routing_algorithms/map_matching.hpp" #include "../routing_algorithms/shortest_path.hpp" +#include "../routing_algorithms/direct_shortest_path.hpp" #include @@ -44,6 +45,7 @@ template class SearchEngine public: ShortestPathRouting shortest_path; + DirectShortestPathRouting direct_shortest_path; AlternativeRouting alternative_path; ManyToManyRouting distance_table; MapMatching map_matching; @@ -51,6 +53,7 @@ template class SearchEngine explicit SearchEngine(DataFacadeT *facade) : facade(facade), shortest_path(facade, engine_working_data), + direct_shortest_path(facade, engine_working_data), alternative_path(facade, engine_working_data), distance_table(facade, engine_working_data), map_matching(facade, engine_working_data) diff --git a/features/options/prepare/help.feature b/features/options/prepare/help.feature index dae522d5614..59265e968ec 100644 --- a/features/options/prepare/help.feature +++ b/features/options/prepare/help.feature @@ -16,7 +16,8 @@ Feature: osrm-prepare command line options: help And stdout should contain "--restrictions" And stdout should contain "--profile" And stdout should contain "--threads" - And stdout should contain 15 lines + And stdout should contain "--core" + And stdout should contain 17 lines And it should exit with code 1 Scenario: osrm-prepare - Help, short @@ -31,7 +32,8 @@ Feature: osrm-prepare command line options: help And stdout should contain "--restrictions" And stdout should contain "--profile" And stdout should contain "--threads" - And stdout should contain 15 lines + And stdout should contain "--core" + And stdout should contain 17 lines And it should exit with code 0 Scenario: osrm-prepare - Help, long @@ -46,5 +48,6 @@ Feature: osrm-prepare command line options: help And stdout should contain "--restrictions" And stdout should contain "--profile" And stdout should contain "--threads" - And stdout should contain 15 lines + And stdout should contain "--core" + And stdout should contain 17 lines And it should exit with code 0 diff --git a/plugins/viaroute.hpp b/plugins/viaroute.hpp index 335bda1afbd..ceaeebf2851 100644 --- a/plugins/viaroute.hpp +++ b/plugins/viaroute.hpp @@ -39,6 +39,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../util/json_renderer.hpp" #include "../util/make_unique.hpp" #include "../util/simple_logger.hpp" +#include "../util/timing_util.hpp" #include @@ -153,10 +154,18 @@ template class ViaRoutePlugin final : public BasePlugin }; osrm::for_each_pair(phantom_node_pair_list, build_phantom_pairs); - if (route_parameters.alternate_route && 1 == raw_route.segment_end_coordinates.size()) + if (1 == raw_route.segment_end_coordinates.size()) { - search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), - raw_route); + if (route_parameters.alternate_route) + { + search_engine_ptr->alternative_path(raw_route.segment_end_coordinates.front(), + raw_route); + } + else + { + search_engine_ptr->direct_shortest_path(raw_route.segment_end_coordinates, + route_parameters.uturns, raw_route); + } } else { diff --git a/routing_algorithms/direct_shortest_path.hpp b/routing_algorithms/direct_shortest_path.hpp new file mode 100644 index 00000000000..45515f2616a --- /dev/null +++ b/routing_algorithms/direct_shortest_path.hpp @@ -0,0 +1,165 @@ +/* + +Copyright (c) 2015, Project OSRM contributors +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +Redistributions of source code must retain the above copyright notice, this list +of conditions and the following disclaimer. +Redistributions in binary form must reproduce the above copyright notice, this +list of conditions and the following disclaimer in the documentation and/or +other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*/ + +#ifndef DIRECT_SHORTEST_PATH_HPP +#define DIRECT_SHORTEST_PATH_HPP + +#include + +#include "routing_base.hpp" +#include "../data_structures/search_engine_data.hpp" +#include "../util/integer_range.hpp" +#include "../util/timing_util.hpp" +#include "../typedefs.h" + +/// 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 +/// by the previous route. +/// This variation is only an optimazation for graphs with slow queries, for example +/// not fully contracted graphs. +template +class DirectShortestPathRouting final + : public BasicRoutingInterface> +{ + using super = BasicRoutingInterface>; + using QueryHeap = SearchEngineData::QueryHeap; + SearchEngineData &engine_working_data; + + public: + DirectShortestPathRouting(DataFacadeT *facade, SearchEngineData &engine_working_data) + : super(facade), engine_working_data(engine_working_data) + { + } + + ~DirectShortestPathRouting() {} + + void operator()(const std::vector &phantom_nodes_vector, + const std::vector &uturn_indicators, + InternalRouteResult &raw_route_data) const + { + engine_working_data.InitializeOrClearFirstThreadLocalStorage( + super::facade->GetNumberOfNodes()); + + QueryHeap &forward_heap = *(engine_working_data.forward_heap_1); + QueryHeap &reverse_heap = *(engine_working_data.reverse_heap_1); + + // Get distance to next pair of target nodes. + BOOST_ASSERT_MSG(1 == phantom_nodes_vector.size(), + "Direct Shortest Path Query only accepts a single source and target pair. Multiple ones have been specified."); + + const auto& phantom_node_pair = phantom_nodes_vector.front(); + + forward_heap.Clear(); + reverse_heap.Clear(); + int distance = INVALID_EDGE_WEIGHT; + NodeID middle = SPECIAL_NODEID; + + const EdgeWeight min_edge_offset = + std::min(-phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), + -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset()); + + // insert new starting nodes into forward heap, adjusted by previous distances. + if (phantom_node_pair.source_phantom.forward_node_id != SPECIAL_NODEID) + { + forward_heap.Insert( + phantom_node_pair.source_phantom.forward_node_id, + -phantom_node_pair.source_phantom.GetForwardWeightPlusOffset(), + phantom_node_pair.source_phantom.forward_node_id); + } + if ( phantom_node_pair.source_phantom.reverse_node_id != SPECIAL_NODEID) + { + forward_heap.Insert( + phantom_node_pair.source_phantom.reverse_node_id, + -phantom_node_pair.source_phantom.GetReverseWeightPlusOffset(), + phantom_node_pair.source_phantom.reverse_node_id); + } + + // insert new backward nodes into backward heap, unadjusted. + if (phantom_node_pair.target_phantom.forward_node_id != SPECIAL_NODEID) + { + reverse_heap.Insert(phantom_node_pair.target_phantom.forward_node_id, + phantom_node_pair.target_phantom.GetForwardWeightPlusOffset(), + phantom_node_pair.target_phantom.forward_node_id); + } + + if (phantom_node_pair.target_phantom.reverse_node_id != SPECIAL_NODEID) + { + reverse_heap.Insert(phantom_node_pair.target_phantom.reverse_node_id, + phantom_node_pair.target_phantom.GetReverseWeightPlusOffset(), + phantom_node_pair.target_phantom.reverse_node_id); + } + + // run two-Target Dijkstra routing step. + while (0 < (forward_heap.Size() + reverse_heap.Size()) ) + { + if (!forward_heap.Empty()) + { + super::RoutingStep(forward_heap, reverse_heap, &middle, &distance, + min_edge_offset, true); + } + if (!reverse_heap.Empty()) + { + super::RoutingStep(reverse_heap, forward_heap, &middle, &distance, + min_edge_offset, false); + } + } + + // No path found for both target nodes? + if ((INVALID_EDGE_WEIGHT == distance)) + { + raw_route_data.shortest_path_length = INVALID_EDGE_WEIGHT; + raw_route_data.alternative_path_length = INVALID_EDGE_WEIGHT; + return; + } + + // Was a paths over one of the forward/reverse nodes not found? + BOOST_ASSERT_MSG((SPECIAL_NODEID == middle || INVALID_EDGE_WEIGHT != distance), + "no path found"); + + // Unpack paths if they exist + std::vector packed_leg; + if (INVALID_EDGE_WEIGHT != distance) + { + super::RetrievePackedPathFromHeap(forward_heap, reverse_heap, middle, packed_leg); + + BOOST_ASSERT_MSG(!packed_leg.empty(), "packed path empty"); + + raw_route_data.unpacked_path_segments.resize(1); + raw_route_data.source_traversed_in_reverse.push_back( + (packed_leg.front() != phantom_node_pair.source_phantom.forward_node_id)); + raw_route_data.target_traversed_in_reverse.push_back( + (packed_leg.back() != phantom_node_pair.target_phantom.forward_node_id)); + + super::UnpackPath(packed_leg, phantom_node_pair, raw_route_data.unpacked_path_segments.front()); + } + + raw_route_data.shortest_path_length = distance; + } +}; + +#endif /* DIRECT_SHORTEST_PATH_HPP */ diff --git a/unit_tests/data_structures/binary_heap.cpp b/unit_tests/data_structures/binary_heap.cpp index d039710c032..300d8984ed7 100644 --- a/unit_tests/data_structures/binary_heap.cpp +++ b/unit_tests/data_structures/binary_heap.cpp @@ -165,6 +165,7 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(decrease_key_test, T, storage_types, RandomData { heap.DecreaseKey(id, weights[id]); BOOST_CHECK_EQUAL(heap.Min(), min_id); + BOOST_CHECK_EQUAL(heap.MinKey(), min_weight); weights[id]--; } @@ -172,6 +173,7 @@ BOOST_FIXTURE_TEST_CASE_TEMPLATE(decrease_key_test, T, storage_types, RandomData weights[id] -= 2; heap.DecreaseKey(id, weights[id]); BOOST_CHECK_EQUAL(heap.Min(), id); + BOOST_CHECK_EQUAL(heap.MinKey(), weights[id]); } }