Add class to translate from bisection ids to cell ids
This commit is contained in:
parent
b2f3b901e0
commit
8f9e980945
24
include/partition/bisection_to_partition.hpp
Normal file
24
include/partition/bisection_to_partition.hpp
Normal 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
|
@ -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
44
include/util/msb.hpp
Normal 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
|
138
src/partition/bisection_to_partition.cpp
Normal file
138
src/partition/bisection_to_partition.cpp
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
}
|
}
|
||||||
|
331
unit_tests/partition/bisection_to_partition.cpp
Normal file
331
unit_tests/partition/bisection_to_partition.cpp
Normal 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()
|
Loading…
Reference in New Issue
Block a user