C++ Boost

Attaching Data to Graphs

Real-world graphs usually need to store data on vertices and edges—for example, vertex names, edge weights, colors, coordinates, etc. BGL provides flexible mechanisms for attaching properties to graphs.

Two Approaches: Internal vs External Properties

BGL supports two ways to store properties:

Both approaches use the same property map interface, so algorithms work with either type transparently.

Internal Properties: The Basics

To add internal properties, use the property template in the graph type definition:

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

using namespace boost;

// Graph with edge weights
typedef adjacency_list<
    vecS,                              // OutEdgeList
    vecS,                              // VertexList
    directedS,                         // Directed
    no_property,                       // VertexProperties
    property<edge_weight_t, int>       // EdgeProperties (weight)
> WeightedGraph;

The template parameters for property are:

  1. Property tag: Identifies the property type (e.g., edge_weight_t, vertex_name_t)
  2. Value type: The data type of the property (e.g., int, std::string, double)

Predefined Property Tags

BGL provides many standard property tags:

TagTypical UseCommon Type
edge_weight_tEdge weights/costsint, double
edge_capacity_tFlow capacityint, double
vertex_name_tVertex labelsstd::string
vertex_color_tSearch statedefault_color_type
vertex_distance_tShortest path distanceint, double
vertex_index_tVertex numberingint
edge_index_tEdge numberingint

See Internal Properties for the complete list.

Setting Property Values During Construction

You can set edge properties when creating a graph:

typedef std::pair<int, int> Edge;

// Define edges
Edge edges[] = {
    Edge(0, 2), Edge(1, 1), Edge(1, 3),
    Edge(2, 1), Edge(2, 3), Edge(3, 4)
};

// Define corresponding weights
int weights[] = {1, 2, 1, 7, 3, 1};

const int num_vertices = 5;
const int num_edges = sizeof(edges) / sizeof(Edge);

// Create graph with edge weights
WeightedGraph g(edges, edges + num_edges, weights, num_vertices);

The weights array is passed to the constructor and automatically assigned to edges in order.

Accessing Internal Properties

Use property maps to access internal properties:

// Get the edge weight property map
typedef property_map<WeightedGraph, edge_weight_t>::type WeightMap;
WeightMap weight_map = get(edge_weight, g);

// Access edge weights
graph_traits<WeightedGraph>::edge_iterator ei, ei_end;
for (boost::tie(ei, ei_end) = edges(g); ei != ei_end; ++ei) {
    std::cout << "Edge (" << source(*ei, g) << "," 
              << target(*ei, g) << ") weight: "
              << weight_map[*ei] << std::endl;
}

Or use the shorthand get() and put() functions:

// Read a property
int w = get(weight_map, edge);

// Write a property  
put(weight_map, edge, 42);

// Or use operator[]
weight_map[edge] = 42;

Multiple Properties

Chain multiple property templates to add multiple properties:

// Graph with edge weight AND edge capacity
typedef adjacency_list<
    vecS, vecS, directedS,
    no_property,
    property<edge_weight_t, int,
        property<edge_capacity_t, int> >
> FlowGraph;

// Access each property separately
typedef property_map<FlowGraph, edge_weight_t>::type WeightMap;
typedef property_map<FlowGraph, edge_capacity_t>::type CapacityMap;

WeightMap weights = get(edge_weight, g);
CapacityMap capacities = get(edge_capacity, g);

Vertex Properties

Vertex properties work the same way:

// Graph with vertex names and edge weights
typedef adjacency_list<
    vecS, vecS, directedS,
    property<vertex_name_t, std::string>,      // Vertex property
    property<edge_weight_t, double>            // Edge property
> NamedGraph;

NamedGraph g(5);

// Get vertex name property map
typedef property_map<NamedGraph, vertex_name_t>::type NameMap;
NameMap name_map = get(vertex_name, g);

// Set vertex names
name_map[0] = "Alice";
name_map[1] = "Bob";
name_map[2] = "Charlie";

// Print vertex names
graph_traits<NamedGraph>::vertex_iterator vi, vi_end;
for (boost::tie(vi, vi_end) = vertices(g); vi != vi_end; ++vi) {
    std::cout << "Vertex " << *vi << ": " 
              << name_map[*vi] << std::endl;
}

External Properties: Using std::vector

For temporary algorithm state, external properties are often simpler:

typedef adjacency_list<vecS, vecS, directedS> Graph;
Graph g(5);
// ... add edges ...

// External property: vertex distances (for Dijkstra's algorithm)
std::vector<int> distances(num_vertices(g));

// std::vector iterators can be used as property maps
dijkstra_shortest_paths(g, source_vertex,
    distance_map(&distances[0]));  // Pass vector as property map

// Access results
for (int i = 0; i < distances.size(); ++i) {
    std::cout << "Distance to vertex " << i 
              << ": " << distances[i] << std::endl;
}

External Properties: Using std::map

When using listS for vertices (where descriptors aren't integers), use std::map:

typedef adjacency_list<vecS, listS, directedS> Graph;
typedef graph_traits<Graph>::vertex_descriptor Vertex;

Graph g;
// ... build graph ...

// Map from vertex descriptor to color
std::map<Vertex, default_color_type> color_map;

// Use associative_property_map wrapper
auto color = make_assoc_property_map(color_map);

// Use in algorithm
breadth_first_search(g, start_vertex,
    visitor(make_bfs_visitor(/* ... */))
    .color_map(color));

Custom Property Types

You can attach custom structs or classes as properties:

struct VertexData {
    std::string name;
    double x, y;  // Coordinates
    int importance;
};

struct EdgeData {
    double weight;
    std::string label;
};

typedef adjacency_list<
    vecS, vecS, directedS,
    VertexData,    // Custom vertex property
    EdgeData       // Custom edge property
> CustomGraph;

CustomGraph g(3);

// Access using [] operator on graph
g[0].name = "Node A";
g[0].x = 10.5;
g[0].y = 20.3;

// For edges, use edge descriptor
auto e = add_edge(0, 1, g).first;
g[e].weight = 3.14;
g[e].label = "important edge";

When to Use Internal vs External Properties

Use Internal Properties When... Use External Properties When...
Property is intrinsic to the graph (weights, names) Property is temporary algorithm state (colors, distances)
Property lifetime matches graph lifetime Multiple algorithms need different property sets
You want to save/load properties with the graph You want to avoid modifying graph type
Property is used by most algorithms Property is used by one specific algorithm

Complete Example

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

using namespace boost;

int main()
{
    // Graph with internal edge weights
    typedef adjacency_list<vecS, vecS, directedS,
                           no_property,
                           property<edge_weight_t, int> > Graph;
    
    typedef std::pair<int, int> Edge;
    Edge edges[] = {Edge(0,1), Edge(0,2), Edge(1,3), Edge(2,3)};
    int weights[] = {5, 3, 2, 7};
    
    Graph g(edges, edges + 4, weights, 4);
    
    // External property for distances
    std::vector<int> distances(num_vertices(g));
    
    // Run Dijkstra (uses internal weights, external distances)
    dijkstra_shortest_paths(g, 0,
        distance_map(&distances[0]));
    
    // Print results
    for (int i = 0; i < distances.size(); ++i)
        std::cout << "Distance to " << i 
                  << ": " << distances[i] << "\n";
    
    return 0;
}

For more details, see:

Copyright © 2000-2001 Jeremy Siek, Indiana University