328 lines
11 KiB
C++
328 lines
11 KiB
C++
#ifndef STATIC_GRAPH_HPP
|
|
#define STATIC_GRAPH_HPP
|
|
|
|
#include "util/graph_traits.hpp"
|
|
#include "util/integer_range.hpp"
|
|
#include "util/percent.hpp"
|
|
#include "util/permutation.hpp"
|
|
#include "util/typedefs.hpp"
|
|
#include "util/vector_view.hpp"
|
|
|
|
#include "storage/shared_memory_ownership.hpp"
|
|
#include "storage/tar_fwd.hpp"
|
|
|
|
#include <boost/assert.hpp>
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
namespace osrm::util
|
|
{
|
|
template <typename EdgeDataT, storage::Ownership Ownership> class StaticGraph;
|
|
|
|
namespace serialization
|
|
{
|
|
template <typename EdgeDataT, storage::Ownership Ownership>
|
|
void read(storage::tar::FileReader &reader,
|
|
const std::string &name,
|
|
StaticGraph<EdgeDataT, Ownership> &graph);
|
|
|
|
template <typename EdgeDataT, storage::Ownership Ownership>
|
|
void write(storage::tar::FileWriter &writer,
|
|
const std::string &name,
|
|
const StaticGraph<EdgeDataT, Ownership> &graph);
|
|
} // namespace serialization
|
|
|
|
namespace static_graph_details
|
|
{
|
|
|
|
using NodeIterator = NodeID;
|
|
using EdgeIterator = NodeID;
|
|
|
|
struct NodeArrayEntry
|
|
{
|
|
// index of the first edge
|
|
EdgeIterator first_edge;
|
|
};
|
|
|
|
template <typename EdgeDataT> struct EdgeArrayEntry;
|
|
|
|
template <typename EdgeDataT> struct EdgeArrayEntry
|
|
{
|
|
NodeID target;
|
|
EdgeDataT data;
|
|
};
|
|
|
|
template <> struct EdgeArrayEntry<void>
|
|
{
|
|
NodeID target;
|
|
};
|
|
|
|
template <typename EdgeDataT> struct SortableEdgeWithData;
|
|
|
|
template <> struct SortableEdgeWithData<void>
|
|
{
|
|
NodeIterator source;
|
|
NodeIterator target;
|
|
|
|
SortableEdgeWithData() = default;
|
|
|
|
SortableEdgeWithData(NodeIterator source, NodeIterator target) : source(source), target(target)
|
|
{
|
|
}
|
|
|
|
bool operator<(const SortableEdgeWithData &right) const
|
|
{
|
|
return std::tie(source, target) < std::tie(right.source, right.target);
|
|
}
|
|
|
|
bool operator==(const SortableEdgeWithData &right) const
|
|
{
|
|
return std::tie(source, target) == std::tie(right.source, right.target);
|
|
}
|
|
};
|
|
|
|
template <typename EdgeDataT> struct SortableEdgeWithData : SortableEdgeWithData<void>
|
|
{
|
|
using Base = SortableEdgeWithData<void>;
|
|
|
|
EdgeDataT data;
|
|
|
|
SortableEdgeWithData() = default;
|
|
|
|
template <typename... Ts>
|
|
SortableEdgeWithData(NodeIterator source, NodeIterator target, Ts &&...data)
|
|
: Base{source, target}, data{std::forward<Ts>(data)...}
|
|
{
|
|
}
|
|
};
|
|
|
|
template <typename EntryT, typename OtherEdge>
|
|
EntryT edgeToEntry(const OtherEdge &from, std::true_type)
|
|
{
|
|
return EntryT{from.target, from.data};
|
|
}
|
|
template <typename EntryT, typename OtherEdge>
|
|
EntryT edgeToEntry(const OtherEdge &from, std::false_type)
|
|
{
|
|
return EntryT{from.target};
|
|
}
|
|
|
|
} // namespace static_graph_details
|
|
|
|
template <typename EdgeDataT, storage::Ownership Ownership = storage::Ownership::Container>
|
|
class StaticGraph
|
|
{
|
|
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
|
|
|
public:
|
|
using InputEdge = static_graph_details::SortableEdgeWithData<EdgeDataT>;
|
|
using NodeIterator = static_graph_details::NodeIterator;
|
|
using EdgeIterator = static_graph_details::EdgeIterator;
|
|
using EdgeRange = range<EdgeIterator>;
|
|
using NodeArrayEntry = static_graph_details::NodeArrayEntry;
|
|
using EdgeArrayEntry = static_graph_details::EdgeArrayEntry<EdgeDataT>;
|
|
|
|
EdgeRange GetAdjacentEdgeRange(const NodeID node) const
|
|
{
|
|
return irange(BeginEdges(node), EndEdges(node));
|
|
}
|
|
|
|
StaticGraph() {}
|
|
|
|
template <typename ContainerT> StaticGraph(const std::uint32_t nodes, const ContainerT &edges)
|
|
{
|
|
BOOST_ASSERT(std::is_sorted(const_cast<ContainerT &>(edges).begin(),
|
|
const_cast<ContainerT &>(edges).end()));
|
|
|
|
InitializeFromSortedEdgeRange(nodes, edges.begin(), edges.end());
|
|
}
|
|
|
|
StaticGraph(Vector<NodeArrayEntry> node_array_, Vector<EdgeArrayEntry> edge_array_)
|
|
: node_array(std::move(node_array_)), edge_array(std::move(edge_array_))
|
|
{
|
|
BOOST_ASSERT(!node_array.empty());
|
|
|
|
number_of_nodes = static_cast<decltype(number_of_nodes)>(node_array.size() - 1);
|
|
number_of_edges = static_cast<decltype(number_of_edges)>(node_array.back().first_edge);
|
|
BOOST_ASSERT(number_of_edges <= edge_array.size());
|
|
BOOST_ASSERT(number_of_nodes == node_array.size() - 1);
|
|
}
|
|
|
|
unsigned GetNumberOfNodes() const { return number_of_nodes; }
|
|
|
|
unsigned GetNumberOfEdges() const { return number_of_edges; }
|
|
|
|
unsigned GetOutDegree(const NodeIterator n) const { return EndEdges(n) - BeginEdges(n); }
|
|
|
|
inline NodeIterator GetTarget(const EdgeIterator e) const
|
|
{
|
|
return NodeIterator(edge_array[e].target);
|
|
}
|
|
|
|
auto &GetEdgeData(const EdgeIterator e) { return edge_array[e].data; }
|
|
|
|
const auto &GetEdgeData(const EdgeIterator e) const { return edge_array[e].data; }
|
|
|
|
EdgeIterator BeginEdges(const NodeIterator n) const
|
|
{
|
|
return EdgeIterator(node_array.at(n).first_edge);
|
|
}
|
|
|
|
EdgeIterator EndEdges(const NodeIterator n) const
|
|
{
|
|
return EdgeIterator(node_array.at(n + 1).first_edge);
|
|
}
|
|
|
|
// searches for a specific edge
|
|
EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const
|
|
{
|
|
for (const auto i : irange(BeginEdges(from), EndEdges(from)))
|
|
{
|
|
if (to == edge_array[i].target)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return SPECIAL_EDGEID;
|
|
}
|
|
|
|
/**
|
|
* Finds the edge with the smallest `.weight` going from `from` to `to`
|
|
* @param from the source node ID
|
|
* @param to the target node ID
|
|
* @param filter a functor that returns a `bool` that determines whether an edge should be
|
|
* tested or not.
|
|
* Takes `EdgeData` as a parameter.
|
|
* @return the ID of the smallest edge if any were found that satisfied *filter*, or
|
|
* `SPECIAL_EDGEID` if no
|
|
* matching edge is found.
|
|
*/
|
|
template <typename FilterFunction>
|
|
EdgeIterator
|
|
FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const
|
|
{
|
|
static_assert(traits::HasDataMember<EdgeArrayEntry>::value,
|
|
"Filtering on .data not possible without .data member attribute");
|
|
|
|
EdgeIterator smallest_edge = SPECIAL_EDGEID;
|
|
EdgeWeight smallest_weight = INVALID_EDGE_WEIGHT;
|
|
for (auto edge : GetAdjacentEdgeRange(from))
|
|
{
|
|
const NodeID target = GetTarget(edge);
|
|
const auto &data = GetEdgeData(edge);
|
|
if (target == to && data.weight < smallest_weight &&
|
|
std::forward<FilterFunction>(filter)(data))
|
|
{
|
|
smallest_edge = edge;
|
|
smallest_weight = data.weight;
|
|
}
|
|
}
|
|
return smallest_edge;
|
|
}
|
|
|
|
EdgeIterator FindEdgeInEitherDirection(const NodeIterator from, const NodeIterator to) const
|
|
{
|
|
EdgeIterator tmp = FindEdge(from, to);
|
|
return (SPECIAL_NODEID != tmp ? tmp : FindEdge(to, from));
|
|
}
|
|
|
|
EdgeIterator
|
|
FindEdgeIndicateIfReverse(const NodeIterator from, const NodeIterator to, bool &result) const
|
|
{
|
|
EdgeIterator current_iterator = FindEdge(from, to);
|
|
if (SPECIAL_NODEID == current_iterator)
|
|
{
|
|
current_iterator = FindEdge(to, from);
|
|
if (SPECIAL_NODEID != current_iterator)
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
return current_iterator;
|
|
}
|
|
|
|
void Renumber(const std::vector<NodeID> &old_to_new_node)
|
|
{
|
|
std::vector<NodeID> new_to_old_node(number_of_nodes);
|
|
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
|
new_to_old_node[old_to_new_node[node]] = node;
|
|
|
|
Vector<NodeArrayEntry> new_node_array(node_array.size());
|
|
|
|
// Build up edge permutation
|
|
auto new_edge_index = 0;
|
|
std::vector<EdgeID> old_to_new_edge(edge_array.size(), SPECIAL_EDGEID);
|
|
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
|
{
|
|
auto new_first_edge = new_edge_index;
|
|
for (auto edge : GetAdjacentEdgeRange(new_to_old_node[node]))
|
|
{
|
|
edge_array[edge].target = old_to_new_node[edge_array[edge].target];
|
|
old_to_new_edge[edge] = new_edge_index++;
|
|
}
|
|
new_node_array[node].first_edge = new_first_edge;
|
|
}
|
|
new_node_array.back().first_edge = new_edge_index;
|
|
node_array = std::move(new_node_array);
|
|
BOOST_ASSERT(std::find(old_to_new_edge.begin(), old_to_new_edge.end(), SPECIAL_EDGEID) ==
|
|
old_to_new_edge.end());
|
|
|
|
util::inplacePermutation(edge_array.begin(), edge_array.end(), old_to_new_edge);
|
|
}
|
|
|
|
friend void serialization::read<EdgeDataT, Ownership>(storage::tar::FileReader &reader,
|
|
const std::string &name,
|
|
StaticGraph<EdgeDataT, Ownership> &graph);
|
|
friend void
|
|
serialization::write<EdgeDataT, Ownership>(storage::tar::FileWriter &writer,
|
|
const std::string &name,
|
|
const StaticGraph<EdgeDataT, Ownership> &graph);
|
|
|
|
protected:
|
|
template <typename IterT>
|
|
void InitializeFromSortedEdgeRange(const std::uint32_t nodes, IterT begin, IterT end)
|
|
{
|
|
number_of_nodes = nodes;
|
|
number_of_edges = static_cast<EdgeIterator>(std::distance(begin, end));
|
|
node_array.reserve(number_of_nodes + 1);
|
|
node_array.push_back(NodeArrayEntry{0u});
|
|
auto iter = begin;
|
|
for (auto node : util::irange(0u, nodes))
|
|
{
|
|
iter =
|
|
std::find_if(iter, end, [node](const auto &edge) { return edge.source != node; });
|
|
unsigned offset = std::distance(begin, iter);
|
|
node_array.push_back(NodeArrayEntry{offset});
|
|
}
|
|
BOOST_ASSERT_MSG(
|
|
iter == end,
|
|
("Still " + std::to_string(std::distance(iter, end)) + " edges left.").c_str());
|
|
BOOST_ASSERT(node_array.size() == number_of_nodes + 1);
|
|
|
|
edge_array.resize(number_of_edges);
|
|
std::transform(begin,
|
|
end,
|
|
edge_array.begin(),
|
|
[](const auto &from)
|
|
{
|
|
return static_graph_details::edgeToEntry<EdgeArrayEntry>(
|
|
from, traits::HasDataMember<EdgeArrayEntry>{});
|
|
});
|
|
}
|
|
|
|
protected:
|
|
NodeIterator number_of_nodes = 0;
|
|
EdgeIterator number_of_edges = 0;
|
|
|
|
Vector<NodeArrayEntry> node_array;
|
|
Vector<EdgeArrayEntry> edge_array;
|
|
};
|
|
|
|
} // namespace osrm::util
|
|
|
|
#endif // STATIC_GRAPH_HPP
|