Add class to translate from bisection ids to cell ids

This commit is contained in:
Patrick Niklaus 2017-03-05 20:31:45 +00:00 committed by Patrick Niklaus
parent b2f3b901e0
commit 8f9e980945
6 changed files with 558 additions and 126 deletions

View File

@ -0,0 +1,24 @@
#ifndef BISECTION_TO_PARTITION_HPP
#define BISECTION_TO_PARTITION_HPP
#include "partition/multi_level_partition.hpp"
#include "partition/recursive_bisection.hpp"
#include <vector>
namespace osrm
{
namespace partition
{
using Partition = std::vector<CellID>;
// Converts a representation of the bisection to cell ids over multiple level
std::tuple<std::vector<Partition>, std::vector<std::uint32_t>>
bisectionToPartition(const std::vector<BisectionID> &node_to_bisection_id,
const std::vector<std::size_t> &max_cell_sizes);
}
}
#endif

View File

@ -5,6 +5,7 @@
#include "util/for_each_pair.hpp" #include "util/for_each_pair.hpp"
#include "util/shared_memory_vector_wrapper.hpp" #include "util/shared_memory_vector_wrapper.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
#include "util/msb.hpp"
#include "storage/io.hpp" #include "storage/io.hpp"
@ -38,27 +39,6 @@ void write(const boost::filesystem::path &file,
namespace detail 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 LevelID = std::uint8_t;
@ -132,7 +112,7 @@ template <bool UseShareMemory> class MultiLevelPartitionImpl final
if (partition[first] == partition[second]) if (partition[first] == partition[second])
return 0; return 0;
auto msb = detail::highestMSB(partition[first] ^ partition[second]); auto msb = util::msb(partition[first] ^ partition[second]);
return level_data.bit_to_level[msb]; return level_data.bit_to_level[msb];
} }

44
include/util/msb.hpp Normal file
View File

@ -0,0 +1,44 @@
#ifndef OSRM_UTIL_MSB_HPP
#define OSRM_UTIL_MSB_HPP
#include <boost/assert.hpp>
#include <cstdint>
#include <utility>
namespace osrm
{
namespace util
{
// get the msb of an integer
// return 0 for integers without msb
template <typename T> std::size_t msb(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 msb(std::uint64_t v)
{
BOOST_ASSERT(v > 0);
return 63UL - __builtin_clzl(v);
}
inline std::size_t msb(std::uint32_t v)
{
BOOST_ASSERT(v > 0);
return 31UL - __builtin_clz(v);
}
#endif
}
}
#endif

View File

@ -0,0 +1,138 @@
#include "partition/bisection_to_partition.hpp"
namespace osrm
{
namespace partition
{
namespace
{
struct CellBisection
{
std::uint32_t begin;
std::uint32_t end;
std::uint8_t depth;
bool tabu; // we will not attempt to split this cell anymore
};
static constexpr std::size_t NUM_BISECTION_BITS = sizeof(BisectionID) * CHAR_BIT;
bool getSide(std::uint8_t depth, BisectionID id)
{
return id & (1UL << (NUM_BISECTION_BITS - 1 - depth));
}
std::vector<std::uint32_t> getLargeCells(const std::size_t max_cell_size,
const std::vector<CellBisection> &cells)
{
std::vector<std::uint32_t> 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<CellBisection> &cells,
const std::vector<std::uint32_t> &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++;
}
return partition;
}
void partitionLevel(const std::vector<BisectionID> &node_to_bisection_id,
std::size_t max_cell_size,
std::vector<std::uint32_t> &permutation,
std::vector<CellBisection> &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.depth < 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.depth
const BisectionID mask = (1UL << (NUM_BISECTION_BITS - cell.depth)) - 1;
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 msb = util::msb(sum & mask);
// depth counts from MSB to LSB
const auto depth = NUM_BISECTION_BITS - 1 - msb;
std::uint32_t middle =
std::partition(permutation.begin() + cell.begin,
permutation.begin() + cell.end,
[depth, &node_to_bisection_id](const auto node_id) {
return getSide(depth, node_to_bisection_id[node_id]);
}) -
permutation.begin();
cell.depth = depth + 1;
if (middle != cell.begin && middle != cell.end)
{
auto old_end = cell.end;
cell.end = middle;
cells.push_back(
CellBisection{middle, old_end, static_cast<std::uint8_t>(depth + 1), false});
}
}
}
}
}
// Implements a greedy algorithm that split cells using the bisection until a target cell size is
// reached
std::tuple<std::vector<Partition>, std::vector<std::uint32_t>>
bisectionToPartition(const std::vector<BisectionID> &node_to_bisection_id,
const std::vector<std::size_t> &max_cell_sizes)
{
std::vector<std::uint32_t> permutation(node_to_bisection_id.size());
std::iota(permutation.begin(), permutation.end(), 0);
std::vector<CellBisection> cells;
cells.push_back(CellBisection{0, static_cast<std::uint32_t>(node_to_bisection_id.size()), 0, false});
std::vector<Partition> partitions(max_cell_sizes.size());
std::vector<std::uint32_t> num_cells(max_cell_sizes.size());
auto level_idx = max_cell_sizes.size() - 1;
for (auto max_cell_size : boost::adaptors::reverse(max_cell_sizes))
{
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));
}
}
}

View File

@ -1,12 +1,12 @@
#include "partition/partitioner.hpp" #include "partition/partitioner.hpp"
#include "partition/annotated_partition.hpp"
#include "partition/bisection_graph.hpp" #include "partition/bisection_graph.hpp"
#include "partition/bisection_to_partition.hpp"
#include "partition/compressed_node_based_graph_reader.hpp" #include "partition/compressed_node_based_graph_reader.hpp"
#include "partition/edge_based_graph_reader.hpp" #include "partition/edge_based_graph_reader.hpp"
#include "partition/io.hpp" #include "partition/io.hpp"
#include "partition/multi_level_partition.hpp"
#include "partition/node_based_graph_to_edge_based_graph_mapping_reader.hpp" #include "partition/node_based_graph_to_edge_based_graph_mapping_reader.hpp"
#include "partition/recursive_bisection.hpp" #include "partition/recursive_bisection.hpp"
#include "partition/multi_level_partition.hpp"
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/geojson_debug_logger.hpp" #include "util/geojson_debug_logger.hpp"
@ -32,27 +32,6 @@ namespace osrm
namespace partition namespace partition
{ {
void LogStatistics(const std::string &filename, std::vector<std::uint32_t> 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, const std::vector<std::uint32_t> &bisection_ids) void LogGeojson(const std::string &filename, const std::vector<std::uint32_t> &bisection_ids)
{ {
// reload graph, since we destroyed the old one // reload graph, since we destroyed the old one
@ -138,9 +117,6 @@ int Partitioner::Run(const PartitionConfig &config)
config.num_optimizing_cuts, config.num_optimizing_cuts,
config.small_component_size); config.small_component_size);
LogStatistics(config.compressed_node_based_graph_path.string(),
recursive_bisection.BisectionIDs());
// Up until now we worked on the compressed node based graph. // Up until now we worked on the compressed node based graph.
// But what we actually need is a partition for the edge based graph to work on. // But what we actually need is a partition for the edge based graph to work on.
// The following loads a mapping from node based graph to edge based graph. // The following loads a mapping from node based graph to edge based graph.
@ -184,87 +160,26 @@ int Partitioner::Run(const PartitionConfig &config)
} }
} }
// FIXME The CellID computation code need to be replaced by a more sophisticated method std::vector<Partition> partitions;
{ std::vector<std::uint32_t> level_to_num_cells;
BOOST_ASSERT(edge_based_partition_ids.size() == edge_based_graph->GetNumberOfNodes()); std::tie(partitions, level_to_num_cells) = bisectionToPartition(edge_based_partition_ids,
{128, 128 * 32, 128 * 32 * 16, 128 * 32 * 16 * 32});
using namespace osrm::partition; TIMER_START(packed_mlp);
MultiLevelPartition mlp{partitions, level_to_num_cells};
TIMER_STOP(packed_mlp);
util::Log() << "MultiLevelPartition constructed in " << TIMER_SEC(packed_mlp) << " seconds";
// find bit size of bisection ids TIMER_START(cell_storage);
int first_nonzero_position = sizeof(BisectionID) * CHAR_BIT; CellStorage storage(mlp, *edge_based_graph);
for (auto id : edge_based_partition_ids) TIMER_STOP(cell_storage);
{ util::Log() << "CellStorage constructed in " << TIMER_SEC(cell_storage) << " seconds";
first_nonzero_position = id == 0 ? first_nonzero_position
: std::min(first_nonzero_position, __builtin_ctz(id));
}
BOOST_ASSERT(first_nonzero_position != sizeof(BisectionID) * CHAR_BIT);
// split bisection id bits into groups starting from SCC and stop at level 1 TIMER_START(writing_mld_data);
BOOST_ASSERT(recursive_bisection.SCCDepth() != 0); io::write(config.mld_partition_path, mlp);
int mask_from = sizeof(BisectionID) * CHAR_BIT - recursive_bisection.SCCDepth(); io::write(config.mld_storage_path, storage);
std::vector<BisectionID> level_masks; TIMER_STOP(writing_mld_data);
for (int mask_to = sizeof(BisectionID) * CHAR_BIT; mask_to > first_nonzero_position; util::Log() << "MLD data writing took " << TIMER_SEC(writing_mld_data) << " seconds";
mask_to = mask_from, mask_from -= 3) // TODO: find better grouping
{
auto bit = std::max(first_nonzero_position, mask_from);
level_masks.push_back(((1u << (sizeof(BisectionID) * CHAR_BIT - bit)) - 1) << bit);
}
util::Log() << "Bisection IDs split for SCC depth " << recursive_bisection.SCCDepth()
<< " and first non-zero bit position " << first_nonzero_position
<< " number of levels is " << level_masks.size();
for (auto x : level_masks)
std::cout << std::setw(8) << std::hex << x << std::dec << "\n";
// collect cell ids as masked bisection ids
std::vector<std::vector<CellID>> partitions(
level_masks.size(), std::vector<CellID>(edge_based_partition_ids.size()));
std::vector<std::unordered_set<CellID>> partition_sets(level_masks.size());
for (std::size_t index = 0; index < edge_based_partition_ids.size(); ++index)
{
auto bisection_id = edge_based_partition_ids[index];
for (std::size_t level = 0; level < level_masks.size(); ++level)
{
CellID cell_id =
bisection_id & level_masks[level_masks.size() - 1 - level];
partitions[level][index] = cell_id;
partition_sets[level].insert(cell_id);
}
}
std::vector<std::uint32_t> level_to_num_cells;
std::transform(partition_sets.begin(),
partition_sets.end(),
std::back_inserter(level_to_num_cells),
[](const std::unordered_set<CellID> &partition_set) {
return partition_set.size();
});
std::cout << "# of cell on levels\n";
for (std::size_t level = 0; level < partition_sets.size(); ++level)
{
std::cout << level_to_num_cells[level] << ": ";
for (auto x : partition_sets[level])
std::cout << " " << x;
std::cout << "\n";
}
TIMER_START(packed_mlp);
MultiLevelPartition mlp{partitions, level_to_num_cells};
TIMER_STOP(packed_mlp);
util::Log() << "MultiLevelPartition constructed in " << TIMER_SEC(packed_mlp)
<< " seconds";
TIMER_START(cell_storage);
CellStorage storage(mlp, *edge_based_graph);
TIMER_STOP(cell_storage);
util::Log() << "CellStorage constructed in " << TIMER_SEC(cell_storage) << " seconds";
TIMER_START(writing_mld_data);
io::write(config.mld_partition_path, mlp);
io::write(config.mld_storage_path, storage);
TIMER_STOP(writing_mld_data);
util::Log() << "MLD data writing took " << TIMER_SEC(writing_mld_data) << " seconds";
}
return 0; return 0;
} }

View File

@ -0,0 +1,331 @@
#include <boost/numeric/conversion/cast.hpp>
#include <boost/test/unit_test.hpp>
#include "partition/bisection_to_partition.hpp"
#define CHECK_SIZE_RANGE(range, ref) BOOST_CHECK_EQUAL(range.end() - range.begin(), ref)
#define CHECK_EQUAL_RANGE(range, ref) \
do \
{ \
const auto &lhs = range; \
const auto &rhs = ref; \
BOOST_CHECK_EQUAL_COLLECTIONS(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); \
} while (0)
using namespace osrm;
using namespace osrm::partition;
BOOST_AUTO_TEST_SUITE(bisection_to_partition_tests)
BOOST_AUTO_TEST_CASE(unsplitable_case)
{
std::vector<Partition> partitions;
std::vector<std::uint32_t> num_cells;
/*
0 | 1
/ \
0 | 1 \
/ \ |
0 | 1 0 | 1 |
/ \ / \ / \
| | | | / \
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
*/
const std::vector<BisectionID> ids_1 = {
0b000, 0b000, 0b001, 0b001, 0b010, 0b010, 0b011, 0b011, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100,
};
// If cell sizes are not a factor of two we will see sub-optimal results like below
std::tie(partitions, num_cells) = bisectionToPartition(ids_1, {2, 4, 8, 16});
BOOST_CHECK_EQUAL(partitions.size(), 4);
std::vector<std::uint32_t> reference_num_cells = {5, 3, 2, 1};
CHECK_EQUAL_RANGE(reference_num_cells, num_cells);
// Four cells of size 2 and one of size 4 (could not be split)
const std::vector<CellID> reference_l1{partitions[0][0], // 0
partitions[0][0], // 1
partitions[0][2], // 2
partitions[0][2], // 3
partitions[0][4], // 4
partitions[0][4], // 5
partitions[0][6], // 6
partitions[0][6], // 7
partitions[0][8], // 8
partitions[0][8], // 9
partitions[0][8], // 10
partitions[0][8], // 11
partitions[0][8], // 12
partitions[0][8], // 13
partitions[0][8], // 14
partitions[0][8]}; // 15
// Two cells of size 4 and one of size 8
const std::vector<CellID> reference_l2{partitions[1][0], // 0
partitions[1][0], // 1
partitions[1][0], // 2
partitions[1][0], // 3
partitions[1][4], // 4
partitions[1][4], // 5
partitions[1][4], // 6
partitions[1][4], // 7
partitions[1][8], // 8
partitions[1][8], // 9
partitions[1][8], // 10
partitions[1][8], // 11
partitions[1][8], // 12
partitions[1][8], // 13
partitions[1][8], // 14
partitions[1][8]}; // 15
// Two cells of size 8
const std::vector<CellID> reference_l3{partitions[2][0], // 0
partitions[2][0], // 1
partitions[2][0], // 2
partitions[2][0], // 3
partitions[2][0], // 4
partitions[2][0], // 5
partitions[2][0], // 6
partitions[2][0], // 7
partitions[2][8], // 8
partitions[2][8], // 9
partitions[2][8], // 10
partitions[2][8], // 11
partitions[2][8], // 12
partitions[2][8], // 13
partitions[2][8], // 14
partitions[2][8]}; // 15
// All in one cell
const std::vector<CellID> reference_l4{partitions[3][0], // 0
partitions[3][0], // 1
partitions[3][0], // 2
partitions[3][0], // 3
partitions[3][0], // 4
partitions[3][0], // 5
partitions[3][0], // 6
partitions[3][0], // 7
partitions[3][0], // 8
partitions[3][0], // 9
partitions[3][0], // 10
partitions[3][0], // 11
partitions[3][0], // 12
partitions[3][0], // 13
partitions[3][0], // 14
partitions[3][0]}; // 15
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
}
BOOST_AUTO_TEST_CASE(power_of_two_case)
{
std::vector<Partition> partitions;
std::vector<std::uint32_t> num_cells;
/*
0 | 1
/ \
0 | 1 |
/ \ |
0 | 1 0 | 1 0 | 1
/ \ / \ / \
| | | | | |
0 1 2 3 4 5 6 7 8 9 10 11
*/
const std::vector<BisectionID> ids_1 = {
0b000, 0b000, 0b001, 0b001, 0b010, 0b010, 0b011, 0b011, 0b100, 0b100, 0b101, 0b101,
};
// If cell sizes are not a factor of two we will see sub-optimal results like below
std::tie(partitions, num_cells) = bisectionToPartition(ids_1, {2, 4, 8, 16});
BOOST_CHECK_EQUAL(partitions.size(), 4);
std::vector<std::uint32_t> reference_num_cells = {6, 3, 2, 1};
CHECK_EQUAL_RANGE(reference_num_cells, num_cells);
// Six cells of size 2
const std::vector<CellID> reference_l1{partitions[0][0], // 0
partitions[0][0], // 1
partitions[0][2], // 2
partitions[0][2], // 3
partitions[0][4], // 4
partitions[0][4], // 5
partitions[0][6], // 6
partitions[0][6], // 7
partitions[0][8], // 8
partitions[0][8], // 9
partitions[0][10], // 10
partitions[0][10]}; // 11
// Three cells of size 4
const std::vector<CellID> reference_l2{partitions[1][0], // 0
partitions[1][0], // 1
partitions[1][0], // 2
partitions[1][0], // 3
partitions[1][4], // 4
partitions[1][4], // 5
partitions[1][4], // 6
partitions[1][4], // 7
partitions[1][8], // 8
partitions[1][8], // 9
partitions[1][8], // 10
partitions[1][8]}; // 11
// Two cells of size 8 and 4
const std::vector<CellID> reference_l3{partitions[2][0], // 0
partitions[2][0], // 1
partitions[2][0], // 2
partitions[2][0], // 3
partitions[2][0], // 4
partitions[2][0], // 5
partitions[2][0], // 6
partitions[2][0], // 7
partitions[2][8], // 8
partitions[2][8], // 9
partitions[2][8], // 10
partitions[2][8]}; // 11
// All in one cell
const std::vector<CellID> reference_l4{partitions[3][0], // 0
partitions[3][0], // 1
partitions[3][0], // 2
partitions[3][0], // 3
partitions[3][0], // 4
partitions[3][0], // 5
partitions[3][0], // 6
partitions[3][0], // 7
partitions[3][0], // 8
partitions[3][0], // 9
partitions[3][0], // 10
partitions[3][0]}; // 11
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
// Inserting zeros at bit position 0, and 2 should not change the result
const std::vector<BisectionID> ids_2 = {
0b00000, 0b00000, 0b00010, 0b00010, 0b00100, 0b00100, 0b00110, 0b00110, 0b10000, 0b10000, 0b10010, 0b10010,
};
std::tie(partitions, num_cells) = bisectionToPartition(ids_2, {2, 4, 8, 16});
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
// Inserting a prefix should not change anything
const std::vector<BisectionID> ids_3 = {
0b101000, 0b101000, 0b101001, 0b101001, 0b101010, 0b101010, 0b101011, 0b101011, 0b101100, 0b101100, 0b101101, 0b101101,
};
std::tie(partitions, num_cells) = bisectionToPartition(ids_3, {2, 4, 8, 16});
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
}
BOOST_AUTO_TEST_CASE(non_factor_two_case)
{
std::vector<Partition> partitions;
std::vector<std::uint32_t> num_cells;
/*
0 | 1
/ \
0 | 1 |
/ \ |
0 | 1 0 | 1 0 | 1
/ \ / \ / \
| | | | | |
0 1 2 3 4 5 6 7 8 9 10 11
*/
const std::vector<BisectionID> ids_1 = {
0b000, 0b000, 0b001, 0b001, 0b010, 0b010, 0b011, 0b011, 0b100, 0b100, 0b101, 0b101,
};
// If cell sizes are not a factor of two we will see sub-optimal results like below
std::tie(partitions, num_cells) = bisectionToPartition(ids_1, {2, 4, 6, 12});
BOOST_CHECK_EQUAL(partitions.size(), 4);
std::vector<std::uint32_t> reference_num_cells = {6, 3, 3, 1};
CHECK_EQUAL_RANGE(reference_num_cells, num_cells);
// Six cells of size 2
const std::vector<CellID> reference_l1{partitions[0][0], // 0
partitions[0][0], // 1
partitions[0][2], // 2
partitions[0][2], // 3
partitions[0][4], // 4
partitions[0][4], // 5
partitions[0][6], // 6
partitions[0][6], // 7
partitions[0][8], // 8
partitions[0][8], // 9
partitions[0][10], // 10
partitions[0][10]}; // 11
// Three cells of size 4
const std::vector<CellID> reference_l2{partitions[1][0], // 0
partitions[1][0], // 1
partitions[1][0], // 2
partitions[1][0], // 3
partitions[1][4], // 4
partitions[1][4], // 5
partitions[1][4], // 6
partitions[1][4], // 7
partitions[1][8], // 8
partitions[1][8], // 9
partitions[1][8], // 10
partitions[1][8]}; // 11
// Again three cells of size 4 (bad)
const std::vector<CellID> reference_l3{partitions[2][0], // 0
partitions[2][0], // 1
partitions[2][0], // 2
partitions[2][0], // 3
partitions[2][4], // 4
partitions[2][4], // 5
partitions[2][4], // 6
partitions[2][4], // 7
partitions[2][8], // 8
partitions[2][8], // 9
partitions[2][8], // 10
partitions[2][8]}; // 11
// All in one cell
const std::vector<CellID> reference_l4{partitions[3][0], // 0
partitions[3][0], // 1
partitions[3][0], // 2
partitions[3][0], // 3
partitions[3][0], // 4
partitions[3][0], // 5
partitions[3][0], // 6
partitions[3][0], // 7
partitions[3][0], // 8
partitions[3][0], // 9
partitions[3][0], // 10
partitions[3][0]}; // 11
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
// Inserting zeros at bit position 0, and 2 should not change the result
const std::vector<BisectionID> ids_2 = {
0b00000, 0b00000, 0b00010, 0b00010, 0b00100, 0b00100, 0b00110, 0b00110, 0b10000, 0b10000, 0b10010, 0b10010,
};
std::tie(partitions, num_cells) = bisectionToPartition(ids_2, {2, 4, 6, 12});
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
// Inserting a prefix should not change anything
const std::vector<BisectionID> ids_3 = {
0b101000, 0b101000, 0b101001, 0b101001, 0b101010, 0b101010, 0b101011, 0b101011, 0b101100, 0b101100, 0b101101, 0b101101,
};
std::tie(partitions, num_cells) = bisectionToPartition(ids_3, {2, 4, 6, 12});
CHECK_EQUAL_RANGE(reference_l1, partitions[0]);
CHECK_EQUAL_RANGE(reference_l2, partitions[1]);
CHECK_EQUAL_RANGE(reference_l3, partitions[2]);
CHECK_EQUAL_RANGE(reference_l4, partitions[3]);
}
BOOST_AUTO_TEST_SUITE_END()