Commit 615a8925 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement all_shortest_paths()

parent c49a9dd6
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "graph_filtering.hh" #include "graph_filtering.hh"
#include "graph_properties.hh" #include "graph_properties.hh"
#include "graph_selectors.hh" #include "graph_selectors.hh"
#include "graph_python_interface.hh"
#include "numpy_bind.hh" #include "numpy_bind.hh"
#include "hash_map_wrap.hh" #include "hash_map_wrap.hh"
...@@ -28,6 +29,10 @@ ...@@ -28,6 +29,10 @@
#include <boost/python/stl_iterator.hpp> #include <boost/python/stl_iterator.hpp>
#include <boost/python.hpp> #include <boost/python.hpp>
#ifdef HAVE_BOOST_COROUTINE
#include <boost/coroutine/all.hpp>
#endif // HAVE_BOOST_COROUTINE
using namespace std; using namespace std;
using namespace boost; using namespace boost;
using namespace graph_tool; using namespace graph_tool;
...@@ -407,7 +412,96 @@ void get_dists(GraphInterface& gi, size_t source, boost::python::object tgt, ...@@ -407,7 +412,96 @@ void get_dists(GraphInterface& gi, size_t source, boost::python::object tgt,
} }
} }
template <class Graph, class Dist, class Pred, class Preds>
void get_all_preds(Graph g, Dist dist, Pred pred, Preds preds)
{
int i, N = num_vertices(g);
#pragma omp parallel for default(shared) private(i) \
schedule(runtime) if (N > 100)
for (i = 0; i < N; ++i)
{
size_t v = vertex(i, g);
if (v == graph_traits<Graph>::null_vertex())
continue;
if (size_t(pred[v]) == v)
continue;
auto d = dist[pred[v]];
for (auto e : in_or_out_edges_range(v, g))
{
auto u = boost::is_directed(g) ? source(e, g) : target(e, g);
if (dist[u] == d)
preds[v].push_back(u);
}
}
};
void do_get_all_preds(GraphInterface& gi, boost::any adist,
boost::any apred, boost::any apreds)
{
typedef property_map_type
::apply<int64_t, GraphInterface::vertex_index_map_t>::type pred_map_t;
typedef property_map_type
::apply<vector<int64_t>, GraphInterface::vertex_index_map_t>::type preds_map_t;
pred_map_t pred = any_cast<pred_map_t>(apred);
preds_map_t preds = any_cast<preds_map_t>(apreds);
run_action<>()
(gi, [&](auto& g, auto dist)
{get_all_preds(g, dist, pred.get_unchecked(num_vertices(g)),
preds.get_unchecked(num_vertices(g)));},
vertex_scalar_properties())(adist);
}
template <class Pred, class Yield>
void get_all_paths(size_t s, size_t t, Pred pred, Yield& yield)
{
vector<size_t> path;
vector<pair<size_t, size_t>> stack = {{t, 0}};
while (!stack.empty())
{
size_t v, i;
std::tie(v, i) = stack.back();
if (v == s)
{
path.clear();
for (auto iter = stack.rbegin(); iter != stack.rend(); ++iter)
path.push_back(iter->first);
yield(wrap_vector_owned<size_t>(path));
}
if (pred[v].size() > i)
{
stack.emplace_back(pred[v][i], 0);
}
else
{
stack.pop_back();
if (!stack.empty())
++stack.back().second;
}
}
};
python::object do_get_all_paths(GraphInterface& gi, size_t s, size_t t,
boost::any apred)
{
#ifdef HAVE_BOOST_COROUTINE
auto dispatch = [&](auto& yield)
{
run_action<>()
(gi, [&](auto&, auto pred) {get_all_paths(s, t, pred, yield);},
vertex_scalar_vector_properties())(apred);
};
return python::object(CoroGenerator(dispatch));
#else
throw GraphException("This functionality is not available because boost::coroutine was not found at compile-time");
#endif // HAVE_BOOST_COROUTINE
}
void export_dists() void export_dists()
{ {
python::def("get_dists", &get_dists); python::def("get_dists", &get_dists);
python::def("get_all_preds", &do_get_all_preds);
python::def("get_all_paths", &do_get_all_paths);
}; };
...@@ -30,6 +30,8 @@ Summary ...@@ -30,6 +30,8 @@ Summary
shortest_distance shortest_distance
shortest_path shortest_path
all_shortest_paths
all_predecessors
pseudo_diameter pseudo_diameter
similarity similarity
isomorphism isomorphism
...@@ -78,9 +80,9 @@ __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph", ...@@ -78,9 +80,9 @@ __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"sequential_vertex_coloring", "label_components", "sequential_vertex_coloring", "label_components",
"label_largest_component", "label_biconnected_components", "label_largest_component", "label_biconnected_components",
"label_out_component", "kcore_decomposition", "shortest_distance", "label_out_component", "kcore_decomposition", "shortest_distance",
"shortest_path", "pseudo_diameter", "is_bipartite", "is_DAG", "shortest_path", "all_shortest_paths", "all_predecessors",
"is_planar", "make_maximal_planar", "similarity", "edge_reciprocity"] "pseudo_diameter", "is_bipartite", "is_DAG", "is_planar",
"make_maximal_planar", "similarity", "edge_reciprocity"]
def similarity(g1, g2, label1=None, label2=None, norm=True): def similarity(g1, g2, label1=None, label2=None, norm=True):
r"""Return the adjacency similarity between the two graphs. r"""Return the adjacency similarity between the two graphs.
...@@ -339,7 +341,7 @@ def subgraph_isomorphism(sub, g, max_n=0, vertex_label=None, edge_label=None, ...@@ -339,7 +341,7 @@ def subgraph_isomorphism(sub, g, max_n=0, vertex_label=None, edge_label=None,
http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.101.5342 http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.101.5342
.. [cordella-subgraph-2004] L. P. Cordella, P. Foggia, C. Sansone, and M. Vento, .. [cordella-subgraph-2004] L. P. Cordella, P. Foggia, C. Sansone, and M. Vento,
"A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs.", "A (Sub)Graph Isomorphism Algorithm for Matching Large Graphs.",
IEEE Trans. Pattern Anal. Mach. Intell., vol. 26, no. 10, pp. 1367-1372, 2004. IEEE Trans. Pattern Anal. Mach. Intell., vol. 26, no. 10, pp. 1367-1372, 2004.
:doi:`10.1109/TPAMI.2004.75` :doi:`10.1109/TPAMI.2004.75`
.. [boost-subgraph-iso] http://www.boost.org/libs/graph/doc/vf2_sub_graph_iso.html .. [boost-subgraph-iso] http://www.boost.org/libs/graph/doc/vf2_sub_graph_iso.html
.. [subgraph-isormophism-wikipedia] http://en.wikipedia.org/wiki/Subgraph_isomorphism_problem .. [subgraph-isormophism-wikipedia] http://en.wikipedia.org/wiki/Subgraph_isomorphism_problem
...@@ -1142,7 +1144,7 @@ def shortest_distance(g, source=None, target=None, weights=None, ...@@ -1142,7 +1144,7 @@ def shortest_distance(g, source=None, target=None, weights=None,
The edge weights. If provided, the shortest path will correspond to the The edge weights. If provided, the shortest path will correspond to the
minimal sum of weights. minimal sum of weights.
negative_weights : ``bool`` (optional, default: ``False``) negative_weights : ``bool`` (optional, default: ``False``)
If `True`, this will trigger the use of Bellman-Ford algorithm. If `True`, this will trigger the use of the Bellman-Ford algorithm.
Ignored if ``source`` is ``None``. Ignored if ``source`` is ``None``.
max_dist : scalar value (optional, default: ``None``) max_dist : scalar value (optional, default: ``None``)
If specified, this limits the maximum distance of the vertices If specified, this limits the maximum distance of the vertices
...@@ -1316,7 +1318,7 @@ def shortest_distance(g, source=None, target=None, weights=None, ...@@ -1316,7 +1318,7 @@ def shortest_distance(g, source=None, target=None, weights=None,
def shortest_path(g, source, target, weights=None, negative_weights=False, def shortest_path(g, source, target, weights=None, negative_weights=False,
pred_map=None): pred_map=None):
"""Return the shortest path from `source` to `target`. """Return the shortest path from ``source`` to ``target``.
Parameters Parameters
---------- ----------
...@@ -1329,7 +1331,7 @@ def shortest_path(g, source, target, weights=None, negative_weights=False, ...@@ -1329,7 +1331,7 @@ def shortest_path(g, source, target, weights=None, negative_weights=False,
weights : :class:`~graph_tool.PropertyMap` (optional, default: None) weights : :class:`~graph_tool.PropertyMap` (optional, default: None)
The edge weights. The edge weights.
negative_weights : ``bool`` (optional, default: ``False``) negative_weights : ``bool`` (optional, default: ``False``)
If `True`, this will trigger the use of Bellman-Ford algorithm. If ``True``, this will trigger the use of the Bellman-Ford algorithm.
pred_map : :class:`~graph_tool.PropertyMap` (optional, default: None) pred_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
Vertex property map with the predecessors in the search tree. If this is Vertex property map with the predecessors in the search tree. If this is
provided, the shortest paths are not computed, and are obtained directly provided, the shortest paths are not computed, and are obtained directly
...@@ -1419,6 +1421,117 @@ def shortest_path(g, source, target, weights=None, negative_weights=False, ...@@ -1419,6 +1421,117 @@ def shortest_path(g, source, target, weights=None, negative_weights=False,
v = p v = p
return vlist, elist return vlist, elist
def all_predecessors(g, dist_map, pred_map):
"""Return a property map with all possible predecessors in the search tree
determined by ``dist_map`` and ``pred_map``.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
dist_map : :class:`~graph_tool.PropertyMap`
Vertex property map with the distances from ``source`` to all other
vertices.
pred_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
Vertex property map with the predecessors in the search tree.
Returns
-------
all_preds_map : :class:`~graph_tool.PropertyMap`
Vector-valued vertex property map with all possible predecessors in the
search tree.
"""
preds = g.new_vertex_property("vector<int64_t>")
libgraph_tool_topology.get_all_preds(g._Graph__graph,
_prop("v", g, dist_map),
_prop("v", g, pred_map),
_prop("v", g, preds))
return preds
def all_shortest_paths(g, source, target, weights=None, negative_weights=False,
dist_map=None, pred_map=None, all_preds_map=None):
"""Return an iterator over all shortest paths from `source` to `target`.
Parameters
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
source : :class:`~graph_tool.Vertex`
Source vertex of the search.
target : :class:`~graph_tool.Vertex`
Target vertex of the search.
weights : :class:`~graph_tool.PropertyMap` (optional, default: None)
The edge weights.
negative_weights : ``bool`` (optional, default: ``False``)
If ``True``, this will trigger the use of the Bellman-Ford algorithm.
dist_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
Vertex property map with the distances from ``source`` to all other
vertices.
pred_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
Vertex property map with the predecessors in the search tree. If this is
provided, the shortest paths are not computed, and are obtained directly
from this map.
all_preds_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
Vector-valued vertex property map with all possible predecessors in the
search tree. If this is provided, the shortest paths are obtained
directly from this map.
Returns
-------
path_iterator : iterator over a sequence of integers
Iterator over sequences of vertices from `source` to `target` in the
shortest path.
Notes
-----
The paths are computed with a breadth-first search (BFS) or Dijkstra's
algorithm [dijkstra]_, if weights are given. If ``negative_weights ==
True``, the Bellman-Ford algorithm is used [bellman_ford]_, which accepts
negative weights, as long as there are no negative loops.
If both ``dist_map`` and ``pred_map` are provided, the search is not
actually performed.
Examples
--------
>>> g = gt.collection.data["pgp-strong-2009"]
>>> for path in gt.all_shortest_paths(g, 92, 45):
... print(path)
[ 92 107 2176 7027 26 21 45]
[ 92 107 2176 7033 26 21 45]
[ 92 82 94 5877 5879 34 45]
[ 92 89 94 5877 5879 34 45]
References
----------
.. [bfs] Edward Moore, "The shortest path through a maze", International
Symposium on the Theory of Switching (1959), Harvard University
Press
.. [bfs-boost] http://www.boost.org/libs/graph/doc/breadth_first_search.html
.. [dijkstra] E. Dijkstra, "A note on two problems in connexion with
graphs." Numerische Mathematik, 1:269-271, 1959.
.. [dijkstra-boost] http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html
.. [bellman-ford] http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html
"""
if dist_map is None or pred_map is None:
dist_map, pred_map = shortest_distance(g, source, weights=weights,
negative_weights=negative_weights,
pred_map=True)
if all_preds_map is None:
all_preds_map = all_predecessors(g, dist_map, pred_map)
path_iterator = libgraph_tool_topology.get_all_paths(g._Graph__graph,
int(source),
int(target),
_prop("v", g, all_preds_map))
return path_iterator
def pseudo_diameter(g, source=None, weights=None): def pseudo_diameter(g, source=None, weights=None):
""" """
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment