mukesh sharma
mukesh sharma

Reputation: 11

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

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

Upvotes: 0

Views: 627

Answers (1)

sehe
sehe

Reputation: 393789

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 --> 

Upvotes: 2

Related Questions