diff --git a/include/partition/annotated_partition.hpp b/include/partition/annotated_partition.hpp new file mode 100644 index 000000000..127192168 --- /dev/null +++ b/include/partition/annotated_partition.hpp @@ -0,0 +1,136 @@ +#ifndef OSRM_PARTITION_ANNOTATE_HPP_ +#define OSRM_PARTITION_ANNOTATE_HPP_ + +#include "partition/bisection_graph.hpp" +#include "util/typedefs.hpp" +#include +#include +#include + +namespace osrm +{ +namespace partition +{ + +// takes the result of a recursive bisection and turns it into an annotated partition for MLD. These +// annotated partitions provide a mapping from every node in the graph to a consecutively +// numbered cell in each level of the multi level partition. Instead of using the bisection directly +// (which can result in a unbalanced tree structure) +//  +// _____o______ +// / \ +// o ____o____ +// / \ / \ +// a b o _o_ +// / \ / \ +// c d o o +// / \ / \ +// e f g h +//  +// we build a balanced structure that will result in a multi-cut on any level. We transform this +// layout into: +// +// _____o__________ +// / | \ +// o | \ +// / \ | \ +// a b o _o_ +// / \ / \ +// c d o o +// / \ / \ +// e f g h +class AnnotatedPartition +{ + public: + // Used to generate an implicit tree representation + struct SizedID + { + BisectionID id; + std::size_t count; + + bool operator<(const SizedID &other) const { return id < other.id; }; + }; + + // Metrics that describe a single level + struct LevelMetrics + { + std::size_t border_nodes; + std::size_t border_arcs; + + // impresses imbalance, if not all nodes are in that cell anymore + std::size_t contained_nodes; + std::size_t number_of_cells; + + std::size_t max_border_nodes_per_cell; + std::size_t max_border_arcs_per_cell; + + std::size_t total_memory_cells; + std::vector cell_sizes; + + std::ostream &print(std::ostream &os) const + { + os << "[level]\n" + << "\t#border nodes: " << border_nodes << " #border arcs: " << border_arcs + << " #cells: " << number_of_cells << " #contained nodes: " << contained_nodes << "\n" + << "\tborder nodes: max: " << max_border_nodes_per_cell + << " avg : " << static_cast(border_nodes) / number_of_cells + << " border arcs: max: " << max_border_arcs_per_cell << " " + << " avg: " << static_cast(border_arcs) / number_of_cells << "\n" + << "\tmemory consumption: " << total_memory_cells / (1024.0 * 1024.0) << " MB." + << "\n"; + os << "\tcell sizes:"; + for (auto s : cell_sizes) + os << " " << s; + os << std::endl; + return os; + } + + std::ostream &logMachinereadable(std::ostream &os, + const std::string &identification, + std::size_t depth, + const bool print_header = false) const + { + if (print_header) + os << "[" << identification << "] # depth cells total_nodes border_nodes " + "max_border_nodes border_arcs max_border_arcs bytes " + "cell_sizes*\n"; + + os << "[" << identification << "] " << depth << " " << number_of_cells << " " + << contained_nodes << " " << border_nodes << " " << max_border_nodes_per_cell << " " + << border_arcs << " " << max_border_arcs_per_cell << " " << total_memory_cells; + + for (auto s : cell_sizes) + os << " " << s; + + os << "\n"; + return os; + } + }; + + AnnotatedPartition(const BisectionGraph &graph, const std::vector &bisection_ids); + + private: + // print distribution of level graph as it is + void PrintBisection(const std::vector &implicit_tree, + const BisectionGraph &graph, + const std::vector &bisection_ids) const; + + // find levels that offer good distribution of average cell sizes + void SearchLevels(const std::vector &implicit_tree, + const BisectionGraph &graph, + const std::vector &bisection_ids) const; + + // set cell_ids[i] == INFTY to exclude element + LevelMetrics AnalyseLevel(const BisectionGraph &graph, + const std::vector &cell_ids) const; + + std::vector + ComputeCellIDs(std::vector> &prefixes, + const BisectionGraph &graph, + const std::vector &bisection_ids) const; +}; + +} // namespace partition +} // namespace osrm + +#endif // OSRM_PARTITION_ANNOTATE_HPP_ diff --git a/include/partition/inertial_flow.hpp b/include/partition/inertial_flow.hpp index 8f3bcfaf0..a1f17aa16 100644 --- a/include/partition/inertial_flow.hpp +++ b/include/partition/inertial_flow.hpp @@ -35,7 +35,7 @@ class InertialFlow SpatialOrder MakeSpatialOrder(double ratio, double slope) const; // Makes n cuts with different spatial orders and returns the best. - DinicMaxFlow::MinCut BestMinCut(std::size_t n, double ratio) const; + DinicMaxFlow::MinCut BestMinCut(std::size_t n, double ratio, double balance) const; // The subgraph to partition into two parts. const GraphView &view; diff --git a/include/partition/recursive_bisection.hpp b/include/partition/recursive_bisection.hpp index f5041f8ac..4a6174f2c 100644 --- a/include/partition/recursive_bisection.hpp +++ b/include/partition/recursive_bisection.hpp @@ -4,6 +4,7 @@ #include "partition/bisection_graph.hpp" #include "partition/graph_view.hpp" #include "partition/recursive_bisection_state.hpp" +#include "util/typedefs.hpp" #include #include @@ -23,7 +24,7 @@ class RecursiveBisection const std::size_t num_optimizing_cuts, const std::size_t small_component_size); - const std::vector &BisectionIDs() const; + const std::vector &BisectionIDs() const; private: BisectionGraph &bisection_graph; diff --git a/include/partition/recursive_bisection_state.hpp b/include/partition/recursive_bisection_state.hpp index e2c1d546c..a4edb7f35 100644 --- a/include/partition/recursive_bisection_state.hpp +++ b/include/partition/recursive_bisection_state.hpp @@ -20,7 +20,6 @@ class RecursiveBisectionState { public: // The ID in the partition array - using BisectionID = std::uint32_t; using NodeIterator = BisectionGraph::ConstNodeIterator; RecursiveBisectionState(BisectionGraph &bisection_graph); @@ -42,7 +41,11 @@ class RecursiveBisectionState const std::vector &BisectionIDs() const; + // return the depth encoded in the SCCs + std::uint32_t SCCDepth() const; + private: + std::uint32_t scc_levels; BisectionGraph &bisection_graph; std::vector bisection_ids; }; diff --git a/include/partition/recursive_bisection_stats.hpp b/include/partition/recursive_bisection_stats.hpp deleted file mode 100644 index c15d57f70..000000000 --- a/include/partition/recursive_bisection_stats.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef OSRM_PARTITION_RECURSIVE_BISECTION_STATS_HPP_ -#define OSRM_PARTITION_RECURSIVE_BISECTION_STATS_HPP_ - -#include "partition/bisection_graph.hpp" -#include "partition/recursive_bisection_state.hpp" - -#include - -namespace osrm -{ -namespace partition -{ -// generates some statistics on a recursive bisection to describe its quality/parameters -void printBisectionStats(std::vector const &bisection_ids, - const BisectionGraph &graph); -} // namespace partition -} // namespace osrm - -#endif // OSRM_PARTITION_RECURSIVE_BISECTION_STATS_HPP_ diff --git a/include/util/static_graph.hpp b/include/util/static_graph.hpp index d8ff00892..42d531741 100644 --- a/include/util/static_graph.hpp +++ b/include/util/static_graph.hpp @@ -81,26 +81,21 @@ template struct SortableEdgeWithData : SortableEdgeWithData } // namespace static_graph_details -template class FlexibleStaticGraph +template class StaticGraph { - static_assert(traits::HasFirstEdgeMember::value, - "Model for compatible Node type requires .first_edge member attribute"); - static_assert(traits::HasTargetMember::value, - "Model for compatible Node type requires .target member attribute"); - public: using NodeIterator = static_graph_details::NodeIterator; using EdgeIterator = static_graph_details::EdgeIterator; using EdgeRange = range; - using NodeArrayEntry = NodeT; - using EdgeArrayEntry = EdgeT; + using NodeArrayEntry = static_graph_details::NodeArrayEntry; + using EdgeArrayEntry = static_graph_details::EdgeArrayEntry; EdgeRange GetAdjacentEdgeRange(const NodeID node) const { return irange(BeginEdges(node), EndEdges(node)); } - template FlexibleStaticGraph(const int nodes, const ContainerT &graph) + template StaticGraph(const int nodes, const ContainerT &graph) { BOOST_ASSERT(std::is_sorted(const_cast(graph).begin(), const_cast(graph).end())); @@ -128,14 +123,15 @@ template class Fl for (const auto i : irange(node_array[node].first_edge, e)) { edge_array[i].target = graph[edge].target; - CopyDataIfAvailable(edge_array[i], graph[edge], traits::HasDataMember{}); + CopyDataIfAvailable( + edge_array[i], graph[edge], traits::HasDataMember{}); edge++; } } } - FlexibleStaticGraph(typename ShM::vector &nodes, - typename ShM::vector &edges) + StaticGraph(typename ShM::vector &nodes, + typename ShM::vector &edges) { number_of_nodes = static_cast(nodes.size() - 1); number_of_edges = static_cast(edges.size()); @@ -198,7 +194,7 @@ template class Fl EdgeIterator FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const { - static_assert(traits::HasDataMember::value, + static_assert(traits::HasDataMember::value, "Filtering on .data not possible without .data member attribute"); EdgeIterator smallest_edge = SPECIAL_EDGEID; @@ -243,13 +239,13 @@ template class Fl private: template - void CopyDataIfAvailable(EdgeT &into, const OtherEdge &from, std::true_type) + void CopyDataIfAvailable(EdgeArrayEntry &into, const OtherEdge &from, std::true_type) { into.data = from.data; } template - void CopyDataIfAvailable(EdgeT &into, const OtherEdge &from, std::false_type) + void CopyDataIfAvailable(EdgeArrayEntry &into, const OtherEdge &from, std::false_type) { // Graph has no .data member, never copy even if `from` has a .data member. (void)into; @@ -259,15 +255,11 @@ template class Fl NodeIterator number_of_nodes; EdgeIterator number_of_edges; - typename ShM::vector node_array; - typename ShM::vector edge_array; + typename ShM::vector node_array; + typename ShM::vector edge_array; }; -template -using StaticGraph = FlexibleStaticGraph, - UseSharedMemory>; -} -} +} // namespace util +} // namespace osrm #endif // STATIC_GRAPH_HPP diff --git a/include/util/typedefs.hpp b/include/util/typedefs.hpp index b68e9c5cb..8518ab2d7 100644 --- a/include/util/typedefs.hpp +++ b/include/util/typedefs.hpp @@ -61,6 +61,7 @@ using EdgeWeight = std::int32_t; using TurnPenalty = std::int16_t; // turn penalty in 100ms units static const std::size_t INVALID_INDEX = std::numeric_limits::max(); +using BisectionID = std::uint32_t; using LaneID = std::uint8_t; static const LaneID INVALID_LANEID = std::numeric_limits::max(); diff --git a/src/partition/annotated_partition.cpp b/src/partition/annotated_partition.cpp new file mode 100644 index 000000000..17c2086f4 --- /dev/null +++ b/src/partition/annotated_partition.cpp @@ -0,0 +1,387 @@ +#include "partition/annotated_partition.hpp" + +#include +#include // for CHAR_BIT +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/timing_util.hpp" + +namespace osrm +{ +namespace partition +{ + +namespace +{ + +// the shift value needed to access the most significant bit of the bisection ID +const constexpr auto SHIFT_TO_MSB_BISECTION_ID = sizeof(BisectionID) * CHAR_BIT - 1; +// an invalid ID for a cell +const constexpr std::uint32_t INVALID_CELLID = std::numeric_limits::max(); + +auto masked(const BisectionID id, const std::int32_t level) +{ + // special treatment for negative level + if (level == -1) + return 0u; + + // 0.01.1 with 1 starting at the level+1_th most significant bit (level = 0 -> 01..1) + const auto cut_below_level = (1 << (SHIFT_TO_MSB_BISECTION_ID - level)) - 1; + const auto mask = std::numeric_limits::max() ^ cut_below_level; + return id & mask; +} + +// create a comparator for a given level +auto makeCompare(const std::uint32_t level) +{ + return [level](const AnnotatedPartition::SizedID lhs, const AnnotatedPartition::SizedID rhs) { + return masked(lhs.id, level) < masked(rhs.id, level); + }; +} + +// build a tree of cells from the IDs present: +auto leftChild(const BisectionID id_prefix, const std::int32_t /*level*/) { return id_prefix; } + +// given the prefix 10.... on level 1 (second level), the the right child would be +// 101.... on level 2 +auto rightChild(const BisectionID id_prefix, const std::int32_t level) +{ + return id_prefix | (1 << (SHIFT_TO_MSB_BISECTION_ID - (level + 1))); +} + +// get the range of all children +auto getChildrenRange(const std::vector &implicit_tree, + const BisectionID id_prefix, + const std::int32_t level) +{ + AnnotatedPartition::SizedID id = {id_prefix, 0}; + + // find all elements of the same prefix as id_prefi + auto range = + std::equal_range(implicit_tree.begin(), implicit_tree.end(), id, makeCompare(level)); + + // don't ever return our sentinel element as included + if (range.second == implicit_tree.end()) + --range.second; + + return range; +}; + +auto getCellSize(const std::vector &implicit_tree, + const BisectionID id_prefix, + const std::uint32_t level) +{ + auto range = getChildrenRange(implicit_tree, id_prefix, level); + return range.second->count - range.first->count; +} + +bool hasChildren(const std::vector &implicit_tree, + const BisectionID id_prefix, + const std::uint32_t level) +{ + auto range = getChildrenRange(implicit_tree, id_prefix, level); + return std::distance(range.first, range.second) > 1; +} + +} // namespace + +AnnotatedPartition::AnnotatedPartition(const BisectionGraph &graph, + const std::vector &bisection_ids) +{ + // create a sorted vector of bisection ids that exist in the network + std::vector implicit_tree = [&]() { + std::map existing_ids; + + // insert an ID into the sized_id set or increase the count if the element should be already + // present in the set of known ids + const auto insert_or_augment = [&existing_ids](const BisectionID id) { + SizedID sized_id = {id, 1}; + auto maybe_existing_id = existing_ids.find(id); + if (maybe_existing_id == existing_ids.end()) + existing_ids[id] = sized_id; + else + maybe_existing_id->second.count++; + }; + std::for_each(bisection_ids.begin(), bisection_ids.end(), insert_or_augment); + + std::vector result; + result.resize(existing_ids.size() + 1); + std::transform(existing_ids.begin(), + existing_ids.end(), + result.begin(), + [](const auto &pair) { return pair.second; }); + + // sentinel + result.back() = {std::numeric_limits::max(), 0}; + + return result; + }(); + + // calculate a prefix sum over all sorted IDs, this allows to get the size of any partition in + // the array/level based on the prefix and lower bound on prefixes. + // e.g 00,01,10,11 allow to search for (0) (1) to find (00) and (10) as lower bounds. The + // difference in count is the size of all cells in the left part of the partition. + std::transform(implicit_tree.begin(), + implicit_tree.end(), + implicit_tree.begin(), + [sum = std::size_t{0}](SizedID id) mutable { + const auto new_sum = sum + id.count; + id.count = sum; + sum = new_sum; + return id; + }); + + PrintBisection(implicit_tree, graph, bisection_ids); + SearchLevels(implicit_tree, graph, bisection_ids); +} + +void AnnotatedPartition::PrintBisection(const std::vector &implicit_tree, + const BisectionGraph &graph, + const std::vector &bisection_ids) const +{ + // print some statistics on the bisection tree + std::queue id_queue; + id_queue.push(0); + + const auto add_child = [&id_queue, &implicit_tree](const BisectionID prefix, + const std::uint32_t level) { + const auto child_range = getChildrenRange(implicit_tree, prefix, level); + if (std::distance(child_range.first, child_range.second) > 1) + id_queue.push(prefix); + }; + + std::vector> current_level; + for (std::int32_t level = -1; !id_queue.empty(); ++level) + { + auto level_size = id_queue.size(); + current_level.clear(); + while (level_size--) + { + const auto prefix = id_queue.front(); + id_queue.pop(); + if (level == -1 || hasChildren(implicit_tree, prefix, level)) + { + current_level.push_back( + std::pair(leftChild(prefix, level), level + 1)); + current_level.push_back( + std::pair(rightChild(prefix, level), level + 1)); + } + add_child(leftChild(prefix, level), level); + add_child(rightChild(prefix, level), level); + } + if (!current_level.empty()) + { + const auto cell_ids = ComputeCellIDs(current_level, graph, bisection_ids); + const auto stats = AnalyseLevel(graph, cell_ids); + stats.logMachinereadable(std::cout, "bisection", level, level == -1); + } + } +} + +void AnnotatedPartition::SearchLevels(const std::vector &implicit_tree, + const BisectionGraph &graph, + const std::vector &bisection_ids) const +{ + std::vector> current_level; + + // start searching with level 0 at prefix 0 + current_level.push_back({static_cast(0), -1}); + std::int32_t level = -1; + + const auto print_level = [&]() { + if (current_level.empty()) + return; + + const auto cell_ids = ComputeCellIDs(current_level, graph, bisection_ids); + const auto stats = AnalyseLevel(graph, cell_ids); + stats.logMachinereadable(std::cout, "dfs-balanced", level, level == -1); + ++level; + }; + + std::size_t max_size = 0.5 * graph.NumberOfNodes(); + std::queue> id_queue; + while (!current_level.empty()) + { + std::size_t total_size = 0; + std::size_t count = 0; + for (auto element : current_level) + { + // don't relax final cells + if (element.second == -1 || hasChildren(implicit_tree, element.first, element.second)) + { + total_size += getCellSize( + implicit_tree, leftChild(element.first, element.second), element.second + 1); + id_queue.push(std::pair( + leftChild(element.first, element.second), element.second + 1)); + + total_size += getCellSize( + implicit_tree, rightChild(element.first, element.second), element.second + 1); + id_queue.push(std::pair( + rightChild(element.first, element.second), element.second + 1)); + count += 2; + } + } + auto avg_size = (total_size / static_cast(count)); + + current_level.clear(); + + const auto relax = [&id_queue, &implicit_tree, avg_size, ¤t_level]( + const std::pair &element) { + const auto size = getCellSize(implicit_tree, element.first, element.second); + if (!hasChildren(implicit_tree, element.first, element.second)) + { + current_level.push_back(element); + } + else + { + const auto left = leftChild(element.first, element.second); + const auto right = rightChild(element.first, element.second); + + const auto get_penalty = [avg_size](const auto size) { + return std::abs(size - avg_size); + }; + + if (get_penalty(size) < + 0.5 * (get_penalty(getCellSize(implicit_tree, left, element.second + 1)) + + get_penalty(getCellSize(implicit_tree, right, element.second + 1)))) + { + current_level.push_back(element); + } + else + { + id_queue.push(std::pair(left, element.second + 1)); + id_queue.push(std::pair(right, element.second + 1)); + } + } + }; + + while (!id_queue.empty()) + { + relax(id_queue.front()); + id_queue.pop(); + } + print_level(); + max_size *= 0.5; + } +} + +AnnotatedPartition::LevelMetrics +AnnotatedPartition::AnalyseLevel(const BisectionGraph &graph, + const std::vector &cell_ids) const +{ + std::unordered_map cell_sizes; + std::unordered_map border_nodes; + std::unordered_map border_arcs; + + // compute basic metrics of the level + std::size_t border_nodes_total = 0; + std::size_t border_arcs_total = 0; + std::size_t contained_nodes = 0; + + // only border nodes on the lowest level can be border nodes in general + for (const auto &node : graph.Nodes()) + { + const auto cell_id = cell_ids[node.original_id]; + if (cell_id == INVALID_CELLID) + continue; + + ++contained_nodes; + + const auto edge_range = graph.Edges(node); + const auto border_arcs_at_node = std::count_if( + edge_range.begin(), edge_range.end(), [&cell_id, &cell_ids, &graph](const auto &edge) { + const auto target_cell_id = cell_ids[graph.Node(edge.target).original_id]; + return target_cell_id != cell_id; + }); + + cell_sizes[cell_id]++; + border_arcs[cell_id] += border_arcs_at_node; + border_arcs_total += border_arcs_at_node; + if (border_arcs_at_node) + { + border_nodes[cell_id]++; + ++border_nodes_total; + } + } + + const auto by_size = [](const std::pair &lhs, + const std::pair &rhs) { + return lhs.second < rhs.second; + }; + const auto max_nodes = + border_nodes.empty() + ? 0 + : std::max_element(border_nodes.begin(), border_nodes.end(), by_size)->second; + const auto max_arcs = + border_arcs.empty() + ? 0 + : std::max_element(border_arcs.begin(), border_arcs.end(), by_size)->second; + + const auto squarded_size = [](const std::size_t accumulated, + const std::pair &element) { + return accumulated + element.second * element.second; + }; + + const auto memory = + 4 * std::accumulate(border_arcs.begin(), border_nodes.end(), std::size_t(0), squarded_size); + + std::vector cell_sizes_vec; + cell_sizes_vec.resize(cell_sizes.size()); + std::transform(cell_sizes.begin(), + cell_sizes.end(), + cell_sizes_vec.begin(), + [](const auto &pair) { return pair.second; }); + return {border_nodes_total, + border_arcs_total, + contained_nodes, + border_nodes.size(), + max_nodes, + max_arcs, + memory, + std::move(cell_sizes_vec)}; +} + +std::vector +AnnotatedPartition::ComputeCellIDs(std::vector> &prefixes, + const BisectionGraph &graph, + const std::vector &bisection_ids) const +{ + std::vector cell_ids(graph.NumberOfNodes(), INVALID_CELLID); + + std::sort(prefixes.begin(), prefixes.end(), [](const auto lhs, const auto rhs) { + return lhs.first < rhs.first; + }); + + for (const auto &node : graph.Nodes()) + { + // find the cell_id of node in the current levels + const auto id = bisection_ids[node.original_id]; + + const auto is_prefixed_by = [id](const auto &prefix) { + return masked(id, prefix.second) == prefix.first; + }; + + const auto prefix = std::lower_bound( + prefixes.begin(), prefixes.end(), id, [&](const auto prefix, const BisectionID id) { + return prefix.first < masked(id, prefix.second); + }); + + if (prefix == prefixes.end()) + continue; + + if (is_prefixed_by(*prefix)) + cell_ids[node.original_id] = std::distance(prefixes.begin(), prefix); + } + return cell_ids; +} + +} // namespace partition +} // namespace osrm diff --git a/src/partition/inertial_flow.cpp b/src/partition/inertial_flow.cpp index fcbf5dd76..84411c9b3 100644 --- a/src/partition/inertial_flow.cpp +++ b/src/partition/inertial_flow.cpp @@ -25,9 +25,7 @@ DinicMaxFlow::MinCut InertialFlow::ComputePartition(const std::size_t num_slopes const double balance, const double source_sink_rate) { - auto cut = BestMinCut(num_slopes, source_sink_rate); - - return cut; + return BestMinCut(num_slopes, source_sink_rate, balance); } InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio, @@ -86,23 +84,36 @@ InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio, return order; } -DinicMaxFlow::MinCut InertialFlow::BestMinCut(const std::size_t n, const double ratio) const +DinicMaxFlow::MinCut +InertialFlow::BestMinCut(const std::size_t n, const double ratio, const double balance) const { DinicMaxFlow::MinCut best; best.num_edges = -1; - const auto get_balance = [this](const auto num_nodes_source) { - double ratio = static_cast(view.NumberOfNodes() - num_nodes_source) / - static_cast(num_nodes_source); - return std::abs(ratio - 1.0); + const auto get_balance = [this, balance](const auto num_nodes_source) { + const auto perfect_balance = view.NumberOfNodes() / 2; + const auto allowed_balance = balance * perfect_balance; + const auto bigger_side = + std::max(num_nodes_source, view.NumberOfNodes() - num_nodes_source); + + if (bigger_side > allowed_balance) + return bigger_side / static_cast(allowed_balance); + else + return 1.0; }; - auto best_balance = 10000; // get_balance(best.num_nodes_source); + auto best_balance = 1; std::mutex lock; tbb::blocked_range range{0, n, 1}; + const auto balance_delta = [this](const auto num_nodes_source) { + const std::int64_t difference = + static_cast(view.NumberOfNodes()) / 2 - num_nodes_source; + return std::abs(difference); + }; + tbb::parallel_for(range, [&, this](const auto &chunk) { for (auto round = chunk.begin(), end = chunk.end(); round != end; ++round) { @@ -116,7 +127,9 @@ DinicMaxFlow::MinCut InertialFlow::BestMinCut(const std::size_t n, const double std::lock_guard guard{lock}; // Swap to keep the destruction of the old object outside of critical section. - if (std::tie(cut.num_edges, cut_balance) < std::tie(best.num_edges, best_balance)) + if (cut.num_edges * cut_balance < best.num_edges * best_balance || + (cut.num_edges == best.num_edges && + balance_delta(cut.num_nodes_source) < balance_delta(best.num_nodes_source))) { best_balance = cut_balance; std::swap(best, cut); diff --git a/src/partition/partitioner.cpp b/src/partition/partitioner.cpp index fd9676476..4706ce7eb 100644 --- a/src/partition/partitioner.cpp +++ b/src/partition/partitioner.cpp @@ -1,7 +1,7 @@ #include "partition/partitioner.hpp" +#include "partition/annotated_partition.hpp" #include "partition/bisection_graph.hpp" #include "partition/recursive_bisection.hpp" -#include "partition/recursive_bisection_stats.hpp" #include "storage/io.hpp" #include "util/coordinate.hpp" @@ -19,6 +19,7 @@ #include "util/geojson_debug_logger.hpp" #include "util/geojson_debug_policies.hpp" #include "util/json_container.hpp" +#include "util/timing_util.hpp" namespace osrm { @@ -65,6 +66,27 @@ CompressedNodeBasedGraph LoadCompressedNodeBasedGraph(const std::string &path) return graph; } +void LogStatistics(const std::string &filename, std::vector bisection_ids) +{ + auto compressed_node_based_graph = LoadCompressedNodeBasedGraph(filename); + + util::Log() << "Loaded compressed node based graph: " + << compressed_node_based_graph.edges.size() << " edges, " + << compressed_node_based_graph.coordinates.size() << " nodes"; + + groupEdgesBySource(begin(compressed_node_based_graph.edges), + end(compressed_node_based_graph.edges)); + + auto graph = + makeBisectionGraph(compressed_node_based_graph.coordinates, + adaptToBisectionEdge(std::move(compressed_node_based_graph.edges))); + + TIMER_START(annotation); + AnnotatedPartition partition(graph, bisection_ids); + TIMER_STOP(annotation); + std::cout << "Annotation took " << TIMER_SEC(annotation) << " seconds" << std::endl; +} + void LogGeojson(const std::string &filename, std::vector bisection_ids) { // reload graph, since we destroyed the old one @@ -87,18 +109,6 @@ void LogGeojson(const std::string &filename, std::vector bisectio return level; }; - const auto reverse_bits = [](std::uint32_t x) { - x = ((x >> 1) & 0x55555555u) | ((x & 0x55555555u) << 1); - x = ((x >> 2) & 0x33333333u) | ((x & 0x33333333u) << 2); - x = ((x >> 4) & 0x0f0f0f0fu) | ((x & 0x0f0f0f0fu) << 4); - x = ((x >> 8) & 0x00ff00ffu) | ((x & 0x00ff00ffu) << 8); - x = ((x >> 16) & 0xffffu) | ((x & 0xffffu) << 16); - return x; - }; - - std::transform(bisection_ids.begin(), bisection_ids.end(), bisection_ids.begin(), reverse_bits); - - printBisectionStats(bisection_ids, graph); std::vector> border_vertices(33); for (NodeID nid = 0; nid < graph.NumberOfNodes(); ++nid) @@ -151,6 +161,10 @@ int Partitioner::Run(const PartitionConfig &config) makeBisectionGraph(compressed_node_based_graph.coordinates, adaptToBisectionEdge(std::move(compressed_node_based_graph.edges))); + util::Log() << " running partition: " << config.maximum_cell_size << " " << config.balance + << " " << config.boundary_factor << " " << config.num_optimizing_cuts << " " + << config.small_component_size + << " # max_cell_size balance boundary cuts small_component_size"; RecursiveBisection recursive_bisection(graph, config.maximum_cell_size, config.balance, @@ -158,8 +172,8 @@ int Partitioner::Run(const PartitionConfig &config) config.num_optimizing_cuts, config.small_component_size); - LogGeojson(config.compressed_node_based_graph_path.string(), - recursive_bisection.BisectionIDs()); + LogStatistics(config.compressed_node_based_graph_path.string(), + recursive_bisection.BisectionIDs()); return 0; } diff --git a/src/partition/recursive_bisection.cpp b/src/partition/recursive_bisection.cpp index 4e3aec029..f6507cd56 100644 --- a/src/partition/recursive_bisection.cpp +++ b/src/partition/recursive_bisection.cpp @@ -60,8 +60,8 @@ RecursiveBisection::RecursiveBisection(BisectionGraph &bisection_graph_, std::vector forest; forest.reserve(last - first); - std::transform(first, last, std::back_inserter(forest), [](auto graph) { - return TreeNode{std::move(graph), 0}; + std::transform(first, last, std::back_inserter(forest), [this](auto graph) { + return TreeNode{std::move(graph), internal_state.SCCDepth()}; }); using Feeder = tbb::parallel_do_feeder; @@ -76,7 +76,7 @@ RecursiveBisection::RecursiveBisection(BisectionGraph &bisection_graph_, node.graph.Begin(), node.graph.End(), node.depth, partition.flags); const auto terminal = [&](const auto &node) { - const auto maximum_depth = sizeof(RecursiveBisectionState::BisectionID) * CHAR_BIT; + const auto maximum_depth = sizeof(BisectionID) * CHAR_BIT; const auto too_small = node.graph.NumberOfNodes() < maximum_cell_size; const auto too_deep = node.depth >= maximum_depth; return too_small || too_deep; @@ -100,7 +100,7 @@ RecursiveBisection::RecursiveBisection(BisectionGraph &bisection_graph_, util::Log() << "Full bisection done in " << TIMER_SEC(bisection) << "s"; } -const std::vector &RecursiveBisection::BisectionIDs() const +const std::vector &RecursiveBisection::BisectionIDs() const { return internal_state.BisectionIDs(); } diff --git a/src/partition/recursive_bisection_state.cpp b/src/partition/recursive_bisection_state.cpp index 12cd7dc62..d5f2874b3 100644 --- a/src/partition/recursive_bisection_state.cpp +++ b/src/partition/recursive_bisection_state.cpp @@ -3,11 +3,9 @@ #include "partition/tarjan_graph_wrapper.hpp" #include +#include // for CHAR_BIT #include - -// TODO remove -#include -#include +#include #include namespace osrm @@ -16,15 +14,14 @@ namespace partition { RecursiveBisectionState::RecursiveBisectionState(BisectionGraph &bisection_graph_) - : bisection_graph(bisection_graph_) + : scc_levels(0), bisection_graph(bisection_graph_) { bisection_ids.resize(bisection_graph.NumberOfNodes(), BisectionID{0}); } RecursiveBisectionState::~RecursiveBisectionState() {} -RecursiveBisectionState::BisectionID -RecursiveBisectionState::GetBisectionID(const NodeID node) const +BisectionID RecursiveBisectionState::GetBisectionID(const NodeID node) const { return bisection_ids[node]; } @@ -35,12 +32,13 @@ RecursiveBisectionState::ApplyBisection(const NodeIterator const_begin, const std::size_t depth, const std::vector &partition) { + BOOST_ASSERT(depth >= scc_levels); // ensure that the iterators belong to the graph BOOST_ASSERT(bisection_graph.GetID(*const_begin) < bisection_graph.NumberOfNodes() && bisection_graph.GetID(*const_begin) + std::distance(const_begin, const_end) <= bisection_graph.NumberOfNodes()); // augment the partition ids - const auto flag = BisectionID{1} << depth; + const auto flag = BisectionID{1} << (sizeof(BisectionID) * CHAR_BIT - depth - 1); for (auto itr = const_begin; itr != const_end; ++itr) { const auto nid = std::distance(const_begin, itr); @@ -129,9 +127,11 @@ RecursiveBisectionState::PrePartitionWithSCC(const std::size_t small_component_s std::vector views; auto last = bisection_graph.CBegin(); auto last_id = transform_id(bisection_graph.Begin()->original_id); + std::set ordered_component_ids; for (auto itr = bisection_graph.CBegin(); itr != bisection_graph.CEnd(); ++itr) { auto itr_id = transform_id(itr->original_id); + ordered_component_ids.insert(itr_id); if (last_id != itr_id) { views.push_back(GraphView(bisection_graph, last, itr)); @@ -151,14 +151,34 @@ RecursiveBisectionState::PrePartitionWithSCC(const std::size_t small_component_s if (!has_small_component) views.push_back(GraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd())); + // apply scc as bisections, we need scc_level bits for this with scc_levels = + // ceil(log_2(components)) + scc_levels = ceil(log(views.size()) / log(2.0)); + + const auto conscutive_component_id = [&](const NodeID nid) { + const auto component_id = transform_id(nid); + const auto itr = ordered_component_ids.find(component_id); + BOOST_ASSERT(itr != ordered_component_ids.end()); + BOOST_ASSERT(static_cast(std::distance(ordered_component_ids.begin(), itr)) < + ordered_component_ids.size()); + return std::distance(ordered_component_ids.begin(), itr); + }; + + const auto shift = sizeof(BisectionID) * CHAR_BIT - scc_levels; + + // store the component ids as first part of the bisection id + for (const auto &node : bisection_graph.Nodes()) + bisection_ids[node.original_id] = conscutive_component_id(node.original_id) << shift; + return views; } -const std::vector & -RecursiveBisectionState::BisectionIDs() const +const std::vector &RecursiveBisectionState::BisectionIDs() const { return bisection_ids; } +std::uint32_t RecursiveBisectionState::SCCDepth() const { return scc_levels; } + } // namespace partition } // namespace osrm diff --git a/src/partition/recursive_bisection_stats.cpp b/src/partition/recursive_bisection_stats.cpp deleted file mode 100644 index 16fdaeb50..000000000 --- a/src/partition/recursive_bisection_stats.cpp +++ /dev/null @@ -1,113 +0,0 @@ -#include "partition/recursive_bisection_stats.hpp" - -#include - -#include -#include -#include -#include -#include - -namespace osrm -{ -namespace partition -{ -void printBisectionStats(std::vector const &bisection_ids, - const BisectionGraph &graph) -{ - BOOST_ASSERT(graph.NumberOfNodes() == bisection_ids.size()); - std::size_t total_border_nodes = 0; - std::unordered_map cell_sizes[32]; - std::unordered_map border_nodes[32]; - - std::unordered_set all_ids[32]; - - std::uint32_t flag = 0; - for (std::uint32_t level = 0; level < 32; ++level) - { - std::uint32_t bit = 1u << (31 - level); - flag |= bit; - - for (std::size_t i = 0; i < bisection_ids.size(); ++i) - all_ids[level].insert(bisection_ids[i] & flag); - - if (level > 0 && all_ids[level - 1] == all_ids[level]) - break; - - std::cout << "Level" << std::endl; - for (auto itr : all_ids[level]) - std::cout << "\t" << std::bitset<32>(itr) << std::endl; - - for (const auto &node : graph.Nodes()) - { - const auto bisection_id_node = bisection_ids[node.original_id]; - - // subrange is not partitioned anymore - if (all_ids[level].count((bisection_id_node & flag) ^ bit) == 0) - continue; - - auto is_border_node = false; - for (const auto &edge : graph.Edges(node)) - { - if (bisection_ids[edge.target] != bisection_id_node) - is_border_node = true; - } - - if (is_border_node && level == 0) - ++total_border_nodes; - - cell_sizes[level][bisection_id_node & flag]++; - if (is_border_node) - { - for (const auto &edge : graph.Edges(node)) - { - if ((bisection_id_node & flag) != (bisection_ids[edge.target] & flag)) - { - border_nodes[level][bisection_id_node & flag]++; - break; - } - } - } - } - } - - std::cout << "Partition statistics\n"; - std::cout << "Total border vertices: " << total_border_nodes << std::endl; - unsigned level = 0; - do - { - std::size_t min_size = -1, max_size = 0, total_size = 0; - std::size_t min_border = -1, max_border = 1, total_border = 0; - - const auto summarize = - [](const std::unordered_map &map, - std::size_t &min, - std::size_t &max, - std::size_t &total) { - for (const auto itr : map) - { - min = std::min(min, itr.second); - max = std::max(max, itr.second); - total += itr.second; - } - }; - - summarize(cell_sizes[level], min_size, max_size, total_size); - summarize(border_nodes[level], min_border, max_border, total_border); - - std::cout << "Level: " << level << " Cells: " << cell_sizes[level].size(); - if (cell_sizes[level].size() > 1) - std::cout << " Border: " << min_border << " " << max_border << " " - << total_border / (double)cell_sizes[level].size(); - - std::cout << " Cell Sizes: " << min_size << " " << max_size << " " - << total_size / (double)cell_sizes[level].size() - << " Total Vertices In Level: " << total_size; - - std::cout << std::endl; - - } while (level < 31 && cell_sizes[++level].size() > 1); -} - -} // namespace partition -} // namespace osrm diff --git a/unit_tests/partition/graph_view.cpp b/unit_tests/partition/graph_view.cpp index cceb76dd8..4407751fd 100644 --- a/unit_tests/partition/graph_view.cpp +++ b/unit_tests/partition/graph_view.cpp @@ -3,6 +3,7 @@ #include "partition/recursive_bisection_state.hpp" #include +#include #include #include @@ -51,7 +52,8 @@ BOOST_AUTO_TEST_CASE(separate_top_bottom) BOOST_CHECK_EQUAL(right.NumberOfNodes(), 4); for (const auto &node : right.Nodes()) { - BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1); + BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), + 1 << (sizeof(BisectionID) * CHAR_BIT - 1)); auto id = right.GetID(node); const auto compare = makeCoordinate(id, 1, step_size); BOOST_CHECK_EQUAL(compare, node.coordinate); @@ -102,7 +104,8 @@ BOOST_AUTO_TEST_CASE(separate_top_bottom_copy) for (NodeID id = 0; id < right.NumberOfNodes(); ++id) { const auto &node = right.Node(id); - BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1); + BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), + 1 << (sizeof(BisectionID) * CHAR_BIT - 1)); const auto compare = makeCoordinate(id, 1, step_size); BOOST_CHECK_EQUAL(compare, node.coordinate); BOOST_CHECK(id < right.NumberOfNodes()); @@ -162,7 +165,8 @@ BOOST_AUTO_TEST_CASE(separate_left_right) auto right_compare = right_coordinates.begin(); for (const auto &node : right.Nodes()) { - BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), 1); + BOOST_CHECK_EQUAL(bisection_state.GetBisectionID(node.original_id), + 1 << (sizeof(BisectionID) * CHAR_BIT - 1)); auto id = right.GetID(node); const auto compare = *right_compare++; BOOST_CHECK_EQUAL(compare, node.coordinate);