Rename namespace partition to partitioner

Rename module partition to partitioner.
This cultivates naming used in existing modules like extractor,
customizer, etc. - noun vs verb (word partition is both though).
This commit is contained in:
Mateusz Loskot
2018-02-01 16:47:43 +01:00
committed by Patrick Niklaus
parent 03f598b93d
commit 8114104a43
61 changed files with 308 additions and 305 deletions
+136
View File
@@ -0,0 +1,136 @@
#ifndef OSRM_PARTITIONER_ANNOTATE_HPP_
#define OSRM_PARTITIONER_ANNOTATE_HPP_
#include "partitioner/bisection_graph.hpp"
#include "util/typedefs.hpp"
#include <cstdint>
#include <utility>
#include <vector>
namespace osrm
{
namespace partitioner
{
// takes the result of a recursive bisection and turns it into an annotated partition for MLD. These
// annotated partitions provide a mapping from every node in the graph to a consecutively
// numbered cell in each level of the multi level partition. Instead of using the bisection directly
// (which can result in a unbalanced tree structure)
//
// _____o______
// / \ 
// o ____o____
// / \ / \ 
// a b o _o_
// / \ / \ 
// c d o o
// / \ / \ 
// e f g h
//
// we build a balanced structure that will result in a multi-cut on any level. We transform this
// layout into:
//
// _____o__________
// / | \ 
// o | \ 
// / \ | \ 
// a b o _o_
// / \ / \ 
// c d o o
// / \ / \ 
// e f g h
class AnnotatedPartition
{
public:
// Used to generate an implicit tree representation
struct SizedID
{
BisectionID id;
std::size_t count;
bool operator<(const SizedID &other) const { return id < other.id; };
};
// Metrics that describe a single level
struct LevelMetrics
{
std::size_t border_nodes;
std::size_t border_arcs;
// impresses imbalance, if not all nodes are in that cell anymore
std::size_t contained_nodes;
std::size_t number_of_cells;
std::size_t max_border_nodes_per_cell;
std::size_t max_border_arcs_per_cell;
std::size_t total_memory_cells;
std::vector<std::size_t> cell_sizes;
std::ostream &print(std::ostream &os) const
{
os << "[level]\n"
<< "\t#border nodes: " << border_nodes << " #border arcs: " << border_arcs
<< " #cells: " << number_of_cells << " #contained nodes: " << contained_nodes << "\n"
<< "\tborder nodes: max: " << max_border_nodes_per_cell
<< " avg : " << static_cast<double>(border_nodes) / number_of_cells
<< " border arcs: max: " << max_border_arcs_per_cell << " "
<< " avg: " << static_cast<double>(border_arcs) / number_of_cells << "\n"
<< "\tmemory consumption: " << total_memory_cells / (1024.0 * 1024.0) << " MB."
<< "\n";
os << "\tcell sizes:";
for (auto s : cell_sizes)
os << " " << s;
os << std::endl;
return os;
}
std::ostream &logMachinereadable(std::ostream &os,
const std::string &identification,
std::size_t depth,
const bool print_header = false) const
{
if (print_header)
os << "[" << identification << "] # depth cells total_nodes border_nodes "
"max_border_nodes border_arcs max_border_arcs bytes "
"cell_sizes*\n";
os << "[" << identification << "] " << depth << " " << number_of_cells << " "
<< contained_nodes << " " << border_nodes << " " << max_border_nodes_per_cell << " "
<< border_arcs << " " << max_border_arcs_per_cell << " " << total_memory_cells;
for (auto s : cell_sizes)
os << " " << s;
os << "\n";
return os;
}
};
AnnotatedPartition(const BisectionGraph &graph, const std::vector<BisectionID> &bisection_ids);
private:
// print distribution of level graph as it is
void PrintBisection(const std::vector<SizedID> &implicit_tree,
const BisectionGraph &graph,
const std::vector<BisectionID> &bisection_ids) const;
// find levels that offer good distribution of average cell sizes
void SearchLevels(const std::vector<SizedID> &implicit_tree,
const BisectionGraph &graph,
const std::vector<BisectionID> &bisection_ids) const;
// set cell_ids[i] == INFTY to exclude element
LevelMetrics AnalyseLevel(const BisectionGraph &graph,
const std::vector<std::uint32_t> &cell_ids) const;
std::vector<std::uint32_t>
ComputeCellIDs(std::vector<std::pair<BisectionID, std::int32_t>> &prefixes,
const BisectionGraph &graph,
const std::vector<BisectionID> &bisection_ids) const;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_ANNOTATE_HPP_
+115
View File
@@ -0,0 +1,115 @@
#ifndef OSRM_PARTITIONER_BISECTION_GRAPH_HPP_
#define OSRM_PARTITIONER_BISECTION_GRAPH_HPP_
#include "util/coordinate.hpp"
#include "util/typedefs.hpp"
#include "partitioner/partition_graph.hpp"
#include "extractor/edge_based_edge.hpp"
#include <cstddef>
#include <algorithm>
#include <iterator>
#include <utility>
namespace osrm
{
namespace partitioner
{
// Node in the bisection graph. We require the original node id (since we remap the nodes all the
// time and can track the correct ID this way). In addtition, the node provides the coordinate its
// located at for use in the inertial flow sorting by slope.
struct BisectionNode
{
BisectionNode(util::Coordinate coordinate_ = {util::FloatLongitude{0}, util::FloatLatitude{0}},
const NodeID original_id_ = SPECIAL_NODEID)
: coordinate(std::move(coordinate_)), original_id(original_id_)
{
}
// the coordinate the node is located at
util::Coordinate coordinate;
// the node id to access the bisection result
NodeID original_id;
};
// For max-flow/min-cut computations, we operate on a undirected graph. This has some benefits:
// - we don't disconnect the graph more than we have to
// - small components will actually be disconnected (no border nodes)
// - parts of the graph that are clonnected in one way (not reachable/not exitable) will remain
// close to their connected nodes
// As a result, we only require a target as our only data member in the edge.
struct BisectionEdge
{
BisectionEdge(const NodeID target_ = SPECIAL_NODEID) : target(target_) {}
// StaticGraph Edge requirement (see static graph traits): .target, .data
NodeID target;
};
// Aliases for the graph used during the bisection, based on the Remappable graph
using BisectionGraphNode = NodeEntryWrapper<BisectionNode>;
using BisectionInputEdge = GraphConstructionWrapper<BisectionEdge>;
using BisectionGraph = RemappableGraph<BisectionGraphNode, BisectionEdge>;
// Factory method to construct the bisection graph form a set of coordinates and Input Edges (need
// to contain source and target). Edges needs to be labeled from zero
inline BisectionGraph makeBisectionGraph(const std::vector<util::Coordinate> &coordinates,
const std::vector<BisectionInputEdge> &edges)
{
std::vector<BisectionGraph::NodeT> result_nodes;
result_nodes.reserve(coordinates.size());
std::vector<BisectionGraph::EdgeT> result_edges;
result_edges.reserve(edges.size());
// find the end of edges that belong to node_id
const auto advance_edge_itr = [&edges, &result_edges](const std::size_t node_id,
auto edge_itr) {
while (edge_itr != edges.end() && edge_itr->source == node_id)
{
result_edges.push_back(edge_itr->Reduce());
++edge_itr;
}
return edge_itr;
};
// create a bisection node, requires the ID of the node as well as the lower bound to its edges
const auto make_bisection_node = [&edges, &coordinates](
const std::size_t node_id, const auto begin_itr, const auto end_itr) {
std::size_t range_begin = std::distance(edges.begin(), begin_itr);
std::size_t range_end = std::distance(edges.begin(), end_itr);
return BisectionGraph::NodeT(range_begin, range_end, coordinates[node_id], node_id);
};
auto edge_itr = edges.begin();
for (std::size_t node_id = 0; node_id < coordinates.size(); ++node_id)
{
const auto begin_itr = edge_itr;
edge_itr = advance_edge_itr(node_id, edge_itr);
result_nodes.emplace_back(make_bisection_node(node_id, begin_itr, edge_itr));
}
return BisectionGraph(std::move(result_nodes), std::move(result_edges));
}
// Reduce any edge to a fitting input edge for the bisection graph
template <typename InputEdge>
std::vector<BisectionInputEdge> adaptToBisectionEdge(std::vector<InputEdge> edges)
{
std::vector<BisectionInputEdge> result;
result.reserve(edges.size());
std::transform(begin(edges), end(edges), std::back_inserter(result), [](const auto &edge) {
return BisectionInputEdge{edge.source, edge.target};
});
return result;
}
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_BISECTION_GRAPH_HPP_
@@ -0,0 +1,70 @@
#ifndef OSRM_PARTITIONER_BISECTION_GRAPHVIEW_HPP_
#define OSRM_PARTITIONER_BISECTION_GRAPHVIEW_HPP_
#include "partitioner/bisection_graph.hpp"
#include <boost/iterator/filter_iterator.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <cstddef>
#include <cstdint>
namespace osrm
{
namespace partitioner
{
// Non-owning immutable sub-graph view into a base graph.
// The part of the graph to select is determined by the recursive bisection state.
class BisectionGraphView
{
public:
using ConstNodeIterator = BisectionGraph::ConstNodeIterator;
using NodeIterator = BisectionGraph::NodeIterator;
using NodeT = BisectionGraph::NodeT;
using EdgeT = BisectionGraph::EdgeT;
// Construction either for a subrange, or for a full range
BisectionGraphView(const BisectionGraph &graph);
BisectionGraphView(const BisectionGraph &graph,
const ConstNodeIterator begin,
const ConstNodeIterator end);
// construction from a different view, no need to keep the graph around
BisectionGraphView(const BisectionGraphView &view,
const ConstNodeIterator begin,
const ConstNodeIterator end);
// Number of nodes _in this sub-graph.
std::size_t NumberOfNodes() const;
// Iteration over all nodes (direct access into the node)
ConstNodeIterator Begin() const;
ConstNodeIterator End() const;
auto Nodes() const { return boost::make_iterator_range(begin, end); }
// Re-Construct the ID of a node from a reference
NodeID GetID(const NodeT &node) const;
// Access into single nodes/Edges
const NodeT &Node(const NodeID nid) const;
const EdgeT &Edge(const EdgeID eid) const;
// Access into all Edges
auto Edges(const NodeT &node) const { return bisection_graph.Edges(node); }
auto Edges(const NodeID nid) const { return bisection_graph.Edges(*(begin + nid)); }
auto BeginEdges(const NodeID nid) const { return bisection_graph.BeginEdges(*(begin + nid)); }
auto EndEdges(const NodeID nid) const { return bisection_graph.EndEdges(*(begin + nid)); }
private:
const BisectionGraph &bisection_graph;
const BisectionGraph::ConstNodeIterator begin;
const BisectionGraph::ConstNodeIterator end;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_GRAPHVIEW_HPP_
@@ -0,0 +1,23 @@
#ifndef OSRM_PARTITIONER_BISECTION_TO_PARTITION_HPP
#define OSRM_PARTITIONER_BISECTION_TO_PARTITION_HPP
#include "partitioner/multi_level_partition.hpp"
#include "partitioner/recursive_bisection.hpp"
#include <vector>
namespace osrm
{
namespace partitioner
{
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
+408
View File
@@ -0,0 +1,408 @@
#ifndef OSRM_PARTITIONER_CUSTOMIZE_CELL_STORAGE_HPP
#define OSRM_PARTITIONER_CUSTOMIZE_CELL_STORAGE_HPP
#include "partitioner/multi_level_partition.hpp"
#include "util/assert.hpp"
#include "util/for_each_range.hpp"
#include "util/log.hpp"
#include "util/typedefs.hpp"
#include "util/vector_view.hpp"
#include "storage/io_fwd.hpp"
#include "storage/shared_memory_ownership.hpp"
#include "customizer/cell_metric.hpp"
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <tbb/parallel_sort.h>
#include <algorithm>
#include <numeric>
#include <utility>
#include <vector>
namespace osrm
{
namespace partitioner
{
namespace detail
{
template <storage::Ownership Ownership> class CellStorageImpl;
}
using CellStorage = detail::CellStorageImpl<storage::Ownership::Container>;
using CellStorageView = detail::CellStorageImpl<storage::Ownership::View>;
namespace serialization
{
template <storage::Ownership Ownership>
inline void read(storage::io::FileReader &reader, detail::CellStorageImpl<Ownership> &storage);
template <storage::Ownership Ownership>
inline void write(storage::io::FileWriter &writer,
const detail::CellStorageImpl<Ownership> &storage);
}
namespace detail
{
template <storage::Ownership Ownership> class CellStorageImpl
{
public:
using ValueOffset = std::uint32_t;
using BoundaryOffset = std::uint32_t;
using BoundarySize = std::uint32_t;
using SourceIndex = std::uint32_t;
using DestinationIndex = std::uint32_t;
static constexpr auto INVALID_VALUE_OFFSET = std::numeric_limits<ValueOffset>::max();
static constexpr auto INVALID_BOUNDARY_OFFSET = std::numeric_limits<BoundaryOffset>::max();
struct CellData
{
ValueOffset value_offset = INVALID_VALUE_OFFSET;
BoundaryOffset source_boundary_offset = INVALID_BOUNDARY_OFFSET;
BoundaryOffset destination_boundary_offset = INVALID_BOUNDARY_OFFSET;
BoundarySize num_source_nodes = 0;
BoundarySize num_destination_nodes = 0;
};
private:
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
// Implementation of the cell view. We need a template parameter here
// because we need to derive a read-only and read-write view from this.
template <typename WeightValueT, typename DurationValueT> class CellImpl
{
private:
using WeightPtrT = WeightValueT *;
using DurationPtrT = DurationValueT *;
BoundarySize num_source_nodes;
BoundarySize num_destination_nodes;
WeightPtrT const weights;
DurationPtrT const durations;
const NodeID *const source_boundary;
const NodeID *const destination_boundary;
using RowIterator = WeightPtrT;
// Possibly replace with
// http://www.boost.org/doc/libs/1_55_0/libs/range/doc/html/range/reference/adaptors/reference/strided.html
class ColumnIterator : public boost::iterator_facade<ColumnIterator,
WeightValueT,
boost::random_access_traversal_tag>
{
typedef boost::iterator_facade<ColumnIterator,
WeightValueT,
boost::random_access_traversal_tag>
base_t;
public:
typedef typename base_t::value_type value_type;
typedef typename base_t::difference_type difference_type;
typedef typename base_t::reference reference;
typedef std::random_access_iterator_tag iterator_category;
explicit ColumnIterator() : current(nullptr), stride(1) {}
explicit ColumnIterator(WeightPtrT begin, std::size_t row_length)
: current(begin), stride(row_length)
{
BOOST_ASSERT(begin != nullptr);
}
private:
void increment() { current += stride; }
void decrement() { current -= stride; }
void advance(difference_type offset) { current += stride * offset; }
bool equal(const ColumnIterator &other) const { return current == other.current; }
reference dereference() const { return *current; }
difference_type distance_to(const ColumnIterator &other) const
{
return (other.current - current) / static_cast<std::intptr_t>(stride);
}
friend class ::boost::iterator_core_access;
WeightPtrT current;
const std::size_t stride;
};
template <typename ValuePtr> auto GetOutRange(const ValuePtr ptr, const NodeID node) const
{
auto iter = std::find(source_boundary, source_boundary + num_source_nodes, node);
if (iter == source_boundary + num_source_nodes)
return boost::make_iterator_range(ptr, ptr);
auto row = std::distance(source_boundary, iter);
auto begin = ptr + num_destination_nodes * row;
auto end = begin + num_destination_nodes;
return boost::make_iterator_range(begin, end);
}
template <typename ValuePtr> auto GetInRange(const ValuePtr ptr, const NodeID node) const
{
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{ptr + column, num_destination_nodes};
auto end = ColumnIterator{ptr + column + num_source_nodes * num_destination_nodes,
num_destination_nodes};
return boost::make_iterator_range(begin, end);
}
public:
auto GetOutWeight(NodeID node) const { return GetOutRange(weights, node); }
auto GetInWeight(NodeID node) const { return GetInRange(weights, node); }
auto GetOutDuration(NodeID node) const { return GetOutRange(durations, node); }
auto GetInDuration(NodeID node) const { return GetInRange(durations, node); }
auto GetSourceNodes() const
{
return boost::make_iterator_range(source_boundary, source_boundary + num_source_nodes);
}
auto GetDestinationNodes() const
{
return boost::make_iterator_range(destination_boundary,
destination_boundary + num_destination_nodes);
}
CellImpl(const CellData &data,
WeightPtrT const all_weights,
DurationPtrT const all_durations,
const NodeID *const all_sources,
const NodeID *const all_destinations)
: num_source_nodes{data.num_source_nodes},
num_destination_nodes{data.num_destination_nodes},
weights{all_weights + data.value_offset},
durations{all_durations + data.value_offset},
source_boundary{all_sources + data.source_boundary_offset},
destination_boundary{all_destinations + data.destination_boundary_offset}
{
BOOST_ASSERT(all_weights != nullptr);
BOOST_ASSERT(all_durations != nullptr);
BOOST_ASSERT(num_source_nodes == 0 || all_sources != nullptr);
BOOST_ASSERT(num_destination_nodes == 0 || all_destinations != nullptr);
}
};
std::size_t LevelIDToIndex(LevelID level) const { return level - 1; }
public:
using Cell = CellImpl<EdgeWeight, EdgeDuration>;
using ConstCell = CellImpl<const EdgeWeight, const EdgeDuration>;
CellStorageImpl() {}
template <typename GraphT,
typename = std::enable_if<Ownership == storage::Ownership::Container>>
CellStorageImpl(const partitioner::MultiLevelPartition &partition, const GraphT &base_graph)
{
// pre-allocate storge for CellData so we can have random access to it by cell id
unsigned number_of_cells = 0;
for (LevelID level = 1u; level < partition.GetNumberOfLevels(); ++level)
{
level_to_cell_offset.push_back(number_of_cells);
number_of_cells += partition.GetNumberOfCells(level);
}
level_to_cell_offset.push_back(number_of_cells);
cells.resize(number_of_cells);
std::vector<std::pair<CellID, NodeID>> level_source_boundary;
std::vector<std::pair<CellID, NodeID>> level_destination_boundary;
std::size_t number_of_unconneced = 0;
for (LevelID level = 1u; level < partition.GetNumberOfLevels(); ++level)
{
auto level_offset = level_to_cell_offset[LevelIDToIndex(level)];
level_source_boundary.clear();
level_destination_boundary.clear();
for (auto node = 0u; node < base_graph.GetNumberOfNodes(); ++node)
{
const CellID cell_id = partition.GetCell(level, node);
bool is_source_node = false;
bool is_destination_node = false;
bool is_boundary_node = false;
for (auto edge : base_graph.GetAdjacentEdgeRange(node))
{
auto other = base_graph.GetTarget(edge);
const auto &data = base_graph.GetEdgeData(edge);
is_boundary_node |= partition.GetCell(level, other) != cell_id;
is_source_node |= partition.GetCell(level, other) == cell_id && data.forward;
is_destination_node |=
partition.GetCell(level, other) == cell_id && data.backward;
}
if (is_boundary_node)
{
if (is_source_node)
level_source_boundary.emplace_back(cell_id, node);
if (is_destination_node)
level_destination_boundary.emplace_back(cell_id, node);
// if a node is unconnected we still need to keep it for correctness
// this adds it to the destination array to form an "empty" column
if (!is_source_node && !is_destination_node)
{
number_of_unconneced++;
util::Log(logWARNING) << "Found unconnected boundary node " << node << "("
<< cell_id << ") on level " << (int)level;
level_destination_boundary.emplace_back(cell_id, node);
}
}
}
tbb::parallel_sort(level_source_boundary.begin(), level_source_boundary.end());
tbb::parallel_sort(level_destination_boundary.begin(),
level_destination_boundary.end());
const auto insert_cell_boundary = [this, level_offset](auto &boundary,
auto set_num_nodes_fn,
auto set_boundary_offset_fn,
auto begin,
auto end) {
BOOST_ASSERT(std::distance(begin, end) > 0);
const auto cell_id = begin->first;
BOOST_ASSERT(level_offset + cell_id < cells.size());
auto &cell = cells[level_offset + cell_id];
set_num_nodes_fn(cell, std::distance(begin, end));
set_boundary_offset_fn(cell, boundary.size());
std::transform(begin,
end,
std::back_inserter(boundary),
[](const auto &cell_and_node) { return cell_and_node.second; });
};
util::for_each_range(
level_source_boundary.begin(),
level_source_boundary.end(),
[this, insert_cell_boundary](auto begin, auto end) {
insert_cell_boundary(
source_boundary,
[](auto &cell, auto value) { cell.num_source_nodes = value; },
[](auto &cell, auto value) { cell.source_boundary_offset = value; },
begin,
end);
});
util::for_each_range(
level_destination_boundary.begin(),
level_destination_boundary.end(),
[this, insert_cell_boundary](auto begin, auto end) {
insert_cell_boundary(
destination_boundary,
[](auto &cell, auto value) { cell.num_destination_nodes = value; },
[](auto &cell, auto value) { cell.destination_boundary_offset = value; },
begin,
end);
});
}
// a partition that contains boundary nodes that have no arcs going into
// the cells or coming out of it is bad. These nodes should be reassigned
// to a different cell.
if (number_of_unconneced > 0)
{
util::Log(logWARNING) << "Node needs to either have incoming or outgoing edges in cell."
<< " Number of unconnected nodes is " << number_of_unconneced;
}
// Set cell values offsets and calculate total storage size
ValueOffset value_offset = 0;
for (auto &cell : cells)
{
cell.value_offset = value_offset;
value_offset += cell.num_source_nodes * cell.num_destination_nodes;
}
}
// Returns a new metric that can be used with this container
customizer::CellMetric MakeMetric() const
{
customizer::CellMetric metric;
if (cells.empty())
{
return metric;
}
const auto &last_cell = cells.back();
ValueOffset total_size =
last_cell.value_offset + last_cell.num_source_nodes * last_cell.num_destination_nodes;
metric.weights.resize(total_size + 1, INVALID_EDGE_WEIGHT);
metric.durations.resize(total_size + 1, MAXIMAL_EDGE_DURATION);
return metric;
}
template <typename = std::enable_if<Ownership == storage::Ownership::View>>
CellStorageImpl(Vector<NodeID> source_boundary_,
Vector<NodeID> destination_boundary_,
Vector<CellData> cells_,
Vector<std::uint64_t> level_to_cell_offset_)
: source_boundary(std::move(source_boundary_)),
destination_boundary(std::move(destination_boundary_)), cells(std::move(cells_)),
level_to_cell_offset(std::move(level_to_cell_offset_))
{
}
ConstCell GetCell(const customizer::detail::CellMetricImpl<Ownership> &metric,
LevelID level,
CellID id) const
{
const auto level_index = LevelIDToIndex(level);
BOOST_ASSERT(level_index < level_to_cell_offset.size());
const auto offset = level_to_cell_offset[level_index];
const auto cell_index = offset + id;
BOOST_ASSERT(cell_index < cells.size());
return ConstCell{cells[cell_index],
metric.weights.data(),
metric.durations.data(),
source_boundary.empty() ? nullptr : source_boundary.data(),
destination_boundary.empty() ? nullptr : destination_boundary.data()};
}
template <typename = std::enable_if<Ownership == storage::Ownership::Container>>
Cell GetCell(customizer::CellMetric &metric, LevelID level, CellID id) const
{
const auto level_index = LevelIDToIndex(level);
BOOST_ASSERT(level_index < level_to_cell_offset.size());
const auto offset = level_to_cell_offset[level_index];
const auto cell_index = offset + id;
BOOST_ASSERT(cell_index < cells.size());
return Cell{cells[cell_index],
metric.weights.data(),
metric.durations.data(),
source_boundary.data(),
destination_boundary.data()};
}
friend void serialization::read<Ownership>(storage::io::FileReader &reader,
detail::CellStorageImpl<Ownership> &storage);
friend void serialization::write<Ownership>(storage::io::FileWriter &writer,
const detail::CellStorageImpl<Ownership> &storage);
private:
Vector<NodeID> source_boundary;
Vector<NodeID> destination_boundary;
Vector<CellData> cells;
Vector<std::uint64_t> level_to_cell_offset;
};
}
}
}
#endif // OSRM_PARTITIONER_CUSTOMIZE_CELL_STORAGE_HPP
@@ -0,0 +1,60 @@
#ifndef OSRM_PARTITIONER_COMPRESSED_NODE_BASED_GRAPH_READER_HPP
#define OSRM_PARTITIONER_COMPRESSED_NODE_BASED_GRAPH_READER_HPP
#include "storage/io.hpp"
#include "util/coordinate.hpp"
#include "util/typedefs.hpp"
#include <string>
#include <vector>
namespace osrm
{
namespace partitioner
{
struct CompressedNodeBasedGraphEdge
{
NodeID source;
NodeID target;
};
struct CompressedNodeBasedGraph
{
CompressedNodeBasedGraph(storage::io::FileReader &reader)
{
// Reads: | Fingerprint | #e | #n | edges | coordinates |
// - uint64: number of edges (from, to) pairs
// - uint64: number of nodes and therefore also coordinates
// - (uint32_t, uint32_t): num_edges * edges
// - (int32_t, int32_t: num_nodes * coordinates (lon, lat)
//
// Gets written in Extractor::WriteCompressedNodeBasedGraph
const auto num_edges = reader.ReadElementCount64();
const auto num_nodes = reader.ReadElementCount64();
edges.resize(num_edges);
coordinates.resize(num_nodes);
reader.ReadInto(edges);
reader.ReadInto(coordinates);
}
std::vector<CompressedNodeBasedGraphEdge> edges;
std::vector<util::Coordinate> coordinates;
};
inline CompressedNodeBasedGraph LoadCompressedNodeBasedGraph(const std::string &path)
{
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader(path, fingerprint);
CompressedNodeBasedGraph graph{reader};
return graph;
}
} // ns partition
} // ns osrm
#endif
+97
View File
@@ -0,0 +1,97 @@
#ifndef OSRM_PARTITIONER_DINIC_MAX_FLOW_HPP_
#define OSRM_PARTITIONER_DINIC_MAX_FLOW_HPP_
#include "partitioner/bisection_graph_view.hpp"
#include <cstdint>
#include <functional>
#include <set>
#include <unordered_set>
#include <utility>
#include <vector>
namespace osrm
{
namespace partitioner
{
class DinicMaxFlow
{
public:
// maximal number of hops in the graph from source to sink
using Level = std::uint32_t;
using MinCut = struct
{
std::size_t num_nodes_source;
std::size_t num_edges;
std::vector<bool> flags;
};
// input parameter storing the set o
using SourceSinkNodes = std::unordered_set<NodeID>;
MinCut operator()(const BisectionGraphView &view,
const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes) const;
// validates the inpiut parameters to the flow algorithm (e.g. not intersecting)
bool Validate(const BisectionGraphView &view,
const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes) const;
private:
// the level of each node in the graph (==hops in BFS from source)
using LevelGraph = std::vector<Level>;
// this is actually faster than using an unordered_set<Edge>, stores all edges that have
// capacity grouped by node
using FlowEdges = std::vector<std::set<NodeID>>;
// The level graph (see [1]) is based on a BFS computation. We assign a level to all nodes
// (starting with 0 for all source nodes) and assign the hop distance in the residual graph as
// the level of the node.
// a
// / \ 
// s t
// \ /
// b
// would assign s = 0, a,b = 1, t=2
LevelGraph ComputeLevelGraph(const BisectionGraphView &view,
const std::vector<NodeID> &border_source_nodes,
const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes,
const FlowEdges &flow) const;
// Using the above levels (see ComputeLevelGraph), we can use multiple DFS (that can now be
// directed at the sink) to find a flow that completely blocks the level graph (i.e. no path
// with increasing level exists from `s` to `t`).
std::size_t BlockingFlow(FlowEdges &flow,
LevelGraph &levels,
const BisectionGraphView &view,
const SourceSinkNodes &source_nodes,
const std::vector<NodeID> &border_sink_nodes) const;
// Finds a single augmenting path from a node to the sink side following levels in the level
// graph. We don't actually remove the edges, so we have to check for increasing level values.
// Since we know which sinks have been reached, we actually search for these paths starting at
// sink nodes, instead of the source, so we can save a few dfs runs
std::vector<NodeID> GetAugmentingPath(LevelGraph &levels,
const NodeID from,
const BisectionGraphView &view,
const FlowEdges &flow,
const SourceSinkNodes &source_nodes) const;
// Builds an actual cut result from a level graph
MinCut MakeCut(const BisectionGraphView &view,
const LevelGraph &levels,
const std::size_t flow_value) const;
};
} // namespace partitioner
} // namespace osrm
// Implementation of Dinics [1] algorithm for max-flow/min-cut.
// [1] https://www.cs.bgu.ac.il/~dinitz/D70.pdf
#endif // OSRM_PARTITIONER_DINIC_MAX_FLOW_HPP_
+49
View File
@@ -0,0 +1,49 @@
#ifndef OSRM_PARTITIONER_EDGE_BASED_GRAPH_HPP
#define OSRM_PARTITIONER_EDGE_BASED_GRAPH_HPP
#include "extractor/edge_based_edge.hpp"
#include "storage/io.hpp"
#include "util/coordinate.hpp"
#include "util/dynamic_graph.hpp"
#include "util/typedefs.hpp"
#include <cstdint>
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
namespace osrm
{
namespace partitioner
{
struct EdgeBasedGraphEdgeData : extractor::EdgeBasedEdge::EdgeData
{
using Base = extractor::EdgeBasedEdge::EdgeData;
using Base::Base;
EdgeBasedGraphEdgeData(const EdgeBasedGraphEdgeData &) = default;
EdgeBasedGraphEdgeData(EdgeBasedGraphEdgeData &&) = default;
EdgeBasedGraphEdgeData &operator=(const EdgeBasedGraphEdgeData &) = default;
EdgeBasedGraphEdgeData &operator=(EdgeBasedGraphEdgeData &&) = default;
EdgeBasedGraphEdgeData(const Base &base) : Base(base) {}
EdgeBasedGraphEdgeData() : Base() {}
};
struct DynamicEdgeBasedGraph : util::DynamicGraph<EdgeBasedGraphEdgeData>
{
using Base = util::DynamicGraph<EdgeBasedGraphEdgeData>;
using Base::Base;
};
struct DynamicEdgeBasedGraphEdge : DynamicEdgeBasedGraph::InputEdge
{
using Base = DynamicEdgeBasedGraph::InputEdge;
using Base::Base;
};
}
}
#endif
@@ -0,0 +1,201 @@
#ifndef OSRM_PARTITIONER_EDGE_BASED_GRAPH_READER_HPP
#define OSRM_PARTITIONER_EDGE_BASED_GRAPH_READER_HPP
#include "partitioner/edge_based_graph.hpp"
#include "extractor/edge_based_edge.hpp"
#include "extractor/files.hpp"
#include "storage/io.hpp"
#include "util/coordinate.hpp"
#include "util/dynamic_graph.hpp"
#include "util/typedefs.hpp"
#include <tbb/blocked_range.h>
#include <tbb/parallel_for.h>
#include <tbb/parallel_reduce.h>
#include <cstdint>
#include <algorithm>
#include <iterator>
#include <memory>
#include <vector>
namespace osrm
{
namespace partitioner
{
// Bidirectional (s,t) to (s,t) and (t,s)
inline std::vector<extractor::EdgeBasedEdge>
splitBidirectionalEdges(const std::vector<extractor::EdgeBasedEdge> &edges)
{
std::vector<extractor::EdgeBasedEdge> directed;
directed.reserve(edges.size() * 2);
for (const auto &edge : edges)
{
if (edge.data.weight == INVALID_EDGE_WEIGHT)
continue;
directed.emplace_back(edge.source,
edge.target,
edge.data.turn_id,
std::max(edge.data.weight, 1),
edge.data.duration,
edge.data.forward,
edge.data.backward);
directed.emplace_back(edge.target,
edge.source,
edge.data.turn_id,
std::max(edge.data.weight, 1),
edge.data.duration,
edge.data.backward,
edge.data.forward);
}
return directed;
}
template <typename OutputEdgeT>
std::vector<OutputEdgeT> prepareEdgesForUsageInGraph(std::vector<extractor::EdgeBasedEdge> edges)
{
// sort into blocks of edges with same source + target
// the we partition by the forward flag to sort all edges with a forward direction first.
// the we sort by weight to ensure the first forward edge is the smallest forward edge
std::sort(begin(edges), end(edges), [](const auto &lhs, const auto &rhs) {
return std::tie(lhs.source, lhs.target, rhs.data.forward, lhs.data.weight) <
std::tie(rhs.source, rhs.target, lhs.data.forward, rhs.data.weight);
});
std::vector<OutputEdgeT> output_edges;
output_edges.reserve(edges.size());
for (auto begin_interval = edges.begin(); begin_interval != edges.end();)
{
const NodeID source = begin_interval->source;
const NodeID target = begin_interval->target;
auto end_interval =
std::find_if_not(begin_interval, edges.end(), [source, target](const auto &edge) {
return std::tie(edge.source, edge.target) == std::tie(source, target);
});
BOOST_ASSERT(begin_interval != end_interval);
// remove eigenloops
if (source == target)
{
begin_interval = end_interval;
continue;
}
BOOST_ASSERT_MSG(begin_interval->data.forward != begin_interval->data.backward,
"The forward and backward flag need to be mutally exclusive");
// find smallest backward edge and check if we can merge
auto first_backward = std::find_if(
begin_interval, end_interval, [](const auto &edge) { return edge.data.backward; });
// thanks to the sorting we know this is the smallest backward edge
// and there is no forward edge
if (begin_interval == first_backward)
{
output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
}
// only a forward edge, thanks to the sorting this is the smallest
else if (first_backward == end_interval)
{
output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
}
// we have both a forward and a backward edge, we need to evaluate
// if we can merge them
else
{
BOOST_ASSERT(begin_interval->data.forward);
BOOST_ASSERT(first_backward->data.backward);
BOOST_ASSERT(first_backward != end_interval);
// same weight, so we can just merge them
if (begin_interval->data.weight == first_backward->data.weight)
{
OutputEdgeT merged{source, target, begin_interval->data};
merged.data.backward = true;
output_edges.push_back(std::move(merged));
}
// we need to insert separate forward and reverse edges
else
{
output_edges.push_back(OutputEdgeT{source, target, begin_interval->data});
output_edges.push_back(OutputEdgeT{source, target, first_backward->data});
}
}
begin_interval = end_interval;
}
return output_edges;
}
inline std::vector<extractor::EdgeBasedEdge>
graphToEdges(const DynamicEdgeBasedGraph &edge_based_graph)
{
auto range = tbb::blocked_range<NodeID>(0, edge_based_graph.GetNumberOfNodes());
auto max_turn_id =
tbb::parallel_reduce(range,
NodeID{0},
[&edge_based_graph](const auto range, NodeID initial) {
NodeID max_turn_id = initial;
for (auto node = range.begin(); node < range.end(); ++node)
{
for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
{
const auto &data = edge_based_graph.GetEdgeData(edge);
max_turn_id = std::max(max_turn_id, data.turn_id);
}
}
return max_turn_id;
},
[](const NodeID lhs, const NodeID rhs) { return std::max(lhs, rhs); });
std::vector<extractor::EdgeBasedEdge> edges(max_turn_id + 1);
tbb::parallel_for(range, [&](const auto range) {
for (auto node = range.begin(); node < range.end(); ++node)
{
for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
{
const auto &data = edge_based_graph.GetEdgeData(edge);
// we only need to save the forward edges, since the read method will
// convert from forward to bi-directional edges again
if (data.forward)
{
auto target = edge_based_graph.GetTarget(edge);
BOOST_ASSERT(data.turn_id <= max_turn_id);
edges[data.turn_id] = extractor::EdgeBasedEdge{node, target, data};
// only save the forward edge
edges[data.turn_id].data.forward = true;
edges[data.turn_id].data.backward = false;
}
}
}
});
return edges;
}
inline DynamicEdgeBasedGraph LoadEdgeBasedGraph(const boost::filesystem::path &path)
{
EdgeID number_of_edge_based_nodes;
std::vector<extractor::EdgeBasedEdge> edges;
extractor::files::readEdgeBasedGraph(path, number_of_edge_based_nodes, edges);
auto directed = splitBidirectionalEdges(edges);
auto tidied = prepareEdgesForUsageInGraph<DynamicEdgeBasedGraphEdge>(std::move(directed));
return DynamicEdgeBasedGraph(number_of_edge_based_nodes, std::move(tidied));
}
} // ns partition
} // ns osrm
#endif
+104
View File
@@ -0,0 +1,104 @@
#ifndef OSRM_PARTITIONER_SERILIZATION_HPP
#define OSRM_PARTITIONER_SERILIZATION_HPP
#include "customizer/edge_based_graph.hpp"
#include "partitioner/serialization.hpp"
#include "storage/io.hpp"
namespace osrm
{
namespace partitioner
{
namespace files
{
// reads .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void readGraph(const boost::filesystem::path &path, MultiLevelGraphT &graph)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
serialization::read(reader, graph);
}
// writes .osrm.mldgr file
template <typename MultiLevelGraphT>
inline void writeGraph(const boost::filesystem::path &path, const MultiLevelGraphT &graph)
{
static_assert(std::is_same<customizer::MultiLevelEdgeBasedGraphView, MultiLevelGraphT>::value ||
std::is_same<customizer::MultiLevelEdgeBasedGraph, MultiLevelGraphT>::value,
"");
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
storage::io::FileWriter writer{path, fingerprint};
serialization::write(writer, graph);
}
// read .osrm.partition file
template <typename MultiLevelPartitionT>
inline void readPartition(const boost::filesystem::path &path, MultiLevelPartitionT &mlp)
{
static_assert(std::is_same<MultiLevelPartitionView, MultiLevelPartitionT>::value ||
std::is_same<MultiLevelPartition, MultiLevelPartitionT>::value,
"");
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
serialization::read(reader, mlp);
}
// writes .osrm.partition file
template <typename MultiLevelPartitionT>
inline void writePartition(const boost::filesystem::path &path, const MultiLevelPartitionT &mlp)
{
static_assert(std::is_same<MultiLevelPartitionView, MultiLevelPartitionT>::value ||
std::is_same<MultiLevelPartition, MultiLevelPartitionT>::value,
"");
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
storage::io::FileWriter writer{path, fingerprint};
serialization::write(writer, mlp);
}
// reads .osrm.cells file
template <typename CellStorageT>
inline void readCells(const boost::filesystem::path &path, CellStorageT &storage)
{
static_assert(std::is_same<CellStorageView, CellStorageT>::value ||
std::is_same<CellStorage, CellStorageT>::value,
"");
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
storage::io::FileReader reader{path, fingerprint};
serialization::read(reader, storage);
}
// writes .osrm.cells file
template <typename CellStorageT>
inline void writeCells(const boost::filesystem::path &path, CellStorageT &storage)
{
static_assert(std::is_same<CellStorageView, CellStorageT>::value ||
std::is_same<CellStorage, CellStorageT>::value,
"");
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
storage::io::FileWriter writer{path, fingerprint};
serialization::write(writer, storage);
}
}
}
}
#endif
+20
View File
@@ -0,0 +1,20 @@
#ifndef OSRM_PARTITIONER_INERTIAL_FLOW_HPP_
#define OSRM_PARTITIONER_INERTIAL_FLOW_HPP_
#include "partitioner/bisection_graph_view.hpp"
#include "partitioner/dinic_max_flow.hpp"
namespace osrm
{
namespace partitioner
{
DinicMaxFlow::MinCut computeInertialFlowCut(const BisectionGraphView &view,
const std::size_t num_slopes,
const double balance,
const double source_sink_rate);
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_INERTIAL_FLOW_HPP_
+212
View File
@@ -0,0 +1,212 @@
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#define OSRM_PARTITIONER_MULTI_LEVEL_GRAPH_HPP
#include "partitioner/multi_level_partition.hpp"
#include "storage/io_fwd.hpp"
#include "storage/shared_memory_ownership.hpp"
#include "util/static_graph.hpp"
#include "util/vector_view.hpp"
#include <tbb/parallel_sort.h>
#include <boost/iterator/permutation_iterator.hpp>
#include <boost/range/combine.hpp>
namespace osrm
{
namespace partitioner
{
template <typename EdgeDataT, storage::Ownership Ownership> class MultiLevelGraph;
namespace serialization
{
template <typename EdgeDataT, storage::Ownership Ownership>
void read(storage::io::FileReader &reader, MultiLevelGraph<EdgeDataT, Ownership> &graph);
template <typename EdgeDataT, storage::Ownership Ownership>
void write(storage::io::FileWriter &writer, const MultiLevelGraph<EdgeDataT, Ownership> &graph);
}
template <typename EdgeDataT, storage::Ownership Ownership>
class MultiLevelGraph : public util::StaticGraph<EdgeDataT, Ownership>
{
private:
using SuperT = util::StaticGraph<EdgeDataT, Ownership>;
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
// We limit each node to have 255 edges
// this is very generous, we could probably pack this
using EdgeOffset = std::uint8_t;
MultiLevelGraph() = default;
MultiLevelGraph(MultiLevelGraph &&) = default;
MultiLevelGraph(const MultiLevelGraph &) = default;
MultiLevelGraph &operator=(MultiLevelGraph &&) = default;
MultiLevelGraph &operator=(const MultiLevelGraph &) = default;
MultiLevelGraph(Vector<typename SuperT::NodeArrayEntry> node_array_,
Vector<typename SuperT::EdgeArrayEntry> edge_array_,
Vector<EdgeOffset> node_to_edge_offset_)
: SuperT(std::move(node_array_), std::move(edge_array_)),
node_to_edge_offset(std::move(node_to_edge_offset_))
{
}
template <typename ContainerT>
MultiLevelGraph(const MultiLevelPartition &mlp,
const std::uint32_t num_nodes,
const ContainerT &edges)
{
auto highest_border_level = GetHighestBorderLevel(mlp, edges);
auto permutation = SortEdgesByHighestLevel(highest_border_level, edges);
auto sorted_edges_begin =
boost::make_permutation_iterator(edges.begin(), permutation.begin());
auto sorted_edges_end = boost::make_permutation_iterator(edges.begin(), permutation.end());
SuperT::InitializeFromSortedEdgeRange(num_nodes, sorted_edges_begin, sorted_edges_end);
// if the node ordering is sorting the border nodes first,
// the id of the maximum border node will be rather low
// enabling us to save some memory here
auto max_border_node_id = 0u;
for (auto edge_index : util::irange<std::size_t>(0, edges.size()))
{
if (highest_border_level[edge_index] > 0)
{
max_border_node_id =
std::max(max_border_node_id,
std::max(edges[edge_index].source, edges[edge_index].target));
}
}
BOOST_ASSERT(max_border_node_id < num_nodes);
auto edge_and_level_range = boost::combine(edges, highest_border_level);
auto sorted_edge_and_level_begin =
boost::make_permutation_iterator(edge_and_level_range.begin(), permutation.begin());
auto sorted_edge_and_level_end =
boost::make_permutation_iterator(edge_and_level_range.begin(), permutation.end());
InitializeOffsetsFromSortedEdges(
mlp, max_border_node_id, sorted_edge_and_level_begin, sorted_edge_and_level_end);
}
// Fast scan over all relevant border edges
// For level 0 this yield the same result as GetAdjacentEdgeRange
auto GetBorderEdgeRange(const LevelID level, const NodeID node) const
{
auto begin = BeginBorderEdges(level, node);
auto end = SuperT::EndEdges(node);
return util::irange<EdgeID>(begin, end);
}
// Fast scan over all relevant internal edges, that is edges that will not
// leave the cell of that node at the given level
// For level 0 this returns an empty edge range
auto GetInternalEdgeRange(const LevelID level, const NodeID node) const
{
auto begin = SuperT::BeginEdges(node);
auto end = BeginBorderEdges(level, node);
return util::irange<EdgeID>(begin, end);
}
EdgeID BeginBorderEdges(const LevelID level, const NodeID node) const
{
auto index = node * GetNumberOfLevels();
// `node_to_edge_offset` has size `max_border_node_id + 1`
// which can be smaller then the total number of nodes.
// this will save memory in case we sort the border nodes first
if (index >= node_to_edge_offset.size() - 1)
{
// On level 0 all edges are border edges
if (level == 0)
return SuperT::BeginEdges(node);
else
return SuperT::EndEdges(node);
}
else
{
return SuperT::BeginEdges(node) + node_to_edge_offset[index + level];
}
}
// We save the level as sentinel at the end
LevelID GetNumberOfLevels() const { return node_to_edge_offset.back(); }
private:
template <typename ContainerT>
auto GetHighestBorderLevel(const MultiLevelPartition &mlp, const ContainerT &edges) const
{
std::vector<LevelID> highest_border_level(edges.size());
std::transform(
edges.begin(), edges.end(), highest_border_level.begin(), [&mlp](const auto &edge) {
return mlp.GetHighestDifferentLevel(edge.source, edge.target);
});
return highest_border_level;
}
template <typename ContainerT>
auto SortEdgesByHighestLevel(const std::vector<LevelID> &highest_border_level,
const ContainerT &edges) const
{
std::vector<std::uint32_t> permutation(edges.size());
std::iota(permutation.begin(), permutation.end(), 0);
tbb::parallel_sort(
permutation.begin(),
permutation.end(),
[&edges, &highest_border_level](const auto &lhs, const auto &rhs) {
// sort by source node and then by level in ascending order
return std::tie(edges[lhs].source, highest_border_level[lhs], edges[lhs].target) <
std::tie(edges[rhs].source, highest_border_level[rhs], edges[rhs].target);
});
return permutation;
}
template <typename ZipIterT>
auto InitializeOffsetsFromSortedEdges(const MultiLevelPartition &mlp,
const NodeID max_border_node_id,
ZipIterT edge_and_level_begin,
ZipIterT edge_and_level_end)
{
auto num_levels = mlp.GetNumberOfLevels();
// we save one sentinel element at the end
node_to_edge_offset.reserve(num_levels * (max_border_node_id + 1) + 1);
auto iter = edge_and_level_begin;
for (auto node : util::irange<NodeID>(0, max_border_node_id + 1))
{
node_to_edge_offset.push_back(0);
auto level_begin = iter;
for (auto level : util::irange<LevelID>(0, mlp.GetNumberOfLevels()))
{
iter = std::find_if(
iter, edge_and_level_end, [node, level](const auto &edge_and_level) {
return boost::get<0>(edge_and_level).source != node ||
boost::get<1>(edge_and_level) != level;
});
EdgeOffset offset = std::distance(level_begin, iter);
node_to_edge_offset.push_back(offset);
}
node_to_edge_offset.pop_back();
}
BOOST_ASSERT(node_to_edge_offset.size() ==
mlp.GetNumberOfLevels() * (max_border_node_id + 1));
// save number of levels as last element so we can reconstruct the stride
node_to_edge_offset.push_back(mlp.GetNumberOfLevels());
}
friend void
serialization::read<EdgeDataT, Ownership>(storage::io::FileReader &reader,
MultiLevelGraph<EdgeDataT, Ownership> &graph);
friend void
serialization::write<EdgeDataT, Ownership>(storage::io::FileWriter &writer,
const MultiLevelGraph<EdgeDataT, Ownership> &graph);
Vector<EdgeOffset> node_to_edge_offset;
};
}
}
#endif
@@ -0,0 +1,339 @@
#ifndef OSRM_PARTITIONER_MULTI_LEVEL_PARTITION_HPP
#define OSRM_PARTITIONER_MULTI_LEVEL_PARTITION_HPP
#include "util/exception.hpp"
#include "util/for_each_pair.hpp"
#include "util/msb.hpp"
#include "util/typedefs.hpp"
#include "util/vector_view.hpp"
#include "storage/io_fwd.hpp"
#include "storage/shared_memory_ownership.hpp"
#include <algorithm>
#include <array>
#include <climits>
#include <cmath>
#include <cstdint>
#include <numeric>
#include <vector>
#include <boost/range/adaptor/reversed.hpp>
namespace osrm
{
namespace partitioner
{
namespace detail
{
template <storage::Ownership Ownership> class MultiLevelPartitionImpl;
}
using MultiLevelPartition = detail::MultiLevelPartitionImpl<storage::Ownership::Container>;
using MultiLevelPartitionView = detail::MultiLevelPartitionImpl<storage::Ownership::View>;
namespace serialization
{
template <storage::Ownership Ownership>
void read(storage::io::FileReader &reader, detail::MultiLevelPartitionImpl<Ownership> &mlp);
template <storage::Ownership Ownership>
void write(storage::io::FileWriter &writer, const detail::MultiLevelPartitionImpl<Ownership> &mlp);
}
namespace detail
{
template <storage::Ownership Ownership> class MultiLevelPartitionImpl final
{
// we will support at most 16 levels
static const constexpr std::uint8_t MAX_NUM_LEVEL = 16;
static const constexpr std::uint8_t NUM_PARTITION_BITS = sizeof(PartitionID) * CHAR_BIT;
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
public:
// Contains all data necessary to describe the level hierarchy
struct LevelData
{
std::uint32_t num_level;
std::array<std::uint8_t, MAX_NUM_LEVEL - 1> lidx_to_offset;
std::array<PartitionID, MAX_NUM_LEVEL - 1> lidx_to_mask;
std::array<LevelID, NUM_PARTITION_BITS> bit_to_level;
std::array<std::uint32_t, MAX_NUM_LEVEL - 1> lidx_to_children_offsets;
};
using LevelDataPtr = typename std::conditional<Ownership == storage::Ownership::View,
LevelData *,
std::unique_ptr<LevelData>>::type;
MultiLevelPartitionImpl();
// cell_sizes is index by level (starting at 0, the base graph).
// However level 0 always needs to have cell size 1, since it is the
// basegraph.
template <typename = typename std::enable_if<Ownership == storage::Ownership::Container>>
MultiLevelPartitionImpl(const std::vector<std::vector<CellID>> &partitions,
const std::vector<std::uint32_t> &lidx_to_num_cells)
: level_data(MakeLevelData(lidx_to_num_cells))
{
InitializePartitionIDs(partitions);
}
template <typename = typename std::enable_if<Ownership == storage::Ownership::View>>
MultiLevelPartitionImpl(LevelDataPtr level_data,
Vector<PartitionID> partition_,
Vector<CellID> cell_to_children_)
: level_data(std::move(level_data)), partition(std::move(partition_)),
cell_to_children(std::move(cell_to_children_))
{
}
// returns the index of the cell the vertex is contained at level l
CellID GetCell(LevelID l, NodeID node) const
{
auto p = partition[node];
auto lidx = LevelIDToIndex(l);
auto masked = p & level_data->lidx_to_mask[lidx];
return masked >> level_data->lidx_to_offset[lidx];
}
LevelID GetQueryLevel(NodeID start, NodeID target, NodeID node) const
{
return std::min(GetHighestDifferentLevel(start, node),
GetHighestDifferentLevel(target, node));
}
LevelID GetHighestDifferentLevel(NodeID first, NodeID second) const
{
if (partition[first] == partition[second])
return 0;
auto msb = util::msb(partition[first] ^ partition[second]);
return level_data->bit_to_level[msb];
}
std::uint8_t GetNumberOfLevels() const { return level_data->num_level; }
std::uint32_t GetNumberOfCells(LevelID level) const
{
return GetCell(level, GetSentinelNode());
}
// Returns the lowest cell id (at `level - 1`) of all children `cell` at `level`
CellID BeginChildren(LevelID level, CellID cell) const
{
BOOST_ASSERT(level > 1);
auto lidx = LevelIDToIndex(level);
auto offset = level_data->lidx_to_children_offsets[lidx];
return cell_to_children[offset + cell];
}
// Returns the highest cell id (at `level - 1`) of all children `cell` at `level`
CellID EndChildren(LevelID level, CellID cell) const
{
BOOST_ASSERT(level > 1);
auto lidx = LevelIDToIndex(level);
auto offset = level_data->lidx_to_children_offsets[lidx];
return cell_to_children[offset + cell + 1];
}
friend void serialization::read<Ownership>(storage::io::FileReader &reader,
MultiLevelPartitionImpl &mlp);
friend void serialization::write<Ownership>(storage::io::FileWriter &writer,
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 std::make_unique<LevelData>(LevelData{num_level, offsets, masks, bits, {{0}}});
}
inline std::size_t LevelIDToIndex(LevelID l) const { return l - 1; }
// We save the sentinel as last node in the partition information.
// It has the highest cell id in each level so we can derived the range
// of cell ids efficiently.
inline NodeID GetSentinelNode() const { return partition.size() - 1; }
void SetCellID(LevelID l, NodeID node, std::size_t cell_id)
{
auto lidx = LevelIDToIndex(l);
auto shifted_id = cell_id << level_data->lidx_to_offset[lidx];
auto cleared_cell = partition[node] & ~level_data->lidx_to_mask[lidx];
partition[node] = cleared_cell | shifted_id;
}
// If we have N cells per level we need log_2 bits for every cell ID
auto MakeLevelOffsets(const std::vector<std::uint32_t> &lidx_to_num_cells) const
{
std::array<std::uint8_t, MAX_NUM_LEVEL - 1> offsets;
auto lidx = 0UL;
auto sum_bits = 0;
for (auto num_cells : lidx_to_num_cells)
{
// bits needed to number all contained vertexes
auto bits = static_cast<std::uint64_t>(std::ceil(std::log2(num_cells + 1)));
offsets[lidx++] = sum_bits;
sum_bits += bits;
if (sum_bits > 64)
{
throw util::exception(
"Can't pack the partition information at level " + std::to_string(lidx) +
" into a 64bit integer. Would require " + std::to_string(sum_bits) + " bits.");
}
}
// sentinel
offsets[lidx++] = sum_bits;
BOOST_ASSERT(lidx < MAX_NUM_LEVEL);
return offsets;
}
auto MakeLevelMasks(const std::array<std::uint8_t, MAX_NUM_LEVEL - 1> &level_offsets,
std::uint32_t num_level) const
{
std::array<PartitionID, MAX_NUM_LEVEL - 1> masks;
auto lidx = 0UL;
util::for_each_pair(level_offsets.begin(),
level_offsets.begin() + num_level,
[&](const auto offset, const auto next_offset) {
// create mask that has `bits` ones at its LSBs.
// 000011
BOOST_ASSERT(offset < NUM_PARTITION_BITS);
PartitionID mask = (1UL << offset) - 1UL;
// 001111
BOOST_ASSERT(next_offset < NUM_PARTITION_BITS);
PartitionID next_mask = (1UL << next_offset) - 1UL;
// 001100
masks[lidx++] = next_mask ^ mask;
});
return masks;
}
auto MakeBitToLevel(const std::array<std::uint8_t, MAX_NUM_LEVEL - 1> &level_offsets,
std::uint32_t num_level) const
{
std::array<LevelID, NUM_PARTITION_BITS> bit_to_level;
for (auto l = 1u; l < num_level; ++l)
{
auto bits = level_offsets[l - 1];
// set all bits to point to the correct level.
for (auto idx = bits; idx < NUM_PARTITION_BITS; ++idx)
{
bit_to_level[idx] = l;
}
}
return bit_to_level;
}
void InitializePartitionIDs(const std::vector<std::vector<CellID>> &partitions)
{
auto num_nodes = partitions.front().size();
std::vector<NodeID> permutation(num_nodes);
std::iota(permutation.begin(), permutation.end(), 0);
// We include a sentinel element at the end of the partition
partition.resize(num_nodes + 1, 0);
NodeID sentinel = num_nodes;
// Sort nodes bottum-up by cell id.
// This ensures that we get a nice grouping from parent to child cells:
//
// intitial:
// level 0: 0 1 2 3 4 5
// level 1: 2 1 3 4 3 4
// level 2: 2 2 0 1 0 1
//
// first round:
// level 0: 1 0 2 4 3 5
// level 1: 1 2 3 3 4 4 (< sorted)
// level 2: 2 2 0 0 1 1
//
// second round:
// level 0: 2 4 3 5 1 0
// level 1: 3 3 4 4 1 2
// level 2: 0 0 1 1 2 2 (< sorted)
for (const auto &partition : partitions)
{
std::stable_sort(permutation.begin(),
permutation.end(),
[&partition](const auto lhs, const auto rhs) {
return partition[lhs] < partition[rhs];
});
}
// top down assign new cell ids
LevelID level = partitions.size();
for (const auto &partition : boost::adaptors::reverse(partitions))
{
BOOST_ASSERT(permutation.size() > 0);
CellID last_cell_id = partition[permutation.front()];
CellID cell_id = 0;
for (const auto node : permutation)
{
if (last_cell_id != partition[node])
{
cell_id++;
last_cell_id = partition[node];
}
SetCellID(level, node, cell_id);
}
// Store the number of cells of the level in the sentinel
SetCellID(level, sentinel, cell_id + 1);
level--;
}
level_data->lidx_to_children_offsets[0] = 0;
for (auto level_idx = 0UL; level_idx < partitions.size() - 1; ++level_idx)
{
const auto &parent_partition = partitions[level_idx + 1];
level_data->lidx_to_children_offsets[level_idx + 1] = cell_to_children.size();
CellID last_parent_id = parent_partition[permutation.front()];
cell_to_children.push_back(GetCell(level_idx + 1, permutation.front()));
for (const auto node : permutation)
{
if (last_parent_id != parent_partition[node])
{
// Note: we use the new cell id here, not the ones contained
// in the input partition
cell_to_children.push_back(GetCell(level_idx + 1, node));
last_parent_id = parent_partition[node];
}
}
// insert sentinel for the last cell
cell_to_children.push_back(GetCell(level_idx + 1, permutation.back()) + 1);
}
}
LevelDataPtr level_data = {};
Vector<PartitionID> partition;
Vector<CellID> cell_to_children;
};
template <>
inline MultiLevelPartitionImpl<storage::Ownership::Container>::MultiLevelPartitionImpl()
: level_data(std::make_unique<LevelData>())
{
}
template <>
inline MultiLevelPartitionImpl<storage::Ownership::View>::MultiLevelPartitionImpl()
: level_data(nullptr)
{
}
}
}
}
#endif
+162
View File
@@ -0,0 +1,162 @@
#ifndef OSRM_PARTITIONER_GRAPH_HPP_
#define OSRM_PARTITIONER_GRAPH_HPP_
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <vector>
#include "util/typedefs.hpp"
#include <boost/range/iterator_range.hpp>
namespace osrm
{
namespace partitioner
{
// forward declaration to allow finding friends
template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph;
// wrapper for nodes to augment with a tag storing first edge id
template <typename Base> class NodeEntryWrapper : public Base
{
public:
template <typename... Args>
NodeEntryWrapper(std::size_t edges_begin_, std::size_t edges_end_, Args &&... args)
: Base(std::forward<Args>(args)...), edges_begin(edges_begin_), edges_end(edges_end_)
{
}
private:
// only to be modified by the graph itself
std::size_t edges_begin;
std::size_t edges_end;
// give the graph access to the node data wrapper
template <typename NodeEntryT, typename EdgeEntryT> friend class RemappableGraph;
};
using RemappableGraphNode = NodeEntryWrapper<struct zero_base_class>;
template <typename Base> class GraphConstructionWrapper : public Base
{
public:
template <typename... Args>
GraphConstructionWrapper(const NodeID source_, Args &&... args)
: Base(std::forward<Args>(args)...), source(source_)
{
}
NodeID source;
Base Reduce() const { return *this; }
};
template <typename RandomIt> void groupEdgesBySource(RandomIt first, RandomIt last)
{
std::sort(
first, last, [](const auto &lhs, const auto &rhs) { return lhs.source < rhs.source; });
}
template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph
{
public:
using NodeT = NodeEntryT;
using EdgeT = EdgeEntryT;
using NodeIterator = typename std::vector<NodeT>::iterator;
using ConstNodeIterator = typename std::vector<NodeT>::const_iterator;
using EdgeIterator = typename std::vector<EdgeT>::iterator;
using ConstEdgeIterator = typename std::vector<EdgeT>::const_iterator;
// Constructs an empty graph with a given number of nodes.
explicit RemappableGraph(std::vector<NodeT> nodes_, std::vector<EdgeT> edges_)
: nodes(std::move(nodes_)), edges(std::move(edges_))
{
}
unsigned NumberOfNodes() const { return nodes.size(); }
auto &Node(const NodeID nid) { return nodes[nid]; }
auto &Node(const NodeID nid) const { return nodes[nid]; }
auto &Edge(const EdgeID eid) { return edges[eid]; }
auto &Edge(const EdgeID eid) const { return edges[eid]; }
auto Edges(const NodeID nid)
{
return boost::make_iterator_range(edges.begin() + nodes[nid].edges_begin,
edges.begin() + nodes[nid].edges_end);
}
auto Edges(const NodeID nid) const
{
return boost::make_iterator_range(edges.begin() + nodes[nid].edges_begin,
edges.begin() + nodes[nid].edges_end);
}
auto Edges(const NodeT &node)
{
return boost::make_iterator_range(edges.begin() + node.edges_begin,
edges.begin() + node.edges_end);
}
auto Edges(const NodeT &node) const
{
return boost::make_iterator_range(edges.begin() + node.edges_begin,
edges.begin() + node.edges_end);
}
auto BeginEdges(const NodeID nid) const { return edges.begin() + nodes[nid].edges_begin; }
auto EndEdges(const NodeID nid) const { return edges.begin() + nodes[nid].edges_end; }
auto BeginEdges(const NodeT &node) const { return edges.begin() + node.edges_begin; }
auto EndEdges(const NodeT &node) const { return edges.begin() + node.edges_end; }
auto BeginEdges(const NodeT &node) { return edges.begin() + node.edges_begin; }
auto EndEdges(const NodeT &node) { return edges.begin() + node.edges_end; }
EdgeID BeginEdgeID(const NodeID nid) const { return nodes[nid].edges_begin; }
EdgeID EndEdgeID(const NodeID nid) const { return nodes[nid].edges_end; }
// iterate over all nodes
auto Nodes() { return boost::make_iterator_range(nodes.begin(), nodes.end()); }
auto Nodes() const { return boost::make_iterator_range(nodes.begin(), nodes.end()); }
NodeID GetID(const NodeT &node) const
{
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
return (&node - &nodes[0]);
}
EdgeID GetID(const EdgeT &edge) const
{
BOOST_ASSERT(&edge >= &edges[0] && &edge <= &edges.back());
return (&edge - &edges[0]);
}
NodeIterator Begin() { return nodes.begin(); }
NodeIterator End() { return nodes.end(); }
ConstNodeIterator CBegin() const { return nodes.cbegin(); }
ConstNodeIterator CEnd() const { return nodes.cend(); }
// removes the edges from the graph that return true for the filter, returns new end
template <typename FilterT> auto RemoveEdges(NodeT &node, FilterT filter)
{
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
// required since we are not on std++17 yet, otherwise we are missing an argument_type
const auto negate_filter = [&](const EdgeT &edge) { return !filter(edge); };
const auto center = std::stable_partition(BeginEdges(node), EndEdges(node), negate_filter);
const auto remaining_edges = std::distance(BeginEdges(node), center);
node.edges_end = node.edges_begin + remaining_edges;
return center;
}
protected:
std::vector<NodeT> nodes;
std::vector<EdgeT> edges;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_GRAPH_HPP_
+21
View File
@@ -0,0 +1,21 @@
#ifndef OSRM_PARTITIONER_PARTITIONER_HPP_
#define OSRM_PARTITIONER_PARTITIONER_HPP_
#include "partitioner/partitioner_config.hpp"
namespace osrm
{
namespace partitioner
{
// tool access to the recursive partitioner
class Partitioner
{
public:
int Run(const PartitionerConfig &config);
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_PARTITIONER_HPP_
@@ -0,0 +1,45 @@
#ifndef OSRM_PARTITIONER_CONFIG_HPP
#define OSRM_PARTITIONER_CONFIG_HPP
#include <boost/filesystem/path.hpp>
#include <array>
#include <string>
#include "storage/io_config.hpp"
namespace osrm
{
namespace partitioner
{
struct PartitionerConfig final : storage::IOConfig
{
PartitionerConfig()
: IOConfig(
{".osrm", ".osrm.fileIndex", ".osrm.ebg_nodes"},
{".osrm.hsgr", ".osrm.cnbg"},
{".osrm.ebg", ".osrm.cnbg", ".osrm.cnbg_to_ebg", ".osrm.partition", ".osrm.cells"}),
requested_num_threads(0), balance(1.2), boundary_factor(0.25), num_optimizing_cuts(10),
small_component_size(1000),
max_cell_sizes({128, 128 * 32, 128 * 32 * 16, 128 * 32 * 16 * 32})
{
}
void UseDefaultOutputNames(const boost::filesystem::path &base)
{
IOConfig::UseDefaultOutputNames(base);
}
unsigned requested_num_threads;
double balance;
double boundary_factor;
std::size_t num_optimizing_cuts;
std::size_t small_component_size;
std::vector<std::size_t> max_cell_sizes;
};
}
}
#endif // OSRM_PARTITIONER_CONFIG_HPP
@@ -0,0 +1,38 @@
#ifndef OSRM_PARTITIONER_RECURSIVE_BISECTION_HPP_
#define OSRM_PARTITIONER_RECURSIVE_BISECTION_HPP_
#include "partitioner/bisection_graph.hpp"
#include "partitioner/recursive_bisection_state.hpp"
#include "util/typedefs.hpp"
#include <cstddef>
#include <vector>
namespace osrm
{
namespace partitioner
{
class RecursiveBisection
{
public:
RecursiveBisection(BisectionGraph &bisection_graph,
const std::size_t maximum_cell_size,
const double balance,
const double boundary_factor,
const std::size_t num_optimizing_cuts,
const std::size_t small_component_size);
const std::vector<BisectionID> &BisectionIDs() const;
std::uint32_t SCCDepth() const;
private:
BisectionGraph &bisection_graph;
RecursiveBisectionState internal_state;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_RECURSIVE_BISECTION_HPP_
@@ -0,0 +1,56 @@
#ifndef OSRM_PARTITIONER_RECURSIVE_BISECTION_STATE_HPP_
#define OSRM_PARTITIONER_RECURSIVE_BISECTION_STATE_HPP_
#include <cstddef>
#include <cstdint>
#include <vector>
#include "partitioner/bisection_graph.hpp"
#include "partitioner/bisection_graph_view.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
namespace partitioner
{
// Keeps track of the bisection ids, modifies the graph accordingly, splitting it into a left/right
// section with consecutively labelled nodes. Requires a GraphView to look at.
class RecursiveBisectionState
{
public:
// The ID in the partition array
using NodeIterator = BisectionGraph::ConstNodeIterator;
RecursiveBisectionState(BisectionGraph &bisection_graph);
~RecursiveBisectionState();
BisectionID GetBisectionID(const NodeID node) const;
// Bisects the node id array's sub-range based on the partition mask.
// Returns: partition point of the bisection: iterator to the second group's first element.
NodeIterator ApplyBisection(NodeIterator begin,
const NodeIterator end,
const std::size_t depth,
const std::vector<bool> &partition);
// perform an initial pre-partitioning into small components
// on larger graphs, SCCs give perfect cuts (think Amerika vs Europe)
// This function performs an initial pre-partitioning using these sccs.
std::vector<BisectionGraphView> PrePartitionWithSCC(const std::size_t small_component_size);
const std::vector<BisectionID> &BisectionIDs() const;
// return the depth encoded in the SCCs
std::uint32_t SCCDepth() const;
private:
std::uint32_t scc_levels;
BisectionGraph &bisection_graph;
std::vector<BisectionID> bisection_ids;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_RECURSIVE_BISECTION_STATE_HPP_
+115
View File
@@ -0,0 +1,115 @@
#ifndef OSRM_PARTITIONER_REMOVE_UNCONNECTED_HPP
#define OSRM_PARTITIONER_REMOVE_UNCONNECTED_HPP
#include "util/log.hpp"
#include "util/typedefs.hpp"
#include <boost/assert.hpp>
#include <algorithm>
#include <vector>
namespace osrm
{
namespace partitioner
{
using Partition = std::vector<CellID>;
template <typename GraphT>
std::size_t removeUnconnectedBoundaryNodes(const GraphT &edge_based_graph,
std::vector<Partition> &partitions)
{
auto num_unconnected = 0;
auto could_not_fix = 0;
for (int level_index = partitions.size() - 1; level_index >= 0; level_index--)
{
struct Witness
{
NodeID id;
std::size_t induced_border_edges;
};
std::vector<Witness> witnesses;
for (NodeID node = 0; node < edge_based_graph.GetNumberOfNodes(); ++node)
{
witnesses.clear();
bool is_source = false;
bool is_target = false;
const auto cell_id = partitions[level_index][node];
for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
{
const auto data = edge_based_graph.GetEdgeData(edge);
const auto target = edge_based_graph.GetTarget(edge);
const auto target_cell_id = partitions[level_index][target];
if (target_cell_id == cell_id)
{
is_source |= data.forward;
is_target |= data.backward;
}
else
{
witnesses.push_back({target, 0});
}
}
const auto unconnected = witnesses.size() > 0 && !is_source && !is_target;
if (unconnected)
{
num_unconnected++;
if (level_index < static_cast<int>(partitions.size() - 1))
{
auto new_end = std::remove_if(
witnesses.begin(), witnesses.end(), [&](const auto &witness) {
return partitions[level_index + 1][node] !=
partitions[level_index + 1][witness.id];
});
witnesses.resize(new_end - witnesses.begin());
}
if (witnesses.size() == 0)
{
could_not_fix++;
continue;
}
for (auto &witness : witnesses)
{
for (auto edge : edge_based_graph.GetAdjacentEdgeRange(node))
{
auto target = edge_based_graph.GetTarget(edge);
for (auto sublevel_index = level_index; sublevel_index >= 0;
--sublevel_index)
{
if (partitions[sublevel_index][target] !=
partitions[sublevel_index][witness.id])
witness.induced_border_edges++;
}
}
}
auto best_witness = std::min_element(
witnesses.begin(), witnesses.end(), [](const auto &lhs, const auto &rhs) {
return lhs.induced_border_edges < rhs.induced_border_edges;
});
BOOST_ASSERT(best_witness != witnesses.end());
// assign `node` to same subcells as `best_witness`
for (auto sublevel_index = level_index; sublevel_index >= 0; --sublevel_index)
{
partitions[sublevel_index][node] = partitions[sublevel_index][best_witness->id];
}
}
}
}
if (could_not_fix > 0)
util::Log(logWARNING) << "Could not fix " << could_not_fix << " unconnected boundary nodes";
return num_unconnected;
}
}
}
#endif
+79
View File
@@ -0,0 +1,79 @@
#ifndef OSRM_PARTITIONER_RENUMBER_HPP
#define OSRM_PARTITIONER_RENUMBER_HPP
#include "extractor/edge_based_node_segment.hpp"
#include "extractor/nbg_to_ebg.hpp"
#include "extractor/node_data_container.hpp"
#include "partitioner/bisection_to_partition.hpp"
#include "partitioner/edge_based_graph.hpp"
#include "util/dynamic_graph.hpp"
#include "util/static_graph.hpp"
namespace osrm
{
namespace partitioner
{
std::vector<std::uint32_t> makePermutation(const DynamicEdgeBasedGraph &graph,
const std::vector<Partition> &partitions);
template <typename EdgeDataT>
inline void renumber(util::DynamicGraph<EdgeDataT> &graph,
const std::vector<std::uint32_t> &permutation)
{
// dynamic graph has own specilization
graph.Renumber(permutation);
}
template <typename EdgeDataT>
inline void renumber(util::StaticGraph<EdgeDataT> &graph,
const std::vector<std::uint32_t> &permutation)
{
// static graph has own specilization
graph.Renumber(permutation);
}
inline void renumber(extractor::EdgeBasedNodeDataContainer &node_data_container,
const std::vector<std::uint32_t> &permutation)
{
node_data_container.Renumber(permutation);
}
inline void renumber(std::vector<Partition> &partitions,
const std::vector<std::uint32_t> &permutation)
{
for (auto &partition : partitions)
{
util::inplacePermutation(partition.begin(), partition.end(), permutation);
}
}
inline void renumber(util::vector_view<extractor::EdgeBasedNodeSegment> &segments,
const std::vector<std::uint32_t> &permutation)
{
for (auto &segment : segments)
{
BOOST_ASSERT(segment.forward_segment_id.enabled);
segment.forward_segment_id.id = permutation[segment.forward_segment_id.id];
if (segment.reverse_segment_id.enabled)
segment.reverse_segment_id.id = permutation[segment.reverse_segment_id.id];
}
}
inline void renumber(std::vector<extractor::NBGToEBG> &mapping,
const std::vector<std::uint32_t> &permutation)
{
for (extractor::NBGToEBG &m : mapping)
{
if (m.backward_ebg_node != SPECIAL_NODEID)
m.backward_ebg_node = permutation[m.backward_ebg_node];
if (m.forward_ebg_node != SPECIAL_NODEID)
m.forward_ebg_node = permutation[m.forward_ebg_node];
}
}
} // namespace partitioner
} // namespace osrm
#endif
@@ -0,0 +1,55 @@
#ifndef OSRM_PARTITIONER_REORDER_FIRST_LAST_HPP
#define OSRM_PARTITIONER_REORDER_FIRST_LAST_HPP
#include <boost/assert.hpp>
#include <algorithm>
#include <cstddef>
#include <iterator>
namespace osrm
{
namespace partitioner
{
// Reorders the first n elements in the range to satisfy the comparator,
// and the last n elements to satisfy the comparator with arguments flipped.
// Note: no guarantees to the element's ordering inside the reordered ranges.
template <typename RandomIt, typename Comparator>
void reorderFirstLast(RandomIt first, RandomIt last, std::size_t n, Comparator comp)
{
BOOST_ASSERT_MSG(n <= (last - first) / std::size_t{2}, "overlapping subranges not allowed");
if (n == 0 || (last - first < 2))
return;
// Reorder first n: guarantees that the predicate holds for the first elements.
std::nth_element(first, first + (n - 1), last, comp);
// Reorder last n: guarantees that the flipped predicate holds for the last k elements.
// We reorder from the end backwards up to the end of the already reordered range.
// We can not use std::not2, since then e.g. std::less<> would lose its irreflexive
// requirements.
std::reverse_iterator<RandomIt> rfirst{last}, rlast{first + n};
const auto flipped = [](auto fn) {
return [fn](auto &&lhs, auto &&rhs) {
return fn(std::forward<decltype(lhs)>(rhs), std::forward<decltype(rhs)>(lhs));
};
};
std::nth_element(rfirst, rfirst + (n - 1), rlast, flipped(comp));
}
template <typename RandomAccessRange, typename Compare>
void reorderFirstLast(RandomAccessRange &rng, std::size_t n, Compare comp)
{
using std::begin;
using std::end;
return reorderFirstLast(begin(rng), end(rng), n, comp);
}
} // ns partition
} // ns osrm
#endif
+76
View File
@@ -0,0 +1,76 @@
#ifndef OSRM_PARTITIONER_SERIALIZATION_HPP
#define OSRM_PARTITIONER_SERIALIZATION_HPP
#include "partitioner/cell_storage.hpp"
#include "partitioner/edge_based_graph.hpp"
#include "partitioner/multi_level_graph.hpp"
#include "partitioner/multi_level_partition.hpp"
#include "storage/io.hpp"
#include "storage/serialization.hpp"
#include "storage/shared_memory_ownership.hpp"
namespace osrm
{
namespace partitioner
{
namespace serialization
{
template <typename EdgeDataT, storage::Ownership Ownership>
inline void read(storage::io::FileReader &reader, MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::read(reader, graph.node_array);
storage::serialization::read(reader, graph.edge_array);
storage::serialization::read(reader, graph.node_to_edge_offset);
}
template <typename EdgeDataT, storage::Ownership Ownership>
inline void write(storage::io::FileWriter &writer,
const MultiLevelGraph<EdgeDataT, Ownership> &graph)
{
storage::serialization::write(writer, graph.node_array);
storage::serialization::write(writer, graph.edge_array);
storage::serialization::write(writer, graph.node_to_edge_offset);
}
template <storage::Ownership Ownership>
inline void read(storage::io::FileReader &reader, detail::MultiLevelPartitionImpl<Ownership> &mlp)
{
reader.ReadInto(*mlp.level_data);
storage::serialization::read(reader, mlp.partition);
storage::serialization::read(reader, mlp.cell_to_children);
}
template <storage::Ownership Ownership>
inline void write(storage::io::FileWriter &writer,
const detail::MultiLevelPartitionImpl<Ownership> &mlp)
{
writer.WriteOne(*mlp.level_data);
storage::serialization::write(writer, mlp.partition);
storage::serialization::write(writer, mlp.cell_to_children);
}
template <storage::Ownership Ownership>
inline void read(storage::io::FileReader &reader, detail::CellStorageImpl<Ownership> &storage)
{
storage::serialization::read(reader, storage.source_boundary);
storage::serialization::read(reader, storage.destination_boundary);
storage::serialization::read(reader, storage.cells);
storage::serialization::read(reader, storage.level_to_cell_offset);
}
template <storage::Ownership Ownership>
inline void write(storage::io::FileWriter &writer,
const detail::CellStorageImpl<Ownership> &storage)
{
storage::serialization::write(writer, storage.source_boundary);
storage::serialization::write(writer, storage.destination_boundary);
storage::serialization::write(writer, storage.cells);
storage::serialization::write(writer, storage.level_to_cell_offset);
}
}
}
}
#endif
@@ -0,0 +1,29 @@
#ifndef OSRM_PARTITIONER_TARJAN_GRAPH_WRAPPER_HPP_
#define OSRM_PARTITIONER_TARJAN_GRAPH_WRAPPER_HPP_
#include "partitioner/bisection_graph.hpp"
#include "util/integer_range.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
namespace partitioner
{
class TarjanGraphWrapper
{
public:
TarjanGraphWrapper(const BisectionGraph &bisection_graph);
std::size_t GetNumberOfNodes() const;
util::range<EdgeID> GetAdjacentEdgeRange(const NodeID nid) const;
NodeID GetTarget(const EdgeID eid) const;
protected:
const BisectionGraph &bisection_graph;
};
} // namespace partitioner
} // namespace osrm
#endif // OSRM_PARTITIONER_TARJAN_GRAPH_WRAPPER_HPP_