I am trying to convert myself from R to C++ and am struggling with a particular graph problem. I have a String Matrix called "Gra" as below.
int main(){
string Gra[4][5] = {{"V0", "V1", "V2", "V3", "V4"},
{"V5", "V6", "NA", "NA", "V7"},
{"V8", "V9", "NA", "NA", "V10"},
{"V11", "V12", "V13", "V14", "V15"}};
Where "V0" represents a node and "NA" is not. This matrix came from the matrix called "base"
int base[4][5] = {{1, 1, 1, 1, 1},
{1, 1, 0, 0, 1},
{1, 1, 0, 0, 1},
{1, 1, 1, 1, 1}};
typedef float Weight;
typedef boost::property<boost::edge_weight_t, Weight> WeightProperty;
typedef boost::property<boost::vertex_name_t, std::string> NameProperty;
typedef boost::adjacency_list < boost::listS, boost::vecS, boost::directedS,
NameProperty, WeightProperty > Graph;
typedef boost::graph_traits < Graph >::vertex_descriptor Vertex;
typedef boost::property_map < Graph, boost::vertex_index_t >::type IndexMap;
typedef boost::property_map < Graph, boost::vertex_name_t >::type NameMap;
typedef boost::iterator_property_map < Vertex*, IndexMap, Vertex, Vertex& > PredecessorMap;
typedef boost::iterator_property_map < Weight*, IndexMap, Weight, Weight& > DistanceMap;
Graph g;
Where the problem is, trying to describe the graph in a loop. I would like to declare the nodes as
Vertex V0 = boost::add_vertex(std::string("V0"), g); // Struggling to implement this in a loop
Vertex V1 = boost::add_vertex(std::string("V1"), g);
Vertex V2 = boost::add_vertex(std::string("V2"), g);
Vertex V3 = boost::add_vertex(std::string("V3"), g);
Vertex V4 = boost::add_vertex(std::string("V4"), g);
Vertex V5 = boost::add_vertex(std::string("V5"), g);
Vertex V6 = boost::add_vertex(std::string("V6"), g);
Vertex V7 = boost::add_vertex(std::string("V7"), g);
Vertex V8 = boost::add_vertex(std::string("V8"), g);
Vertex V9 = boost::add_vertex(std::string("V9"), g);
Vertex V10 = boost::add_vertex(std::string("V10"), g);
Vertex V11 = boost::add_vertex(std::string("V11"), g);
Vertex V12 = boost::add_vertex(std::string("V12"), g);
Vertex V13 = boost::add_vertex(std::string("V13"), g);
Vertex V14 = boost::add_vertex(std::string("V14"), g);
Vertex V15 = boost::add_vertex(std::string("V15"), g);
How I have tried to replicate this is through a loop like this.
for ( int i=0; i < 4; i++) // So this will run along all elements of our base vector
{
for ( int j=0; j < 5; j++) // Length is the number of elements in our array
{
if( !(Gra[i][j] == "NA")) // Whilst going along each element inspecting whether it is a true node
{
Vertex Gra[i][j] = boost::add_vertex(std::Gra[i][j], g); // This is where the problem is
}
}
}
So the problem comes from using a string to define this object of class Vertex. Could any body help me? I'm pretty sure this is a naming convention issue that I'm struggling with. If this is solved then I can solve the rest of my problems in terms of creating the edges which I also have the same problem of trying to call an object of class "vertex" using a string.
Thanks in advance Cyrill
The expression !Gra[i][j] == "NA" will not do what you expect it to. It will first check that Gra[i][j] is not "false" and then compare the boolean result with the string "NA".
Instead either use parentheses around the equality check, or do a not-equal check. So either
!(Gra[i][j] == "NA")
or
Gra[i][j] != "NA"
There's also the problem that you declare a local array-of-arrays variable Gra inside the inner loop, which will cause clashes with your outer Gra variable. I guess that's why you use std::Gra there, but Gra is not declared in the standard namespace. Neither can you use ::Gra because Gra is not declared in the global namespace either.
Instead of declaring a new variable inside the inner loop, declare an array Vertexes outside the loop:
Vertex Vertexes[4][5];
Then use that variable to store the result of boost::add_vertex.
Related
I am trying to solve the traveling salesmen problem on a graph created using boost::adjacency_list. I am using the metric_tsp_approx to solve the tsp.
The problem I am facing is that the solution does not follow the graph edges. The solution connects vertices in the graph which are not directly connected. I wanted to know if this is how the library works or if I am doing something wrong. The solution does not look correct either. I had 4 vertices forming a square, the solution should have been going along the perimeter, but it was going along the diagonal instead. There was no edge along the diagonal.
This is my adjacency_list:
boost::adjacency_list<boost::setS, boost::listS, boost::undirectedS,
boost::property<boost::vertex_index_t, int>,
boost::property<boost::edge_weight_t, double>,
boost::no_property>
Add vertex and add edge functions:
boost::add_vertex(id, graph);
boost::add_edge(id1, id2, weight, graph); // weight is euclidean distance
TSP solver:
std::vector<VertexDescriptor> tsp_path; //VertexDescriptor is adjacency_list::vertex_descriptor
metric_tsp_approx_tour(graph, back_inserter(tsp_path));
I also tried passing the weightmap to the metric_tsp_approx_tour but the same problem persists.
Can someone help me solve this? If the boost metric_tsp_approx_tour does not consider the edges of the graph, is there a way to make it consider them?
The docs: https://www.boost.org/doc/libs/1_74_0/libs/graph/doc/metric_tsp_approx.html
This is a traveling salesperson heuristic for generating a tour of vertices for a fully connected undirected graph with weighted edges that obey the triangle inequality.
(emphasis mine)
The "fully connected graph" clause does state that all vertices are assumed to be connected.
Note as well, the vertex index is assumed to map to [0,num_vertices(graph)).
BONUS
As a bonus I tried to work out a minimal working example for the algorithm. It does seem to work as advertised:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graph_utility.hpp>
#include <boost/graph/metric_tsp_approx.hpp>
using Graph =
boost::adjacency_list<boost::setS, boost::listS, boost::undirectedS,
boost::property<boost::vertex_index_t, int>,
boost::property<boost::edge_weight_t, double>,
boost::no_property>;
using VertexDescriptor = Graph::vertex_descriptor;
int main() {
std::vector const points { std::pair
{ 4., 9. }, // these can be randomized
{ 2., 6. },
{ 4., 1. },
{ 1., 1. },
};
Graph graph;
for (auto i = 0u; i < points.size(); ++i) {
add_vertex(i, graph);
}
for (auto i = 0u; i < points.size(); ++i) {
auto va = vertex(i, graph);
// undirected, so only need traverse higher vertices for connections
for (auto j = i+1; j < points.size(); ++j) {
auto vb = vertex(j, graph);
auto const [ax, ay] = points.at(i);
auto const [bx, by] = points.at(j);
auto const dx = bx - ax;
auto const dy = by - ay;
add_edge(va, vb, sqrt(dx*dx + dy*dy), graph); // weight is euclidean distance
}
}
print_graph(graph);
std::vector<VertexDescriptor> tsp_path(num_vertices(graph)); //VertexDescriptor is adjacency_list::vertex_descriptor
metric_tsp_approx_tour(graph, back_inserter(tsp_path));
auto idmap = get(boost::vertex_index, graph);
for (auto vd : tsp_path) {
if (vd != graph.null_vertex()) {
auto [x,y] = points.at(idmap[vd]);
std::cout << " {" << x << "," << y << "}";
}
}
}
Prints
0 <--> 1 2 3
1 <--> 0 2 3
2 <--> 0 1 3
3 <--> 0 1 2
{4,9} {2,6} {1,1} {4,1} {4,9}
I have a simple graph, I suceeded writing properties with the vertex, but when I use make_label_writer to write properties to the edges, the complier always complains. Could someone help with it?
My code is as following:
int main (int argc, char * argv[]) {
typedef std::pair<int ,int> Edge;
std::vector<Edge> used_by = {Edge(1, 0), Edge(2, 1),
Edge(1,2), Edge(2, 0)};
using namespace boost;
typedef adjacency_list<vecS, vecS, directedS
> Graph;
Graph g(used_by.begin(), used_by.end(), 3);
std::ofstream dmp;
dmp.open("dmp.dot");
//name for vertex
std::vector<std::string> name{"one", "two", "three"};
//name for edge
std::vector<std::string> name1{"e1", "e2", "e3", "e4"};
write_graphviz(std::cout, g, make_label_writer(&name[0])
,make_label_writer(&name1[0]));
}
The write_graphviz() will ofc called the template, which is perfectly fine :
template <typename Graph, typename VertexWriter, typename
EdgeWriter>
inline void
write_graphviz(std::ostream& out, const Graph& g,
VertexWriter vw, EdgeWriter ew
BOOST_GRAPH_ENABLE_IF_MODELS_PARM(Graph,vertex_list_graph_tag))
{
default_writer gw;
write_graphviz(out, g, vw, ew, gw);
}
So the problem is now: when I only write the vertex properties using make_label_writer(&name[0]]]), the code runs perfectly. But when I add make_label_writer(&name1[0]), there is error.
The default vertex index is integral, which is why you can use the address of the first vertex name as implied associative property map.
The edge descriptor is a different beast and requires you to either
create an explicit iterator property map (using an extra index property map to map from edge descriptor to the integral index into the name1 vector)
or use a model of the Associative PropertyMap concept.
In this case you should property do the later using a std::map<edge_descriptor, std::string>.
Please also consider making your life with properties a lot simpler by using Bundled Properties.
Associative Property Map
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS> Graph;
int main() {
Graph g(3);
auto e1 = add_edge(1, 0, g).first;
auto e2 = add_edge(2, 1, g).first;
auto e3 = add_edge(1, 2, g).first;
auto e4 = add_edge(2, 0, g).first;
std::vector<std::string> vname{ "one", "two", "three" };
std::map<Graph::edge_descriptor, std::string> ename{
{ e1, "e1" },
{ e2, "e2" },
{ e3, "e3" },
{ e4, "e4" },
};
write_graphviz(std::cout, g,
boost::make_label_writer(&vname[0]),
make_label_writer(boost::make_assoc_property_map(ename)));
}
Prints
Bundled Properties Instead
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/graphviz.hpp>
struct VertexProps { std::string name; };
struct EdgeProps { std::string name; };
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexProps, EdgeProps> Graph;
int main() {
Graph g(3);
g[0].name = "one";
g[1].name = "two";
g[2].name = "three";
add_edge(1, 0, {"e1"}, g);
add_edge(2, 1, {"e2"}, g);
add_edge(1, 2, {"e3"}, g);
add_edge(2, 0, {"e4"}, g);
write_graphviz(std::cout, g,
make_label_writer(get(&VertexProps::name, g)),
make_label_writer(get(&EdgeProps::name, g)));
}
Prints the same
I am boost noob. I am wondering why compilation fails in the following code. I am creating a set of vertices, and trying to assign my own vertex indices and vertex names. (I am following this page: http://fireflyblue.blogspot.com/2008/01/boost-graph-library.html. )
I understand that vertS vertex lists in Boost does not need explicit vertex id creations, and I have also seen this very related question in Stackoverflow (how provide a vertex_index property for my graph) which discusses how to use an associative_property_map to assign vertex indices. The following though - getting the vertex_index map, and assigning the key value pairs - seems a fairly straightforward thing to do, and I would like to understand why it fails. Any help is greatly appreciated!
The compile error is as below:
error: expression is not assignable
vertIndx[v] = i;
//Define graph
typedef boost::property<boost::vertex_name_t, std::string> sv_namePty;
typedef boost::property<boost::vertex_index_t, int, sv_namePty > sv_indx_n_name_pty;
typedef boost::property<boost::edge_weight_t, int> se_weightPty;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS,
sv_indx_n_name_pty, se_weightPty> ScafGraph;
//descriptors
typedef boost::graph_traits<ScafGraph>::vertex_descriptor SV;
typedef boost::graph_traits<ScafGraph>::edge_descriptor SE;
//Graph Object
ScafGraph SG;
//property accessors
boost::property_map<ScafGraph,
boost::vertex_name_t>::type vertName = boost::get(boost::vertex_name, SG);
boost::property_map<ScafGraph,
boost::vertex_index_t>::type vertIndx = boost::get(boost::vertex_index, SG);
boost::property_map<ScafGraph,
boost::edge_weight_t>::type edgeWeight = boost::get(boost::edge_weight, SG);
//Populate Graph
std::vector<SV> svlist;
for(int i=0; i<4; i++) {
SV v = boost::add_vertex(SG);
svlist.push_back(v);
vertName[v] = std::to_string(i);
vertIndx[v] = i;
}
The expression vertIndx[v] returns a Vertex by value. Thus you get the error because it's not an lvalue when you try to assign to it.
Furthermore, it actually returns v. Here's the code run by vertIndx[v]:
inline value_type operator[](key_type v) const { return v; }
Here's a version that is hopefully clear about how it works:
#include <boost\graph\adjacency_list.hpp>
int main()
{
//Define graph
typedef boost::adjacency_list
<
boost::vecS //! edge list
, boost::vecS //! vertex list
, boost::undirectedS //! undirected graph
, boost::property<boost::vertex_name_t, std::string> //! vertex properties : name
, boost::property<boost::edge_weight_t, int> //! edge properties : weight
> ScafGraph;
//descriptors
typedef boost::graph_traits<ScafGraph>::vertex_descriptor SV;
typedef boost::graph_traits<ScafGraph>::edge_descriptor SE;
//Graph Object
ScafGraph SG;
//property accessors
boost::property_map<ScafGraph,
boost::vertex_name_t>::type vertName = boost::get(boost::vertex_name, SG);
boost::property_map<ScafGraph,
boost::vertex_index_t>::type vertIndx = boost::get(boost::vertex_index, SG);
boost::property_map<ScafGraph,
boost::edge_weight_t>::type edgeWeight = boost::get(boost::edge_weight, SG);
//Populate Graph
std::vector<SV> svlist;
for (int i = 0; i < 4; i++) {
SV v = boost::add_vertex(ScafGraph::vertex_property_type(std::to_string(i)), SG);
svlist.push_back(v);
assert(vertName[v] == std::to_string(i));
assert(vertIndx[v] == i);
}
return 0;
}
I want to know, how can i use bellman-ford algorithm with such graph:
typedef boost::property <boost::vertex_name_t,std::string> VertexProperty;
typedef boost::property <boost::edge_weight_t,int> EdgeProperty;
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::directedS,VertexProperty,EdgeProperty> DiGraph;
obtained from by this way:
boost::dynamic_properties dp;
dp.property("name",boost::get(boost::vertex_name,digraph));
dp.property("weight",boost::get(boost::edge_weight,digraph));
try
{
read_graphml(file_stream,digraph,dp);
}
catch(boost::graph_exception &ge)
{
myprint<<ge.what();
}
Thanks in advance.
For your example of graph, just after having read your graph and having set your source vertex in source_node_index:
const int nb_vertices = num_vertices(g);
// gets the weight property
property_map<DiGraph, boost::edge_weight_t>::type weight_pmap =
get(boost::edge_weight_t(), g);
// init the distance
std::vector<int> distance(nb_vertices, (std::numeric_limits<int>::max)());
distance[source_node_index] = 0; // the source is at distance 0
// init the predecessors (identity function)
std::vector<std::size_t> parent(nb_vertices);
for (int i = 0; i < nb_vertices; ++i)
parent[i] = i;
// call to the algorithm
bool r = bellman_ford_shortest_paths(
g,
nb_vertices,
weight_map(weight_pmap).
distance_map(&distance[0]).
predecessor_map(&parent[0])
);
The call to bellman_ford_shortest_paths is a bit weird and not very well documented (this bgl_named_params is a bit confusing).
I have a graph with multiple edge weightings stored as
namespace boost {
enum edge_weightvector_t {
edge_weightvector = 1337
};
BOOST_INSTALL_PROPERTY(edge, weightvector);
}
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::undirectedS,
boost::no_property,
boost::property<boost::edge_weightvector_t, std::vector<int> >
> graph_t;
The weightings are all pushed onto the vector.
Now I want to call the prim_minimum_spanning_tree() function on the graph, with the first elements in the vector used as weightings.
How can I perform a correct function call?
I've did it now by first copying the desired weightings to an additional property, then running the algorithm and copying back afterwards. It is ugly, but it does the trick in my case.
I recently tried to do the same (to use a vector property) and failed to run algorithms only with one of the values. However, I found that using exterior properties is a good approach that won't lead to unnecessary copy actions and to pass the property map explicitly to the algorithm.
If you use random access containers you can use boost::iterator_property_map that will wrap that container and make it a property_map. Instead of edge descriptors it requires 0-based edge indices for the efficient mapping between edges and property values. Here is the punchline, further done you find the complete example:
// ...
EdgeIndexMap edgeIds = get(edge_index, g);
// ...
typedef std::vector<int> Weights;
typedef std::vector<Weights> WeightsVector;
typedef iterator_property_map <Weights::iterator, EdgeIndexMap> WeightMap;
// ...
Weights weights; // = ...
WeightMap wm(weights.begin(), edgeIds);
// ...
some_bgl_algorithm(g, wm);
And here a complete example:
using namespace boost;
void sampleExteriorProperties()
{
typedef adjacency_list<vecS, vecS, undirectedS,
no_property,
//property<edge_index_t, int, property<edge_weight_t, int> >
property<edge_index_t, std::size_t>
> Graph;
typedef graph_traits<Graph>::edge_descriptor Edge;
typedef graph_traits<Graph>::edge_iterator EdgeIterator;
typedef property_map<Graph, edge_index_t>::type EdgeIndexMap;
//typedef property_map<Graph, edge_weight_t>::type WeightMap;
const int NVERTICES = 5;
const int NEDGES = 8;
Graph g(NVERTICES);
// Add edges WITH indexes.
int edgeIndex = 0;
add_edge(0, 1, edgeIndex++, g);
add_edge(0, 2, edgeIndex++, g);
add_edge(0, 3, edgeIndex++, g);
add_edge(1, 2, edgeIndex++, g);
add_edge(1, 4, edgeIndex++, g);
add_edge(2, 3, edgeIndex++, g);
add_edge(2, 4, edgeIndex++, g);
add_edge(3, 4, edgeIndex++, g);
// Weights: there must be a weight for every edge.
// Weights will be later on accessed by edge index.
assert(num_edges(g) == NEDGES);
typedef std::vector<int> Weights;
typedef std::vector<Weights> WeightsVector;
WeightsVector weightVector({ { 2, 3, 5, 7, 9, 11, 13, 17 },
{ 8, 7, 6, 5, 4, 3, 2, 1 }
});
EdgeIndexMap edgeIds = get(edge_index, g);
for (Weights &weights : weightVector)
{
// Use the iterator_property_map to read the properties from a
// random access container. Remember: Edge ids are used to access
// the correct value from the container!
typedef iterator_property_map <Weights::iterator, EdgeIndexMap> WeightMap;
WeightMap wm(weights.begin(), edgeIds);
EdgeIterator eIt, eItEnd;
tie(eIt, eItEnd) = edges(g);
while (eIt!=eItEnd)
{
std::cout << *eIt << ": " << wm[*eIt] << " ";
++eIt;
}
std::cout << std::endl;
// Explicitly pass the exterior map to the algorithm.
std::vector<Edge> mstEdges;
kruskal_minimum_spanning_tree(g, std::back_inserter(mstEdges),
weight_map(wm));
std::for_each(mstEdges.begin(), mstEdges.end(),
[](const Edge &val){std::cout << val << " ";});
std::cout << std::endl;
}
}