Skip to content

Latest commit

 

History

History
136 lines (104 loc) · 6.45 KB

osrm_partition_detail.md

File metadata and controls

136 lines (104 loc) · 6.45 KB

OSRM Partition Detail

Partition Input

The input for partition step is cnbg_to_ebg and ebg.
cnbg_to_ebg maps node based graph to edge based graph. It records edges in the format as

struct CompressedNodeBasedGraphEdge
{
    NodeID source;
    NodeID target;
};

ebg contains uncontracted edge-based graph, indexed by EBG NodeID

struct Coordinate
{
    FixedLongitude lon;
    FixedLatitude lat;
}

More context could be found here

Generate Graph

BisectionGraph(type, interface) is the data structure used for graph partition step. makeBisectionGraph is the function used to construct which, it reorder edges and build adjacent list

inline BisectionGraph makeBisectionGraph(const std::vector<util::Coordinate> &coordinates,
                                        const std::vector<BisectionInputEdge> &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 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);
   };
//...

}

The definition of two important data structure worth you to take a look: BisectionInputEdge and BisectionGraphNode.

Bisec Graph

Most of partition logic happened in the constructor of RecursiveBisection. The use of Intel-thread-building-blocks could speed up these process dramatically.

    // Parallelize recursive bisection trees. Root cut happens serially (well, this is a lie:
    // since we handle big components in parallel, too. But we don't know this and
    // don't have to. TBB's scheduler handles nested parallelism just fine).
    //
    //     [   |   ]
    //      /     \         root cut
    //  [ | ]     [ | ]
    //  /   \     /   \     descend, do cuts in parallel
    //
    // https://www.threadingbuildingblocks.org/docs/help/index.htm#reference/algorithms/parallel_do_func.html

    // [Perry] TreeNode likes bisection tasks, until reach stop condition it will continue to split and add to the tree
    struct TreeNode
    {
        BisectionGraphView graph;
        std::uint64_t depth;
    };
    
    // Bisect graph into two parts. Get partition point and recurse left and right in parallel.
    tbb::parallel_do(begin(forest), end(forest), [&](const TreeNode &node, Feeder &feeder) {
        // ...
    }

Find strong connected components

PrePartitionWithSCC is the function calculate strong connected component, and you could find the implementation of algorithm here

Find most suitable bisection

Inertial flow is the algorithm used for graph partition, which is the most important part of CRP and MLD. This blog describe the logic pretty clear, and its coming from author for this part's code.

// entrance point
DinicMaxFlow::MinCut computeInertialFlowCut(const BisectionGraphView &view,
                                            const std::size_t num_slopes,
                                            const double balance,
                                            const double source_sink_rate)
//...

// main logic happened in this function
DinicMaxFlow::MinCut bestMinCut(const BisectionGraphView &view,
                                const std::size_t n,
                                const double ratio,
                                const double balance)

bestMinCut will try with different set of source/sink nodes and find best cut on each of them, then pick the one with minimum cost for bestMinCut.
For selecting different source/sink nodes, you could go to function makeSpatialOrder, and function reorderFirstLast is the one you should not missed.
After source/sink nodes be selected, OSRM use Dinic algorithm to calculate max flow min cut. Implementation is here and you could follow the steps of function listed below

    DinicMaxFlow::ComputeLevelGraph

    DinicMaxFlow::BlockingFlow

    DinicMaxFlow::GetAugmentingPath

    DinicMaxFlow::MakeCut

Convert bisection result to partition

Logic can be found in function bisectionToPartition.

Remove unconnected boundary edges

Logic can be found in function removeUnconnectedBoundaryNodes.

For example,

    /*
    0---1  4---5  8---7 
     \ /    \ /    \ /
      2      3------6
    */

If the partition algorithm put {0, 1, 2, 3} in one partition and {4, 5, 6, 7, 8} in another partition, this function will dectect that and make partition as {3, 4, 5, 6, 7, 8}

Renumber graph

Logic can be found in function makePermutation

Output