Clear vertex of all neighbors of v - c++

I'm implementing an algorithm in C++ with Boost Graph.
I want to find all the vertex in the neighborhood of v (so, all its neighbors), then change a property of their and finally clear all of their edges.
I found in Boost the function adjacent_vertices(v,g) (where v is the vertex and g is the graph) to find all the neighbors. Then I want to apply on all of them the function clear_vertex(v,g) (again, v is the vertex and g is the graph) to remove all of their edges.
At this point, I have a problem. The adjacent_vertices function returns a pair of adjacency_iterator, while for the clear_vertex function I need vertex_iterator (if I understand correctly how these functions work).
So, there is an easy way to transform the adjacency_iterator in vertex_iterator? If I keep the adjacency_iterator and pass it to the clear_vertex function, the problem is that it doesn't remove the edges (or remove them randomly to some vertices).
My wrong code is:
Graph::adjacency_iterator v,vend;
for(boost::tie(v,vend) = neighbours; v != vend ; ++v) {
clear_vertex(*v,g2);
}

It depends on the edge container selectors.
The easiest way is when the containers are node-based, i.e. only the iterators/descriptors to any removed edges are invalidated.
Another way is when you split the "query" and "modification" aspects, e.g.
Compiler Explorer
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/random.hpp>
#include <random>
void clear_all_neighbours(auto v, auto& g) {
auto neigh = adjacent_vertices(v, g);
std::set to_clear(neigh.first, neigh.second);
for (auto u : to_clear)
clear_vertex(u, g);
}
int main()
{
std::mt19937 prng(std::random_device{}());
boost::adjacency_list<> g;
generate_random_graph(g, 1000,2000, prng);
std::cout << "Before: " << num_edges(g) << "\n";
auto v = vertex(prng() % num_vertices(g), g);
clear_all_neighbours(v, g);
std::cout << "After: " << num_edges(g) << "\n";
}
Possible output:
Before: 2000
After: 1983

Related

Find an Edge by its vertices in an undirected graph [C++ BGL]

My question is very simple. I'm looking to find out an edge given the vertices it's connecting. The complicated way would be to iterate through all edges and compare each source and target with the vertices im using. I figured there would have to be a better way to do this which is why I'm asking
It depends on your graph model: https://valelab4.ucsf.edu/svn/3rdpartypublic/boost/libs/graph/doc/graph_concepts.html
AdjacencyMatrix has what you want directly:
auto e = boost::edge(v, u, graph);
(where e, v and u are descriptors).
Assuming you have a non-bidirectional instance of AdjacencyList you will have to look through the adjacencies of the source vertex:
using Graph = boost::adjacency_list<>;
using V = Graph::vertex_descriptor;
using E = Graph::edge_descriptor;
E my_find_edge(V v, V u, Graph const& g)
{
for (auto e : boost::make_iterator_range(out_edges(v, g))) {
if (target(e, g) == u)
return e;
}
throw std::domain_error("my_find_edge: not found");
}
In the case of Bidirectional graphs you have the option of going from the target vertex.
DEMO
Live On Compiler Explorer
#include "boost/graph/adjacency_list.hpp"
#include <boost/range/iterator_range.hpp>
using Graph = boost::adjacency_list<>;
using V = Graph::vertex_descriptor;
using E = Graph::edge_descriptor;
E my_find_edge(V v, V u, Graph const& g)
{
for (auto e : boost::make_iterator_range(out_edges(v, g))) {
if (target(e, g) == u)
return e;
}
throw std::domain_error("my_find_edge: not found");
}
#include "boost/graph/random.hpp"
#include <random>
int main() {
std::mt19937 prng { std::random_device{}() };
for (auto i = 0; i < 10; ++i)
try {
Graph g;
generate_random_graph(g, 10, 20, prng);
E e = my_find_edge(7, 2, g);
std::cout << "Found: " << e << "\n";
} catch (std::exception const& e) {
std::cout << e.what() << "\n";
}
}
Prints e.g.
my_find_edge: not found
my_find_edge: not found
Found: (7,2)
Found: (7,2)
my_find_edge: not found
my_find_edge: not found
my_find_edge: not found
my_find_edge: not found
my_find_edge: not found
Found: (7,2)
Further Optimization
In the case of setS vertex selector you MIGHT be able to optimize using std::binary_search and friends (std::lower_bound, std::upper_bound or std::equal_range).
I would heavily consult my profiler to see whether that actually improves performance. I have a feeling it might not unless you have very very high out-degrees.

How does the attractive force of Fruchterman Reingold work with Boost Graph Library

I am learning the Fruchterman-Reingold algorithm in Boost Graph Library. By reading the document, I know that the algorithm is to compute the positions for all nodes in terms of graph layout, but my problem is I cannot understand the calculation steps of attractive forces in Boost Graph Library.
For example, if the topology is rectangle with height 100 and width 100, each vertex is labelled as string, and the relation between each pair vertex as:
"0" "5"
"Kevin" "Martin"
"Ryan" "Leo"
"Y" "S"
"Kevin" "S"
"American" "USA"
Each row denotes the two labelled vertices are connected. The formula of attractive force for each vertex is supposed to be:
f = (d^2) / k
where d is the distance between two vertices and k is the optimal distances. But I don't understand how to get the distance d in the code of Fruchterman-Reingold in Boost Graph Library. In this example, does it compute the ASCII value difference between each pair vertices as the distance d? (ASCII value of '0' is 48, and ASCII value of '5' is 53. Is it true that Fruchterman-Reingold computes 53 - 48 = 5 as d in BGL?) I really appreciate if anyone can help me.
Furchterman-Reingold implementation takes an IN/OUT topology.
It expects the topology to be initialized to some state before execution. The distance passed to the attraction function will be the one from the topology at that iteration.
Note Note that (unless progressive is set to true) Furterman-Reingold will initialize the topology randomly by default (using random_graph_layout).
All the above taken from in the documentation.
Here's a tiny demo using your input graph that shows how to implement such an attractive_force function:
struct AttractionF {
template <typename EdgeDescriptor, typename Graph>
double operator()(EdgeDescriptor /*ed*/, double k, double d, Graph const& /*g*/) const {
//std::cout << "DEBUG af('" << g[source(ed, g)].name << " -> " << g[target(ed, g)].name << "; k:" << k << "; d:" << d << ")\n";
return (d*d/k);
}
};
See Live On Coliru
#include <memory>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/fruchterman_reingold.hpp>
#include <boost/graph/random_layout.hpp>
#include <libs/graph/src/read_graphviz_new.cpp>
#include <boost/graph/topology.hpp>
#include <boost/random.hpp>
using namespace boost;
struct Vertex {
std::string name;
};
struct AttractionF {
template <typename EdgeDescriptor, typename Graph>
double operator()(EdgeDescriptor /*ed*/, double k, double d, Graph const& /*g*/) const {
//std::cout << "DEBUG af('" << g[source(ed, g)].name << " -> " << g[target(ed, g)].name << "; k:" << k << "; d:" << d << ")\n";
return (d*d/k);
}
};
using Graph = adjacency_list<vecS, vecS, undirectedS, Vertex>;
Graph make_sample();
int main() {
auto g = make_sample();
using Topology = square_topology<boost::mt19937>;
using Position = Topology::point_type;
std::vector<Position> positions(num_vertices(g));
square_topology<boost::mt19937> topology;
random_graph_layout(g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology);
fruchterman_reingold_force_directed_layout(
g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology,
attractive_force(AttractionF())
);
dynamic_properties dp;
dp.property("node_id", get(&Vertex::name, g));
write_graphviz_dp(std::cout, g, dp);
}
Graph make_sample() {
std::string const sample_dot = R"(
graph {
"0" -- "5";
"Kevin" -- "Martin";
"Ryan" -- "Leo";
"Y" -- "S";
"Kevin" -- "S";
"American" -- "USA";
}
)";
Graph g;
dynamic_properties dp;
dp.property("node_id", get(&Vertex::name, g));
read_graphviz(sample_dot, g, dp);
return g;
}
Note that in c++11 you can equally well use a lambda:
fruchterman_reingold_force_directed_layout(
g,
make_iterator_property_map(positions.begin(), boost::identity_property_map{}),
topology,
attractive_force([](Graph::edge_descriptor, double k, double d, Graph const&) { return (d*d)/k; })
);

Dijkstra graph with a table of weights on each edge

I have a boost graph with multiples weights for each edges (imagine one set of weights per hour of the day). Every one of those weights values is stored in a propretyEdge class :
class propretyEdge {
std::map<std::string,double> weights; // Date indexed
}
I created a graph with those properties, and then filled it with the right values.
The problem is now that I want to launch the Dijkstra algorithm over a particular set of weight on the graph : for example a function that could be :
void Dijkstra (string date, parameters ... )
That would use the
weights[date]
value for each Edge of the graph.
I read over and over the documentation, and I couldn't have a clear picture of what I have to do. I surely need to write something like this, but I have no idea were to start :
boost::dijkstra_shortest_paths (
(*graph_m),
vertex_origin_num_l,
// weight_map (get (edge_weight, (*graph_m)))
// predecessor_map(boost::make_iterator_property_map(predecessors.begin(), get(boost::vertex_index, (*graph_m)))).
// distance_map(boost::make_iterator_property_map(distances.begin (), get(vertex_index,(*graph_m) )))
predecessor_map(predecessorMap).
distance_map(distanceMap)
);
Thank you for your help.
Edit
Thanks to the wonderful Answer of Sehe, I was able to do exactly what I wanted on MacOS and on Ubuntu.
But when we tried to compile this piece of code on Visual Studio 2012, it appeared that VS wasn't very good at understanding pointer function of boost. So we modified the part of Sehe :
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
by :
class dated_weight_f {
public:
dated_weight_f(Graph* graph_p,std::string date_p){
graph_m=graph_p;
date_m=date_p;
}
typedef double result_type;
result_type operator()(Edge edge_p) const{
return (*graph_m)[edge_p].weights.at(date_m);
}
private:
Graph* graph_m;
std::string date_m;
};
const auto dated_weight_map = make_function_property_map<Edge>(dated_weight_f(graph_m,date_l));
Which had the advantage of not using a pointer function.
Since it's apparently not immediately clear that this question is answered in the other answer, I'll explain.
All you really need is a custom weight_map parameter that is "stateful" and can select a certain value for a given date.
You can make this as complicated as you wish ¹, so you could even interpolate/extrapolate a weight given an unknown date ², but let's for the purpose of this demonstration keep it simple.
Let's define the graph type (roughly) as above:
struct propretyEdge {
std::map<std::string, double> weights; // Date indexed
};
using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;
Now, let's generate a random graph, with random weights for 3 different dates:
int main() {
Graph g;
std::mt19937 prng { std::random_device{}() };
generate_random_graph(g, 8, 12, prng);
uniform_real<double> weight_dist(10,42);
for (auto e : make_iterator_range(edges(g)))
for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
g[e].weights[date] = weight_dist(prng);
And, jumping to the goal:
for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
Dijkstra(date, g, 0);
}
}
Now how do you implement Dijkstra(...)? Gleaning from the documentation sample, you'd do something like
void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {
// magic postponed ...
std::vector<Graph::vertex_descriptor> p(num_vertices(g));
std::vector<double> d(num_vertices(g));
std::vector<default_color_type> color_map(num_vertices(g));
boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated
dijkstra_shortest_paths(g, vertex_origin_num_l,
weight_map(dated_weight_map).
predecessor_map(make_iterator_property_map(p.data(), vid)).
distance_map(make_iterator_property_map(d.data(), vid)).
color_map(make_iterator_property_map(color_map.data(), vid))
);
Now the only unclear bit here should be dated_weight_map.
Enter Boost Property Maps
As I showed in the linked Is it possible to have several edge weight property maps for one graph BOOST?, you can have all kinds of property maps ³, including invocation of user-defined functions. This is the missing piece:
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
Voilà: done
I hope that by now, the correspondence in the question as well as the answer of the linked question is clear. All that's left to do is post the full live sample and the outcome in a pretty picture:
Live On Coliru
#include <boost/property_map/property_map.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <boost/property_map/property_map_iterator.hpp>
#include <random>
#include <boost/graph/random.hpp>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <fstream>
using namespace boost;
struct propretyEdge {
std::map<std::string, double> weights; // Date indexed
};
using Graph = adjacency_list<vecS, vecS, directedS, no_property, propretyEdge>;
void Dijkstra(std::string const& date, Graph const& g, int vertex_origin_num_l = 0) {
auto dated_weight_f = [&](Graph::edge_descriptor ed) {
return g[ed].weights.at(date);
};
auto dated_weight_map = make_function_property_map<Graph::edge_descriptor, double>(dated_weight_f);
std::vector<Graph::vertex_descriptor> p(num_vertices(g));
std::vector<double> d(num_vertices(g));
std::vector<default_color_type> color_map(num_vertices(g));
boost::typed_identity_property_map<Graph::vertex_descriptor> vid; // T* property maps were deprecated
dijkstra_shortest_paths(g, vertex_origin_num_l,
weight_map(dated_weight_map).
predecessor_map(make_iterator_property_map(p.data(), vid)).
distance_map(make_iterator_property_map(d.data(), vid)).
color_map(make_iterator_property_map(color_map.data(), vid))
);
std::cout << "distances and parents for '" + date + "':" << std::endl;
for (auto vd : make_iterator_range(vertices(g)))
{
std::cout << "distance(" << vd << ") = " << d[vd] << ", ";
std::cout << "parent(" << vd << ") = " << p[vd] << std::endl;
}
std::cout << std::endl;
std::ofstream dot_file("dijkstra-eg-" + date + ".dot");
dot_file << "digraph D {\n"
" rankdir=LR\n"
" size=\"6,4\"\n"
" ratio=\"fill\"\n"
" graph[label=\"shortest path on " + date + "\"];\n"
" edge[style=\"bold\"]\n"
" node[shape=\"circle\"]\n";
for (auto ed : make_iterator_range(edges(g))) {
auto u = source(ed, g),
v = target(ed, g);
dot_file
<< u << " -> " << v << "[label=\"" << get(dated_weight_map, ed) << "\""
<< (p[v] == u?", color=\"black\"" : ", color=\"grey\"")
<< "]";
}
dot_file << "}";
}
int main() {
Graph g;
std::mt19937 prng { std::random_device{}() };
generate_random_graph(g, 8, 12, prng);
uniform_real<double> weight_dist(10,42);
for (auto e : make_iterator_range(edges(g)))
for (auto&& date : { "2014-01-01", "2014-02-01", "2014-03-01" })
g[e].weights[date] = weight_dist(prng);
for (std::string const& date : { "2014-01-01", "2014-02-01", "2014-03-01" }) {
Dijkstra(date, g, 0);
}
}
Output, e.g.
¹ As long as you keep the invariants required by the algorithm you're invoking. In particular, you must return the same weight consistently during the execution, given the same edge. Also, some algorithms don't support negative weight etc.
² I'd highly suggest using a Boost ICL interval_map in such a case but I digress
³ see also map set/get requests into C++ class/structure changes

What is a property map in BOOST?

Can someone explain to a Boost beginner like me what is a property map is in Boost?
I came across this when trying to use the BGL for calculating strong connected components.
I went through the documentation for the property map and graph module and still don't know what to make of it.
Take this code, for example:
what is the make_iterator_property_map function doing?
and what is the meaning of this code: get(vertex_index, G)?
#include <boost/config.hpp>
#include <vector>
#include <iostream>
#include <boost/graph/strong_components.hpp>
#include <boost/graph/adjacency_list.hpp>
int main()
{
using namespace boost;
typedef adjacency_list < vecS, vecS, directedS > Graph;
const int N = 6;
Graph G(N);
add_edge(0, 1, G);
add_edge(1, 1, G);
add_edge(1, 3, G);
add_edge(1, 4, G);
add_edge(3, 4, G);
add_edge(3, 0, G);
add_edge(4, 3, G);
add_edge(5, 2, G);
std::vector<int> c(N);
int num = strong_components
(G, make_iterator_property_map(c.begin(), get(vertex_index, G), c[0]));
std::cout << "Total number of components: " << num << std::endl;
std::vector < int >::iterator i;
for (i = c.begin(); i != c.end(); ++i)
std::cout << "Vertex " << i - c.begin()
<< " is in component " << *i << std::endl;
return EXIT_SUCCESS;
}
PropertyMaps at their core are an abstraction of data access. A problem that comes up very quickly in generic programming is: How do I get data associated with some object? It could be stored in the object itself, the object could be a pointer, it could be outside of the object in some mapping structure.
You can of course encapsulate data-access in a functor, but that becomes tedious very quickly and you look for a more narrow solution, the one chosen in Boost are PropertyMaps.
Remember this is just the concept. Concrete instances are for example an std::map (with some syntactic adaption), a function returning a member of the key (again, with some syntactic adaption).
Towards your edit: make_iterator_property_map builds an iterator_property_map. The first argument provides an iterator for a basis of offset calculations. The second argument is again a property_map to do the offset calculation. Together this provides a way to use an vertex_descriptor to write data to the vector based on the index of the vertex_descriptor.

Removing edges temporarily from a boost graph

I have written an algorithm which does (some sort of) 'topological sorting' (not exact). This algorithm copies the given graph and then manipulates the copy (by removing edges). On a million node boost graph, if my algorithm takes 3.1 seconds, 2.19 seconds are consumed by copying the given graph into a new one.
Can I remove edges without actually removing them permanently e.g. sort of masking in boost::graph library? And when algorithm is done, I unmask all edges the graph regains it original state. I suspect this should make my algorithm run much faster.
Boost.Graph's filtered_graph seems a good fit for what you want. Unfortunately I really have no idea if it will perform better than your current approach (I suspect it will). If you decide to implement this approach I would love to hear about the results.
Example on LWS.
#include <iostream>
#include <tuple>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/unordered_set.hpp>
struct Vertex
{
Vertex(){}
Vertex(int val):name(val){}
int name;
};
typedef boost::adjacency_list<boost::vecS,boost::vecS,boost::directedS,Vertex> graph_type;
typedef boost::graph_traits<graph_type>::vertex_descriptor vertex_descriptor;
typedef boost::graph_traits<graph_type>::edge_descriptor edge_descriptor;
// A hash function for edges.
struct edge_hash:std::unary_function<edge_descriptor, std::size_t>
{
edge_hash(graph_type const& g):g(g){}
std::size_t operator()(edge_descriptor const& e) const {
std::size_t seed = 0;
boost::hash_combine(seed, source(e,g));
boost::hash_combine(seed, target(e,g));
//if you don't use vecS as your VertexList container
//you will need to create and initialize a vertex_index property and then use:
//boost::hash_combine(seed,get(boost::vertex_index, g, source(e,g)));
//boost::hash_combine(seed,get(boost::vertex_index, g, target(e,g)));
return seed;
}
graph_type const& g;
};
typedef boost::unordered_set<edge_descriptor, edge_hash> edge_set;
typedef boost::filtered_graph<graph_type,boost::is_not_in_subset<edge_set> > filtered_graph_type;
template <typename Graph>
void print_topological_order(Graph const& g)
{
std::vector<vertex_descriptor> output;
topological_sort(g,std::back_inserter(output));
std::vector<vertex_descriptor>::reverse_iterator iter=output.rbegin(),end=output.rend();
for(;iter!=end;++iter)
std::cout << g[*iter].name << " ";
std::cout << std::endl;
}
int main()
{
graph_type g;
//BUILD THE GRAPH
vertex_descriptor v0 = add_vertex(0,g);
vertex_descriptor v1 = add_vertex(1,g);
vertex_descriptor v2 = add_vertex(2,g);
vertex_descriptor v3 = add_vertex(3,g);
vertex_descriptor v4 = add_vertex(4,g);
vertex_descriptor v5 = add_vertex(5,g);
edge_descriptor e4,e5;
add_edge(v0,v1,g);
add_edge(v0,v3,g);
add_edge(v2,v4,g);
add_edge(v1,v4,g);
std::tie(e4,std::ignore) = add_edge(v4,v3,g);
std::tie(e5,std::ignore) = add_edge(v2,v5,g);
//GRAPH BUILT
std::cout << "Original graph:" << std::endl;
print_topological_order(g);
edge_hash hasher(g);
edge_set removed(0,hasher); //need to pass "hasher" in the constructor since it is not default constructible
filtered_graph_type fg(g,removed); //creates the filtered graph
removed.insert(e4); //you can "remove" edges from the graph by adding them to this set
removed.insert(e5);
std::cout << "Filtered Graph after \"removing\" 2 edges" << std::endl;
print_topological_order(fg);
removed.clear(); //clearing the set restores your original graph
std::cout << "Filtered Graph after resetting" << std::endl;
print_topological_order(fg);
}