always emit a small component view

Unit Tests for Reodering by Predicate
This commit is contained in:
Moritz Kobitzsch
2017-02-01 15:50:06 +01:00
committed by Patrick Niklaus
parent b9ed20bb9b
commit c3cc79f798
17 changed files with 323 additions and 195 deletions
+6 -17
View File
@@ -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);
}
}
+93 -110
View File
@@ -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;
+18 -8
View File
@@ -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);
}
-5
View File
@@ -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
}
+7 -1
View File
@@ -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)));
+16
View File
@@ -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
+1 -2
View File
@@ -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