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
@@ -19,8 +19,9 @@ namespace osrm
|
||||
namespace partition
|
||||
{
|
||||
|
||||
// Graph node and its corresponding coordinate.
|
||||
// The coordinate will be used in the partitioning step.
|
||||
// Node in the bisection graph. We require the original node id (since we remap the nodes all the
|
||||
// time and can track the correct ID this way). In addtition, the node provides the coordinate its
|
||||
// located at for use in the inertial flow sorting by slope.
|
||||
struct BisectionNode
|
||||
{
|
||||
BisectionNode(util::Coordinate coordinate_ = {util::FloatLongitude{0}, util::FloatLatitude{0}},
|
||||
@@ -36,7 +37,12 @@ struct BisectionNode
|
||||
NodeID original_id;
|
||||
};
|
||||
|
||||
// Graph edge and data for Max-Flow Min-Cut augmentation.
|
||||
// For max-flow/min-cut computations, we operate on a undirected graph. This has some benefits:
|
||||
// - we don't disconnect the graph more than we have to
|
||||
// - small components will actually be disconnected (no border nodes)
|
||||
// - parts of the graph that are clonnected in one way (not reachable/not exitable) will remain
|
||||
// close to their connected nodes
|
||||
// As a result, we only require a target as our only data member in the edge.
|
||||
struct BisectionEdge
|
||||
{
|
||||
BisectionEdge(const NodeID target_ = SPECIAL_NODEID) : target(target_) {}
|
||||
@@ -44,11 +50,13 @@ struct BisectionEdge
|
||||
NodeID target;
|
||||
};
|
||||
|
||||
// The graph layout we use as a basis for partitioning.
|
||||
using RemappableGraphNode = NodeEntryWrapper<BisectionNode>;
|
||||
// Aliases for the graph used during the bisection, based on the Remappable graph
|
||||
using BisectionGraphNode = NodeEntryWrapper<BisectionNode>;
|
||||
using BisectionInputEdge = GraphConstructionWrapper<BisectionEdge>;
|
||||
using BisectionGraph = RemappableGraph<RemappableGraphNode, BisectionEdge>;
|
||||
using BisectionGraph = RemappableGraph<BisectionGraphNode, BisectionEdge>;
|
||||
|
||||
// Factory method to construct the bisection graph form a set of coordinates and Input Edges (need
|
||||
// to contain source and target)
|
||||
inline BisectionGraph makeBisectionGraph(const std::vector<util::Coordinate> &coordinates,
|
||||
const std::vector<BisectionInputEdge> &edges)
|
||||
{
|
||||
@@ -69,23 +77,25 @@ inline BisectionGraph makeBisectionGraph(const std::vector<util::Coordinate> &co
|
||||
};
|
||||
|
||||
// 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 edge_itr) {
|
||||
std::size_t range_begin = std::distance(edges.begin(), edge_itr);
|
||||
return BisectionGraph::NodeT(range_begin, range_begin, coordinates[node_id], node_id);
|
||||
const auto make_bisection_node = [&edges, &coordinates](
|
||||
const std::size_t node_id, const auto begin_itr, const auto end_itr) {
|
||||
std::size_t range_begin = std::distance(edges.begin(), begin_itr);
|
||||
std::size_t range_end = std::distance(edges.begin(), end_itr);
|
||||
return BisectionGraph::NodeT(range_begin, range_end, coordinates[node_id], node_id);
|
||||
};
|
||||
|
||||
auto edge_itr = edges.begin();
|
||||
for (std::size_t node_id = 0; node_id < coordinates.size(); ++node_id)
|
||||
{
|
||||
result_nodes.emplace_back(make_bisection_node(node_id, edge_itr));
|
||||
const auto begin_itr = edge_itr;
|
||||
edge_itr = advance_edge_itr(node_id, edge_itr);
|
||||
result_nodes.back().edges_end = std::distance(edges.begin(), edge_itr);
|
||||
result_nodes.emplace_back(make_bisection_node(node_id, begin_itr, edge_itr));
|
||||
}
|
||||
|
||||
return BisectionGraph(std::move(result_nodes), std::move(result_edges));
|
||||
}
|
||||
|
||||
// Reduce any edge to a fitting input edge for the bisection graph
|
||||
template <typename InputEdge>
|
||||
std::vector<BisectionInputEdge> adaptToBisectionEdge(std::vector<InputEdge> edges)
|
||||
{
|
||||
|
||||
@@ -6,23 +6,10 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <set>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <> struct hash<std::pair<NodeID, NodeID>>
|
||||
{
|
||||
std::size_t operator()(const std::pair<NodeID, NodeID> &flow_edge) const
|
||||
{
|
||||
std::size_t combined = (static_cast<std::size_t>(flow_edge.first) << 32) | flow_edge.second;
|
||||
return std::hash<std::size_t>()(combined);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace osrm
|
||||
{
|
||||
namespace partition
|
||||
@@ -31,42 +18,68 @@ namespace partition
|
||||
class DinicMaxFlow
|
||||
{
|
||||
public:
|
||||
// maximal number of hops in the graph from source to sink
|
||||
using Level = std::uint32_t;
|
||||
|
||||
using MinCut = struct
|
||||
{
|
||||
std::size_t num_nodes_source;
|
||||
std::size_t num_edges;
|
||||
std::vector<bool> flags;
|
||||
};
|
||||
|
||||
// input parameter storing the set o
|
||||
using SourceSinkNodes = std::unordered_set<NodeID>;
|
||||
using LevelGraph = std::vector<Level>;
|
||||
using FlowEdges = std::vector<std::set<NodeID>>;
|
||||
|
||||
MinCut operator()(const GraphView &view,
|
||||
const SourceSinkNodes &sink_nodes,
|
||||
const SourceSinkNodes &source_nodes) const;
|
||||
|
||||
private:
|
||||
// the level of each node in the graph (==hops in BFS from source)
|
||||
using LevelGraph = std::vector<Level>;
|
||||
|
||||
// this is actually faster than using an unordered_set<Edge>, stores all edges that have
|
||||
// capacity grouped by node
|
||||
using FlowEdges = std::vector<std::set<NodeID>>;
|
||||
|
||||
// The level graph (see [1]) is based on a BFS computation. We assign a level to all nodes
|
||||
// (starting with 0 for all source nodes) and assign the hop distance in the residual graph as
|
||||
// the level of the node.
|
||||
// a
|
||||
// / \
|
||||
// s t
|
||||
// \ /
|
||||
// b
|
||||
// would assign s = 0, a,b = 1, t=2
|
||||
LevelGraph ComputeLevelGraph(const GraphView &view,
|
||||
const std::vector<NodeID> &border_source_nodes,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const SourceSinkNodes &sink_nodes,
|
||||
const FlowEdges &flow) const;
|
||||
|
||||
std::uint32_t BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const;
|
||||
// Using the above levels (see ComputeLevelGraph), we can use multiple DFS (that can now be
|
||||
// directed at the sink) to find a flow that completely blocks the level graph (i.e. no path
|
||||
// with increasing level exists from `s` to `t`).
|
||||
std::size_t BlockingFlow(FlowEdges &flow,
|
||||
LevelGraph &levels,
|
||||
const GraphView &view,
|
||||
const SourceSinkNodes &source_nodes,
|
||||
const std::vector<NodeID> &border_sink_nodes) const;
|
||||
|
||||
// Finds a single augmenting path from a node to the sink side following levels in the level
|
||||
// graph. We don't actually remove the edges, so we have to check for increasing level values.
|
||||
// Since we know which sinks have been reached, we actually search for these paths starting at
|
||||
// sink nodes, instead of the source, so we can save a few dfs runs
|
||||
std::vector<NodeID> GetAugmentingPath(LevelGraph &levels,
|
||||
const NodeID from,
|
||||
const GraphView &view,
|
||||
const FlowEdges &flow,
|
||||
const SourceSinkNodes &sink_nodes) const;
|
||||
const SourceSinkNodes &source_nodes) const;
|
||||
|
||||
// Builds an actual cut result from a level graph
|
||||
MinCut MakeCut(const GraphView &view, const LevelGraph &levels) const;
|
||||
MinCut
|
||||
MakeCut(const GraphView &view, const LevelGraph &levels, const std::size_t flow_value) const;
|
||||
};
|
||||
|
||||
} // namespace partition
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
|
||||
#include <boost/iterator/filter_iterator.hpp>
|
||||
#include <boost/iterator/iterator_facade.hpp>
|
||||
#include <boost/range/iterator_range.hpp>
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
@@ -20,32 +21,38 @@ namespace partition
|
||||
class GraphView
|
||||
{
|
||||
public:
|
||||
GraphView(const BisectionGraph &graph,
|
||||
const BisectionGraph::ConstNodeIterator begin,
|
||||
const BisectionGraph::ConstNodeIterator end);
|
||||
using ConstNodeIterator = BisectionGraph::ConstNodeIterator;
|
||||
using NodeIterator = BisectionGraph::NodeIterator;
|
||||
using NodeT = BisectionGraph::NodeT;
|
||||
using EdgeT = BisectionGraph::EdgeT;
|
||||
|
||||
// Construction either for a subrange, or for a full range
|
||||
GraphView(const BisectionGraph &graph);
|
||||
GraphView(const BisectionGraph &graph,
|
||||
const ConstNodeIterator begin,
|
||||
const ConstNodeIterator end);
|
||||
|
||||
// construction from a different view, no need to keep the graph around
|
||||
GraphView(const GraphView &view, const ConstNodeIterator begin, const ConstNodeIterator end);
|
||||
|
||||
// Number of nodes _in this sub-graph.
|
||||
std::size_t NumberOfNodes() const;
|
||||
|
||||
BisectionGraph::ConstNodeIterator Begin() const;
|
||||
BisectionGraph::ConstNodeIterator End() const;
|
||||
// Iteration over all nodes (direct access into the node)
|
||||
ConstNodeIterator Begin() const;
|
||||
ConstNodeIterator End() const;
|
||||
|
||||
const BisectionNode &GetNode(const NodeID nid) const;
|
||||
const BisectionEdge &GetEdge(const EdgeID eid) const;
|
||||
// Re-Construct the ID of a node from a reference
|
||||
NodeID GetID(const NodeT &node) const;
|
||||
|
||||
NodeID GetID(const BisectionGraph::NodeT &node) const;
|
||||
// Access into single nodes/Edges
|
||||
const NodeT &Node(const NodeID nid) const;
|
||||
const EdgeT &Edge(const EdgeID eid) 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));
|
||||
}
|
||||
// Access into all Edges
|
||||
auto Edges(const NodeID nid) const { return bisection_graph.Edges(*(begin + nid)); }
|
||||
auto BeginEdges(const NodeID nid) const { return bisection_graph.BeginEdges(*(begin + nid)); }
|
||||
auto EndEdges(const NodeID nid) const { return bisection_graph.EndEdges(*(begin + nid)); }
|
||||
|
||||
private:
|
||||
const BisectionGraph &bisection_graph;
|
||||
|
||||
@@ -15,6 +15,9 @@ namespace osrm
|
||||
namespace partition
|
||||
{
|
||||
|
||||
// forward declaration to allow finding friends
|
||||
template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph;
|
||||
|
||||
// wrapper for nodes to augment with a tag storing first edge id
|
||||
template <typename Base> class NodeEntryWrapper : public Base
|
||||
{
|
||||
@@ -25,10 +28,17 @@ template <typename Base> class NodeEntryWrapper : public Base
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
// only to be modified by the graph itself
|
||||
std::size_t edges_begin;
|
||||
std::size_t edges_end;
|
||||
|
||||
// give the graph access to the node data wrapper
|
||||
template <typename NodeEntryT, typename EdgeEntryT> friend class RemappableGraph;
|
||||
};
|
||||
|
||||
using RemappableGraphNode = NodeEntryWrapper<struct zero_base_class>;
|
||||
|
||||
template <typename Base> class GraphConstructionWrapper : public Base
|
||||
{
|
||||
public:
|
||||
@@ -106,10 +116,19 @@ template <typename NodeEntryT, typename EdgeEntryT> class RemappableGraph
|
||||
auto BeginEdges(const NodeT &node) { return edges.begin() + node.edges_begin; }
|
||||
auto EndEdges(const NodeT &node) { return edges.begin() + node.edges_end; }
|
||||
|
||||
EdgeID BeginEdgeID(const NodeID nid) const { return nodes[nid].edges_begin; }
|
||||
EdgeID EndEdgeID(const NodeID nid) const { return nodes[nid].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()); }
|
||||
|
||||
NodeID GetID(const NodeT &node)
|
||||
{
|
||||
BOOST_ASSERT(&node >= &nodes[0] && &node <= &nodes.back());
|
||||
return (&node - &nodes[0]);
|
||||
}
|
||||
|
||||
NodeIterator Begin() { return nodes.begin(); }
|
||||
NodeIterator End() { return nodes.end(); }
|
||||
ConstNodeIterator CBegin() const { return nodes.cbegin(); }
|
||||
|
||||
@@ -22,6 +22,8 @@ class RecursiveBisection
|
||||
std::size_t num_optimizing_cuts,
|
||||
BisectionGraph &bisection_graph);
|
||||
|
||||
const std::vector<RecursiveBisectionState::BisectionID> &BisectionIDs() const;
|
||||
|
||||
private:
|
||||
BisectionGraph &bisection_graph;
|
||||
RecursiveBisectionState internal_state;
|
||||
|
||||
@@ -77,6 +77,8 @@ class RecursiveBisectionState
|
||||
const std::size_t depth,
|
||||
const std::vector<bool> &partition);
|
||||
|
||||
const std::vector<BisectionID> &BisectionIDs() const;
|
||||
|
||||
private:
|
||||
BisectionGraph &bisection_graph;
|
||||
std::vector<BisectionID> bisection_ids;
|
||||
|
||||
Reference in New Issue
Block a user