0

I want to create the graph as following, first column is vertex, other's are adjacency vertex

  • 1 2 3 4 7
  • 2 1 3 4
  • 3 1 2 4
  • 4 1 2 3 5
  • 5 4 6 7 8
  • 6 5 7 8
  • 7 1 5 6 8
  • 8 5 6 7

I add the edges into the graph like this

using MinCutG = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS>;

MinCutG graph;
std::vector<std::vector<int> > input{ {1,2,3,4,7}, {2,1,3,4}, {3,1,2,4},
            {4,1,2,3,5}, {5,4,6,7,8}, {6,5,7,8}, {7,1,5,6,8}, {8,5,6,7}};
for(auto const &data : input){
    auto begin = std::begin(data);
    auto end = std::end(data);
    if(begin != end){
        auto const Vertex = *begin;
        ++begin;
        while(begin != end){
            boost::add_edge(Vertex, *begin, graph);        
            ++begin;
        }
    }
}

print the graph

template<typename Graph>
void print_adjacent_vertex(Graph const &g)
{
    for (auto vertex = vertices(g); vertex.first != vertex.second; ++vertex.first){
        std::cout << *vertex.first << " is connected with ";
        for (auto neighbour = adjacent_vertices(*vertex.first, g);
             neighbour.first != neighbour.second; ++neighbour.first){
            std::cout << *neighbour.first << " ";
        }
        std::cout<<std::endl;
    }
}

I expect the outputshould same as the input, but it is not The results are

  • 1 is connected with 2 3 4 7 2 3 4 7
  • 2 is connected with 1 1 3 4 3 4
  • 3 is connected with 1 2 1 2 4 4
  • 4 is connected with 1 2 3 1 2 3 5 5
  • 5 is connected with 4 4 6 7 8 6 7 8
  • 6 is connected with 5 5 7 8 7 8
  • 7 is connected with 1 5 6 1 5 6 8 8
  • 8 is connected with 5 6 7 5 6 7

my expected results

  • 1 is connected with 2 3 4 7
  • 2 is connected with 1 3 4
  • 3 is connected with 1 2 4
  • 4 is connected with 1 2 3 5
  • 5 is connected with 4 6 7 8
  • 6 is connected with 6 5 7 8
  • 7 is connected with 7 1 5 6 8
  • 8 is connected with 8 5 6 7
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
StereoMatching
  • 4,971
  • 6
  • 38
  • 70

2 Answers2

3

In short, using setS for the OutEdgeList template parameter will disable parallel edges, resulting in the desired output.

The first template parameter to boost::adjacency_list is OutEdgeList and it controls certain graph behaviors, such as allowing or disallowing parallel edges. In the case of undirected MinCutG graph, vecS is used as the OutEdgeList which will enable parallel edges. For example, if an undirected graph supported parallel edges, then with:

add_edge(1, 2, graph); // Vertex 1 and 2 are adjacent to one another via edge A.
add_edge(2, 1, graph); // Vertex 1 and 2 are adjacent to one another via edge B,
                       // which is parallel to edge A.

The adjacent_vertices() for vertex 1 will contain vertex 2 twice, once for each edge (A and B).

As noted in the documentation, parallel edges can be disabled by using setS or hash_setS selectors for the OutEdgeList. For example, change:

using MinCutG = boost::adjacency_list<
  boost::vecS, // OutEdgeList with parallel edges
  boost::vecS,
  boost::undirectedS>;

to:

using MinCutG = boost::adjacency_list<
  boost::setS, // OutEdgeList with no parallel edges
  boost::vecS,
  boost::undirectedS>;

Here is an example using the original code and only changing the OutEdgeList from vecS to setS:

#include <iostream>
#include <vector>
#include <boost/graph/adjacency_list.hpp>

template<typename Graph>
void print_adjacent_vertex(Graph const &g)
{
    for (auto vertex = vertices(g); vertex.first != vertex.second; 
         ++vertex.first){
        std::cout << *vertex.first << " is connected with ";
        for (auto neighbour = adjacent_vertices(*vertex.first, g);
             neighbour.first != neighbour.second; ++neighbour.first){
            std::cout << *neighbour.first << " ";
        }
        std::cout<<std::endl;
    }
}

int main()
{
    using MinCutG = boost::adjacency_list<
        boost::setS, boost::vecS, boost::undirectedS>;

    MinCutG graph;
    std::vector<std::vector<int> > input
    {
        {1,2,3,4,7},
        {2,1,3,4},
        {3,1,2,4},
        {4,1,2,3,5},
        {5,4,6,7,8},
        {6,5,7,8},
        {7,1,5,6,8},
        {8,5,6,7}
    };

    for(auto const &data : input){
        auto begin = std::begin(data);
        auto end = std::end(data);
        if(begin != end){
            auto const Vertex = *begin;
            ++begin;
            while(begin != end){
                boost::add_edge(Vertex, *begin, graph);        
                ++begin;
            }
        }
    }
    print_adjacent_vertex(graph);
}

Which produces the following output:

0 is connected with 
1 is connected with 2 3 4 7 
2 is connected with 1 3 4 
3 is connected with 1 2 4 
4 is connected with 1 2 3 5 
5 is connected with 4 6 7 8 
6 is connected with 5 7 8 
7 is connected with 1 5 6 8 
8 is connected with 5 6 7
Tanner Sansbury
  • 51,153
  • 9
  • 112
  • 169
0

As Tanner explained, you essentially add every edge twice, first as (a,b) and later as (b,a). One but rather expensive way to avoid this is to use setS as the template parameter which controls edges container. And this is what Tanner suggested in his answer.

Alternative is to check whether edge exists before adding it. It means the line

boost::add_edge(Vertex, *begin, graph);   

should be replaced with

if (! edge(Vertex, *begin, graph).second ) //edge does not exist yet
    boost::add_edge(Vertex, *begin, graph); 

In most situations this code is likely to be faster and to consume less memory.

Michael Simbirsky
  • 3,045
  • 1
  • 12
  • 24