How does boost::copy_graph's vertex_copy work? - c++

I am using boost::copy_graph to copy an adjacency_list into another adjacency_list with a different VertexProperties template. To do this, I am trying to use the vertex_copy parameter (doc here). I am running into a compiler error that tells the I have a wrong type for the vertex properties of the second (to-be-copied) graph.
Minimal Example
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
typedef boost::adjacency_list<boost::vecS,
boost::vecS,
boost::undirectedS,
uint32_t,
float> AdjacencyList;
typedef AdjacencyList::vertex_descriptor VertexID;
struct custom_property
{
uint32_t label;
float f;
};
typedef boost::adjacency_list<boost::vecS,
boost::vecS,
boost::undirectedS,
custom_property,
float> AdjacencyListCustom;
struct vertex_copier
{
void operator() (uint32_t &input, custom_property &output)
{
output.label = input;
output.f = 0.;
}
};
int main(int argc, char** argv)
{
AdjacencyList adj;
VertexID id_0 = boost::add_vertex(0, adj);
VertexID id_1 = boost::add_vertex(1, adj);
VertexID id_2 = boost::add_vertex(2, adj);
VertexID id_3 = boost::add_vertex(4, adj);
boost::add_edge(id_0, id_1, 1.0f, adj);
boost::add_edge(id_2, id_3, 2.0f, adj);
AdjacencyListCustom adj_custom;
boost::copy_graph(adj, adj_custom, boost::vertex_copy(vertex_copier()));
}
g++ compilation error
...
/usr/include/boost/graph/copy.hpp:164:22: error: no match for call to '(vertex_copier) (boost::iterators::detail::iterator_facade_base<boost::range_detail::integer_iterator<long unsigned int>, long unsigned int, boost::iterators::random_access_traversal_tag, long unsigned int, long int, false, false>::reference, boost::graph_traits<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, custom_property, float> >::vertex_descriptor&)'
copy_vertex(*vi, new_v);
~~~~~~~~~~~^~~~~~~~~~~~
/path/to/file.cpp: note: candidate: void vertex_copier::operator()(uint32_t&, custom_property&)
void operator() (uint32_t &input, custom_property &output)
^~~~~~~~
/path/to/file.cpp: note: no known conversion for argument 2 from 'boost::graph_traits<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, custom_property, float> >::vertex_descriptor {aka long unsigned int}' to 'custom_property&'
This tells me that that vertex_copy is actually trying to copy the vertex_descriptor, which is a long unsigned int, not a custom_property. This seems to go against what is stated in the docs:
This is a Binary Function that copies the properties of a vertex in the original graph into the corresponding vertex in the copy.
How does vertex_copy work? Can it be used to set/define vertex properties in the copied graph that are not in the original? If not, do these properties have to be set after the copying by iterating through the graph? Can I apply a mapping en masse or does each vertex have to be visited and updated?
EDIT:
If I attempt to use copy_graph without specifying vertex_copy, then there is an error because the = operator does not exist between custom_property and uint32_t.

Sidestepping the question for a second, the simplest way to achieve things would be to add the appropriate conversion constructor to the custom property:
struct custom_property {
custom_property(uint32_t label = 0, float f = 0) : label(label), f(f) {}
uint32_t label;
float f;
};
In which case, a simple copy will work:
boost::copy_graph(adj, adj_custom);
See it Live On Coliru
Regarding the vertex copier, it receives the vertex decriptors. To access the vertex properties, you need to have the graph reference:
struct vertex_copier {
AdjacencyList& from;
AdjacencyListCustom& to;
void operator()(AdjacencyList::vertex_descriptor input, AdjacencyListCustom::vertex_descriptor output) const {
to[output] = { from[input], 0.f };
}
};
In which case you'd invoke things:
boost::copy_graph(adj, adj_custom, boost::vertex_copy(vertex_copier{adj, adj_custom}));
Again, Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/copy.hpp>
#include <iostream>
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, uint32_t, float> AdjacencyList;
typedef AdjacencyList::vertex_descriptor VertexID;
struct custom_property {
//custom_property(uint32_t label = 0, float f = 0) : label(label), f(f) {}
uint32_t label;
float f;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, custom_property, float> AdjacencyListCustom;
struct vertex_copier {
AdjacencyList& from;
AdjacencyListCustom& to;
void operator()(AdjacencyList::vertex_descriptor input, AdjacencyListCustom::vertex_descriptor output) const {
to[output] = { from[input], 0.f };
}
};
int main(int argc, char **argv) {
AdjacencyList adj;
VertexID id_0 = boost::add_vertex(0, adj);
VertexID id_1 = boost::add_vertex(1, adj);
VertexID id_2 = boost::add_vertex(2, adj);
VertexID id_3 = boost::add_vertex(4, adj);
boost::add_edge(id_0, id_1, 1.0f, adj);
boost::add_edge(id_2, id_3, 2.0f, adj);
AdjacencyListCustom adj_custom;
boost::copy_graph(adj, adj_custom, boost::vertex_copy(vertex_copier{adj, adj_custom}));
}

Related

boost::astar_search on filtered graph

I need to run A* on a graph with some of the edges removed. To do so I construct a filtered graph with blacklisted edges and I want to run A* on the filtered graph. The blacklisted edges are embedded in the class BlackListEdgeConstraint, that I initialize by passing to its constructor the list of the forbidden edges. This BlackListEdgeConstraint is correctly constructed, and I construct the filtered graph with these constrain. The problem is that when I run A* on the filtered graph, another BlackListEdgeConstraint object is mysteriously constructed with an empty constructor, and no edge is in effect blacklisted. Why is it happening?
This is my complete code:
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/astar_search.hpp>
using namespace std;
typedef std::pair<int, int> Edge;
typedef boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, boost::property<boost::edge_weight_t, int> > graph_t;
typedef boost::graph_traits<graph_t>::vertex_descriptor vertex_descriptor;
struct BlackListEdgeConstraint
{
private:
std::set<Edge> blackList;
graph_t* g;
public:
BlackListEdgeConstraint():blackList(std::set<Edge>() ),g(NULL){throw std::runtime_error("This should not be called");};
BlackListEdgeConstraint(std::set<Edge>& list, graph_t* g_) : blackList(list), g(g_)
{
Edge edge = *blackList.begin();
std::cout<<"The black list contains "<< edge.first<<"-"<<edge.second<<std::endl;
}
/**
* This is the "predicate function" used by boost::filtered_graph (
* see http://www.boost.org/doc/libs/1_64_0/libs/graph/doc/filtered_graph.html )
* It it returns true, the edge is included in the filtered graph, otherwise it is excluded.
*/
bool operator()(const boost::graph_traits<graph_t>::edge_descriptor& e) const
{
Edge edge(source(e,*g), target(e,*g) );
std::cout<<"Should we include edge "<<source(e,*g)<<" ->"<< target(e,*g)<<" represented by a descriptor "<<e<<"? ";
//Include the edge if it's not in the blacklist.
bool include = (blackList.find( edge ) == blackList.end() );
std::cout<<include<<std::endl;
return include;
}
};
template<class GraphType>
class MyHeuristic : public boost::astar_heuristic<GraphType, double>
{
private:
const GraphType* g;
public:
MyHeuristic(const GraphType* g_): g(g_) {};
double operator()(vertex_descriptor v)
{
return 0;
}
};
//Used to terminate our search
struct GoalsReached{};
class MyVisitor : public boost::default_astar_visitor
{
private:
vertex_descriptor goal;
public:
MyVisitor(vertex_descriptor goal_): goal(goal_){};
template<class GraphType>
void examine_vertex(vertex_descriptor u, const GraphType& g)
{ if (u==goal) throw GoalsReached(); }
};
int main()
{
Edge edge_array[] = {Edge(0,1), Edge(1,2), Edge(2,3), Edge(3,0), Edge(1,3) };
int weights[] = {1,1,1,1,1};
int num_arcs = sizeof(edge_array) / sizeof(Edge);
int num_nodes = 4;
// Graph created from the list of edges
graph_t g(edge_array, edge_array + num_arcs, weights, num_nodes);
// Create descriptor for the source node
vertex_descriptor s = vertex(0, g);
vertex_descriptor goal = vertex(3,g) ;
std::set<Edge> blacklist; blacklist.insert( Edge(1,3) );
BlackListEdgeConstraint filter(blacklist, &g);
boost::filtered_graph<graph_t, BlackListEdgeConstraint> filtered(g, filter);
cout<<"filtered constructed"<<endl;
// Create vectors to store the predecessors (p) and the distances from the root (d)
std::vector<vertex_descriptor> p(num_vertices(filtered));
std::vector<int> d(num_vertices(filtered));
try{
cout<<"Launching astar_search"<<endl;
boost::astar_search(filtered, s,
MyHeuristic<boost::filtered_graph<graph_t, BlackListEdgeConstraint>>(&filtered),
boost::predecessor_map(&p[0]).
distance_map(&d[0]).
visitor(MyVisitor(goal) )
);
cout<<"astar_search launched"<<endl;
}catch(const GoalsReached& )
{ // Print the path
std::vector<boost::graph_traits<graph_t>::vertex_descriptor > path;
boost::graph_traits<graph_t>::vertex_descriptor current = goal;
while(current!=s)
{
path.push_back(current);
current = p[current];
}
path.push_back(s);
// Prints the path obtained in reverse
std::vector<boost::graph_traits<graph_t>::vertex_descriptor >::reverse_iterator it;
for (it = path.rbegin(); it != path.rend(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
}
return EXIT_SUCCESS;
}
And this is the output:
The black list contains 1-3
filtered constructed
Launching astar_search
terminate called after throwing an instance of 'std::runtime_error'
what(): This should not be called
My boost version is 1.54
The problem is that, when boost::astar_search(..) is invoked, the empty constructor BlackListEdgeConstraint() is called.
I don't know how you reach the conclusion. I cannot reproduce that:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/astar_search.hpp>
struct VertexProperties {
};
struct EdgeProperties {
double weight = 1;
};
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexProperties, EdgeProperties> MyGraph;
struct StreetDirectory {
using Graph = MyGraph;
using Edge = Graph::edge_descriptor;
using Vertex = Graph::vertex_descriptor;
};
struct BlackListEdgeConstraint : StreetDirectory
{
private:
std::set<StreetDirectory::Edge> blackList;
public:
BlackListEdgeConstraint(const std::set<Edge>& list) : blackList(list) {};
BlackListEdgeConstraint()
{
throw std::runtime_error("This should not be called");
};
bool operator()(const Edge& e) const
{
//Include the edge if it's not in the blacklist.
return blackList.find(e) == blackList.end();
}
};
int main() {
MyGraph graph;
const std::set<StreetDirectory::Edge> blacklistEdges {
add_edge(1,2,graph).first,
add_edge(1,3,graph).first,
add_edge(2,4,graph).first,
};
add_edge(4,2,graph);
BlackListEdgeConstraint filter(blacklistEdges);
boost::filtered_graph<MyGraph, BlackListEdgeConstraint> filtered(graph, filter);
std::vector<StreetDirectory::Vertex> p(boost::num_vertices(filtered)); //Output variable
std::vector<double> d(boost::num_vertices(filtered)); //Output variable
boost::default_astar_visitor vis;
boost::astar_search(
filtered,
1,
[](auto /*vd*/) { return 1; },
boost::predecessor_map(&p[0]).
weight_map(boost::get(&EdgeProperties::weight, filtered)).
distance_map(&d[0]).
visitor(vis)
);
}
Notes
in general functors are passed by value in (standard) library algorithms. So you'd use std::reference_wrapper<BlackListEdgeConstraint> if you wanted to use the same instance. But like I said, I don't see it happening, so it's not a problem AFAICT
in your sample there seemed to be no edge-weight map indicated. I don't see how that should have compiled

BGL Fruchterman Reingold Force Directed Layout

I am trying to run the fruchterman_reingold_force_directed_layout algorithm on a boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, Vertex, Edge> graph.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
typedef boost::rectangle_topology<> RectTopology;
typedef RectTopology::point_type point;
struct Vertex {
Vertex(int i) : index(i) {};
int index;
point position;
};
struct Edge {
double weight;
};
typedef boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS, Vertex, Edge> Graph;
int main() {
Graph graph;
auto vertexIdPropertyMap = boost::get(&Vertex::index, graph);
Vertex n1(0);
boost::add_vertex(std::move(n1), graph);
Vertex n2(1);
boost::add_vertex(std::move(n2), graph);
Vertex n3(2);
boost::add_vertex(std::move(n3), graph);
boost::add_edge(boost::vertex(0, graph), boost::vertex(1, graph), Edge(), graph);
boost::add_edge(boost::vertex(0, graph), boost::vertex(2, graph), Edge(), graph);
boost::minstd_rand gen;
RectTopology rectangleTopology(gen, -25, -25, 25, 25);
boost::random_graph_layout(graph, boost::get(&Vertex::position, graph), rectangleTopology);
boost::fruchterman_reingold_force_directed_layout(graph, boost::get(&Vertex::position, graph), rectangleTopology);
return 0;
}
When compiling, the first error is the following:
error: forming reference to void
typedef value_type& reference;
I have done some research and apparently this is due to the fact that it is a listS vertex list - with vecS it works.
However, I still want to use a listS and apparently it is possible:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
enum vertex_position_t { vertex_position };
namespace boost { BOOST_INSTALL_PROPERTY(vertex, position); }
typedef boost::rectangle_topology<> RectTopology;
typedef RectTopology::point_type point;
struct simple_edge {
int first, second;
};
int main() {
enum { A, B, C };
simple_edge triangular_edges[2] = {
{A, B}, {A, C}
};
typedef boost::adjacency_list<boost::listS, boost::listS, boost::bidirectionalS,
// Vertex properties
boost::property<boost::vertex_index_t, int,
boost::property<vertex_position_t, point> >,
// Edge properties
boost::property<boost::edge_weight_t, double>> Graph;
Graph g(&triangular_edges[0], &triangular_edges[1], 3);
boost::minstd_rand gen;
RectTopology rect_top(gen, -25, -25, 25, 25);
boost::random_graph_layout(g, boost::get(vertex_position, g), rect_top);
boost::fruchterman_reingold_force_directed_layout(g, boost::get(vertex_position, g), rect_top);
return 0;
}
The problem here is that I don't want to use the "old-style" properties but the bundled properties instead. Here someone was attempting something similar, but the fruchterman_reingold_force_directed_layout does not offer this index map if I am not mistaking.

boost DFS does not work with setS vertice lists

The following code is not compiled.
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/function_output_iterator.hpp>
typedef boost::adjacency_list<
boost::setS, // outedge list
boost::setS, // vertex list
boost::directedS, // undirected
boost::no_property, // vertex prop
boost::no_property, // edge prop
boost::no_property, // graph prop
boost::setS // edgelist
> Graph;
bool hasCycle(const Graph& aG)
{
try {
boost::topological_sort(
aG,
boost::make_function_output_iterator([](int){}));
} catch(boost::not_a_dag const&)
{
return true;
}
return false;
}
It works after changing vertice lists to be vecS
http://coliru.stacked-crooked.com/a/abeb9e3f96e92af0
Is this limitation because DFS needs a deterministic visits?
Thank you,
The "limitation" is documented as the need for a vertex index. You could add one (note that you also should replace int by Graph::vertex_descriptor), or adjust the graph type to include one:
Adding an external index property map
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/function_output_iterator.hpp>
typedef boost::adjacency_list<
boost::setS, // outedge list
boost::setS, // vertex list
boost::directedS, // undirected
boost::no_property, // vertex prop
boost::no_property, // edge prop
boost::no_property, // graph prop
boost::setS // edgelist
> Graph;
bool hasCycle(const Graph& aG)
{
try {
std::map<Graph::vertex_descriptor, size_t> index;
auto pmap = boost::make_assoc_property_map(index);
for (auto vd : boost::make_iterator_range(boost::vertices(aG)))
index[vd] = index.size();
boost::topological_sort(
aG,
boost::make_function_output_iterator([](Graph::vertex_descriptor){}),
boost::vertex_index_map(pmap));
}
catch (boost::not_a_dag const&)
{
return true;
}
return false;
}
int main() {
}
Adding an internal index property map
This amounts to shifting the burden to the caller. This might make (a lot of) sense depending on the performance requirements for you application.
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/function_output_iterator.hpp>
typedef boost::adjacency_list<
boost::setS, // outedge list
boost::setS, // vertex list
boost::directedS, // undirected
boost::property<boost::vertex_index_t, int>, // vertex prop
boost::no_property, // edge prop
boost::no_property, // graph prop
boost::setS // edgelist
> Graph;
bool hasCycle(const Graph& aG)
{
try {
boost::topological_sort(
aG,
boost::make_function_output_iterator([](Graph::vertex_descriptor){})
);
}
catch (boost::not_a_dag const&)
{
return true;
}
return false;
}
#include <boost/graph/random.hpp>
#include <random>
int main() {
std::mt19937 prng{ std::random_device{}() };
Graph g;
boost::generate_random_graph(g, 100, 200, prng);
auto index = boost::get(boost::vertex_index, g);
int gen_id = 0;
for (auto vd : boost::make_iterator_range(boost::vertices(g))) {
boost::put(index, vd, gen_id++);
std::cout << "Vertex " << boost::get(index, vd) << "\n";
}
bool check = hasCycle(g);
}

boost graph equality and subgraph

I'm writing code for graph mining using boost library and I want to know how to test:
if two graphs are equal using isomorphism (return true only if graphs have the same structure and same labels)
if a graph is a subgraph of another?
This is the graphs file: 3test.txt
and Here is some parts of the source code that I have make:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/config.hpp>
#include <boost/graph/isomorphism.hpp>
#include <boost/graph/graph_utility.hpp>
#include <fstream>
#include <iostream>
#include <vector>
//for mmap:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
using namespace boost;
//==========STRUCTURES==========
// vertex
struct VertexProperties {
int id;
int label;
VertexProperties(unsigned i = 0, unsigned l = 0) : id(i), label(l) {}
};
// edge
struct EdgeProperties {
unsigned label;
EdgeProperties(unsigned l = 0) :label(l) {}
};
// Graph
struct GraphProperties {
unsigned id;
unsigned label;
GraphProperties(unsigned i = 0, unsigned l = 0) : id(i), label(l) {}
};
// adjency list
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties,
GraphProperties> Graph;
// descriptors
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_t;
typedef std::pair<boost::graph_traits<Graph>::edge_descriptor, bool> edge_t;
// iterators
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
typedef graph_traits<Graph>::edge_iterator edge_iter;
typedef std::pair<edge_iter, edge_iter> edge_pair;
//*********global variables*************
vector<Graph> dataG;
//=================callback used fro subgraph_iso=================================================================
// Default print_callback
template <typename Graph1,typename Graph2>
struct my_callback {
my_callback(const Graph1& graph1, const Graph2& graph2)
: graph1_(graph1), graph2_(graph2) {}
template <typename CorrespondenceMap1To2,
typename CorrespondenceMap2To1>
bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1) const {
return true;
}
private:
const Graph1& graph1_;
const Graph2& graph2_;
};
//==========handle_error==========
void handle_error(const char *msg) {
perror(msg);
exit(255);
}
//============READ ALL THE FILE AND RETURN A STRING===================
const char *readfromfile(const char *fname, size_t &length) {
int fd = open(fname, O_RDONLY);
if (fd == -1)
handle_error("open");
// obtain file size
struct stat sb;
if (fstat(fd, &sb) == -1)
handle_error("fstat");
length = sb.st_size;
const char *addr = static_cast<const char *>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u));
if (addr == MAP_FAILED)
handle_error("mmap");
// TODO close fd at some point in time, call munmap(...)
return addr;
}
//==========SPLIT THE STRING BY NEWLINE (\n) ==========
vector<string> splitstringtolines(string const& str) {
std::vector<string> split_vector;
split(split_vector, str, is_any_of("\n"));
return split_vector;
}
//============Get a string starting from pos============
string getpos(int const& pos, string const& yy) {
size_t i = pos;
string str;
for (; ((yy[i] != ' ') && (i < yy.length())); i++) {str += yy[i];}
return str;
}
//==================read string vector and return graphs vector===================
std::vector<Graph> creategraphs(std::vector<string> const& fichlines) {
for (string yy : fichlines) {
switch (yy[0]) {
case 't': {
string str2 = getpos(4, yy);
unsigned gid = atoi(str2.c_str());
dataG.emplace_back(GraphProperties(gid, gid));
} break;
case 'v': {
assert(!dataG.empty()); // assert will terminate the program if its argument turns out to be false
// cout<<yy<<endl;
int vId, vLabel;
string vvv = getpos(2, yy);
vId = atoi(vvv.c_str());
string vvvv = getpos((int)vvv.length() + 3, yy);
// cout<<vvvv<<endl;
vLabel = atoi(vvvv.c_str());
boost::add_vertex(VertexProperties(vId, vLabel), dataG.back());
}
break;
case 'e': { // cout<<yy<<endl;
assert(!dataG.empty()); // assert will terminate the program if its argument turns out to be false
int fromId, toId, eLabel;
string eee = getpos(2, yy);
// cout<<eee<<endl;
fromId = atoi(eee.c_str());
string eee2 = getpos((int)eee.length() + 3, yy);
// cout<<eee2<<endl;
toId = atoi(eee2.c_str());
int c = (int)eee.length() + (int)eee2.length() + 4;
// cout<<c<<endl;
string eee3 = getpos(c, yy);
// cout<<eee3<<endl;
eLabel = atoi(eee3.c_str());
for (size_t i = 0; i < num_vertices(dataG.back()); ++i) // size_t vertice number in the graph
{
if(dataG.back()[i].id==fromId) fromId=i;
else if(dataG.back()[i].id==toId) toId=i;
}
boost::add_edge(fromId, toId, EdgeProperties(eLabel), dataG.back());
} break;
}
}
return dataG;
}
//==============================M A I N P R O G R A M =======================================
int main()
{
size_t length;
std::vector<Graph> dataG =creategraphs(splitstringtolines(readfromfile("3test.txt", length)));
cout<<"***PS***\n dataG[0] is a subgraph of dataG[1]\n dataG[2] is the same as dataG[0]\n dataG[3] is the same as dataG[0] but with other labels.\n"<<endl;
my_callback<Graph, Graph> my_callback(dataG[0], dataG[3]);
cout<<"equal(dataG[0], dataG[3],my_callback)="<<vf2_graph_iso(dataG[0], dataG[3],my_callback)<<endl;
mcgregor_common_subgraphs(dataG[0], dataG[3], true, my_callback);
}
Unfortunately when I run it I got these errors:
/usr/include/boost/graph/mcgregor_common_subgraphs.hpp||In instantiation of ‘bool boost::detail::mcgregor_common_subgraphs_internal(const GraphFirst&, const GraphSecond&, const VertexIndexMapFirst&, const VertexIndexMapSecond&, CorrespondenceMapFirstToSecond, CorrespondenceMapSecondToFirst, VertexStackFirst&, EdgeEquivalencePredicate, VertexEquivalencePredicate, bool, SubGraphInternalCallback) [with GraphFirst = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; GraphSecond = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; VertexIndexMapFirst = boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int>; VertexIndexMapSecond = boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int>; CorrespondenceMapFirstToSecond = boost::shared_array_property_map<unsigned int, boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int> >; CorrespondenceMapSecondToFirst = boost::shared_array_property_map<unsigned int, boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int> >; VertexStackFirst = std::stack<unsigned int, std::deque<unsigned int, std::allocator<unsigned int> > >; EdgeEquivalencePredicate = boost::always_equivalent; VertexEquivalencePredicate = boost::always_equivalent; SubGraphInternalCallback = my_callback<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>, boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties> >]’:|
/usr/include/boost/graph/mcgregor_common_subgraphs.hpp|433|required from ‘void boost::detail::mcgregor_common_subgraphs_internal_init(const GraphFirst&, const GraphSecond&, VertexIndexMapFirst, VertexIndexMapSecond, EdgeEquivalencePredicate, VertexEquivalencePredicate, bool, SubGraphInternalCallback) [with GraphFirst = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; GraphSecond = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; VertexIndexMapFirst = boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int>; VertexIndexMapSecond = boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int>; EdgeEquivalencePredicate = boost::always_equivalent; VertexEquivalencePredicate = boost::always_equivalent; SubGraphInternalCallback = my_callback<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>, boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties> >]’|
/usr/include/boost/graph/mcgregor_common_subgraphs.hpp|484|required from ‘void boost::mcgregor_common_subgraphs(const GraphFirst&, const GraphSecond&, bool, SubGraphCallback) [with GraphFirst = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; GraphSecond = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; SubGraphCallback = my_callback<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>, boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties> >]’|
/home/mohsenuss91/Bureau/NouvelleApprocheBOOST/stackoverflow.cpp|211|required from here|
/usr/include/boost/graph/mcgregor_common_subgraphs.hpp|337|error: no match for call to ‘(my_callback<boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>, boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties> >) (boost::shared_array_property_map<unsigned int, boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int> >&, boost::shared_array_property_map<unsigned int, boost::vec_adj_list_vertex_id_map<VertexProperties, unsigned int> >&, VertexSizeFirst&)’|
/home/mohsenuss91/Bureau/NouvelleApprocheBOOST/stackoverflow.cpp|69|note: candidate is:|
/home/mohsenuss91/Bureau/NouvelleApprocheBOOST/stackoverflow.cpp|76|note: template<class CorrespondenceMap1To2, class CorrespondenceMap2To1> bool my_callback<Graph1, Graph2>::operator()(CorrespondenceMap1To2, CorrespondenceMap2To1) const [with CorrespondenceMap1To2 = CorrespondenceMap1To2; CorrespondenceMap2To1 = CorrespondenceMap2To1; Graph1 = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>; Graph2 = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties, GraphProperties>]|
/home/mohsenuss91/Bureau/NouvelleApprocheBOOST/stackoverflow.cpp|76|note: template argument deduction/substitution failed:|
/usr/include/boost/graph/mcgregor_common_subgraphs.hpp|337|note: candidate expects 2 arguments, 3 provided|
||=== Build failed: 1 error(s), 4 warning(s) (0 minute(s), 6 second(s)) ===|
Docs:
OUT: SubGraphCallback user_callback
A function object that will be invoked when a subgraph has been discovered. The operator() must have the following form:
template <typename CorrespondenceMapFirstToSecond,
typename CorrespondenceMapSecondToFirst>
bool operator()(
CorrespondenceMapFirstToSecond correspondence_map_1_to_2,
CorrespondenceMapSecondToFirst correspondence_map_2_to_1,
typename graph_traits<GraphFirst>::vertices_size_type subgraph_size
);
So at the very least make the functor take the extra argument:
template <typename Graph1, typename Graph2>
struct my_callback {
my_callback(const Graph1 &graph1, const Graph2 &graph2) : graph1_(graph1), graph2_(graph2) {}
template <typename CorrespondenceMap1To2, typename CorrespondenceMap2To1>
bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1) const {
// vf2_graph_iso
return true;
}
template <typename CorrespondenceMap1To2, typename CorrespondenceMap2To1, typename X>
bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1, X) const {
// mcgregor_common_subgraphs
return true;
}
private:
const Graph1 &graph1_;
const Graph2 &graph2_;
};
Of course, this /just/ fixes the compilation error. It doesn't look like the callbacks do useful work.
Output:
***PS***
dataG[0] is a subgraph of dataG[1]
dataG[2] is the same as dataG[0]
dataG[3] is the same as dataG[0] but with other labels.
equal(dataG[0], dataG[3],my_callback)=1
Working sample:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/mcgregor_common_subgraphs.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/config.hpp>
#include <boost/graph/isomorphism.hpp>
#include <boost/graph/graph_utility.hpp>
#include <fstream>
#include <iostream>
#include <vector>
// for mmap:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
using namespace std;
using namespace boost;
//==========STRUCTURES==========
// vertex
struct VertexProperties {
int id;
int label;
VertexProperties(unsigned i = 0, unsigned l = 0) : id(i), label(l) {}
};
// edge
struct EdgeProperties {
unsigned label;
EdgeProperties(unsigned l = 0) : label(l) {}
};
// Graph
struct GraphProperties {
unsigned id;
unsigned label;
GraphProperties(unsigned i = 0, unsigned l = 0) : id(i), label(l) {}
};
// adjency list
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, VertexProperties, EdgeProperties,
GraphProperties> Graph;
// descriptors
typedef boost::graph_traits<Graph>::vertex_descriptor vertex_t;
typedef std::pair<boost::graph_traits<Graph>::edge_descriptor, bool> edge_t;
// iterators
typedef graph_traits<Graph>::vertex_iterator vertex_iter;
typedef graph_traits<Graph>::edge_iterator edge_iter;
typedef std::pair<edge_iter, edge_iter> edge_pair;
//*********global variables*************
vector<Graph> dataG;
//=================callback used fro subgraph_iso=================================================================
// Default print_callback
template <typename Graph1, typename Graph2>
struct my_callback {
my_callback(const Graph1 &graph1, const Graph2 &graph2) : graph1_(graph1), graph2_(graph2) {}
template <typename CorrespondenceMap1To2, typename CorrespondenceMap2To1>
bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1) const {
// vf2_graph_iso
return true;
}
template <typename CorrespondenceMap1To2, typename CorrespondenceMap2To1, typename X>
bool operator()(CorrespondenceMap1To2 f, CorrespondenceMap2To1, X) const {
// mcgregor_common_subgraphs
return true;
}
private:
const Graph1 &graph1_;
const Graph2 &graph2_;
};
//==========handle_error==========
void handle_error(const char *msg) {
perror(msg);
exit(255);
}
//============READ ALL THE FILE AND RETURN A STRING===================
const char *readfromfile(const char *fname, size_t &length) {
int fd = open(fname, O_RDONLY);
if (fd == -1)
handle_error("open");
// obtain file size
struct stat sb;
if (fstat(fd, &sb) == -1)
handle_error("fstat");
length = sb.st_size;
const char *addr = static_cast<const char *>(mmap(NULL, length, PROT_READ, MAP_PRIVATE, fd, 0u));
if (addr == MAP_FAILED)
handle_error("mmap");
// TODO close fd at some point in time, call munmap(...)
return addr;
}
//==========SPLIT THE STRING BY NEWLINE (\n) ==========
vector<string> splitstringtolines(string const &str) {
std::vector<string> split_vector;
split(split_vector, str, is_any_of("\n"));
return split_vector;
}
//============Get a string starting from pos============
string getpos(int const &pos, string const &yy) {
size_t i = pos;
string str;
for (; ((yy[i] != ' ') && (i < yy.length())); i++) {
str += yy[i];
}
return str;
}
//==================read string vector and return graphs vector===================
std::vector<Graph> creategraphs(std::vector<string> const &fichlines) {
for (string yy : fichlines) {
switch (yy[0]) {
case 't': {
string str2 = getpos(4, yy);
unsigned gid = atoi(str2.c_str());
dataG.emplace_back(GraphProperties(gid, gid));
} break;
case 'v': {
assert(!dataG.empty()); // assert will terminate the program if its argument turns out to be false
// cout<<yy<<endl;
int vId, vLabel;
string vvv = getpos(2, yy);
vId = atoi(vvv.c_str());
string vvvv = getpos((int)vvv.length() + 3, yy);
// cout<<vvvv<<endl;
vLabel = atoi(vvvv.c_str());
boost::add_vertex(VertexProperties(vId, vLabel), dataG.back());
}
break;
case 'e': { // cout<<yy<<endl;
assert(!dataG.empty()); // assert will terminate the program if its argument turns out to be false
int fromId, toId, eLabel;
string eee = getpos(2, yy);
// cout<<eee<<endl;
fromId = atoi(eee.c_str());
string eee2 = getpos((int)eee.length() + 3, yy);
// cout<<eee2<<endl;
toId = atoi(eee2.c_str());
int c = (int)eee.length() + (int)eee2.length() + 4;
// cout<<c<<endl;
string eee3 = getpos(c, yy);
// cout<<eee3<<endl;
eLabel = atoi(eee3.c_str());
for (size_t i = 0; i < num_vertices(dataG.back()); ++i) // size_t vertice number in the graph
{
if (dataG.back()[i].id == fromId)
fromId = i;
else if (dataG.back()[i].id == toId)
toId = i;
}
boost::add_edge(fromId, toId, EdgeProperties(eLabel), dataG.back());
} break;
}
}
return dataG;
}
//==============================M A I N P R O G R A M =======================================
int main() {
size_t length;
std::vector<Graph> dataG = creategraphs(splitstringtolines(readfromfile("3test.txt", length)));
cout << "***PS***\n dataG[0] is a subgraph of dataG[1]\n dataG[2] is the same as dataG[0]\n dataG[3] is the same "
"as dataG[0] but with other labels.\n" << endl;
my_callback<Graph, Graph> my_callback(dataG[0], dataG[3]);
cout << "equal(dataG[0], dataG[3],my_callback)=" << vf2_graph_iso(dataG[0], dataG[3], my_callback) << endl;
mcgregor_common_subgraphs(dataG[0], dataG[3], true, my_callback);
}

How do I attach objects to the nodes and edges of a graph using the Boost Graph Library?

I would like to use the Boost Graph Library more effectively by attaching properly encapsulated classes to graph nodes & edges. I am not interested in attaching int's or POD struct's. Following suggestions on other StackOverFlow articles, I have developed the following sample app. Can anybody tell me the magic I need to sprinkle onto the EdgeInfo class to make this thing compile?
I am using Visual Studio 2010 with Boost 1.54.0.
//------------------------------------------------------------------------
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <iostream>
//------------------------------------------------------------------------
struct VertexInfo
{
struct Tag
{
typedef boost::vertex_property_tag kind;
static std::size_t const num; // ???
};
typedef boost::property<Tag, VertexInfo> Property;
};
std::size_t const VertexInfo::Tag::num = reinterpret_cast<std::size_t> (&VertexInfo::Tag::num);
//------------------------------------------------------------------------
class EdgeInfo
{
int _nWeight;
public:
int getWeight () const {return _nWeight;}
struct Tag
{
typedef boost::edge_property_tag kind;
static std::size_t const num; // ???
};
typedef boost::property<boost::edge_weight_t, int> Weight;
typedef boost::property<Tag, EdgeInfo> Property;
EdgeInfo (int nWeight = 9999) : _nWeight (nWeight) {}
};
std::size_t const EdgeInfo::Tag::num = reinterpret_cast<std::size_t> (&EdgeInfo::Tag::num);
//------------------------------------------------------------------------
typedef boost::property<boost::edge_weight_t, int> EdgeProperty;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexInfo::Property, EdgeProperty> GraphWorking;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexInfo::Property, EdgeInfo::Property> GraphBroken;
//------------------------------------------------------------------------
template<typename GraphType, typename EdgeType> void
dijkstra (GraphType g, EdgeType e)
{
typedef boost::graph_traits<GraphType>::vertex_descriptor VertexDesc;
typedef boost::graph_traits<GraphType>::edge_descriptor EdgeDesc;
VertexDesc u = add_vertex (g);
VertexDesc v = add_vertex (g);
std::pair<EdgeDesc, bool> result = add_edge (u, v, e, g);
std::vector<VertexDesc> vecParent (num_vertices (g), 0);
dijkstra_shortest_paths (g, u, boost::predecessor_map (&vecParent[0]));
}
//------------------------------------------------------------------------
int
main (int argc, char** argv)
{
#if defined(BOOST_MSVC) && BOOST_MSVC <= 1300
std::cout << "Buy a new compiler\n";
#else
std::cout << "Your compiler is fine\n";
#endif
GraphWorking gWorking;
GraphBroken gBroken;
dijkstra (gWorking, 3);
dijkstra (gBroken, EdgeInfo (4));
}
//------------------------------------------------------------------------
When I run your code i get an error in numeric_limits that results from a distance map in dijkstra.
"
Error 1 error C2440: '' : cannot convert from 'int' to 'D' c:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\include\limits 92
"
probably from this part of http://www.boost.org/doc/libs/1_55_0/boost/graph/dijkstra_shortest_paths.hpp
typedef typename property_traits<DistanceMap>::value_type D;
D inf = choose_param(get_param(params, distance_inf_t()),
(std::numeric_limits<D>::max)());
I think there may be an easier way to tie a real class for your nodes and edges. Its more trouble than its worth to create vertex and edge property classes that will provide all the needed tagged properties (index, weight, color, etc) needed for most boost algorihtms.
Don't forget Edge class != Edge property.
The edge class is really the graph_traits::edge_discriptor.
Properties are the data associated with each edge. Same for vertex.
I would use bundled properties and add a pointer to your class in each one.
Here is an example
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/properties.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/property_map/property_map.hpp>
#include <iostream>
//Fancy Edge class
class EdgeData
{
int _data;
public:
EdgeData(){
_data=0;
}
EdgeData(int data){
_data= data;
}
void printHello(){
std::cout << "hello " << _data << std::endl;
}
};
//Fancy Vert class
class VertexData
{
int _data;
public:
VertexData(){
_data=0;
}
VertexData(int data){
_data= data;
}
void printHello(){
std::cout << "hello " << _data << std::endl;
}
};
//bundled properties
struct VertexProps
{
VertexData* data;
};
struct EdgeProps
{
size_t weight;
EdgeData* data;
};
//Graph
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS,
VertexProps,EdgeProps> Graph;
//helpers
//Vertex
typedef boost::graph_traits<Graph>::vertex_descriptor Vertex;
//Edge
typedef boost::graph_traits<Graph>::edge_descriptor Edge;
//------------------------------------------------------------------------
template<typename GraphType> void
templateFunction (GraphType g)
{
typedef boost::graph_traits<GraphType>::edge_iterator edge_iter;
std::pair<edge_iter, edge_iter> ep;
edge_iter ei, ei_end;
ep = edges(g);
ei_end = ep.second;
for (ei = ep.first; ei != ei_end; ++ei){
g[*ei].data->printHello();
}
}
//if you want to alter the graph use referenced &graph
template<typename GraphType,typename EdgePropType> void
templateFuctionProps(GraphType &g, EdgePropType e)
{
typedef boost::graph_traits<GraphType>::vertex_descriptor VertexDesc;
VertexDesc v = add_vertex(g);
VertexDesc u = add_vertex(g);
//add an edge with the Edge property
add_edge(v,u,e,g);
}
//------------------------------------------------------------------------
int
main (int argc, char** argv)
{
Graph g;
//vertex holder
std::vector<Vertex> verts;
//add some verts
for(size_t i = 0; i < 5; ++i){
Vertex v = add_vertex(g);
g[v].data = new VertexData(i%2);
verts.push_back(v);
}
//add some edges
for(size_t i = 0; i < 4; ++i){
std::pair<Edge,bool> p = add_edge(verts.at(i),verts.at(i+1),g);
Edge e = p.first;
g[e].data = new EdgeData(i%3);
g[e].weight = 5;
}
//iterate edges and call a class function
typedef boost::graph_traits<Graph>::edge_iterator edge_iter;
std::pair<edge_iter, edge_iter> ep;
edge_iter ei, ei_end;
ep = edges(g);
ei_end = ep.second;
for (ei = ep.first; ei != ei_end; ++ei){
g[*ei].data->printHello();
}
std::cout << "Iterate with template with template " << std::endl;
templateFunction(g);
//Use an edge property in a function
EdgeProps edgeProp;
edgeProp.weight = 5;
edgeProp.data = new EdgeData(150);
std::cout << "Modity graph with template function " << std::endl;
templateFuctionProps(g,edgeProp);
std::cout << "Iterate again with template" << std::endl;
templateFunction(g);
//getting the weight property
boost::property_map<Graph,size_t EdgeProps::*>::type w
= get(&EdgeProps::weight, g);
std::cout << "Print weights" << std::endl;
ep = edges(g);
ei_end = ep.second;
for (ei = ep.first; ei != ei_end; ++ei){
std::cout << w[*ei] << std::endl;
}
std::cin.get();
}
//------------------------------------------------------------------------
Also I see you are using vecS, meaning that both vectors and edges are stored as vectors with a fixed ordering.
You could just have a class that stores your Edge and Vertex classes with a pointer to the vertex map or edge map for the graph.
I don't know your goals for this project, but I would definitely have higher level classes than manage all of this boost stuff behind the scenes. Meaning storing classes in a vector with an index look up would be hidden and encapsulated from applications that want to use your nice graph class.