Optimise Flow Algorithm/Datastructures in use

This commit is contained in:
Moritz Kobitzsch 2017-01-26 10:34:01 +01:00 committed by Patrick Niklaus
parent dd60ae31ae
commit 786be6f570
23 changed files with 794 additions and 425 deletions

View File

@ -46,21 +46,21 @@ template <typename GraphT> class TarjanSCC
std::vector<unsigned> components_index; std::vector<unsigned> components_index;
std::vector<NodeID> component_size_vector; std::vector<NodeID> component_size_vector;
std::shared_ptr<const GraphT> m_graph; const GraphT &m_graph;
std::size_t size_one_counter; std::size_t size_one_counter;
public: public:
TarjanSCC(std::shared_ptr<const GraphT> graph) TarjanSCC(const GraphT &graph)
: components_index(graph->GetNumberOfNodes(), SPECIAL_NODEID), m_graph(graph), : components_index(graph.GetNumberOfNodes(), SPECIAL_NODEID), m_graph(graph),
size_one_counter(0) size_one_counter(0)
{ {
BOOST_ASSERT(m_graph->GetNumberOfNodes() > 0); BOOST_ASSERT(m_graph.GetNumberOfNodes() > 0);
} }
void Run() void Run()
{ {
TIMER_START(SCC_RUN); TIMER_START(SCC_RUN);
const NodeID max_node_id = m_graph->GetNumberOfNodes(); const NodeID max_node_id = m_graph.GetNumberOfNodes();
// The following is a hack to distinguish between stuff that happens // The following is a hack to distinguish between stuff that happens
// before the recursive call and stuff that happens after // before the recursive call and stuff that happens after
@ -105,9 +105,9 @@ template <typename GraphT> class TarjanSCC
tarjan_node_list[v].on_stack = true; tarjan_node_list[v].on_stack = true;
++index; ++index;
for (const auto current_edge : m_graph->GetAdjacentEdgeRange(v)) for (const auto current_edge : m_graph.GetAdjacentEdgeRange(v))
{ {
const auto vprime = m_graph->GetTarget(current_edge); const auto vprime = m_graph.GetTarget(current_edge);
if (SPECIAL_NODEID == tarjan_node_list[vprime].index) if (SPECIAL_NODEID == tarjan_node_list[vprime].index)
{ {

View File

@ -2,16 +2,16 @@
#define OSRM_BISECTION_GRAPH_HPP_ #define OSRM_BISECTION_GRAPH_HPP_
#include "util/coordinate.hpp" #include "util/coordinate.hpp"
#include "util/static_graph.hpp"
#include "util/typedefs.hpp" #include "util/typedefs.hpp"
#include "partition/partition_graph.hpp"
#include "extractor/edge_based_edge.hpp" #include "extractor/edge_based_edge.hpp"
#include <cstddef> #include <cstddef>
#include <algorithm> #include <algorithm>
#include <iterator> #include <iterator>
#include <tuple>
#include <utility> #include <utility>
namespace osrm namespace osrm
@ -23,77 +23,77 @@ namespace partition
// The coordinate will be used in the partitioning step. // The coordinate will be used in the partitioning step.
struct BisectionNode struct BisectionNode
{ {
// StaticGraph Node requirement (see static graph traits): .first_edge BisectionNode(util::Coordinate coordinate_ = {util::FloatLongitude{0}, util::FloatLatitude{0}},
std::size_t first_edge; const NodeID original_id_ = SPECIAL_NODEID)
: coordinate(std::move(coordinate_)), original_id(original_id_)
{
}
// the coordinate the node is located at
util::Coordinate coordinate; util::Coordinate coordinate;
// the node id to access the bisection result
NodeID original_id;
}; };
// Graph edge and data for Max-Flow Min-Cut augmentation. // Graph edge and data for Max-Flow Min-Cut augmentation.
struct BisectionEdge struct BisectionEdge
{ {
BisectionEdge(const NodeID target_ = SPECIAL_NODEID) : target(target_) {}
// StaticGraph Edge requirement (see static graph traits): .target, .data // StaticGraph Edge requirement (see static graph traits): .target, .data
NodeID target; NodeID target;
// TODO: add data for augmentation here. In case we want to keep it completely external, the
// static graph can be modified to no longer require a .data member by SFINAE-ing out features
// based on the available compile time traits.
std::int32_t data;
}; };
// The graph layout we use as a basis for partitioning. // The graph layout we use as a basis for partitioning.
using BisectionGraph = util::FlexibleStaticGraph<BisectionNode, BisectionEdge>; using RemappableGraphNode = NodeEntryWrapper<BisectionNode>;
using BisectionInputEdge = GraphConstructionWrapper<BisectionEdge>;
using BisectionGraph = RemappableGraph<RemappableGraphNode, BisectionEdge>;
template <typename RandomIt> void sortBySourceThenTarget(RandomIt first, RandomIt last) inline BisectionGraph makeBisectionGraph(const std::vector<util::Coordinate> &coordinates,
const std::vector<BisectionInputEdge> &edges)
{ {
std::sort(first, last, [](const auto &lhs, const auto &rhs) { std::vector<BisectionGraph::NodeT> result_nodes;
return std::tie(lhs.source, lhs.target) < std::tie(rhs.source, rhs.target); result_nodes.reserve(coordinates.size());
}); std::vector<BisectionGraph::EdgeT> result_edges;
} result_edges.reserve(edges.size());
template <typename InputEdge>
std::vector<BisectionNode> computeNodes(const std::vector<util::Coordinate> &coordinates,
const std::vector<InputEdge> &edges)
{
std::vector<BisectionNode> result;
result.reserve(coordinates.size() + 1 /*sentinel*/);
// find the end of edges that belong to node_id // find the end of edges that belong to node_id
const auto advance_edge_itr = [&edges](const std::size_t node_id, auto edge_itr) { const auto advance_edge_itr = [&edges, &result_edges](const std::size_t node_id,
auto edge_itr) {
while (edge_itr != edges.end() && edge_itr->source == node_id) while (edge_itr != edges.end() && edge_itr->source == node_id)
{
result_edges.push_back(edge_itr->Reduce());
++edge_itr; ++edge_itr;
}
return edge_itr; return edge_itr;
}; };
// create a bisection node, requires the ID of the node as well as the lower bound to its edges // create a bisection node, requires the ID of the node as well as the lower bound to its edges
const auto make_bisection_node = [&edges, &coordinates](const std::size_t node_id, const auto make_bisection_node = [&edges, &coordinates](const std::size_t node_id,
const auto edge_itr) -> BisectionNode { const auto edge_itr) {
return {static_cast<std::size_t>(std::distance(edges.begin(), edge_itr)), std::size_t range_begin = std::distance(edges.begin(), edge_itr);
coordinates[node_id]}; return BisectionGraph::NodeT(range_begin, range_begin, coordinates[node_id], node_id);
}; };
auto edge_itr = edges.begin(); auto edge_itr = edges.begin();
for (std::size_t node_id = 0; node_id < coordinates.size(); ++node_id) for (std::size_t node_id = 0; node_id < coordinates.size(); ++node_id)
{ {
result.emplace_back(make_bisection_node(node_id,edge_itr)); result_nodes.emplace_back(make_bisection_node(node_id, edge_itr));
edge_itr = advance_edge_itr(node_id,edge_itr); edge_itr = advance_edge_itr(node_id, edge_itr);
result_nodes.back().edges_end = std::distance(edges.begin(), edge_itr);
} }
auto null_island = util::Coordinate(util::FloatLongitude{0.0}, util::FloatLatitude{0.0}); return BisectionGraph(std::move(result_nodes), std::move(result_edges));
auto sentinel = BisectionNode{edges.size(), std::move(null_island)};
result.emplace_back(std::move(sentinel));
return result;
} }
template <typename InputEdge> template <typename InputEdge>
std::vector<BisectionEdge> adaptToBisectionEdge(std::vector<InputEdge> edges) std::vector<BisectionInputEdge> adaptToBisectionEdge(std::vector<InputEdge> edges)
{ {
std::vector<BisectionEdge> result(edges.size()); std::vector<BisectionInputEdge> result;
result.reserve(edges.size());
std::transform(begin(edges), end(edges), begin(result), [](const auto &edge) { std::transform(begin(edges), end(edges), std::back_inserter(result), [](const auto &edge) {
return BisectionEdge{edge.target, 1}; return BisectionInputEdge{edge.source, edge.target};
}); });
return result; return result;

View File

@ -31,36 +31,42 @@ namespace partition
class DinicMaxFlow class DinicMaxFlow
{ {
public: public:
using PartitionResult = struct using Level = std::uint32_t;
using MinCut = struct
{ {
std::size_t num_nodes_source;
std::size_t num_edges; std::size_t num_edges;
std::vector<bool> flags; std::vector<bool> flags;
}; };
using SourceSinkNodes = std::set<NodeID>; using SourceSinkNodes = std::unordered_set<NodeID>;
using LevelGraph = std::unordered_map<NodeID, std::uint32_t>; using LevelGraph = std::vector<Level>;
using FlowEdges = std::unordered_set<std::pair<NodeID, NodeID>>; using FlowEdges = std::vector<std::set<NodeID>>;
PartitionResult operator()(const GraphView &view, MinCut operator()(const GraphView &view,
const SourceSinkNodes &sink_nodes, const SourceSinkNodes &sink_nodes,
const SourceSinkNodes &source_nodes) const; const SourceSinkNodes &source_nodes) const;
private: private:
LevelGraph ComputeLevelGraph(const GraphView &view, LevelGraph ComputeLevelGraph(const GraphView &view,
const SourceSinkNodes &source_nodes, const std::vector<NodeID> &border_source_nodes,
const FlowEdges &flow) const;
void AugmentFlow(FlowEdges &flow,
const GraphView &view,
const SourceSinkNodes &source_nodes, const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes, const SourceSinkNodes &sink_nodes,
const LevelGraph &levels) const; const FlowEdges &flow) const;
bool findPath(const NodeID from, std::uint32_t BlockingFlow(FlowEdges &flow,
std::vector<NodeID> &path, LevelGraph &levels,
const GraphView &view,
const SourceSinkNodes &source_nodes,
const std::vector<NodeID> &border_sink_nodes) const;
std::vector<NodeID> GetAugmentingPath(LevelGraph &levels,
const NodeID from,
const GraphView &view, const GraphView &view,
const LevelGraph &levels,
const FlowEdges &flow, const FlowEdges &flow,
const SourceSinkNodes &sink_nodes) const; const SourceSinkNodes &sink_nodes) const;
// Builds an actual cut result from a level graph
MinCut MakeCut(const GraphView &view, const LevelGraph &levels) const;
}; };
} // namespace partition } // namespace partition

View File

@ -8,88 +8,50 @@
#include <boost/iterator/iterator_facade.hpp> #include <boost/iterator/iterator_facade.hpp>
#include <cstddef> #include <cstddef>
#include <cstdint>
namespace osrm namespace osrm
{ {
namespace partition namespace partition
{ {
// Predicate for EdgeIDs checking their partition ids for equality.
// Used in filter iterator below to discard edges in different partitions.
struct HasSamePartitionID
{
HasSamePartitionID(const RecursiveBisectionState::BisectionID bisection_id,
const BisectionGraph &bisection_graph,
const RecursiveBisectionState &recursive_bisection_state);
bool operator()(const EdgeID eid) const;
private:
const RecursiveBisectionState::BisectionID bisection_id;
const BisectionGraph &bisection_graph;
const RecursiveBisectionState &recursive_bisection_state;
};
// Random Access Iterator on top of contiguous integral EdgeIDs
class EdgeIDIterator : public boost::iterator_facade<EdgeIDIterator,
EdgeID const,
boost::random_access_traversal_tag>
{
public:
EdgeIDIterator() : position(SPECIAL_EDGEID) {}
explicit EdgeIDIterator(EdgeID position_) : position(position_) {}
private:
friend class boost::iterator_core_access;
// Implements the facade's core operations required for random access iterators:
// http://www.boost.org/doc/libs/1_63_0/libs/iterator/doc/iterator_facade.html#core-operations
void increment() { ++position; }
void decrement() { --position; }
void advance(difference_type offset) { position += offset; }
bool equal(const EdgeIDIterator &other) const { return position == other.position; }
reference dereference() const { return position; }
difference_type distance_to(const EdgeIDIterator &other) const
{
return static_cast<difference_type>(other.position - position);
}
value_type position;
};
// Non-owning immutable sub-graph view into a base graph. // Non-owning immutable sub-graph view into a base graph.
// The part of the graph to select is determined by the recursive bisection state. // The part of the graph to select is determined by the recursive bisection state.
class GraphView class GraphView
{ {
public: public:
using EdgeIterator = boost::filter_iterator<HasSamePartitionID, EdgeIDIterator>;
GraphView(const BisectionGraph &graph, GraphView(const BisectionGraph &graph,
const RecursiveBisectionState &bisection_state, const BisectionGraph::ConstNodeIterator begin,
const RecursiveBisectionState::IDIterator begin, const BisectionGraph::ConstNodeIterator end);
const RecursiveBisectionState::IDIterator end);
GraphView(const BisectionGraph &graph);
// Number of nodes _in this sub-graph. // Number of nodes _in this sub-graph.
std::size_t NumberOfNodes() const; std::size_t NumberOfNodes() const;
RecursiveBisectionState::IDIterator Begin() const; BisectionGraph::ConstNodeIterator Begin() const;
RecursiveBisectionState::IDIterator End() const; BisectionGraph::ConstNodeIterator End() const;
EdgeIterator EdgeBegin(const NodeID nid) const;
EdgeIterator EdgeEnd(const NodeID nid) const;
NodeID GetTarget(const EdgeID eid) const;
const BisectionNode &GetNode(const NodeID nid) const; const BisectionNode &GetNode(const NodeID nid) const;
const BisectionEdge &GetEdge(const EdgeID eid) const; const BisectionEdge &GetEdge(const EdgeID eid) const;
NodeID GetID(const BisectionGraph::NodeT &node) const;
inline auto Edges(const NodeID nid) const { return bisection_graph.Edges(*(begin + nid)); }
inline auto BeginEdges(const NodeID nid) const
{
return bisection_graph.BeginEdges(*(begin + nid));
}
inline auto EndEdges(const NodeID nid) const
{
return bisection_graph.EndEdges(*(begin + nid));
}
private: private:
const BisectionGraph &bisection_graph; const BisectionGraph &bisection_graph;
const RecursiveBisectionState &bisection_state;
const RecursiveBisectionState::IDIterator begin; const BisectionGraph::ConstNodeIterator begin;
const RecursiveBisectionState::IDIterator end; const BisectionGraph::ConstNodeIterator end;
}; };
} // namespace partition } // namespace partition

View File

@ -1,6 +1,7 @@
#ifndef OSRM_PARTITION_INERTIAL_FLOW_HPP_ #ifndef OSRM_PARTITION_INERTIAL_FLOW_HPP_
#define OSRM_PARTITION_INERTIAL_FLOW_HPP_ #define OSRM_PARTITION_INERTIAL_FLOW_HPP_
#include "partition/dinic_max_flow.hpp"
#include "partition/graph_view.hpp" #include "partition/graph_view.hpp"
#include <unordered_set> #include <unordered_set>
@ -16,7 +17,9 @@ class InertialFlow
public: public:
InertialFlow(const GraphView &view); InertialFlow(const GraphView &view);
std::vector<bool> ComputePartition(const double balance, const double source_sink_rate); DinicMaxFlow::MinCut ComputePartition(const std::size_t num_slopes,
const double balance,
const double source_sink_rate);
private: private:
// Spatially ordered sources and sink ids. // Spatially ordered sources and sink ids.
@ -32,7 +35,7 @@ class InertialFlow
SpatialOrder MakeSpatialOrder(double ratio, double slope) const; SpatialOrder MakeSpatialOrder(double ratio, double slope) const;
// Makes n cuts with different spatial orders and returns the best. // Makes n cuts with different spatial orders and returns the best.
MinCut bestMinCut(std::size_t n, double ratio) const; DinicMaxFlow::MinCut BestMinCut(std::size_t n, double ratio) const;
// The subgraph to partition into two parts. // The subgraph to partition into two parts.
const GraphView &view; const GraphView &view;

View File

@ -0,0 +1,138 @@
#ifndef OSRM_PARTITION_GRAPH_HPP_
#define OSRM_PARTITION_GRAPH_HPP_
#include <algorithm>
#include <cstddef>
#include <functional>
#include <iterator>
#include <vector>
#include "util/typedefs.hpp"
#include <boost/range/iterator_range.hpp>
namespace osrm
{
namespace partition
{
// wrapper for nodes to augment with a tag storing first edge id
template <typename Base> class NodeEntryWrapper : public Base
{
public:
template <typename... Args>
NodeEntryWrapper(std::size_t edges_begin_, std::size_t edges_end_, Args &&... args)
: Base(std::forward<Args>(args)...), edges_begin(edges_begin_), edges_end(edges_end_)
{
}
std::size_t edges_begin;
std::size_t edges_end;
};
template <typename Base> class GraphConstructionWrapper : public Base
{
public:
template <typename... Args>
GraphConstructionWrapper(const NodeID source_, Args &&... args)
: Base(std::forward<Args>(args)...), source(source_)
{
}
NodeID source;
Base Reduce() const { return *this; }
};
template <typename RandomIt> void groupEdgesBySource(RandomIt first, RandomIt last)
{
std::sort(
first, last, [](const auto &lhs, const auto &rhs) { return lhs.source < rhs.source; });
}
template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph
{
public:
using NodeT = NodeEntryT;
using EdgeT = EdgeEntryT;
using NodeIterator = typename std::vector<NodeT>::iterator;
using ConstNodeIterator = typename std::vector<NodeT>::const_iterator;
using EdgeIterator = typename std::vector<EdgeT>::iterator;
using ConstEdgeIterator = typename std::vector<EdgeT>::const_iterator;
// Constructs an empty graph with a given number of nodes.
explicit RemappableGraph(std::vector<NodeT> nodes_, std::vector<EdgeT> edges_)
: nodes(std::move(nodes_)), edges(std::move(edges_))
{
}
unsigned NumberOfNodes() const { return nodes.size(); }
auto &Node(const NodeID nid) { return nodes[nid]; }
auto &Node(const NodeID nid) const { return nodes[nid]; }
auto &Edge(const EdgeID eid) { return edges[eid]; }
auto &Edge(const EdgeID eid) const { return edges[eid]; }
auto Edges(const NodeID nid)
{
return boost::make_iterator_range(edges.begin() + nodes[nid].edges_begin,
edges.begin() + nodes[nid].edges_end);
}
auto Edges(const NodeID nid) const
{
return boost::make_iterator_range(edges.begin() + nodes[nid].edges_begin,
edges.begin() + nodes[nid].edges_end);
}
auto Edges(const NodeT &node)
{
return boost::make_iterator_range(edges.begin() + node.edges_begin,
edges.begin() + node.edges_end);
}
auto Edges(const NodeT &node) const
{
return boost::make_iterator_range(edges.begin() + node.edges_begin,
edges.begin() + node.edges_end);
}
auto BeginEdges(const NodeID nid) const { return edges.begin() + nodes[nid].edges_begin; }
auto EndEdges(const NodeID nid) const { return edges.begin() + nodes[nid].edges_end; }
auto BeginEdges(const NodeT &node) const { return edges.begin() + node.edges_begin; }
auto EndEdges(const NodeT &node) const { return edges.begin() + node.edges_end; }
auto BeginEdges(const NodeT &node) { return edges.begin() + node.edges_begin; }
auto EndEdges(const NodeT &node) { return edges.begin() + node.edges_end; }
// iterate over all nodes
auto Nodes() { return boost::make_iterator_range(nodes.begin(), nodes.end()); }
auto Nodes() const { return boost::make_iterator_range(nodes.begin(), nodes.end()); }
NodeIterator Begin() { return nodes.begin(); }
NodeIterator End() { return nodes.end(); }
ConstNodeIterator CBegin() const { return nodes.cbegin(); }
ConstNodeIterator CEnd() const { return nodes.cend(); }
// removes the edges from the graph that return true for the filter, returns new end
template <typename FilterT> auto RemoveEdges(NodeT &node, FilterT filter)
{
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
// required since we are not on std++17 yet, otherwise we are missing an argument_type
const auto negate_filter = [&](const EdgeT &edge) { return !filter(edge); };
const auto center = std::stable_partition(BeginEdges(node), EndEdges(node), negate_filter);
const auto remaining_edges = std::distance(BeginEdges(node), center);
node.edges_end = node.edges_begin + remaining_edges;
return center;
};
protected:
std::vector<NodeT> nodes;
std::vector<EdgeT> edges;
};
} // namespace partition
} // namespace osrm
#endif // OSRM_PARTITION_GRAPH_HPP_

View File

@ -2,9 +2,11 @@
#define OSRM_PARTITION_RECURSIVE_BISECTION_HPP_ #define OSRM_PARTITION_RECURSIVE_BISECTION_HPP_
#include "partition/bisection_graph.hpp" #include "partition/bisection_graph.hpp"
#include "partition/graph_view.hpp"
#include "partition/recursive_bisection_state.hpp" #include "partition/recursive_bisection_state.hpp"
#include <cstddef> #include <cstddef>
#include <vector>
namespace osrm namespace osrm
{ {
@ -17,11 +19,15 @@ class RecursiveBisection
RecursiveBisection(std::size_t maximum_cell_size, RecursiveBisection(std::size_t maximum_cell_size,
double balance, double balance,
double boundary_factor, double boundary_factor,
const BisectionGraph &bisection_graph); BisectionGraph &bisection_graph);
private: private:
const BisectionGraph &bisection_graph; BisectionGraph &bisection_graph;
RecursiveBisectionState internal_state; RecursiveBisectionState internal_state;
// on larger graphs, SCCs give perfect cuts (think Amerika vs Europe)
// This function performs an initial pre-partitioning using these sccs.
std::vector<GraphView> FakeFirstPartitionWithSCC(const std::size_t small_component_size);
}; };
} // namespace partition } // namespace partition

View File

@ -2,6 +2,7 @@
#define OSRM_PARTITION_RECURSIVE_BISECTION_STATE_HPP_ #define OSRM_PARTITION_RECURSIVE_BISECTION_STATE_HPP_
#include <cstddef> #include <cstddef>
#include <cstdint>
#include <vector> #include <vector>
#include "partition/bisection_graph.hpp" #include "partition/bisection_graph.hpp"
@ -62,26 +63,22 @@ class RecursiveBisectionState
public: public:
// The ID in the partition array // The ID in the partition array
using BisectionID = std::uint32_t; using BisectionID = std::uint32_t;
using IDIterator = std::vector<NodeID>::const_iterator; using NodeIterator = BisectionGraph::ConstNodeIterator;
RecursiveBisectionState(const BisectionGraph &bisection_graph); RecursiveBisectionState(BisectionGraph &bisection_graph);
~RecursiveBisectionState(); ~RecursiveBisectionState();
BisectionID GetBisectionID(const NodeID nid) const; BisectionID GetBisectionID(const NodeID node) const;
// Bisects the node id array's sub-range based on the partition mask. // Bisects the node id array's sub-range based on the partition mask.
// Returns: partition point of the bisection: iterator to the second group's first element. // Returns: partition point of the bisection: iterator to the second group's first element.
IDIterator ApplyBisection(const IDIterator begin, NodeIterator ApplyBisection(NodeIterator begin,
const IDIterator end, const NodeIterator end,
const std::size_t depth,
const std::vector<bool> &partition); const std::vector<bool> &partition);
const IDIterator Begin() const;
const IDIterator End() const;
private: private:
const BisectionGraph &bisection_graph; BisectionGraph &bisection_graph;
std::vector<NodeID> id_array;
std::vector<BisectionID> bisection_ids; std::vector<BisectionID> bisection_ids;
}; };

View File

@ -20,7 +20,7 @@ void reorderFirstLast(RandomIt first, RandomIt last, std::size_t n, Comparator c
{ {
BOOST_ASSERT_MSG(n <= (last - first) / std::size_t{2}, "overlapping subranges not allowed"); BOOST_ASSERT_MSG(n <= (last - first) / std::size_t{2}, "overlapping subranges not allowed");
if (n == 0 or (last - first < 2)) if (n == 0 || (last - first < 2))
return; return;
// Reorder first n: guarantees that the predicate holds for the first elements. // Reorder first n: guarantees that the predicate holds for the first elements.

View File

@ -0,0 +1,29 @@
#ifndef OSRM_PARTITION_TARJAN_GRAPH_WRAPPER_HPP_
#define OSRM_PARTITION_TARJAN_GRAPH_WRAPPER_HPP_
#include "partition/bisection_graph.hpp"
#include "util/integer_range.hpp"
#include "util/typedefs.hpp"
namespace osrm
{
namespace partition
{
class TarjanGraphWrapper
{
public:
TarjanGraphWrapper(const BisectionGraph &bisection_graph);
std::size_t GetNumberOfNodes() const;
util::range<EdgeID> GetAdjacentEdgeRange(const NodeID nid) const;
NodeID GetTarget(const EdgeID eid) const;
protected:
const BisectionGraph &bisection_graph;
};
} // namespace partition
} // namespace osrm
#endif // OSRM_PARTITION_TARJAN_GRAPH_WRAPPER_HPP_

View File

@ -63,9 +63,9 @@ template <typename EdgeDataT> class SortableEdgeWithData
template <typename NodeT, typename EdgeT, bool UseSharedMemory = false> class FlexibleStaticGraph template <typename NodeT, typename EdgeT, bool UseSharedMemory = false> class FlexibleStaticGraph
{ {
static_assert(traits::HasFirstEdgeMember<NodeT>(), static_assert(traits::HasFirstEdgeMember<NodeT>::value,
"Model for compatible Node type requires .first_edge member attribute"); "Model for compatible Node type requires .first_edge member attribute");
static_assert(traits::HasDataAndTargetMember<EdgeT>(), static_assert(traits::HasDataAndTargetMember<EdgeT>::value,
"Model for compatible Edge type requires .data and .target member attribute"); "Model for compatible Edge type requires .data and .target member attribute");
public: public:

View File

@ -34,7 +34,7 @@ struct HasTargetMember<T, decltype((void)(sizeof(std::declval<T>().target) > 0))
// Static Graph requires edges to have a .target and .data member attribute // Static Graph requires edges to have a .target and .data member attribute
template <typename Edge> template <typename Edge>
struct HasDataAndTargetMember struct HasDataAndTargetMember
: std::integral_constant<bool, HasDataMember<Edge>() && HasTargetMember<Edge>()> : std::integral_constant<bool, HasDataMember<Edge>::value && HasTargetMember<Edge>::value>
{ {
}; };

View File

@ -7,7 +7,6 @@
#include "engine/trip/trip_nearest_neighbour.hpp" #include "engine/trip/trip_nearest_neighbour.hpp"
#include "util/dist_table_wrapper.hpp" // to access the dist table more easily #include "util/dist_table_wrapper.hpp" // to access the dist table more easily
#include "util/json_container.hpp" #include "util/json_container.hpp"
#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
#include <boost/assert.hpp> #include <boost/assert.hpp>

View File

@ -380,10 +380,9 @@ void Extractor::FindComponents(unsigned max_edge_id,
auto new_end = std::unique(edges.begin(), edges.end()); auto new_end = std::unique(edges.begin(), edges.end());
edges.resize(new_end - edges.begin()); edges.resize(new_end - edges.begin());
auto uncontractor_graph = std::make_shared<UncontractedGraph>(max_edge_id + 1, edges); auto uncontracted_graph = UncontractedGraph(max_edge_id + 1, edges);
TarjanSCC<UncontractedGraph> component_search( TarjanSCC<UncontractedGraph> component_search(uncontracted_graph);
std::const_pointer_cast<const UncontractedGraph>(uncontractor_graph));
component_search.Run(); component_search.Run();
for (auto &node : input_nodes) for (auto &node : input_nodes)

View File

@ -1,74 +1,167 @@
#include "partition/dinic_max_flow.hpp" #include "partition/dinic_max_flow.hpp"
#include <algorithm>
#include <limits>
#include <queue> #include <queue>
#include <set>
#include <stack>
namespace osrm namespace osrm
{ {
namespace partition namespace partition
{ {
DinicMaxFlow::PartitionResult DinicMaxFlow::operator()(const GraphView &view, namespace
{
const auto constexpr INVALID_LEVEL = std::numeric_limits<DinicMaxFlow::Level>::max();
} // end namespace
DinicMaxFlow::MinCut DinicMaxFlow::operator()(const GraphView &view,
const SourceSinkNodes &source_nodes, const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes) const const SourceSinkNodes &sink_nodes) const
{ {
FlowEdges flow; std::vector<NodeID> border_source_nodes;
border_source_nodes.reserve(0.01 * source_nodes.size());
for (auto node : source_nodes)
{
for (const auto &edge : view.Edges(node))
{
const auto target = edge.target;
if (0 == source_nodes.count(target))
{
border_source_nodes.push_back(node);
break;
}
}
}
std::vector<NodeID> border_sink_nodes;
border_sink_nodes.reserve(0.01 * sink_nodes.size());
for (auto node : sink_nodes)
{
for (const auto &edge : view.Edges(node))
{
const auto target = edge.target;
if (0 == sink_nodes.count(target))
{
border_sink_nodes.push_back(node);
break;
}
}
}
// edges in current flow that have capacity
// The graph (V,E) contains undirected edges for all (u,v) \in V x V. We describe the flow as a
// set of vertices (s,t) with flow set to `true`. Since flow can be either from `s` to `t` or
// from `t` to `s`, we can remove `(s,t)` from the flow, if we send flow back the first time,
// and insert `(t,s)` only if we send flow again.
FlowEdges flow(view.NumberOfNodes());
do do
{ {
const auto levels = ComputeLevelGraph(view, source_nodes, flow); std::cout << "." << std::flush;
auto levels = ComputeLevelGraph(view, border_source_nodes, source_nodes, sink_nodes, flow);
// check if the sink can be reached from the source // check if the sink can be reached from the source
const auto separated = const auto separated = std::find_if(border_sink_nodes.begin(),
std::find_if(sink_nodes.begin(), sink_nodes.end(), [&levels](const auto node) { border_sink_nodes.end(),
return levels.find(node) != levels.end(); [&levels, &view](const auto node) {
}) == sink_nodes.end(); return levels[node] != INVALID_LEVEL;
}) == border_sink_nodes.end();
// no further elements can be found if (!separated)
if (separated)
{ {
// all elements within `levels` are on the source side BlockingFlow(flow, levels, view, source_nodes, border_sink_nodes);
PartitionResult result(view.NumberOfNodes(), true); }
for (auto itr = view.Begin(); itr != view.End(); ++itr) else
{ {
if (levels.find(*itr) != levels.end()) // mark levels for all sources to not confuse make-cut
result[std::distance(view.Begin(), itr)] = false; for (auto s : source_nodes)
levels[s] = 0;
return MakeCut(view, levels);
} }
return result;
}
AugmentFlow(flow, view, source_nodes, sink_nodes, levels);
} while (true); } while (true);
} }
DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view, DinicMaxFlow::MinCut DinicMaxFlow::MakeCut(const GraphView &view, const LevelGraph &levels) const
{
const auto is_sink_side = [&view, &levels](const NodeID nid) {
return levels[nid] == INVALID_LEVEL;
};
// all elements within `levels` are on the source side
// This part should opt to find the most balanced cut, which is not necessarily the case right
// now
std::vector<bool> result(view.NumberOfNodes(), true);
std::size_t source_side_count = view.NumberOfNodes();
for (auto itr = view.Begin(); itr != view.End(); ++itr)
{
if (is_sink_side(std::distance(view.Begin(), itr)))
{
result[std::distance(view.Begin(), itr)] = false;
--source_side_count;
}
}
std::size_t num_edges = 0;
for (auto itr = view.Begin(); itr != view.End(); ++itr)
{
const auto nid = std::distance(view.Begin(), itr);
const auto sink_side = is_sink_side(nid);
for (const auto &edge : view.Edges(nid))
{
if (is_sink_side(edge.target) != sink_side)
{
++num_edges;
}
}
}
return {source_side_count, num_edges, std::move(result)};
}
DinicMaxFlow::LevelGraph
DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
const std::vector<NodeID> &border_source_nodes,
const SourceSinkNodes &source_nodes, const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes,
const FlowEdges &flow) const const FlowEdges &flow) const
{ {
LevelGraph levels; LevelGraph levels(view.NumberOfNodes(), INVALID_LEVEL);
std::queue<NodeID> level_queue; std::queue<NodeID> level_queue;
for (const auto node : source_nodes) for (const auto node_id : border_source_nodes)
{ {
levels[node] = 0; levels[node_id] = 0;
level_queue.push(node); level_queue.push(node_id);
for (const auto &edge : view.Edges(node_id))
{
const auto target = edge.target;
if (source_nodes.count(target))
{
levels[target] = 0;
}
}
} }
// check if there is flow present on an edge // check if there is flow present on an edge
const auto has_flow = [&](const NodeID from, const NodeID to) { const auto has_flow = [&](const NodeID from, const NodeID to) {
return flow.find(std::make_pair(from, to)) != flow.end(); return flow[from].find(to) != flow[from].end();
}; };
// perform a relaxation step in the BFS algorithm // perform a relaxation step in the BFS algorithm
const auto relax_node = [&](const NodeID node_id, const std::uint32_t level) { const auto relax_node = [&](const NodeID node_id, const Level level) {
for (auto itr = view.EdgeBegin(node_id); itr != view.EdgeEnd(node_id); ++itr) // don't relax sink nodes
{ if (sink_nodes.count(node_id))
const auto target = view.GetTarget(*itr); return;
for (const auto &edge : view.Edges(node_id))
{
const auto target = edge.target;
// don't relax edges with flow on them // don't relax edges with flow on them
if (has_flow(node_id, target)) if (has_flow(node_id, target))
continue; continue;
// don't go back, only follow edges to new nodes // don't go back, only follow edges to new nodes
if (levels.find(target) == levels.end()) if (levels[target] > level)
{ {
level_queue.push(target); level_queue.push(target);
levels[target] = level; levels[target] = level;
@ -77,7 +170,7 @@ DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
}; };
// compute the levels of level graph using BFS // compute the levels of level graph using BFS
for (std::uint32_t level = 1; !level_queue.empty(); ++level) for (Level level = 1; !level_queue.empty(); ++level)
{ {
// run through the current level // run through the current level
auto steps = level_queue.size(); auto steps = level_queue.size();
@ -91,86 +184,126 @@ DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
return levels; return levels;
} }
void DinicMaxFlow::AugmentFlow(FlowEdges &flow, std::uint32_t DinicMaxFlow::BlockingFlow(FlowEdges &flow,
LevelGraph &levels,
const GraphView &view, const GraphView &view,
const SourceSinkNodes &source_nodes, const SourceSinkNodes &source_nodes,
const SourceSinkNodes &sink_nodes, const std::vector<NodeID> &border_sink_nodes) const
const LevelGraph &levels) const
{ {
const auto has_flow = [&](const auto from, const auto to) { std::uint32_t flow_increase = 0;
return flow.find(std::make_pair(from, to)) != flow.end(); // augment the flow along a path in the level graph
}; const auto augment_flow = [&flow, &view](const std::vector<NodeID> &path) {
const auto augment_one = [&flow, &view](const NodeID from, const NodeID to) {
// find a path and augment the flow along its edges // check if there is flow in the opposite direction
// do a dfs on the level graph, augment all paths that are found auto existing_edge = flow[to].find(from);
const auto find_and_augment_path = [&](const NodeID source) { if (existing_edge != flow[to].end())
std::vector<NodeID> path;
auto has_path = findPath(source, path, view, levels, flow, sink_nodes);
if (has_path)
{ {
NodeID last = path.back(); // remove flow from reverse edges first
path.pop_back(); flow[to].erase(existing_edge);
// augment the flow graph with the flow
while (!path.empty())
{
flow.insert(std::make_pair(path.back(), last));
// update the residual capacities correctly
const auto reverse_flow = flow.find(std::make_pair(last, path.back()));
if (reverse_flow != flow.end())
flow.erase(reverse_flow);
last = path.back();
path.pop_back();
}
return true;
} }
else else
{ {
return false; // only add flow if no opposite flow exists
flow[from].insert(to);
} }
// for adjacent find
return false;
};
// augment all adjacent edges
std::adjacent_find(path.begin(), path.end(), augment_one);
}; };
// find and augment the blocking flow // find and augment the blocking flow
for (auto source : source_nodes)
std::vector<std::pair<std::uint32_t, NodeID>> reached_sinks;
for (auto sink : border_sink_nodes)
{ {
while (find_and_augment_path(source)) if (levels[sink] != INVALID_LEVEL)
{ /*augment*/ {
reached_sinks.push_back(std::make_pair(levels[sink], sink));
} }
} }
std::sort(reached_sinks.begin(), reached_sinks.end());
for (auto sink_itr = reached_sinks.begin(); sink_itr != reached_sinks.end();)
{
auto path = GetAugmentingPath(levels, sink_itr->second, view, flow, source_nodes);
if (!path.empty())
augment_flow(path);
else
++sink_itr;
}
return flow_increase;
} }
bool DinicMaxFlow::findPath(const NodeID node_id, std::vector<NodeID> DinicMaxFlow::GetAugmentingPath(LevelGraph &levels,
std::vector<NodeID> &path, const NodeID node_id,
const GraphView &view, const GraphView &view,
const LevelGraph &levels,
const FlowEdges &flow, const FlowEdges &flow,
const SourceSinkNodes &sink_nodes) const const SourceSinkNodes &source_nodes) const
{ {
path.push_back(node_id); std::vector<NodeID> path;
if (sink_nodes.find(node_id) != sink_nodes.end()) BOOST_ASSERT(source_nodes.find(node_id) == source_nodes.end());
return true;
const auto node_level = levels.find(node_id)->second; // Keeps the local state of the DFS in forms of the iterators
for (auto itr = view.EdgeBegin(node_id); itr != view.EdgeEnd(node_id); ++itr) using DFSState = struct
{ {
const auto target = view.GetTarget(*itr); BisectionGraph::ConstEdgeIterator edge_iterator;
const BisectionGraph::ConstEdgeIterator end_iterator;
};
// don't relax edges with flow on them std::stack<DFSState> dfs_stack;
if (flow.find(std::make_pair(node_id, target)) != flow.end()) DFSState initial_state = {view.BeginEdges(node_id), view.EndEdges(node_id)};
continue; dfs_stack.push(std::move(initial_state));
path.push_back(node_id);
// don't go back, only follow edges to new nodes while (!dfs_stack.empty())
const auto level = levels.find(target)->second; {
if( level != node_level + 1 ) // the dfs_stack and the path have to be kept in sync
continue; BOOST_ASSERT(dfs_stack.size() == path.size());
if( findPath(target,path,view,levels,flow,sink_nodes) ) while (dfs_stack.top().edge_iterator != dfs_stack.top().end_iterator)
return true; {
const auto target = dfs_stack.top().edge_iterator->target;
// look at every edge only once, so advance the state of the current node (last in
// path)
dfs_stack.top().edge_iterator++;
// check if the edge is valid
const auto has_capacity = flow[target].count(path.back()) == 0;
const auto descends_level_graph = levels[target] + 1 == levels[path.back()];
if (has_capacity && descends_level_graph)
{
// recurse
path.push_back(target);
// termination
if (source_nodes.find(target) != source_nodes.end())
{
std::reverse(path.begin(), path.end());
return path;
} }
// start next iteration
dfs_stack.push({view.BeginEdges(target), view.EndEdges(target)});
}
}
// backtrack - mark that there is no way to the target
levels[path.back()] = -1;
path.pop_back(); path.pop_back();
return false; dfs_stack.pop();
}
BOOST_ASSERT(path.empty());
return path;
} }
} // namespace partition } // namespace partition

View File

@ -8,77 +8,38 @@ namespace osrm
namespace partition namespace partition
{ {
HasSamePartitionID::HasSamePartitionID(const RecursiveBisectionState::BisectionID bisection_id_, GraphView::GraphView(const BisectionGraph &bisection_graph_)
const BisectionGraph &bisection_graph_, : bisection_graph(bisection_graph_), begin(bisection_graph.CBegin()),
const RecursiveBisectionState &recursive_bisection_state_) end(bisection_graph.CEnd())
: bisection_id(bisection_id_), bisection_graph(bisection_graph_),
recursive_bisection_state(recursive_bisection_state_)
{ {
} }
bool HasSamePartitionID::operator()(const EdgeID eid) const
{
return recursive_bisection_state.GetBisectionID(bisection_graph.GetTarget(eid)) == bisection_id;
}
GraphView::GraphView(const BisectionGraph &bisection_graph_, GraphView::GraphView(const BisectionGraph &bisection_graph_,
const RecursiveBisectionState &bisection_state_, const BisectionGraph::ConstNodeIterator begin_,
const RecursiveBisectionState::IDIterator begin_, const BisectionGraph::ConstNodeIterator end_)
const RecursiveBisectionState::IDIterator end_) : bisection_graph(bisection_graph_), begin(begin_), end(end_)
: bisection_graph(bisection_graph_), bisection_state(bisection_state_), begin(begin_), end(end_)
{ {
std::cout << "Graph\n";
for (auto itr = begin; itr != end; ++itr)
{
std::cout << "Node: " << *itr << std::endl;
for (auto eitr = EdgeBegin(*itr); eitr != EdgeEnd(*itr); ++eitr)
{
std::cout << "\t" << *eitr << " -> " << GetTarget(*eitr) << std::endl;
}
}
} }
RecursiveBisectionState::IDIterator GraphView::Begin() const { return begin; } NodeID GraphView::GetID(const BisectionGraph::NodeT &node) const
{
return static_cast<NodeID>(&node - &(*begin));
}
RecursiveBisectionState::IDIterator GraphView::End() const { return end; } BisectionGraph::ConstNodeIterator GraphView::Begin() const { return begin; }
BisectionGraph::ConstNodeIterator GraphView::End() const { return end; }
std::size_t GraphView::NumberOfNodes() const { return std::distance(begin, end); } std::size_t GraphView::NumberOfNodes() const { return std::distance(begin, end); }
GraphView::EdgeIterator GraphView::EdgeBegin(const NodeID nid) const
{
HasSamePartitionID predicate{
bisection_state.GetBisectionID(nid), bisection_graph, bisection_state};
EdgeIDIterator first{bisection_graph.BeginEdges(nid)};
EdgeIDIterator last{bisection_graph.EndEdges(nid)};
return boost::make_filter_iterator(predicate, first, last);
}
GraphView::EdgeIterator GraphView::EdgeEnd(const NodeID nid) const
{
HasSamePartitionID predicate{
bisection_state.GetBisectionID(nid), bisection_graph, bisection_state};
EdgeIDIterator last{bisection_graph.EndEdges(nid)};
return boost::make_filter_iterator(predicate, last, last);
}
NodeID GraphView::GetTarget(const EdgeID eid) const
{
return bisection_graph.GetTarget(eid);
}
const BisectionNode &GraphView::GetNode(const NodeID nid) const const BisectionNode &GraphView::GetNode(const NodeID nid) const
{ {
return bisection_graph.GetNode(nid); return bisection_graph.Node(nid);
} }
const BisectionEdge &GraphView::GetEdge(const EdgeID eid) const const BisectionEdge &GraphView::GetEdge(const EdgeID eid) const
{ {
return bisection_graph.GetEdge(eid); return bisection_graph.Edge(eid);
} }
} // namespace partition } // namespace partition

View File

@ -1,6 +1,5 @@
#include "partition/inertial_flow.hpp" #include "partition/inertial_flow.hpp"
#include "partition/bisection_graph.hpp" #include "partition/bisection_graph.hpp"
#include "partition/dinic_max_flow.hpp"
#include "partition/reorder_first_last.hpp" #include "partition/reorder_first_last.hpp"
#include <algorithm> #include <algorithm>
@ -9,6 +8,7 @@
#include <iterator> #include <iterator>
#include <mutex> #include <mutex>
#include <set> #include <set>
#include <tuple>
#include <utility> #include <utility>
#include <tbb/blocked_range.h> #include <tbb/blocked_range.h>
@ -21,17 +21,13 @@ namespace partition
InertialFlow::InertialFlow(const GraphView &view_) : view(view_) {} InertialFlow::InertialFlow(const GraphView &view_) : view(view_) {}
std::vector<bool> InertialFlow::ComputePartition(const double balance, DinicMaxFlow::MinCut InertialFlow::ComputePartition(const std::size_t num_slopes,
const double balance,
const double source_sink_rate) const double source_sink_rate)
{ {
auto cut = bestMinCut(10 /* should be taken from outside */, source_sink_rate); auto cut = BestMinCut(num_slopes, source_sink_rate);
std::cout << "Partition: "; return cut;
for (auto b : cut.flags)
std::cout << b;
std::cout << std::endl;
return cut.flags;
} }
InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio, InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
@ -53,8 +49,12 @@ InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
Embedding embedding; Embedding embedding;
embedding.reserve(view.NumberOfNodes()); embedding.reserve(view.NumberOfNodes());
std::transform(view.Begin(), view.End(), std::back_inserter(embedding), [&](const auto nid) { // adress of the very first node
return NodeWithCoordinate{nid, view.GetNode(nid).coordinate}; const auto node_zero = &(*view.Begin());
std::transform(view.Begin(), view.End(), std::back_inserter(embedding), [&](const auto &node) {
const auto node_id = static_cast<NodeID>(&node - node_zero);
return NodeWithCoordinate{node_id, node.coordinate};
}); });
const auto project = [slope](const auto &each) { const auto project = [slope](const auto &each) {
@ -86,14 +86,22 @@ InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
return order; return order;
} }
MinCut InertialFlow::bestMinCut(const std::size_t n, const double ratio) const DinicMaxFlow::MinCut InertialFlow::BestMinCut(const std::size_t n, const double ratio) const
{ {
auto base = MakeSpatialOrder(ratio, -1.); DinicMaxFlow::MinCut best;
auto best = DinicMaxFlow()(view, base.sources, base.sinks); best.num_edges = -1;
const auto get_balance = [this](const auto num_nodes_source) {
double ratio = static_cast<double>(view.NumberOfNodes() - num_nodes_source) /
static_cast<double>(num_nodes_source);
return std::abs(ratio - 1.0);
};
auto best_balance = 10000; // get_balance(best.num_nodes_source);
std::mutex lock; std::mutex lock;
tbb::blocked_range<std::size_t> range{1, n + 1}; tbb::blocked_range<std::size_t> range{0, n, 1};
tbb::parallel_for(range, [&, this](const auto &chunk) { tbb::parallel_for(range, [&, this](const auto &chunk) {
for (auto round = chunk.begin(), end = chunk.end(); round != end; ++round) for (auto round = chunk.begin(), end = chunk.end(); round != end; ++round)
@ -101,17 +109,24 @@ MinCut InertialFlow::bestMinCut(const std::size_t n, const double ratio) const
const auto slope = -1. + round * (2. / n); const auto slope = -1. + round * (2. / n);
auto order = this->MakeSpatialOrder(ratio, slope); auto order = this->MakeSpatialOrder(ratio, slope);
auto cut = Dinic(view, order.sources, order.sinks);
auto cut = DinicMaxFlow()(view, order.sources, order.sinks); auto cut = DinicMaxFlow()(view, order.sources, order.sinks);
auto cut_balance = get_balance(cut.num_nodes_source);
{ {
std::lock_guard<std::mutex> guard{lock}; std::lock_guard<std::mutex> guard{lock};
// Swap to keep the destruction of the old object outside of critical section. // Swap to keep the destruction of the old object outside of critical section.
if (cut.num_edges < best.num_edges) if (std::tie(cut.num_edges, cut_balance) < std::tie(best.num_edges, best_balance))
{
std::cout << "New Cut: " << cut.num_edges << " " << cut_balance << std::endl;
best_balance = cut_balance;
std::swap(best, cut); std::swap(best, cut);
} }
else
{
std::cout << "Bad Cut: " << cut.num_edges << " " << cut_balance << std::endl;
}
}
// cut gets destroyed here // cut gets destroyed here
} }
}); });

View File

@ -1,14 +1,16 @@
#include "partition/partitioner.hpp" #include "partition/partitioner.hpp"
#include "partition/bisection_graph.hpp" #include "partition/bisection_graph.hpp"
#include "partition/recursive_bisection.hpp" #include "partition/recursive_bisection.hpp"
// TODO remove after testing #include "util/log.hpp"
#include "util/coordinate.hpp"
#include <iterator> #include <iterator>
#include <vector> #include <vector>
#include <boost/assert.hpp>
#include <tbb/task_scheduler_init.h>
namespace osrm namespace osrm
{ {
namespace partition namespace partition
@ -16,61 +18,19 @@ namespace partition
int Partitioner::Run(const PartitionConfig &config) int Partitioner::Run(const PartitionConfig &config)
{ {
struct TestEdge const unsigned recommended_num_threads = tbb::task_scheduler_init::default_num_threads();
{ const auto number_of_threads = std::min(recommended_num_threads, config.requested_num_threads);
NodeID source; tbb::task_scheduler_init init(number_of_threads);
NodeID target;
};
std::vector<TestEdge> input_edges; auto compressed_node_based_graph =
LoadCompressedNodeBasedGraph(config.compressed_node_based_graph_path.string());
// 0 - 1 - 2 - 3 groupEdgesBySource(begin(compressed_node_based_graph.edges),
// | \ | | end(compressed_node_based_graph.edges));
// 4 - 5 - 6 - 7
input_edges.push_back({0, 1}); auto graph =
input_edges.push_back({0, 4}); makeBisectionGraph(compressed_node_based_graph.coordinates,
input_edges.push_back({0, 5}); adaptToBisectionEdge(std::move(compressed_node_based_graph.edges)));
input_edges.push_back({1, 0});
input_edges.push_back({1, 2});
input_edges.push_back({2, 1});
input_edges.push_back({2, 3});
input_edges.push_back({2, 6});
input_edges.push_back({3, 2});
input_edges.push_back({3, 7});
input_edges.push_back({4, 0});
input_edges.push_back({4, 5});
input_edges.push_back({5, 0});
input_edges.push_back({5, 4});
input_edges.push_back({5, 6});
input_edges.push_back({6, 2});
input_edges.push_back({6, 5});
input_edges.push_back({6, 7});
input_edges.push_back({7, 3});
input_edges.push_back({7, 6});
sortBySourceThenTarget(begin(input_edges), end(input_edges));
std::vector<util::Coordinate> coordinates;
for (std::size_t i = 0; i < 8; ++i)
{
coordinates.push_back(
{util::FloatLongitude{(i % 4) / 4.0}, util::FloatLatitude{(double)(i / 4)}});
}
// do the partitioning
std::vector<BisectionNode> nodes = computeNodes(coordinates, input_edges);
std::vector<BisectionEdge> edges = adaptToBisectionEdge(input_edges);
BisectionGraph graph(nodes, edges);
RecursiveBisection recursive_bisection(1024, 1.1, 0.25, graph); RecursiveBisection recursive_bisection(1024, 1.1, 0.25, graph);

View File

@ -4,6 +4,16 @@
#include "partition/graph_view.hpp" #include "partition/graph_view.hpp"
#include "partition/recursive_bisection_state.hpp" #include "partition/recursive_bisection_state.hpp"
#include "util/timing_util.hpp"
#include "util/geojson_debug_logger.hpp"
#include "util/geojson_debug_policies.hpp"
#include "extractor/tarjan_scc.hpp"
#include "partition/tarjan_graph_wrapper.hpp"
#include <unordered_map>
namespace osrm namespace osrm
{ {
namespace partition namespace partition
@ -12,26 +22,140 @@ namespace partition
RecursiveBisection::RecursiveBisection(std::size_t maximum_cell_size, RecursiveBisection::RecursiveBisection(std::size_t maximum_cell_size,
double balance, double balance,
double boundary_factor, double boundary_factor,
const BisectionGraph &bisection_graph_) BisectionGraph &bisection_graph_)
: bisection_graph(bisection_graph_), internal_state(bisection_graph_) : bisection_graph(bisection_graph_), internal_state(bisection_graph_)
{ {
GraphView view(bisection_graph, internal_state, internal_state.Begin(), internal_state.End()); auto views = FakeFirstPartitionWithSCC(1000);
std::cout << "Components: " << views.size() << std::endl;
;
TIMER_START(bisection);
GraphView view = views.front();
InertialFlow flow(view); InertialFlow flow(view);
const auto partition = flow.ComputePartition(balance, boundary_factor); const auto partition = flow.ComputePartition(10, balance, boundary_factor);
const auto center = internal_state.ApplyBisection(view.Begin(), view.End(), partition); const auto center = internal_state.ApplyBisection(view.Begin(), view.End(), 0, partition.flags);
{ {
auto state = internal_state; auto state = internal_state;
} }
TIMER_STOP(bisection);
std::cout << "Bisection completed in " << TIMER_SEC(bisection)
<< " Cut Size: " << partition.num_edges << " Balance: " << partition.num_nodes_source
<< std::endl;
GraphView recursive_view_lhs(bisection_graph, internal_state, view.Begin(), center); util::ScopedGeojsonLoggerGuard<util::CoordinateVectorToLineString, util::LoggingScenario(0)>
logger_zero("level_0.geojson");
for (NodeID nid = 0; nid < bisection_graph.NumberOfNodes(); ++nid)
{
for (const auto &edge : bisection_graph.Edges(nid))
{
const auto target = edge.target;
if (internal_state.GetBisectionID(nid) != internal_state.GetBisectionID(target))
{
std::vector<util::Coordinate> coordinates;
coordinates.push_back(bisection_graph.Node(nid).coordinate);
coordinates.push_back(bisection_graph.Node(target).coordinate);
logger_zero.Write(coordinates);
}
}
}
TIMER_START(bisection_2_1);
GraphView recursive_view_lhs(bisection_graph, view.Begin(), center);
InertialFlow flow_lhs(recursive_view_lhs); InertialFlow flow_lhs(recursive_view_lhs);
const auto partition_lhs = flow_lhs.ComputePartition(balance,boundary_factor); const auto partition_lhs = flow_lhs.ComputePartition(10, balance, boundary_factor);
internal_state.ApplyBisection(recursive_view_lhs.Begin(),recursive_view_lhs.End(),partition_lhs); internal_state.ApplyBisection(
recursive_view_lhs.Begin(), recursive_view_lhs.End(), 1, partition_lhs.flags);
TIMER_STOP(bisection_2_1);
std::cout << "Bisection(2) completed in " << TIMER_SEC(bisection_2_1)
<< " Cut Size: " << partition_lhs.num_edges
<< " Balance: " << partition_lhs.num_nodes_source << std::endl;
GraphView recursive_view_rhs(bisection_graph, internal_state, center, view.End()); TIMER_START(bisection_2_2);
GraphView recursive_view_rhs(bisection_graph, center, view.End());
InertialFlow flow_rhs(recursive_view_rhs); InertialFlow flow_rhs(recursive_view_rhs);
const auto partition_rhs = flow_rhs.ComputePartition(balance,boundary_factor); const auto partition_rhs = flow_rhs.ComputePartition(10, balance, boundary_factor);
internal_state.ApplyBisection(recursive_view_rhs.Begin(),recursive_view_rhs.End(),partition_rhs); internal_state.ApplyBisection(
recursive_view_rhs.Begin(), recursive_view_rhs.End(), 1, partition_rhs.flags);
TIMER_STOP(bisection_2_2);
std::cout << "Bisection(3) completed in " << TIMER_SEC(bisection_2_2)
<< " Cut Size: " << partition_rhs.num_edges
<< " Balance: " << partition_rhs.num_nodes_source << std::endl;
util::ScopedGeojsonLoggerGuard<util::CoordinateVectorToLineString, util::LoggingScenario(1)>
logger_one("level_1.geojson");
for (NodeID nid = 0; nid < bisection_graph.NumberOfNodes(); ++nid)
{
for (const auto &edge : bisection_graph.Edges(nid))
{
const auto target = edge.target;
if (internal_state.GetBisectionID(nid) != internal_state.GetBisectionID(target))
{
std::vector<util::Coordinate> coordinates;
coordinates.push_back(bisection_graph.Node(nid).coordinate);
coordinates.push_back(bisection_graph.Node(target).coordinate);
logger_one.Write(coordinates);
}
}
}
}
std::vector<GraphView>
RecursiveBisection::FakeFirstPartitionWithSCC(const std::size_t small_component_size)
{
// since our graphs are unidirectional, we don't realy need the scc. But tarjan is so nice and
// assigns IDs and counts sizes
TarjanGraphWrapper wrapped_graph(bisection_graph);
extractor::TarjanSCC<TarjanGraphWrapper> scc_algo(wrapped_graph);
scc_algo.Run();
// Map Edges to Sccs
const auto in_small = [&scc_algo, small_component_size](const NodeID node_id) {
return scc_algo.GetComponentSize(scc_algo.GetComponentID(node_id)) <= small_component_size;
};
const constexpr std::size_t small_component_id = -1;
std::unordered_map<std::size_t, std::size_t> component_map;
const auto transform_id = [&](const NodeID node_id) -> std::size_t {
if (in_small(node_id))
return small_component_id;
else
return scc_algo.GetComponentID(node_id);
};
std::vector<NodeID> mapping(bisection_graph.NumberOfNodes(), SPECIAL_NODEID);
for (const auto &node : bisection_graph.Nodes())
mapping[node.original_id] = component_map[transform_id(node.original_id)]++;
// needs to remove edges, if we should ever switch to directed graphs here
std::stable_sort(
bisection_graph.Begin(), bisection_graph.End(), [&](const auto &lhs, const auto &rhs) {
return transform_id(lhs.original_id) < transform_id(rhs.original_id);
});
// remap all remaining edges
std::for_each(bisection_graph.Begin(), bisection_graph.End(), [&](const auto &node) {
for (auto &edge : bisection_graph.Edges(node))
edge.target = mapping[edge.target];
});
std::vector<GraphView> views;
auto last = bisection_graph.CBegin();
auto last_id = transform_id(bisection_graph.Begin()->original_id);
for (auto itr = bisection_graph.CBegin(); itr != bisection_graph.CEnd(); ++itr)
{
auto itr_id = transform_id(itr->original_id);
if (last_id != itr_id)
{
views.push_back(GraphView(bisection_graph, last, itr));
last_id = itr_id;
last = itr;
}
}
views.push_back(GraphView(bisection_graph, last, bisection_graph.CEnd()));
return views;
} }
} // namespace partition } // namespace partition

View File

@ -1,5 +1,6 @@
#include "partition/recursive_bisection_state.hpp" #include "partition/recursive_bisection_state.hpp"
#include <algorithm>
#include <numeric> #include <numeric>
// TODO remove // TODO remove
@ -11,63 +12,72 @@ namespace osrm
namespace partition namespace partition
{ {
RecursiveBisectionState::RecursiveBisectionState(const BisectionGraph &bisection_graph_) RecursiveBisectionState::RecursiveBisectionState(BisectionGraph &bisection_graph_)
: bisection_graph(bisection_graph_) : bisection_graph(bisection_graph_)
{ {
id_array.resize(bisection_graph.GetNumberOfNodes()); bisection_ids.resize(bisection_graph.NumberOfNodes(), BisectionID{0});
std::iota(id_array.begin(), id_array.end(), NodeID{0});
bisection_ids.resize(bisection_graph.GetNumberOfNodes(), BisectionID{0});
} }
RecursiveBisectionState::~RecursiveBisectionState() RecursiveBisectionState::~RecursiveBisectionState() {}
RecursiveBisectionState::BisectionID
RecursiveBisectionState::GetBisectionID(const NodeID node) const
{ {
std::cout << "Internal Result\n"; return bisection_ids[node];
std::cout << "IDArray:";
for (auto id : id_array)
std::cout << " " << id;
std::cout << std::endl;
std::cout << "BisectionIDs:";
for (auto id : bisection_ids)
std::cout << " " << (std::bitset<4>(id));
std::cout << std::endl;
} }
const RecursiveBisectionState::IDIterator RecursiveBisectionState::Begin() const RecursiveBisectionState::NodeIterator
{ RecursiveBisectionState::ApplyBisection(const NodeIterator const_begin,
return id_array.cbegin(); const NodeIterator const_end,
} const std::size_t depth,
const std::vector<bool> &partition)
const RecursiveBisectionState::IDIterator RecursiveBisectionState::End() const
{
return id_array.cend();
}
RecursiveBisectionState::BisectionID RecursiveBisectionState::GetBisectionID(const NodeID nid) const
{
return bisection_ids[nid];
}
RecursiveBisectionState::IDIterator RecursiveBisectionState::ApplyBisection(
const IDIterator begin, const IDIterator end, const std::vector<bool> &partition)
{ {
// augment the partition ids // augment the partition ids
for (auto itr = begin; itr != end; ++itr) const auto flag = BisectionID{1} << depth;
for (auto itr = const_begin; itr != const_end; ++itr)
{ {
bisection_ids[*itr] <<= 1; const auto nid = std::distance(const_begin, itr);
bisection_ids[*itr] |= partition[std::distance(begin, itr)]; if (partition[nid])
bisection_ids[itr->original_id] |= flag;
} }
auto first = id_array.begin() + std::distance(id_array.cbegin(), begin);
auto last = id_array.begin() + std::distance(id_array.cbegin(), end);
// Keep items with `0` as partition id to the left, move other to the right // Keep items with `0` as partition id to the left, move other to the right
auto by_last_bit = [this](const auto nid) { auto by_flag_bit = [this, flag](const auto &node) {
return BisectionID{0} == (bisection_ids[nid] & 1); return BisectionID{0} == (bisection_ids[node.original_id] & flag);
}; };
return std::stable_partition(first, last, by_last_bit); auto begin = bisection_graph.Begin() + std::distance(bisection_graph.CBegin(), const_begin);
const auto end = begin + std::distance(const_begin, const_end);
// remap the edges
std::vector<NodeID> mapping(std::distance(const_begin, const_end), SPECIAL_NODEID);
// calculate a mapping of all node ids
std::size_t lesser_id = 0, upper_id = 0;
std::transform(const_begin,
const_end,
mapping.begin(),
[by_flag_bit, &lesser_id, &upper_id](const auto &node) {
return by_flag_bit(node) ? lesser_id++ : upper_id++;
});
// erase all edges that point into different partitions
std::for_each(begin, end, [&](auto &node) {
const auto node_flag = by_flag_bit(node);
bisection_graph.RemoveEdges(node, [&](const BisectionGraph::EdgeT &edge) {
const auto target_flag = by_flag_bit(*(const_begin + edge.target));
return (node_flag != target_flag);
});
});
auto center = std::stable_partition(begin, end, by_flag_bit);
// remap all remaining edges
std::for_each(const_begin, const_end, [&](const auto &node) {
for (auto &edge : bisection_graph.Edges(node))
edge.target = mapping[edge.target];
});
return const_begin + std::distance(begin, center);
} }
} // namespace partition } // namespace partition

View File

@ -0,0 +1,27 @@
#include "partition/tarjan_graph_wrapper.hpp"
namespace osrm
{
namespace partition
{
TarjanGraphWrapper::TarjanGraphWrapper(const BisectionGraph &bisection_graph_)
: bisection_graph(bisection_graph_)
{
}
std::size_t TarjanGraphWrapper::GetNumberOfNodes() const { return bisection_graph.NumberOfNodes(); }
util::range<EdgeID> TarjanGraphWrapper::GetAdjacentEdgeRange(const NodeID nid) const
{
const auto &node = bisection_graph.Node(nid);
return util::irange<EdgeID>(node.edges_begin, node.edges_end);
}
NodeID TarjanGraphWrapper::GetTarget(const EdgeID eid) const
{
return bisection_graph.Edge(eid).target;
}
} // namespace partition
} // namespace osrm

View File

@ -166,7 +166,7 @@ int main(int argc, char *argv[])
util::Log() << "Starting SCC graph traversal"; util::Log() << "Starting SCC graph traversal";
extractor::TarjanSCC<tools::TarjanGraph> tarjan{graph}; extractor::TarjanSCC<tools::TarjanGraph> tarjan{*graph};
tarjan.Run(); tarjan.Run();
util::Log() << "Identified: " << tarjan.GetNumberOfComponents() << " components"; util::Log() << "Identified: " << tarjan.GetNumberOfComponents() << " components";

View File

@ -1,5 +1,5 @@
#include "partition/partitioner.hpp"
#include "partition/partition_config.hpp" #include "partition/partition_config.hpp"
#include "partition/partitioner.hpp"
#include "util/log.hpp" #include "util/log.hpp"
#include "util/version.hpp" #include "util/version.hpp"