Search code examples
c++boostboost-graph

Extract the adjacency matrix from a BGL graph


Using the Boost Graph Library I am looking for a way to extract the adjacency matrix from an underlying graph represented by either boost::adjacency_list or boost::adjacency_matrix. I'd like to use this matrix in conjunction with boost::numeric::ublas to solve a system of simultaneous linear equations.

Here is a minimal example to get you going:

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/adjacency_matrix.hpp>

using namespace boost;

typedef boost::adjacency_list< listS, vecS, directedS > ListGraph;
typedef boost::adjacency_matrix< directedS > MatrixGraph;

int main(){ 

  ListGraph lg; 
  add_edge (0, 1, lg); 
  add_edge (0, 3, lg); 
  add_edge (1, 2, lg); 
  add_edge (2, 3, lg); 

  //How do I get the adjacency matrix underlying lg?

  MatrixGraph mg(3); 
  add_edge (0, 1, mg); 
  add_edge (0, 3, mg); 
  add_edge (1, 2, mg); 
  add_edge (2, 3, mg); 

  //How do I get the adjacency matrix underlying mg?

}

If anyone could come up with an efficient way to obtain the adjacency matrix I would be much obliged. Ideally the solution is compatible with uBLAS. I wonder if there is a way to avoid iteration through the entire graph.


Solution

  • The easiest way to convert adjacency_list into adjacency_matrix is to use boost::copy_graph

    Your code for MatrixGraph mg should be modified as follows

    #include <boost/graph/copy.hpp>
    #include <cassert>
    
    using namespace boost;
    
    typedef boost::adjacency_list< listS, vecS, directedS > ListGraph;
    typedef boost::adjacency_matrix< directedS > MatrixGraph;
    
    int main(){
    
        ListGraph lg;
        add_edge(0, 1, lg);
        add_edge(0, 3, lg);
        add_edge(1, 2, lg);
        add_edge(2, 3, lg);
    
        //How do I get the adjacency matrix underlying lg?
    
        //How do I get the adjacency matrix underlying mg?   
        MatrixGraph mg( num_vertices(lg));
        boost::copy_graph(lg, mg);
    }
    

    Now, to use adjacency matrix with ublas or similar, you can write a simple "access" class to make syntax more compliant with ublas. Continuing previous snippet we get:

    template <class Graph>
    class MatrixAccessor
    {
    public:
        typedef typename Graph::Matrix Matrix; //actually a vector<
        typedef typename Matrix::const_reference const_reference;
    
    
        MatrixAccessor(const Graph* g)
            : m_g(g)
        {
            static_assert(boost::is_same<size_t, typename Graph::vertex_descriptor>::value, "Vertex descriptor should be of integer type");
        }
    
        const_reference operator()(size_t u, size_t v) const
        {
            return m_g->get_edge(u, v);
        }
    
        const Graph* m_g;
    };
    
    void use_matrix(const MatrixGraph & mg)
    {
        MatrixAccessor<MatrixGraph> matr(&mg);
        assert(matr(0, 1) == 1);
        assert(matr(0, 2) == 0);
    }
    

    In case your adjacency_matrix has some edge-bundled properties, you might need to modify the operator() in MatrixAccessor.

    Depending on how much uBLAS you use, you can refine MatrixAccessor further. For example, out_edge_iterator for a given vertex of a MatrixGraph is actually an iterator over matrix column; vertex_iterator can be treated as iterator over matrix rows, etc.

    Of course, graph matrix is immutable and as such should be used with care.