always emit a small component view
Unit Tests for Reodering by Predicate
This commit is contained in:
committed by
Patrick Niklaus
parent
b9ed20bb9b
commit
c3cc79f798
@@ -743,15 +743,8 @@ void Extractor::WriteCompressedNodeBasedGraph(const std::string &path,
|
||||
|
||||
BOOST_ASSERT_MSG(num_nodes == externals.size(), "graph and embedding out of sync");
|
||||
|
||||
const auto die = [] {
|
||||
throw util::exception("Writing the compressed node based graph to disk failed");
|
||||
};
|
||||
|
||||
if (!writer.WriteElementCount64(num_edges))
|
||||
die();
|
||||
|
||||
if (!writer.WriteElementCount64(num_nodes))
|
||||
die();
|
||||
writer.WriteElementCount64(num_edges);
|
||||
writer.WriteElementCount64(num_nodes);
|
||||
|
||||
// For all nodes iterate over its edges and dump (from, to) pairs
|
||||
for (const NodeID from_node : util::irange(0u, num_nodes))
|
||||
@@ -760,19 +753,15 @@ void Extractor::WriteCompressedNodeBasedGraph(const std::string &path,
|
||||
{
|
||||
const auto to_node = graph.GetTarget(edge);
|
||||
|
||||
if (!writer.WriteOne(from_node))
|
||||
die();
|
||||
if (!writer.WriteOne(to_node))
|
||||
die();
|
||||
writer.WriteOne(from_node);
|
||||
writer.WriteOne(to_node);
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto &qnode : externals)
|
||||
{
|
||||
if (!writer.WriteOne(qnode.lon))
|
||||
die();
|
||||
if (!writer.WriteOne(qnode.lat))
|
||||
die();
|
||||
writer.WriteOne(qnode.lon);
|
||||
writer.WriteOne(qnode.lat);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "partition/dinic_max_flow.hpp"
|
||||
#include "util/integer_range.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <stack>
|
||||
@@ -13,43 +15,46 @@ namespace partition
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
const auto constexpr INVALID_LEVEL = std::numeric_limits<DinicMaxFlow::Level>::max();
|
||||
|
||||
auto makeHasNeighborNotInCheck(const DinicMaxFlow::SourceSinkNodes &set, const GraphView &view)
|
||||
{
|
||||
return [&](const NodeID nid) {
|
||||
const auto is_not_contained = [&set](const BisectionEdge &edge) {
|
||||
return set.count(edge.target) == 0;
|
||||
};
|
||||
return view.EndEdges(nid) !=
|
||||
std::find_if(view.BeginEdges(nid), view.EndEdges(nid), is_not_contained);
|
||||
};
|
||||
}
|
||||
|
||||
} // end namespace
|
||||
|
||||
DinicMaxFlow::MinCut DinicMaxFlow::operator()(const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes) const
|
||||
{
|
||||
// for the inertial flow algorithm, we use quite a large set of nodes as source/sink nodes. Only
|
||||
// a few of them can be part of the process, since they are grouped together. A standard
|
||||
// parameterisation would be 25% sink/source nodes. This already includes 50% of the graph. By
|
||||
// only focussing on a small set on the outside of the source/sink blob, we can save quite some
|
||||
// overhead in initialisation/search cost.
|
||||
|
||||
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::copy_if(source_nodes.begin(),
|
||||
source_nodes.end(),
|
||||
std::back_inserter(border_source_nodes),
|
||||
makeHasNeighborNotInCheck(source_nodes, view));
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::copy_if(sink_nodes.begin(),
|
||||
sink_nodes.end(),
|
||||
std::back_inserter(border_sink_nodes),
|
||||
makeHasNeighborNotInCheck(sink_nodes, view));
|
||||
|
||||
// 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
|
||||
@@ -57,13 +62,14 @@ DinicMaxFlow::MinCut DinicMaxFlow::operator()(const GraphView &view,
|
||||
// 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.
|
||||
|
||||
// allocate storage for the flow
|
||||
FlowEdges flow(view.NumberOfNodes());
|
||||
std::size_t flow_value = 0;
|
||||
do
|
||||
{
|
||||
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, it's enough to check the border
|
||||
const auto separated = std::find_if(border_sink_nodes.begin(),
|
||||
border_sink_nodes.end(),
|
||||
[&levels, &view](const auto node) {
|
||||
@@ -72,51 +78,36 @@ DinicMaxFlow::MinCut DinicMaxFlow::operator()(const GraphView &view,
|
||||
|
||||
if (!separated)
|
||||
{
|
||||
BlockingFlow(flow, levels, view, source_nodes, border_sink_nodes);
|
||||
flow_value += BlockingFlow(flow, levels, view, source_nodes, border_sink_nodes);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mark levels for all sources to not confuse make-cut
|
||||
// mark levels for all sources to not confuse make-cut (due to the border nodes
|
||||
// heuristic)
|
||||
for (auto s : source_nodes)
|
||||
levels[s] = 0;
|
||||
return MakeCut(view, levels);
|
||||
const auto cut = MakeCut(view, levels, flow_value);
|
||||
return cut;
|
||||
}
|
||||
} while (true);
|
||||
}
|
||||
|
||||
DinicMaxFlow::MinCut DinicMaxFlow::MakeCut(const GraphView &view, const LevelGraph &levels) const
|
||||
DinicMaxFlow::MinCut DinicMaxFlow::MakeCut(const GraphView &view,
|
||||
const LevelGraph &levels,
|
||||
const std::size_t flow_value) const
|
||||
{
|
||||
const auto is_sink_side = [&view, &levels](const NodeID nid) {
|
||||
return levels[nid] == INVALID_LEVEL;
|
||||
};
|
||||
const auto is_valid_level = [](const Level level) { return level != 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)};
|
||||
// now. There is potential for optimisation here.
|
||||
std::vector<bool> result(view.NumberOfNodes());
|
||||
|
||||
BOOST_ASSERT(view.NumberOfNodes() == levels.size());
|
||||
std::size_t source_side_count = std::count_if(levels.begin(), levels.end(), is_valid_level);
|
||||
std::transform(levels.begin(), levels.end(), result.begin(), is_valid_level);
|
||||
|
||||
return {source_side_count, flow_value, std::move(result)};
|
||||
}
|
||||
|
||||
DinicMaxFlow::LevelGraph
|
||||
@@ -129,18 +120,16 @@ DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
LevelGraph levels(view.NumberOfNodes(), INVALID_LEVEL);
|
||||
std::queue<NodeID> level_queue;
|
||||
|
||||
// set the front of the source nodes to zero and add them to the BFS queue. In addition, set all
|
||||
// neighbors to zero as well (which allows direct usage of the levels to see what we visited,
|
||||
// and still don't go back into the hughe set of sources)
|
||||
for (const auto node_id : border_source_nodes)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
if (source_nodes.count(edge.target))
|
||||
levels[edge.target] = 0;
|
||||
}
|
||||
// check if there is flow present on an edge
|
||||
const auto has_flow = [&](const NodeID from, const NodeID to) {
|
||||
@@ -148,11 +137,12 @@ DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
};
|
||||
|
||||
// perform a relaxation step in the BFS algorithm
|
||||
const auto relax_node = [&](const NodeID node_id, const Level level) {
|
||||
const auto relax_node = [&](const NodeID node_id) {
|
||||
// don't relax sink nodes
|
||||
if (sink_nodes.count(node_id))
|
||||
return;
|
||||
|
||||
const auto level = levels[node_id] + 1;
|
||||
for (const auto &edge : view.Edges(node_id))
|
||||
{
|
||||
const auto target = edge.target;
|
||||
@@ -170,44 +160,38 @@ DinicMaxFlow::ComputeLevelGraph(const GraphView &view,
|
||||
};
|
||||
|
||||
// compute the levels of level graph using BFS
|
||||
for (Level level = 1; !level_queue.empty(); ++level)
|
||||
while (!level_queue.empty())
|
||||
{
|
||||
// run through the current level
|
||||
auto steps = level_queue.size();
|
||||
while (steps--)
|
||||
{
|
||||
relax_node(level_queue.front(), level);
|
||||
level_queue.pop();
|
||||
}
|
||||
relax_node(level_queue.front());
|
||||
level_queue.pop();
|
||||
}
|
||||
|
||||
return levels;
|
||||
}
|
||||
|
||||
std::uint32_t DinicMaxFlow::BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const
|
||||
std::size_t DinicMaxFlow::BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const
|
||||
{
|
||||
std::uint32_t flow_increase = 0;
|
||||
// track the number of augmenting paths (which in sum will equal the number of unique border
|
||||
// edges) (since our graph is undirected)
|
||||
std::size_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) {
|
||||
|
||||
// add/remove flow edges from the current residual graph
|
||||
const auto augment_one = [&flow, &view](const NodeID from, const NodeID to) {
|
||||
// check if there is flow in the opposite direction
|
||||
auto existing_edge = flow[to].find(from);
|
||||
if (existing_edge != flow[to].end())
|
||||
{
|
||||
// remove flow from reverse edges first
|
||||
flow[to].erase(existing_edge);
|
||||
}
|
||||
flow[to].erase(existing_edge); // remove flow from reverse edges first
|
||||
else
|
||||
{
|
||||
// only add flow if no opposite flow exists
|
||||
flow[from].insert(to);
|
||||
}
|
||||
// for adjacent find
|
||||
flow[from].insert(to); // only add flow if no opposite flow exists
|
||||
|
||||
// do augmentation on all pairs, never stop early:
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -215,32 +199,31 @@ std::uint32_t DinicMaxFlow::BlockingFlow(FlowEdges &flow,
|
||||
std::adjacent_find(path.begin(), path.end(), augment_one);
|
||||
};
|
||||
|
||||
// find and augment the blocking flow
|
||||
const auto augment_all_paths = [&](const NodeID sink_node_id) {
|
||||
// only augment sinks
|
||||
if (levels[sink_node_id] == INVALID_LEVEL)
|
||||
return;
|
||||
|
||||
std::vector<std::pair<std::uint32_t, NodeID>> reached_sinks;
|
||||
for (auto sink : border_sink_nodes)
|
||||
{
|
||||
if (levels[sink] != INVALID_LEVEL)
|
||||
while (true)
|
||||
{
|
||||
reached_sinks.push_back(std::make_pair(levels[sink], sink));
|
||||
// as long as there are augmenting paths from the sink, add them
|
||||
const auto path = GetAugmentingPath(levels, sink_node_id, view, flow, source_nodes);
|
||||
if (path.empty())
|
||||
break;
|
||||
else
|
||||
{
|
||||
augment_flow(path);
|
||||
++flow_increase;
|
||||
}
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
std::for_each(border_sink_nodes.begin(), border_sink_nodes.end(), augment_all_paths);
|
||||
return flow_increase;
|
||||
}
|
||||
|
||||
// performs a dfs in the level graph, by adjusting levels that don't offer any further paths to
|
||||
// INVALID_LEVEL and by following the level graph, this looks at every edge at most `c` times (O(E))
|
||||
std::vector<NodeID> DinicMaxFlow::GetAugmentingPath(LevelGraph &levels,
|
||||
const NodeID node_id,
|
||||
const GraphView &view,
|
||||
@@ -251,7 +234,7 @@ std::vector<NodeID> DinicMaxFlow::GetAugmentingPath(LevelGraph &levels,
|
||||
BOOST_ASSERT(source_nodes.find(node_id) == source_nodes.end());
|
||||
|
||||
// Keeps the local state of the DFS in forms of the iterators
|
||||
using DFSState = struct
|
||||
struct DFSState
|
||||
{
|
||||
BisectionGraph::ConstEdgeIterator edge_iterator;
|
||||
const BisectionGraph::ConstEdgeIterator end_iterator;
|
||||
|
||||
@@ -3,14 +3,15 @@
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
{
|
||||
|
||||
GraphView::GraphView(const BisectionGraph &bisection_graph_)
|
||||
: bisection_graph(bisection_graph_), begin(bisection_graph.CBegin()),
|
||||
end(bisection_graph.CEnd())
|
||||
: GraphView(bisection_graph_, bisection_graph_.CBegin(), bisection_graph_.CEnd())
|
||||
{
|
||||
}
|
||||
|
||||
@@ -21,23 +22,32 @@ GraphView::GraphView(const BisectionGraph &bisection_graph_,
|
||||
{
|
||||
}
|
||||
|
||||
NodeID GraphView::GetID(const BisectionGraph::NodeT &node) const
|
||||
GraphView::GraphView(const GraphView &other_view,
|
||||
const BisectionGraph::ConstNodeIterator begin_,
|
||||
const BisectionGraph::ConstNodeIterator end_)
|
||||
: GraphView(other_view.bisection_graph, begin_, end_)
|
||||
{
|
||||
return static_cast<NodeID>(&node - &(*begin));
|
||||
}
|
||||
|
||||
std::size_t GraphView::NumberOfNodes() const { return std::distance(begin, end); }
|
||||
|
||||
NodeID GraphView::GetID(const NodeT &node) const
|
||||
{
|
||||
const auto node_id = static_cast<NodeID>(&node - &(*begin));
|
||||
BOOST_ASSERT(node_id < NumberOfNodes());
|
||||
return node_id;
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
const BisectionNode &GraphView::GetNode(const NodeID nid) const
|
||||
const GraphView::NodeT &GraphView::Node(const NodeID nid) const
|
||||
{
|
||||
return bisection_graph.Node(nid);
|
||||
}
|
||||
|
||||
const BisectionEdge &GraphView::GetEdge(const EdgeID eid) const
|
||||
const GraphView::EdgeT &GraphView::Edge(const EdgeID eid) const
|
||||
{
|
||||
return bisection_graph.Edge(eid);
|
||||
}
|
||||
|
||||
@@ -118,14 +118,9 @@ DinicMaxFlow::MinCut InertialFlow::BestMinCut(const std::size_t n, const double
|
||||
// Swap to keep the destruction of the old object outside of critical section.
|
||||
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
|
||||
}
|
||||
|
||||
@@ -9,13 +9,16 @@
|
||||
|
||||
#include <iterator>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/assert.hpp>
|
||||
|
||||
#include <tbb/task_scheduler_init.h>
|
||||
|
||||
#include "util/geojson_debug_logger.hpp"
|
||||
#include "util/geojson_debug_policies.hpp"
|
||||
#include "util/json_container.hpp"
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
@@ -137,6 +140,9 @@ int Partitioner::Run(const PartitionConfig &config)
|
||||
<< compressed_node_based_graph.edges.size() << " edges, "
|
||||
<< compressed_node_based_graph.coordinates.size() << " nodes";
|
||||
|
||||
groupEdgesBySource(begin(compressed_node_based_graph.edges),
|
||||
end(compressed_node_based_graph.edges));
|
||||
|
||||
auto graph =
|
||||
makeBisectionGraph(compressed_node_based_graph.coordinates,
|
||||
adaptToBisectionEdge(std::move(compressed_node_based_graph.edges)));
|
||||
|
||||
@@ -158,8 +158,24 @@ RecursiveBisection::FakeFirstPartitionWithSCC(const std::size_t small_component_
|
||||
}
|
||||
}
|
||||
views.push_back(GraphView(bisection_graph, last, bisection_graph.CEnd()));
|
||||
|
||||
bool has_small_component = [&]() {
|
||||
for (std::size_t i = 0; i < scc_algo.GetNumberOfComponents(); ++i)
|
||||
if (scc_algo.GetComponentSize(i) <= small_component_size)
|
||||
return true;
|
||||
return false;
|
||||
}();
|
||||
|
||||
if (!has_small_component)
|
||||
views.push_back(GraphView(bisection_graph, bisection_graph.CEnd(), bisection_graph.CEnd()));
|
||||
|
||||
return views;
|
||||
}
|
||||
|
||||
const std::vector<RecursiveBisectionState::BisectionID> &RecursiveBisection::BisectionIDs() const
|
||||
{
|
||||
return internal_state.BisectionIDs();
|
||||
}
|
||||
|
||||
} // namespace partition
|
||||
} // namespace osrm
|
||||
|
||||
@@ -80,5 +80,11 @@ RecursiveBisectionState::ApplyBisection(const NodeIterator const_begin,
|
||||
return const_begin + std::distance(begin, center);
|
||||
}
|
||||
|
||||
const std::vector<RecursiveBisectionState::BisectionID> &
|
||||
RecursiveBisectionState::BisectionIDs() const
|
||||
{
|
||||
return bisection_ids;
|
||||
}
|
||||
|
||||
} // namespace partition
|
||||
} // namespace osrm
|
||||
|
||||
@@ -14,8 +14,7 @@ std::size_t TarjanGraphWrapper::GetNumberOfNodes() const { return bisection_grap
|
||||
|
||||
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);
|
||||
return util::irange<EdgeID>(bisection_graph.BeginEdgeID(nid), bisection_graph.EndEdgeID(nid));
|
||||
}
|
||||
|
||||
NodeID TarjanGraphWrapper::GetTarget(const EdgeID eid) const
|
||||
|
||||
Reference in New Issue
Block a user