Search code examples
c++algorithmboostgraphboost-graph

Boost graph list or vec


I've been spending quite a few days working with the boost graph library. As far as I understand, when considering VertexList and EdgeList storage :

vecS :

  • possess an index, so can be access with it
  • when removing vertices, iterator are invalidated

listS :

  • no index
  • does not invalidate iterator

It's a bit short but that's to the point for my problem. I need those index number and I want to be able to easily remove vertices later on.

I have a working algorithm with this graph structure :

typedef boost::adjacency_list<
        boost::vecS, boost::vecS, boost::undirectedS, 
        topologicalmap::Intersection_Graph ,
        boost::edge_weight_t, 
        boost::no_property > Graph_boost;

I have a custom structure Intersection_Graph for my vertices that I need to use. Here I use vecS.

I want to use listS instead to be able to remove vertices. As well, I want to be able to use it later with Dijkstra algorithm.

I kind of understand that I need to have boost::vertex_index_t in my list but I am really confused as to how to do it and keep my custom struct at the same time.

I tried something along those lines :

typedef boost::adjacency_list<
        boost::listS, boost::listS, boost::undirectedS, 
        boost::property<boost::vertex_index_t, topologicalmap::Intersection_Graph>,
        boost::edge_weight_t, 
        boost::no_property > Graph_boost;

But I can't even access my custom struct anymore. Plus, index access don't work.

I really need that index access capability since the algorithm my graph will depend on return the index of the parent node. I feel like I could get away with using a Vertex instead of indexes but it would imply a major re-writing of the code and I want to know if I can avoid it.

So my question is : is there any way to have listS behaving in a vecS like manner while keeping the advantages of listS ?

Please, bear with me if it sounds stupid. I'm quite confuse at the moment, so I might have say something stupid. If you need more information, just ask.


Solution

  • The interior properties formulation is:

    property<tag, type, next_property>
    

    Of course, unless you make Intersection_Graph behave like an integral type you cannot use it directly as the type of the vertex_index property. It's also likely not what you wanted.

    This looks closer:

    boost::property<boost::vertex_index_t, int, topologicalmap::Intersection_Graph>
    

    And it would declare two properties:

    1. an interior property tagged vertex_index_t (type int)
    2. a bundled property (typed Intersection_Graph). Note that bundled properties are implicitly accessible through the vertex_bundle_t tag.

    Now with this in mind, everything should be plain sailing:

    Live On Coliru

    #include <boost/graph/adjacency_list.hpp>
    #include <boost/graph/random.hpp>
    #include <boost/graph/graph_utility.hpp>
    #include <boost/graph/iteration_macros.hpp>
    
    #include <random>
    #include <iostream>
    
    using namespace boost;
    
    namespace topologicalmap {
        struct Intersection_Graph {
            std::string bundled;
        };
    }
    
    typedef boost::adjacency_list<
            boost::listS, boost::listS, boost::undirectedS, 
            boost::property<boost::vertex_index_t, int, topologicalmap::Intersection_Graph>,
            boost::edge_weight_t, 
            boost::no_property > Graph_boost;
    
    int main() {
    
        std::mt19937 prng { std::random_device {} () };
        Graph_boost g;
    
        generate_random_graph(g, 10, 20, prng);
    
        // assign indices
        int i = 0;
        BGL_FORALL_VERTICES(v, g, Graph_boost) { 
            get(vertex_index, g)[v] = i; 
            g[v].bundled = "id:" + std::to_string(i);
    
            i++;
        }
    
        // print the graph using the `bundled` property as a label:
        print_graph(g, get(&topologicalmap::Intersection_Graph::bundled, g));
    
        // do some index accesses:
        for (int i : {1,7})
            std::cout << "\nVertex at index #" << i << " has a bundled property of '" << g[vertex(i,g)].bundled << "'";
    }
    

    Which prints e.g. (random generated each run)

    id:0 <--> id:8 id:8 id:7 id:6 id:1 
    id:1 <--> id:3 id:4 id:4 id:3 id:0 id:2 
    id:2 <--> id:7 id:1 
    id:3 <--> id:1 id:7 id:1 id:9 id:4 
    id:4 <--> id:1 id:1 id:5 id:6 id:3 
    id:5 <--> id:4 id:9 
    id:6 <--> id:0 id:9 id:4 id:8 
    id:7 <--> id:3 id:0 id:2 id:9 
    id:8 <--> id:0 id:0 id:6 
    id:9 <--> id:7 id:6 id:3 id:5 
    
    Vertex at index #1 has a bundled property of 'id:1'
    Vertex at index #7 has a bundled property of 'id:7'
    

    Notes:

    • the fact that the graph "knows" vertex_index now doesn't mean it gets maintained; you have to fill it yourself:

      int i = 0;
      BGL_FORALL_VERTICES(v, g, Graph_boost) get(vertex_index, g)[v] = i++; 
      
    • you don't actually need to have vertex_index associated with your graph type, because you can pass it in as a named parameter to all relevant algorithms AFAIK. This includes constructing derived property maps that rely on a vertex_index (e.g. make_iterator_property_map)

    • I believe it's also possible to associate a vertex index using graph traits (but I haven't done so in the past). This seems like a nice way to go if you e.g. wanted to store the index in a member of your Intersection_Graph struct.
    • Like I said in my comment you could probably not require any of this if you stored vertex_descriptors instead of integral indexes.