Implement exclude flags on CH using shared core
The core is fully contracted for each exclude flag and stored in a merged graph data structure.
This commit is contained in:
committed by
Patrick Niklaus
parent
4b75cb8b0e
commit
61c430c098
@@ -0,0 +1,87 @@
|
||||
#ifndef OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP
|
||||
#define OSRM_CONTRACTOR_CONTRACT_EXCLUDABLE_GRAPH_HPP
|
||||
|
||||
#include "contractor/contracted_edge_container.hpp"
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
#include "contractor/graph_contractor_adaptors.hpp"
|
||||
#include "contractor/query_graph.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
using GraphFilterAndCore =
|
||||
std::tuple<QueryGraph, std::vector<std::vector<bool>>, std::vector<std::vector<bool>>>;
|
||||
|
||||
inline auto contractExcludableGraph(ContractorGraph contractor_graph_,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
const std::vector<std::vector<bool>> &filters,
|
||||
const float core_factor = 1.0)
|
||||
{
|
||||
auto num_nodes = contractor_graph_.GetNumberOfNodes();
|
||||
ContractedEdgeContainer edge_container;
|
||||
ContractorGraph shared_core_graph;
|
||||
std::vector<bool> is_shared_core;
|
||||
{
|
||||
ContractorGraph contractor_graph = std::move(contractor_graph_);
|
||||
std::vector<bool> always_allowed(num_nodes, true);
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
for (const auto node : util::irange<NodeID>(0, num_nodes))
|
||||
{
|
||||
always_allowed[node] = always_allowed[node] && filter[node];
|
||||
}
|
||||
}
|
||||
|
||||
// By not contracting all contractable nodes we avoid creating
|
||||
// a very dense core. This increases the overall graph sizes a little bit
|
||||
// but increases the final CH quality and contraction speed.
|
||||
constexpr float BASE_CORE = 0.9;
|
||||
is_shared_core = contractGraph(contractor_graph,
|
||||
std::move(always_allowed),
|
||||
node_weights,
|
||||
std::min<float>(BASE_CORE, core_factor));
|
||||
|
||||
// Add all non-core edges to container
|
||||
{
|
||||
auto non_core_edges = toEdges<QueryEdge>(contractor_graph);
|
||||
auto new_end =
|
||||
std::remove_if(non_core_edges.begin(), non_core_edges.end(), [&](const auto &edge) {
|
||||
return is_shared_core[edge.source] && is_shared_core[edge.target];
|
||||
});
|
||||
non_core_edges.resize(new_end - non_core_edges.begin());
|
||||
edge_container.Insert(std::move(non_core_edges));
|
||||
}
|
||||
|
||||
// Extract core graph for further contraction
|
||||
shared_core_graph = contractor_graph.Filter(
|
||||
[&is_shared_core](const NodeID node) { return is_shared_core[node]; });
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>> cores;
|
||||
for (const auto &filter : filters)
|
||||
{
|
||||
auto filtered_core_graph =
|
||||
shared_core_graph.Filter([&filter](const NodeID node) { return filter[node]; });
|
||||
|
||||
auto core = contractGraph(
|
||||
filtered_core_graph, is_shared_core, is_shared_core, node_weights, core_factor);
|
||||
if (core_factor == 1.0)
|
||||
{
|
||||
core.clear();
|
||||
}
|
||||
cores.push_back(std::move(core));
|
||||
|
||||
edge_container.Merge(toEdges<QueryEdge>(std::move(filtered_core_graph)));
|
||||
}
|
||||
|
||||
return GraphFilterAndCore{QueryGraph{num_nodes, std::move(edge_container.edges)},
|
||||
edge_container.MakeEdgeFilters(),
|
||||
std::move(cores)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,153 @@
|
||||
#ifndef OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP
|
||||
#define OSRM_CONTRACTOR_CONTRACTED_EDGE_CONTAINER_HPP
|
||||
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
struct ContractedEdgeContainer
|
||||
{
|
||||
private:
|
||||
using MergedFlags = std::uint8_t;
|
||||
static constexpr auto ALL_FLAGS = 0xFF;
|
||||
|
||||
static bool mergeCompare(const QueryEdge &lhs, const QueryEdge &rhs)
|
||||
{
|
||||
return std::tie(lhs.source,
|
||||
lhs.target,
|
||||
lhs.data.shortcut,
|
||||
lhs.data.turn_id,
|
||||
lhs.data.weight,
|
||||
lhs.data.duration,
|
||||
lhs.data.forward,
|
||||
lhs.data.backward) < std::tie(rhs.source,
|
||||
rhs.target,
|
||||
rhs.data.shortcut,
|
||||
rhs.data.turn_id,
|
||||
rhs.data.weight,
|
||||
rhs.data.duration,
|
||||
rhs.data.forward,
|
||||
rhs.data.backward);
|
||||
}
|
||||
|
||||
static bool mergable(const QueryEdge &lhs, const QueryEdge &rhs)
|
||||
{
|
||||
// only true if both are equal
|
||||
return !mergeCompare(lhs, rhs) && !mergeCompare(rhs, lhs);
|
||||
}
|
||||
|
||||
public:
|
||||
void Insert(util::DeallocatingVector<QueryEdge> new_edges)
|
||||
{
|
||||
BOOST_ASSERT(edges.size() == 0);
|
||||
BOOST_ASSERT(flags.empty());
|
||||
|
||||
edges = std::move(new_edges);
|
||||
flags.resize(edges.size(), ALL_FLAGS);
|
||||
}
|
||||
|
||||
void Merge(util::DeallocatingVector<QueryEdge> new_edges)
|
||||
{
|
||||
BOOST_ASSERT(index < sizeof(MergedFlags) * CHAR_BIT);
|
||||
|
||||
const MergedFlags flag = 1 << index++;
|
||||
|
||||
std::vector<MergedFlags> merged_flags;
|
||||
merged_flags.reserve(flags.size() * 1.1);
|
||||
util::DeallocatingVector<QueryEdge> merged_edges;
|
||||
merged_edges.reserve(edges.size() * 1.1);
|
||||
|
||||
auto flags_iter = flags.begin();
|
||||
// destructive iterators, this is single-pass only
|
||||
// FIXME using dbegin() dend() will result in segfaults.
|
||||
auto edges_iter = edges.dbegin();
|
||||
auto edges_end = edges.dend();
|
||||
auto new_edges_iter = new_edges.dbegin();
|
||||
auto new_edges_end = new_edges.dend();
|
||||
|
||||
while (edges_iter != edges_end && new_edges_iter != new_edges_end)
|
||||
{
|
||||
while (edges_iter != edges_end && mergeCompare(*edges_iter, *new_edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*edges_iter);
|
||||
merged_flags.push_back(*flags_iter);
|
||||
edges_iter++;
|
||||
flags_iter++;
|
||||
}
|
||||
|
||||
if (edges_iter == edges_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (new_edges_iter != new_edges_end && mergeCompare(*new_edges_iter, *edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*new_edges_iter);
|
||||
merged_flags.push_back(flag);
|
||||
new_edges_iter++;
|
||||
}
|
||||
|
||||
if (new_edges_iter == new_edges_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
while (edges_iter != edges_end && new_edges_iter != new_edges_end &&
|
||||
mergable(*edges_iter, *new_edges_iter))
|
||||
{
|
||||
merged_edges.push_back(*edges_iter);
|
||||
merged_flags.push_back(*flags_iter | flag);
|
||||
|
||||
edges_iter++;
|
||||
flags_iter++;
|
||||
new_edges_iter++;
|
||||
}
|
||||
}
|
||||
|
||||
while (edges_iter != edges_end)
|
||||
{
|
||||
BOOST_ASSERT(new_edges_iter == new_edges_end);
|
||||
merged_edges.push_back(*edges_iter++);
|
||||
merged_flags.push_back(*flags_iter++);
|
||||
}
|
||||
|
||||
while (new_edges_iter != new_edges_end)
|
||||
{
|
||||
BOOST_ASSERT(edges_iter == edges_end);
|
||||
merged_edges.push_back(*new_edges_iter++);
|
||||
merged_flags.push_back(flag);
|
||||
}
|
||||
|
||||
flags = std::move(merged_flags);
|
||||
edges = std::move(merged_edges);
|
||||
}
|
||||
|
||||
auto MakeEdgeFilters() const
|
||||
{
|
||||
std::vector<std::vector<bool>> filters(index);
|
||||
for (const auto flag_index : util::irange<std::size_t>(0, index))
|
||||
{
|
||||
MergedFlags mask = 1 << flag_index;
|
||||
for (const auto flag : flags)
|
||||
{
|
||||
filters[flag_index].push_back(flag & mask);
|
||||
}
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
std::size_t index = 0;
|
||||
std::vector<MergedFlags> flags;
|
||||
util::DeallocatingVector<QueryEdge> edges;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -43,12 +43,9 @@ namespace contractor
|
||||
struct ContractorConfig final : storage::IOConfig
|
||||
{
|
||||
ContractorConfig()
|
||||
: IOConfig(
|
||||
{
|
||||
".osrm.ebg",
|
||||
},
|
||||
{},
|
||||
{".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}),
|
||||
: IOConfig({".osrm.ebg", ".osrm.ebg_nodes", ".osrm.properties"},
|
||||
{},
|
||||
{".osrm.level", ".osrm.core", ".osrm.hsgr", ".osrm.enw"}),
|
||||
requested_num_threads(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
#ifndef OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
#define OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
// allow access to the heap itself, add Dijkstra functionality on top
|
||||
class ContractorDijkstra
|
||||
{
|
||||
public:
|
||||
ContractorDijkstra(std::size_t heap_size);
|
||||
|
||||
// search the graph up
|
||||
void Run(const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const int weight_limit,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph);
|
||||
|
||||
// adaption of the heap interface
|
||||
void Clear();
|
||||
bool WasInserted(const NodeID node) const;
|
||||
void Insert(const NodeID node,
|
||||
const ContractorHeap::WeightType weight,
|
||||
const ContractorHeap::DataType &data);
|
||||
|
||||
// cannot be const due to node-hash access in the binary heap :(
|
||||
ContractorHeap::WeightType GetKey(const NodeID node);
|
||||
|
||||
private:
|
||||
void RelaxNode(const NodeID node,
|
||||
const int node_weight,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph);
|
||||
|
||||
ContractorHeap heap;
|
||||
};
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef OSRM_CONTRACTOR_SEARCH_HPP
|
||||
#define OSRM_CONTRACTOR_SEARCH_HPP
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
void search(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node);
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
#endif // OSRM_CONTRACTOR_DIJKSTRA_HPP
|
||||
@@ -16,54 +16,85 @@ namespace files
|
||||
{
|
||||
// reads .osrm.core
|
||||
template <typename CoreVectorT>
|
||||
void readCoreMarker(const boost::filesystem::path &path, CoreVectorT &is_core_node)
|
||||
void readCoreMarker(const boost::filesystem::path &path, std::vector<CoreVectorT> &cores)
|
||||
{
|
||||
static_assert(util::is_view_or_vector<bool, CoreVectorT>::value,
|
||||
"is_core_node must be a vector");
|
||||
"cores must be a vector of boolean vectors");
|
||||
storage::io::FileReader reader(path, storage::io::FileReader::VerifyFingerprint);
|
||||
|
||||
storage::serialization::read(reader, is_core_node);
|
||||
auto num_cores = reader.ReadElementCount64();
|
||||
cores.resize(num_cores);
|
||||
for (const auto index : util::irange<std::size_t>(0, num_cores))
|
||||
{
|
||||
storage::serialization::read(reader, cores[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// writes .osrm.core
|
||||
template <typename CoreVectorT>
|
||||
void writeCoreMarker(const boost::filesystem::path &path, const CoreVectorT &is_core_node)
|
||||
void writeCoreMarker(const boost::filesystem::path &path, const std::vector<CoreVectorT> &cores)
|
||||
{
|
||||
static_assert(util::is_view_or_vector<bool, CoreVectorT>::value,
|
||||
"is_core_node must be a vector");
|
||||
"cores must be a vector of boolean vectors");
|
||||
storage::io::FileWriter writer(path, storage::io::FileWriter::GenerateFingerprint);
|
||||
|
||||
storage::serialization::write(writer, is_core_node);
|
||||
writer.WriteElementCount64(cores.size());
|
||||
for (const auto &core : cores)
|
||||
{
|
||||
storage::serialization::write(writer, core);
|
||||
}
|
||||
}
|
||||
|
||||
// reads .osrm.hsgr file
|
||||
template <typename QueryGraphT>
|
||||
inline void readGraph(const boost::filesystem::path &path, unsigned &checksum, QueryGraphT &graph)
|
||||
template <typename QueryGraphT, typename EdgeFilterT>
|
||||
inline void readGraph(const boost::filesystem::path &path,
|
||||
unsigned &checksum,
|
||||
QueryGraphT &graph,
|
||||
std::vector<EdgeFilterT> &edge_filter)
|
||||
{
|
||||
static_assert(std::is_same<QueryGraphView, QueryGraphT>::value ||
|
||||
std::is_same<QueryGraph, QueryGraphT>::value,
|
||||
"graph must be of type QueryGraph<>");
|
||||
static_assert(std::is_same<EdgeFilterT, std::vector<bool>>::value ||
|
||||
std::is_same<EdgeFilterT, util::vector_view<bool>>::value,
|
||||
"edge_filter must be a container of vector<bool> or vector_view<bool>");
|
||||
|
||||
const auto fingerprint = storage::io::FileReader::VerifyFingerprint;
|
||||
storage::io::FileReader reader{path, fingerprint};
|
||||
|
||||
reader.ReadInto(checksum);
|
||||
util::serialization::read(reader, graph);
|
||||
auto count = reader.ReadElementCount64();
|
||||
edge_filter.resize(count);
|
||||
for (const auto index : util::irange<std::size_t>(0, count))
|
||||
{
|
||||
storage::serialization::read(reader, edge_filter[index]);
|
||||
}
|
||||
}
|
||||
|
||||
// writes .osrm.hsgr file
|
||||
template <typename QueryGraphT>
|
||||
inline void
|
||||
writeGraph(const boost::filesystem::path &path, unsigned checksum, const QueryGraphT &graph)
|
||||
template <typename QueryGraphT, typename EdgeFilterT>
|
||||
inline void writeGraph(const boost::filesystem::path &path,
|
||||
unsigned checksum,
|
||||
const QueryGraphT &graph,
|
||||
const std::vector<EdgeFilterT> &edge_filter)
|
||||
{
|
||||
static_assert(std::is_same<QueryGraphView, QueryGraphT>::value ||
|
||||
std::is_same<QueryGraph, QueryGraphT>::value,
|
||||
"graph must be of type QueryGraph<>");
|
||||
static_assert(std::is_same<EdgeFilterT, std::vector<bool>>::value ||
|
||||
std::is_same<EdgeFilterT, util::vector_view<bool>>::value,
|
||||
"edge_filter must be a container of vector<bool> or vector_view<bool>");
|
||||
const auto fingerprint = storage::io::FileWriter::GenerateFingerprint;
|
||||
storage::io::FileWriter writer{path, fingerprint};
|
||||
|
||||
writer.WriteOne(checksum);
|
||||
util::serialization::write(writer, graph);
|
||||
writer.WriteElementCount64(edge_filter.size());
|
||||
for (const auto &filter : edge_filter)
|
||||
{
|
||||
storage::serialization::write(writer, filter);
|
||||
}
|
||||
}
|
||||
|
||||
// reads .levels file
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
|
||||
#include "util/filtered_graph.hpp"
|
||||
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
@@ -11,21 +13,29 @@ namespace osrm
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
using LevelAndCore = std::tuple<std::vector<float>, std::vector<bool>>;
|
||||
std::vector<bool> contractGraph(ContractorGraph &graph,
|
||||
std::vector<bool> node_is_uncontracted,
|
||||
std::vector<bool> node_is_contractable,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0);
|
||||
|
||||
LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
std::vector<float> cached_node_levels,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0);
|
||||
|
||||
// Overload for contracting withcout cache
|
||||
inline LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0)
|
||||
// Overload for contracting all nodes
|
||||
inline auto contractGraph(ContractorGraph &graph,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0)
|
||||
{
|
||||
return contractGraph(graph, {}, std::move(node_weights), core_factor);
|
||||
return contractGraph(graph, {}, {}, std::move(node_weights), core_factor);
|
||||
}
|
||||
|
||||
// Overload no contracted nodes
|
||||
inline auto contractGraph(ContractorGraph &graph,
|
||||
std::vector<bool> node_is_contractable,
|
||||
std::vector<EdgeWeight> node_weights,
|
||||
double core_factor = 1.0)
|
||||
{
|
||||
return contractGraph(
|
||||
graph, {}, std::move(node_is_contractable), std::move(node_weights), core_factor);
|
||||
}
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
||||
|
||||
@@ -23,7 +23,8 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
||||
|
||||
for (const auto &input_edge : input_edge_list)
|
||||
{
|
||||
BOOST_ASSERT(input_edge.data.weight < INVALID_EDGE_WEIGHT);
|
||||
if (input_edge.data.weight == INVALID_EDGE_WEIGHT)
|
||||
continue;
|
||||
|
||||
#ifndef NDEBUG
|
||||
const unsigned int constexpr DAY_IN_DECI_SECONDS = 24 * 60 * 60 * 10;
|
||||
@@ -124,7 +125,7 @@ ContractorGraph toContractorGraph(NodeID number_of_nodes, InputEdgeContainer inp
|
||||
return ContractorGraph{number_of_nodes, edges};
|
||||
}
|
||||
|
||||
template <class Edge> inline util::DeallocatingVector<Edge> toEdges(ContractorGraph graph)
|
||||
template <class Edge, typename GraphT> inline util::DeallocatingVector<Edge> toEdges(GraphT graph)
|
||||
{
|
||||
util::DeallocatingVector<Edge> edges;
|
||||
|
||||
|
||||
@@ -21,6 +21,17 @@ struct QueryEdge
|
||||
{
|
||||
}
|
||||
|
||||
EdgeData(const NodeID turn_id,
|
||||
const bool shortcut,
|
||||
const EdgeWeight weight,
|
||||
const EdgeWeight duration,
|
||||
const bool forward,
|
||||
const bool backward)
|
||||
: turn_id(turn_id), shortcut(shortcut), weight(weight), duration(duration),
|
||||
forward(forward), backward(backward)
|
||||
{
|
||||
}
|
||||
|
||||
template <class OtherT> EdgeData(const OtherT &other)
|
||||
{
|
||||
weight = other.weight;
|
||||
|
||||
Reference in New Issue
Block a user