#include "partition/bisection_to_partition.hpp" namespace osrm { namespace partition { namespace { struct CellBisection { std::uint32_t begin; std::uint32_t end; std::uint8_t bit; bool tabu; // we will not attempt to split this cell anymore }; static constexpr std::size_t NUM_BISECTION_BITS = sizeof(BisectionID) * CHAR_BIT; std::vector getLargeCells(const std::size_t max_cell_size, const std::vector &cells) { std::vector large_cells; for (auto index = 0u; index < cells.size(); ++index) { if (!cells[index].tabu && cells[index].end - cells[index].begin > max_cell_size) large_cells.push_back(index); } return large_cells; } Partition cellsToPartition(const std::vector &cells, const std::vector &permutation) { Partition partition(permutation.size(), INVALID_CELL_ID); CellID cell_id = 0; for (const auto &cell : cells) { std::for_each(permutation.begin() + cell.begin, permutation.begin() + cell.end, [&partition, cell_id](const auto node_id) { partition[node_id] = cell_id; }); cell_id++; } BOOST_ASSERT(std::find(partition.begin(), partition.end(), INVALID_CELL_ID) == partition.end()); return partition; } void partitionLevel(const std::vector &node_to_bisection_id, std::size_t max_cell_size, std::vector &permutation, std::vector &cells) { for (auto large_cells = getLargeCells(max_cell_size, cells); large_cells.size() > 0; large_cells = getLargeCells(max_cell_size, cells)) { for (const auto cell_index : large_cells) { auto &cell = cells[cell_index]; BOOST_ASSERT(cell.bit < NUM_BISECTION_BITS); // Go over all nodes and sum up the bits to determine at which position the first one // bit is BisectionID sum = std::accumulate(permutation.begin() + cell.begin, permutation.begin() + cell.end, BisectionID{0}, [&node_to_bisection_id](const BisectionID lhs, const NodeID rhs) { return lhs | node_to_bisection_id[rhs]; }); // masks all bit strictly higher then cell.bit BOOST_ASSERT(sizeof(unsigned long long) * CHAR_BIT > sizeof(BisectionID) * CHAR_BIT); const BisectionID mask = (1ULL << (cell.bit + 1)) - 1; BOOST_ASSERT(mask == 0 || util::msb(mask) == cell.bit); const auto masked_sum = sum & mask; // we can't split the cell anymore, but it also doesn't conform to the max size // constraint // -> we need to remove it from the optimization if (masked_sum == 0) { cell.tabu = true; continue; } const auto bit = util::msb(masked_sum); // determines if an bisection ID is on the left side of the partition const BisectionID is_left_mask = 1ULL << bit; BOOST_ASSERT(util::msb(is_left_mask) == bit); std::uint32_t middle = std::partition(permutation.begin() + cell.begin, permutation.begin() + cell.end, [is_left_mask, &node_to_bisection_id](const auto node_id) { return node_to_bisection_id[node_id] & is_left_mask; }) - permutation.begin(); if (bit > 0) cell.bit = bit - 1; else cell.tabu = true; if (middle != cell.begin && middle != cell.end) { auto old_end = cell.end; cell.end = middle; cells.push_back( CellBisection{middle, old_end, static_cast(cell.bit), cell.tabu}); } } } } } // Implements a greedy algorithm that split cells using the bisection until a target cell size is // reached std::tuple, std::vector> bisectionToPartition(const std::vector &node_to_bisection_id, const std::vector &max_cell_sizes) { std::vector permutation(node_to_bisection_id.size()); std::iota(permutation.begin(), permutation.end(), 0); std::vector cells; cells.push_back(CellBisection{ 0, static_cast(node_to_bisection_id.size()), NUM_BISECTION_BITS - 1, false}); std::vector partitions(max_cell_sizes.size()); std::vector num_cells(max_cell_sizes.size()); int level_idx = max_cell_sizes.size() - 1; for (auto max_cell_size : boost::adaptors::reverse(max_cell_sizes)) { BOOST_ASSERT(level_idx >= 0); partitionLevel(node_to_bisection_id, max_cell_size, permutation, cells); partitions[level_idx] = cellsToPartition(cells, permutation); num_cells[level_idx] = cells.size(); level_idx--; } return std::make_tuple(std::move(partitions), std::move(num_cells)); } } }