Commit fb37166a authored by Tiago Peixoto's avatar Tiago Peixoto

Add support for iterating over all vertices in {dfs,bfs,dijkstra}_{search,iterator}()

parent 5752f5eb
Pipeline #262 passed with stage
in 184 minutes and 22 seconds
...@@ -576,4 +576,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core) ...@@ -576,4 +576,7 @@ BOOST_PYTHON_MODULE(libgraph_tool_core)
.add_property("gcc_version", &LibInfo::GetGCCVersion); .add_property("gcc_version", &LibInfo::GetGCCVersion);
def("get_graph_type", &get_graph_type); def("get_graph_type", &get_graph_type);
def("get_null_vertex",
+[](){ return graph_traits<GraphInterface::multigraph_t>::null_vertex();});
} }
...@@ -106,20 +106,28 @@ private: ...@@ -106,20 +106,28 @@ private:
boost::python::object _vis; boost::python::object _vis;
}; };
struct do_bfs
{
template <class Graph, class Visitor>
void operator()(Graph& g, size_t s, Visitor vis) const
{
breadth_first_search(g, vertex(s, g), visitor(vis));
}
};
void bfs_search(GraphInterface& g, size_t s, python::object vis) void bfs_search(GraphInterface& gi, size_t s, python::object vis)
{ {
run_action<graph_tool::all_graph_views,mpl::true_>() run_action<graph_tool::all_graph_views,mpl::true_>()
(g, std::bind(do_bfs(), std::placeholders::_1, s, (gi,
BFSVisitorWrapper(g, vis)))(); [&](auto &g)
{
typedef typename std::remove_reference<decltype(g)>::type g_t;
typename vprop_map_t<default_color_type>::type
color(get(vertex_index_t(), g));
auto visw = BFSVisitorWrapper(gi, vis);
auto v = vertex(s, g);
if (v == graph_traits<g_t>::null_vertex())
{
for (auto u : vertices_range(g))
breadth_first_search(g, u, visitor(visw).color_map(color));
}
else
{
breadth_first_visit(g, v, visitor(visw).color_map(color));
}
})();
} }
#ifdef HAVE_BOOST_COROUTINE #ifdef HAVE_BOOST_COROUTINE
...@@ -152,7 +160,28 @@ boost::python::object bfs_search_generator(GraphInterface& g, size_t s) ...@@ -152,7 +160,28 @@ boost::python::object bfs_search_generator(GraphInterface& g, size_t s)
{ {
BFSGeneratorVisitor vis(g, yield); BFSGeneratorVisitor vis(g, yield);
run_action<graph_tool::all_graph_views,mpl::true_>() run_action<graph_tool::all_graph_views,mpl::true_>()
(g, std::bind(do_bfs(), std::placeholders::_1, s, vis))(); (g,
[&](auto &g)
{
typedef typename std::remove_reference<decltype(g)>::type g_t;
typename vprop_map_t<default_color_type>::type
color(get(vertex_index_t(), g));
auto v = vertex(s, g);
if (v == graph_traits<g_t>::null_vertex())
{
for (auto u : vertices_range(g))
{
if (color[u] == color_traits<default_color_type>::black())
continue;
breadth_first_visit(g, u, visitor(vis).color_map(color));
}
}
else
{
breadth_first_visit(g, v, visitor(vis).color_map(color));
}
})();
}; };
return boost::python::object(CoroGenerator(dispatch)); return boost::python::object(CoroGenerator(dispatch));
#else #else
......
...@@ -90,25 +90,22 @@ private: ...@@ -90,25 +90,22 @@ private:
python::object _vis; python::object _vis;
}; };
struct do_dfs void dfs_search(GraphInterface& gi, size_t s, python::object vis)
{ {
template <class Graph, class VertexIndexMap, class Visitor> run_action<graph_tool::all_graph_views, mpl::true_>()
void operator()(Graph& g, VertexIndexMap vertex_index, size_t s, (gi,
Visitor vis) const [&](auto &g)
{ {
typename property_map_type::apply<default_color_type, typedef typename std::remove_reference<decltype(g)>::type g_t;
VertexIndexMap>::type typename vprop_map_t<default_color_type>::type
color(vertex_index); color(get(vertex_index_t(), g));
depth_first_visit(g, vertex(s, g), vis, color); auto visw = DFSVisitorWrapper(gi, vis);
} auto v = vertex(s, g);
}; if (v == graph_traits<g_t>::null_vertex())
depth_first_search(g, visw, color);
else
void dfs_search(GraphInterface& g, size_t s, python::object vis) depth_first_visit(g, v, visw, color);
{ })();
run_action<graph_tool::all_graph_views,mpl::true_>()
(g, std::bind(do_dfs(), std::placeholders::_1, g.get_vertex_index(),
s, DFSVisitorWrapper(g, vis)))();
} }
#ifdef HAVE_BOOST_COROUTINE #ifdef HAVE_BOOST_COROUTINE
...@@ -142,8 +139,18 @@ boost::python::object dfs_search_generator(GraphInterface& g, size_t s) ...@@ -142,8 +139,18 @@ boost::python::object dfs_search_generator(GraphInterface& g, size_t s)
{ {
DFSGeneratorVisitor vis(g, yield); DFSGeneratorVisitor vis(g, yield);
run_action<graph_tool::all_graph_views,mpl::true_>() run_action<graph_tool::all_graph_views,mpl::true_>()
(g, std::bind(do_dfs(), std::placeholders::_1, (g,
g.get_vertex_index(), s, vis))(); [&](auto &g)
{
typedef typename std::remove_reference<decltype(g)>::type g_t;
typename vprop_map_t<default_color_type>::type
color(get(vertex_index_t(), g));
auto v = vertex(s, g);
if (v == graph_traits<g_t>::null_vertex())
depth_first_search(g, vis, color);
else
depth_first_visit(g, v, vis, color);
})();
}; };
return boost::python::object(CoroGenerator(dispatch)); return boost::python::object(CoroGenerator(dispatch));
#else #else
......
...@@ -140,11 +140,33 @@ struct do_djk_search ...@@ -140,11 +140,33 @@ struct do_djk_search
typedef typename graph_traits<Graph>::edge_descriptor edge_t; typedef typename graph_traits<Graph>::edge_descriptor edge_t;
DynamicPropertyMapWrap<dtype_t, edge_t> weight(aweight, DynamicPropertyMapWrap<dtype_t, edge_t> weight(aweight,
edge_properties()); edge_properties());
dijkstra_shortest_paths_no_color_map
(g, vertex(s, g), visitor(vis).weight_map(weight). if (vertex(s, g) == graph_traits<Graph>::null_vertex())
predecessor_map(pred_map). {
distance_map(dist).distance_compare(cmp). for (auto u : vertices_range(g))
distance_combine(cmb).distance_inf(i).distance_zero(z)); {
vis.initialize_vertex(u, g);
put(dist, u, i);
put(pred_map, u, u);
}
for (auto u : vertices_range(g))
{
if (dist[u] != i)
continue;
dist[u] = z;
dijkstra_shortest_paths_no_color_map_no_init
(g, u, pred_map, dist, weight, get(vertex_index_t(), g),
cmp, cmb, i, z, vis);
}
}
else
{
dijkstra_shortest_paths_no_color_map
(g, vertex(s, g), visitor(vis).weight_map(weight).
predecessor_map(pred_map).
distance_map(dist).distance_compare(cmp).
distance_combine(cmb).distance_inf(i).distance_zero(z));
}
} }
}; };
...@@ -158,9 +180,31 @@ struct do_djk_search_fast ...@@ -158,9 +180,31 @@ struct do_djk_search_fast
typedef typename property_traits<DistanceMap>::value_type dtype_t; typedef typename property_traits<DistanceMap>::value_type dtype_t;
dtype_t z = python::extract<dtype_t>(range.first); dtype_t z = python::extract<dtype_t>(range.first);
dtype_t i = python::extract<dtype_t>(range.second); dtype_t i = python::extract<dtype_t>(range.second);
dijkstra_shortest_paths_no_color_map
(g, vertex(s, g), visitor(vis).weight_map(weight). if (vertex(s, g) == graph_traits<Graph>::null_vertex())
distance_map(dist).distance_inf(i).distance_zero(z)); {
for (auto u : vertices_range(g))
{
vis.initialize_vertex(u, g);
put(dist, u, i);
}
for (auto u : vertices_range(g))
{
if (dist[u] != i)
continue;
dist[u] = z;
dijkstra_shortest_paths_no_color_map_no_init
(g, u, dummy_property_map(), dist, weight,
get(vertex_index_t(), g), std::less<dtype_t>(),
boost::closed_plus<dtype_t>(), i, z, vis);
}
}
else
{
dijkstra_shortest_paths_no_color_map
(g, vertex(s, g), visitor(vis).weight_map(weight).
distance_map(dist).distance_inf(i).distance_zero(z));
}
} }
}; };
......
...@@ -3261,6 +3261,7 @@ VertexBase.is_valid = Vertex.is_valid ...@@ -3261,6 +3261,7 @@ VertexBase.is_valid = Vertex.is_valid
Vertex = VertexBase Vertex = VertexBase
Vertex.__name__ = "Vertex" Vertex.__name__ = "Vertex"
_get_null_vertex = libcore.get_null_vertex
# Add convenience function to vector classes # Add convenience function to vector classes
def _get_array_view(self): def _get_array_view(self):
......
...@@ -92,7 +92,7 @@ if sys.version_info < (3,): ...@@ -92,7 +92,7 @@ if sys.version_info < (3,):
from .. dl_import import dl_import from .. dl_import import dl_import
dl_import("from . import libgraph_tool_search") dl_import("from . import libgraph_tool_search")
from .. import _prop, _python_type from .. import _prop, _python_type, _get_null_vertex
import weakref import weakref
import numpy import numpy
...@@ -157,15 +157,17 @@ class BFSVisitor(object): ...@@ -157,15 +157,17 @@ class BFSVisitor(object):
return return
def bfs_search(g, source, visitor=BFSVisitor()): def bfs_search(g, source=None, visitor=BFSVisitor()):
r"""Breadth-first traversal of a directed or undirected graph. r"""Breadth-first traversal of a directed or undirected graph.
Parameters Parameters
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex` source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
visitor : :class:`~graph_tool.search.BFSVisitor` (optional, default: ``BFSVisitor()``) visitor : :class:`~graph_tool.search.BFSVisitor` (optional, default: ``BFSVisitor()``)
A visitor object that is invoked at the event points inside the A visitor object that is invoked at the event points inside the
algorithm. This should be a subclass of algorithm. This should be a subclass of
...@@ -284,12 +286,16 @@ def bfs_search(g, source, visitor=BFSVisitor()): ...@@ -284,12 +286,16 @@ def bfs_search(g, source, visitor=BFSVisitor()):
""" """
try: try:
if source is None:
source = _get_null_vertex()
else:
source = int(source)
libgraph_tool_search.bfs_search(g._Graph__graph, libgraph_tool_search.bfs_search(g._Graph__graph,
int(source), visitor) source, visitor)
except StopSearch: except StopSearch:
pass pass
def bfs_iterator(g, source): def bfs_iterator(g, source=None):
r"""Return an iterator of the edges corresponding to a breath-first traversal of r"""Return an iterator of the edges corresponding to a breath-first traversal of
the graph. the graph.
...@@ -297,8 +303,10 @@ def bfs_iterator(g, source): ...@@ -297,8 +303,10 @@ def bfs_iterator(g, source):
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex` source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
Returns Returns
------- -------
...@@ -341,8 +349,11 @@ def bfs_iterator(g, source): ...@@ -341,8 +349,11 @@ def bfs_iterator(g, source):
.. [bfs-bgl] http://www.boost.org/doc/libs/release/libs/graph/doc/breadth_first_search.html .. [bfs-bgl] http://www.boost.org/doc/libs/release/libs/graph/doc/breadth_first_search.html
.. [bfs-wikipedia] http://en.wikipedia.org/wiki/Breadth-first_search .. [bfs-wikipedia] http://en.wikipedia.org/wiki/Breadth-first_search
""" """
if source is None:
return libgraph_tool_search.bfs_search_generator(g._Graph__graph, int(source)) source = _get_null_vertex()
else:
source = int(source)
return libgraph_tool_search.bfs_search_generator(g._Graph__graph, source)
class DFSVisitor(object): class DFSVisitor(object):
...@@ -415,15 +426,17 @@ class DFSVisitor(object): ...@@ -415,15 +426,17 @@ class DFSVisitor(object):
return return
def dfs_search(g, source, visitor=DFSVisitor()): def dfs_search(g, source=None, visitor=DFSVisitor()):
r"""Depth-first traversal of a directed or undirected graph. r"""Depth-first traversal of a directed or undirected graph.
Parameters Parameters
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex` source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
visitor : :class:`~graph_tool.search.DFSVisitor` (optional, default: ``DFSVisitor()``) visitor : :class:`~graph_tool.search.DFSVisitor` (optional, default: ``DFSVisitor()``)
A visitor object that is invoked at the event points inside the A visitor object that is invoked at the event points inside the
algorithm. This should be a subclass of algorithm. This should be a subclass of
...@@ -563,15 +576,20 @@ def dfs_search(g, source, visitor=DFSVisitor()): ...@@ -563,15 +576,20 @@ def dfs_search(g, source, visitor=DFSVisitor()):
---------- ----------
.. [dfs-bgl] http://www.boost.org/doc/libs/release/libs/graph/doc/depth_first_search.html .. [dfs-bgl] http://www.boost.org/doc/libs/release/libs/graph/doc/depth_first_search.html
.. [dfs-wikipedia] http://en.wikipedia.org/wiki/Depth-first_search .. [dfs-wikipedia] http://en.wikipedia.org/wiki/Depth-first_search
""" """
try: try:
if source is None:
source = _get_null_vertex()
else:
source = int(source)
libgraph_tool_search.dfs_search(g._Graph__graph, libgraph_tool_search.dfs_search(g._Graph__graph,
int(source), visitor) source, visitor)
except StopSearch: except StopSearch:
pass pass
def dfs_iterator(g, source): def dfs_iterator(g, source=None):
r"""Return an iterator of the edges corresponding to a depth-first traversal of r"""Return an iterator of the edges corresponding to a depth-first traversal of
the graph. the graph.
...@@ -579,8 +597,10 @@ def dfs_iterator(g, source): ...@@ -579,8 +597,10 @@ def dfs_iterator(g, source):
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex` source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
Returns Returns
------- -------
...@@ -622,7 +642,11 @@ def dfs_iterator(g, source): ...@@ -622,7 +642,11 @@ def dfs_iterator(g, source):
.. [dfs-wikipedia] http://en.wikipedia.org/wiki/Depth-first_search .. [dfs-wikipedia] http://en.wikipedia.org/wiki/Depth-first_search
""" """
return libgraph_tool_search.dfs_search_generator(g._Graph__graph, int(source)) if source is None:
source = _get_null_vertex()
else:
source = int(source)
return libgraph_tool_search.dfs_search_generator(g._Graph__graph, source)
class DijkstraVisitor(object): class DijkstraVisitor(object):
r"""A visitor object that is invoked at the event-points inside the r"""A visitor object that is invoked at the event-points inside the
...@@ -686,7 +710,7 @@ class DijkstraVisitor(object): ...@@ -686,7 +710,7 @@ class DijkstraVisitor(object):
return return
def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None, def dijkstra_search(g, weight, source=None, visitor=DijkstraVisitor(), dist_map=None,
pred_map=None, combine=lambda a, b: a + b, pred_map=None, combine=lambda a, b: a + b,
compare=lambda a, b: a < b, zero=0, infinity=numpy.inf): compare=lambda a, b: a < b, zero=0, infinity=numpy.inf):
r"""Dijsktra traversal of a directed or undirected graph, with non-negative weights. r"""Dijsktra traversal of a directed or undirected graph, with non-negative weights.
...@@ -695,10 +719,12 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None, ...@@ -695,10 +719,12 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None,
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex`
Source vertex.
weight : :class:`~graph_tool.PropertyMap` weight : :class:`~graph_tool.PropertyMap`
Edge property map with weight values. Edge property map with weight values.
source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
visitor : :class:`~graph_tool.search.DijkstraVisitor` (optional, default: ``DijkstraVisitor()``) visitor : :class:`~graph_tool.search.DijkstraVisitor` (optional, default: ``DijkstraVisitor()``)
A visitor object that is invoked at the event points inside the A visitor object that is invoked at the event points inside the
algorithm. This should be a subclass of algorithm. This should be a subclass of
...@@ -815,7 +841,7 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None, ...@@ -815,7 +841,7 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None,
With the above class defined, we can perform the Dijkstra search as follows. With the above class defined, we can perform the Dijkstra search as follows.
>>> time = g.new_vertex_property("int") >>> time = g.new_vertex_property("int")
>>> dist, pred = gt.dijkstra_search(g, g.vertex(0), weight, VisitorExample(name, time)) >>> dist, pred = gt.dijkstra_search(g, weight, g.vertex(0), VisitorExample(name, time))
--> Bob has been discovered! --> Bob has been discovered!
edge (Bob, Eve) has been examined... edge (Bob, Eve) has been examined...
edge (Bob, Eve) has been relaxed... edge (Bob, Eve) has been relaxed...
...@@ -910,8 +936,12 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None, ...@@ -910,8 +936,12 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None,
infinity = _python_type(dist_map.value_type())(infinity) infinity = _python_type(dist_map.value_type())(infinity)
try: try:
if source is None:
source = _get_null_vertex()
else:
source = int(source)
libgraph_tool_search.dijkstra_search(g._Graph__graph, libgraph_tool_search.dijkstra_search(g._Graph__graph,
int(source), source,
_prop("v", g, dist_map), _prop("v", g, dist_map),
_prop("v", g, pred_map), _prop("v", g, pred_map),
_prop("e", g, weight), visitor, _prop("e", g, weight), visitor,
...@@ -921,7 +951,7 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None, ...@@ -921,7 +951,7 @@ def dijkstra_search(g, source, weight, visitor=DijkstraVisitor(), dist_map=None,
return dist_map, pred_map return dist_map, pred_map
def dijkstra_iterator(g, source, weight, dist_map=None, combine=None, def dijkstra_iterator(g, weight, source=None, dist_map=None, combine=None,
compare=None, zero=0, infinity=numpy.inf): compare=None, zero=0, infinity=numpy.inf):
r"""Return an iterator of the edges corresponding to a Dijkstra traversal of r"""Return an iterator of the edges corresponding to a Dijkstra traversal of
the graph. the graph.
...@@ -930,10 +960,12 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None, ...@@ -930,10 +960,12 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None,
---------- ----------
g : :class:`~graph_tool.Graph` g : :class:`~graph_tool.Graph`
Graph to be used. Graph to be used.
source : :class:`~graph_tool.Vertex`
Source vertex.
weight : :class:`~graph_tool.PropertyMap` weight : :class:`~graph_tool.PropertyMap`
Edge property map with weight values. Edge property map with weight values.
source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex. If unspecified, all vertices will be traversed, by
iterating over starting vertices according to their index in increasing
order.
dist_map : :class:`~graph_tool.PropertyMap` (optional, default: ``None``) dist_map : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
A vertex property map where the distances from the source will be A vertex property map where the distances from the source will be
stored. stored.
...@@ -972,7 +1004,7 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None, ...@@ -972,7 +1004,7 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None,
Examples Examples
-------- --------
>>> for e in gt.dijkstra_iterator(g, g.vertex(0), weight): >>> for e in gt.dijkstra_iterator(g, weight, g.vertex(0)):
... print(name[e.source()], "->", name[e.target()]) ... print(name[e.source()], "->", name[e.target()])
Bob -> Eve Bob -> Eve
Bob -> Chuck Bob -> Chuck
...@@ -1009,9 +1041,13 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None, ...@@ -1009,9 +1041,13 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None,
infinity = (weight.a.max() + 1) * g.num_vertices() infinity = (weight.a.max() + 1) * g.num_vertices()
infinity = _python_type(dist_map.value_type())(infinity) infinity = _python_type(dist_map.value_type())(infinity)
if source is None:
source = _get_null_vertex()
else:
source = int(source)
if compare is None and combine is None: if compare is None and combine is None:
return libgraph_tool_search.dijkstra_generator_fast(g._Graph__graph, return libgraph_tool_search.dijkstra_generator_fast(g._Graph__graph,
int(source), source,
_prop("v", g, dist_map), _prop("v", g, dist_map),
_prop("e", g, weight), _prop("e", g, weight),
zero, infinity) zero, infinity)
...@@ -1021,7 +1057,7 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None, ...@@ -1021,7 +1057,7 @@ def dijkstra_iterator(g, source, weight, dist_map=None, combine=None,
if combine is None: if combine is None:
combine = lambda a, b: a + b combine = lambda a, b: a + b
return libgraph_tool_search.dijkstra_generator(g._Graph__graph, return libgraph_tool_search.dijkstra_generator(g._Graph__graph,
int(source), source,
_prop("v", g, dist_map), _prop("v", g, dist_map),
_prop("e", g, weight), _prop("e", g, weight),
compare, combine, compare, combine,
......
Markdown is supported
0% or .