I have my custom vertex and edge properties
namespace boost {
enum vertex_diagonal_t{vertex_diagonal = 999};
BOOST_INSTALL_PROPERTY(vertex, diagonal);
}
namespace boost {
enum edge_dominance_t{edge_dominance = 998};
BOOST_INSTALL_PROPERTY(edge, dominance);
}
I create my adjacency list with boost::property
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::bidirectionalS,
boost::property<boost::vertex_diagonal_t, const khut::diagonal*>,
boost::property<boost::edge_dominance_t, float>
> diagonal_dominance_graph;
typedef boost::property_map<diagonal_dominance_graph, boost::vertex_diagonal_t>::type diagonal_map_type;
typedef boost::property_map<diagonal_dominance_graph, boost::edge_dominance_t>::type dominance_map_type;
Now I want to loop through my own containers and add vertex
diagonal_dominance_graph graph;
for(storage_type::const_iterator i = repo_begining.begin(); i != repo_begining.end(); ++i){
diagonal_dominance_graph::vertex_descriptor dia_vertex = boost::add_vertex(graph);
//>> ?? HOW CAN I write Properties to dia_vertex HERE ?
//boost::property<boost::vertex_diagonal_t, const khut::diagonal*> p;
//boost::put(p, dia_vertex);
}
What I am not getting is How can I set properties of a vertex through vertex_descriptor. may be I am missing a simple function.
Please I don't need anything that makes BGL even more complex, or something that cleans and restructures the types in my example. I just need to know how to read/write properties through a vertex_descriptor or edge_descriptor
You're using property lists: they're documented here.
So in your example, you'd use
diagonal_map_type vp = get(boost::vertex_diagonal, graph);
using storage_type = std::vector<int>;
storage_type repo_begining(10);
for(storage_type::const_iterator i = repo_begining.begin(); i != repo_begining.end(); ++i) {
diagonal_dominance_graph::vertex_descriptor dia_vertex = boost::add_vertex(graph);
khut::diagonal* v = nullptr;
boost::put(vp, dia_vertex, v);
}
// likewise for edges
dominance_map_type ep = get(boost::edge_dominance, graph);
See it Live On Coliru
Bundled Properties
The very same documentation page says:
NOTE: The Boost Graph Library supports two interchangeable methods for specifying interior properties: bundled properties and property lists. The former is easier to use and requires less effort, whereas the latter is compatible with older, broken compilers and is backward-compatible with Boost versions prior to 1.32.0. If you absolutely require these compatibility features, read on to learn about property lists. Otherwise, we strongly suggest that you read about the bundled properties mechanism.
Boost 1.32 dates over 10 years ago! So, I'd suggest bundled properties:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace khut {
struct diagonal { };
struct MyVertexProperties {
diagonal const* diag_ptr;
};
struct MyEdgeProperties {
float dominance;
};
}
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::bidirectionalS,
khut::MyVertexProperties,
khut::MyEdgeProperties
> diagonal_dominance_graph;
#include <iostream>
int main() {
using namespace boost;
diagonal_dominance_graph g;
khut::diagonal d1, d2;
{
auto v1 = add_vertex(khut::MyVertexProperties { &d1 }, g);
auto v2 = add_vertex(khut::MyVertexProperties { &d2 }, g);
/*auto e1 = */add_edge(v1, v2, khut::MyEdgeProperties { 42.31415926 }, g);
}
for(diagonal_dominance_graph::vertex_descriptor vd : make_iterator_range(vertices(g)))
std::cout << "Is diagonal d1? " << std::boolalpha << (&d1 == g[vd].diag_ptr) << "\n";
for(diagonal_dominance_graph::edge_descriptor ed : make_iterator_range(edges(g)))
std::cout << "Edge dominance: " << g[ed].dominance << "\n";
}
Prints
Is diagonal d1? true
Is diagonal d1? false
Edge dominance: 42.3142
Related
Objective
Generate a random spanning tree on a randomly generated graph.
Why: because I don't know yet if I can directly generate random trees with specific number of nodes or leaves in BGL.
My problem
I think it boils down to struggling initializing the Auxillary Property Maps to a sensical default.
As you will see I've tried a number of combination of solutions, in the current state the code fails to compile with a cannot form a reference to 'void'.
What I tried
template<class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng)
{
// create empty graph
Graph g;
using vertex_t = typename Graph::vertex_descriptor;
using edge_t = typename Graph::edge_descriptor;
// The key and value types of the map must both be the graph's vertex type.
using predecessor_map_t = boost::static_property_map<vertex_t, vertex_t>;
predecessor_map_t predecessor_map = boost::make_static_property_map<vertex_t,vertex_t>(vertex_t());
// unweighted version selected by passing an object of type static_property_map<double> as the weight map, so let's go
using weight_map_t = boost::static_property_map< double >;
// weight_map_t weight_map = boost::make_transform_value_property_map([](edge_t& e) { return 1.0; }, get(boost::edge_bundle, g)); // nope
//weight_map_t weight_map = boost::make_static_property_map<double>(1.0); // yes but complicated
// weight_map_t weight_map; // nope: why isn't it default constructible?
double my_constant_weight = 1.0;
weight_map_t weight_map(my_constant_weight);
using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type;
color_map_t color_map ; // i suspect this is faulty
// mutate graph
boost::generate_random_graph(g, n_vertices, n_edges, rng);
// pick root, I guess we could as well pick 1st vertex
auto root = boost::random_vertex(g, rng);
boost::random_spanning_tree(g, rng, root, predecessor_map, weight_map, color_map);
return g;
}
First: Your own suspect
using color_map_t = typename boost::property_map<Graph, boost::vertex_color_t>::type;
color_map_t color_map; // i suspect this is faulty
Yes. PropertyMaps map properties. They are like references. Here, color_map
is essentially an unitialized reference. You need something like
color_map_t color_map = get(boost::vertex_color, g);
This, of course, assumes that a vertex_color_t property map has been
associated with the graph by traits. In other words, this assumes that the
property is an iternal property of the graph. Internal properties are often
used by default.
Second: A constant cannot be modified
You use a static property map:
auto predecessor_map =
boost::make_static_property_map<vertex_t, vertex_t>(vertex_t());
That just creates a "virtual" property map (without a backing data structure)
that returns the construction parameter on every key. Logically, the return
value is constant. However, predecessor map is an output parameter:
You will need an LValuePropertyMap there. E.g.
std::map<vertex_t, vertex_t> predecessors;
auto predecessor_map =boost::make_assoc_property_map(predecessors);
Or even
auto vindex = get(boost::vertex_index, g);
auto predecessors = std::vector<vertex_t>(num_vertices(g));
auto predecessor_map = boost::make_safe_iterator_property_map(
predecessors.begin(), predecessors.size(), vindex);
Which uses a vertex index to (optionally) translate descriptors into vector
indices. Note that the second is fixed-size, so initialize it after creating
all vertices.
Other Points Of Interest
// weight_map_t weight_map; // nope: why isn't it default constructible?
What would it do? Surely it won't default to what you think is a good default
(1.0). So I'd just write
auto weight_map = boost::static_property_map(1.0);
Simplified
I'd write the entire function as:
template <class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) {
using vertex_t = typename Graph::vertex_descriptor;
Graph g;
generate_random_graph(g, n_vertices, n_edges, rng);
std::map<vertex_t, vertex_t> predecessors;
random_spanning_tree(g, rng, random_vertex(g, rng),
boost::make_assoc_property_map(predecessors),
boost::static_property_map(1.0), // unweighted
get(boost::vertex_color, g));
return g;
}
Functional Problems
You're asking some good questions yourself. But let me start with some observations.
You have Unspecified/Undefined
Behaviour because your
input graph doesn't conform to the requirements:
There must be a path from every non-root vertex of the graph to the
root; the algorithm typically enters an infinite loop when given a
graph that does not satisfy this property, but may also throw the
exception loop_erased_random_walk_stuck if the search reaches a vertex
with no outgoing edges
Indeed, running your code only completes for a few random seeds, and fails
or runs infinitely for others (this is even increasing the chance of
satisfying the requirements by using undirectedS):
Listing
while true; do (set -x; time ./build/sotest& sleep 3; kill %1); done
You are creating the random spanning tree only to completely forget about
it. Did you intend to return the predecessor map as well (or a derived path
representation)?
Your own questions:
"cannot form a reference to 'void'"
Usually indicates an associated property map could not be found (e.g.
what happens if you fail to supply the vertex_color interior
property. In
this case the remedy is simply to use the default color
map:
random_spanning_tree(
g, rng,
boost::root_vertex(random_vertex(g, rng))
.predecessor_map(boost::make_assoc_property_map(predecessors))
.weight_map(boost::static_property_map(1.0)) // unweighted
);
"I don't know yet if I can directly generate random trees with specific
number of nodes or leaves in BGL."
You can generate random graphs with specific number of nodes and leaves -
as you already demonstrate.
trees.
You can also find random spanning trees (given a graph satisfying the preconditions).
To adhere to the preconditions the simplest way would be to generate
undirected graphs, whilst additionally making sure that the result is
connected. A simple, possible inefficient(?) way to ensure it would be to
explicitly connect components:
if (int n = boost::connected_components(ug, cmap); n > 1) {
std::cout << "Connecting " << n << " components:\n";
for (int c = 1; c < n; ++c)
std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n";
}
It might be more effective to write your own generating algorithm.
BONUS EXAMPLE
Showing the use of connected_components to make sure the graph is fully
connected, and even building a directed tree from undirected source graph. Also
writing graphviz representations of the "raw" (undirected) source and "tree"
(directed spanning tree), it seems to work pretty well.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/connected_components.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/graphviz.hpp>
#include <boost/graph/random.hpp>
#include <boost/graph/random_spanning_tree.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iomanip>
#include <random>
namespace detail {
template <typename T> struct make_undirected { using type = void; };
template <typename A, typename B, typename C, typename D, typename E, typename F>
struct make_undirected<boost::adjacency_list<A, B, C, D, E, F>> {
using type = boost::adjacency_list<A, B, boost::undirectedS, D, E, F>;
};
} // namespace detail
template <typename T> using Undirect = typename detail::make_undirected<T>::type;
template <class Graph, class Generator>
auto generate_random_spanning_tree(int n_vertices, int n_edges, Generator& rng) {
using UG = Undirect<Graph>;
using vertex_t = typename UG::vertex_descriptor;
// assuming integral vertex index for simplicity
static_assert(std::is_same_v<vertex_t, size_t>);
static_assert(std::is_same_v<typename UG::vertex_descriptor,
typename Graph::vertex_descriptor>);
UG ug;
generate_random_graph(ug, n_vertices, n_edges, rng);
vertex_t const root = random_vertex(ug, rng);
print_graph(ug, std::cout << "Raw root: " << root << ", graph:\n");
{ // make connected
std::map<vertex_t, int> components;
auto from = [&](int component) { // just picking the first...
for (auto& [v, c] : components) if (c == component) return v;
throw std::range_error("component");
};
auto cmap = boost::make_assoc_property_map(components);
if (int n = connected_components(ug, cmap); n > 1) {
std::cout << "Connecting " << n << " components:\n";
for (int c = 1; c < n; ++c)
std::cout << "Added " << add_edge(from(c - 1), from(c), ug).first << "\n";
}
}
std::map<vertex_t, vertex_t> predecessors;
random_spanning_tree(
ug, rng,
boost::root_vertex(root) //
.predecessor_map(boost::make_assoc_property_map(predecessors)));
Graph tree(num_vertices(ug)); // build a tree copy
for (auto v : boost::make_iterator_range(vertices(ug)))
if (predecessors.contains(v))
if (auto pred = predecessors.at(v); ug.null_vertex() != pred)
add_edge(predecessors.at(v), v, tree);
auto save = [&predecessors](auto& g, auto name) {
using edge_t = typename std::decay_t<decltype(g)>::edge_descriptor;
auto tree_edge = [&predecessors](auto s, auto t) {
auto it = predecessors.find(s);
return it != end(predecessors) && it->second == t;
};
boost::dynamic_properties dp;
dp.property("node_id", get(boost::vertex_index, g));
dp.property("color",
boost::make_function_property_map<edge_t>([tree_edge, &g](edge_t e) {
auto s = source(e, g), t = target(e, g);
return tree_edge(s, t) || tree_edge(t, s) ? "red" : "gray";
}));
std::ofstream os(name);
write_graphviz_dp(os, g, dp);
};
save(ug, "raw.dot");
save(tree, "tree.dot");
return std::pair(std::move(tree), root);
}
int main(int argc, char** argv) {
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS>;
auto const seed = argc > 1 ? std::stoull(argv[1]) : std::random_device{}();
std::cout << "seed: " << seed << std::endl;
std::mt19937 prng(seed);
auto [tree, root] = generate_random_spanning_tree<G>(10, 20, prng);
print_graph(tree, std::cout << "From root: " << root << ", tree:\n");
}
Prints the sample seed: 1577455792
Raw root: 7, graph:
0 <--> 7 2 3 7 5 2 8
1 <-->
2 <--> 8 0 4 0 4 9
3 <--> 9 5 0 8
4 <--> 7 7 2 2 5
5 <--> 8 3 0 4
6 <-->
7 <--> 4 4 0 8 0
8 <--> 2 5 7 9 0 3
9 <--> 3 8 2
Connecting 3 components:
Added (0,1)
Added (1,6)
From root: 7, tree:
0 --> 1 3
1 --> 6
2 --> 9
3 --> 5
4 --> 2
5 --> 4 8
6 -->
7 --> 0
8 -->
9 -->
Running locally with:
watch './build/sotest; for a in raw tree; do (set -x ; dot -Tpng -o $a.png $a.dot); done'
Shows random solutions like:
I'm trying to create a graph where lanes represent vertices and edges connections between. The idea is to later on use a_star algorithm to traverse the graph and find the best route.
My question is how can I get vertex (descriptor?) by using just (town_id, road_id, lane_id)?
I tryied battling with the examples that are shipped with boost and basically lost. In them they always traverse thru all the vertices and then get the vertex_descriptor, but I want to do that in constant time.
What is need is something like boost::get_vertex(std::tuple(1, 2, 3), graph).
My situation is marked as PROBLEM bellow in the code. The code bellow compiles because there is 12 that I guess corresponds to the index in boost::vecS, but I want to use a tuple (or something else that can hold the triplet) that could get me the vertex descriptor as my starting location.
using TRL = std::tuple<int, int, int>;
struct TRL_VProp {
/// For some reason this object has to be default constructable
/// use invalid data to flag it as invalid
TRL_VProp()
: trl(-1, -1, 0) {}
TRL_VProp(TRL trl)
: trl(trl) {}
TRL_VProp(int town_id, int road_id, int lane_id)
: trl(town_id, road_id, lane_id) {}
TRL trl;
};
using DirectedGraph = boost::adjacency_list<boost::listS,
boost::vecS,
boost::directedS,
RSL_VProp>;
using Vertex = boost::graph_traits<DirectedGraph>::vertex_descriptor;
using VertexI = boost::graph_traits<DirectedGraph>::vertex_iterator;
using EdgeI = boost::graph_traits<DirectedGraph>::edge_iterator;
using Edge = boost::graph_traits<DirectedGraph>::edge_descriptor;
using AdjI = boost::graph_traits<DirectedGraph>::adjacency_iterator;
using Route = std:vector<TRL>;
Route BuildRoute(Map & map) {
Route result;
DirectedGraph graph;
const boost::property_map<DirectedGraph, RSL RSL_VProp:: *>::type trl =
boost::get(&TRL_VProp::rsl, graph);
bool is_inserted;
Edge e_desc;
for (auto & town: map.GetTowns()) {
for (auto & road: town.GetRoads()) {
for (auto & lane: road.GetLanes()) {
auto vtx_1 = boost::add_vertex(
RSL_VProp(town.GetId(), road.GetId(), lane.GetId()),
graph);
const auto next_lanes = map.GetNextLanes(town.GetId(), road.GetId(), lane.GetId());
for(const auto & next_lane : next_lanes) {
auto vtx_2 = boost::add_vertex(
TRL_VProp(lane.GetTown().GetId(), lane.GetRoad().GetId(), lane.GetId()),
graph);
std::tie(e_desc, is_inserted) = boost::add_edge(vtx_1, vtx_2, graph);
assert(is_inserted);
}
}
}
}
// debug part
TRL temp_trl;
std::pair<AdjI, AdjI> adj_i = boost::adjacent_vertices(12, graph); // <--- PROBLEM
for( auto i = adj_i.first; i != adj_i.second; i++) {
temp_trl = trl[*i]; // trl prop map
std:: cout << "\Town id: " << std::get<0>(temp_trl)
<< "\nRoad id: " << std::get<1>(temp_trl)
<< "\nLane id: " << std::get<2>(temp_trl);
result.push_back(
std::make_tuple(
std::get<0>(temp_trl),
std::get<1>(temp_trl),
std::get<2>(temp_trl)));
}
return result;
}
For completeness, I don't plan to change data inside the property or the graph itself. Once thing is created it will probably stay that way. Maybe lateron I'll probably have to add the weight or whatever is necessary to get the traversing algorithm to work.
Edit: I forgot to mention that there is a bug in the code because I'm adding vertices before checking was vertex with identical (town, road, lane) already been created. I wonder is there a builtin boost::graph way to not insert duplicated so I won't have to use a std::unordered_map or something.
Having defined my objects myType, I need to store relations between these objects. These relations are stored on a matrix.
The number of elements is not known in advance, not all elements have a relation (element1 can have a relation with element3, but may not have one with 5) and memory is an issue. For example it could look like:
element45 is connected with:
element3 with characteristic [3,1;1,4]
element12 with characteristic [1,1;1,1]
element1780 with characteristic [8,1;1,4]
element1661 is connected with:
element3 with characteristic [3,1;6,4]
element1 with characteristic [1,1;1,9]
element1780 with characteristic [8,1;1,1]
Having:
myType* element1;
myType* element2;
I would like to have something like (properly pointed the elements):
my_table[element1][element2][1][2]=7;
I have thought on creating a nested hash table using boost library:
boost::unordered_map<myType*, boost::unordered_map<myType*,
std::vector<std::vector <unsigned short int> > > > my_table;
However, even if the code compiles, it crashes (Segmentation fault, it points to a line calculating the hash key) running a simple line like:
my_table[element1][element2].resize(2);
for(int t=0; t<2; ++t)
my_table[element1][element2][t].resize(2);
Anyone can give me some light about this? Is this practically or conceptually wrong?
Any other approach to this problem is welcome.
Thank you
Right off the bat it seems obvious to me that your datastructure represent a graph (with attributed vertices and edges connecting them).
Furthermore when you say "These relations are stored on a matrix." you apparently mean "I visualize this as a matrix", since a true matrix representation¹ would become horrifically space-inefficient for larger number of vertices and sparse edge coverage.
Boost has a library for that: Boost Graph Library (BGL)
If we assume you want to be able to read a graph like²
graph X {
element1; element12; element166; element1780; element3; element4;
element45 -- element3 [ label="[3,1;1,4]" ];
element45 -- element12 [ label="[1,1;1,1]" ];
element45 -- element1780 [ label="[8,1;1,4]" ];
element1661 -- element1 [ label="[1,1;1,9]" ];
element1661 -- element3 [ label="[3,1;6,4]" ];
element1661 -- element1780 [ label="[8,1;1,1]" ];
}
Into a BGL compatible model, use typedefs like e.g.:
struct Vertex {
std::string node_id;
};
struct Edge {
Box box;
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
Now you leverage the full facilities of the BGL:
Reading the graph from a file
Reading from a graphviz file is a feature³:
std::ifstream ifs("input.txt");
Graph result;
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, result));
dp.property("label", boost::get(&Edge::box, result));
read_graphviz(ifs, result, dp);
Manipulating the graph
The many algorithms (dijkstra, flow, spanning trees, connected components, etc.) are at your disposal. Or you can mix and match. For example let's filter the nodes that have no connections out:
struct Filter {
Graph const* _g;
bool operator()(Graph::vertex_descriptor v) const {
return boost::size(boost::adjacent_vertices(v, *_g))>0;
}
template <typename T>
bool operator()(T&&) const { return true; /*catch-all*/ }
};
using Filtered = filtered_graph<Graph, Filter, Filter>;
Filter filter { &graph };
Filtered filtered(graph, filter, filter);
Let's write it to graphviz again:
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, filtered));
dp.property("label", boost::get(&Edge::box, filtered));
write_graphviz_dp(std::cout, filtered, dp);
DEMO TIME
The full demo takes your input graph:
And filters it into:
Full Code
Live On Coliru
// http://stackoverflow.com/questions/32279268/using-two-objects-as-hash-key-for-an-unordered-map-or-alternatives
#include <cassert>
#include <iostream>
template <typename T> struct BasicBox {
struct Point { T x, y; };
Point tl, br;
friend std::ostream& operator<<(std::ostream& os, Point const& p) { return os << p.x << ',' << p.y; }
friend std::ostream& operator<<(std::ostream& os, BasicBox const& b) { return os << '[' << b.tl << ';' << b.br << ']'; }
friend std::istream& operator>>(std::istream& is, Point& p) {
char comma;
if (!(is >> p.x >> comma >> p.y) && (comma == ',')) {
is.setstate(std::ios::failbit | is.rdstate());
}
return is;
}
friend std::istream& operator>>(std::istream& is, BasicBox& b) {
char lbrace, semi, rbrace;
if (!(
(is >> lbrace >> b.tl >> semi >> b.br >> rbrace) &&
(lbrace == '[' && semi == ';' && rbrace == ']')
)) {
is.setstate(std::ios::failbit | is.rdstate());
}
return is;
}
};
using Box = BasicBox<int>;
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
#include <libs/graph/src/read_graphviz_new.cpp>
struct Vertex {
std::string node_id;
};
struct Edge {
Box box;
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Vertex, Edge>;
#include <fstream>
#include <boost/graph/filtered_graph.hpp>
struct Filter {
Graph const* _g;
bool operator()(Graph::vertex_descriptor v) const {
return boost::size(boost::adjacent_vertices(v, *_g))>0;
}
template <typename T>
bool operator()(T&&) const { return true; /*catch-all*/ }
};
int main() {
using namespace boost;
Graph const graph = []{
std::ifstream ifs("input.txt");
Graph result;
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, result));
dp.property("label", boost::get(&Edge::box, result));
read_graphviz(ifs, result, dp);
return result;
}();
// let's do some random task. Like. You know. Like... Filter out the unconnected nodes
using Filtered = filtered_graph<Graph, Filter, Filter>;
Filter filter { &graph };
Filtered filtered(graph, filter, filter);
boost::dynamic_properties dp;
dp.property("node_id", boost::get(&Vertex::node_id, filtered));
dp.property("label", boost::get(&Edge::box, filtered));
write_graphviz_dp(std::cout, filtered, dp);
}
¹ like e.g. BGL's AdjacencyMatrix
² the format chosen being Graphviz' DOT format: http://www.graphviz.org/
³ Of course you can also use Boost Serialization with BGL models, so you can opt for a more compact binary representation e.g.
You could use a boost::unordered_map with key std::pair<myType*, myType*> in combine with boost::hash. You could declare it as:
boost::unordered_map<std::pair<myType*, myType*>,
std::vector<std::vector<char>>,
boost::hash<std::pair<myType*, myType*>>> dictionary;
Then you could load the characteristics of each pair in the dictionary like in the example below:
dictionary[std::make_pair(&a, &b)] = std::vector<std::vector<char>>(1, {1, 2, 3, 4, 5});
And access them as:
dictionary[std::make_pair(&a, &b)][0][0];
LIVE DEMO
I have my custom vertex and edge properties
namespace boost {
enum vertex_diagonal_t{vertex_diagonal = 999};
BOOST_INSTALL_PROPERTY(vertex, diagonal);
}
namespace boost {
enum edge_dominance_t{edge_dominance = 998};
BOOST_INSTALL_PROPERTY(edge, dominance);
}
I create my adjacency list with boost::property
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::bidirectionalS,
boost::property<boost::vertex_diagonal_t, const khut::diagonal*>,
boost::property<boost::edge_dominance_t, float>
> diagonal_dominance_graph;
typedef boost::property_map<diagonal_dominance_graph, boost::vertex_diagonal_t>::type diagonal_map_type;
typedef boost::property_map<diagonal_dominance_graph, boost::edge_dominance_t>::type dominance_map_type;
Now I want to loop through my own containers and add vertex
diagonal_dominance_graph graph;
for(storage_type::const_iterator i = repo_begining.begin(); i != repo_begining.end(); ++i){
diagonal_dominance_graph::vertex_descriptor dia_vertex = boost::add_vertex(graph);
//>> ?? HOW CAN I write Properties to dia_vertex HERE ?
//boost::property<boost::vertex_diagonal_t, const khut::diagonal*> p;
//boost::put(p, dia_vertex);
}
What I am not getting is How can I set properties of a vertex through vertex_descriptor. may be I am missing a simple function.
Please I don't need anything that makes BGL even more complex, or something that cleans and restructures the types in my example. I just need to know how to read/write properties through a vertex_descriptor or edge_descriptor
You're using property lists: they're documented here.
So in your example, you'd use
diagonal_map_type vp = get(boost::vertex_diagonal, graph);
using storage_type = std::vector<int>;
storage_type repo_begining(10);
for(storage_type::const_iterator i = repo_begining.begin(); i != repo_begining.end(); ++i) {
diagonal_dominance_graph::vertex_descriptor dia_vertex = boost::add_vertex(graph);
khut::diagonal* v = nullptr;
boost::put(vp, dia_vertex, v);
}
// likewise for edges
dominance_map_type ep = get(boost::edge_dominance, graph);
See it Live On Coliru
Bundled Properties
The very same documentation page says:
NOTE: The Boost Graph Library supports two interchangeable methods for specifying interior properties: bundled properties and property lists. The former is easier to use and requires less effort, whereas the latter is compatible with older, broken compilers and is backward-compatible with Boost versions prior to 1.32.0. If you absolutely require these compatibility features, read on to learn about property lists. Otherwise, we strongly suggest that you read about the bundled properties mechanism.
Boost 1.32 dates over 10 years ago! So, I'd suggest bundled properties:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
namespace khut {
struct diagonal { };
struct MyVertexProperties {
diagonal const* diag_ptr;
};
struct MyEdgeProperties {
float dominance;
};
}
typedef boost::adjacency_list<
boost::listS,
boost::vecS,
boost::bidirectionalS,
khut::MyVertexProperties,
khut::MyEdgeProperties
> diagonal_dominance_graph;
#include <iostream>
int main() {
using namespace boost;
diagonal_dominance_graph g;
khut::diagonal d1, d2;
{
auto v1 = add_vertex(khut::MyVertexProperties { &d1 }, g);
auto v2 = add_vertex(khut::MyVertexProperties { &d2 }, g);
/*auto e1 = */add_edge(v1, v2, khut::MyEdgeProperties { 42.31415926 }, g);
}
for(diagonal_dominance_graph::vertex_descriptor vd : make_iterator_range(vertices(g)))
std::cout << "Is diagonal d1? " << std::boolalpha << (&d1 == g[vd].diag_ptr) << "\n";
for(diagonal_dominance_graph::edge_descriptor ed : make_iterator_range(edges(g)))
std::cout << "Edge dominance: " << g[ed].dominance << "\n";
}
Prints
Is diagonal d1? true
Is diagonal d1? false
Edge dominance: 42.3142
I am trying to create a graph of objects which I need to traverse using some traversal algorithm. At this very moment I am stuck trying to create the graph using my custom objects. The way I am trying to accomplish it is as follows:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
#include <iostream>
using namespace std;
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::directedS> CustomGraph;
typedef boost::graph_traits<CustomGraph>::vertex_descriptor CustomVertex;
class CustomVisitor:public boost::default_dfs_visitor
{
public:
void discover_vertex(CustomVertex v,const CustomGraph& taskGraph) const
{
cerr<<v<<endl;
return;
}
};
class CustomObject{
private:
int currentId;
public:
CustomObject(int id){
currentId = id;
}
};
int main()
{
CustomGraph customGraph;
CustomObject* obj0 = new CustomObject(0);
CustomObject* obj1 = new CustomObject(1);
CustomObject* obj2 = new CustomObject(2);
CustomObject* obj3 = new CustomObject(3);
typedef std::pair<CustomObject*,CustomObject*> Edge;
std::vector<Edge> edgeVec;
edgeVec.push_back(Edge(obj0,obj1));
edgeVec.push_back(Edge(obj0,obj2));
edgeVec.push_back(Edge(obj1,obj2));
edgeVec.push_back(Edge(obj1,obj3));
customGraph(edgeVec.begin(),edgeVec.end());
CustomVisitor vis;
boost::depth_first_search(customGraph,boost::visitor(vis));
return 0;
}
But this doesn't seem to be the right way to create objects within the vertices. Can somebody guide me as to what is the correct way to create nodes such that I can retrieve my objects while traversing the graph.
Thanks
Hi I know this is a rather old question, but there might others that can benefit from an answer.
It seems as if you have forgotten to define that your Graph will have a custom class as vertices. You have to add a fourth parameter to your typedef and add a typedef for your edge:
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::directedS, CustomObject> CustomGraph;
typedef boost::graph_traits<CustomGraph>::vertex_descriptor CustomVertex;
typedef boost::graph_traits<CustomGraph>::edge_descriptor CustomEdge;
Then I usually add my nodes before connecting them with edges:
// Create graph and custom obj's
CustomGraph customGraph
CustomObject obj0(0);
CustomObject obj1(1);
CustomObject obj2(2);
CustomObject obj3(3);
// Add custom obj's to the graph
// (Creating boost vertices)
CustomVertex v0 = boost::add_vertex(obj0, customGraph);
CustomVertex v1 = boost::add_vertex(obj1, customGraph);
CustomVertex v2 = boost::add_vertex(obj2, customGraph);
CustomVertex v3 = boost::add_vertex(obj3, customGraph);
// Add edge
CustomEdge edge;
bool edgeExists;
// check if edge allready exist (only if you don't want parallel edges)
boost::tie(edge, edgeExists) = boost::edge(v0 , v1, customGraph);
if(!edgeExists)
boost::add_edge(v0 , v1, customGraph);
// write graph to console
cout << "\n-- graphviz output START --" << endl;
boost::write_graphviz(cout, customGraph);
cout << "\n-- graphviz output END --" << endl;