can boost vf2 deal with muti-graph like this situation? - c++

I want to use vf2 in this situation.
Graph gsmall,glarge;
add_vertex(vertex_prop('a'),gsmall);
add_vertex(vertex_prop('b'),gsmall);
add_edge(0, 1, edge_prop('m'), gsmall);
add_vertex(vertex_prop('a'),glarge);
add_vertex(vertex_prop('b'),glarge);
add_edge(0, 1, edge_prop('m'), glarge);
add_edge(0, 1, edge_prop('n'), glarge);
std::cout << is_subgraph_isomorphic(gsmall,glarge) << std::endl;
If the pattern's property of edge can match with graph's part of properties of edge, then return true, but now it must match all. That example returns false. I want to make it true, so how?
Edit:
I solved this question. Use vector and overload operator "=="
http://coliru.stacked-crooked.com/a/6307210b2861bc63
But I found another problem. It will give wrong results when there is self-loops in graph.
http://coliru.stacked-crooked.com/a/46d336ecfddbbab9 is true
but http://coliru.stacked-crooked.com/a/413d56146ceffd42 is false.
I think they are both ture. I can't understand how it could be like this.
Please help me again! Thanks!

Boost can deal with it. However, you're not looking for a isomorphism in the sense of the library:
An isomorphism between two graphs G1=(V1, E1) and G2=(V2, E2) is a bijective mapping M of the vertices of one graph to vertices of the other graph that preserves the edge structure of the graphs
So, for all corresponding vertices, the same edges need to be present. In other words, the subgraph may be smaller (lower order) but each vertex must have equivalent structure (this implies the same number of edges).
In your case the small graph is structurally different because the large graph has a self loop, but the small doesn't. (The self loop is significant because both vertices exist in the subgraph).
If you really think for your purpose you need to ignore self loops, you'll have to filter them out.
Here's an example that employs the filtered_graph adaptor to achieve that:
Live On Coliru
#include <vector>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/vf2_sub_graph_iso.hpp>
#include <boost/graph/filtered_graph.hpp>
#include <boost/container/flat_set.hpp>
#include <boost/container/small_vector.hpp>
template <typename SortedRange1, typename SortedRange2,
typename V = std::common_type_t<typename boost::range_value<SortedRange1>::type, typename boost::range_value<SortedRange2>::type>,
typename Cmp = std::less<V> >
static inline bool has_intersection(SortedRange1 const& a, SortedRange2 const& b, Cmp cmp = {}) {
auto equivalent = [cmp](V const& a, V const& b)
{ return !cmp(a,b) && !cmp(b,a); };
auto ai = a.begin();
auto bi = b.begin();
while (ai != a.end() && (bi = b.lower_bound(*ai)) != b.end())
if (equivalent(*ai++, *bi))
return true;
return false;
}
// Define graph type
using Label = char;
struct EdgeProperties {
using Labels = boost::container::flat_set<char, std::less<>, boost::container::small_vector<char, 3> >;
EdgeProperties(std::initializer_list<Label> elabels = {}) :_elabels(elabels) {}
bool operator==(EdgeProperties const& other) const {
return has_intersection(_elabels, other._elabels);
}
Labels _elabels;
};
typedef boost::property<boost::edge_name_t, EdgeProperties> edge_prop;
typedef boost::property<boost::vertex_name_t, long/*, boost::property<boost::vertex_index_t, int>*/ > vertex_prop;
typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::bidirectionalS, vertex_prop, edge_prop> Graph;
int main()
{
Graph gsmall, glarge;
add_vertex(vertex_prop('a'),gsmall);
add_vertex(vertex_prop('b'),gsmall);
add_edge(0, 1, edge_prop({'m'}), gsmall);
//add_edge(0, 0, edge_prop({'n'}), gsmall);
add_vertex(vertex_prop('a'),glarge);
add_vertex(vertex_prop('b'),glarge);
add_vertex(vertex_prop('c'),glarge);
add_edge(0, 1, edge_prop({'m'}), glarge);
add_edge(0, 0, edge_prop({'n'}), glarge);
add_edge(0, 2, edge_prop({'o'}), glarge);
// Create predicate of edge
auto edge_comp = make_property_map_equivalent(
get(boost::edge_name, gsmall),
get(boost::edge_name, glarge));
// Create callback
boost::vf2_print_callback<Graph, Graph> callback(gsmall, glarge);
struct FilterSelfEdges {
Graph const* _g;
bool operator()(Graph::edge_descriptor ed) const {
return source(ed, *_g) != target(ed, *_g);
}
};
using Filtered = boost::filtered_graph<Graph, FilterSelfEdges>;
// Execute
const bool result = boost::vf2_subgraph_iso(
gsmall, Filtered(glarge, FilterSelfEdges{&glarge}), callback, boost::vertex_order_by_mult(gsmall),
boost::edges_equivalent(edge_comp));
std::cout << "subgraph isomorphic? " << std::boolalpha << result << std::endl;
}
Prints
(0, 0) (1, 1)
subgraph isomorphic? true

Related

How to create a boost property map between rdkit molecular bond graph edges and BGL biconnected component identifiers?

The rdkit library provides a molecule class ROMol that provides a member function getTopology that returns a BGL graph of type adjacency_list<vecS, vecS, undirectedS, Atom *, Bond *>. I understand that the rdkit type Bond defines the edge properties of the graph. I know that Bond provides a member function getIdx that returns a unique integer identifying the bond, so the graph must have a notion of edge numbering.
To make use of the BGL algorithm for biconnected_components one requires a component property map that maps objects of the edge descriptor type of the graph (I understand this type is Bond?) to component identifying integers. As rdkit does not deal in biconnected components, I conclude this property map must be exterior to the graph. In this case, the BGL documentation suggests to use the iterator_property_map adaptor class (see section "Exterior Properties"). I struggle to determine the correct template arguments for iterator_property_map to get the required property map. If this is the correct approach what are the missing template arguments?
Unfortunately I did not get very far with my code before getting lost in the BGL documentation:
void my_function(const RDKit::ROMol& molecule) {
const RDKit::MolGraph& molecule_graph{molecule.getTopology()};
using EdgesSize =
boost::graph_traits<RDKit::MolGraph>::edges_size_type; // map value type
using Edge =
boost::graph_traits<RDKit::MolGraph>::edge_descriptor; // map key type
std::vector<EdgesSize> component_map(boost::num_edges(molecule_graph)
); // range, begin() yields random access iterator
// boost::iterator_property_map<?>;
// boost::biconnected_components(molecule_graph, ?);
}
one requires a component property map that maps objects of the edge descriptor type of the graph (I understand this type is Bond?)
Edge descriptors are internal descriptors, sort of like stable iterators. E.g.
edge_descriptor e = *edges(g).begin();
Edge properties are entirely separate notions. You get the edge properties from an edge descriptor like so:
Bond* e_props = g[e]; // equivalent to:
e_props = get(boost::edge_bundle, g, e);
Unsurprisingly the same goes for vertex descriptors vs. properties:
Atom* first_a_prop = g[vertex(0, g)];
The Complication
A notable difference between the two descriptor types is that - only because the graph type is using vecS as the vertex container selector - the vertex descriptor is guaranteed to be integral, where edge descriptor is opaque (similar to a void*).
Hence, the edge-decriptor cannot be the key type to a vector-based property map (because it would require an integral indexer).
Instead make an associative property-map:
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
Sample
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <iostream>
struct Atom {
};
struct Bond {
int idx_ = 42;
int getIdx() const { return idx_; }
};
using G = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Atom*, Bond*>;
using vertex_descriptor = G::vertex_descriptor;
using edge_descriptor = G::edge_descriptor;
int main() {
std::array<Atom, 4> atoms;
std::array<Bond, 2> bonds{Bond{111}, Bond{222}};
G g;
add_vertex(atoms.data()+0, g);
add_vertex(atoms.data()+1, g);
add_vertex(atoms.data()+2, g);
add_vertex(atoms.data()+3, g);
// using the fact that vertex_descriptor is 0..3 here:
add_edge(0, 2, bonds.data()+0, g);
add_edge(2, 3, bonds.data()+1, g);
{
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
// Now use it:
size_t n = boost::biconnected_components(g, c);
for (auto [e, component_id] : component_map) {
std::cout << "Edge " << e << " (idx:" << g[e]->getIdx()
<< ") mapped to component " << component_id << " out of "
<< n << "\n";
}
}
{
std::map<edge_descriptor, int> component_map;
auto c = boost::make_assoc_property_map(component_map);
// also writing articulation points:
[[maybe_unused]] auto [n, out] = boost::biconnected_components(
g, c,
std::ostream_iterator<vertex_descriptor>(
std::cout << "articulation points: ", " "));
std::cout << "\n";
}
}
Prints
Edge (0,2) (idx:111) mapped to component 1 out of 2
Edge (2,3) (idx:222) mapped to component 0 out of 2
articulation points: 2
Advanced Example
You can force a vector as mapping storage, but that requires you to map the edges (bonds) to a contiguous integral range [0..num_edges(g)).
I couldn't assume that getIdx() satisfies the criterion, but if it did:
// first map edges to 0..num_edges using getIdx
auto edge_index = boost::make_function_property_map<edge_descriptor>(
[&g](edge_descriptor e) { return g[e]->getIdx(); });
// provide num_edges storage for component-ids:
std::vector<int> component_ids(num_edges(g));
// project the vector through edge_index to make a Writable Property
// Map indexed by edge_descriptor;
auto c = boost::make_safe_iterator_property_map(component_ids.begin(),
component_ids.size(), edge_index);
Let's apply it to the graph from the documentation:
Live On Coliru
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/biconnected_components.hpp>
#include <boost/property_map/function_property_map.hpp>
#include <iostream>
enum letter : char { A, B, C, D, E, F, G, H, I };
struct Atom {
Atom(letter) {}
};
struct Bond {
int idx_;
int getIdx() const { return idx_; }
};
using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS, Atom*, Bond*>;
using vertex_descriptor = Graph::vertex_descriptor;
using edge_descriptor = Graph::edge_descriptor;
int main() {
std::array<Atom, 9> atoms{A, B, C, D, E, F, G, H, I};
std::array<Bond, 11> bonds{
{{0}, {1}, {2}, {3}, {4}, {5}, {6}, {7}, {8}, {9}, {10}}};
Graph g;
for (auto& atom : atoms)
add_vertex(&atom, g);
// using the fact that vertex_descriptor is vertex index:
add_edge(A, B, &bonds.at(0), g);
add_edge(A, F, &bonds.at(1), g);
add_edge(A, G, &bonds.at(2), g);
add_edge(B, C, &bonds.at(3), g);
add_edge(B, D, &bonds.at(4), g);
add_edge(B, E, &bonds.at(5), g);
add_edge(C, D, &bonds.at(6), g);
add_edge(E, F, &bonds.at(7), g);
add_edge(G, H, &bonds.at(8), g);
add_edge(G, I, &bonds.at(9), g);
add_edge(H, I, &bonds.at(10), g);
// OUT: ComponentMap c
// must be a model of Writable Property Map. The value type should be
// an integer type, preferably the same as the edges_size_type of the
// graph. The key type must be the graph's edge descriptor type.
// first map edges to 0..10 using getIdx
auto edge_index = boost::make_function_property_map<edge_descriptor>(
[&g](edge_descriptor e) { return g[e]->getIdx(); });
// provide num_edges storage for component-ids:
std::vector<int> component_ids(num_edges(g));
// project the vector through edge_index to make a Writable Property
// Map indexed by edge_descriptor;
auto c = boost::make_safe_iterator_property_map(component_ids.begin(),
component_ids.size(), edge_index);
// Now use it:
size_t n = boost::biconnected_components(g, c);
for (auto e : boost::make_iterator_range(edges(g))) {
// edge_index or getIdx, equivalent here:
assert(edge_index[e] == g[e]->getIdx());
auto idx =edge_index[e];
auto cid = component_ids.at(idx);
std::cout << "Edge " << e << " (idx:" << idx << ") mapped to component "
<< cid << " out of " << n << "\n";
}
}
Which prints prints the expected mapping
Edge (0,1) (idx:0) mapped to component 1 out of 4
Edge (0,5) (idx:1) mapped to component 1 out of 4
Edge (0,6) (idx:2) mapped to component 3 out of 4
Edge (1,2) (idx:3) mapped to component 0 out of 4
Edge (1,3) (idx:4) mapped to component 0 out of 4
Edge (1,4) (idx:5) mapped to component 1 out of 4
Edge (2,3) (idx:6) mapped to component 0 out of 4
Edge (4,5) (idx:7) mapped to component 1 out of 4
Edge (6,7) (idx:8) mapped to component 2 out of 4
Edge (6,8) (idx:9) mapped to component 2 out of 4
Edge (7,8) (idx:10) mapped to component 2 out of 4
In fact, if we add a little bit of bonus wizardry, we can render that:

Boost graph max flow algorithm -- question about role of reverse arcs if they are already present in the original input graph

Consider the following original input graph on which the max flow algorithm is applied:
The following code (thanks to user sehe) compiles and runs fine and gives the correct output. Code is reproduced below for completion:
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/boykov_kolmogorov_max_flow.hpp>
#include <boost/range/adaptors.hpp>
#include <fmt/ostream.h>
#include <fmt/ranges.h>
using boost::adaptors::filtered;
using Traits = boost::adjacency_list_traits<boost::vecS, boost::vecS, boost::directedS>;
using V = Traits::vertex_descriptor;
using E = Traits::edge_descriptor;
using Capacity = double;
using Color = boost::default_color_type;
struct VertexProps {
// std::string name;
Color color;
Capacity distance;
E preedcessor;
};
struct EdgeProps {
int id;
Capacity weight, residual;
E reverse;
};
using Graph = boost::adjacency_list<
boost::vecS, boost::vecS, boost::directedS,
VertexProps,
// see https://stackoverflow.com/a/64744086/85371 :(
boost::property<boost::edge_capacity_t, Capacity, EdgeProps>>;
struct MyGraph {
MyGraph(size_t nnodes) : _g(nnodes) {}
void runSimulation(std::vector<std::pair<V, V>> const& arcs,
std::vector<Capacity> const& capacities)
{
reconfigure(arcs, capacities);
Capacity maxflow = solve_max_flow(0, 3);
auto cap = get(boost::edge_capacity, _g);
auto is_source = [this](V v) { return _g[v].color == Color::black_color; };
fmt::print("Max flow {}\nNodes {} are in source subset\n", maxflow,
vertices(_g) | filtered(is_source));
for (E e : boost::make_iterator_range(edges(_g))) {
bool st_cut =
is_source(source(e, _g)) and
not is_source(target(e, _g));
fmt::print("Edge {} (id #{:2}), capacity {:3} {}\n", e, _g[e].id,
cap[e], st_cut ? "(ST Cut)" : "");
}
}
private:
Graph _g;
void reconfigure(auto const& arcs, auto const& capacities)
{
assert(arcs.size() == capacities.size());
for (auto v : boost::make_iterator_range(vertices(_g))) {
// boost::clear_out_edges(v, g);
boost::clear_vertex(v, _g);
}
auto cap = get(boost::edge_capacity, _g);
auto eidx = get(&EdgeProps::id, _g);
auto rev = get(&EdgeProps::reverse, _g);
auto eindex = 0;
for (auto [fr, to] : arcs) {
auto edf = add_edge(fr, to, _g).first;
auto edr = add_edge(to, fr, _g).first;
eidx[edf] = 2 * eindex;
eidx[edr] = eidx[edf] + 1;
cap[edf] = cap[edr] = capacities[eindex];
rev[edf] = edr;
rev[edr] = edf;
++eindex;
}
}
Capacity solve_max_flow(V src, V sink)
{
return boykov_kolmogorov_max_flow(
_g, src, sink,
// named arguments
boost::reverse_edge_map(get(&EdgeProps::reverse, _g))
.residual_capacity_map(get(&EdgeProps::residual, _g))
.vertex_color_map(get(&VertexProps::color, _g))
.predecessor_map(get(&VertexProps::preedcessor, _g))
.distance_map(get(&VertexProps::distance, _g))
// end named arguments
);
}
};
int main() {
MyGraph g{4};
g.runSimulation({{0, 1}, {0, 2}, {1, 2}, {2, 1}, {1, 3}, {2, 3}},
{10, 1, 10, 1, 1, 10});
}
My questions relate particularly to arcs (1->2) and (2->1).
(a) Boost documentation for the max flow algorithm requires the user to explicitly provide the reverse arcs for each of the arcs in the original input graph. So, in the example above, the directed arcs (1 -> 2) and (2 -> 1) are added to the graph object twice in the following code snippet:
auto edf = add_edge(fr, to, _g).first; //Arc (1->2) added, Arc (2->1) added
auto edr = add_edge(to, fr, _g).first; //Arc (2->1) added, Arc (1->2) added
eidx[edf] = 2 * eindex;
eidx[edr] = eidx[edf] + 1;
cap[edf] = cap[edr] = capacities[eindex];
rev[edf] = edr;
rev[edr] = edf;
While in this particular example the correctness of the solution could be seen, is this guaranteed in all cases? That is, can the repeated arc addition of the same arc multiple times (once as the forward arc, and once as the reverse arc) to the graph object cause any internal stuff to break in the boost algorithm for this problem?
(b) The boost documentation states the following:
Remarks: While the push-relabel method states that each edge in E^T has
to have capacity of 0, the reverse edges for this algorithm ARE
allowed to carry capacities. If there are already reverse edges in the
input Graph G, those can be used. This can halve the amount of edges
and will noticeably increase the performance.
Does this mean that in this particular case, when I add the forward arc (1->2), I need NOT explicitly add the reverse arc (2->1) and likewise when I add the forward arc (2->1), I need NOT explicitly add the reverse arc (1->2) as happened in the code snippet above?

How to use boost make_label_writer to write edge properties?

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

in boost graph lib, how do I get a specific out-edge of a vertex without iterating over all the out-edges of that vertex?

Let's say I have a graph, with edges each containing a char. From a vertex, I want to get a specific out-edge with a specific char. Since the edge container can be set to a set or a hash-set, I assume there is a way to do this without iterating through the vertex's out-edges. I'm also assuming/hoping the edge container is keyed on the type the edge contains.
#include <boost/graph/adjacency_list.hpp>
using namespace boost;
typedef boost::adjacency_list<setS, vecS, directedS, std::string, char> MyGraph;
typedef boost::graph_traits<MyGraph>::vertex_descriptor Vertex;
typedef boost::graph_traits<MyGraph>::edge_descriptor Edge;
MyGraph g;
//setup
add_vertex(std::string("xxx"), g);
Vertex currentVertex = g.vertex_set()[0];
Vertex endVertex = add_vertex(std::string("yyy"), g);
add_edge(currentVertex, endVertex, 'i', g);
//later...
//Now I want that edge containing the letter 'i'.
//out_edges returns a pair of edge iterators.
std::pair<iterator, iterator> iterators = out_edges(currentVertex, g); // do not want!
Edge iEdge = how_do_I_get_This?(currentVertex, g); // want!
Is there a way to do this, or is iterating through the out-edges the only option?
update:
I think this will get me the container.
std::set<?> edges = g.out_edge_list(currentVertex);
Now I cannot figure out what the ? template type is.
update2:
This seems to compile, but I need an edge_descriptor, not an edge_property to pass to target.
std::set<boost::detail::stored_edge_property<long unsigned int, char> > edges = fGraph.out_edge_list(currentVertex);
update3:
Guess I don't need an edge descriptor. Got what I needed like this:
std::set<boost::detail::stored_edge_property<long unsigned int, char> > edges = fGraph.out_edge_list(currentVertex);
std::_Rb_tree_const_iterator<boost::detail::stored_edge_property<long unsigned int, char> > edge = edges.find(*i);
Vertex target = edge.get_target();
This all compiles and seems to work, but it is massively ugly.
Are you looking for how to use edge descriptors?
Edge i_edge = add_edge(currentVertex, endVertex, 'i', g).first;
i_edge is the vertex-descriptor for the 'i' edge.
// later...
// Now I want that edge containing the letter 'i'.
char yougotit = g[i_edge];
Check it:
assert('i' == yougotit);
See it Live On Coliru
If you really want to search, and can use c++1y you might find this elegant: Also Live
#include <boost/graph/adjacency_list.hpp>
#include <boost/range/algorithm.hpp>
#include <boost/range/adaptors.hpp>
#include <iostream>
using namespace boost::adaptors;
using namespace boost;
typedef boost::adjacency_list<setS, vecS, directedS, std::string, char> MyGraph;
typedef boost::graph_traits<MyGraph>::vertex_descriptor Vertex;
typedef boost::graph_traits<MyGraph>::edge_descriptor Edge;
int main() {
MyGraph g;
// setup
add_vertex(std::string("xxx"), g);
Vertex currentVertex = g.vertex_set()[0];
Vertex endVertex = add_vertex(std::string("yyy"), g);
add_edge(currentVertex, endVertex, 'i', g);
for (auto matching : boost::edges(g) | filtered([&g](auto const& e) { return g[e] == 'i'; }))
std::cout << matching << " --> " << g[matching] << "\n";
}
Output:
(0,1) --> i

Limited depth search in BGL without O(number_of_vertices) used memory or time?

Is it possible to do a depth or breadth first search/visit up to some distance from a vertex in BGL without accessing, filtering, indexing, etc. all vertices in the graph?
The closest thing I've managed to write is (creates the graph 0<->1<->2<->3<->4<->5 but only visits vertices 0 to 3):
#include <iostream>
#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/depth_first_search.hpp>
using namespace std;
struct custom_dfs_visitor : public boost::default_dfs_visitor {
template < typename Vertex, typename Graph >
void discover_vertex(const Vertex& v, const Graph& g) const {
std::cout << v << std::endl;
}
};
struct Terminator {
template<class Vertex, class Graph>
bool operator()(const Vertex& v, const Graph& g) {
return v > 2;
}
};
int main()
{
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::undirectedS
> Graph_T;
Graph_T g(6);
boost::add_edge(0, 1, g);
boost::add_edge(1, 2, g);
boost::add_edge(2, 3, g);
boost::add_edge(3, 4, g);
boost::add_edge(4, 5, g);
std::vector<boost::default_color_type> color_map(boost::num_vertices(g));
boost::depth_first_visit(
g,
boost::vertex(0, g),
custom_dfs_visitor(),
boost::make_iterator_property_map(
color_map.begin(),
boost::get(boost::vertex_index, g),
color_map[0]
),
Terminator()
);
return 0;
}
which does only print 0 1 2 3 instead of visiting all vertices, but the code still requires a color map as large as the whole graph (boost::num_vertices(g)). Is there a way to make the search complexity not be at all comparable to the total number of edges/vertices in the graph?
Using a bundled color would be acceptable because many searches would be done in different parts of the graph, but is it possible to reduce the complexity of each individual search in the same graph from O(number_of_vertices)?
Initial coloring of vertices will hopefully also stop when Terminator returns true but that seems to be taken care of already.
Maybe a related question: What about indexing if the graph uses something else than vecS? Can BFS/DFS do without indexing in that case?
Thanks for any help.
Turns out using bundled properties is the easiest way to accomplish this. The fact that the color property is included in every vertex is better than creating the color property for each vertex every time a dfs is done. The graph type should be
typedef boost::adjacency_list<
boost::vecS,
boost::vecS,
boost::undirectedS,
property<vertex_color_t, boost::default_color_type>
> Graph_T;
and the call to dfv is
depth_first_visit(
g,
vertex(0, g),
custom_dfs_visitor(),
get(vertex_color_t(), g),
Terminator()
);
With the above, doing a limited dfs in a graph with 100 M vertices doesn't increase memory consumption (76.2 % of total memory) while with an external vector of colors memory usage increases from 76.2 % to 78.5 % while searching.