40

I am trying to figure out how to use boost::graph to store some information. However, there is information I want tied to each vertex. Staring at the documentation for the library reveals either(a)badly written documentation, or (b), I'm obviously not as good at C++ as I thought. Pick two.

I am looking for a simple example use.

Tim
  • 41,901
  • 18
  • 127
  • 145
Paul Nathan
  • 39,638
  • 28
  • 112
  • 212

5 Answers5

82

Bundled properties are straightforward to use:

using namespace boost;

struct vertex_info { 
    std::string whatever; 
    int othervalue; 
    std::vector<int> some_values; 
};

typedef adjacency_list<vecS, vecS, undirectedS, vertex_info> graph_t;

graph_t g(n);

g[0].whatever = "Vertex 0";

[...]

and so on.

Please also refer to the docs.

The other type of vertex property that are very useful are external properties. You can declare std::vectors of the appropriate size and use them as properties.

baol
  • 4,362
  • 34
  • 44
  • 17
    This needs to be the accepted answer. Especially because a competing answer, currently top voted, is a reimplementation of this feature you've shown is already in the library! Your example code is also the most straightforward example I've found on the web of how to set up a simple boost graph library usage. Thanks. – Dennis Jun 05 '10 at 21:13
  • +1; sorry I'm late to the party :) Just a side note re: "You can declare std::vectors" -- that is only true if you use `vecS`, and then only for vertices (not edges) IIRC. – phooji Apr 20 '11 at 19:03
  • 1
    You can also use multiple property through the magic of TMP : here ->http://www.informit.com/articles/article.aspx?p=25756&seqNum=7 – Ramadheer Singh Jul 19 '11 at 23:51
  • Just for reference, here is [the relevant page of the boost documentation](http://www.boost.org/doc/libs/release/libs/graph/doc/bundles.html). – Connor McKay Nov 17 '12 at 21:51
16

I don't like the nested-template property approach of boost::graph, so I wrote a small wrapper around everything, that basically allows to put any struct/class as a vertex/edge property. One can access properties accessing the struct members.

To keep it flexible these structs are defined as template parameter.

Here the Code:

/* definition of basic boost::graph properties */
enum vertex_properties_t { vertex_properties };
enum edge_properties_t { edge_properties };
namespace boost {
    BOOST_INSTALL_PROPERTY(vertex, properties);
    BOOST_INSTALL_PROPERTY(edge, properties);
}


/* the graph base class template */
template < typename VERTEXPROPERTIES, typename EDGEPROPERTIES >
class Graph
{
public:

    /* an adjacency_list like we need it */
    typedef adjacency_list<
        setS, // disallow parallel edges
        listS, // vertex container
        bidirectionalS, // directed graph
        property<vertex_properties_t, VERTEXPROPERTIES>,
        property<edge_properties_t, EDGEPROPERTIES>
    > GraphContainer;


    /* a bunch of graph-specific typedefs */
    typedef typename graph_traits<GraphContainer>::vertex_descriptor Vertex;
    typedef typename graph_traits<GraphContainer>::edge_descriptor Edge;
    typedef std::pair<Edge, Edge> EdgePair;

    typedef typename graph_traits<GraphContainer>::vertex_iterator vertex_iter;
    typedef typename graph_traits<GraphContainer>::edge_iterator edge_iter;
    typedef typename graph_traits<GraphContainer>::adjacency_iterator adjacency_iter;
    typedef typename graph_traits<GraphContainer>::out_edge_iterator out_edge_iter;

    typedef typename graph_traits<GraphContainer>::degree_size_type degree_t;

    typedef std::pair<adjacency_iter, adjacency_iter> adjacency_vertex_range_t;
    typedef std::pair<out_edge_iter, out_edge_iter> out_edge_range_t;
    typedef std::pair<vertex_iter, vertex_iter> vertex_range_t;
    typedef std::pair<edge_iter, edge_iter> edge_range_t;


    /* constructors etc. */
    Graph()
    {}

    Graph(const Graph& g) :
        graph(g.graph)
    {}

    virtual ~Graph()
    {}


    /* structure modification methods */
    void Clear()
    {
        graph.clear();
    }

    Vertex AddVertex(const VERTEXPROPERTIES& prop)
    {
        Vertex v = add_vertex(graph);
        properties(v) = prop;
        return v;
    }

    void RemoveVertex(const Vertex& v)
    {
        clear_vertex(v, graph);
        remove_vertex(v, graph);
    }

    EdgePair AddEdge(const Vertex& v1, const Vertex& v2, const EDGEPROPERTIES& prop_12, const EDGEPROPERTIES& prop_21)
    {
        /* TODO: maybe one wants to check if this edge could be inserted */
        Edge addedEdge1 = add_edge(v1, v2, graph).first;
        Edge addedEdge2 = add_edge(v2, v1, graph).first;

        properties(addedEdge1) = prop_12;
        properties(addedEdge2) = prop_21;

        return EdgePair(addedEdge1, addedEdge2);
    }


    /* property access */
    VERTEXPROPERTIES& properties(const Vertex& v)
    {
        typename property_map<GraphContainer, vertex_properties_t>::type param = get(vertex_properties, graph);
        return param[v];
    }

    const VERTEXPROPERTIES& properties(const Vertex& v) const
    {
        typename property_map<GraphContainer, vertex_properties_t>::const_type param = get(vertex_properties, graph);
        return param[v];
    }

    EDGEPROPERTIES& properties(const Edge& v)
    {
        typename property_map<GraphContainer, edge_properties_t>::type param = get(edge_properties, graph);
        return param[v];
    }

    const EDGEPROPERTIES& properties(const Edge& v) const
    {
        typename property_map<GraphContainer, edge_properties_t>::const_type param = get(edge_properties, graph);
        return param[v];
    }


    /* selectors and properties */
    const GraphContainer& getGraph() const
    {
        return graph;
    }

    vertex_range_t getVertices() const
    {
        return vertices(graph);
    }

    adjacency_vertex_range_t getAdjacentVertices(const Vertex& v) const
    {
        return adjacent_vertices(v, graph);
    }

    int getVertexCount() const
    {
        return num_vertices(graph);
    }

    int getVertexDegree(const Vertex& v) const
    {
        return out_degree(v, graph);
    }


    /* operators */
    Graph& operator=(const Graph &rhs)
    {
        graph = rhs.graph;
        return *this;
    }

protected:
    GraphContainer graph;
};

Using this you can access properties like this:

struct VertexProperties {
    int i;
};

struct EdgeProperties {
};

typedef Graph<VertexProperties, EdgeProperties> MyGraph;

MyGraph g;

VertexProperties vp;
vp.i = 42;

MyGraph::Vertex v = g.AddVertex(vp);

g.properties(v).i = 23;

Of course you may have other needs for your graph's structure, but modification of the code above should be pretty easy.

grefab
  • 1,997
  • 2
  • 22
  • 32
  • Great! This code is what made Boost Graph usable for me. I also don't like working with nested templates. – conradlee Mar 16 '10 at 12:16
  • 3
    Just for avoid problems to newbies like me. It's needed to add at the beginning of the code:#include #include #include #include #include #include using namespace boost; (I'm sorry for this horrible comment) – Manuel Feb 16 '12 at 18:12
  • AMAZING solution! It saved my day =) I'm just stuck on the iterators... I can't find a way to access the root node of my graph, any idea? – Tex Feb 22 '12 at 00:02
  • Sorry for the late reply. The code above just defines a graph structure (V,E) and does not store meta information like the presence of a root vertex. However, AddVertex() returns the created vertex, so you are able to take care for that yourself. – grefab Jul 05 '12 at 12:15
  • What "nested template"? If you mean the graph property "protocol", they're largely unnecessary. BGL has ***[Bundled Properties](http://www.boost.org/doc/libs/1_65_1/libs/graph/doc/bundles.html)*** (which may or may not have existed in 2009). I think this wrapper class has more downsides than upsides in modern BGL/C++. I've demonstrated it side-by-side in [this new post](https://stackoverflow.com/questions/47211401/what-should-be-the-return-value-of-a-custom-function-addedge-in-a-new-class-base) – sehe Nov 10 '17 at 00:29
  • Not sure if they existed then. The code above was intended to make the use of BGL as it was then easier so it is not necessary to write templated types everywhere vertices, edges or ranges of them are required. But this was more than eight years ago. A more modern solution using C++1x and ranges would look different, like you explained in your post. – grefab Nov 10 '17 at 07:04
4

Below is code I used to attach some properties to vertices, edges, and graphs. Note that vertex name and graph name are predefined properties (see boost/properties.hpp for a complete list) so that vertex_name_t and graph_name_t are already defined. However, vertex_location_t, edge_length_t, and graph_notes_t are my own properties and hence need definition. I cobbled together this code from various examples and documentation, and I'm not sure exactly what BOOST_INSTALL_PROPERTY does, but the code seems to work fine.

// Define custom properties
enum vertex_location_t { vertex_location };
enum edge_length_t     { edge_length     };
enum graph_notes_t     { graph_notes     };

namespace boost
{
    BOOST_INSTALL_PROPERTY(vertex, location);
    BOOST_INSTALL_PROPERTY(edge,   length  );
    BOOST_INSTALL_PROPERTY(graph,  notes   );
}

// Define vertex properties:  vertex name and location
typedef property<vertex_name_t,     string,
        property<vertex_location_t, Point3> >
VertexProperties;

// Define edge properties:  length
typedef property<edge_length_t, double> EdgeProperties;

// Define graph properties:  graph name and notes
typedef property<graph_name_t,  string,
        property<graph_notes_t, string> >
GraphProperties;

// Define a graph type
typedef adjacency_list
<
    vecS,       // edge container type
    vecS,       // vertex container type
    undirectedS,
    VertexProperties,
    EdgeProperties,
    GraphProperties
> Graph;
1

I consider Boost.Graph to have a very good documentation, but not truly for beginners on the matter. So here goes an example that i hope is simple enough !

//includes

// Create a name for your information
struct VertexInformation
{
  typedef boost::vertex_property_type type;
};

// Graph type, customize it to your needs
// This is when you decide what information will be attached to vertices and/or edges
// of MyGraph objects
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS,
  boost::property<VertexInformation, double> > MyGraph;

int main()
{
  MyGraph graph;

  // Create accessor for information
  typedef boost::property_map<MyGraph, VertexInformation>::type  InformationAccessor;
  InformationAccessor information( get( VertexInformation(), graph ) );

  // Create a vertex (for example purpose)
  typedef boost::graph_traits<MyGraph>::vertex_descriptor MyVertex;
  MyVertex vertex = add_vertex( graph );

  // Now you can access your information
  put( information, vertex, 1. );

  // returns 1 !
  get( information, vertex );
  return 0;
}
Benoît
  • 16,798
  • 8
  • 46
  • 66
  • 1
    So when you set the vertex properties template argument to `boost::property`, you are effectively "naming" a `double` vertex property "VertexInformation"? That is, why would you not put the `double value;` INSIDE the `VertexInformation` struct? – David Doria Nov 09 '16 at 17:46
1

I found the examples pretty useful. On windows it will be in your \Program Files\boost\boost_1_38\libs\graph\example directory.

kevin_bacon2.cpp uses vertex properties to store the names of actors.

Your vertex and edge properties can be stored in regular structs or classes.

eodonohoe
  • 264
  • 2
  • 6