Optimise Flow Algorithm/Datastructures in use
This commit is contained in:
committed by
Patrick Niklaus
parent
dd60ae31ae
commit
786be6f570
@@ -7,7 +7,6 @@
|
||||
#include "engine/trip/trip_nearest_neighbour.hpp"
|
||||
#include "util/dist_table_wrapper.hpp" // to access the dist table more easily
|
||||
#include "util/json_container.hpp"
|
||||
#include "util/matrix_graph_wrapper.hpp" // wrapper to use tarjan scc on dist table
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
@@ -263,4 +262,4 @@ Status TripPlugin::HandleRequest(const std::shared_ptr<const datafacade::BaseDat
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,10 +380,9 @@ void Extractor::FindComponents(unsigned max_edge_id,
|
||||
auto new_end = std::unique(edges.begin(), edges.end());
|
||||
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(
|
||||
std::const_pointer_cast<const UncontractedGraph>(uncontractor_graph));
|
||||
TarjanSCC<UncontractedGraph> component_search(uncontracted_graph);
|
||||
component_search.Run();
|
||||
|
||||
for (auto &node : input_nodes)
|
||||
|
||||
@@ -1,74 +1,167 @@
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
{
|
||||
|
||||
DinicMaxFlow::PartitionResult DinicMaxFlow::operator()(const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes) const
|
||||
namespace
|
||||
{
|
||||
FlowEdges flow;
|
||||
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 &sink_nodes) const
|
||||
{
|
||||
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
|
||||
{
|
||||
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
|
||||
const auto separated =
|
||||
std::find_if(sink_nodes.begin(), sink_nodes.end(), [&levels](const auto node) {
|
||||
return levels.find(node) != levels.end();
|
||||
}) == sink_nodes.end();
|
||||
const auto separated = std::find_if(border_sink_nodes.begin(),
|
||||
border_sink_nodes.end(),
|
||||
[&levels, &view](const auto node) {
|
||||
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
|
||||
PartitionResult result(view.NumberOfNodes(), true);
|
||||
for (auto itr = view.Begin(); itr != view.End(); ++itr)
|
||||
{
|
||||
if (levels.find(*itr) != levels.end())
|
||||
result[std::distance(view.Begin(), itr)] = false;
|
||||
}
|
||||
return result;
|
||||
BlockingFlow(flow, levels, view, source_nodes, border_sink_nodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mark levels for all sources to not confuse make-cut
|
||||
for (auto s : source_nodes)
|
||||
levels[s] = 0;
|
||||
return MakeCut(view, levels);
|
||||
}
|
||||
|
||||
AugmentFlow(flow, view, source_nodes, sink_nodes, levels);
|
||||
} while (true);
|
||||
}
|
||||
|
||||
DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const FlowEdges &flow) const
|
||||
DinicMaxFlow::MinCut DinicMaxFlow::MakeCut(const GraphView &view, const LevelGraph &levels) const
|
||||
{
|
||||
LevelGraph levels;
|
||||
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 &sink_nodes,
|
||||
const FlowEdges &flow) const
|
||||
{
|
||||
LevelGraph levels(view.NumberOfNodes(), INVALID_LEVEL);
|
||||
std::queue<NodeID> level_queue;
|
||||
|
||||
for (const auto node : source_nodes)
|
||||
for (const auto node_id : border_source_nodes)
|
||||
{
|
||||
levels[node] = 0;
|
||||
level_queue.push(node);
|
||||
levels[node_id] = 0;
|
||||
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
|
||||
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
|
||||
const auto relax_node = [&](const NodeID node_id, const std::uint32_t level) {
|
||||
for (auto itr = view.EdgeBegin(node_id); itr != view.EdgeEnd(node_id); ++itr)
|
||||
{
|
||||
const auto target = view.GetTarget(*itr);
|
||||
const auto relax_node = [&](const NodeID node_id, const Level level) {
|
||||
// don't relax sink nodes
|
||||
if (sink_nodes.count(node_id))
|
||||
return;
|
||||
|
||||
for (const auto &edge : view.Edges(node_id))
|
||||
{
|
||||
const auto target = edge.target;
|
||||
// don't relax edges with flow on them
|
||||
if (has_flow(node_id, target))
|
||||
continue;
|
||||
|
||||
// don't go back, only follow edges to new nodes
|
||||
if (levels.find(target) == levels.end())
|
||||
if (levels[target] > level)
|
||||
{
|
||||
level_queue.push(target);
|
||||
levels[target] = level;
|
||||
@@ -77,7 +170,7 @@ DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
};
|
||||
|
||||
// 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
|
||||
auto steps = level_queue.size();
|
||||
@@ -91,86 +184,126 @@ DinicMaxFlow::LevelGraph DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
return levels;
|
||||
}
|
||||
|
||||
void DinicMaxFlow::AugmentFlow(FlowEdges &flow,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes,
|
||||
const LevelGraph &levels) const
|
||||
std::uint32_t DinicMaxFlow::BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const
|
||||
{
|
||||
const auto has_flow = [&](const auto from, const auto to) {
|
||||
return flow.find(std::make_pair(from, to)) != flow.end();
|
||||
};
|
||||
std::uint32_t flow_increase = 0;
|
||||
// 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
|
||||
// do a dfs on the level graph, augment all paths that are found
|
||||
const auto find_and_augment_path = [&](const NodeID source) {
|
||||
std::vector<NodeID> path;
|
||||
auto has_path = findPath(source, path, view, levels, flow, sink_nodes);
|
||||
|
||||
if (has_path)
|
||||
{
|
||||
NodeID last = path.back();
|
||||
path.pop_back();
|
||||
// augment the flow graph with the flow
|
||||
while (!path.empty())
|
||||
// check if there is flow in the opposite direction
|
||||
auto existing_edge = flow[to].find(from);
|
||||
if (existing_edge != flow[to].end())
|
||||
{
|
||||
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();
|
||||
// remove flow from reverse edges first
|
||||
flow[to].erase(existing_edge);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
else
|
||||
{
|
||||
// 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
|
||||
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))
|
||||
{ /*augment*/
|
||||
if (levels[sink] != INVALID_LEVEL)
|
||||
{
|
||||
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> &path,
|
||||
const GraphView &view,
|
||||
const LevelGraph &levels,
|
||||
const FlowEdges &flow,
|
||||
const SourceSinkNodes &sink_nodes) const
|
||||
std::vector<NodeID> DinicMaxFlow::GetAugmentingPath(LevelGraph &levels,
|
||||
const NodeID node_id,
|
||||
const GraphView &view,
|
||||
const FlowEdges &flow,
|
||||
const SourceSinkNodes &source_nodes) const
|
||||
{
|
||||
path.push_back(node_id);
|
||||
if (sink_nodes.find(node_id) != sink_nodes.end())
|
||||
return true;
|
||||
std::vector<NodeID> path;
|
||||
BOOST_ASSERT(source_nodes.find(node_id) == source_nodes.end());
|
||||
|
||||
const auto node_level = levels.find(node_id)->second;
|
||||
for (auto itr = view.EdgeBegin(node_id); itr != view.EdgeEnd(node_id); ++itr)
|
||||
// Keeps the local state of the DFS in forms of the iterators
|
||||
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
|
||||
if (flow.find(std::make_pair(node_id, target)) != flow.end())
|
||||
continue;
|
||||
std::stack<DFSState> dfs_stack;
|
||||
DFSState initial_state = {view.BeginEdges(node_id), view.EndEdges(node_id)};
|
||||
dfs_stack.push(std::move(initial_state));
|
||||
path.push_back(node_id);
|
||||
|
||||
// don't go back, only follow edges to new nodes
|
||||
const auto level = levels.find(target)->second;
|
||||
if( level != node_level + 1 )
|
||||
continue;
|
||||
while (!dfs_stack.empty())
|
||||
{
|
||||
// the dfs_stack and the path have to be kept in sync
|
||||
BOOST_ASSERT(dfs_stack.size() == path.size());
|
||||
|
||||
if( findPath(target,path,view,levels,flow,sink_nodes) )
|
||||
return true;
|
||||
while (dfs_stack.top().edge_iterator != dfs_stack.top().end_iterator)
|
||||
{
|
||||
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();
|
||||
dfs_stack.pop();
|
||||
}
|
||||
path.pop_back();
|
||||
return false;
|
||||
BOOST_ASSERT(path.empty());
|
||||
return path;
|
||||
}
|
||||
|
||||
} // namespace partition
|
||||
|
||||
@@ -8,77 +8,38 @@ namespace osrm
|
||||
namespace partition
|
||||
{
|
||||
|
||||
HasSamePartitionID::HasSamePartitionID(const RecursiveBisectionState::BisectionID bisection_id_,
|
||||
const BisectionGraph &bisection_graph_,
|
||||
const RecursiveBisectionState &recursive_bisection_state_)
|
||||
: bisection_id(bisection_id_), bisection_graph(bisection_graph_),
|
||||
recursive_bisection_state(recursive_bisection_state_)
|
||||
GraphView::GraphView(const BisectionGraph &bisection_graph_)
|
||||
: bisection_graph(bisection_graph_), begin(bisection_graph.CBegin()),
|
||||
end(bisection_graph.CEnd())
|
||||
{
|
||||
}
|
||||
|
||||
bool HasSamePartitionID::operator()(const EdgeID eid) const
|
||||
{
|
||||
return recursive_bisection_state.GetBisectionID(bisection_graph.GetTarget(eid)) == bisection_id;
|
||||
}
|
||||
|
||||
GraphView::GraphView(const BisectionGraph &bisection_graph_,
|
||||
const RecursiveBisectionState &bisection_state_,
|
||||
const RecursiveBisectionState::IDIterator begin_,
|
||||
const RecursiveBisectionState::IDIterator end_)
|
||||
: bisection_graph(bisection_graph_), bisection_state(bisection_state_), begin(begin_), end(end_)
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
: bisection_graph(bisection_graph_), 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); }
|
||||
|
||||
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
|
||||
{
|
||||
return bisection_graph.GetNode(nid);
|
||||
return bisection_graph.Node(nid);
|
||||
}
|
||||
|
||||
const BisectionEdge &GraphView::GetEdge(const EdgeID eid) const
|
||||
{
|
||||
return bisection_graph.GetEdge(eid);
|
||||
return bisection_graph.Edge(eid);
|
||||
}
|
||||
|
||||
} // namespace partition
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "partition/inertial_flow.hpp"
|
||||
#include "partition/bisection_graph.hpp"
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
#include "partition/reorder_first_last.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@@ -9,6 +8,7 @@
|
||||
#include <iterator>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <tbb/blocked_range.h>
|
||||
@@ -21,17 +21,13 @@ namespace partition
|
||||
|
||||
InertialFlow::InertialFlow(const GraphView &view_) : view(view_) {}
|
||||
|
||||
std::vector<bool> InertialFlow::ComputePartition(const double balance,
|
||||
const double source_sink_rate)
|
||||
DinicMaxFlow::MinCut InertialFlow::ComputePartition(const std::size_t num_slopes,
|
||||
const double balance,
|
||||
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: ";
|
||||
for (auto b : cut.flags)
|
||||
std::cout << b;
|
||||
std::cout << std::endl;
|
||||
|
||||
return cut.flags;
|
||||
return cut;
|
||||
}
|
||||
|
||||
InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
|
||||
@@ -53,8 +49,12 @@ InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
|
||||
Embedding embedding;
|
||||
embedding.reserve(view.NumberOfNodes());
|
||||
|
||||
std::transform(view.Begin(), view.End(), std::back_inserter(embedding), [&](const auto nid) {
|
||||
return NodeWithCoordinate{nid, view.GetNode(nid).coordinate};
|
||||
// adress of the very first node
|
||||
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) {
|
||||
@@ -86,14 +86,22 @@ InertialFlow::SpatialOrder InertialFlow::MakeSpatialOrder(const double ratio,
|
||||
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.);
|
||||
auto best = DinicMaxFlow()(view, base.sources, base.sinks);
|
||||
DinicMaxFlow::MinCut best;
|
||||
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;
|
||||
|
||||
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) {
|
||||
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);
|
||||
|
||||
auto order = this->MakeSpatialOrder(ratio, slope);
|
||||
auto cut = Dinic(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};
|
||||
|
||||
// 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Bad Cut: " << cut.num_edges << " " << cut_balance << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
// cut gets destroyed here
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
#include "partition/partitioner.hpp"
|
||||
#include "partition/bisection_graph.hpp"
|
||||
|
||||
#include "partition/recursive_bisection.hpp"
|
||||
|
||||
// TODO remove after testing
|
||||
#include "util/coordinate.hpp"
|
||||
#include "util/log.hpp"
|
||||
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
@@ -16,61 +18,19 @@ namespace partition
|
||||
|
||||
int Partitioner::Run(const PartitionConfig &config)
|
||||
{
|
||||
struct TestEdge
|
||||
{
|
||||
NodeID source;
|
||||
NodeID target;
|
||||
};
|
||||
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);
|
||||
tbb::task_scheduler_init init(number_of_threads);
|
||||
|
||||
std::vector<TestEdge> input_edges;
|
||||
auto compressed_node_based_graph =
|
||||
LoadCompressedNodeBasedGraph(config.compressed_node_based_graph_path.string());
|
||||
|
||||
// 0 - 1 - 2 - 3
|
||||
// | \ | |
|
||||
// 4 - 5 - 6 - 7
|
||||
groupEdgesBySource(begin(compressed_node_based_graph.edges),
|
||||
end(compressed_node_based_graph.edges));
|
||||
|
||||
input_edges.push_back({0, 1});
|
||||
input_edges.push_back({0, 4});
|
||||
input_edges.push_back({0, 5});
|
||||
|
||||
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);
|
||||
auto graph =
|
||||
makeBisectionGraph(compressed_node_based_graph.coordinates,
|
||||
adaptToBisectionEdge(std::move(compressed_node_based_graph.edges)));
|
||||
|
||||
RecursiveBisection recursive_bisection(1024, 1.1, 0.25, graph);
|
||||
|
||||
|
||||
@@ -4,6 +4,16 @@
|
||||
#include "partition/graph_view.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 partition
|
||||
@@ -12,26 +22,140 @@ namespace partition
|
||||
RecursiveBisection::RecursiveBisection(std::size_t maximum_cell_size,
|
||||
double balance,
|
||||
double boundary_factor,
|
||||
const BisectionGraph &bisection_graph_)
|
||||
BisectionGraph &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);
|
||||
const auto partition = flow.ComputePartition(balance, boundary_factor);
|
||||
const auto center = internal_state.ApplyBisection(view.Begin(), view.End(), partition);
|
||||
const auto partition = flow.ComputePartition(10, balance, boundary_factor);
|
||||
const auto center = internal_state.ApplyBisection(view.Begin(), view.End(), 0, partition.flags);
|
||||
{
|
||||
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);
|
||||
const auto partition_lhs = flow_lhs.ComputePartition(balance,boundary_factor);
|
||||
internal_state.ApplyBisection(recursive_view_lhs.Begin(),recursive_view_lhs.End(),partition_lhs);
|
||||
const auto partition_lhs = flow_lhs.ComputePartition(10, balance, boundary_factor);
|
||||
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);
|
||||
const auto partition_rhs = flow_rhs.ComputePartition(balance,boundary_factor);
|
||||
internal_state.ApplyBisection(recursive_view_rhs.Begin(),recursive_view_rhs.End(),partition_rhs);
|
||||
const auto partition_rhs = flow_rhs.ComputePartition(10, balance, boundary_factor);
|
||||
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
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "partition/recursive_bisection_state.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
// TODO remove
|
||||
@@ -11,63 +12,72 @@ namespace osrm
|
||||
namespace partition
|
||||
{
|
||||
|
||||
RecursiveBisectionState::RecursiveBisectionState(const BisectionGraph &bisection_graph_)
|
||||
RecursiveBisectionState::RecursiveBisectionState(BisectionGraph &bisection_graph_)
|
||||
: bisection_graph(bisection_graph_)
|
||||
{
|
||||
id_array.resize(bisection_graph.GetNumberOfNodes());
|
||||
std::iota(id_array.begin(), id_array.end(), NodeID{0});
|
||||
bisection_ids.resize(bisection_graph.GetNumberOfNodes(), BisectionID{0});
|
||||
bisection_ids.resize(bisection_graph.NumberOfNodes(), BisectionID{0});
|
||||
}
|
||||
|
||||
RecursiveBisectionState::~RecursiveBisectionState()
|
||||
RecursiveBisectionState::~RecursiveBisectionState() {}
|
||||
|
||||
RecursiveBisectionState::BisectionID
|
||||
RecursiveBisectionState::GetBisectionID(const NodeID node) const
|
||||
{
|
||||
std::cout << "Internal Result\n";
|
||||
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;
|
||||
return bisection_ids[node];
|
||||
}
|
||||
|
||||
const RecursiveBisectionState::IDIterator RecursiveBisectionState::Begin() const
|
||||
{
|
||||
return id_array.cbegin();
|
||||
}
|
||||
|
||||
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)
|
||||
RecursiveBisectionState::NodeIterator
|
||||
RecursiveBisectionState::ApplyBisection(const NodeIterator const_begin,
|
||||
const NodeIterator const_end,
|
||||
const std::size_t depth,
|
||||
const std::vector<bool> &partition)
|
||||
{
|
||||
// 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;
|
||||
bisection_ids[*itr] |= partition[std::distance(begin, itr)];
|
||||
const auto nid = std::distance(const_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
|
||||
auto by_last_bit = [this](const auto nid) {
|
||||
return BisectionID{0} == (bisection_ids[nid] & 1);
|
||||
auto by_flag_bit = [this, flag](const auto &node) {
|
||||
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
|
||||
|
||||
@@ -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
|
||||
@@ -166,7 +166,7 @@ int main(int argc, char *argv[])
|
||||
|
||||
util::Log() << "Starting SCC graph traversal";
|
||||
|
||||
extractor::TarjanSCC<tools::TarjanGraph> tarjan{graph};
|
||||
extractor::TarjanSCC<tools::TarjanGraph> tarjan{*graph};
|
||||
tarjan.Run();
|
||||
|
||||
util::Log() << "Identified: " << tarjan.GetNumberOfComponents() << " components";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "partition/partitioner.hpp"
|
||||
#include "partition/partition_config.hpp"
|
||||
#include "partition/partitioner.hpp"
|
||||
|
||||
#include "util/log.hpp"
|
||||
#include "util/version.hpp"
|
||||
|
||||
Reference in New Issue
Block a user