Pull everthing in the facades

This commit is contained in:
Patrick Niklaus 2017-03-01 22:55:18 +00:00 committed by Patrick Niklaus
parent ff0a98196f
commit 108fce896b
17 changed files with 478 additions and 283 deletions

View File

@ -23,10 +23,10 @@ struct MLD final
{
};
template<typename AlgorithmT> const char* name();
template<> inline const char* name<CH>() { return "CH"; }
template<> inline const char* name<CoreCH>() { return "CoreCH"; }
template<> inline const char* name<MLD>() { return "MLD"; }
template <typename AlgorithmT> const char *name();
template <> inline const char *name<CH>() { return "CH"; }
template <> inline const char *name<CoreCH>() { return "CoreCH"; }
template <> inline const char *name<MLD>() { return "MLD"; }
}
namespace algorithm_trais

View File

@ -3,7 +3,9 @@
#include "contractor/query_edge.hpp"
#include "engine/algorithm.hpp"
#include "util/cell_storage.hpp"
#include "util/integer_range.hpp"
#include "util/multi_level_partition.hpp"
namespace osrm
{
@ -58,6 +60,14 @@ template <> class AlgorithmDataFacade<algorithm::CoreCH>
virtual bool IsCoreNode(const NodeID id) const = 0;
};
template <> class AlgorithmDataFacade<algorithm::MLD>
{
public:
virtual const util::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
virtual const util::CellStorage<true> &GetCellStorage() const = 0;
};
}
}
}

View File

@ -251,10 +251,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
std::shared_ptr<util::RangeTable<16, true>> m_bearing_ranges_table;
util::ShM<DiscreteBearing, true>::vector m_bearing_values_table;
// MLD data
util::PackedMultiLevelPartition<true> mld_partition;
util::CellStorage<true> mld_cell_storage;
// allocator that keeps the allocation data
std::shared_ptr<ContiguousBlockAllocator> allocator;
@ -540,27 +536,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
m_entry_class_table = std::move(entry_class_table);
}
void InitializeMLDDataPointers(storage::DataLayout &data_layout, char *memory_block)
{
if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_PARTITION) > 0)
{
auto mld_partition_ptr =
data_layout.GetBlockPtr<char>(memory_block, storage::DataLayout::MLD_CELL_PARTITION);
mld_partition.InitializePointers(
mld_partition_ptr,
mld_partition_ptr + data_layout.num_entries[storage::DataLayout::MLD_CELL_PARTITION]);
}
if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_STORAGE) > 0)
{
auto mld_cell_storage_ptr =
data_layout.GetBlockPtr<char>(memory_block, storage::DataLayout::MLD_CELL_STORAGE);
mld_cell_storage.InitializePointers(
mld_cell_storage_ptr,
mld_cell_storage_ptr + data_layout.num_entries[storage::DataLayout::MLD_CELL_STORAGE]);
}
}
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
{
InitializeChecksumPointer(data_layout, memory_block);
@ -574,7 +549,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
InitializeProfilePropertiesPointer(data_layout, memory_block);
InitializeRTreePointers(data_layout, memory_block);
InitializeIntersectionClassPointers(data_layout, memory_block);
InitializeMLDDataPointers(data_layout, memory_block);
}
public:
@ -1077,13 +1051,6 @@ class ContiguousInternalMemoryDataFacadeBase : public BaseDataFacade
m_lane_description_masks.begin() +
m_lane_description_offsets[lane_description_id + 1]);
}
const util::PackedMultiLevelPartition<true> &GetMultiLevelPartition() const
{
return mld_partition;
}
const util::CellStorage<true> &GetCellStorage() const { return mld_cell_storage; }
};
template <typename AlgorithmT> class ContiguousInternalMemoryDataFacade;
@ -1115,6 +1082,83 @@ class ContiguousInternalMemoryDataFacade<algorithm::CoreCH> final
{
}
};
template <>
class ContiguousInternalMemoryAlgorithmDataFacade<algorithm::MLD>
: public datafacade::AlgorithmDataFacade<algorithm::MLD>
{
// MLD data
util::MultiLevelPartitionView mld_partition;
util::CellStorage<true> mld_cell_storage;
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
{
InitializeMLDDataPointers(data_layout, memory_block);
}
void InitializeMLDDataPointers(storage::DataLayout &data_layout, char *memory_block)
{
if (data_layout.GetBlockSize(storage::DataLayout::MLD_PARTITION) > 0)
{
BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_LEVEL_DATA) > 0);
BOOST_ASSERT(data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_TO_CHILDREN) > 0);
auto level_data = *data_layout.GetBlockPtr<util::MultiLevelPartitionView::LevelData>(memory_block, storage::DataLayout::MLD_PARTITION);
auto mld_partition_ptr = data_layout.GetBlockPtr<util::PartitionID>(memory_block, storage::DataLayout::MLD_PARTITION);
auto partition_entries_count = data_layout.GetBlockEntries(storage::DataLayout::MLD_PARTITION);
util::ShM<util::PartitionID, true>::vector partition(mld_partition_ptr, partition_entries_count);
auto mld_chilren_ptr = data_layout.GetBlockPtr<util::CellID>(memory_block, storage::DataLayout::MLD_CELL_TO_CHILDREN);
auto children_entries_count = data_layout.GetBlockEntries(storage::DataLayout::MLD_CELL_TO_CHILDREN);
util::ShM<util::CellID, true>::vector cell_to_children(mld_chilren_ptr, children_entries_count);
mld_partition = util::MultiLevelPartitionView{level_data, partition, cell_to_children};
}
if (data_layout.GetBlockSize(storage::DataLayout::MLD_CELL_STORAGE) > 0)
{
auto mld_cell_storage_ptr =
data_layout.GetBlockPtr<char>(memory_block, storage::DataLayout::MLD_CELL_STORAGE);
mld_cell_storage.InitializePointers(
mld_cell_storage_ptr,
mld_cell_storage_ptr +
data_layout.num_entries[storage::DataLayout::MLD_CELL_STORAGE]);
}
}
// allocator that keeps the allocation data
std::shared_ptr<ContiguousBlockAllocator> allocator;
public:
ContiguousInternalMemoryAlgorithmDataFacade(
std::shared_ptr<ContiguousBlockAllocator> allocator_)
: allocator(std::move(allocator_))
{
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
}
const util::MultiLevelPartitionView &GetMultiLevelPartition() const
{
return mld_partition;
}
const util::CellStorage<true> &GetCellStorage() const { return mld_cell_storage; }
};
template <>
class ContiguousInternalMemoryDataFacade<algorithm::MLD>
: public ContiguousInternalMemoryDataFacadeBase,
public ContiguousInternalMemoryAlgorithmDataFacade<algorithm::MLD>
{
public:
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator)
: ContiguousInternalMemoryDataFacadeBase(allocator),
ContiguousInternalMemoryAlgorithmDataFacade<algorithm::MLD>(allocator)
{
}
};
}
}
}

View File

@ -61,6 +61,9 @@ namespace engine
* now.
* - Algorithm::CoreCH
* Contractoin Hierachies with partial contraction for faster pre-processing but slower queries.
* - Algorithm::MLD
* Multi Level Dijkstra which is experimental and moderately fast in both pre-processing and
* query.
*
* Algorithm::CH is specified we will automatically upgrade to CoreCH if we find the data for it.
* If Algorithm::CoreCH is specified and we don't find the speedup data, we fail hard.
@ -73,8 +76,9 @@ struct EngineConfig final
enum class Algorithm
{
CH, // will upgrade to CoreCH if it finds core data
CoreCH // will fail hard if there is no core data
CH, // will upgrade to CoreCH if it finds core data
CoreCH, // will fail hard if there is no core data
MLD
};
storage::StorageConfig storage_config;

View File

@ -65,54 +65,29 @@ template <typename AlgorithmT> class RoutingAlgorithms final : public RoutingAlg
virtual ~RoutingAlgorithms() = default;
InternalRouteResult
AlternativePathSearch(const PhantomNodes &phantom_node_pair) const final override
{
return routing_algorithms::alternativePathSearch(heaps, facade, phantom_node_pair);
}
AlternativePathSearch(const PhantomNodes &phantom_node_pair) const final override;
InternalRouteResult ShortestPathSearch(
const std::vector<PhantomNodes> &phantom_node_pair,
const boost::optional<bool> continue_straight_at_waypoint) const final override
{
return routing_algorithms::shortestPathSearch(
heaps, facade, phantom_node_pair, continue_straight_at_waypoint);
}
const boost::optional<bool> continue_straight_at_waypoint) const final override;
InternalRouteResult
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override
{
return routing_algorithms::directShortestPathSearch(heaps, facade, phantom_nodes);
}
DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const final override;
std::vector<EdgeWeight>
ManyToManySearch(const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices) const final override
{
return routing_algorithms::manyToManySearch(
heaps, facade, phantom_nodes, source_indices, target_indices);
}
const std::vector<std::size_t> &target_indices) const final override;
routing_algorithms::SubMatchingList MapMatching(
const routing_algorithms::CandidateLists &candidates_list,
const std::vector<util::Coordinate> &trace_coordinates,
const std::vector<unsigned> &trace_timestamps,
const std::vector<boost::optional<double>> &trace_gps_precision) const final override
{
return routing_algorithms::mapMatching(heaps,
facade,
candidates_list,
trace_coordinates,
trace_timestamps,
trace_gps_precision);
}
const std::vector<boost::optional<double>> &trace_gps_precision) const final override;
std::vector<routing_algorithms::TurnData>
GetTileTurns(const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes) const final override
{
return routing_algorithms::getTileTurns(facade, edges, sorted_edge_indexes);
}
const std::vector<std::size_t> &sorted_edge_indexes) const final override;
bool HasAlternativePathSearch() const final override
{
@ -149,6 +124,108 @@ template <typename AlgorithmT> class RoutingAlgorithms final : public RoutingAlg
// Owned by shared-ptr passed to the query
const datafacade::ContiguousInternalMemoryDataFacade<AlgorithmT> &facade;
};
template <typename AlgorithmT>
InternalRouteResult
RoutingAlgorithms<AlgorithmT>::AlternativePathSearch(const PhantomNodes &phantom_node_pair) const
{
return routing_algorithms::alternativePathSearch(heaps, facade, phantom_node_pair);
}
template <typename AlgorithmT>
InternalRouteResult RoutingAlgorithms<AlgorithmT>::ShortestPathSearch(
const std::vector<PhantomNodes> &phantom_node_pair,
const boost::optional<bool> continue_straight_at_waypoint) const
{
return routing_algorithms::shortestPathSearch(
heaps, facade, phantom_node_pair, continue_straight_at_waypoint);
}
template <typename AlgorithmT>
InternalRouteResult
RoutingAlgorithms<AlgorithmT>::DirectShortestPathSearch(const PhantomNodes &phantom_nodes) const
{
return routing_algorithms::directShortestPathSearch(heaps, facade, phantom_nodes);
}
template <typename AlgorithmT>
std::vector<EdgeWeight> RoutingAlgorithms<AlgorithmT>::ManyToManySearch(
const std::vector<PhantomNode> &phantom_nodes,
const std::vector<std::size_t> &source_indices,
const std::vector<std::size_t> &target_indices) const
{
return routing_algorithms::manyToManySearch(
heaps, facade, phantom_nodes, source_indices, target_indices);
}
template <typename AlgorithmT>
inline routing_algorithms::SubMatchingList RoutingAlgorithms<AlgorithmT>::MapMatching(
const routing_algorithms::CandidateLists &candidates_list,
const std::vector<util::Coordinate> &trace_coordinates,
const std::vector<unsigned> &trace_timestamps,
const std::vector<boost::optional<double>> &trace_gps_precision) const
{
return routing_algorithms::mapMatching(
heaps, facade, candidates_list, trace_coordinates, trace_timestamps, trace_gps_precision);
}
template <typename AlgorithmT>
inline std::vector<routing_algorithms::TurnData> RoutingAlgorithms<AlgorithmT>::GetTileTurns(
const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &edges,
const std::vector<std::size_t> &sorted_edge_indexes) const
{
return routing_algorithms::getTileTurns(facade, edges, sorted_edge_indexes);
}
// MLD overrides for not implemented
template <>
InternalRouteResult inline RoutingAlgorithms<algorithm::MLD>::AlternativePathSearch(
const PhantomNodes &) const
{
throw util::exception("AlternativePathSearch is not implemented");
}
template <>
inline InternalRouteResult
RoutingAlgorithms<algorithm::MLD>::ShortestPathSearch(const std::vector<PhantomNodes> &,
const boost::optional<bool>) const
{
throw util::exception("ShortestPathSearch is not implemented");
}
template <>
InternalRouteResult inline RoutingAlgorithms<algorithm::MLD>::DirectShortestPathSearch(
const PhantomNodes &) const
{
throw util::exception("DirectShortestPathSearch is not implemented");
}
template <>
inline std::vector<EdgeWeight>
RoutingAlgorithms<algorithm::MLD>::ManyToManySearch(const std::vector<PhantomNode> &,
const std::vector<std::size_t> &,
const std::vector<std::size_t> &) const
{
throw util::exception("ManyToManySearch is not implemented");
}
template <>
inline routing_algorithms::SubMatchingList
RoutingAlgorithms<algorithm::MLD>::MapMatching(const routing_algorithms::CandidateLists &,
const std::vector<util::Coordinate> &,
const std::vector<unsigned> &,
const std::vector<boost::optional<double>> &) const
{
throw util::exception("MapMatching is not implemented");
}
template <>
inline std::vector<routing_algorithms::TurnData> RoutingAlgorithms<algorithm::MLD>::GetTileTurns(
const std::vector<datafacade::BaseDataFacade::RTreeLeaf> &,
const std::vector<std::size_t> &) const
{
throw util::exception("GetTileTurns is not implemented");
}
}
}

28
include/partition/io.hpp Normal file
View File

@ -0,0 +1,28 @@
#ifndef OSRM_PARTITION_IO_HPP
#define OSRM_PARTITION_IO_HPP
#include "storage/io.hpp"
#include "util/multi_level_partition.hpp"
namespace osrm
{
namespace partition
{
namespace io
{
template <>
inline void write(const boost::filesystem::path &path, const util::MultiLevelPartition &mlp)
{
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
storage::io::FileWriter writer{path, fingerprint};
writer.WriteOne(mlp.level_data);
writer.SerializeVector(mlp.partition);
writer.SerializeVector(mlp.cell_to_children);
}
}
}
}
#endif

View File

@ -13,10 +13,7 @@ namespace partition
struct PartitionConfig
{
PartitionConfig()
: requested_num_threads(0)
{
}
PartitionConfig() : requested_num_threads(0) {}
void UseDefaults()
{
@ -37,8 +34,8 @@ struct PartitionConfig
compressed_node_based_graph_path = basepath + ".osrm.cnbg";
cnbg_ebg_mapping_path = basepath + ".osrm.cnbg_to_ebg";
partition_path = basepath + ".osrm.partition";
mld_partition_path = basepath + ".osrm.mld_partition";
mld_storage_path = basepath + ".osrm.mld_storage";
mld_partition_path = basepath + ".osrm.partition";
mld_storage_path = basepath + ".osrm.cells";
}
// might be changed to the node based graph at some point

View File

@ -141,6 +141,13 @@ class FileReader
return sizeof(count) + sizeof(T) * count;
}
template <typename T> std::size_t ReadVectorSize()
{
const auto count = ReadElementCount64();
Skip<T>(count);
return count;
}
template <typename T> void *DeserializeVector(void *begin, const void *end)
{
auto count = ReadElementCount64();

View File

@ -57,7 +57,9 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
"LANE_DESCRIPTION_MASKS",
"TURN_WEIGHT_PENALTIES",
"TURN_DURATION_PENALTIES",
"MLD_CELL_PARTITION",
"MLD_LEVEL_DATA",
"MLD_PARTITION",
"MLD_CELL_TO_CHILDREN",
"MLD_CELL_STORAGE"};
struct DataLayout
@ -103,7 +105,9 @@ struct DataLayout
LANE_DESCRIPTION_MASKS,
TURN_WEIGHT_PENALTIES,
TURN_DURATION_PENALTIES,
MLD_CELL_PARTITION,
MLD_LEVEL_DATA,
MLD_PARTITION,
MLD_CELL_TO_CHILDREN,
MLD_CELL_STORAGE,
NUM_BLOCKS
};
@ -122,6 +126,11 @@ struct DataLayout
entry_align[bid] = alignof(T);
}
inline uint64_t GetBlockEntries(BlockID bid) const
{
return num_entries[bid];
}
inline uint64_t GetBlockSize(BlockID bid) const
{
// special bit encoding
@ -169,6 +178,13 @@ struct DataLayout
return ptr;
}
template <typename T>
inline T *GetBlockEnd(char *shared_memory, BlockID bid) const
{
auto begin = GetBlockPtr<T>(shared_memory, bid);
return begin + GetBlockEntries(bid);
}
template <typename T, bool WRITE_CANARY = false>
inline T *GetBlockPtr(char *shared_memory, BlockID bid) const
{

View File

@ -20,6 +20,27 @@
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
@ -49,187 +70,121 @@ inline std::size_t highestMSB(std::uint64_t v)
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();
// Mock interface, can be removed when we have an actual implementation
class MultiLevelPartition
namespace detail
{
public:
// Returns the cell id of `node` at `level`
virtual CellID GetCell(LevelID level, NodeID node) const = 0;
// Returns the lowest cell id (at `level - 1`) of all children `cell` at `level`
virtual CellID BeginChildren(LevelID level, CellID cell) const = 0;
// Returns the highest cell id (at `level - 1`) of all children `cell` at `level`
virtual CellID EndChildren(LevelID level, CellID cell) const = 0;
// Returns the highest level in which `first` and `second` are still in different cells
virtual LevelID GetHighestDifferentLevel(NodeID first, NodeID second) const = 0;
// Returns the level at which a `node` is relevant for a query from start to target
virtual LevelID GetQueryLevel(NodeID start, NodeID target, NodeID node) const = 0;
virtual std::uint8_t GetNumberOfLevels() const = 0;
virtual std::uint32_t GetNumberOfCells(LevelID level) const = 0;
};
template <bool UseShareMemory> class PackedMultiLevelPartition final : public MultiLevelPartition
template <bool UseShareMemory> class MultiLevelPartitionImpl final
{
using PartitionID = std::uint64_t;
// 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:
PackedMultiLevelPartition() {}
// 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.
PackedMultiLevelPartition(const std::vector<std::vector<CellID>> &partitions,
const std::vector<std::uint32_t> &level_to_num_cells)
: level_offsets(makeLevelOffsets(level_to_num_cells)), level_masks(makeLevelMasks()),
bit_to_level(makeBitToLevel())
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);
InitializePartitionIDs(partitions);
}
PackedMultiLevelPartition(const boost::filesystem::path &file_name) { Read(file_name); }
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 final override
CellID GetCell(LevelID l, NodeID node) const
{
auto p = partition[node];
auto lidx = LevelIDToIndex(l);
auto masked = p & level_masks[lidx];
return masked >> level_offsets[lidx];
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 final override
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 final override
LevelID GetHighestDifferentLevel(NodeID first, NodeID second) const
{
if (partition[first] == partition[second])
return 0;
auto msb = detail::highestMSB(partition[first] ^ partition[second]);
return bit_to_level[msb];
return level_data.bit_to_level[msb];
}
std::uint8_t GetNumberOfLevels() const final override { return level_offsets.size(); }
std::uint8_t GetNumberOfLevels() const { return level_data.num_level; }
std::uint32_t GetNumberOfCells(LevelID level) const final override
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 final override
CellID BeginChildren(LevelID level, CellID cell) const
{
BOOST_ASSERT(level > 1);
auto lidx = LevelIDToIndex(level);
auto offset = level_to_children_offset[lidx];
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 final override
CellID EndChildren(LevelID level, CellID cell) const
{
BOOST_ASSERT(level > 1);
auto lidx = LevelIDToIndex(level);
auto offset = level_to_children_offset[lidx];
auto offset = level_data.lidx_to_children_offsets[lidx];
return cell_to_children[offset + cell + 1];
}
std::size_t GetRequiredMemorySize(const boost::filesystem::path &path) const
{
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
std::size_t memory_size = 0;
memory_size += reader.GetVectorMemorySize<decltype(level_offsets[0])>();
memory_size += reader.GetVectorMemorySize<decltype(partition[0])>();
memory_size += reader.GetVectorMemorySize<decltype(level_to_children_offset[0])>();
memory_size += reader.GetVectorMemorySize<decltype(cell_to_children[0])>();
return memory_size;
}
template <bool Q = UseShareMemory>
typename std::enable_if<Q>::type
Read(const boost::filesystem::path &path, void *begin, const void *end) const
{
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
begin = reader.DeserializeVector<typename decltype(level_offsets)::value_type>(begin, end);
begin = reader.DeserializeVector<typename decltype(partition)::value_type>(begin, end);
begin = reader.DeserializeVector<typename decltype(level_to_children_offset)::value_type>(
begin, end);
begin =
reader.DeserializeVector<typename decltype(cell_to_children)::value_type>(begin, end);
}
template <bool Q = UseShareMemory>
typename std::enable_if<Q>::type InitializePointers(char *begin, const char *end)
{
auto level_offsets_size = *reinterpret_cast<std::uint64_t *>(begin);
begin += sizeof(level_offsets_size);
level_offsets.reset(begin, level_offsets_size);
begin += sizeof(decltype(level_offsets[0])) * level_offsets_size;
auto partition_size = *reinterpret_cast<std::uint64_t *>(begin);
begin += sizeof(partition_size);
partition.reset(begin, partition_size);
begin += sizeof(decltype(partition[0])) * partition_size;
auto level_to_children_offset_size = *reinterpret_cast<std::uint64_t *>(begin);
begin += sizeof(level_to_children_offset_size);
level_to_children_offset.reset(begin, level_to_children_offset_size);
begin += sizeof(decltype(level_to_children_offset[0])) * level_to_children_offset_size;
auto cell_to_children_size = *reinterpret_cast<std::uint64_t *>(begin);
begin += sizeof(cell_to_children_size);
cell_to_children.reset(begin, cell_to_children_size);
begin += sizeof(decltype(cell_to_children[0])) * level_to_children_offset_size;
BOOST_ASSERT(begin <= end);
level_masks = makeLevelMasks();
bit_to_level = makeBitToLevel();
}
template <bool Q = UseShareMemory>
typename std::enable_if<!Q>::type Read(const boost::filesystem::path &path)
{
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
reader.DeserializeVector(level_offsets);
reader.DeserializeVector(partition);
reader.DeserializeVector(level_to_children_offset);
reader.DeserializeVector(cell_to_children);
level_masks = makeLevelMasks();
bit_to_level = makeBitToLevel();
}
void Write(const boost::filesystem::path &path) const
{
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
storage::io::FileWriter writer{path, fingerprint};
writer.SerializeVector(level_offsets);
writer.SerializeVector(partition);
writer.SerializeVector(level_to_children_offset);
writer.SerializeVector(cell_to_children);
}
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.
@ -241,46 +196,45 @@ template <bool UseShareMemory> class PackedMultiLevelPartition final : public Mu
{
auto lidx = LevelIDToIndex(l);
auto shifted_id = cell_id << level_offsets[lidx];
auto cleared_cell = partition[node] & ~level_masks[lidx];
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
std::vector<std::uint8_t>
makeLevelOffsets(const std::vector<std::uint32_t> &level_to_num_cells) const
auto MakeLevelOffsets(const std::vector<std::uint32_t> &lidx_to_num_cells) const
{
std::vector<std::uint8_t> offsets;
offsets.reserve(level_to_num_cells.size());
std::array<std::uint8_t, MAX_NUM_LEVEL - 1> offsets;
auto lidx = 0UL;
auto sum_bits = 0;
for (auto num_cells : level_to_num_cells)
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.push_back(sum_bits);
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(offsets.size()) +
" into a 64bit integer. Would require " +
std::to_string(sum_bits) + " bits.");
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.push_back(sum_bits);
offsets[lidx++] = sum_bits;
BOOST_ASSERT(lidx < MAX_NUM_LEVEL);
return offsets;
}
std::vector<PartitionID> makeLevelMasks() const
auto MakeLevelMasks(const std::array<std::uint8_t, MAX_NUM_LEVEL - 1> &level_offsets, std::uint32_t num_level) const
{
std::vector<PartitionID> masks;
masks.reserve(level_offsets.size());
std::array<PartitionID, MAX_NUM_LEVEL - 1> masks;
auto lidx = 0UL;
util::for_each_pair(level_offsets.begin(),
level_offsets.end(),
level_offsets.begin() + num_level,
[&](const auto offset, const auto next_offset) {
// create mask that has `bits` ones at its LSBs.
// 000011
@ -290,31 +244,30 @@ template <bool UseShareMemory> class PackedMultiLevelPartition final : public Mu
BOOST_ASSERT(next_offset < NUM_PARTITION_BITS);
PartitionID next_mask = (1UL << next_offset) - 1UL;
// 001100
masks.push_back(next_mask ^ mask);
masks[lidx++] = next_mask ^ mask;
});
return masks;
}
std::array<LevelID, NUM_PARTITION_BITS> makeBitToLevel() const
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;
LevelID l = 1;
for (auto bits : level_offsets)
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;
}
l++;
}
return bit_to_level;
}
void initializePartitionIDs(const std::vector<std::vector<CellID>> &partitions)
void InitializePartitionIDs(const std::vector<std::vector<CellID>> &partitions)
{
auto num_nodes = partitions.front().size();
std::vector<NodeID> permutation(num_nodes);
@ -370,14 +323,13 @@ template <bool UseShareMemory> class PackedMultiLevelPartition final : public Mu
level--;
}
// level 1 does not have child cells
level_to_children_offset.push_back(0);
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_to_children_offset.push_back(cell_to_children.size());
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()));
@ -396,14 +348,13 @@ template <bool UseShareMemory> class PackedMultiLevelPartition final : public Mu
}
}
typename util::ShM<std::uint8_t, UseShareMemory>::vector level_offsets;
typename util::ShM<PartitionID, UseShareMemory>::vector partition;
typename util::ShM<std::uint32_t, UseShareMemory>::vector level_to_children_offset;
typename util::ShM<CellID, UseShareMemory>::vector cell_to_children;
std::vector<PartitionID> level_masks;
std::array<LevelID, NUM_PARTITION_BITS> bit_to_level;
//! this is always owned by this class because it is so small
LevelData level_data;
Vector<PartitionID> partition;
Vector<CellID> cell_to_children;
};
}
}
}
#endif

View File

@ -45,6 +45,9 @@ OSRM::OSRM(engine::EngineConfig &config)
case EngineConfig::Algorithm::CoreCH:
engine_ = std::make_unique<engine::Engine<engine::algorithm::CoreCH>>(config);
break;
case EngineConfig::Algorithm::MLD:
engine_ = std::make_unique<engine::Engine<engine::algorithm::MLD>>(config);
break;
default:
util::exception("Algorithm not implemented!");
}

View File

@ -5,6 +5,7 @@
#include "partition/edge_based_graph_reader.hpp"
#include "partition/node_based_graph_to_edge_based_graph_mapping_reader.hpp"
#include "partition/recursive_bisection.hpp"
#include "partition/io.hpp"
#include "util/cell_storage.hpp"
#include "util/coordinate.hpp"
@ -21,7 +22,6 @@
#include <vector>
#include <boost/assert.hpp>
#include <boost/container/small_vector.hpp>
#include "util/geojson_debug_logger.hpp"
#include "util/geojson_debug_policies.hpp"
@ -203,7 +203,7 @@ int Partitioner::Run(const PartitionConfig &config)
// split bisection id bits into groups starting from SCC and stop at level 1
BOOST_ASSERT(recursive_bisection.SCCDepth() != 0);
int mask_from = sizeof(BisectionID) * CHAR_BIT - recursive_bisection.SCCDepth();
boost::container::small_vector<BisectionID, 8> level_masks;
std::vector<BisectionID> level_masks;
for (int mask_to = sizeof(BisectionID) * CHAR_BIT; mask_to > first_nonzero_position;
mask_to = mask_from, mask_from -= 3) // TODO: find better grouping
{
@ -250,7 +250,7 @@ int Partitioner::Run(const PartitionConfig &config)
}
TIMER_START(packed_mlp);
osrm::util::PackedMultiLevelPartition<false> mlp{partitions, level_to_num_cells};
osrm::util::MultiLevelPartition mlp{partitions, level_to_num_cells};
TIMER_STOP(packed_mlp);
util::Log() << "PackedMultiLevelPartition constructed in " << TIMER_SEC(packed_mlp)
<< " seconds";
@ -261,7 +261,7 @@ int Partitioner::Run(const PartitionConfig &config)
util::Log() << "CellStorage constructed in " << TIMER_SEC(cell_storage) << " seconds";
TIMER_START(writing_mld_data);
mlp.Write(config.mld_partition_path);
io::write(config.mld_partition_path, mlp);
storage.Write(config.mld_storage_path);
TIMER_STOP(writing_mld_data);
util::Log() << "MLD data writing took " << TIMER_SEC(writing_mld_data) << " seconds";

View File

@ -400,12 +400,21 @@ void Storage::PopulateLayout(DataLayout &layout)
// Loading MLD Data
if (boost::filesystem::exists(config.mld_partition_path))
{
auto mld_partition_size = util::PackedMultiLevelPartition<true>().GetRequiredMemorySize(
config.mld_partition_path);
layout.SetBlockSize<char>(DataLayout::MLD_CELL_PARTITION, mld_partition_size);
io::FileReader reader(config.mld_partition_path, io::FileReader::VerifyFingerprint);
reader.Skip<util::MultiLevelPartition::LevelData>(1);
layout.SetBlockSize<util::MultiLevelPartition::LevelData>(DataLayout::MLD_LEVEL_DATA, 1);
const auto partition_entries_count = reader.ReadVectorSize<util::PartitionID>();
layout.SetBlockSize<util::PartitionID>(DataLayout::MLD_PARTITION, partition_entries_count);
const auto children_entries_count = reader.ReadVectorSize<util::CellID>();
layout.SetBlockSize<util::CellID>(DataLayout::MLD_CELL_TO_CHILDREN, children_entries_count);
}
else
layout.SetBlockSize<char>(DataLayout::MLD_CELL_PARTITION, 0);
{
layout.SetBlockSize<util::MultiLevelPartition::LevelData>(DataLayout::MLD_LEVEL_DATA, 0);
layout.SetBlockSize<util::PartitionID>(DataLayout::MLD_PARTITION, 0);
layout.SetBlockSize<util::CellID>(DataLayout::MLD_CELL_TO_CHILDREN, 0);
}
if (boost::filesystem::exists(config.mld_storage_path))
{
@ -866,13 +875,17 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr)
{
// Loading MLD Data
const auto mld_partition_ptr =
layout.GetBlockPtr<char, true>(memory_ptr, DataLayout::MLD_CELL_PARTITION);
const auto mld_partition_size = layout.GetBlockSize(DataLayout::MLD_CELL_PARTITION);
if (boost::filesystem::exists(config.mld_partition_path))
{
util::PackedMultiLevelPartition<true>().Read(
config.mld_partition_path, mld_partition_ptr, mld_partition_ptr + mld_partition_size);
auto mld_level_data_ptr = layout.GetBlockPtr<util::MultiLevelPartition::LevelData, true>(memory_ptr, DataLayout::MLD_LEVEL_DATA);
auto mld_partition_ptr = layout.GetBlockPtr<util::PartitionID, true>(memory_ptr, DataLayout::MLD_PARTITION);
auto mld_chilren_ptr = layout.GetBlockPtr<util::CellID, true>(memory_ptr, DataLayout::MLD_CELL_TO_CHILDREN);
io::FileReader reader(config.mld_partition_path, io::FileReader::VerifyFingerprint);
reader.ReadInto(mld_level_data_ptr);
reader.ReadInto(mld_partition_ptr, layout.GetBlockEntries(DataLayout::MLD_PARTITION));
reader.ReadInto(mld_chilren_ptr, layout.GetBlockEntries(DataLayout::MLD_CELL_TO_CHILDREN));
}
const auto mld_cell_storage_ptr =
@ -881,8 +894,8 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr)
if (boost::filesystem::exists(config.mld_storage_path))
{
util::CellStorage<true>().Read(config.mld_storage_path,
mld_cell_storage_ptr,
mld_cell_storage_ptr + mld_cell_storage_size);
mld_cell_storage_ptr,
mld_cell_storage_ptr + mld_cell_storage_size);
}
}
}

View File

@ -20,8 +20,8 @@ StorageConfig::StorageConfig(const boost::filesystem::path &base)
names_data_path{base.string() + ".names"}, properties_path{base.string() + ".properties"},
intersection_class_path{base.string() + ".icd"}, turn_lane_data_path{base.string() + ".tld"},
turn_lane_description_path{base.string() + ".tls"},
mld_partition_path{base.string() + ".mld_partition"},
mld_storage_path{base.string() + ".mld_storage"}
mld_partition_path{base.string() + ".partition"},
mld_storage_path{base.string() + ".cell"}
{
}

View File

@ -55,6 +55,8 @@ EngineConfig::Algorithm stringToAlgorithm(const std::string &algorithm)
return EngineConfig::Algorithm::CH;
if (algorithm == "CoreCH")
return EngineConfig::Algorithm::CoreCH;
if (algorithm == "MLD")
return EngineConfig::Algorithm::MLD;
throw util::exception("Invalid algorithm name: " + algorithm);
}
@ -100,7 +102,7 @@ inline unsigned generateServerProgramOptions(const int argc,
"Load data from shared memory") //
("algorithm,a",
value<std::string>(&algorithm)->default_value("CH"),
"Algorithm to use for the data. Can be CH, CoreCH") //
"Algorithm to use for the data. Can be CH, CoreCH, MLD.") //
("max-viaroute-size",
value<int>(&max_locations_viaroute)->default_value(500),
"Max. locations supported in viaroute query") //

View File

@ -16,32 +16,6 @@
using namespace osrm;
using namespace osrm::util;
class MockMLP final : public MultiLevelPartition
{
public:
CellID GetCell(LevelID level, NodeID node) const { return levels[level - 1][node]; };
LevelID GetHighestDifferentLevel(NodeID, NodeID) const { return 3; };
LevelID GetQueryLevel(NodeID, NodeID, NodeID) const { return 3; };
std::uint8_t GetNumberOfLevels() const { return levels.size() + 1; }
std::uint32_t GetNumberOfCells(LevelID level) const
{
auto max_id = 0;
for (auto cell : levels[level - 1])
max_id = std::max<CellID>(max_id, cell);
return max_id + 1;
}
CellID BeginChildren(LevelID, CellID) const { return 0; }
CellID EndChildren(LevelID, CellID) const { return 0; }
MockMLP(std::vector<std::vector<CellID>> levels_) : levels(std::move(levels_)) {}
std::vector<std::vector<CellID>> levels;
};
struct MockEdge
{
NodeID start;
@ -84,7 +58,7 @@ BOOST_AUTO_TEST_CASE(mutable_cell_storage)
std::vector<CellID> l2{{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3}};
std::vector<CellID> l3{{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}};
std::vector<CellID> l4{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
MockMLP mlp{{l1, l2, l3, l4}};
MultiLevelPartition mlp{{l1, l2, l3, l4}, {2, 4, 8, 12}};
std::vector<MockEdge> edges = {
// edges sorted into border/internal by level
@ -193,7 +167,7 @@ BOOST_AUTO_TEST_CASE(immutable_cell_storage)
std::vector<CellID> l2{{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3}};
std::vector<CellID> l3{{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}};
std::vector<CellID> l4{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
MockMLP mlp{{l1, l2, l3, l4}};
MultiLevelPartition mlp{{l1, l2, l3, l4}, {2, 4, 8, 12}};
std::vector<MockEdge> edges = {
// edges sorted into border/internal by level

View File

@ -17,14 +17,83 @@ using namespace osrm::util;
BOOST_AUTO_TEST_SUITE(multi_level_partition_tests)
BOOST_AUTO_TEST_CASE(packed_mlp)
BOOST_AUTO_TEST_CASE(mlp_one)
{
// node: 0 1 2 3 4 5 6 7 8 9 10 11
std::vector<CellID> l1{{4, 4, 2, 2, 1, 1, 3, 3, 2, 2, 5, 5}};
MultiLevelPartition mlp{{l1}, {6}};
BOOST_CHECK_EQUAL(mlp.GetCell(1, 0), mlp.GetCell(1, 1));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 2), mlp.GetCell(1, 3));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 4), mlp.GetCell(1, 5));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 6), mlp.GetCell(1, 7));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 8), mlp.GetCell(1, 9));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 10), mlp.GetCell(1, 11));
}
BOOST_AUTO_TEST_CASE(mlp_shuffled)
{
// node: 0 1 2 3 4 5 6 7 8 9 10 11
std::vector<CellID> l1{{4, 4, 2, 2, 1, 1, 3, 3, 2, 2, 5, 5}};
std::vector<CellID> l2{{3, 3, 3, 3, 1, 1, 1, 1, 2, 2, 0, 0}};
std::vector<CellID> l3{{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}};
std::vector<CellID> l4{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
MultiLevelPartition mlp{{l1, l2, l3, l4}, {6, 4, 2, 1}};
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(1), 6);
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(2), 4);
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(3), 2);
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(4), 1);
BOOST_CHECK_EQUAL(mlp.GetCell(1, 0), mlp.GetCell(1, 1));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 2), mlp.GetCell(1, 3));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 4), mlp.GetCell(1, 5));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 6), mlp.GetCell(1, 7));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 8), mlp.GetCell(1, 9));
BOOST_CHECK_EQUAL(mlp.GetCell(1, 10), mlp.GetCell(1, 11));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 0), mlp.GetCell(2, 1));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 0), mlp.GetCell(2, 2));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 0), mlp.GetCell(2, 3));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 4), mlp.GetCell(2, 5));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 4), mlp.GetCell(2, 6));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 4), mlp.GetCell(2, 7));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 8), mlp.GetCell(2, 9));
BOOST_CHECK_EQUAL(mlp.GetCell(2, 10), mlp.GetCell(2, 11));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 0), mlp.GetCell(3, 1));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 0), mlp.GetCell(3, 2));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 0), mlp.GetCell(3, 3));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 5));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 6));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 7));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 8));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 9));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 10));
BOOST_CHECK_EQUAL(mlp.GetCell(3, 4), mlp.GetCell(3, 11));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 1));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 2));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 3));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 4));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 5));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 6));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 7));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 8));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 9));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 10));
BOOST_CHECK_EQUAL(mlp.GetCell(4, 0), mlp.GetCell(4, 11));
}
BOOST_AUTO_TEST_CASE(mlp_sorted)
{
// node: 0 1 2 3 4 5 6 7 8 9 10 11
std::vector<CellID> l1{{0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5}};
std::vector<CellID> l2{{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3}};
std::vector<CellID> l3{{0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}};
std::vector<CellID> l4{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}};
PackedMultiLevelPartition<false> mlp{{l1, l2, l3, l4}, {6, 4, 2, 1}};
MultiLevelPartition mlp{{l1, l2, l3, l4}, {6, 4, 2, 1}};
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(1), 6);
BOOST_CHECK_EQUAL(mlp.GetNumberOfCells(2), 4);