Need to find sub graphs from one big graph using boost::graph

浪尽此生 提交于 2020-01-03 05:13:11

问题


PH -> PH1
PH -> PH2
PH1 -> N1
PH1 -> N2
PH2 -> N3
PH2 -> N4

required output as :

sub graph 1 : 

    PH1 -> N1
    PH1 -> N2

sub graph 2 : 
    PH2 -> N3
    PH2 -> N3

回答1:


This is almost trivial using connected_components.

The complicating thing is to ignore the PH node. You didn't say whether this node is given or should be detected. I have written some code to try to detect it.

Let's Start

#include <boost/graph/adjacency_list.hpp>

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
    boost::property<boost::vertex_name_t, std::string> >;

We want to implement roughly the following steps:

using ComponentId = int;
using Mappings    = std::vector<ComponentId>;
using Graphs      = std::vector<Graph>;

Graph build();
Mappings map_components(Graph const&);
Graphs split(Graph const&, Mappings const&);

And the main program will look like

#include <boost/graph/graph_utility.hpp>
int main() {
    Graph g = build();

    Mappings components = map_components(g);

    for (auto& sub : split(g, components)) {
        std::cout << "\n========================\n";
        print_graph(sub, get(boost::vertex_name, sub));
    }
}

Sample Data

This is straight-forward since we used the vertex_name property:

using Vertex = Graph::vertex_descriptor;

Graph build() {
    Graph g;

    Vertex PH = add_vertex({"PH"}, g);
    Vertex PH1 = add_vertex({"PH1"}, g);
    Vertex PH2 = add_vertex({"PH2"}, g);
    Vertex N1 = add_vertex({"N1"}, g);
    Vertex N2 = add_vertex({"N2"}, g);
    Vertex N3 = add_vertex({"N3"}, g);
    Vertex N4 = add_vertex({"N4"}, g);

    add_edge(PH, PH1, g);
    add_edge(PH, PH2, g);
    add_edge(PH1, N1, g);
    add_edge(PH1, N2, g);
    add_edge(PH2, N3, g);
    add_edge(PH2, N4, g);
    return g;
}

Mapping Components

This is not too bad:

#include <boost/graph/connected_components.hpp> // connected_components
Mappings naive_components(Graph const& g) {
    Mappings mappings(num_vertices(g));
    int num = boost::connected_components(g, mappings.data());
    return mappings;
}

Except, everything is connected, so we get 1 component containing all the vertices... Let's use articulation_points to "ignore" a vertex first:

#include <boost/graph/biconnected_components.hpp> // articulation_points
#include <boost/graph/connected_components.hpp> // connected_components
#include <boost/graph/filtered_graph.hpp>
#include <boost/function.hpp>

using Filtered = boost::filtered_graph<Graph, boost::keep_all, boost::function<bool(Vertex)> >;

Mappings map_components(Graph const& g) {
    Mappings mappings(num_vertices(g));

    std::vector<Vertex> ap;
    articulation_points(g, back_inserter(ap));

    if (!ap.empty()) {
        // get the articulation point with the lowest degree
        nth_element(ap.begin(), ap.begin()+1, ap.end(), [&](Vertex a, Vertex b) { return degree(a, g) < degree(b, g); });
        Vertex ignored = ap.front();

        std::cout << "Igoring articulation point " << get(boost::vertex_name, g, ignored) << " from graph\n";
        Filtered fg(g, {}, [&](Vertex v) { return ignored != v; });

        int num = boost::connected_components(fg, mappings.data());
        mappings[ignored] = num; // make sure the ignored vertex is in its own component
    }
    return mappings;
}

That's basically doing the same thing, but it ignores the PH node. Note that we try to make sure we cut as few edges as possible (by sorting by degree).

Splitting

Splitting into separate graphs is almost a formality (re-using the same Filtered graph declarations):

#include <boost/graph/copy.hpp>

Graphs split(Graph const& g, Mappings const& components) {
    if (components.empty())
        return {};

    Graphs results;

    auto highest = *std::max_element(components.begin(), components.end());
    for (int c = 0; c <= highest; ++c) {
        results.emplace_back();
        boost::copy_graph(Filtered(g, {}, [c, &components](Vertex v) { return components.at(v) == c; }), results.back());
    }

    return results;
}

Full Listing

Live On Coliru

#include <boost/graph/adjacency_list.hpp>

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, boost::property<boost::vertex_name_t, std::string> >;
using ComponentId = int;
using Mappings    = std::vector<ComponentId>;
using Graphs      = std::vector<Graph>;

Graph build();
Mappings map_components(Graph const&);
Graphs split(Graph const&, Mappings const&);

#include <boost/graph/graph_utility.hpp>
int main() {
    Graph g = build();

    Mappings components = map_components(g);

    for (auto& sub : split(g, components)) {
        std::cout << "\n========================\n";
        print_graph(sub, get(boost::vertex_name, sub));
    }
}

using Vertex = Graph::vertex_descriptor;

Graph build() {
    Graph g;

    Vertex PH = add_vertex({"PH"}, g);
    Vertex PH1 = add_vertex({"PH1"}, g);
    Vertex PH2 = add_vertex({"PH2"}, g);
    Vertex N1 = add_vertex({"N1"}, g);
    Vertex N2 = add_vertex({"N2"}, g);
    Vertex N3 = add_vertex({"N3"}, g);
    Vertex N4 = add_vertex({"N4"}, g);

    add_edge(PH, PH1, g);
    add_edge(PH, PH2, g);
    add_edge(PH1, N1, g);
    add_edge(PH1, N2, g);
    add_edge(PH2, N3, g);
    add_edge(PH2, N4, g);
    return g;
}

#include <boost/graph/biconnected_components.hpp> // articulation_points
#include <boost/graph/connected_components.hpp> // connected_components
#include <boost/graph/filtered_graph.hpp>
#include <boost/function.hpp>

using Filtered = boost::filtered_graph<Graph, boost::keep_all, boost::function<bool(Vertex)> >;

Mappings map_components(Graph const& g) {
    Mappings mappings(num_vertices(g));

    std::vector<Vertex> ap;
    articulation_points(g, back_inserter(ap));

    if (!ap.empty()) {
        // get the articulation point with the lowest degree
        nth_element(ap.begin(), ap.begin()+1, ap.end(), [&](Vertex a, Vertex b) { return degree(a, g) < degree(b, g); });
        Vertex ignored = ap.front();

        std::cout << "Igoring articulation point " << get(boost::vertex_name, g, ignored) << " from graph\n";
        Filtered fg(g, {}, [&](Vertex v) { return ignored != v; });

        int num = boost::connected_components(fg, mappings.data());
        mappings[ignored] = num; // make sure the ignored vertex is in its own component
    }
    return mappings;
}

#include <boost/graph/copy.hpp>

Graphs split(Graph const& g, Mappings const& components) {
    if (components.empty())
        return {};

    Graphs results;

    auto highest = *std::max_element(components.begin(), components.end());
    for (int c = 0; c <= highest; ++c) {
        results.emplace_back();
        boost::copy_graph(Filtered(g, {}, [c, &components](Vertex v) { return components.at(v) == c; }), results.back());
    }

    return results;
}

Prints

Igoring articulation point PH from graph

========================
PH1 --> N1 N2 
N1 --> 
N2 --> 

========================
PH2 --> N3 N4 
N3 --> 
N4 --> 

========================
PH --> 


来源:https://stackoverflow.com/questions/48011373/need-to-find-sub-graphs-from-one-big-graph-using-boostgraph

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!