Moved MultiLevelPartition and CellStorage to partition namespace
This commit is contained in:
committed by
Patrick Niklaus
parent
694bf9d8b1
commit
00d01946cd
@@ -1,350 +0,0 @@
|
||||
#ifndef OSRM_UTIL_CELL_STORAGE_HPP
|
||||
#define OSRM_UTIL_CELL_STORAGE_HPP
|
||||
|
||||
#include "util/assert.hpp"
|
||||
#include "util/for_each_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/multi_level_partition.hpp"
|
||||
#include "util/shared_memory_vector_wrapper.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace detail {
|
||||
template <bool UseShareMemory> class CellStorageImpl;
|
||||
}
|
||||
using CellStorage = detail::CellStorageImpl<false>;
|
||||
using CellStorageView = detail::CellStorageImpl<true>;
|
||||
}
|
||||
namespace partition {
|
||||
namespace io {
|
||||
template <bool UseShareMemory>
|
||||
inline void write(const boost::filesystem::path &path, const util::detail::CellStorageImpl<UseShareMemory> &storage);
|
||||
}
|
||||
}
|
||||
|
||||
namespace util
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <bool UseShareMemory> class CellStorageImpl
|
||||
{
|
||||
public:
|
||||
using WeightOffset = std::uint32_t;
|
||||
using BoundaryOffset = std::uint32_t;
|
||||
using BoundarySize = std::uint32_t;
|
||||
using SourceIndex = std::uint32_t;
|
||||
using DestinationIndex = std::uint32_t;
|
||||
|
||||
static constexpr auto INVALID_WEIGHT_OFFSET = std::numeric_limits<WeightOffset>::max();
|
||||
static constexpr auto INVALID_BOUNDARY_OFFSET = std::numeric_limits<BoundaryOffset>::max();
|
||||
|
||||
struct CellData
|
||||
{
|
||||
WeightOffset weight_offset = INVALID_WEIGHT_OFFSET;
|
||||
BoundaryOffset source_boundary_offset = INVALID_BOUNDARY_OFFSET;
|
||||
BoundaryOffset destination_boundary_offset = INVALID_BOUNDARY_OFFSET;
|
||||
BoundarySize num_source_nodes = 0;
|
||||
BoundarySize num_destination_nodes = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
template <typename T> using Vector = typename util::ShM<T, UseShareMemory>::vector;
|
||||
|
||||
// Implementation of the cell view. We need a template parameter here
|
||||
// because we need to derive a read-only and read-write view from this.
|
||||
template <typename WeightValueT> class CellImpl
|
||||
{
|
||||
private:
|
||||
using WeightPtrT = WeightValueT *;
|
||||
using WeightRefT = WeightValueT &;
|
||||
BoundarySize num_source_nodes;
|
||||
BoundarySize num_destination_nodes;
|
||||
|
||||
WeightPtrT const weights;
|
||||
const NodeID *const source_boundary;
|
||||
const NodeID *const destination_boundary;
|
||||
|
||||
using RowIterator = WeightPtrT;
|
||||
// Possibly replace with
|
||||
// http://www.boost.org/doc/libs/1_55_0/libs/range/doc/html/range/reference/adaptors/reference/strided.html
|
||||
class ColumnIterator : public std::iterator<std::random_access_iterator_tag, EdgeWeight>
|
||||
{
|
||||
public:
|
||||
explicit ColumnIterator(WeightPtrT begin, std::size_t row_length)
|
||||
: current(begin), stride(row_length)
|
||||
{
|
||||
BOOST_ASSERT(begin != nullptr);
|
||||
}
|
||||
|
||||
WeightRefT operator*() const { return *current; }
|
||||
|
||||
ColumnIterator &operator++()
|
||||
{
|
||||
current += stride;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ColumnIterator &operator+=(int amount)
|
||||
{
|
||||
current += stride * amount;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool operator==(const ColumnIterator &other) const { return current == other.current; }
|
||||
|
||||
bool operator!=(const ColumnIterator &other) const { return current != other.current; }
|
||||
|
||||
std::int64_t operator-(const ColumnIterator &other) const
|
||||
{
|
||||
return (current - other.current) / stride;
|
||||
}
|
||||
|
||||
private:
|
||||
WeightPtrT current;
|
||||
std::size_t stride;
|
||||
};
|
||||
|
||||
std::size_t GetRow(NodeID node) const
|
||||
{
|
||||
auto iter = std::find(source_boundary, source_boundary + num_source_nodes, node);
|
||||
BOOST_ASSERT(iter != source_boundary + num_source_nodes);
|
||||
return iter - source_boundary;
|
||||
}
|
||||
|
||||
std::size_t GetColumn(NodeID node) const
|
||||
{
|
||||
auto iter =
|
||||
std::find(destination_boundary, destination_boundary + num_destination_nodes, node);
|
||||
BOOST_ASSERT(iter != destination_boundary + num_destination_nodes);
|
||||
return iter - destination_boundary;
|
||||
}
|
||||
|
||||
public:
|
||||
auto GetOutWeight(NodeID node) const
|
||||
{
|
||||
auto row = GetRow(node);
|
||||
auto begin = weights + num_destination_nodes * row;
|
||||
auto end = begin + num_destination_nodes;
|
||||
return boost::make_iterator_range(begin, end);
|
||||
}
|
||||
|
||||
auto GetInWeight(NodeID node) const
|
||||
{
|
||||
auto column = GetColumn(node);
|
||||
auto begin = ColumnIterator{weights + column, num_destination_nodes};
|
||||
auto end = ColumnIterator{weights + column + num_source_nodes * num_destination_nodes,
|
||||
num_destination_nodes};
|
||||
return boost::make_iterator_range(begin, end);
|
||||
}
|
||||
|
||||
auto GetSourceNodes() const
|
||||
{
|
||||
return boost::make_iterator_range(source_boundary, source_boundary + num_source_nodes);
|
||||
}
|
||||
|
||||
auto GetDestinationNodes() const
|
||||
{
|
||||
return boost::make_iterator_range(destination_boundary,
|
||||
destination_boundary + num_destination_nodes);
|
||||
}
|
||||
|
||||
CellImpl(const CellData &data,
|
||||
WeightPtrT const all_weight,
|
||||
const NodeID *const all_sources,
|
||||
const NodeID *const all_destinations)
|
||||
: num_source_nodes{data.num_source_nodes},
|
||||
num_destination_nodes{data.num_destination_nodes},
|
||||
weights{all_weight + data.weight_offset},
|
||||
source_boundary{all_sources + data.source_boundary_offset},
|
||||
destination_boundary{all_destinations + data.destination_boundary_offset}
|
||||
{
|
||||
BOOST_ASSERT(all_weight != nullptr);
|
||||
BOOST_ASSERT(all_sources != nullptr);
|
||||
BOOST_ASSERT(all_destinations != nullptr);
|
||||
}
|
||||
};
|
||||
|
||||
std::size_t LevelIDToIndex(LevelID level) const { return level - 1; }
|
||||
|
||||
public:
|
||||
using Cell = CellImpl<EdgeWeight>;
|
||||
using ConstCell = CellImpl<const EdgeWeight>;
|
||||
|
||||
CellStorageImpl() {}
|
||||
|
||||
template <typename GraphT, typename = std::enable_if<!UseShareMemory>>
|
||||
CellStorageImpl(const MultiLevelPartition &partition, const GraphT &base_graph)
|
||||
{
|
||||
// pre-allocate storge for CellData so we can have random access to it by cell id
|
||||
unsigned number_of_cells = 0;
|
||||
for (LevelID level = 1u; level < partition.GetNumberOfLevels(); ++level)
|
||||
{
|
||||
level_to_cell_offset.push_back(number_of_cells);
|
||||
number_of_cells += partition.GetNumberOfCells(level);
|
||||
}
|
||||
level_to_cell_offset.push_back(number_of_cells);
|
||||
cells.resize(number_of_cells);
|
||||
|
||||
std::vector<std::pair<CellID, NodeID>> level_source_boundary;
|
||||
std::vector<std::pair<CellID, NodeID>> level_destination_boundary;
|
||||
|
||||
for (LevelID level = 1u; level < partition.GetNumberOfLevels(); ++level)
|
||||
{
|
||||
auto level_offset = level_to_cell_offset[LevelIDToIndex(level)];
|
||||
|
||||
level_source_boundary.clear();
|
||||
level_destination_boundary.clear();
|
||||
|
||||
for (auto node = 0u; node < base_graph.GetNumberOfNodes(); ++node)
|
||||
{
|
||||
const CellID cell_id = partition.GetCell(level, node);
|
||||
bool is_source_node = false;
|
||||
bool is_destination_node = false;
|
||||
bool is_boundary_node = false;
|
||||
|
||||
for (auto edge : base_graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
auto other = base_graph.GetTarget(edge);
|
||||
const auto &data = base_graph.GetEdgeData(edge);
|
||||
|
||||
is_boundary_node |= partition.GetCell(level, other) != cell_id;
|
||||
is_source_node |= partition.GetCell(level, other) == cell_id && data.forward;
|
||||
is_destination_node |=
|
||||
partition.GetCell(level, other) == cell_id && data.backward;
|
||||
}
|
||||
|
||||
if (is_boundary_node)
|
||||
{
|
||||
if (is_source_node)
|
||||
level_source_boundary.emplace_back(cell_id, node);
|
||||
if (is_destination_node)
|
||||
level_destination_boundary.emplace_back(cell_id, node);
|
||||
// a partition that contains boundary nodes that have no arcs going into
|
||||
// the cells or coming out of it is invalid. These nodes should be reassigned
|
||||
// to a different cell.
|
||||
BOOST_ASSERT_MSG(
|
||||
is_source_node || is_destination_node,
|
||||
"Node needs to either have incoming or outgoing edges in cell");
|
||||
}
|
||||
}
|
||||
|
||||
tbb::parallel_sort(level_source_boundary.begin(), level_source_boundary.end());
|
||||
tbb::parallel_sort(level_destination_boundary.begin(),
|
||||
level_destination_boundary.end());
|
||||
|
||||
const auto insert_cell_boundary = [this, level_offset](auto &boundary,
|
||||
auto set_num_nodes_fn,
|
||||
auto set_boundary_offset_fn,
|
||||
auto begin,
|
||||
auto end) {
|
||||
BOOST_ASSERT(std::distance(begin, end) > 0);
|
||||
|
||||
const auto cell_id = begin->first;
|
||||
BOOST_ASSERT(level_offset + cell_id < cells.size());
|
||||
auto &cell = cells[level_offset + cell_id];
|
||||
set_num_nodes_fn(cell, std::distance(begin, end));
|
||||
set_boundary_offset_fn(cell, boundary.size());
|
||||
|
||||
std::transform(begin,
|
||||
end,
|
||||
std::back_inserter(boundary),
|
||||
[](const auto &cell_and_node) { return cell_and_node.second; });
|
||||
};
|
||||
|
||||
util::for_each_range(
|
||||
level_source_boundary.begin(),
|
||||
level_source_boundary.end(),
|
||||
[this, insert_cell_boundary](auto begin, auto end) {
|
||||
insert_cell_boundary(
|
||||
source_boundary,
|
||||
[](auto &cell, auto value) { cell.num_source_nodes = value; },
|
||||
[](auto &cell, auto value) { cell.source_boundary_offset = value; },
|
||||
begin,
|
||||
end);
|
||||
});
|
||||
util::for_each_range(
|
||||
level_destination_boundary.begin(),
|
||||
level_destination_boundary.end(),
|
||||
[this, insert_cell_boundary](auto begin, auto end) {
|
||||
insert_cell_boundary(
|
||||
destination_boundary,
|
||||
[](auto &cell, auto value) { cell.num_destination_nodes = value; },
|
||||
[](auto &cell, auto value) { cell.destination_boundary_offset = value; },
|
||||
begin,
|
||||
end);
|
||||
});
|
||||
}
|
||||
|
||||
// Set weight offsets and calculate total storage size
|
||||
WeightOffset weight_offset = 0;
|
||||
for (auto &cell : cells)
|
||||
{
|
||||
cell.weight_offset = weight_offset;
|
||||
weight_offset += cell.num_source_nodes * cell.num_destination_nodes;
|
||||
}
|
||||
|
||||
weights.resize(weight_offset + 1, INVALID_EDGE_WEIGHT);
|
||||
}
|
||||
|
||||
template <typename = std::enable_if<UseShareMemory>>
|
||||
CellStorageImpl(Vector<EdgeWeight> weights_,
|
||||
Vector<NodeID> source_boundary_,
|
||||
Vector<NodeID> destination_boundary_,
|
||||
Vector<CellData> cells_,
|
||||
Vector<std::uint64_t> level_to_cell_offset_)
|
||||
: weights(std::move(weights_)), source_boundary(std::move(source_boundary_)),
|
||||
destination_boundary(std::move(destination_boundary_)), cells(std::move(cells_)),
|
||||
level_to_cell_offset(std::move(level_to_cell_offset_))
|
||||
{
|
||||
}
|
||||
|
||||
ConstCell GetCell(LevelID level, CellID id) const
|
||||
{
|
||||
const auto level_index = LevelIDToIndex(level);
|
||||
BOOST_ASSERT(level_index < level_to_cell_offset.size());
|
||||
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()};
|
||||
}
|
||||
|
||||
template <typename = std::enable_if<!UseShareMemory>>
|
||||
Cell GetCell(LevelID level, CellID id)
|
||||
{
|
||||
const auto level_index = LevelIDToIndex(level);
|
||||
BOOST_ASSERT(level_index < level_to_cell_offset.size());
|
||||
const auto offset = level_to_cell_offset[level_index];
|
||||
const auto cell_index = offset + id;
|
||||
BOOST_ASSERT(cell_index < cells.size());
|
||||
return Cell{
|
||||
cells[cell_index], weights.data(), source_boundary.data(), destination_boundary.data()};
|
||||
}
|
||||
|
||||
friend void partition::io::write<UseShareMemory>(const boost::filesystem::path &path, const util::detail::CellStorageImpl<UseShareMemory> &storage);
|
||||
|
||||
private:
|
||||
Vector<EdgeWeight> weights;
|
||||
Vector<NodeID> source_boundary;
|
||||
Vector<NodeID> destination_boundary;
|
||||
Vector<CellData> cells;
|
||||
Vector<std::uint64_t> level_to_cell_offset;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -1,358 +0,0 @@
|
||||
#ifndef OSRM_UTIL_MULTI_LEVEL_PARTITION_HPP
|
||||
#define OSRM_UTIL_MULTI_LEVEL_PARTITION_HPP
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/for_each_pair.hpp"
|
||||
#include "util/shared_memory_vector_wrapper.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <climits>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/range/adaptor/reversed.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
|
||||
namespace util
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <bool UseShareMemory> class MultiLevelPartitionImpl;
|
||||
}
|
||||
using MultiLevelPartition = detail::MultiLevelPartitionImpl<false>;
|
||||
using MultiLevelPartitionView = detail::MultiLevelPartitionImpl<true>;
|
||||
}
|
||||
|
||||
namespace partition
|
||||
{
|
||||
namespace io
|
||||
{
|
||||
template <bool UseShareMemory>
|
||||
void write(const boost::filesystem::path &file,
|
||||
const util::detail::MultiLevelPartitionImpl<UseShareMemory> &mlp);
|
||||
}
|
||||
}
|
||||
|
||||
namespace util
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
// get the msb of an integer
|
||||
// return 0 for integers without msb
|
||||
template <typename T> std::size_t highestMSB(T value)
|
||||
{
|
||||
static_assert(std::is_integral<T>::value, "Integer required.");
|
||||
std::size_t msb = 0;
|
||||
while (value > 0)
|
||||
{
|
||||
value >>= 1u;
|
||||
msb++;
|
||||
}
|
||||
return msb;
|
||||
}
|
||||
|
||||
#if (defined(__clang__) || defined(__GNUC__) || defined(__GNUG__)) && __x86_64__
|
||||
inline std::size_t highestMSB(std::uint64_t v)
|
||||
{
|
||||
BOOST_ASSERT(v > 0);
|
||||
return 63UL - __builtin_clzl(v);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
using LevelID = std::uint8_t;
|
||||
using CellID = std::uint32_t;
|
||||
using PartitionID = std::uint64_t;
|
||||
|
||||
static constexpr CellID INVALID_CELL_ID = std::numeric_limits<CellID>::max();
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <bool UseShareMemory> class MultiLevelPartitionImpl final
|
||||
{
|
||||
// we will support at most 16 levels
|
||||
static const constexpr std::uint8_t MAX_NUM_LEVEL = 16;
|
||||
static const constexpr std::uint8_t NUM_PARTITION_BITS = sizeof(PartitionID) * CHAR_BIT;
|
||||
|
||||
template <typename T> using Vector = typename util::ShM<T, UseShareMemory>::vector;
|
||||
|
||||
public:
|
||||
// Contains all data necessary to describe the level hierarchy
|
||||
struct LevelData
|
||||
{
|
||||
|
||||
std::uint32_t num_level;
|
||||
std::array<std::uint8_t, MAX_NUM_LEVEL - 1> lidx_to_offset;
|
||||
std::array<PartitionID, MAX_NUM_LEVEL - 1> lidx_to_mask;
|
||||
std::array<LevelID, NUM_PARTITION_BITS> bit_to_level;
|
||||
std::array<std::uint32_t, MAX_NUM_LEVEL - 1> lidx_to_children_offsets;
|
||||
};
|
||||
|
||||
MultiLevelPartitionImpl() = default;
|
||||
|
||||
// cell_sizes is index by level (starting at 0, the base graph).
|
||||
// However level 0 always needs to have cell size 1, since it is the
|
||||
// basegraph.
|
||||
template <typename = typename std::enable_if<!UseShareMemory>>
|
||||
MultiLevelPartitionImpl(const std::vector<std::vector<CellID>> &partitions,
|
||||
const std::vector<std::uint32_t> &lidx_to_num_cells)
|
||||
: level_data(MakeLevelData(lidx_to_num_cells))
|
||||
{
|
||||
InitializePartitionIDs(partitions);
|
||||
}
|
||||
|
||||
template <typename = typename std::enable_if<UseShareMemory>>
|
||||
MultiLevelPartitionImpl(LevelData level_data,
|
||||
Vector<PartitionID> partition_,
|
||||
Vector<CellID> cell_to_children_)
|
||||
: level_data(std::move(level_data)), partition(std::move(partition_)),
|
||||
cell_to_children(std::move(cell_to_children_))
|
||||
{
|
||||
}
|
||||
|
||||
// returns the index of the cell the vertex is contained at level l
|
||||
CellID GetCell(LevelID l, NodeID node) const
|
||||
{
|
||||
auto p = partition[node];
|
||||
auto lidx = LevelIDToIndex(l);
|
||||
auto masked = p & level_data.lidx_to_mask[lidx];
|
||||
return masked >> level_data.lidx_to_offset[lidx];
|
||||
}
|
||||
|
||||
LevelID GetQueryLevel(NodeID start, NodeID target, NodeID node) const
|
||||
{
|
||||
return std::min(GetHighestDifferentLevel(start, node),
|
||||
GetHighestDifferentLevel(target, node));
|
||||
}
|
||||
|
||||
LevelID GetHighestDifferentLevel(NodeID first, NodeID second) const
|
||||
{
|
||||
if (partition[first] == partition[second])
|
||||
return 0;
|
||||
|
||||
auto msb = detail::highestMSB(partition[first] ^ partition[second]);
|
||||
return level_data.bit_to_level[msb];
|
||||
}
|
||||
|
||||
std::uint8_t GetNumberOfLevels() const { return level_data.num_level; }
|
||||
|
||||
std::uint32_t GetNumberOfCells(LevelID level) const
|
||||
{
|
||||
return GetCell(level, GetSenitileNode());
|
||||
}
|
||||
|
||||
// Returns the lowest cell id (at `level - 1`) of all children `cell` at `level`
|
||||
CellID BeginChildren(LevelID level, CellID cell) const
|
||||
{
|
||||
BOOST_ASSERT(level > 1);
|
||||
auto lidx = LevelIDToIndex(level);
|
||||
auto offset = level_data.lidx_to_children_offsets[lidx];
|
||||
return cell_to_children[offset + cell];
|
||||
}
|
||||
|
||||
// Returns the highest cell id (at `level - 1`) of all children `cell` at `level`
|
||||
CellID EndChildren(LevelID level, CellID cell) const
|
||||
{
|
||||
BOOST_ASSERT(level > 1);
|
||||
auto lidx = LevelIDToIndex(level);
|
||||
auto offset = level_data.lidx_to_children_offsets[lidx];
|
||||
return cell_to_children[offset + cell + 1];
|
||||
}
|
||||
|
||||
friend void partition::io::write<UseShareMemory>(const boost::filesystem::path &file,
|
||||
const MultiLevelPartitionImpl &mlp);
|
||||
|
||||
private:
|
||||
auto MakeLevelData(const std::vector<std::uint32_t> &lidx_to_num_cells)
|
||||
{
|
||||
std::uint32_t num_level = lidx_to_num_cells.size() + 1;
|
||||
auto offsets = MakeLevelOffsets(lidx_to_num_cells);
|
||||
auto masks = MakeLevelMasks(offsets, num_level);
|
||||
auto bits = MakeBitToLevel(offsets, num_level);
|
||||
return LevelData{num_level, offsets, masks, bits, {0}};
|
||||
}
|
||||
|
||||
inline std::size_t LevelIDToIndex(LevelID l) const { return l - 1; }
|
||||
|
||||
// We save the sentinel as last node in the partition information.
|
||||
// It has the highest cell id in each level so we can derived the range
|
||||
// of cell ids efficiently.
|
||||
inline NodeID GetSenitileNode() const { return partition.size() - 1; }
|
||||
|
||||
void SetCellID(LevelID l, NodeID node, std::size_t cell_id)
|
||||
{
|
||||
auto lidx = LevelIDToIndex(l);
|
||||
|
||||
auto shifted_id = cell_id << level_data.lidx_to_offset[lidx];
|
||||
auto cleared_cell = partition[node] & ~level_data.lidx_to_mask[lidx];
|
||||
partition[node] = cleared_cell | shifted_id;
|
||||
}
|
||||
|
||||
// If we have N cells per level we need log_2 bits for every cell ID
|
||||
auto MakeLevelOffsets(const std::vector<std::uint32_t> &lidx_to_num_cells) const
|
||||
{
|
||||
std::array<std::uint8_t, MAX_NUM_LEVEL - 1> offsets;
|
||||
|
||||
auto lidx = 0UL;
|
||||
auto sum_bits = 0;
|
||||
for (auto num_cells : lidx_to_num_cells)
|
||||
{
|
||||
// bits needed to number all contained vertexes
|
||||
auto bits = static_cast<std::uint64_t>(std::ceil(std::log2(num_cells + 1)));
|
||||
offsets[lidx++] = sum_bits;
|
||||
sum_bits += bits;
|
||||
if (sum_bits > 64)
|
||||
{
|
||||
throw util::exception(
|
||||
"Can't pack the partition information at level " + std::to_string(lidx) +
|
||||
" into a 64bit integer. Would require " + std::to_string(sum_bits) + " bits.");
|
||||
}
|
||||
}
|
||||
// sentinel
|
||||
offsets[lidx++] = sum_bits;
|
||||
BOOST_ASSERT(lidx < MAX_NUM_LEVEL);
|
||||
|
||||
return offsets;
|
||||
}
|
||||
|
||||
auto MakeLevelMasks(const std::array<std::uint8_t, MAX_NUM_LEVEL - 1> &level_offsets,
|
||||
std::uint32_t num_level) const
|
||||
{
|
||||
std::array<PartitionID, MAX_NUM_LEVEL - 1> masks;
|
||||
|
||||
auto lidx = 0UL;
|
||||
util::for_each_pair(level_offsets.begin(),
|
||||
level_offsets.begin() + num_level,
|
||||
[&](const auto offset, const auto next_offset) {
|
||||
// create mask that has `bits` ones at its LSBs.
|
||||
// 000011
|
||||
BOOST_ASSERT(offset < NUM_PARTITION_BITS);
|
||||
PartitionID mask = (1UL << offset) - 1UL;
|
||||
// 001111
|
||||
BOOST_ASSERT(next_offset < NUM_PARTITION_BITS);
|
||||
PartitionID next_mask = (1UL << next_offset) - 1UL;
|
||||
// 001100
|
||||
masks[lidx++] = next_mask ^ mask;
|
||||
});
|
||||
|
||||
return masks;
|
||||
}
|
||||
|
||||
auto MakeBitToLevel(const std::array<std::uint8_t, MAX_NUM_LEVEL - 1> &level_offsets,
|
||||
std::uint32_t num_level) const
|
||||
{
|
||||
std::array<LevelID, NUM_PARTITION_BITS> bit_to_level;
|
||||
|
||||
for (auto l = 1u; l < num_level; ++l)
|
||||
{
|
||||
auto bits = level_offsets[l - 1];
|
||||
// set all bits to point to the correct level.
|
||||
for (auto idx = bits; idx < NUM_PARTITION_BITS; ++idx)
|
||||
{
|
||||
bit_to_level[idx] = l;
|
||||
}
|
||||
}
|
||||
|
||||
return bit_to_level;
|
||||
}
|
||||
|
||||
void InitializePartitionIDs(const std::vector<std::vector<CellID>> &partitions)
|
||||
{
|
||||
auto num_nodes = partitions.front().size();
|
||||
std::vector<NodeID> permutation(num_nodes);
|
||||
std::iota(permutation.begin(), permutation.end(), 0);
|
||||
// We include a sentinel element at the end of the partition
|
||||
partition.resize(num_nodes + 1, 0);
|
||||
NodeID sentinel = num_nodes;
|
||||
|
||||
// Sort nodes bottum-up by cell id.
|
||||
// This ensures that we get a nice grouping from parent to child cells:
|
||||
//
|
||||
// intitial:
|
||||
// level 0: 0 1 2 3 4 5
|
||||
// level 1: 2 1 3 4 3 4
|
||||
// level 2: 2 2 0 1 0 1
|
||||
//
|
||||
// first round:
|
||||
// level 0: 1 0 2 4 3 5
|
||||
// level 1: 1 2 3 3 4 4 (< sorted)
|
||||
// level 2: 2 2 0 0 1 1
|
||||
//
|
||||
// second round:
|
||||
// level 0: 2 4 3 5 1 0
|
||||
// level 1: 3 3 4 4 1 2
|
||||
// level 2: 0 0 1 1 2 2 (< sorted)
|
||||
for (const auto &partition : partitions)
|
||||
{
|
||||
std::stable_sort(permutation.begin(),
|
||||
permutation.end(),
|
||||
[&partition](const auto lhs, const auto rhs) {
|
||||
return partition[lhs] < partition[rhs];
|
||||
});
|
||||
}
|
||||
|
||||
// top down assign new cell ids
|
||||
LevelID level = partitions.size();
|
||||
for (const auto &partition : boost::adaptors::reverse(partitions))
|
||||
{
|
||||
BOOST_ASSERT(permutation.size() > 0);
|
||||
CellID last_cell_id = partition[permutation.front()];
|
||||
CellID cell_id = 0;
|
||||
for (const auto node : permutation)
|
||||
{
|
||||
if (last_cell_id != partition[node])
|
||||
{
|
||||
cell_id++;
|
||||
last_cell_id = partition[node];
|
||||
}
|
||||
SetCellID(level, node, cell_id);
|
||||
}
|
||||
// Store the number of cells of the level in the sentinel
|
||||
SetCellID(level, sentinel, cell_id + 1);
|
||||
level--;
|
||||
}
|
||||
|
||||
level_data.lidx_to_children_offsets[0] = 0;
|
||||
|
||||
for (auto level_idx = 0UL; level_idx < partitions.size() - 1; ++level_idx)
|
||||
{
|
||||
const auto &parent_partition = partitions[level_idx + 1];
|
||||
|
||||
level_data.lidx_to_children_offsets[level_idx + 1] = cell_to_children.size();
|
||||
|
||||
CellID last_parent_id = parent_partition[permutation.front()];
|
||||
cell_to_children.push_back(GetCell(level_idx + 1, permutation.front()));
|
||||
for (const auto node : permutation)
|
||||
{
|
||||
if (last_parent_id != parent_partition[node])
|
||||
{
|
||||
// Note: we use the new cell id here, not the ones contained
|
||||
// in the input partition
|
||||
cell_to_children.push_back(GetCell(level_idx + 1, node));
|
||||
last_parent_id = parent_partition[node];
|
||||
}
|
||||
}
|
||||
// insert sentinel for the last cell
|
||||
cell_to_children.push_back(GetCell(level_idx + 1, permutation.back()) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
//! this is always owned by this class because it is so small
|
||||
LevelData level_data;
|
||||
Vector<PartitionID> partition;
|
||||
Vector<CellID> cell_to_children;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user