Commit 0313ff37 authored by Tiago Peixoto's avatar Tiago Peixoto
Browse files

Implement "negative_weights" in shortest_distance/path()

parent 441495fa
Pipeline #45 passed with stage
......@@ -23,7 +23,8 @@
#include "hash_map_wrap.hh"
#include <boost/graph/breadth_first_search.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/dijkstra_shortest_paths_no_color_map.hpp>
#include <boost/graph/bellman_ford_shortest_paths.hpp>
#include <boost/python/stl_iterator.hpp>
#include <boost/python.hpp>
......@@ -38,9 +39,22 @@ class bfs_max_visitor:
public boost::bfs_visitor<null_visitor>
{
public:
bfs_max_visitor(DistMap dist_map, PredMap pred, size_t max_dist, size_t target)
: _dist_map(dist_map), _pred(pred), _max_dist(max_dist), _target(target),
_dist(0) {}
bfs_max_visitor(DistMap dist_map, PredMap pred, size_t max_dist,
size_t source, size_t target)
: _dist_map(dist_map), _pred(pred), _max_dist(max_dist),
_source(source), _target(target), _dist(0) {}
template <class Graph>
void initialize_vertex(typename graph_traits<Graph>::vertex_descriptor v,
Graph&)
{
typedef typename property_traits<DistMap>::value_type dist_t;
dist_t inf = std::is_floating_point<dist_t>::value ?
numeric_limits<dist_t>::infinity() :
numeric_limits<dist_t>::max();
_dist_map[v] = (v == _source) ? 0 : inf;
_pred[v] = v;
}
template <class Graph>
void tree_edge(typename graph_traits<Graph>::edge_descriptor e,
......@@ -73,6 +87,7 @@ private:
DistMap _dist_map;
PredMap _pred;
size_t _max_dist;
size_t _source;
size_t _target;
size_t _dist;
};
......@@ -83,10 +98,22 @@ class bfs_max_multiple_targets_visitor:
{
public:
bfs_max_multiple_targets_visitor(DistMap dist_map, PredMap pred,
size_t max_dist,
size_t max_dist, size_t source,
gt_hash_set<std::size_t> target)
: _dist_map(dist_map), _pred(pred), _max_dist(max_dist), _target(target),
_dist(0) {}
: _dist_map(dist_map), _pred(pred), _max_dist(max_dist),
_source(source), _target(target), _dist(0) {}
template <class Graph>
void initialize_vertex(typename graph_traits<Graph>::vertex_descriptor v,
Graph&)
{
typedef typename property_traits<DistMap>::value_type dist_t;
dist_t inf = std::is_floating_point<dist_t>::value ?
numeric_limits<dist_t>::infinity() :
numeric_limits<dist_t>::max();
_dist_map[v] = (v == _source) ? 0 : inf;
_pred[v] = v;
}
template <class Graph>
void tree_edge(typename graph_traits<Graph>::edge_descriptor e,
......@@ -112,10 +139,10 @@ public:
return;
_dist_map[v] = _dist_map[_pred[v]] + 1;
auto search = _target.find(v);
if (search != _target.end())
auto iter = _target.find(v);
if (iter != _target.end())
{
_target.erase(*search);
_target.erase(iter);
if (_target.empty())
throw stop_search();
};
......@@ -125,6 +152,7 @@ private:
DistMap _dist_map;
PredMap _pred;
size_t _max_dist;
size_t _source;
gt_hash_set<std::size_t> _target;
size_t _dist;
};
......@@ -178,10 +206,10 @@ public:
if (_dist_map[u] > _max_dist)
throw stop_search();
auto search = _target.find(u);
if (search != _target.end())
auto iter = _target.find(u);
if (iter != _target.end())
{
_target.erase(*search);
_target.erase(iter);
if (_target.empty())
throw stop_search();
};
......@@ -194,7 +222,6 @@ private:
gt_hash_set<std::size_t> _target;
};
struct do_bfs_search
{
template <class Graph, class VertexIndexMap, class DistMap, class PredMap>
......@@ -209,20 +236,14 @@ struct do_bfs_search
gt_hash_set<std::size_t> tgt(target_list.begin(),
target_list.end());
dist_t max_d = (max_dist > 0) ?
max_dist : numeric_limits<dist_t>::max();
dist_t inf = std::is_floating_point<dist_t>::value ?
numeric_limits<dist_t>::infinity() :
numeric_limits<dist_t>::max();
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)
dist_map[i] = numeric_limits<dist_t>::max();
dist_map[source] = 0;
dist_t max_d = (max_dist > 0) ? max_dist : inf;
pred_map[vertex(source, g)] = vertex(source, g);
unchecked_vector_property_map<boost::default_color_type, VertexIndexMap>
color_map(vertex_index, num_vertices(g));
try
{
if (tgt.size() <= 1)
......@@ -232,7 +253,8 @@ struct do_bfs_search
*tgt.begin();
breadth_first_search(g, vertex(source, g),
visitor(bfs_max_visitor<DistMap, PredMap>
(dist_map, pred_map, max_d, target)).
(dist_map, pred_map, max_d,
source, target)).
vertex_index_map(vertex_index).
color_map(color_map));
}
......@@ -240,7 +262,8 @@ struct do_bfs_search
{
breadth_first_search(g, vertex(source, g),
visitor(bfs_max_multiple_targets_visitor<DistMap, PredMap>
(dist_map, pred_map, max_d, tgt)).
(dist_map, pred_map, max_d,
source, tgt)).
vertex_index_map(vertex_index).
color_map(color_map));
}
......@@ -262,16 +285,22 @@ struct do_djk_search
auto target_list = get_array<int64_t, 1>(otarget_list);
typedef typename property_traits<DistMap>::value_type dist_t;
dist_t max_d = (max_dist > 0) ?
max_dist : numeric_limits<dist_t>::max();
max_dist : (std::is_floating_point<dist_t>::value ?
numeric_limits<dist_t>::infinity() :
numeric_limits<dist_t>::max());
gt_hash_set<std::size_t> tgt(target_list.begin(),
target_list.end());
dist_t inf = (std::is_floating_point<dist_t>::value) ?
numeric_limits<dist_t>::infinity() :
numeric_limits<dist_t>::max();
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)
dist_map[i] = numeric_limits<dist_t>::max();
dist_map[i] = inf;
dist_map[source] = 0;
try
......@@ -281,23 +310,27 @@ struct do_djk_search
size_t target = tgt.empty() ?
graph_traits<GraphInterface::multigraph_t>::null_vertex() :
*tgt.begin();
dijkstra_shortest_paths(g, vertex(source, g),
weight_map(weight).
distance_map(dist_map).
vertex_index_map(vertex_index).
predecessor_map(pred_map).
visitor(djk_max_visitor<DistMap>
(dist_map, max_d, target)));
dijkstra_shortest_paths_no_color_map
(g, vertex(source, g),
weight_map(weight).
distance_map(dist_map).
vertex_index_map(vertex_index).
predecessor_map(pred_map).
distance_inf(inf).
visitor(djk_max_visitor<DistMap>
(dist_map, max_d, target)));
}
else
{
dijkstra_shortest_paths(g, vertex(source, g),
weight_map(weight).
distance_map(dist_map).
vertex_index_map(vertex_index).
predecessor_map(pred_map).
visitor(djk_max_multiple_targets_visitor<DistMap>
(dist_map, max_d, tgt)));
dijkstra_shortest_paths_no_color_map
(g, vertex(source, g),
weight_map(weight).
distance_map(dist_map).
vertex_index_map(vertex_index).
predecessor_map(pred_map).
distance_inf(inf).
visitor(djk_max_multiple_targets_visitor<DistMap>
(dist_map, max_d, tgt)));
}
}
......@@ -305,9 +338,35 @@ struct do_djk_search
}
};
struct do_bf_search
{
template <class Graph, class DistMap, class PredMap, class WeightMap>
void operator()(const Graph& g, size_t source, DistMap dist_map,
PredMap pred_map, WeightMap weight) const
{
bool ret = bellman_ford_shortest_paths(g, root_vertex(source).
predecessor_map(pred_map).
distance_map(dist_map).
weight_map(weight));
if (!ret)
throw ValueException("Graph contains negative loops");
// consistency with dijkstra
typedef typename property_traits<DistMap>::value_type dist_t;
if (std::is_floating_point<dist_t>::value)
{
for (auto v : vertices_range(g))
{
if (dist_map[v] == numeric_limits<dist_t>::max())
dist_map[v] = numeric_limits<dist_t>::infinity();
}
}
}
};
void get_dists(GraphInterface& gi, size_t source, boost::python::object tgt,
boost::any dist_map, boost::any weight, boost::any pred_map,
long double max_dist)
long double max_dist, bool bf)
{
typedef property_map_type
::apply<int64_t, GraphInterface::vertex_index_map_t>::type pred_map_t;
......@@ -325,14 +384,26 @@ void get_dists(GraphInterface& gi, size_t source, boost::python::object tgt,
}
else
{
run_action<>()
(gi, std::bind(do_djk_search(), placeholders::_1, source, tgt, gi.get_vertex_index(),
placeholders::_2, pmap.get_unchecked(num_vertices(gi.get_graph())),
placeholders::_3, max_dist),
writable_vertex_scalar_properties(),
edge_scalar_properties())
(dist_map, weight);
if (bf)
{
run_action<>()
(gi, std::bind(do_bf_search(), placeholders::_1, source,
placeholders::_2, pmap.get_unchecked(num_vertices(gi.get_graph())),
placeholders::_3),
writable_vertex_scalar_properties(),
edge_scalar_properties())
(dist_map, weight);
}
else
{
run_action<>()
(gi, std::bind(do_djk_search(), placeholders::_1, source, tgt, gi.get_vertex_index(),
placeholders::_2, pmap.get_unchecked(num_vertices(gi.get_graph())),
placeholders::_3, max_dist),
writable_vertex_scalar_properties(),
edge_scalar_properties())
(dist_map, weight);
}
}
}
......
......@@ -1095,9 +1095,9 @@ def kcore_decomposition(g, deg="out", vprop=None):
return vprop
def shortest_distance(g, source=None, target=None, weights=None, max_dist=None,
directed=None, dense=False, dist_map=None,
pred_map=False):
def shortest_distance(g, source=None, target=None, weights=None,
negative_weights=False, max_dist=None, directed=None,
dense=False, dist_map=None, pred_map=False):
"""Calculate the distance from a source to a target vertex, or to of all
vertices from a given source, or the all pairs shortest paths, if the source
is not specified.
......@@ -1106,50 +1106,59 @@ def shortest_distance(g, source=None, target=None, weights=None, max_dist=None,
----------
g : :class:`~graph_tool.Graph`
Graph to be used.
source : :class:`~graph_tool.Vertex` (optional, default: None)
source : :class:`~graph_tool.Vertex` (optional, default: ``None``)
Source vertex of the search. If unspecified, the all pairs shortest
distances are computed.
target : :class:`~graph_tool.Vertex` or iterable of such objects (optional, default: None)
target : :class:`~graph_tool.Vertex` or iterable of such objects (optional, default: ``None``)
Target vertex (or vertices) of the search. If unspecified, the distance
to all vertices from the source will be computed.
weights : :class:`~graph_tool.PropertyMap` (optional, default: None)
The edge weights. If provided, the minimum spanning tree will minimize
the edge weights.
max_dist : scalar value (optional, default: None)
weights : :class:`~graph_tool.PropertyMap` (optional, default: ``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.
Ignored if ``source`` is ``None``.
max_dist : scalar value (optional, default: ``None``)
If specified, this limits the maximum distance of the vertices
searched. This parameter has no effect if source is None.
directed : bool (optional, default:None)
searched. This parameter has no effect if source is ``None``, or if
`negative_weights=True`.
directed : ``bool`` (optional, default:``None``)
Treat graph as directed or not, independently of its actual
directionality.
dense : bool (optional, default: False)
If true, and source is None, the Floyd-Warshall algorithm is used,
otherwise the Johnson algorithm is used. If source is not None, this option
dense : ``bool`` (optional, default: ``False``)
If ``True``, and source is ``None``, the Floyd-Warshall algorithm is used,
otherwise the Johnson algorithm is used. If source is not ``None``, this option
has no effect.
dist_map : :class:`~graph_tool.PropertyMap` (optional, default: None)
dist_map : :class:`~graph_tool.PropertyMap` (optional, default: ``None``)
Vertex property to store the distances. If none is supplied, one
is created.
pred_map : bool (optional, default: False)
If true, a vertex property map with the predecessors is returned.
Ignored if source=None.
pred_map : ``bool`` (optional, default: ``False``)
If ``True``, a vertex property map with the predecessors is returned.
Ignored if ``source`` is ``None``.
Returns
-------
dist_map : :class:`~graph_tool.PropertyMap`
Vertex property map with the distances from source. If source is 'None',
Vertex property map with the distances from source. If source is ``None``,
it will have a vector value type, with the distances to every vertex.
pred_map : :class:`~graph_tool.PropertyMap` (optional, if ``pred_map == True``)
Vertex property map with the predecessors in the search tree.
Notes
-----
If a source is given, the distances are calculated with a breadth-first
search (BFS) or Dijkstra's algorithm [dijkstra]_, if weights are given. If
source is not given, the distances are calculated with Johnson's algorithm
[johnson-apsp]_. If dense=True, the Floyd-Warshall algorithm
[floyd-warshall-apsp]_ is used instead.
``negative_weights == True``, the Bellman-Ford algorithm is used
[bellman_ford]_, which accepts negative weights, as long as there are no
negative loops. If source is not given, the distances are calculated with
Johnson's algorithm [johnson-apsp]_. If dense=True, the Floyd-Warshall
algorithm [floyd-warshall-apsp]_ is used instead.
If source is specified, the algorithm runs in :math:`O(V + E)` time, or
:math:`O(V \log V)` if weights are given. If source is not specified, it
runs in :math:`O(VE\log V)` time, or :math:`O(V^3)` if dense == True.
:math:`O(V \log V)` if weights are given. If ``negative_weights == True``,
the complexity is :math:`O(VE)`. If source is not specified, it runs in
:math:`O(VE\log V)` time, or :math:`O(V^3)` if dense == True.
Examples
--------
......@@ -1218,6 +1227,7 @@ def shortest_distance(g, source=None, target=None, weights=None, max_dist=None,
.. [dijkstra-boost] http://www.boost.org/libs/graph/doc/dijkstra_shortest_paths.html
.. [johnson-apsp] http://www.boost.org/libs/graph/doc/johnson_all_pairs_shortest.html
.. [floyd-warshall-apsp] http://www.boost.org/libs/graph/doc/floyd_warshall_shortest.html
.. [bellman-ford] http://www.boost.org/libs/graph/doc/bellman_ford_shortest.html
"""
if isinstance(target, collections.Iterable):
......@@ -1260,7 +1270,8 @@ def shortest_distance(g, source=None, target=None, weights=None, max_dist=None,
_prop("v", g, dist_map),
_prop("e", g, weights),
_prop("v", g, pmap),
float(max_dist))
float(max_dist),
negative_weights)
else:
libgraph_tool_topology.get_all_dists(u._Graph__graph,
_prop("v", g, dist_map),
......@@ -1277,9 +1288,9 @@ def shortest_distance(g, source=None, target=None, weights=None, max_dist=None,
else:
return dist_map
def shortest_path(g, source, target, weights=None, pred_map=None):
"""
Return the shortest path from `source` to `target`.
def shortest_path(g, source, target, weights=None, negative_weights=False,
pred_map=None):
"""Return the shortest path from `source` to `target`.
Parameters
----------
......@@ -1291,6 +1302,8 @@ def shortest_path(g, source, target, weights=None, pred_map=None):
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 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
......@@ -1307,7 +1320,9 @@ def shortest_path(g, source, target, weights=None, pred_map=None):
-----
The paths are computed with a breadth-first search (BFS) or Dijkstra's
algorithm [dijkstra]_, if weights are given.
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.
The algorithm runs in :math:`O(V + E)` time, or :math:`O(V \log V)` if
weights are given.
......@@ -1338,11 +1353,12 @@ def shortest_path(g, source, target, weights=None, pred_map=None):
.. [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 pred_map is None:
pred_map = shortest_distance(g, source, target,
weights=weights,
pred_map = shortest_distance(g, source, target, weights=weights,
negative_weights=negative_weights,
pred_map=True)[1]
if pred_map[target] == int(target): # no path to target
......@@ -2037,4 +2053,4 @@ def sequential_vertex_coloring(g, order=None, color=None):
return color
from .. flow import libgraph_tool_flow
from .. flow import libgraph_tool_flow
\ No newline at end of file
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