From bc2e06502efd8b28f679180403d062c8af5b6913 Mon Sep 17 00:00:00 2001 From: Michael Krasnyk Date: Mon, 20 Feb 2017 14:20:22 +0100 Subject: [PATCH] Added cells customizer --- include/customizer/cell_customizer.hpp | 148 +++++++++++++ include/partition/cell_storage.hpp | 36 ++-- unit_tests/partition/cell_storage.cpp | 3 + unit_tests/util/cell_customization.cpp | 277 +++++++++++++++++++++++++ 4 files changed, 446 insertions(+), 18 deletions(-) create mode 100644 include/customizer/cell_customizer.hpp create mode 100644 unit_tests/util/cell_customization.cpp diff --git a/include/customizer/cell_customizer.hpp b/include/customizer/cell_customizer.hpp new file mode 100644 index 000000000..bdf7ac19c --- /dev/null +++ b/include/customizer/cell_customizer.hpp @@ -0,0 +1,148 @@ +#ifndef OSRM_CELLS_CUSTOMIZER_HPP +#define OSRM_CELLS_CUSTOMIZER_HPP + +#include "partition/cell_storage.hpp" +#include "partition/multi_level_partition.hpp" +#include "util/binary_heap.hpp" + +#include +#include + +namespace osrm +{ +namespace customizer +{ + +class CellCustomizer +{ + + public: + CellCustomizer(const partition::MultiLevelPartition &partition) : partition(partition) {} + + template + void Customize(const GraphT &graph, + partition::CellStorage &cells, + partition::LevelID level, + partition::CellID id) + { + auto cell = cells.GetCell(level, id); + auto destinations = cell.GetDestinationNodes(); + + // for each source do forward search + for (auto source : cell.GetSourceNodes()) + { + std::unordered_set destinations_set(destinations.begin(), destinations.end()); + Heap heap(graph.GetNumberOfNodes()); + heap.Insert(source, 0, {}); + + // explore search space + while (!heap.Empty() && !destinations_set.empty()) + { + const NodeID node = heap.DeleteMin(); + const EdgeWeight weight = heap.GetKey(node); + + if (level == 1) + RelaxNode(graph, cells, heap, level, id, node, weight); + else + RelaxNode(graph, cells, heap, level, id, node, weight); + + destinations_set.erase(node); + } + + // fill a map of destination nodes to placeholder pointers + auto destination_iter = destinations.begin(); + for (auto &weight : cell.GetOutWeight(source)) + { + BOOST_ASSERT(destination_iter != destinations.end()); + const auto destination = *destination_iter++; + weight = + heap.WasInserted(destination) ? heap.GetKey(destination) : INVALID_EDGE_WEIGHT; + } + } + } + + template void Customize(const GraphT &graph, partition::CellStorage &cells) + { + for (std::size_t level = 1; level < partition.GetNumberOfLevels(); ++level) + { + tbb::parallel_for(tbb::blocked_range(0, partition.GetNumberOfCells(level)), + [&](const tbb::blocked_range &range) { + for (auto id = range.begin(), end = range.end(); id != end; ++id) + { + Customize(graph, cells, level, id); + } + }); + } + } + + private: + struct HeapData + { + }; + using Heap = util:: + BinaryHeap>; + using HeapPtr = boost::thread_specific_ptr; + + template + void RelaxNode(const GraphT &graph, + const partition::CellStorage &cells, + Heap &heap, + partition::LevelID level, + partition::CellID id, + NodeID node, + EdgeWeight weight) const + { + if (!first_level) + { + // Relax sub-cell nodes + auto subcell_id = partition.GetCell(level - 1, node); + auto subcell = cells.GetCell(level - 1, subcell_id); + auto subcell_destination = subcell.GetDestinationNodes().begin(); + for (auto subcell_weight : subcell.GetOutWeight(node)) + { + if (subcell_weight != INVALID_EDGE_WEIGHT) + { + const NodeID to = *subcell_destination; + const EdgeWeight to_weight = subcell_weight + weight; + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_weight, {}); + } + else if (to_weight < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_weight); + } + } + + ++subcell_destination; + } + } + + // Relax base graph edges if a sub-cell border edge + for (auto edge : graph.GetAdjacentEdgeRange(node)) + { + const NodeID to = graph.GetTarget(edge); + const auto &data = graph.GetEdgeData(edge); + if (data.forward && partition.GetCell(level, to) == id && + (first_level || + partition.GetCell(level - 1, node) != partition.GetCell(level - 1, to))) + { + const EdgeWeight to_weight = data.weight + weight; + if (!heap.WasInserted(to)) + { + heap.Insert(to, to_weight, {}); + } + else if (to_weight < heap.GetKey(to)) + { + heap.DecreaseKey(to, to_weight); + } + } + } + } + + const partition::MultiLevelPartition &partition; +}; +} +} + +#endif // OSRM_CELLS_CUSTOMIZER_HPP diff --git a/include/partition/cell_storage.hpp b/include/partition/cell_storage.hpp index b2a64f1a1..875a670cc 100644 --- a/include/partition/cell_storage.hpp +++ b/include/partition/cell_storage.hpp @@ -83,13 +83,19 @@ template class CellStorageImpl class ColumnIterator : public std::iterator { public: + explicit ColumnIterator() : current(nullptr), stride(1) {} + explicit ColumnIterator(WeightPtrT begin, std::size_t row_length) : current(begin), stride(row_length) { BOOST_ASSERT(begin != nullptr); } - WeightRefT operator*() const { return *current; } + WeightRefT operator*() const + { + BOOST_ASSERT(current); + return *current; + } ColumnIterator &operator++() { @@ -117,25 +123,14 @@ template class CellStorageImpl std::size_t stride; }; - std::size_t GetRow(NodeID node) const - { - auto iter = std::find(source_boundary, source_boundary + num_source_nodes, node); - BOOST_ASSERT(iter != source_boundary + num_source_nodes); - return iter - source_boundary; - } - - std::size_t GetColumn(NodeID node) const - { - auto iter = - std::find(destination_boundary, destination_boundary + num_destination_nodes, node); - BOOST_ASSERT(iter != destination_boundary + num_destination_nodes); - return iter - destination_boundary; - } - public: auto GetOutWeight(NodeID node) const { - auto row = GetRow(node); + auto iter = std::find(source_boundary, source_boundary + num_source_nodes, node); + if (iter == source_boundary + num_source_nodes) + return boost::make_iterator_range(weights, weights); + + auto row = std::distance(source_boundary, iter); auto begin = weights + num_destination_nodes * row; auto end = begin + num_destination_nodes; return boost::make_iterator_range(begin, end); @@ -143,7 +138,12 @@ template class CellStorageImpl auto GetInWeight(NodeID node) const { - auto column = GetColumn(node); + auto iter = + std::find(destination_boundary, destination_boundary + num_destination_nodes, node); + if (iter == destination_boundary + num_destination_nodes) + return boost::make_iterator_range(ColumnIterator{}, ColumnIterator{}); + + auto column = std::distance(destination_boundary, iter); auto begin = ColumnIterator{weights + column, num_destination_nodes}; auto end = ColumnIterator{weights + column + num_source_nodes * num_destination_nodes, num_destination_nodes}; diff --git a/unit_tests/partition/cell_storage.cpp b/unit_tests/partition/cell_storage.cpp index 56a6fff67..3459939ad 100644 --- a/unit_tests/partition/cell_storage.cpp +++ b/unit_tests/partition/cell_storage.cpp @@ -16,6 +16,8 @@ using namespace osrm; using namespace osrm::partition; +namespace +{ struct MockEdge { NodeID start; @@ -41,6 +43,7 @@ auto makeGraph(const std::vector &mock_edges) std::sort(edges.begin(), edges.end()); return util::StaticGraph(max_id + 1, edges); } +} BOOST_AUTO_TEST_SUITE(cell_storage_tests) diff --git a/unit_tests/util/cell_customization.cpp b/unit_tests/util/cell_customization.cpp new file mode 100644 index 000000000..ededd37df --- /dev/null +++ b/unit_tests/util/cell_customization.cpp @@ -0,0 +1,277 @@ +#include +#include + +#include "customizer/cell_customizer.hpp" +#include "partition/cell_storage.hpp" +#include "partition/multi_level_partition.hpp" +#include "util/static_graph.hpp" + +#define REQUIRE_SIZE_RANGE(range, ref) BOOST_REQUIRE_EQUAL(range.size(), ref) +#define CHECK_EQUAL_RANGE(range, ...) \ + do \ + { \ + const auto &lhs = range; \ + const auto &rhs = {__VA_ARGS__}; \ + BOOST_CHECK_EQUAL_COLLECTIONS(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); \ + } while (0) +#define CHECK_EQUAL_COLLECTIONS(lhs, rhs) \ + do \ + { \ + BOOST_CHECK_EQUAL_COLLECTIONS(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); \ + } while (0) + +using namespace osrm; +using namespace osrm::customizer; +using namespace osrm::partition; +using namespace osrm::util; + +namespace +{ +struct MockEdge +{ + NodeID start; + NodeID target; + EdgeWeight weight; +}; + +auto makeGraph(const std::vector &mock_edges) +{ + struct EdgeData + { + EdgeWeight weight; + bool forward; + bool backward; + }; + using Edge = static_graph_details::SortableEdgeWithData; + std::vector edges; + std::size_t max_id = 0; + for (const auto &m : mock_edges) + { + max_id = std::max(max_id, std::max(m.start, m.target)); + edges.push_back(Edge{m.start, m.target, m.weight, true, false}); + edges.push_back(Edge{m.target, m.start, m.weight, false, true}); + } + std::sort(edges.begin(), edges.end()); + return util::StaticGraph(max_id + 1, edges); +} +} + +BOOST_AUTO_TEST_SUITE(cell_customization_tests) + +BOOST_AUTO_TEST_CASE(two_level_test) +{ + // node: 0 1 2 3 + std::vector l1{{0, 0, 1, 1}}; + MultiLevelPartition mlp{{l1}, {2}}; + + BOOST_REQUIRE_EQUAL(mlp.GetNumberOfLevels(), 2); + + std::vector edges = {{0, 1, 1}, {0, 2, 1}, {2, 3, 1}, {3, 1, 1}, {3, 2, 1}}; + + auto graph = makeGraph(edges); + + CellStorage storage(mlp, graph); + CellCustomizer customizer(mlp); + + auto cell_1_0 = storage.GetCell(1, 0); + auto cell_1_1 = storage.GetCell(1, 1); + + REQUIRE_SIZE_RANGE(cell_1_0.GetSourceNodes(), 1); + REQUIRE_SIZE_RANGE(cell_1_0.GetDestinationNodes(), 1); + REQUIRE_SIZE_RANGE(cell_1_1.GetSourceNodes(), 2); + REQUIRE_SIZE_RANGE(cell_1_1.GetDestinationNodes(), 2); + + CHECK_EQUAL_RANGE(cell_1_0.GetSourceNodes(), 0); + CHECK_EQUAL_RANGE(cell_1_0.GetDestinationNodes(), 1); + CHECK_EQUAL_RANGE(cell_1_1.GetSourceNodes(), 2, 3); + CHECK_EQUAL_RANGE(cell_1_1.GetDestinationNodes(), 2, 3); + + REQUIRE_SIZE_RANGE(cell_1_0.GetOutWeight(0), 1); + REQUIRE_SIZE_RANGE(cell_1_0.GetOutWeight(1), 0); + REQUIRE_SIZE_RANGE(cell_1_0.GetInWeight(1), 1); + REQUIRE_SIZE_RANGE(cell_1_0.GetInWeight(0), 0); + REQUIRE_SIZE_RANGE(cell_1_1.GetOutWeight(2), 2); + REQUIRE_SIZE_RANGE(cell_1_1.GetInWeight(3), 2); + + customizer.Customize(graph, storage, 1, 0); + customizer.Customize(graph, storage, 1, 1); + + // cell 0 + // check row source -> destination + CHECK_EQUAL_RANGE(cell_1_0.GetOutWeight(0), 1); + // check column destination -> source + CHECK_EQUAL_RANGE(cell_1_0.GetInWeight(1), 1); + + // cell 1 + // check row source -> destination + CHECK_EQUAL_RANGE(cell_1_1.GetOutWeight(2), 0, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetOutWeight(3), 1, 0); + // check column destination -> source + CHECK_EQUAL_RANGE(cell_1_1.GetInWeight(2), 0, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetInWeight(3), 1, 0); +} + +BOOST_AUTO_TEST_CASE(four_levels_test) +{ + // node: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 + std::vector l1{{0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3}}; + std::vector l2{{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}}; + std::vector l3{{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; + MultiLevelPartition mlp{{l1, l2, l3}, {4, 2, 1}}; + + BOOST_REQUIRE_EQUAL(mlp.GetNumberOfLevels(), 4); + + std::vector edges = { + {0, 1, 1}, // cell (0, 0, 0) + {0, 2, 1}, {3, 1, 1}, {3, 2, 1}, + + {4, 5, 1}, // cell (1, 0, 0) + {4, 6, 1}, {4, 7, 1}, {5, 4, 1}, {5, 6, 1}, {5, 7, 1}, {6, 4, 1}, + {6, 5, 1}, {6, 7, 1}, {7, 4, 1}, {7, 5, 1}, {7, 6, 1}, + + {9, 11, 1}, // cell (2, 1, 0) + {10, 8, 1}, {11, 10, 1}, + + {13, 12, 10}, // cell (3, 1, 0) + {15, 14, 1}, + + {2, 4, 1}, // edge between cells (0, 0, 0) -> (1, 0, 0) + {5, 12, 1}, // edge between cells (1, 0, 0) -> (3, 1, 0) + {8, 3, 1}, // edge between cells (2, 1, 0) -> (0, 0, 0) + {9, 3, 1}, // edge between cells (2, 1, 0) -> (0, 0, 0) + {12, 5, 1}, // edge between cells (3, 1, 0) -> (1, 0, 0) + {13, 7, 1}, // edge between cells (3, 1, 0) -> (1, 0, 0) + {14, 9, 1}, // edge between cells (2, 1, 0) -> (0, 0, 0) + {14, 11, 1} // edge between cells (2, 1, 0) -> (0, 0, 0) + }; + + auto graph = makeGraph(edges); + + CellStorage storage(mlp, graph); + + auto cell_1_0 = storage.GetCell(1, 0); + auto cell_1_1 = storage.GetCell(1, 1); + auto cell_1_2 = storage.GetCell(1, 2); + auto cell_1_3 = storage.GetCell(1, 3); + auto cell_2_0 = storage.GetCell(2, 0); + auto cell_2_1 = storage.GetCell(2, 1); + auto cell_3_0 = storage.GetCell(3, 0); + + REQUIRE_SIZE_RANGE(cell_1_0.GetSourceNodes(), 1); + REQUIRE_SIZE_RANGE(cell_1_0.GetDestinationNodes(), 1); + CHECK_EQUAL_RANGE(cell_1_0.GetSourceNodes(), 3); + CHECK_EQUAL_RANGE(cell_1_0.GetDestinationNodes(), 2); + REQUIRE_SIZE_RANGE(cell_1_0.GetOutWeight(3), 1); + REQUIRE_SIZE_RANGE(cell_1_0.GetInWeight(2), 1); + + REQUIRE_SIZE_RANGE(cell_1_1.GetSourceNodes(), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetDestinationNodes(), 3); + CHECK_EQUAL_RANGE(cell_1_1.GetSourceNodes(), 4, 5, 7); + CHECK_EQUAL_RANGE(cell_1_1.GetDestinationNodes(), 4, 5, 7); + REQUIRE_SIZE_RANGE(cell_1_1.GetOutWeight(4), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetOutWeight(5), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetOutWeight(7), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetInWeight(4), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetInWeight(5), 3); + REQUIRE_SIZE_RANGE(cell_1_1.GetInWeight(7), 3); + + REQUIRE_SIZE_RANGE(cell_1_2.GetSourceNodes(), 2); + REQUIRE_SIZE_RANGE(cell_1_2.GetDestinationNodes(), 2); + CHECK_EQUAL_RANGE(cell_1_2.GetSourceNodes(), 9, 11); + CHECK_EQUAL_RANGE(cell_1_2.GetDestinationNodes(), 8, 11); + REQUIRE_SIZE_RANGE(cell_1_2.GetOutWeight(9), 2); + REQUIRE_SIZE_RANGE(cell_1_2.GetOutWeight(11), 2); + REQUIRE_SIZE_RANGE(cell_1_2.GetInWeight(8), 2); + REQUIRE_SIZE_RANGE(cell_1_2.GetInWeight(11), 2); + + REQUIRE_SIZE_RANGE(cell_1_3.GetSourceNodes(), 1); + REQUIRE_SIZE_RANGE(cell_1_3.GetDestinationNodes(), 2); + CHECK_EQUAL_RANGE(cell_1_3.GetSourceNodes(), 13); + CHECK_EQUAL_RANGE(cell_1_3.GetDestinationNodes(), 12, 14); + REQUIRE_SIZE_RANGE(cell_1_3.GetOutWeight(13), 2); + REQUIRE_SIZE_RANGE(cell_1_3.GetInWeight(12), 1); + REQUIRE_SIZE_RANGE(cell_1_3.GetInWeight(14), 1); + + REQUIRE_SIZE_RANGE(cell_2_0.GetSourceNodes(), 3); + REQUIRE_SIZE_RANGE(cell_2_0.GetDestinationNodes(), 2); + CHECK_EQUAL_RANGE(cell_2_0.GetSourceNodes(), 3, 5, 7); + CHECK_EQUAL_RANGE(cell_2_0.GetDestinationNodes(), 5, 7); + REQUIRE_SIZE_RANGE(cell_2_0.GetOutWeight(3), 2); + REQUIRE_SIZE_RANGE(cell_2_0.GetOutWeight(5), 2); + REQUIRE_SIZE_RANGE(cell_2_0.GetOutWeight(7), 2); + REQUIRE_SIZE_RANGE(cell_2_0.GetInWeight(5), 3); + REQUIRE_SIZE_RANGE(cell_2_0.GetInWeight(7), 3); + + REQUIRE_SIZE_RANGE(cell_2_1.GetSourceNodes(), 2); + REQUIRE_SIZE_RANGE(cell_2_1.GetDestinationNodes(), 3); + CHECK_EQUAL_RANGE(cell_2_1.GetSourceNodes(), 9, 13); + CHECK_EQUAL_RANGE(cell_2_1.GetDestinationNodes(), 8, 9, 12); + REQUIRE_SIZE_RANGE(cell_2_1.GetOutWeight(9), 3); + REQUIRE_SIZE_RANGE(cell_2_1.GetOutWeight(13), 3); + REQUIRE_SIZE_RANGE(cell_2_1.GetInWeight(8), 2); + REQUIRE_SIZE_RANGE(cell_2_1.GetInWeight(9), 2); + REQUIRE_SIZE_RANGE(cell_2_1.GetInWeight(12), 2); + + REQUIRE_SIZE_RANGE(cell_3_0.GetSourceNodes(), 0); + REQUIRE_SIZE_RANGE(cell_3_0.GetDestinationNodes(), 0); + + CellCustomizer customizer(mlp); + + customizer.Customize(graph, storage, 1, 0); + customizer.Customize(graph, storage, 1, 1); + customizer.Customize(graph, storage, 1, 2); + customizer.Customize(graph, storage, 1, 3); + + customizer.Customize(graph, storage, 2, 0); + customizer.Customize(graph, storage, 2, 1); + + // level 1 + // cell 0 + CHECK_EQUAL_RANGE(cell_1_0.GetOutWeight(3), 1); + CHECK_EQUAL_RANGE(cell_1_0.GetInWeight(2), 1); + + // cell 1 + CHECK_EQUAL_RANGE(cell_1_1.GetOutWeight(4), 0, 1, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetOutWeight(5), 1, 0, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetOutWeight(7), 1, 1, 0); + CHECK_EQUAL_RANGE(cell_1_1.GetInWeight(4), 0, 1, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetInWeight(5), 1, 0, 1); + CHECK_EQUAL_RANGE(cell_1_1.GetInWeight(7), 1, 1, 0); + + // cell 2 + CHECK_EQUAL_RANGE(cell_1_2.GetOutWeight(9), 3, 1); + CHECK_EQUAL_RANGE(cell_1_2.GetOutWeight(11), 2, 0); + CHECK_EQUAL_RANGE(cell_1_2.GetInWeight(8), 3, 2); + CHECK_EQUAL_RANGE(cell_1_2.GetInWeight(11), 1, 0); + + // cell 3 + CHECK_EQUAL_RANGE(cell_1_3.GetOutWeight(13), 10, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_1_3.GetInWeight(12), 10); + CHECK_EQUAL_RANGE(cell_1_3.GetInWeight(14), INVALID_EDGE_WEIGHT); + + // level 2 + // cell 0 + CHECK_EQUAL_RANGE(cell_2_0.GetOutWeight(3), 3, 3); + CHECK_EQUAL_RANGE(cell_2_0.GetOutWeight(5), 0, 1); + CHECK_EQUAL_RANGE(cell_2_0.GetOutWeight(7), 1, 0); + CHECK_EQUAL_RANGE(cell_2_0.GetInWeight(5), 3, 0, 1); + CHECK_EQUAL_RANGE(cell_2_0.GetInWeight(7), 3, 1, 0); + + // cell 1 + CHECK_EQUAL_RANGE(cell_2_1.GetOutWeight(9), 3, 0, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetOutWeight(13), INVALID_EDGE_WEIGHT, INVALID_EDGE_WEIGHT, 10); + CHECK_EQUAL_RANGE(cell_2_1.GetInWeight(8), 3, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetInWeight(9), 0, INVALID_EDGE_WEIGHT); + CHECK_EQUAL_RANGE(cell_2_1.GetInWeight(12), INVALID_EDGE_WEIGHT, 10); + + CellStorage storage_rec(mlp, graph); + customizer.Customize(graph, storage_rec); + + CHECK_EQUAL_COLLECTIONS(cell_2_1.GetOutWeight(9), storage_rec.GetCell(2, 1).GetOutWeight(9)); + CHECK_EQUAL_COLLECTIONS(cell_2_1.GetOutWeight(13), storage_rec.GetCell(2, 1).GetOutWeight(13)); + CHECK_EQUAL_COLLECTIONS(cell_2_1.GetInWeight(8), storage_rec.GetCell(2, 1).GetInWeight(8)); + CHECK_EQUAL_COLLECTIONS(cell_2_1.GetInWeight(9), storage_rec.GetCell(2, 1).GetInWeight(9)); + CHECK_EQUAL_COLLECTIONS(cell_2_1.GetInWeight(12), storage_rec.GetCell(2, 1).GetInWeight(12)); +} + +BOOST_AUTO_TEST_SUITE_END()