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

Implement all_shortest_paths()

parent c49a9dd6
......@@ -19,6 +19,7 @@
#include "graph_filtering.hh"
#include "graph_properties.hh"
#include "graph_selectors.hh"
#include "graph_python_interface.hh"
#include "numpy_bind.hh"
#include "hash_map_wrap.hh"
......@@ -28,6 +29,10 @@
#include <boost/python/stl_iterator.hpp>
#include <boost/python.hpp>
#ifdef HAVE_BOOST_COROUTINE
#include <boost/coroutine/all.hpp>
#endif // HAVE_BOOST_COROUTINE
using namespace std;
using namespace boost;
using namespace graph_tool;
......@@ -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()
{
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
shortest_distance
shortest_path
all_shortest_paths
all_predecessors
pseudo_diameter
similarity
isomorphism
......@@ -78,9 +80,9 @@ __all__ = ["isomorphism", "subgraph_isomorphism", "mark_subgraph",
"sequential_vertex_coloring", "label_components",
"label_largest_component", "label_biconnected_components",
"label_out_component", "kcore_decomposition", "shortest_distance",
"shortest_path", "pseudo_diameter", "is_bipartite", "is_DAG",
"is_planar", "make_maximal_planar", "similarity", "edge_reciprocity"]
"shortest_path", "all_shortest_paths", "all_predecessors",
"pseudo_diameter", "is_bipartite", "is_DAG", "is_planar",
"make_maximal_planar", "similarity", "edge_reciprocity"]
def similarity(g1, g2, label1=None, label2=None, norm=True):
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,
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,
"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`
.. [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
......@@ -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
minimal sum of weights.
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``.
max_dist : scalar value (optional, default: ``None``)
If specified, this limits the maximum distance of the vertices
......@@ -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,
pred_map=None):
"""Return the shortest path from `source` to `target`.
"""Return the shortest path from ``source`` to ``target``.
Parameters
----------
......@@ -1329,7 +1331,7 @@ def shortest_path(g, source, target, weights=None, negative_weights=False,
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 Bellman-Ford algorithm.
If ``True``, this will trigger the use of the Bellman-Ford algorithm.
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
......@@ -1419,6 +1421,117 @@ def shortest_path(g, source, target, weights=None, negative_weights=False,
v = p
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):
"""
......
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