diff --git a/CMakeLists.txt b/CMakeLists.txt index 04cbf63ec..a474b7692 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ add_library(PHANTOMNODE OBJECT DataStructures/phantom_node.cpp) set(ExtractorSources extractor.cpp ${ExtractorGlob}) add_executable(osrm-extract ${ExtractorSources} $ $ $ $ $) -file(GLOB PrepareGlob Contractor/*.cpp DataStructures/HilbertValue.cpp DataStructures/restriction_map.cpp Util/compute_angle.cpp) +file(GLOB PrepareGlob contractor/*.cpp DataStructures/HilbertValue.cpp DataStructures/restriction_map.cpp Util/compute_angle.cpp) set(PrepareSources prepare.cpp ${PrepareGlob}) add_executable(osrm-prepare ${PrepareSources} $ $ $ $ $) diff --git a/contractor/contractor.hpp b/contractor/contractor.hpp new file mode 100644 index 000000000..257dae656 --- /dev/null +++ b/contractor/contractor.hpp @@ -0,0 +1,973 @@ +/* + +Copyright (c) 2014, Project OSRM, Dennis Luxen, others +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 CONTRACTOR_HPP +#define CONTRACTOR_HPP + +#include "../DataStructures/BinaryHeap.h" +#include "../DataStructures/DeallocatingVector.h" +#include "../DataStructures/DynamicGraph.h" +#include "../DataStructures/Percent.h" +#include "../DataStructures/QueryEdge.h" +#include "../DataStructures/XORFastHash.h" +#include "../DataStructures/XORFastHashStorage.h" +#include "../Util/integer_range.hpp" +#include "../Util/simple_logger.hpp" +#include "../Util/StringUtil.h" +#include "../Util/TimingUtil.h" +#include "../typedefs.h" + +#include + +#include + +#include +#include +#include + +#include +#include +#include + +class Contractor +{ + + private: + struct ContractorEdgeData + { + ContractorEdgeData() + : distance(0), id(0), originalEdges(0), shortcut(0), forward(0), backward(0), + is_original_via_node_ID(false) + { + } + ContractorEdgeData(unsigned distance, + unsigned original_edges, + unsigned id, + bool shortcut, + bool forward, + bool backward) + : distance(distance), id(id), + originalEdges(std::min((unsigned)1 << 28, original_edges)), shortcut(shortcut), + forward(forward), backward(backward), is_original_via_node_ID(false) + { + } + unsigned distance; + unsigned id; + unsigned originalEdges : 28; + bool shortcut : 1; + bool forward : 1; + bool backward : 1; + bool is_original_via_node_ID : 1; + } data; + + struct ContractorHeapData + { + short hop; + bool target; + ContractorHeapData() : hop(0), target(false) {} + ContractorHeapData(short h, bool t) : hop(h), target(t) {} + }; + + using ContractorGraph = DynamicGraph; + // using ContractorHeap = BinaryHeap + // >; + using ContractorHeap = BinaryHeap>; + using ContractorEdge = ContractorGraph::InputEdge; + + struct ContractorThreadData + { + ContractorHeap heap; + std::vector inserted_edges; + std::vector neighbours; + explicit ContractorThreadData(NodeID nodes) : heap(nodes) {} + }; + + struct NodePriorityData + { + int depth; + NodePriorityData() : depth(0) {} + }; + + struct ContractionStats + { + int edges_deleted_count; + int edges_added_count; + int original_edges_deleted_count; + int original_edges_added_count; + ContractionStats() + : edges_deleted_count(0), edges_added_count(0), original_edges_deleted_count(0), + original_edges_added_count(0) + { + } + }; + + struct RemainingNodeData + { + RemainingNodeData() : id(0), is_independent(false) {} + NodeID id : 31; + bool is_independent : 1; + }; + + + struct ThreadDataContainer + { + explicit ThreadDataContainer(int number_of_nodes) : number_of_nodes(number_of_nodes) {} + + inline ContractorThreadData* getThreadData() + { + bool exists = false; + auto& ref = data.local(exists); + if (!exists) + { + ref = std::make_shared(number_of_nodes); + } + + return ref.get(); + } + + int number_of_nodes; + using EnumerableThreadData = tbb::enumerable_thread_specific>; + EnumerableThreadData data; + }; + + public: + template Contractor(int nodes, ContainerT &input_edge_list) + { + std::vector edges; + edges.reserve(input_edge_list.size() * 2); + + const auto dend = input_edge_list.dend(); + for (auto diter = input_edge_list.dbegin(); diter != dend; ++diter) + { + BOOST_ASSERT_MSG(static_cast(std::max(diter->weight, 1)) > 0, "edge distance < 1"); +#ifndef NDEBUG + if (static_cast(std::max(diter->weight, 1)) > 24 * 60 * 60 * 10) + { + SimpleLogger().Write(logWARNING) << "Edge weight large -> " + << static_cast(std::max(diter->weight, 1)); + } +#endif + edges.emplace_back(diter->source, diter->target, + static_cast(std::max(diter->weight, 1)), + 1, + diter->edge_id, + false, + diter->forward ? true : false, + diter->backward ? true : false); + + edges.emplace_back(diter->target, diter->source, + static_cast(std::max(diter->weight, 1)), + 1, + diter->edge_id, + false, + diter->backward ? true : false, + diter->forward ? true : false); + } + // clear input vector + input_edge_list.clear(); + edges.shrink_to_fit(); + + tbb::parallel_sort(edges.begin(), edges.end()); + NodeID edge = 0; + for (NodeID i = 0; i < edges.size();) + { + const NodeID source = edges[i].source; + const NodeID target = edges[i].target; + const NodeID id = edges[i].data.id; + // remove eigenloops + if (source == target) + { + ++i; + continue; + } + ContractorEdge forward_edge; + ContractorEdge reverse_edge; + forward_edge.source = reverse_edge.source = source; + forward_edge.target = reverse_edge.target = target; + forward_edge.data.forward = reverse_edge.data.backward = true; + forward_edge.data.backward = reverse_edge.data.forward = false; + forward_edge.data.shortcut = reverse_edge.data.shortcut = false; + forward_edge.data.id = reverse_edge.data.id = id; + forward_edge.data.originalEdges = reverse_edge.data.originalEdges = 1; + forward_edge.data.distance = reverse_edge.data.distance = + std::numeric_limits::max(); + // remove parallel edges + while (i < edges.size() && edges[i].source == source && edges[i].target == target) + { + if (edges[i].data.forward) + { + forward_edge.data.distance = + std::min(edges[i].data.distance, forward_edge.data.distance); + } + if (edges[i].data.backward) + { + reverse_edge.data.distance = + std::min(edges[i].data.distance, reverse_edge.data.distance); + } + ++i; + } + // merge edges (s,t) and (t,s) into bidirectional edge + if (forward_edge.data.distance == reverse_edge.data.distance) + { + if ((int)forward_edge.data.distance != std::numeric_limits::max()) + { + forward_edge.data.backward = true; + edges[edge++] = forward_edge; + } + } + else + { // insert seperate edges + if (((int)forward_edge.data.distance) != std::numeric_limits::max()) + { + edges[edge++] = forward_edge; + } + if ((int)reverse_edge.data.distance != std::numeric_limits::max()) + { + edges[edge++] = reverse_edge; + } + } + } + std::cout << "merged " << edges.size() - edge << " edges out of " << edges.size() + << std::endl; + edges.resize(edge); + contractor_graph = std::make_shared(nodes, edges); + edges.clear(); + edges.shrink_to_fit(); + + BOOST_ASSERT(0 == edges.capacity()); + // unsigned maxdegree = 0; + // NodeID highestNode = 0; + // + // for(unsigned i = 0; i < contractor_graph->GetNumberOfNodes(); ++i) { + // unsigned degree = contractor_graph->EndEdges(i) - + // contractor_graph->BeginEdges(i); + // if(degree > maxdegree) { + // maxdegree = degree; + // highestNode = i; + // } + // } + // + // SimpleLogger().Write() << "edges at node with id " << highestNode << " has degree + // " << maxdegree; + // for(unsigned i = contractor_graph->BeginEdges(highestNode); i < + // contractor_graph->EndEdges(highestNode); ++i) { + // SimpleLogger().Write() << " ->(" << highestNode << "," << + // contractor_graph->GetTarget(i) + // << "); via: " << contractor_graph->GetEdgeData(i).via; + // } + + std::cout << "contractor finished initalization" << std::endl; + } + + ~Contractor() { } + + void Run() + { + // for the preperation we can use a big grain size, which is much faster (probably cache) + constexpr size_t InitGrainSize = 100000; + constexpr size_t PQGrainSize = 100000; + // auto_partitioner will automatically increase the blocksize if we have + // a lot of data. It is *important* for the last loop iterations + // (which have a very small dataset) that it is devisible. + constexpr size_t IndependentGrainSize = 1; + constexpr size_t ContractGrainSize = 1; + constexpr size_t NeighboursGrainSize = 1; + constexpr size_t DeleteGrainSize = 1; + + const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes(); + Percent p(number_of_nodes); + + ThreadDataContainer thread_data_list(number_of_nodes); + + NodeID number_of_contracted_nodes = 0; + std::vector remaining_nodes(number_of_nodes); + std::vector node_priorities(number_of_nodes); + std::vector node_data(number_of_nodes); + + + // initialize priorities in parallel + tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, InitGrainSize), + [&remaining_nodes](const tbb::blocked_range& range) + { + for (int x = range.begin(); x != range.end(); ++x) + { + remaining_nodes[x].id = x; + } + } + ); + + + std::cout << "initializing elimination PQ ..." << std::flush; + tbb::parallel_for(tbb::blocked_range(0, number_of_nodes, PQGrainSize), + [this, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range& range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int x = range.begin(); x != range.end(); ++x) + { + node_priorities[x] = this->EvaluateNodePriority(data, &node_data[x], x); + } + } + ); + std::cout << "ok" << std::endl << "preprocessing " << number_of_nodes << " nodes ..." + << std::flush; + + bool flushed_contractor = false; + while (number_of_nodes > 2 && number_of_contracted_nodes < number_of_nodes) + { + if (!flushed_contractor && (number_of_contracted_nodes > (number_of_nodes * 0.65))) + { + DeallocatingVector new_edge_set; // this one is not explicitely + // cleared since it goes out of + // scope anywa + std::cout << " [flush " << number_of_contracted_nodes << " nodes] " << std::flush; + + // Delete old heap data to free memory that we need for the coming operations + thread_data_list.data.clear(); + + // Create new priority array + std::vector new_node_priority(remaining_nodes.size()); + // this map gives the old IDs from the new ones, necessary to get a consistent graph + // at the end of contraction + orig_node_id_to_new_id_map.resize(remaining_nodes.size()); + // this map gives the new IDs from the old ones, necessary to remap targets from the + // remaining graph + std::vector new_node_id_from_orig_id_map(number_of_nodes, UINT_MAX); + + // build forward and backward renumbering map and remap ids in remaining_nodes and + // Priorities. + for (const auto new_node_id : osrm::irange(0, remaining_nodes.size())) + { + // create renumbering maps in both directions + orig_node_id_to_new_id_map[new_node_id] = remaining_nodes[new_node_id].id; + new_node_id_from_orig_id_map[remaining_nodes[new_node_id].id] = new_node_id; + new_node_priority[new_node_id] = + node_priorities[remaining_nodes[new_node_id].id]; + remaining_nodes[new_node_id].id = new_node_id; + } + // walk over all nodes + for (const auto i : osrm::irange(0, contractor_graph->GetNumberOfNodes())) + { + const NodeID source = i; + for (auto current_edge : contractor_graph->GetAdjacentEdgeRange(source)) + { + ContractorGraph::EdgeData &data = + contractor_graph->GetEdgeData(current_edge); + const NodeID target = contractor_graph->GetTarget(current_edge); + if (SPECIAL_NODEID == new_node_id_from_orig_id_map[i]) + { + external_edge_list.push_back({source, target, data}); + } + else + { + // node is not yet contracted. + // add (renumbered) outgoing edges to new DynamicGraph. + ContractorEdge new_edge = { + new_node_id_from_orig_id_map[source], + new_node_id_from_orig_id_map[target], + data + }; + + new_edge.data.is_original_via_node_ID = true; + BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[source], + "new source id not resolveable"); + BOOST_ASSERT_MSG(UINT_MAX != new_node_id_from_orig_id_map[target], + "new target id not resolveable"); + new_edge_set.push_back(new_edge); + } + } + } + + // Delete map from old NodeIDs to new ones. + new_node_id_from_orig_id_map.clear(); + new_node_id_from_orig_id_map.shrink_to_fit(); + + // Replace old priorities array by new one + node_priorities.swap(new_node_priority); + // Delete old node_priorities vector + new_node_priority.clear(); + new_node_priority.shrink_to_fit(); + // old Graph is removed + contractor_graph.reset(); + + // create new graph + std::sort(new_edge_set.begin(), new_edge_set.end()); + contractor_graph = + std::make_shared(remaining_nodes.size(), new_edge_set); + + new_edge_set.clear(); + flushed_contractor = true; + + // INFO: MAKE SURE THIS IS THE LAST OPERATION OF THE FLUSH! + // reinitialize heaps and ThreadData objects with appropriate size + thread_data_list.number_of_nodes = contractor_graph->GetNumberOfNodes(); + } + + const int last = (int)remaining_nodes.size(); + tbb::parallel_for(tbb::blocked_range(0, last, IndependentGrainSize), + [this, &node_priorities, &remaining_nodes, &thread_data_list](const tbb::blocked_range& range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + // determine independent node set + for (int i = range.begin(); i != range.end(); ++i) + { + const NodeID node = remaining_nodes[i].id; + remaining_nodes[i].is_independent = + this->IsNodeIndependent(node_priorities, data, node); + } + } + ); + + const auto first = stable_partition(remaining_nodes.begin(), + remaining_nodes.end(), + [](RemainingNodeData node_data) + { return !node_data.is_independent; }); + const int first_independent_node = static_cast(first - remaining_nodes.begin()); + + // contract independent nodes + tbb::parallel_for(tbb::blocked_range(first_independent_node, last, ContractGrainSize), + [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range& range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int position = range.begin(); position != range.end(); ++position) + { + const NodeID x = remaining_nodes[position].id; + this->ContractNode(data, x); + } + } + ); + // make sure we really sort each block + tbb::parallel_for(thread_data_list.data.range(), + [&](const ThreadDataContainer::EnumerableThreadData::range_type& range) + { + for (auto& data : range) + std::sort(data->inserted_edges.begin(), + data->inserted_edges.end()); + } + ); + tbb::parallel_for(tbb::blocked_range(first_independent_node, last, DeleteGrainSize), + [this, &remaining_nodes, &thread_data_list](const tbb::blocked_range& range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int position = range.begin(); position != range.end(); ++position) + { + const NodeID x = remaining_nodes[position].id; + this->DeleteIncomingEdges(data, x); + } + } + ); + + // insert new edges + for (auto& data : thread_data_list.data) + { + for (const ContractorEdge &edge : data->inserted_edges) + { + const EdgeID current_edge_ID = contractor_graph->FindEdge(edge.source, edge.target); + if (current_edge_ID < contractor_graph->EndEdges(edge.source)) + { + ContractorGraph::EdgeData ¤t_data = + contractor_graph->GetEdgeData(current_edge_ID); + if (current_data.shortcut && edge.data.forward == current_data.forward && + edge.data.backward == current_data.backward && + edge.data.distance < current_data.distance) + { + // found a duplicate edge with smaller weight, update it. + current_data = edge.data; + continue; + } + } + contractor_graph->InsertEdge(edge.source, edge.target, edge.data); + } + data->inserted_edges.clear(); + } + + tbb::parallel_for(tbb::blocked_range(first_independent_node, last, NeighboursGrainSize), + [this, &remaining_nodes, &node_priorities, &node_data, &thread_data_list](const tbb::blocked_range& range) + { + ContractorThreadData *data = thread_data_list.getThreadData(); + for (int position = range.begin(); position != range.end(); ++position) + { + NodeID x = remaining_nodes[position].id; + this->UpdateNodeNeighbours(node_priorities, node_data, data, x); + } + } + ); + + // remove contracted nodes from the pool + number_of_contracted_nodes += last - first_independent_node; + remaining_nodes.resize(first_independent_node); + remaining_nodes.shrink_to_fit(); + // unsigned maxdegree = 0; + // unsigned avgdegree = 0; + // unsigned mindegree = UINT_MAX; + // unsigned quaddegree = 0; + // + // for(unsigned i = 0; i < remaining_nodes.size(); ++i) { + // unsigned degree = contractor_graph->EndEdges(remaining_nodes[i].first) + // - + // contractor_graph->BeginEdges(remaining_nodes[i].first); + // if(degree > maxdegree) + // maxdegree = degree; + // if(degree < mindegree) + // mindegree = degree; + // + // avgdegree += degree; + // quaddegree += (degree*degree); + // } + // + // avgdegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() ); + // quaddegree /= std::max((unsigned)1,(unsigned)remaining_nodes.size() ); + // + // SimpleLogger().Write() << "rest: " << remaining_nodes.size() << ", max: " + // << maxdegree << ", min: " << mindegree << ", avg: " << avgdegree << ", + // quad: " << quaddegree; + + p.printStatus(number_of_contracted_nodes); + } + + thread_data_list.data.clear(); + } + + template inline void GetEdges(DeallocatingVector &edges) + { + Percent p(contractor_graph->GetNumberOfNodes()); + SimpleLogger().Write() << "Getting edges of minimized graph"; + const NodeID number_of_nodes = contractor_graph->GetNumberOfNodes(); + if (contractor_graph->GetNumberOfNodes()) + { + Edge new_edge; + for (const auto node : osrm::irange(0u, number_of_nodes)) + { + p.printStatus(node); + for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const NodeID target = contractor_graph->GetTarget(edge); + const ContractorGraph::EdgeData &data = contractor_graph->GetEdgeData(edge); + if (!orig_node_id_to_new_id_map.empty()) + { + new_edge.source = orig_node_id_to_new_id_map[node]; + new_edge.target = orig_node_id_to_new_id_map[target]; + } + else + { + new_edge.source = node; + new_edge.target = target; + } + BOOST_ASSERT_MSG(UINT_MAX != new_edge.source, "Source id invalid"); + BOOST_ASSERT_MSG(UINT_MAX != new_edge.target, "Target id invalid"); + new_edge.data.distance = data.distance; + new_edge.data.shortcut = data.shortcut; + if (!data.is_original_via_node_ID && !orig_node_id_to_new_id_map.empty()) + { + new_edge.data.id = orig_node_id_to_new_id_map[data.id]; + } + else + { + new_edge.data.id = data.id; + } + BOOST_ASSERT_MSG(new_edge.data.id != INT_MAX, // 2^31 + "edge id invalid"); + new_edge.data.forward = data.forward; + new_edge.data.backward = data.backward; + edges.push_back(new_edge); + } + } + } + contractor_graph.reset(); + orig_node_id_to_new_id_map.clear(); + orig_node_id_to_new_id_map.shrink_to_fit(); + + BOOST_ASSERT(0 == orig_node_id_to_new_id_map.capacity()); + + edges.append(external_edge_list.begin(), external_edge_list.end()); + external_edge_list.clear(); + } + + private: + inline void Dijkstra(const int max_distance, + const unsigned number_of_targets, + const int maxNodes, + ContractorThreadData *const data, + const NodeID middleNode) + { + + ContractorHeap &heap = data->heap; + + int nodes = 0; + unsigned number_of_targets_found = 0; + while (!heap.Empty()) + { + const NodeID node = heap.DeleteMin(); + const int distance = heap.GetKey(node); + const short current_hop = heap.GetData(node).hop + 1; + + if (++nodes > maxNodes) + { + return; + } + if (distance > max_distance) + { + return; + } + + // Destination settled? + if (heap.GetData(node).target) + { + ++number_of_targets_found; + if (number_of_targets_found >= number_of_targets) + { + return; + } + } + + // iterate over all edges of node + for (auto edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &data = contractor_graph->GetEdgeData(edge); + if (!data.forward) + { + continue; + } + const NodeID to = contractor_graph->GetTarget(edge); + if (middleNode == to) + { + continue; + } + const int to_distance = distance + data.distance; + + // New Node discovered -> Add to Heap + Node Info Storage + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_distance, ContractorHeapData(current_hop, false)); + } + // Found a shorter Path -> Update distance + else if (to_distance < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_distance); + heap.GetData(to).hop = current_hop; + } + } + } + } + + inline float EvaluateNodePriority(ContractorThreadData *const data, + NodePriorityData *const node_data, + const NodeID node) + { + ContractionStats stats; + + // perform simulated contraction + ContractNode(data, node, &stats); + + // Result will contain the priority + float result; + if (0 == (stats.edges_deleted_count * stats.original_edges_deleted_count)) + { + result = 1.f * node_data->depth; + } + else + { + result = 2.f * (((float)stats.edges_added_count) / stats.edges_deleted_count) + + 4.f * (((float)stats.original_edges_added_count) / + stats.original_edges_deleted_count) + + 1.f * node_data->depth; + } + BOOST_ASSERT(result >= 0); + return result; + } + + template + inline bool + ContractNode(ContractorThreadData *data, const NodeID node, ContractionStats *stats = nullptr) + { + ContractorHeap &heap = data->heap; + int inserted_edges_size = data->inserted_edges.size(); + std::vector &inserted_edges = data->inserted_edges; + + for (auto in_edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &in_data = contractor_graph->GetEdgeData(in_edge); + const NodeID source = contractor_graph->GetTarget(in_edge); + if (RUNSIMULATION) + { + BOOST_ASSERT(stats != nullptr); + ++stats->edges_deleted_count; + stats->original_edges_deleted_count += in_data.originalEdges; + } + if (!in_data.backward) + { + continue; + } + + heap.Clear(); + heap.Insert(source, 0, ContractorHeapData()); + int max_distance = 0; + unsigned number_of_targets = 0; + + for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge); + if (!out_data.forward) + { + continue; + } + const NodeID target = contractor_graph->GetTarget(out_edge); + const int path_distance = in_data.distance + out_data.distance; + max_distance = std::max(max_distance, path_distance); + if (!heap.WasInserted(target)) + { + heap.Insert(target, INT_MAX, ContractorHeapData(0, true)); + ++number_of_targets; + } + } + + if (RUNSIMULATION) + { + Dijkstra(max_distance, number_of_targets, 1000, data, node); + } + else + { + Dijkstra(max_distance, number_of_targets, 2000, data, node); + } + for (auto out_edge : contractor_graph->GetAdjacentEdgeRange(node)) + { + const ContractorEdgeData &out_data = contractor_graph->GetEdgeData(out_edge); + if (!out_data.forward) + { + continue; + } + const NodeID target = contractor_graph->GetTarget(out_edge); + const int path_distance = in_data.distance + out_data.distance; + const int distance = heap.GetKey(target); + if (path_distance < distance) + { + if (RUNSIMULATION) + { + BOOST_ASSERT(stats != nullptr); + stats->edges_added_count += 2; + stats->original_edges_added_count += + 2 * (out_data.originalEdges + in_data.originalEdges); + } + else + { + inserted_edges.emplace_back(source, target, path_distance, + out_data.originalEdges + in_data.originalEdges, + node, + true, + true, + false); + + inserted_edges.emplace_back(target, source, path_distance, + out_data.originalEdges + in_data.originalEdges, + node, + true, + false, + true); + } + } + } + } + if (!RUNSIMULATION) + { + int iend = inserted_edges.size(); + for (int i = inserted_edges_size; i < iend; ++i) + { + bool found = false; + for (int other = i + 1; other < iend; ++other) + { + if (inserted_edges[other].source != inserted_edges[i].source) + { + continue; + } + if (inserted_edges[other].target != inserted_edges[i].target) + { + continue; + } + if (inserted_edges[other].data.distance != inserted_edges[i].data.distance) + { + continue; + } + if (inserted_edges[other].data.shortcut != inserted_edges[i].data.shortcut) + { + continue; + } + inserted_edges[other].data.forward |= inserted_edges[i].data.forward; + inserted_edges[other].data.backward |= inserted_edges[i].data.backward; + found = true; + break; + } + if (!found) + { + inserted_edges[inserted_edges_size++] = inserted_edges[i]; + } + } + inserted_edges.resize(inserted_edges_size); + } + return true; + } + + inline void DeleteIncomingEdges(ContractorThreadData *data, const NodeID node) + { + std::vector &neighbours = data->neighbours; + neighbours.clear(); + + // find all neighbours + for (auto e : contractor_graph->GetAdjacentEdgeRange(node)) + { + const NodeID u = contractor_graph->GetTarget(e); + if (u != node) + { + neighbours.push_back(u); + } + } + // eliminate duplicate entries ( forward + backward edges ) + std::sort(neighbours.begin(), neighbours.end()); + neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin()); + + for (const auto i : osrm::irange(0, neighbours.size())) + { + contractor_graph->DeleteEdgesTo(neighbours[i], node); + } + } + + inline bool UpdateNodeNeighbours(std::vector &priorities, + std::vector &node_data, + ContractorThreadData *const data, + const NodeID node) + { + std::vector &neighbours = data->neighbours; + neighbours.clear(); + + // find all neighbours + for (auto e : contractor_graph->GetAdjacentEdgeRange(node)) + { + const NodeID u = contractor_graph->GetTarget(e); + if (u == node) + { + continue; + } + neighbours.push_back(u); + node_data[u].depth = (std::max)(node_data[node].depth + 1, node_data[u].depth); + } + // eliminate duplicate entries ( forward + backward edges ) + std::sort(neighbours.begin(), neighbours.end()); + neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin()); + + // re-evaluate priorities of neighboring nodes + for (const NodeID u : neighbours) + { + priorities[u] = EvaluateNodePriority(data, &(node_data)[u], u); + } + return true; + } + + inline bool IsNodeIndependent( + const std::vector &priorities, + ContractorThreadData *const data, + NodeID node) const + { + const float priority = priorities[node]; + + std::vector &neighbours = data->neighbours; + neighbours.clear(); + + for (auto e : contractor_graph->GetAdjacentEdgeRange(node)) + { + const NodeID target = contractor_graph->GetTarget(e); + if (node == target) + { + continue; + } + const float target_priority = priorities[target]; + BOOST_ASSERT(target_priority >= 0); + // found a neighbour with lower priority? + if (priority > target_priority) + { + return false; + } + // tie breaking + if (std::abs(priority - target_priority) < std::numeric_limits::epsilon() && + bias(node, target)) + { + return false; + } + neighbours.push_back(target); + } + + std::sort(neighbours.begin(), neighbours.end()); + neighbours.resize(std::unique(neighbours.begin(), neighbours.end()) - neighbours.begin()); + + // examine all neighbours that are at most 2 hops away + for (const NodeID u : neighbours) + { + for (auto e : contractor_graph->GetAdjacentEdgeRange(u)) + { + const NodeID target = contractor_graph->GetTarget(e); + if (node == target) + { + continue; + } + const float target_priority = priorities[target]; + BOOST_ASSERT(target_priority >= 0); + // found a neighbour with lower priority? + if (priority > target_priority) + { + return false; + } + // tie breaking + if (std::abs(priority - target_priority) < std::numeric_limits::epsilon() && + bias(node, target)) + { + return false; + } + } + } + return true; + } + + // This bias function takes up 22 assembly instructions in total on X86 + inline bool bias(const NodeID a, const NodeID b) const + { + const unsigned short hasha = fast_hash(a); + const unsigned short hashb = fast_hash(b); + + // The compiler optimizes that to conditional register flags but without branching + // statements! + if (hasha != hashb) + { + return hasha < hashb; + } + return a < b; + } + + std::shared_ptr contractor_graph; + std::vector contracted_edge_list; + stxxl::vector external_edge_list; + std::vector orig_node_id_to_new_id_map; + XORFastHash fast_hash; +}; + +#endif // CONTRACTOR_HPP diff --git a/Contractor/EdgeBasedGraphFactory.cpp b/contractor/edge_based_graph_factory.cpp similarity index 99% rename from Contractor/EdgeBasedGraphFactory.cpp rename to contractor/edge_based_graph_factory.cpp index 75197264a..5915780b8 100644 --- a/Contractor/EdgeBasedGraphFactory.cpp +++ b/contractor/edge_based_graph_factory.cpp @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "EdgeBasedGraphFactory.h" +#include "edge_based_graph_factory.hpp" #include "../algorithms/bfs_components.hpp" #include "../DataStructures/Percent.h" #include "../Util/compute_angle.hpp" diff --git a/Contractor/EdgeBasedGraphFactory.h b/contractor/edge_based_graph_factory.hpp similarity index 96% rename from Contractor/EdgeBasedGraphFactory.h rename to contractor/edge_based_graph_factory.hpp index eafea3148..16f1b68b2 100644 --- a/Contractor/EdgeBasedGraphFactory.h +++ b/contractor/edge_based_graph_factory.hpp @@ -27,9 +27,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // This class constructs the edge-expanded routing graph -#ifndef EDGEBASEDGRAPHFACTORY_H_ -#define EDGEBASEDGRAPHFACTORY_H_ +#ifndef EDGE_BASED_GRAPH_FACTORY_HPP_ +#define EDGE_BASED_GRAPH_FACTORY_HPP_ +#include "geometry_compressor.hpp" #include "../typedefs.h" #include "../DataStructures/DeallocatingVector.h" #include "../DataStructures/EdgeBasedNode.h" @@ -38,7 +39,6 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "../DataStructures/TurnInstructions.h" #include "../DataStructures/NodeBasedGraph.h" #include "../DataStructures/restriction_map.hpp" -#include "GeometryCompressor.h" #include #include @@ -123,4 +123,4 @@ class EdgeBasedGraphFactory NodeID max_id; }; -#endif /* EDGEBASEDGRAPHFACTORY_H_ */ +#endif /* EDGE_BASED_GRAPH_FACTORY_HPP_ */ diff --git a/Contractor/GeometryCompressor.cpp b/contractor/geometry_compressor.cpp similarity index 99% rename from Contractor/GeometryCompressor.cpp rename to contractor/geometry_compressor.cpp index af0e1be1c..9458c4415 100644 --- a/Contractor/GeometryCompressor.cpp +++ b/contractor/geometry_compressor.cpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2013, Project OSRM, Dennis Luxen, others +Copyright (c) 2014, Project OSRM, Dennis Luxen, others All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "GeometryCompressor.h" +#include "geometry_compressor.hpp" #include "../Util/simple_logger.hpp" #include diff --git a/Contractor/GeometryCompressor.h b/contractor/geometry_compressor.hpp similarity index 93% rename from Contractor/GeometryCompressor.h rename to contractor/geometry_compressor.hpp index f0af3710c..dd5748d91 100644 --- a/Contractor/GeometryCompressor.h +++ b/contractor/geometry_compressor.hpp @@ -1,6 +1,6 @@ /* -Copyright (c) 2013, Project OSRM, Dennis Luxen, others +Copyright (c) 2014, Project OSRM, Dennis Luxen, others All rights reserved. Redistribution and use in source and binary forms, with or without modification, @@ -25,8 +25,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#ifndef GEOMETRY_COMPRESSOR_H -#define GEOMETRY_COMPRESSOR_H +#ifndef GEOMETRY_COMPRESSOR_HPP_ +#define GEOMETRY_COMPRESSOR_HPP_ #include "../typedefs.h" @@ -64,4 +64,4 @@ class GeometryCompressor std::unordered_map m_edge_id_to_list_index_map; }; -#endif // GEOMETRY_COMPRESSOR_H +#endif // GEOMETRY_COMPRESSOR_HPP_ diff --git a/Contractor/Prepare.cpp b/contractor/processing_chain.cpp similarity index 99% rename from Contractor/Prepare.cpp rename to contractor/processing_chain.cpp index 1a0c7ee28..2f0accec4 100644 --- a/Contractor/Prepare.cpp +++ b/contractor/processing_chain.cpp @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Prepare.h" +#include "processing_chain.hpp" #include "contractor.hpp" diff --git a/Contractor/Prepare.h b/contractor/processing_chain.hpp similarity index 98% rename from Contractor/Prepare.h rename to contractor/processing_chain.hpp index 3731e129a..3594bc17a 100644 --- a/Contractor/Prepare.h +++ b/contractor/processing_chain.hpp @@ -1,7 +1,7 @@ #ifndef PREPARE_H #define PREPARE_H -#include "EdgeBasedGraphFactory.h" +#include "edge_based_graph_factory.hpp" #include "../DataStructures/QueryEdge.h" #include "../DataStructures/StaticGraph.h" class FingerPrint; diff --git a/prepare.cpp b/prepare.cpp index 3cf397d0f..e50f134c3 100644 --- a/prepare.cpp +++ b/prepare.cpp @@ -25,7 +25,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include "Contractor/Prepare.h" +#include "contractor/processing_chain.hpp" #include