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:
parent
4b75cb8b0e
commit
61c430c098
@ -365,6 +365,7 @@ script:
|
||||
- pushd ${OSRM_BUILD_DIR}
|
||||
- ./unit_tests/library-tests
|
||||
- ./unit_tests/extractor-tests
|
||||
- ./unit_tests/contractor-tests
|
||||
- ./unit_tests/engine-tests
|
||||
- ./unit_tests/util-tests
|
||||
- ./unit_tests/server-tests
|
||||
|
@ -20,7 +20,6 @@ Feature: Testbot - Exclude flags
|
||||
| fg | primary | | always drivable |
|
||||
| gd | primary | | always drivable |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude nothing
|
||||
When I route I should get
|
||||
| from | to | route |
|
||||
@ -38,7 +37,6 @@ Feature: Testbot - Exclude flags
|
||||
| a | 0 | 115 |
|
||||
| d | 115 | 0 |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude motorway
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
@ -59,7 +57,6 @@ Feature: Testbot - Exclude flags
|
||||
| a | 0 | 125 |
|
||||
| d | 125 | 0 |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude toll
|
||||
Given the query options
|
||||
| exclude | toll |
|
||||
@ -72,7 +69,6 @@ Feature: Testbot - Exclude flags
|
||||
| a | f | |
|
||||
| f | d | fg,gd,gd |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude motorway and toll
|
||||
Given the query options
|
||||
| exclude | motorway,toll |
|
||||
@ -85,7 +81,6 @@ Feature: Testbot - Exclude flags
|
||||
| a | f | |
|
||||
| f | d | fg,gd,gd |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude with unsupported exclude combination
|
||||
Given the query options
|
||||
| exclude | TwoWords2 |
|
||||
@ -94,7 +89,6 @@ Feature: Testbot - Exclude flags
|
||||
| from | to | status | message |
|
||||
| a | d | 400 | Exclude flag combination is not supported. |
|
||||
|
||||
@mld
|
||||
Scenario: Testbot - exclude with invalid exclude class name
|
||||
Given the query options
|
||||
| exclude | foo |
|
||||
@ -103,12 +97,3 @@ Feature: Testbot - Exclude flags
|
||||
| from | to | status | message |
|
||||
| a | d | 400 | Exclude flag combination is not supported. |
|
||||
|
||||
@ch
|
||||
Scenario: Testbot - Check error message for exclude on non-MLD
|
||||
Given the query options
|
||||
| exclude | motorway |
|
||||
|
||||
When I route I should get
|
||||
| from | to | status | message |
|
||||
| a | d | 400 | This algorithm does not support exclude flags. |
|
||||
|
||||
|
87
include/contractor/contract_excludable_graph.hpp
Normal file
87
include/contractor/contract_excludable_graph.hpp
Normal file
@ -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
|
153
include/contractor/contracted_edge_container.hpp
Normal file
153
include/contractor/contracted_edge_container.hpp
Normal file
@ -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
|
26
include/contractor/contractor_search.hpp
Normal file
26
include/contractor/contractor_search.hpp
Normal file
@ -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;
|
||||
|
@ -79,6 +79,9 @@ template <> struct HasManyToManySearch<ch::Algorithm> final : std::true_type
|
||||
template <> struct HasGetTileTurns<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasExcludeFlags<ch::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
// Algorithms supported by Contraction Hierarchies with core
|
||||
// the rest is disabled because of performance reasons
|
||||
@ -94,6 +97,9 @@ template <> struct HasMapMatching<corech::Algorithm> final : std::true_type
|
||||
template <> struct HasGetTileTurns<corech::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
template <> struct HasExcludeFlags<corech::Algorithm> final : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
// Algorithms supported by Multi-Level Dijkstra
|
||||
template <> struct HasAlternativePathSearch<mld::Algorithm> final : std::true_type
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "partition/cell_storage.hpp"
|
||||
#include "partition/multi_level_partition.hpp"
|
||||
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
namespace osrm
|
||||
@ -22,14 +23,13 @@ using CH = routing_algorithms::ch::Algorithm;
|
||||
using CoreCH = routing_algorithms::corech::Algorithm;
|
||||
using MLD = routing_algorithms::mld::Algorithm;
|
||||
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
template <typename AlgorithmT> class AlgorithmDataFacade;
|
||||
|
||||
template <> class AlgorithmDataFacade<CH>
|
||||
{
|
||||
public:
|
||||
using EdgeData = contractor::QueryEdge::EdgeData;
|
||||
using EdgeRange = util::filtered_range<EdgeID, util::vector_view<bool>>;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
@ -42,10 +42,6 @@ template <> class AlgorithmDataFacade<CH>
|
||||
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeID BeginEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeID EndEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
// searches for a specific edge
|
||||
@ -73,6 +69,7 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
{
|
||||
public:
|
||||
using EdgeData = extractor::EdgeBasedEdge::EdgeData;
|
||||
using EdgeRange = util::range<EdgeID>;
|
||||
|
||||
// search graph access
|
||||
virtual unsigned GetNumberOfNodes() const = 0;
|
||||
@ -85,10 +82,6 @@ template <> class AlgorithmDataFacade<MLD>
|
||||
|
||||
virtual const EdgeData &GetEdgeData(const EdgeID e) const = 0;
|
||||
|
||||
virtual EdgeID BeginEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeID EndEdges(const NodeID n) const = 0;
|
||||
|
||||
virtual EdgeRange GetAdjacentEdgeRange(const NodeID node) const = 0;
|
||||
|
||||
virtual const partition::MultiLevelPartitionView &GetMultiLevelPartition() const = 0;
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/guidance/bearing_class.hpp"
|
||||
#include "util/guidance/entry_class.hpp"
|
||||
#include "util/guidance/turn_bearing.hpp"
|
||||
@ -68,7 +69,7 @@ template <>
|
||||
class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::AlgorithmDataFacade<CH>
|
||||
{
|
||||
private:
|
||||
using QueryGraph = contractor::QueryGraphView;
|
||||
using QueryGraph = util::FilteredGraphView<contractor::QueryGraphView>;
|
||||
using GraphNode = QueryGraph::NodeArrayEntry;
|
||||
using GraphEdge = QueryGraph::EdgeArrayEntry;
|
||||
|
||||
@ -77,7 +78,9 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
// allocator that keeps the allocation data
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator;
|
||||
|
||||
void InitializeGraphPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeGraphPointer(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
auto graph_nodes_ptr = data_layout.GetBlockPtr<GraphNode>(
|
||||
memory_block, storage::DataLayout::CH_GRAPH_NODE_LIST);
|
||||
@ -85,24 +88,34 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
auto graph_edges_ptr = data_layout.GetBlockPtr<GraphEdge>(
|
||||
memory_block, storage::DataLayout::CH_GRAPH_EDGE_LIST);
|
||||
|
||||
auto filter_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::CH_EDGE_FILTER_0 + exclude_index);
|
||||
|
||||
auto edge_filter_ptr = data_layout.GetBlockPtr<unsigned>(memory_block, filter_block_id);
|
||||
|
||||
util::vector_view<GraphNode> node_list(
|
||||
graph_nodes_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_NODE_LIST]);
|
||||
util::vector_view<GraphEdge> edge_list(
|
||||
graph_edges_ptr, data_layout.num_entries[storage::DataLayout::CH_GRAPH_EDGE_LIST]);
|
||||
m_query_graph = QueryGraph(node_list, edge_list);
|
||||
|
||||
util::vector_view<bool> edge_filter(edge_filter_ptr,
|
||||
data_layout.num_entries[filter_block_id]);
|
||||
m_query_graph = QueryGraph({node_list, edge_list}, edge_filter);
|
||||
}
|
||||
|
||||
public:
|
||||
ContiguousInternalMemoryAlgorithmDataFacade(
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_, std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeGraphPointer(data_layout, memory_block);
|
||||
InitializeGraphPointer(data_layout, memory_block, exclude_index);
|
||||
}
|
||||
|
||||
// search graph access
|
||||
@ -122,10 +135,6 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CH> : public datafacade::Algor
|
||||
return m_query_graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
EdgeID BeginEdges(const NodeID n) const override final { return m_query_graph.BeginEdges(n); }
|
||||
|
||||
EdgeID EndEdges(const NodeID n) const override final { return m_query_graph.EndEdges(n); }
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return m_query_graph.GetAdjacentEdgeRange(node);
|
||||
@ -166,32 +175,37 @@ class ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>
|
||||
// allocator that keeps the allocation data
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator;
|
||||
|
||||
void InitializeCoreInformationPointer(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeCoreInformationPointer(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
auto core_marker_ptr =
|
||||
data_layout.GetBlockPtr<unsigned>(memory_block, storage::DataLayout::CH_CORE_MARKER);
|
||||
util::vector_view<bool> is_core_node(
|
||||
core_marker_ptr, data_layout.num_entries[storage::DataLayout::CH_CORE_MARKER]);
|
||||
auto core_block_id = static_cast<storage::DataLayout::BlockID>(
|
||||
storage::DataLayout::CH_CORE_MARKER_0 + exclude_index);
|
||||
auto core_marker_ptr = data_layout.GetBlockPtr<unsigned>(memory_block, core_block_id);
|
||||
util::vector_view<bool> is_core_node(core_marker_ptr,
|
||||
data_layout.num_entries[core_block_id]);
|
||||
m_is_core_node = std::move(is_core_node);
|
||||
}
|
||||
|
||||
public:
|
||||
ContiguousInternalMemoryAlgorithmDataFacade(
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_)
|
||||
std::shared_ptr<ContiguousBlockAllocator> allocator_, const std::size_t exclude_index)
|
||||
: allocator(std::move(allocator_))
|
||||
{
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory());
|
||||
InitializeInternalPointers(allocator->GetLayout(), allocator->GetMemory(), exclude_index);
|
||||
}
|
||||
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout, char *memory_block)
|
||||
void InitializeInternalPointers(storage::DataLayout &data_layout,
|
||||
char *memory_block,
|
||||
const std::size_t exclude_index)
|
||||
{
|
||||
InitializeCoreInformationPointer(data_layout, memory_block);
|
||||
InitializeCoreInformationPointer(data_layout, memory_block, exclude_index);
|
||||
}
|
||||
|
||||
bool IsCoreNode(const NodeID id) const override final
|
||||
{
|
||||
BOOST_ASSERT(id < m_is_core_node.size());
|
||||
return m_is_core_node[id];
|
||||
BOOST_ASSERT(m_is_core_node.empty() || id < m_is_core_node.size());
|
||||
return !m_is_core_node.empty() || m_is_core_node[id];
|
||||
}
|
||||
};
|
||||
|
||||
@ -942,7 +956,7 @@ class ContiguousInternalMemoryDataFacade<CH>
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator,
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacadeBase(allocator, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator)
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CH>(allocator, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
@ -957,7 +971,7 @@ class ContiguousInternalMemoryDataFacade<CoreCH> final
|
||||
ContiguousInternalMemoryDataFacade(std::shared_ptr<ContiguousBlockAllocator> allocator,
|
||||
const std::size_t exclude_index)
|
||||
: ContiguousInternalMemoryDataFacade<CH>(allocator, exclude_index),
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>(allocator)
|
||||
ContiguousInternalMemoryAlgorithmDataFacade<CoreCH>(allocator, exclude_index)
|
||||
|
||||
{
|
||||
}
|
||||
@ -1130,10 +1144,6 @@ template <> class ContiguousInternalMemoryAlgorithmDataFacade<MLD> : public Algo
|
||||
return query_graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
EdgeID BeginEdges(const NodeID n) const override final { return query_graph.BeginEdges(n); }
|
||||
|
||||
EdgeID EndEdges(const NodeID n) const override final { return query_graph.EndEdges(n); }
|
||||
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID node) const override final
|
||||
{
|
||||
return query_graph.GetAdjacentEdgeRange(node);
|
||||
|
@ -176,7 +176,7 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
|
||||
|
||||
auto mem = storage::makeSharedMemory(barrier.data().region);
|
||||
auto layout = reinterpret_cast<storage::DataLayout *>(mem->Ptr());
|
||||
return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER) >
|
||||
return layout->GetBlockSize(storage::DataLayout::CH_CORE_MARKER_0) >
|
||||
sizeof(std::uint64_t) + sizeof(util::FingerPrint);
|
||||
}
|
||||
else
|
||||
@ -185,9 +185,10 @@ bool Engine<routing_algorithms::corech::Algorithm>::CheckCompability(const Engin
|
||||
return false;
|
||||
storage::io::FileReader in(config.storage_config.GetPath(".osrm.core"),
|
||||
storage::io::FileReader::VerifyFingerprint);
|
||||
in.ReadElementCount64(); // number of core markers
|
||||
const auto number_of_core_markers = in.ReadElementCount64();
|
||||
|
||||
auto size = in.GetSize();
|
||||
return size > sizeof(std::uint64_t);
|
||||
return number_of_core_markers > 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,11 +28,13 @@ class BisectionGraphView
|
||||
// Construction either for a subrange, or for a full range
|
||||
BisectionGraphView(const BisectionGraph &graph);
|
||||
BisectionGraphView(const BisectionGraph &graph,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
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);
|
||||
BisectionGraphView(const BisectionGraphView &view,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
|
||||
// Number of nodes _in this sub-graph.
|
||||
std::size_t NumberOfNodes() const;
|
||||
|
@ -83,8 +83,9 @@ class DinicMaxFlow
|
||||
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;
|
||||
MinCut MakeCut(const BisectionGraphView &view,
|
||||
const LevelGraph &levels,
|
||||
const std::size_t flow_value) const;
|
||||
};
|
||||
|
||||
} // namespace partition
|
||||
|
@ -1,8 +1,8 @@
|
||||
#ifndef OSRM_PARTITION_INERTIAL_FLOW_HPP_
|
||||
#define OSRM_PARTITION_INERTIAL_FLOW_HPP_
|
||||
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
|
@ -26,6 +26,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
|
||||
"CLASSES_LIST",
|
||||
"CH_GRAPH_NODE_LIST",
|
||||
"CH_GRAPH_EDGE_LIST",
|
||||
"CH_EDGE_FILTER_0",
|
||||
"CH_EDGE_FILTER_1",
|
||||
"CH_EDGE_FILTER_2",
|
||||
"CH_EDGE_FILTER_3",
|
||||
"CH_EDGE_FILTER_4",
|
||||
"CH_EDGE_FILTER_5",
|
||||
"CH_EDGE_FILTER_6",
|
||||
"CH_EDGE_FILTER_7",
|
||||
"COORDINATE_LIST",
|
||||
"OSM_NODE_ID_LIST",
|
||||
"TURN_INSTRUCTION",
|
||||
@ -43,7 +51,14 @@ const constexpr char *block_id_to_name[] = {"NAME_CHAR_DATA",
|
||||
"HSGR_CHECKSUM",
|
||||
"TIMESTAMP",
|
||||
"FILE_INDEX_PATH",
|
||||
"CH_CORE_MARKER",
|
||||
"CH_CORE_MARKER_0",
|
||||
"CH_CORE_MARKER_1",
|
||||
"CH_CORE_MARKER_2",
|
||||
"CH_CORE_MARKER_3",
|
||||
"CH_CORE_MARKER_4",
|
||||
"CH_CORE_MARKER_5",
|
||||
"CH_CORE_MARKER_6",
|
||||
"CH_CORE_MARKER_7",
|
||||
"DATASOURCES_NAMES",
|
||||
"PROPERTIES",
|
||||
"BEARING_CLASSID",
|
||||
@ -98,6 +113,14 @@ struct DataLayout
|
||||
CLASSES_LIST,
|
||||
CH_GRAPH_NODE_LIST,
|
||||
CH_GRAPH_EDGE_LIST,
|
||||
CH_EDGE_FILTER_0,
|
||||
CH_EDGE_FILTER_1,
|
||||
CH_EDGE_FILTER_2,
|
||||
CH_EDGE_FILTER_3,
|
||||
CH_EDGE_FILTER_4,
|
||||
CH_EDGE_FILTER_5,
|
||||
CH_EDGE_FILTER_6,
|
||||
CH_EDGE_FILTER_7,
|
||||
COORDINATE_LIST,
|
||||
OSM_NODE_ID_LIST,
|
||||
TURN_INSTRUCTION,
|
||||
@ -115,7 +138,14 @@ struct DataLayout
|
||||
HSGR_CHECKSUM,
|
||||
TIMESTAMP,
|
||||
FILE_INDEX_PATH,
|
||||
CH_CORE_MARKER,
|
||||
CH_CORE_MARKER_0,
|
||||
CH_CORE_MARKER_1,
|
||||
CH_CORE_MARKER_2,
|
||||
CH_CORE_MARKER_3,
|
||||
CH_CORE_MARKER_4,
|
||||
CH_CORE_MARKER_5,
|
||||
CH_CORE_MARKER_6,
|
||||
CH_CORE_MARKER_7,
|
||||
DATASOURCES_NAMES,
|
||||
PROPERTIES,
|
||||
BEARING_CLASSID,
|
||||
@ -179,7 +209,7 @@ struct DataLayout
|
||||
inline uint64_t GetBlockSize(BlockID bid) const
|
||||
{
|
||||
// special bit encoding
|
||||
if (bid == CH_CORE_MARKER)
|
||||
if (bid >= CH_CORE_MARKER_0 && bid <= CH_CORE_MARKER_7)
|
||||
{
|
||||
return (num_entries[bid] / 32 + 1) * entry_size[bid];
|
||||
}
|
||||
|
@ -33,6 +33,27 @@ template <typename EdgeDataT, bool UseSharedMemory>
|
||||
void write(storage::io::FileWriter &writer, const DynamicGraph<EdgeDataT> &graph);
|
||||
}
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// These types need to live outside of DynamicGraph
|
||||
// to be not dependable. We need this for transforming graphs
|
||||
// with different data.
|
||||
|
||||
template <typename EdgeIterator> struct DynamicNode
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator first_edge;
|
||||
// amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
template <typename NodeIterator, typename EdgeDataT> struct DynamicEdge
|
||||
{
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename EdgeDataT> class DynamicGraph
|
||||
{
|
||||
public:
|
||||
@ -41,6 +62,11 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
using EdgeIterator = std::uint32_t;
|
||||
using EdgeRange = range<EdgeIterator>;
|
||||
|
||||
using Node = detail::DynamicNode<EdgeIterator>;
|
||||
using Edge = detail::DynamicEdge<NodeIterator, EdgeDataT>;
|
||||
|
||||
template <typename E> friend class DynamicGraph;
|
||||
|
||||
class InputEdge
|
||||
{
|
||||
public:
|
||||
@ -120,6 +146,9 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
}
|
||||
}
|
||||
|
||||
// Copy&move for the same data
|
||||
//
|
||||
|
||||
DynamicGraph(const DynamicGraph &other)
|
||||
{
|
||||
number_of_nodes = other.number_of_nodes;
|
||||
@ -130,7 +159,7 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
edge_list = other.edge_list;
|
||||
}
|
||||
|
||||
DynamicGraph&operator=(const DynamicGraph &other)
|
||||
DynamicGraph &operator=(const DynamicGraph &other)
|
||||
{
|
||||
auto copy_other = other;
|
||||
*this = std::move(other);
|
||||
@ -159,6 +188,38 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Removes all edges to and from nodes for which filter(node_id) returns false
|
||||
template <typename Pred> auto Filter(Pred filter) const &
|
||||
{
|
||||
DynamicGraph other;
|
||||
|
||||
other.number_of_nodes = number_of_nodes;
|
||||
other.number_of_edges = static_cast<std::uint32_t>(number_of_edges);
|
||||
other.edge_list.reserve(edge_list.size());
|
||||
other.node_array.resize(node_array.size());
|
||||
|
||||
NodeID node_id = 0;
|
||||
std::transform(
|
||||
node_array.begin(), node_array.end(), other.node_array.begin(), [&](const Node &node) {
|
||||
const EdgeIterator first_edge = other.edge_list.size();
|
||||
if (filter(node_id++))
|
||||
{
|
||||
std::copy_if(edge_list.begin() + node.first_edge,
|
||||
edge_list.begin() + node.first_edge + node.edges,
|
||||
std::back_inserter(other.edge_list),
|
||||
[&](const auto &edge) { return filter(edge.target); });
|
||||
const unsigned num_edges = other.edge_list.size() - first_edge;
|
||||
return Node{first_edge, num_edges};
|
||||
}
|
||||
else
|
||||
{
|
||||
return Node{first_edge, 0};
|
||||
}
|
||||
});
|
||||
|
||||
return other;
|
||||
}
|
||||
|
||||
unsigned GetNumberOfNodes() const { return number_of_nodes; }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return number_of_edges; }
|
||||
@ -366,6 +427,7 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
for (auto edge : GetAdjacentEdgeRange(node))
|
||||
{
|
||||
edge_list[edge].target = old_to_new_node[edge_list[edge].target];
|
||||
BOOST_ASSERT(edge_list[edge].target != SPECIAL_NODEID);
|
||||
old_to_new_edge[edge] = new_edge_index++;
|
||||
}
|
||||
node_array[node].first_edge = new_first_edge;
|
||||
@ -400,20 +462,6 @@ template <typename EdgeDataT> class DynamicGraph
|
||||
edge_list[edge].target = (std::numeric_limits<NodeIterator>::max)();
|
||||
}
|
||||
|
||||
struct Node
|
||||
{
|
||||
// index of the first edge
|
||||
EdgeIterator first_edge;
|
||||
// amount of edges
|
||||
unsigned edges;
|
||||
};
|
||||
|
||||
struct Edge
|
||||
{
|
||||
NodeIterator target;
|
||||
EdgeDataT data;
|
||||
};
|
||||
|
||||
NodeIterator number_of_nodes;
|
||||
std::atomic_uint number_of_edges;
|
||||
|
||||
|
35
include/util/exclude_flag.hpp
Normal file
35
include/util/exclude_flag.hpp
Normal file
@ -0,0 +1,35 @@
|
||||
#ifndef OSRM_UTIL_EXCLUDE_FLAG_HPP
|
||||
#define OSRM_UTIL_EXCLUDE_FLAG_HPP
|
||||
|
||||
#include "extractor/node_data_container.hpp"
|
||||
#include "extractor/profile_properties.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
inline std::vector<std::vector<bool>>
|
||||
excludeFlagsToNodeFilter(const NodeID number_of_nodes,
|
||||
const extractor::EdgeBasedNodeDataContainer &node_data,
|
||||
const extractor::ProfileProperties &properties)
|
||||
{
|
||||
std::vector<std::vector<bool>> filters;
|
||||
for (auto mask : properties.excludable_classes)
|
||||
{
|
||||
if (mask != extractor::INAVLID_CLASS_DATA)
|
||||
{
|
||||
std::vector<bool> allowed_nodes(number_of_nodes);
|
||||
for (const auto node : util::irange<NodeID>(0, number_of_nodes))
|
||||
{
|
||||
allowed_nodes[node] = (node_data.GetClassData(node) & mask) == 0;
|
||||
}
|
||||
filters.push_back(std::move(allowed_nodes));
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
165
include/util/filtered_graph.hpp
Normal file
165
include/util/filtered_graph.hpp
Normal file
@ -0,0 +1,165 @@
|
||||
#ifndef OSRM_UTIL_FILTERED_GRAPH_HPP
|
||||
#define OSRM_UTIL_FILTERED_GRAPH_HPP
|
||||
|
||||
#include "storage/shared_memory_ownership.hpp"
|
||||
|
||||
#include "util/dynamic_graph.hpp"
|
||||
#include "util/filtered_integer_range.hpp"
|
||||
#include "util/static_graph.hpp"
|
||||
#include "util/vector_view.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <typename GraphT, storage::Ownership Ownership> class FilteredGraphImpl;
|
||||
|
||||
// For static graphs we can save the filters as a static vector since
|
||||
// we don't modify the structure of the graph. This also makes it easy to
|
||||
// swap out the filter.
|
||||
template <typename EdgeDataT, storage::Ownership Ownership>
|
||||
class FilteredGraphImpl<util::StaticGraph<EdgeDataT, Ownership>, Ownership>
|
||||
{
|
||||
template <typename T> using Vector = util::ViewOrVector<T, Ownership>;
|
||||
|
||||
public:
|
||||
using Graph = util::StaticGraph<EdgeDataT, Ownership>;
|
||||
using EdgeIterator = typename Graph::EdgeIterator;
|
||||
using NodeIterator = typename Graph::NodeIterator;
|
||||
using NodeArrayEntry = typename Graph::NodeArrayEntry;
|
||||
using EdgeArrayEntry = typename Graph::EdgeArrayEntry;
|
||||
using EdgeRange = util::filtered_range<EdgeIterator, Vector<bool>>;
|
||||
|
||||
unsigned GetNumberOfNodes() const { return graph.GetNumberOfNodes(); }
|
||||
|
||||
unsigned GetNumberOfEdges() const { return graph.GetNumberOfEdges(); }
|
||||
|
||||
unsigned GetOutDegree(const NodeIterator n) const
|
||||
{
|
||||
auto range = graph.GetAdjacentEdgeRange(n);
|
||||
return std::count_if(range.begin(), range.end(), [this](const EdgeIterator edge) {
|
||||
return edge_filter[edge];
|
||||
});
|
||||
}
|
||||
|
||||
inline NodeIterator GetTarget(const EdgeIterator e) const
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetTarget(e);
|
||||
}
|
||||
|
||||
auto &GetEdgeData(const EdgeIterator e)
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
const auto &GetEdgeData(const EdgeIterator e) const
|
||||
{
|
||||
BOOST_ASSERT(edge_filter[e]);
|
||||
return graph.GetEdgeData(e);
|
||||
}
|
||||
|
||||
auto GetAdjacentEdgeRange(const NodeIterator n) const
|
||||
{
|
||||
return EdgeRange{graph.BeginEdges(n), graph.EndEdges(n), edge_filter};
|
||||
}
|
||||
|
||||
// searches for a specific edge
|
||||
EdgeIterator FindEdge(const NodeIterator from, const NodeIterator to) const
|
||||
{
|
||||
for (const auto edge : GetAdjacentEdgeRange(from))
|
||||
{
|
||||
if (to == GetTarget(edge))
|
||||
{
|
||||
return edge;
|
||||
}
|
||||
}
|
||||
return SPECIAL_EDGEID;
|
||||
}
|
||||
|
||||
template <typename FilterFunction>
|
||||
EdgeIterator
|
||||
FindSmallestEdge(const NodeIterator from, const NodeIterator to, FilterFunction &&filter) const
|
||||
{
|
||||
static_assert(traits::HasDataMember<typename Graph::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;
|
||||
}
|
||||
|
||||
FilteredGraphImpl() = default;
|
||||
|
||||
FilteredGraphImpl(Graph graph, Vector<bool> edge_filter_)
|
||||
: graph(std::move(graph)), edge_filter(std::move(edge_filter_))
|
||||
{
|
||||
BOOST_ASSERT(edge_filter.empty() || edge_filter.size() == graph.GetNumberOfEdges());
|
||||
}
|
||||
|
||||
// Takes a graph and a function that maps EdgeID to true
|
||||
// if the edge should be included in the graph.
|
||||
template <typename Pred>
|
||||
FilteredGraphImpl(Graph graph, Pred filter)
|
||||
: graph(std::move(graph)), edge_filter(graph.GetNumberOfEdges())
|
||||
{
|
||||
auto edge_ids = util::irange<EdgeID>(0, graph.GetNumberOfEdges());
|
||||
std::transform(edge_ids.begin(), edge_ids.end(), edge_filter.begin(), filter);
|
||||
}
|
||||
|
||||
void Renumber(const std::vector<NodeID> &old_to_new_node)
|
||||
{
|
||||
graph.Renumber(old_to_new_node);
|
||||
// FIXME the edge filter needs to be renumbered with a different permutation
|
||||
// util::inplacePermutation(edge_filter.begin(), edge_filter.end(), old_to_new_node);
|
||||
}
|
||||
|
||||
private:
|
||||
Graph graph;
|
||||
Vector<bool> edge_filter;
|
||||
};
|
||||
}
|
||||
|
||||
template <typename GraphT>
|
||||
using FilteredGraphContainer = detail::FilteredGraphImpl<GraphT, storage::Ownership::Container>;
|
||||
template <typename GraphT>
|
||||
using FilteredGraphView = detail::FilteredGraphImpl<GraphT, storage::Ownership::View>;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
101
include/util/filtered_integer_range.hpp
Normal file
101
include/util/filtered_integer_range.hpp
Normal file
@ -0,0 +1,101 @@
|
||||
#ifndef FILTERED_INTEGER_RANGE_HPP
|
||||
#define FILTERED_INTEGER_RANGE_HPP
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace util
|
||||
{
|
||||
|
||||
// This implements a single-pass integer range.
|
||||
// We need our own implementation here because using boost::adaptor::filtered() has
|
||||
// the problem that the return-type depends on the lambda-type you pass into the function.
|
||||
// That makes it unsuitable to use in interface where we would expect all filtered ranges
|
||||
// to be off the same type.
|
||||
|
||||
template <typename Integer, typename Filter>
|
||||
class filtered_integer_iterator
|
||||
: public boost::iterator_facade<filtered_integer_iterator<Integer, Filter>,
|
||||
Integer,
|
||||
boost::single_pass_traversal_tag,
|
||||
Integer>
|
||||
{
|
||||
typedef boost::iterator_facade<filtered_integer_iterator<Integer, Filter>,
|
||||
Integer,
|
||||
boost::single_pass_traversal_tag,
|
||||
Integer>
|
||||
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;
|
||||
|
||||
filtered_integer_iterator() : value(), filter(nullptr) {}
|
||||
explicit filtered_integer_iterator(value_type x, value_type end_value, const Filter *filter)
|
||||
: value(x), end_value(end_value), filter(filter)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
void increment()
|
||||
{
|
||||
do
|
||||
{
|
||||
++value;
|
||||
} while (value < end_value && !(*filter)[value]);
|
||||
}
|
||||
bool equal(const filtered_integer_iterator &other) const { return value == other.value; }
|
||||
reference dereference() const { return value; }
|
||||
|
||||
friend class ::boost::iterator_core_access;
|
||||
value_type value;
|
||||
value_type end_value;
|
||||
const Filter *filter;
|
||||
};
|
||||
|
||||
template <typename Integer, typename Filter> class filtered_range
|
||||
{
|
||||
public:
|
||||
typedef filtered_integer_iterator<Integer, Filter> const_iterator;
|
||||
typedef filtered_integer_iterator<Integer, Filter> iterator;
|
||||
|
||||
filtered_range(Integer begin, Integer end, const Filter &filter) : last(end, end, &filter)
|
||||
{
|
||||
while (begin < end && !filter[begin])
|
||||
{
|
||||
begin++;
|
||||
}
|
||||
|
||||
iter = iterator(begin, end, &filter);
|
||||
}
|
||||
|
||||
iterator begin() const noexcept { return iter; }
|
||||
iterator end() const noexcept { return last; }
|
||||
|
||||
private:
|
||||
iterator iter;
|
||||
iterator last;
|
||||
};
|
||||
|
||||
// convenience function to construct an integer range with type deduction
|
||||
template <typename Integer, typename Filter>
|
||||
filtered_range<Integer, Filter>
|
||||
filtered_irange(const Integer first,
|
||||
const Integer last,
|
||||
const Filter &filter,
|
||||
typename std::enable_if<std::is_integral<Integer>::value>::type * = 0) noexcept
|
||||
{
|
||||
return filtered_range<Integer, Filter>(first, last, filter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif // INTEGER_RANGE_HPP
|
@ -152,6 +152,7 @@ class QueryHeap
|
||||
|
||||
void Insert(NodeID node, Weight weight, const Data &data)
|
||||
{
|
||||
BOOST_ASSERT(node < std::numeric_limits<NodeID>::max());
|
||||
const auto index = static_cast<Key>(inserted_nodes.size());
|
||||
const auto handle = heap.push(std::make_pair(weight, index));
|
||||
inserted_nodes.emplace_back(HeapNode{handle, node, weight, data});
|
||||
|
@ -1,4 +1,6 @@
|
||||
#include "contractor/contractor.hpp"
|
||||
#include "contractor/contract_excludable_graph.hpp"
|
||||
#include "contractor/contracted_edge_container.hpp"
|
||||
#include "contractor/crc32_processor.hpp"
|
||||
#include "contractor/files.hpp"
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
@ -6,6 +8,7 @@
|
||||
|
||||
#include "extractor/compressed_edge_container.hpp"
|
||||
#include "extractor/edge_based_graph_factory.hpp"
|
||||
#include "extractor/files.hpp"
|
||||
#include "extractor/node_based_edge.hpp"
|
||||
|
||||
#include "storage/io.hpp"
|
||||
@ -14,6 +17,8 @@
|
||||
|
||||
#include "util/exception.hpp"
|
||||
#include "util/exception_utils.hpp"
|
||||
#include "util/exclude_flag.hpp"
|
||||
#include "util/filtered_graph.hpp"
|
||||
#include "util/graph_loader.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
#include "util/log.hpp"
|
||||
@ -42,6 +47,11 @@ int Contractor::Run()
|
||||
throw util::exception("Core factor must be between 0.0 to 1.0 (inclusive)" + SOURCE_REF);
|
||||
}
|
||||
|
||||
if (config.use_cached_priority)
|
||||
{
|
||||
util::Log(logWARNING) << "Using cached priorities is deprecated and they will be ignored.";
|
||||
}
|
||||
|
||||
TIMER_START(preparing);
|
||||
|
||||
util::Log() << "Reading node weights.";
|
||||
@ -63,41 +73,36 @@ int Contractor::Run()
|
||||
// Contracting the edge-expanded graph
|
||||
|
||||
TIMER_START(contraction);
|
||||
std::vector<bool> is_core_node;
|
||||
std::vector<float> node_levels;
|
||||
if (config.use_cached_priority)
|
||||
|
||||
std::vector<std::vector<bool>> node_filters;
|
||||
{
|
||||
files::readLevels(config.GetPath(".osrm.level"), node_levels);
|
||||
extractor::EdgeBasedNodeDataContainer node_data;
|
||||
extractor::files::readNodeData(config.GetPath(".osrm.ebg_nodes"), node_data);
|
||||
|
||||
extractor::ProfileProperties properties;
|
||||
extractor::files::readProfileProperties(config.GetPath(".osrm.properties"), properties);
|
||||
|
||||
node_filters = util::excludeFlagsToNodeFilter(max_edge_id + 1, node_data, properties);
|
||||
}
|
||||
|
||||
util::DeallocatingVector<QueryEdge> contracted_edge_list;
|
||||
{ // own scope to not keep the contractor around
|
||||
auto contractor_graph = toContractorGraph(max_edge_id+1, std::move(edge_based_edge_list));
|
||||
std::tie(node_levels, is_core_node) = contractGraph(contractor_graph,
|
||||
std::move(node_levels),
|
||||
std::move(node_weights),
|
||||
config.core_factor);
|
||||
RangebasedCRC32 crc32_calculator;
|
||||
const unsigned checksum = crc32_calculator(edge_based_edge_list);
|
||||
|
||||
contracted_edge_list = toEdges<QueryEdge>(std::move(contractor_graph));
|
||||
}
|
||||
QueryGraph query_graph;
|
||||
std::vector<std::vector<bool>> edge_filters;
|
||||
std::vector<std::vector<bool>> cores;
|
||||
std::tie(query_graph, edge_filters, cores) =
|
||||
contractExcludableGraph(toContractorGraph(max_edge_id + 1, std::move(edge_based_edge_list)),
|
||||
std::move(node_weights),
|
||||
std::move(node_filters),
|
||||
config.core_factor);
|
||||
TIMER_STOP(contraction);
|
||||
|
||||
util::Log() << "Contracted graph has " << query_graph.GetNumberOfEdges() << " edges.";
|
||||
util::Log() << "Contraction took " << TIMER_SEC(contraction) << " sec";
|
||||
|
||||
{
|
||||
RangebasedCRC32 crc32_calculator;
|
||||
const unsigned checksum = crc32_calculator(contracted_edge_list);
|
||||
files::writeGraph(config.GetPath(".osrm.hsgr"), checksum, query_graph, edge_filters);
|
||||
|
||||
files::writeGraph(config.GetPath(".osrm.hsgr"),
|
||||
checksum,
|
||||
QueryGraph{max_edge_id + 1, std::move(contracted_edge_list)});
|
||||
}
|
||||
|
||||
files::writeCoreMarker(config.GetPath(".osrm.core"), is_core_node);
|
||||
if (!config.use_cached_priority)
|
||||
{
|
||||
files::writeLevels(config.GetPath(".osrm.level"), node_levels);
|
||||
}
|
||||
files::writeCoreMarker(config.GetPath(".osrm.core"), cores);
|
||||
|
||||
TIMER_STOP(preparing);
|
||||
|
||||
|
@ -1,23 +1,65 @@
|
||||
#include "contractor/contractor_dijkstra.hpp"
|
||||
#include "contractor/contractor_search.hpp"
|
||||
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_heap.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
ContractorDijkstra::ContractorDijkstra(const std::size_t heap_size) : heap(heap_size) {}
|
||||
namespace
|
||||
{
|
||||
void relaxNode(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const NodeID node,
|
||||
const EdgeWeight node_weight,
|
||||
const NodeID forbidden_node)
|
||||
{
|
||||
const short current_hop = heap.GetData(node).hop + 1;
|
||||
for (auto edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const auto &data = graph.GetEdgeData(edge);
|
||||
if (!data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID to = graph.GetTarget(edge);
|
||||
BOOST_ASSERT(to != SPECIAL_NODEID);
|
||||
if (forbidden_node == to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const EdgeWeight to_weight = node_weight + data.weight;
|
||||
|
||||
void ContractorDijkstra::Run(const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph)
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_weight, ContractorHeapData{current_hop, false});
|
||||
}
|
||||
// Found a shorter Path -> Update weight
|
||||
else if (to_weight < heap.GetKey(to))
|
||||
{
|
||||
heap.DecreaseKey(to, to_weight);
|
||||
heap.GetData(to).hop = current_hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void search(ContractorHeap &heap,
|
||||
const ContractorGraph &graph,
|
||||
const unsigned number_of_targets,
|
||||
const int node_limit,
|
||||
const EdgeWeight weight_limit,
|
||||
const NodeID forbidden_node)
|
||||
{
|
||||
int nodes = 0;
|
||||
unsigned number_of_targets_found = 0;
|
||||
while (!heap.Empty())
|
||||
{
|
||||
const NodeID node = heap.DeleteMin();
|
||||
BOOST_ASSERT(node != SPECIAL_NODEID);
|
||||
const auto node_weight = heap.GetKey(node);
|
||||
if (++nodes > node_limit)
|
||||
{
|
||||
@ -38,59 +80,8 @@ void ContractorDijkstra::Run(const unsigned number_of_targets,
|
||||
}
|
||||
}
|
||||
|
||||
RelaxNode(node, node_weight, forbidden_node, graph);
|
||||
relaxNode(heap, graph, node, node_weight, forbidden_node);
|
||||
}
|
||||
}
|
||||
|
||||
void ContractorDijkstra::RelaxNode(const NodeID node,
|
||||
const EdgeWeight node_weight,
|
||||
const NodeID forbidden_node,
|
||||
const ContractorGraph &graph)
|
||||
{
|
||||
const short current_hop = heap.GetData(node).hop + 1;
|
||||
for (auto edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
const ContractorEdgeData &data = graph.GetEdgeData(edge);
|
||||
if (!data.forward)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const NodeID to = graph.GetTarget(edge);
|
||||
if (forbidden_node == to)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const EdgeWeight to_weight = node_weight + data.weight;
|
||||
|
||||
// New Node discovered -> Add to Heap + Node Info Storage
|
||||
if (!heap.WasInserted(to))
|
||||
{
|
||||
heap.Insert(to, to_weight, ContractorHeapData{current_hop, false});
|
||||
}
|
||||
// Found a shorter Path -> Update weight
|
||||
else if (to_weight < GetKey(to))
|
||||
{
|
||||
heap.DecreaseKey(to, to_weight);
|
||||
heap.GetData(to).hop = current_hop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ContractorDijkstra::Clear() { heap.Clear(); }
|
||||
|
||||
bool ContractorDijkstra::WasInserted(const NodeID node) const { return heap.WasInserted(node); }
|
||||
|
||||
void ContractorDijkstra::Insert(const NodeID node,
|
||||
const ContractorHeap::WeightType weight,
|
||||
const ContractorHeap::DataType &data)
|
||||
{
|
||||
heap.Insert(node, weight, data);
|
||||
}
|
||||
|
||||
ContractorHeap::WeightType ContractorDijkstra::GetKey(const NodeID node)
|
||||
{
|
||||
return heap.GetKey(node);
|
||||
}
|
||||
|
||||
} // namespace contractor
|
||||
} // namespace osrm
|
@ -1,6 +1,6 @@
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
#include "contractor/contractor_dijkstra.hpp"
|
||||
#include "contractor/contractor_graph.hpp"
|
||||
#include "contractor/contractor_search.hpp"
|
||||
#include "contractor/query_edge.hpp"
|
||||
#include "util/deallocating_vector.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
@ -14,6 +14,7 @@
|
||||
|
||||
#include <tbb/enumerable_thread_specific.h>
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/parallel_invoke.h>
|
||||
#include <tbb/parallel_sort.h>
|
||||
|
||||
#include <algorithm>
|
||||
@ -29,10 +30,10 @@ namespace
|
||||
{
|
||||
struct ContractorThreadData
|
||||
{
|
||||
ContractorDijkstra dijkstra;
|
||||
ContractorHeap heap;
|
||||
std::vector<ContractorEdge> inserted_edges;
|
||||
std::vector<NodeID> neighbours;
|
||||
explicit ContractorThreadData(NodeID nodes) : dijkstra(nodes) {}
|
||||
explicit ContractorThreadData(NodeID nodes) : heap(nodes) {}
|
||||
};
|
||||
|
||||
struct ContractorNodeData
|
||||
@ -42,34 +43,37 @@ struct ContractorNodeData
|
||||
using NodeLevel = float;
|
||||
|
||||
ContractorNodeData(std::size_t number_of_nodes,
|
||||
std::vector<NodePriority> priorities_,
|
||||
std::vector<bool> uncontracted_nodes_,
|
||||
std::vector<bool> contractable_,
|
||||
std::vector<EdgeWeight> weights_)
|
||||
: is_core(number_of_nodes, true), priorities(std::move(priorities_)),
|
||||
weights(std::move(weights_))
|
||||
: is_core(std::move(uncontracted_nodes_)), contractable(std::move(contractable_)),
|
||||
priorities(number_of_nodes), weights(std::move(weights_)), depths(number_of_nodes, 0)
|
||||
{
|
||||
// no cached priorities
|
||||
if (priorities.empty())
|
||||
if (contractable.empty())
|
||||
{
|
||||
depths.resize(number_of_nodes, 0);
|
||||
levels.resize(number_of_nodes);
|
||||
priorities.resize(number_of_nodes);
|
||||
contractable.resize(number_of_nodes, true);
|
||||
}
|
||||
if (is_core.empty())
|
||||
{
|
||||
is_core.resize(number_of_nodes, true);
|
||||
}
|
||||
}
|
||||
|
||||
void Renumber(const std::vector<NodeID> &old_to_new)
|
||||
{
|
||||
util::inplacePermutation(priorities.begin(), priorities.end(), old_to_new);
|
||||
util::inplacePermutation(weights.begin(), weights.end(), old_to_new);
|
||||
util::inplacePermutation(levels.begin(), levels.end(), old_to_new);
|
||||
util::inplacePermutation(is_core.begin(), is_core.end(), old_to_new);
|
||||
util::inplacePermutation(depths.begin(), depths.end(), old_to_new);
|
||||
tbb::parallel_invoke(
|
||||
[&] { util::inplacePermutation(priorities.begin(), priorities.end(), old_to_new); },
|
||||
[&] { util::inplacePermutation(weights.begin(), weights.end(), old_to_new); },
|
||||
[&] { util::inplacePermutation(is_core.begin(), is_core.end(), old_to_new); },
|
||||
[&] { util::inplacePermutation(contractable.begin(), contractable.end(), old_to_new); },
|
||||
[&] { util::inplacePermutation(depths.begin(), depths.end(), old_to_new); });
|
||||
}
|
||||
|
||||
std::vector<bool> is_core;
|
||||
std::vector<bool> contractable;
|
||||
std::vector<NodePriority> priorities;
|
||||
std::vector<EdgeWeight> weights;
|
||||
std::vector<NodeDepth> depths;
|
||||
std::vector<NodeLevel> levels;
|
||||
};
|
||||
|
||||
struct ContractionStats
|
||||
@ -87,7 +91,8 @@ struct ContractionStats
|
||||
|
||||
struct RemainingNodeData
|
||||
{
|
||||
RemainingNodeData() : id(0), is_independent(false) {}
|
||||
RemainingNodeData() = default;
|
||||
RemainingNodeData(NodeID id, bool is_independent) : id(id), is_independent(is_independent) {}
|
||||
NodeID id : 31;
|
||||
bool is_independent : 1;
|
||||
};
|
||||
@ -102,8 +107,7 @@ struct ThreadDataContainer
|
||||
auto &ref = data.local(exists);
|
||||
if (!exists)
|
||||
{
|
||||
// ref = std::make_shared<ContractorThreadData>(number_of_nodes);
|
||||
ref = std::make_shared<ContractorThreadData>(4000);
|
||||
ref = std::make_shared<ContractorThreadData>(number_of_nodes);
|
||||
}
|
||||
|
||||
return ref.get();
|
||||
@ -130,14 +134,14 @@ inline bool Bias(const util::XORFastHash<> &fast_hash, const NodeID a, const Nod
|
||||
return a < b;
|
||||
}
|
||||
|
||||
template <bool RUNSIMULATION>
|
||||
template <bool RUNSIMULATION, typename ContractorGraph>
|
||||
void ContractNode(ContractorThreadData *data,
|
||||
const ContractorGraph &graph,
|
||||
const NodeID node,
|
||||
std::vector<EdgeWeight> &node_weights,
|
||||
ContractionStats *stats = nullptr)
|
||||
{
|
||||
auto &dijkstra = data->dijkstra;
|
||||
auto &heap = data->heap;
|
||||
std::size_t inserted_edges_size = data->inserted_edges.size();
|
||||
std::vector<ContractorEdge> &inserted_edges = data->inserted_edges;
|
||||
constexpr bool SHORTCUT_ARC = true;
|
||||
@ -164,8 +168,8 @@ void ContractNode(ContractorThreadData *data,
|
||||
continue;
|
||||
}
|
||||
|
||||
dijkstra.Clear();
|
||||
dijkstra.Insert(source, 0, ContractorHeapData{});
|
||||
heap.Clear();
|
||||
heap.Insert(source, 0, ContractorHeapData{});
|
||||
EdgeWeight max_weight = 0;
|
||||
unsigned number_of_targets = 0;
|
||||
|
||||
@ -230,9 +234,9 @@ void ContractNode(ContractorThreadData *data,
|
||||
continue;
|
||||
}
|
||||
max_weight = std::max(max_weight, path_weight);
|
||||
if (!dijkstra.WasInserted(target))
|
||||
if (!heap.WasInserted(target))
|
||||
{
|
||||
dijkstra.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true});
|
||||
heap.Insert(target, INVALID_EDGE_WEIGHT, ContractorHeapData{0, true});
|
||||
++number_of_targets;
|
||||
}
|
||||
}
|
||||
@ -240,12 +244,12 @@ void ContractNode(ContractorThreadData *data,
|
||||
if (RUNSIMULATION)
|
||||
{
|
||||
const int constexpr SIMULATION_SEARCH_SPACE_SIZE = 1000;
|
||||
dijkstra.Run(number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, max_weight, node, graph);
|
||||
search(heap, graph, number_of_targets, SIMULATION_SEARCH_SPACE_SIZE, max_weight, node);
|
||||
}
|
||||
else
|
||||
{
|
||||
const int constexpr FULL_SEARCH_SPACE_SIZE = 2000;
|
||||
dijkstra.Run(number_of_targets, FULL_SEARCH_SPACE_SIZE, max_weight, node, graph);
|
||||
search(heap, graph, number_of_targets, FULL_SEARCH_SPACE_SIZE, max_weight, node);
|
||||
}
|
||||
for (auto out_edge : graph.GetAdjacentEdgeRange(node))
|
||||
{
|
||||
@ -259,7 +263,7 @@ void ContractNode(ContractorThreadData *data,
|
||||
continue;
|
||||
|
||||
const EdgeWeight path_weight = in_data.weight + out_data.weight;
|
||||
const EdgeWeight weight = dijkstra.GetKey(target);
|
||||
const EdgeWeight weight = heap.GetKey(target);
|
||||
if (path_weight < weight)
|
||||
{
|
||||
if (RUNSIMULATION)
|
||||
@ -371,9 +375,9 @@ void RenumberGraph(ContractorGraph &graph, const std::vector<NodeID> &old_to_new
|
||||
|
||||
/* Reorder nodes for better locality during contraction */
|
||||
void RenumberData(std::vector<RemainingNodeData> &remaining_nodes,
|
||||
std::vector<NodeID> &new_to_old_node_id,
|
||||
ContractorNodeData &node_data,
|
||||
ContractorGraph &graph)
|
||||
std::vector<NodeID> &new_to_old_node_id,
|
||||
ContractorNodeData &node_data,
|
||||
ContractorGraph &graph)
|
||||
{
|
||||
std::vector<NodeID> current_to_new_node_id(graph.GetNumberOfNodes(), SPECIAL_NODEID);
|
||||
|
||||
@ -407,7 +411,8 @@ void RenumberData(std::vector<RemainingNodeData> &remaining_nodes,
|
||||
RenumberGraph(graph, current_to_new_node_id);
|
||||
}
|
||||
|
||||
float EvaluateNodePriority(const ContractionStats &stats, const ContractorNodeData::NodeDepth node_depth)
|
||||
float EvaluateNodePriority(const ContractionStats &stats,
|
||||
const ContractorNodeData::NodeDepth node_depth)
|
||||
{
|
||||
// Result will contain the priority
|
||||
float result;
|
||||
@ -476,14 +481,18 @@ bool UpdateNodeNeighbours(ContractorNodeData &node_data,
|
||||
// re-evaluate priorities of neighboring nodes
|
||||
for (const NodeID u : neighbours)
|
||||
{
|
||||
node_data.priorities[u] = EvaluateNodePriority(
|
||||
SimulateNodeContraction(data, graph, u, node_data.weights), node_data.depths[u]);
|
||||
if (node_data.contractable[u])
|
||||
{
|
||||
node_data.priorities[u] = EvaluateNodePriority(
|
||||
SimulateNodeContraction(data, graph, u, node_data.weights), node_data.depths[u]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsNodeIndependent(const util::XORFastHash<> &hash,
|
||||
const std::vector<float> &priorities,
|
||||
const std::vector<NodeID> &new_to_old_node_id,
|
||||
const ContractorGraph &graph,
|
||||
ContractorThreadData *const data,
|
||||
const NodeID node)
|
||||
@ -509,7 +518,7 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash,
|
||||
}
|
||||
// tie breaking
|
||||
if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
|
||||
Bias(hash, node, target))
|
||||
Bias(hash, new_to_old_node_id[node], new_to_old_node_id[target]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -538,7 +547,7 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash,
|
||||
}
|
||||
// tie breaking
|
||||
if (std::abs(priority - target_priority) < std::numeric_limits<float>::epsilon() &&
|
||||
Bias(hash, node, target))
|
||||
Bias(hash, new_to_old_node_id[node], new_to_old_node_id[target]))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -548,15 +557,16 @@ bool IsNodeIndependent(const util::XORFastHash<> &hash,
|
||||
}
|
||||
}
|
||||
|
||||
LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
std::vector<float> cached_node_levels_,
|
||||
std::vector<EdgeWeight> node_weights_,
|
||||
double core_factor)
|
||||
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)
|
||||
{
|
||||
BOOST_ASSERT(node_weights_.size() == graph.GetNumberOfNodes());
|
||||
util::XORFastHash<> fast_hash;
|
||||
|
||||
// for the preperation we can use a big grain size, which is much faster (probably cache)
|
||||
const constexpr size_t InitGrainSize = 100000;
|
||||
const constexpr size_t PQGrainSize = 100000;
|
||||
// auto_partitioner will automatically increase the blocksize if we have
|
||||
// a lot of data. It is *important* for the last loop iterations
|
||||
@ -575,56 +585,71 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
// Fill the map with an identiy mapping
|
||||
std::iota(new_to_old_node_id.begin(), new_to_old_node_id.end(), 0);
|
||||
|
||||
bool use_cached_node_priorities = !cached_node_levels_.empty();
|
||||
ContractorNodeData node_data{
|
||||
graph.GetNumberOfNodes(), std::move(cached_node_levels_), std::move(node_weights_)};
|
||||
ContractorNodeData node_data{graph.GetNumberOfNodes(),
|
||||
std::move(node_is_uncontracted_),
|
||||
std::move(node_is_contractable_),
|
||||
std::move(node_weights_)};
|
||||
|
||||
std::vector<RemainingNodeData> remaining_nodes(number_of_nodes);
|
||||
// initialize priorities in parallel
|
||||
tbb::parallel_for(tbb::blocked_range<NodeID>(0, number_of_nodes, InitGrainSize),
|
||||
[&remaining_nodes](const tbb::blocked_range<NodeID> &range) {
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
{
|
||||
remaining_nodes[x].id = x;
|
||||
}
|
||||
});
|
||||
std::vector<RemainingNodeData> remaining_nodes;
|
||||
remaining_nodes.reserve(number_of_nodes);
|
||||
for (auto node : util::irange<NodeID>(0, number_of_nodes))
|
||||
{
|
||||
if (node_data.is_core[node])
|
||||
{
|
||||
if (node_data.contractable[node])
|
||||
{
|
||||
remaining_nodes.emplace_back(node, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
node_data.priorities[node] =
|
||||
std::numeric_limits<ContractorNodeData::NodePriority>::max();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
node_data.priorities[node] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (!use_cached_node_priorities)
|
||||
{
|
||||
util::UnbufferedLog log;
|
||||
log << "initializing node priorities...";
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(0, number_of_nodes, PQGrainSize),
|
||||
[&node_data, &graph, &thread_data_list](const tbb::blocked_range<NodeID> &range) {
|
||||
ContractorThreadData *data = thread_data_list.GetThreadData();
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
{
|
||||
node_data.priorities[x] = EvaluateNodePriority(
|
||||
SimulateNodeContraction(data, graph, x, node_data.weights),
|
||||
node_data.depths[x]);
|
||||
}
|
||||
});
|
||||
tbb::parallel_for(tbb::blocked_range<std::size_t>(0, remaining_nodes.size(), PQGrainSize),
|
||||
[&](const auto &range) {
|
||||
ContractorThreadData *data = thread_data_list.GetThreadData();
|
||||
for (auto x = range.begin(), end = range.end(); x != end; ++x)
|
||||
{
|
||||
auto node = remaining_nodes[x].id;
|
||||
BOOST_ASSERT(node_data.contractable[node]);
|
||||
node_data.priorities[node] = EvaluateNodePriority(
|
||||
SimulateNodeContraction(data, graph, node, node_data.weights),
|
||||
node_data.depths[node]);
|
||||
}
|
||||
});
|
||||
log << " ok.";
|
||||
}
|
||||
|
||||
util::Log() << "preprocessing " << number_of_nodes << " nodes...";
|
||||
auto number_of_core_nodes = std::max<std::size_t>(0, (1 - core_factor) * number_of_nodes);
|
||||
auto number_of_nodes_to_contract = remaining_nodes.size() - number_of_core_nodes;
|
||||
util::Log() << "preprocessing " << number_of_nodes_to_contract << " ("
|
||||
<< (number_of_nodes_to_contract / (float)number_of_nodes * 100.) << "%) nodes...";
|
||||
|
||||
util::UnbufferedLog log;
|
||||
util::Percent p(log, number_of_nodes);
|
||||
util::Percent p(log, remaining_nodes.size());
|
||||
|
||||
const util::XORFastHash<> hash;
|
||||
|
||||
unsigned current_level = 0;
|
||||
std::size_t next_renumbering = number_of_nodes * 0.65 * core_factor;
|
||||
while (remaining_nodes.size() > 1 &&
|
||||
number_of_contracted_nodes < static_cast<NodeID>(number_of_nodes * core_factor))
|
||||
std::size_t next_renumbering = number_of_nodes * 0.35;
|
||||
while (remaining_nodes.size() > number_of_core_nodes)
|
||||
{
|
||||
if (number_of_contracted_nodes > next_renumbering)
|
||||
if (remaining_nodes.size() < next_renumbering)
|
||||
{
|
||||
RenumberData(remaining_nodes, new_to_old_node_id, node_data, graph);
|
||||
log << "[renumbered]";
|
||||
// only one renumbering for now
|
||||
next_renumbering = number_of_nodes;
|
||||
next_renumbering = 0;
|
||||
}
|
||||
|
||||
tbb::parallel_for(
|
||||
@ -635,8 +660,8 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
for (auto i = range.begin(), end = range.end(); i != end; ++i)
|
||||
{
|
||||
const NodeID node = remaining_nodes[i].id;
|
||||
remaining_nodes[i].is_independent =
|
||||
IsNodeIndependent(hash, node_data.priorities, graph, data, node);
|
||||
remaining_nodes[i].is_independent = IsNodeIndependent(
|
||||
hash, node_data.priorities, new_to_old_node_id, graph, data, node);
|
||||
}
|
||||
});
|
||||
|
||||
@ -649,21 +674,6 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
std::distance(remaining_nodes.begin(), begin_independent_nodes);
|
||||
auto end_independent_nodes_idx = remaining_nodes.size();
|
||||
|
||||
if (!use_cached_node_priorities)
|
||||
{
|
||||
// write out contraction level
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(
|
||||
begin_independent_nodes_idx, end_independent_nodes_idx, ContractGrainSize),
|
||||
[&](const auto &range) {
|
||||
for (auto position = range.begin(), end = range.end(); position != end;
|
||||
++position)
|
||||
{
|
||||
node_data.levels[remaining_nodes[position].id] = current_level;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// contract independent nodes
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(
|
||||
@ -673,11 +683,17 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
for (auto position = range.begin(), end = range.end(); position != end; ++position)
|
||||
{
|
||||
const NodeID node = remaining_nodes[position].id;
|
||||
node_data.is_core[node] = false;
|
||||
ContractNode(data, graph, node, node_data.weights);
|
||||
}
|
||||
});
|
||||
|
||||
// core flags need to be set in serial since vector<bool> is not thread safe
|
||||
for (auto position :
|
||||
util::irange<std::size_t>(begin_independent_nodes_idx, end_independent_nodes_idx))
|
||||
{
|
||||
node_data.is_core[remaining_nodes[position].id] = false;
|
||||
}
|
||||
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(
|
||||
begin_independent_nodes_idx, end_independent_nodes_idx, DeleteGrainSize),
|
||||
@ -691,12 +707,10 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
});
|
||||
|
||||
// make sure we really sort each block
|
||||
tbb::parallel_for(thread_data_list.data.range(),
|
||||
[&](const auto &range) {
|
||||
for (auto &data : range)
|
||||
tbb::parallel_sort(data->inserted_edges.begin(),
|
||||
data->inserted_edges.end());
|
||||
});
|
||||
tbb::parallel_for(thread_data_list.data.range(), [&](const auto &range) {
|
||||
for (auto &data : range)
|
||||
tbb::parallel_sort(data->inserted_edges.begin(), data->inserted_edges.end());
|
||||
});
|
||||
|
||||
// insert new edges
|
||||
for (auto &data : thread_data_list.data)
|
||||
@ -706,7 +720,7 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
const EdgeID current_edge_ID = graph.FindEdge(edge.source, edge.target);
|
||||
if (current_edge_ID != SPECIAL_EDGEID)
|
||||
{
|
||||
ContractorGraph::EdgeData ¤t_data = graph.GetEdgeData(current_edge_ID);
|
||||
auto ¤t_data = graph.GetEdgeData(current_edge_ID);
|
||||
if (current_data.shortcut && edge.data.forward == current_data.forward &&
|
||||
edge.data.backward == current_data.backward)
|
||||
{
|
||||
@ -724,22 +738,17 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
data->inserted_edges.clear();
|
||||
}
|
||||
|
||||
if (!use_cached_node_priorities)
|
||||
{
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(
|
||||
begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize),
|
||||
[&](
|
||||
const auto &range) {
|
||||
ContractorThreadData *data = thread_data_list.GetThreadData();
|
||||
for (auto position = range.begin(), end = range.end(); position != end;
|
||||
++position)
|
||||
{
|
||||
NodeID node = remaining_nodes[position].id;
|
||||
UpdateNodeNeighbours(node_data, data, graph, node);
|
||||
}
|
||||
});
|
||||
}
|
||||
tbb::parallel_for(
|
||||
tbb::blocked_range<NodeID>(
|
||||
begin_independent_nodes_idx, end_independent_nodes_idx, NeighboursGrainSize),
|
||||
[&](const auto &range) {
|
||||
ContractorThreadData *data = thread_data_list.GetThreadData();
|
||||
for (auto position = range.begin(), end = range.end(); position != end; ++position)
|
||||
{
|
||||
NodeID node = remaining_nodes[position].id;
|
||||
UpdateNodeNeighbours(node_data, data, graph, node);
|
||||
}
|
||||
});
|
||||
|
||||
// remove contracted nodes from the pool
|
||||
BOOST_ASSERT(end_independent_nodes_idx - begin_independent_nodes_idx > 0);
|
||||
@ -749,21 +758,11 @@ LevelAndCore contractGraph(ContractorGraph &graph,
|
||||
p.PrintStatus(number_of_contracted_nodes);
|
||||
++current_level;
|
||||
}
|
||||
log << "\n";
|
||||
|
||||
util::Log() << "[core] " << remaining_nodes.size() << " nodes " << graph.GetNumberOfEdges()
|
||||
<< " edges.";
|
||||
|
||||
node_data.Renumber(new_to_old_node_id);
|
||||
RenumberGraph(graph, new_to_old_node_id);
|
||||
|
||||
if (remaining_nodes.size() <= 2)
|
||||
{
|
||||
// in this case we don't need core markers since we fully contracted the graph
|
||||
node_data.is_core.clear();
|
||||
}
|
||||
|
||||
return LevelAndCore {std::move(node_data.levels), std::move(node_data.is_core)};
|
||||
return std::move(node_data.is_core);
|
||||
}
|
||||
|
||||
} // namespace contractor
|
||||
|
@ -14,6 +14,7 @@
|
||||
|
||||
#include "updater/updater.hpp"
|
||||
|
||||
#include "util/exclude_flag.hpp"
|
||||
#include "util/log.hpp"
|
||||
#include "util/timing_util.hpp"
|
||||
|
||||
@ -94,27 +95,6 @@ auto LoadAndUpdateEdgeExpandedGraph(const CustomizationConfig &config,
|
||||
return edge_based_graph;
|
||||
}
|
||||
|
||||
std::vector<std::vector<bool>>
|
||||
excludeFlagsToNodeFilter(const MultiLevelEdgeBasedGraph &graph,
|
||||
const extractor::EdgeBasedNodeDataContainer &node_data,
|
||||
const extractor::ProfileProperties &properties)
|
||||
{
|
||||
std::vector<std::vector<bool>> filters;
|
||||
for (auto mask : properties.excludable_classes)
|
||||
{
|
||||
if (mask != extractor::INAVLID_CLASS_DATA)
|
||||
{
|
||||
std::vector<bool> allowed_nodes(graph.GetNumberOfNodes(), true);
|
||||
for (const auto node : util::irange<NodeID>(0, graph.GetNumberOfNodes()))
|
||||
{
|
||||
allowed_nodes[node] = (node_data.GetClassData(node) & mask) == 0;
|
||||
}
|
||||
filters.push_back(std::move(allowed_nodes));
|
||||
}
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
|
||||
std::vector<CellMetric> customizeFilteredMetrics(const MultiLevelEdgeBasedGraph &graph,
|
||||
const partition::CellStorage &storage,
|
||||
const CellCustomizer &customizer,
|
||||
@ -157,7 +137,7 @@ int Customizer::Run(const CustomizationConfig &config)
|
||||
util::Log() << "Loading partition data took " << TIMER_SEC(loading_data) << " seconds";
|
||||
|
||||
TIMER_START(cell_customize);
|
||||
auto filter = excludeFlagsToNodeFilter(graph, node_data, properties);
|
||||
auto filter = util::excludeFlagsToNodeFilter(graph.GetNumberOfNodes(), node_data, properties);
|
||||
auto metrics = customizeFilteredMetrics(graph, storage, CellCustomizer{mlp}, filter);
|
||||
TIMER_STOP(cell_customize);
|
||||
util::Log() << "Cells customization took " << TIMER_SEC(cell_customize) << " seconds";
|
||||
|
@ -16,15 +16,15 @@ BisectionGraphView::BisectionGraphView(const BisectionGraph &bisection_graph_)
|
||||
}
|
||||
|
||||
BisectionGraphView::BisectionGraphView(const BisectionGraph &bisection_graph_,
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
: bisection_graph(bisection_graph_), begin(begin_), end(end_)
|
||||
{
|
||||
}
|
||||
|
||||
BisectionGraphView::BisectionGraphView(const BisectionGraphView &other_view,
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
: BisectionGraphView(other_view.bisection_graph, begin_, end_)
|
||||
{
|
||||
}
|
||||
@ -42,7 +42,10 @@ BisectionGraph::ConstNodeIterator BisectionGraphView::Begin() const { return beg
|
||||
|
||||
BisectionGraph::ConstNodeIterator BisectionGraphView::End() const { return end; }
|
||||
|
||||
const BisectionGraphView::NodeT &BisectionGraphView::Node(const NodeID nid) const { return *(begin + nid); }
|
||||
const BisectionGraphView::NodeT &BisectionGraphView::Node(const NodeID nid) const
|
||||
{
|
||||
return *(begin + nid);
|
||||
}
|
||||
|
||||
const BisectionGraphView::EdgeT &BisectionGraphView::Edge(const EdgeID eid) const
|
||||
{
|
||||
|
@ -18,7 +18,8 @@ namespace
|
||||
|
||||
const auto constexpr INVALID_LEVEL = std::numeric_limits<DinicMaxFlow::Level>::max();
|
||||
|
||||
auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set, const BisectionGraphView &view)
|
||||
auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set,
|
||||
const BisectionGraphView &view)
|
||||
{
|
||||
return [&](const NodeID nid) {
|
||||
const auto is_not_contained = [&set](const BisectionEdge &edge) {
|
||||
|
@ -33,7 +33,8 @@ struct SpatialOrder
|
||||
|
||||
// Creates a spatial order of n * sources "first" and n * sink "last" node ids.
|
||||
// The slope determines the spatial order for sorting node coordinates.
|
||||
SpatialOrder makeSpatialOrder(const BisectionGraphView &view, const double ratio, const double slope)
|
||||
SpatialOrder
|
||||
makeSpatialOrder(const BisectionGraphView &view, const double ratio, const double slope)
|
||||
{
|
||||
struct NodeWithCoordinate
|
||||
{
|
||||
@ -89,8 +90,10 @@ SpatialOrder makeSpatialOrder(const BisectionGraphView &view, const double ratio
|
||||
}
|
||||
|
||||
// Makes n cuts with different spatial orders and returns the best.
|
||||
DinicMaxFlow::MinCut
|
||||
bestMinCut(const BisectionGraphView &view, const std::size_t n, const double ratio, const double balance)
|
||||
DinicMaxFlow::MinCut bestMinCut(const BisectionGraphView &view,
|
||||
const std::size_t n,
|
||||
const double ratio,
|
||||
const double balance)
|
||||
{
|
||||
DinicMaxFlow::MinCut best;
|
||||
best.num_edges = -1;
|
||||
|
@ -149,7 +149,8 @@ RecursiveBisectionState::PrePartitionWithSCC(const std::size_t small_component_s
|
||||
}();
|
||||
|
||||
if (!has_small_component)
|
||||
views.push_back(BisectionGraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd()));
|
||||
views.push_back(
|
||||
BisectionGraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd()));
|
||||
|
||||
// apply scc as bisections, we need scc_level bits for this with scc_levels =
|
||||
// ceil(log_2(components))
|
||||
|
@ -268,12 +268,30 @@ void Storage::PopulateLayout(DataLayout &layout)
|
||||
reader.Skip<std::uint32_t>(1); // checksum
|
||||
auto num_nodes = reader.ReadVectorSize<contractor::QueryGraph::NodeArrayEntry>();
|
||||
auto num_edges = reader.ReadVectorSize<contractor::QueryGraph::EdgeArrayEntry>();
|
||||
auto num_metrics = reader.ReadElementCount64();
|
||||
|
||||
if (num_metrics > NUM_METRICS)
|
||||
{
|
||||
throw util::exception("Only " + std::to_string(NUM_METRICS) +
|
||||
" metrics are supported at the same time.");
|
||||
}
|
||||
|
||||
layout.SetBlockSize<unsigned>(DataLayout::HSGR_CHECKSUM, 1);
|
||||
layout.SetBlockSize<contractor::QueryGraph::NodeArrayEntry>(DataLayout::CH_GRAPH_NODE_LIST,
|
||||
num_nodes);
|
||||
layout.SetBlockSize<contractor::QueryGraph::EdgeArrayEntry>(DataLayout::CH_GRAPH_EDGE_LIST,
|
||||
num_edges);
|
||||
|
||||
for (const auto index : util::irange<std::size_t>(0, num_metrics))
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(
|
||||
static_cast<DataLayout::BlockID>(DataLayout::CH_EDGE_FILTER_0 + index), num_edges);
|
||||
}
|
||||
for (const auto index : util::irange<std::size_t>(num_metrics, NUM_METRICS))
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(
|
||||
static_cast<DataLayout::BlockID>(DataLayout::CH_EDGE_FILTER_0 + index), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -282,6 +300,11 @@ void Storage::PopulateLayout(DataLayout &layout)
|
||||
0);
|
||||
layout.SetBlockSize<contractor::QueryGraph::EdgeArrayEntry>(DataLayout::CH_GRAPH_EDGE_LIST,
|
||||
0);
|
||||
for (const auto index : util::irange<std::size_t>(0, NUM_METRICS))
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(
|
||||
static_cast<DataLayout::BlockID>(DataLayout::CH_EDGE_FILTER_0 + index), 0);
|
||||
}
|
||||
}
|
||||
|
||||
// load rsearch tree size
|
||||
@ -313,12 +336,36 @@ void Storage::PopulateLayout(DataLayout &layout)
|
||||
{
|
||||
io::FileReader core_marker_file(config.GetPath(".osrm.core"),
|
||||
io::FileReader::VerifyFingerprint);
|
||||
const auto num_metrics = core_marker_file.ReadElementCount64();
|
||||
if (num_metrics > NUM_METRICS)
|
||||
{
|
||||
throw util::exception("Only " + std::to_string(NUM_METRICS) +
|
||||
" metrics are supported at the same time.");
|
||||
}
|
||||
|
||||
const auto number_of_core_markers = core_marker_file.ReadElementCount64();
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER, number_of_core_markers);
|
||||
for (const auto index : util::irange<std::size_t>(0, num_metrics))
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(
|
||||
static_cast<DataLayout::BlockID>(DataLayout::CH_CORE_MARKER_0 + index),
|
||||
number_of_core_markers);
|
||||
}
|
||||
for (const auto index : util::irange<std::size_t>(num_metrics, NUM_METRICS))
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(
|
||||
static_cast<DataLayout::BlockID>(DataLayout::CH_CORE_MARKER_0 + index), 0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_0, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_1, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_2, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_3, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_4, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_5, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_6, 0);
|
||||
layout.SetBlockSize<unsigned>(DataLayout::CH_CORE_MARKER_7, 0);
|
||||
}
|
||||
|
||||
// load turn weight penalties
|
||||
@ -577,8 +624,19 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr)
|
||||
util::vector_view<contractor::QueryGraphView::EdgeArrayEntry> edge_list(
|
||||
graph_edges_ptr, layout.num_entries[storage::DataLayout::CH_GRAPH_EDGE_LIST]);
|
||||
|
||||
std::vector<util::vector_view<bool>> edge_filter;
|
||||
for (auto index : util::irange<std::size_t>(0, NUM_METRICS))
|
||||
{
|
||||
auto block_id =
|
||||
static_cast<DataLayout::BlockID>(storage::DataLayout::CH_EDGE_FILTER_0 + index);
|
||||
auto data_ptr = layout.GetBlockPtr<unsigned, true>(memory_ptr, block_id);
|
||||
auto num_entries = layout.num_entries[block_id];
|
||||
edge_filter.emplace_back(data_ptr, num_entries);
|
||||
}
|
||||
|
||||
contractor::QueryGraphView graph_view(std::move(node_list), std::move(edge_list));
|
||||
contractor::files::readGraph(config.GetPath(".osrm.hsgr"), *checksum, graph_view);
|
||||
contractor::files::readGraph(
|
||||
config.GetPath(".osrm.hsgr"), *checksum, graph_view, edge_filter);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -872,12 +930,17 @@ void Storage::PopulateData(const DataLayout &layout, char *memory_ptr)
|
||||
|
||||
if (boost::filesystem::exists(config.GetPath(".osrm.core")))
|
||||
{
|
||||
auto core_marker_ptr =
|
||||
layout.GetBlockPtr<unsigned, true>(memory_ptr, storage::DataLayout::CH_CORE_MARKER);
|
||||
util::vector_view<bool> is_core_node(
|
||||
core_marker_ptr, layout.num_entries[storage::DataLayout::CH_CORE_MARKER]);
|
||||
std::vector<util::vector_view<bool>> cores;
|
||||
for (auto index : util::irange<std::size_t>(0, NUM_METRICS))
|
||||
{
|
||||
auto block_id =
|
||||
static_cast<DataLayout::BlockID>(storage::DataLayout::CH_CORE_MARKER_0 + index);
|
||||
auto data_ptr = layout.GetBlockPtr<unsigned, true>(memory_ptr, block_id);
|
||||
auto num_entries = layout.num_entries[block_id];
|
||||
cores.emplace_back(data_ptr, num_entries);
|
||||
}
|
||||
|
||||
contractor::files::readCoreMarker(config.GetPath(".osrm.core"), is_core_node);
|
||||
contractor::files::readCoreMarker(config.GetPath(".osrm.core"), cores);
|
||||
}
|
||||
|
||||
// load profile properties
|
||||
|
@ -2,6 +2,10 @@ file(GLOB EngineTestsSources
|
||||
engine_tests.cpp
|
||||
engine/*.cpp)
|
||||
|
||||
file(GLOB ContractorTestsSources
|
||||
contractor_tests.cpp
|
||||
contractor/*.cpp)
|
||||
|
||||
file(GLOB ExtractorTestsSources
|
||||
extractor_tests.cpp
|
||||
extractor/*.cpp)
|
||||
@ -44,6 +48,11 @@ add_executable(engine-tests
|
||||
${EngineTestsSources}
|
||||
$<TARGET_OBJECTS:ENGINE> $<TARGET_OBJECTS:STORAGE> $<TARGET_OBJECTS:UTIL>)
|
||||
|
||||
add_executable(contractor-tests
|
||||
EXCLUDE_FROM_ALL
|
||||
${ContractorTestsSources}
|
||||
$<TARGET_OBJECTS:CONTRACTOR> $<TARGET_OBJECTS:UPDATER> $<TARGET_OBJECTS:UTIL>)
|
||||
|
||||
add_executable(extractor-tests
|
||||
EXCLUDE_FROM_ALL
|
||||
${ExtractorTestsSources}
|
||||
@ -104,6 +113,7 @@ target_compile_definitions(library-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_D
|
||||
target_compile_definitions(library-extract-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_DATA_DIR="${TEST_DATA_DIR}")
|
||||
target_compile_definitions(library-contract-tests PRIVATE COMPILE_DEFINITIONS OSRM_TEST_DATA_DIR="${TEST_DATA_DIR}")
|
||||
target_compile_definitions(updater-tests PRIVATE COMPILE_DEFINITIONS TEST_DATA_DIR="${UPDATER_TEST_DATA_DIR}")
|
||||
target_compile_definitions(contractor-tests PRIVATE COMPILE_DEFINITIONS TEST_DATA_DIR="${TEST_DATA_DIR}")
|
||||
|
||||
target_include_directories(engine-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(library-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
@ -113,6 +123,7 @@ target_include_directories(util-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(partition-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(customizer-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(updater-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
target_include_directories(contractor-tests PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(engine-tests ${ENGINE_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
target_link_libraries(extractor-tests ${EXTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
@ -124,6 +135,7 @@ target_link_libraries(library-extract-tests osrm_extract ${Boost_UNIT_TEST_FRAME
|
||||
target_link_libraries(library-contract-tests osrm_contract ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
target_link_libraries(server-tests osrm ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
target_link_libraries(util-tests ${UTIL_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
target_link_libraries(contractor-tests ${CONTRACTOR_LIBRARIES} ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY})
|
||||
|
||||
add_custom_target(tests
|
||||
DEPENDS engine-tests extractor-tests partition-tests updater-tests customizer-tests library-tests library-extract-tests server-tests util-tests)
|
||||
DEPENDS engine-tests extractor-tests contractor-tests partition-tests updater-tests customizer-tests library-tests library-extract-tests server-tests util-tests)
|
||||
|
109
unit_tests/contractor/contracted_edge_container.cpp
Normal file
109
unit_tests/contractor/contracted_edge_container.cpp
Normal file
@ -0,0 +1,109 @@
|
||||
#include "contractor/contracted_edge_container.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::contractor;
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace contractor
|
||||
{
|
||||
|
||||
bool operator!=(const QueryEdge &lhs, const QueryEdge &rhs) { return !(lhs == rhs); }
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const QueryEdge::EdgeData &data)
|
||||
{
|
||||
out << "{" << data.turn_id << ", " << data.shortcut << ", " << data.duration << ", "
|
||||
<< data.weight << ", " << data.forward << ", " << data.backward << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const QueryEdge &edge)
|
||||
{
|
||||
out << "{" << edge.source << ", " << edge.target << ", " << edge.data << "}";
|
||||
return out;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(contracted_edge_container)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(merge_edge_of_multiple_graph)
|
||||
{
|
||||
ContractedEdgeContainer container;
|
||||
|
||||
util::DeallocatingVector<QueryEdge> edges;
|
||||
edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{2, 0, {3, false, 3, 6, false, true}});
|
||||
edges.push_back(QueryEdge{2, 1, {4, false, 3, 6, false, true}});
|
||||
container.Insert(edges);
|
||||
|
||||
edges.clear();
|
||||
edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}});
|
||||
edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}});
|
||||
container.Merge(edges);
|
||||
|
||||
edges.clear();
|
||||
edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}});
|
||||
container.Merge(edges);
|
||||
|
||||
util::DeallocatingVector<QueryEdge> reference_edges;
|
||||
reference_edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{2, 0, {3, false, 3, 6, false, true}});
|
||||
reference_edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}});
|
||||
reference_edges.push_back(QueryEdge{2, 1, {4, false, 3, 6, false, true}});
|
||||
reference_edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}});
|
||||
CHECK_EQUAL_COLLECTIONS(container.edges, reference_edges);
|
||||
|
||||
auto filters = container.MakeEdgeFilters();
|
||||
BOOST_CHECK_EQUAL(filters.size(), 2);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filters[0], 7);
|
||||
CHECK_EQUAL_RANGE(filters[0], true, true, false, true, true, true, true);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filters[1], 7);
|
||||
CHECK_EQUAL_RANGE(filters[1], true, true, true, true, false, true, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(merge_edge_of_multiple_disjoint_graph)
|
||||
{
|
||||
ContractedEdgeContainer container;
|
||||
|
||||
util::DeallocatingVector<QueryEdge> edges;
|
||||
edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}});
|
||||
edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}});
|
||||
edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}});
|
||||
container.Merge(edges);
|
||||
|
||||
edges.clear();
|
||||
edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}});
|
||||
container.Merge(edges);
|
||||
|
||||
util::DeallocatingVector<QueryEdge> reference_edges;
|
||||
reference_edges.push_back(QueryEdge{0, 1, {1, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{1, 2, {2, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{1, 4, {5, false, 3, 6, true, false}});
|
||||
reference_edges.push_back(QueryEdge{2, 0, {3, false, 12, 24, false, true}});
|
||||
reference_edges.push_back(QueryEdge{2, 1, {4, false, 12, 24, false, true}});
|
||||
CHECK_EQUAL_COLLECTIONS(container.edges, reference_edges);
|
||||
|
||||
auto filters = container.MakeEdgeFilters();
|
||||
BOOST_CHECK_EQUAL(filters.size(), 2);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filters[0], 5);
|
||||
CHECK_EQUAL_RANGE(filters[0], true, true, false, true, true);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filters[1], 5);
|
||||
CHECK_EQUAL_RANGE(filters[1], false, false, true, false, false);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
156
unit_tests/contractor/graph_contractor.cpp
Normal file
156
unit_tests/contractor/graph_contractor.cpp
Normal file
@ -0,0 +1,156 @@
|
||||
#include "contractor/graph_contractor.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::contractor;
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(graph_contractor)
|
||||
|
||||
using TestEdge = std::tuple<unsigned, unsigned, int>;
|
||||
ContractorGraph makeGraph(const std::vector<TestEdge> &edges)
|
||||
{
|
||||
std::vector<ContractorEdge> input_edges;
|
||||
auto id = 0u;
|
||||
auto max_id = 0u;
|
||||
for (const auto &edge : edges)
|
||||
{
|
||||
unsigned start;
|
||||
unsigned target;
|
||||
int weight;
|
||||
std::tie(start, target, weight) = edge;
|
||||
max_id = std::max(std::max(start, target), max_id);
|
||||
input_edges.push_back(ContractorEdge{
|
||||
start, target, ContractorEdgeData{weight, weight * 2, id++, 0, false, true, false}});
|
||||
input_edges.push_back(ContractorEdge{
|
||||
target, start, ContractorEdgeData{weight, weight * 2, id++, 0, false, false, true}});
|
||||
}
|
||||
std::sort(input_edges.begin(), input_edges.end());
|
||||
|
||||
return ContractorGraph{max_id + 1, std::move(input_edges)};
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(contract_graph)
|
||||
{
|
||||
tbb::task_scheduler_init scheduler(1);
|
||||
/*
|
||||
* <--1--<
|
||||
* (0) >--3--> (1) >--3--> (3)
|
||||
* v ^ v ^
|
||||
* \ / \ |
|
||||
* 1 1 1 1
|
||||
* \ ^ \ /
|
||||
* >(5) > (4) >
|
||||
*/
|
||||
std::vector<TestEdge> edges = {TestEdge{0, 1, 3},
|
||||
TestEdge{0, 5, 1},
|
||||
TestEdge{1, 3, 3},
|
||||
TestEdge{1, 4, 1},
|
||||
TestEdge{3, 1, 1},
|
||||
TestEdge{4, 3, 1},
|
||||
TestEdge{5, 1, 1}};
|
||||
auto reference_graph = makeGraph(edges);
|
||||
|
||||
auto contracted_graph = reference_graph;
|
||||
std::vector<bool> core = contractGraph(contracted_graph, {1, 1, 1, 1, 1, 1});
|
||||
|
||||
// This contraction order is dependent on the priority caculation in the contractor
|
||||
// but deterministic for the same graph.
|
||||
CHECK_EQUAL_RANGE(core, false, false, false, false, false, false);
|
||||
|
||||
/* After contracting 0 and 2:
|
||||
*
|
||||
* Deltes edges 5 -> 0, 1 -> 0
|
||||
*
|
||||
* <--1--<
|
||||
* (0) ---3--> (1) >--3--> (3)
|
||||
* \ ^ v ^
|
||||
* \ / \ |
|
||||
* 1 1 1 1
|
||||
* \ ^ \ /
|
||||
* >(5) > (4) >
|
||||
*/
|
||||
reference_graph.DeleteEdgesTo(5, 0);
|
||||
reference_graph.DeleteEdgesTo(1, 0);
|
||||
|
||||
/* After contracting 5:
|
||||
*
|
||||
* Deletes edges 1 -> 5
|
||||
*
|
||||
* <--1--<
|
||||
* (0) ---3--> (1) >--3--> (3)
|
||||
* \ ^ v ^
|
||||
* \ / \ |
|
||||
* 1 1 1 1
|
||||
* \ / \ /
|
||||
* >(5) > (4) >
|
||||
*/
|
||||
reference_graph.DeleteEdgesTo(5, 0);
|
||||
reference_graph.DeleteEdgesTo(1, 0);
|
||||
|
||||
/* After contracting 3:
|
||||
*
|
||||
* Deletes edges 1 -> 3
|
||||
* Deletes edges 4 -> 3
|
||||
* Insert edge 4 -> 1
|
||||
*
|
||||
* <--1---
|
||||
* (0) ---3--> (1) >--3--- (3)
|
||||
* \ ^ v ^ |
|
||||
* \ / \ \ |
|
||||
* 1 1 1 2 1
|
||||
* \ / \ \ /
|
||||
* >(5) > (4) >
|
||||
*/
|
||||
reference_graph.DeleteEdgesTo(1, 3);
|
||||
reference_graph.DeleteEdgesTo(4, 3);
|
||||
// Insert shortcut
|
||||
reference_graph.InsertEdge(4, 1, {2, 4, 3, 0, true, true, false});
|
||||
|
||||
/* After contracting 4:
|
||||
*
|
||||
* Delete edges 1 -> 4
|
||||
*
|
||||
* <--1---
|
||||
* (0) ---3--> (1) >--3--- (3)
|
||||
* \ ^ v ^ |
|
||||
* \ / \ \ |
|
||||
* 1 1 1 2 1
|
||||
* \ / \ \ /
|
||||
* >(5) \ (4) >
|
||||
*/
|
||||
reference_graph.DeleteEdgesTo(1, 4);
|
||||
|
||||
/* After contracting 1:
|
||||
*
|
||||
* Delete no edges.
|
||||
*
|
||||
* <--1---
|
||||
* (0) ---3--> (1) >--3--- (3)
|
||||
* \ ^ v ^ |
|
||||
* \ / \ \ |
|
||||
* 1 1 1 2 1
|
||||
* \ / \ \ /
|
||||
* >(5) \ (4) >
|
||||
*/
|
||||
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(0), 2);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(0, 1) != SPECIAL_EDGEID);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(0, 5) != SPECIAL_EDGEID);
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(1), 0);
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(2), 0);
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(3), 3);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(3, 1) != SPECIAL_EDGEID);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(3, 4) != SPECIAL_EDGEID);
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(4), 2);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(4, 1) != SPECIAL_EDGEID);
|
||||
REQUIRE_SIZE_RANGE(contracted_graph.GetAdjacentEdgeRange(5), 1);
|
||||
BOOST_CHECK(contracted_graph.FindEdge(5, 1) != SPECIAL_EDGEID);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
7
unit_tests/contractor_tests.cpp
Normal file
7
unit_tests/contractor_tests.cpp
Normal file
@ -0,0 +1,7 @@
|
||||
#define BOOST_TEST_MODULE contractor tests
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
/*
|
||||
* This file will contain an automatically generated main function.
|
||||
*/
|
@ -280,11 +280,9 @@ class MockAlgorithmDataFacade<engine::datafacade::CH>
|
||||
unsigned GetOutDegree(const NodeID /* n */) const override { return 0; }
|
||||
NodeID GetTarget(const EdgeID /* e */) const override { return SPECIAL_NODEID; }
|
||||
const EdgeData &GetEdgeData(const EdgeID /* e */) const override { return foo; }
|
||||
EdgeID BeginEdges(const NodeID /* n */) const override { return SPECIAL_EDGEID; }
|
||||
EdgeID EndEdges(const NodeID /* n */) const override { return SPECIAL_EDGEID; }
|
||||
osrm::engine::datafacade::EdgeRange GetAdjacentEdgeRange(const NodeID /* node */) const override
|
||||
EdgeRange GetAdjacentEdgeRange(const NodeID /* node */) const override
|
||||
{
|
||||
return util::irange(static_cast<EdgeID>(0), static_cast<EdgeID>(0));
|
||||
return EdgeRange(static_cast<EdgeID>(0), static_cast<EdgeID>(0), {});
|
||||
}
|
||||
EdgeID FindEdge(const NodeID /* from */, const NodeID /* to */) const override
|
||||
{
|
||||
|
@ -1,6 +1,6 @@
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
#include "partition/graph_generator.hpp"
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "partition/recursive_bisection_state.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -1,5 +1,5 @@
|
||||
#include "partition/graph_generator.hpp"
|
||||
#include "partition/bisection_graph_view.hpp"
|
||||
#include "partition/graph_generator.hpp"
|
||||
#include "partition/recursive_bisection_state.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
|
@ -1,6 +1,8 @@
|
||||
#include "util/dynamic_graph.hpp"
|
||||
#include "util/typedefs.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
@ -120,4 +122,41 @@ BOOST_AUTO_TEST_CASE(renumber_test)
|
||||
BOOST_CHECK_EQUAL(simple_graph.GetEdgeData(eit).id, 2);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(filter_test)
|
||||
{
|
||||
/*
|
||||
* (0) -1-> (1)
|
||||
* ^ ^ ^
|
||||
* 2 5 1
|
||||
* | | |
|
||||
* (3) -3-> (4)
|
||||
* <-4-
|
||||
*/
|
||||
std::vector<TestInputEdge> input_edges = {TestInputEdge{0, 1, TestData{1}},
|
||||
TestInputEdge{3, 0, TestData{2}},
|
||||
TestInputEdge{3, 0, TestData{5}},
|
||||
TestInputEdge{3, 4, TestData{3}},
|
||||
TestInputEdge{4, 1, TestData{1}},
|
||||
TestInputEdge{4, 3, TestData{4}}};
|
||||
TestDynamicGraph simple_graph(5, input_edges);
|
||||
|
||||
// only keep 0, 1, 4 -> filter out all edges from/to 3
|
||||
auto filtered_simple_graph =
|
||||
simple_graph.Filter([](const NodeID node) { return node == 0 || node == 1 || node == 4; });
|
||||
|
||||
REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(0), 1);
|
||||
CHECK_EQUAL_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(0),
|
||||
filtered_simple_graph.FindEdge(0, 1));
|
||||
|
||||
REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(1), 0);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(2), 0);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(3), 0);
|
||||
|
||||
REQUIRE_SIZE_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(4), 1);
|
||||
CHECK_EQUAL_RANGE(filtered_simple_graph.GetAdjacentEdgeRange(4),
|
||||
filtered_simple_graph.FindEdge(4, 1));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
||||
|
38
unit_tests/util/filtered_integer_range.cpp
Normal file
38
unit_tests/util/filtered_integer_range.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "util/filtered_integer_range.hpp"
|
||||
|
||||
#include "../common/range_tools.hpp"
|
||||
|
||||
#include <boost/test/test_case_template.hpp>
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_AUTO_TEST_SUITE(bit_range_test)
|
||||
|
||||
using namespace osrm;
|
||||
using namespace osrm::util;
|
||||
|
||||
BOOST_AUTO_TEST_CASE(filtered_irange_test)
|
||||
{
|
||||
std::vector<bool> filter = {// 0 1 2 3 4 5
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false};
|
||||
|
||||
CHECK_EQUAL_RANGE(filtered_irange<std::uint8_t>(0, 2, filter), 0);
|
||||
CHECK_EQUAL_RANGE(filtered_irange<std::uint8_t>(1, 4, filter), 3);
|
||||
CHECK_EQUAL_RANGE(filtered_irange<std::uint8_t>(1, 5, filter), 3, 4);
|
||||
CHECK_EQUAL_RANGE(filtered_irange<std::uint8_t>(0, 6, filter), 0, 3, 4);
|
||||
|
||||
auto empty_1 = filtered_irange<std::uint8_t>(1, 3, filter);
|
||||
auto empty_2 = filtered_irange<std::uint8_t>(3, 3, filter);
|
||||
auto empty_3 = filtered_irange<std::uint8_t>(4, 4, filter);
|
||||
auto empty_4 = filtered_irange<std::uint8_t>(5, 5, filter);
|
||||
BOOST_CHECK(empty_1.begin() == empty_1.end());
|
||||
BOOST_CHECK(empty_2.begin() == empty_2.end());
|
||||
BOOST_CHECK(empty_3.begin() == empty_3.end());
|
||||
BOOST_CHECK(empty_4.begin() == empty_4.end());
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_SUITE_END()
|
Loading…
Reference in New Issue
Block a user