2174 lines
67 KiB
Python
2174 lines
67 KiB
Python
|
# -*- coding: utf-8 -*-
|
|||
|
# Copyright (C) 2004-2019 by
|
|||
|
# Aric Hagberg <hagberg@lanl.gov>
|
|||
|
# Dan Schult <dschult@colgate.edu>
|
|||
|
# Pieter Swart <swart@lanl.gov>
|
|||
|
# All rights reserved.
|
|||
|
# BSD license.
|
|||
|
#
|
|||
|
# Authors: Aric Hagberg <hagberg@lanl.gov>
|
|||
|
# Loïc Séguin-C. <loicseguin@gmail.com>
|
|||
|
# Dan Schult <dschult@colgate.edu>
|
|||
|
# Niels van Adrichem <n.l.m.vanadrichem@tudelft.nl>
|
|||
|
"""
|
|||
|
Shortest path algorithms for weighed graphs.
|
|||
|
"""
|
|||
|
|
|||
|
from collections import deque
|
|||
|
from heapq import heappush, heappop
|
|||
|
from itertools import count
|
|||
|
import networkx as nx
|
|||
|
from networkx.utils import generate_unique_node
|
|||
|
|
|||
|
|
|||
|
__all__ = ['dijkstra_path',
|
|||
|
'dijkstra_path_length',
|
|||
|
'bidirectional_dijkstra',
|
|||
|
'single_source_dijkstra',
|
|||
|
'single_source_dijkstra_path',
|
|||
|
'single_source_dijkstra_path_length',
|
|||
|
'multi_source_dijkstra',
|
|||
|
'multi_source_dijkstra_path',
|
|||
|
'multi_source_dijkstra_path_length',
|
|||
|
'all_pairs_dijkstra',
|
|||
|
'all_pairs_dijkstra_path',
|
|||
|
'all_pairs_dijkstra_path_length',
|
|||
|
'dijkstra_predecessor_and_distance',
|
|||
|
'bellman_ford_path',
|
|||
|
'bellman_ford_path_length',
|
|||
|
'single_source_bellman_ford',
|
|||
|
'single_source_bellman_ford_path',
|
|||
|
'single_source_bellman_ford_path_length',
|
|||
|
'all_pairs_bellman_ford_path',
|
|||
|
'all_pairs_bellman_ford_path_length',
|
|||
|
'bellman_ford_predecessor_and_distance',
|
|||
|
'negative_edge_cycle',
|
|||
|
'goldberg_radzik',
|
|||
|
'johnson']
|
|||
|
|
|||
|
|
|||
|
def _weight_function(G, weight):
|
|||
|
"""Returns a function that returns the weight of an edge.
|
|||
|
|
|||
|
The returned function is specifically suitable for input to
|
|||
|
functions :func:`_dijkstra` and :func:`_bellman_ford_relaxation`.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If it is callable, `weight` itself is returned. If it is a string,
|
|||
|
it is assumed to be the name of the edge attribute that represents
|
|||
|
the weight of an edge. In that case, a function is returned that
|
|||
|
gets the edge weight according to the specified edge attribute.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
function
|
|||
|
This function returns a callable that accepts exactly three inputs:
|
|||
|
a node, an node adjacent to the first one, and the edge attribute
|
|||
|
dictionary for the eedge joining those nodes. That function returns
|
|||
|
a number representing the weight of an edge.
|
|||
|
|
|||
|
If `G` is a multigraph, and `weight` is not callable, the
|
|||
|
minimum edge weight over all parallel edges is returned. If any edge
|
|||
|
does not have an attribute with key `weight`, it is assumed to
|
|||
|
have weight one.
|
|||
|
|
|||
|
"""
|
|||
|
if callable(weight):
|
|||
|
return weight
|
|||
|
# If the weight keyword argument is not callable, we assume it is a
|
|||
|
# string representing the edge attribute containing the weight of
|
|||
|
# the edge.
|
|||
|
if G.is_multigraph():
|
|||
|
return lambda u, v, d: min(attr.get(weight, 1) for attr in d.values())
|
|||
|
return lambda u, v, data: data.get(weight, 1)
|
|||
|
|
|||
|
|
|||
|
def dijkstra_path(G, source, target, weight='weight'):
|
|||
|
"""Returns the shortest weighted path from source to target in G.
|
|||
|
|
|||
|
Uses Dijkstra's Method to compute the shortest weighted path
|
|||
|
between two nodes in a graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node
|
|||
|
Starting node
|
|||
|
|
|||
|
target : node
|
|||
|
Ending node
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
path : list
|
|||
|
List of nodes in a shortest path.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXNoPath
|
|||
|
If no path exists between source and target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> print(nx.dijkstra_path(G,0,4))
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
The weight function can be used to include node weights.
|
|||
|
|
|||
|
>>> def func(u, v, d):
|
|||
|
... node_u_wt = G.nodes[u].get('node_weight', 1)
|
|||
|
... node_v_wt = G.nodes[v].get('node_weight', 1)
|
|||
|
... edge_wt = d.get('weight', 1)
|
|||
|
... return node_u_wt/2 + node_v_wt/2 + edge_wt
|
|||
|
|
|||
|
In this example we take the average of start and end node
|
|||
|
weights of an edge and add it to the weight of the edge.
|
|||
|
|
|||
|
The function :func:`single_source_dijkstra` computes both
|
|||
|
path and length-of-path if you need both, use that.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
bidirectional_dijkstra(), bellman_ford_path()
|
|||
|
single_source_dijkstra()
|
|||
|
"""
|
|||
|
(length, path) = single_source_dijkstra(G, source, target=target,
|
|||
|
weight=weight)
|
|||
|
return path
|
|||
|
|
|||
|
|
|||
|
def dijkstra_path_length(G, source, target, weight='weight'):
|
|||
|
"""Returns the shortest weighted path length in G from source to target.
|
|||
|
|
|||
|
Uses Dijkstra's Method to compute the shortest weighted path length
|
|||
|
between two nodes in a graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
starting node for path
|
|||
|
|
|||
|
target : node label
|
|||
|
ending node for path
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length : number
|
|||
|
Shortest path length.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXNoPath
|
|||
|
If no path exists between source and target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> print(nx.dijkstra_path_length(G,0,4))
|
|||
|
4
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
The function :func:`single_source_dijkstra` computes both
|
|||
|
path and length-of-path if you need both, use that.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
bidirectional_dijkstra(), bellman_ford_path_length()
|
|||
|
single_source_dijkstra()
|
|||
|
|
|||
|
"""
|
|||
|
if source == target:
|
|||
|
return 0
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
length = _dijkstra(G, source, weight, target=target)
|
|||
|
try:
|
|||
|
return length[target]
|
|||
|
except KeyError:
|
|||
|
raise nx.NetworkXNoPath(
|
|||
|
"Node %s not reachable from %s" % (target, source))
|
|||
|
|
|||
|
|
|||
|
def single_source_dijkstra_path(G, source, cutoff=None, weight='weight'):
|
|||
|
"""Find shortest weighted paths in G from a source node.
|
|||
|
|
|||
|
Compute shortest path between source and all other reachable
|
|||
|
nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node
|
|||
|
Starting node for path.
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
paths : dictionary
|
|||
|
Dictionary of shortest path lengths keyed by target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> path=nx.single_source_dijkstra_path(G,0)
|
|||
|
>>> path[4]
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra(), single_source_bellman_ford()
|
|||
|
|
|||
|
"""
|
|||
|
return multi_source_dijkstra_path(G, {source}, cutoff=cutoff,
|
|||
|
weight=weight)
|
|||
|
|
|||
|
|
|||
|
def single_source_dijkstra_path_length(G, source, cutoff=None,
|
|||
|
weight='weight'):
|
|||
|
"""Find shortest weighted path lengths in G from a source node.
|
|||
|
|
|||
|
Compute the shortest path length between source and all other
|
|||
|
reachable nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length : dict
|
|||
|
Dict keyed by node to shortest path length from source.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length = nx.single_source_dijkstra_path_length(G, 0)
|
|||
|
>>> length[4]
|
|||
|
4
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 3
|
|||
|
4: 4
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra(), single_source_bellman_ford_path_length()
|
|||
|
|
|||
|
"""
|
|||
|
return multi_source_dijkstra_path_length(G, {source}, cutoff=cutoff,
|
|||
|
weight=weight)
|
|||
|
|
|||
|
|
|||
|
def single_source_dijkstra(G, source, target=None, cutoff=None,
|
|||
|
weight='weight'):
|
|||
|
"""Find shortest weighted paths and lengths from a source node.
|
|||
|
|
|||
|
Compute the shortest path length between source and all other
|
|||
|
reachable nodes for a weighted graph.
|
|||
|
|
|||
|
Uses Dijkstra's algorithm to compute shortest paths and lengths
|
|||
|
between a source and all other reachable nodes in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
target : node label, optional
|
|||
|
Ending node for path
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance, path : pair of dictionaries, or numeric and list.
|
|||
|
If target is None, paths and lengths to all nodes are computed.
|
|||
|
The return value is a tuple of two dictionaries keyed by target nodes.
|
|||
|
The first dictionary stores distance to each target node.
|
|||
|
The second stores the path to each target node.
|
|||
|
If target is not None, returns a tuple (distance, path), where
|
|||
|
distance is the distance from source to target and path is a list
|
|||
|
representing the path from source to target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length, path = nx.single_source_dijkstra(G, 0)
|
|||
|
>>> print(length[4])
|
|||
|
4
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 3
|
|||
|
4: 4
|
|||
|
>>> path[4]
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
>>> length, path = nx.single_source_dijkstra(G, 0, 1)
|
|||
|
>>> length
|
|||
|
1
|
|||
|
>>> path
|
|||
|
[0, 1]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
Based on the Python cookbook recipe (119466) at
|
|||
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/119466
|
|||
|
|
|||
|
This algorithm is not guaranteed to work if edge weights
|
|||
|
are negative or are floating point numbers
|
|||
|
(overflows and roundoff errors can cause problems).
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra_path()
|
|||
|
single_source_dijkstra_path_length()
|
|||
|
single_source_bellman_ford()
|
|||
|
"""
|
|||
|
return multi_source_dijkstra(G, {source}, cutoff=cutoff, target=target,
|
|||
|
weight=weight)
|
|||
|
|
|||
|
|
|||
|
def multi_source_dijkstra_path(G, sources, cutoff=None, weight='weight'):
|
|||
|
"""Find shortest weighted paths in G from a given set of source
|
|||
|
nodes.
|
|||
|
|
|||
|
Compute shortest path between any of the source nodes and all other
|
|||
|
reachable nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
sources : non-empty set of nodes
|
|||
|
Starting nodes for paths. If this is just a set containing a
|
|||
|
single node, then all paths computed by this function will start
|
|||
|
from that node. If there are two or more nodes in the set, the
|
|||
|
computed paths may begin from any one of the start nodes.
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
paths : dictionary
|
|||
|
Dictionary of shortest paths keyed by target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> path = nx.multi_source_dijkstra_path(G, {0, 4})
|
|||
|
>>> path[1]
|
|||
|
[0, 1]
|
|||
|
>>> path[3]
|
|||
|
[4, 3]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
ValueError
|
|||
|
If `sources` is empty.
|
|||
|
NodeNotFound
|
|||
|
If any of `sources` is not in `G`.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
multi_source_dijkstra(), multi_source_bellman_ford()
|
|||
|
|
|||
|
"""
|
|||
|
length, path = multi_source_dijkstra(G, sources, cutoff=cutoff,
|
|||
|
weight=weight)
|
|||
|
return path
|
|||
|
|
|||
|
|
|||
|
def multi_source_dijkstra_path_length(G, sources, cutoff=None,
|
|||
|
weight='weight'):
|
|||
|
"""Find shortest weighted path lengths in G from a given set of
|
|||
|
source nodes.
|
|||
|
|
|||
|
Compute the shortest path length between any of the source nodes and
|
|||
|
all other reachable nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
sources : non-empty set of nodes
|
|||
|
Starting nodes for paths. If this is just a set containing a
|
|||
|
single node, then all paths computed by this function will start
|
|||
|
from that node. If there are two or more nodes in the set, the
|
|||
|
computed paths may begin from any one of the start nodes.
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length : dict
|
|||
|
Dict keyed by node to shortest path length to nearest source.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length = nx.multi_source_dijkstra_path_length(G, {0, 4})
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 1
|
|||
|
4: 0
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
ValueError
|
|||
|
If `sources` is empty.
|
|||
|
NodeNotFound
|
|||
|
If any of `sources` is not in `G`.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
multi_source_dijkstra()
|
|||
|
|
|||
|
"""
|
|||
|
if not sources:
|
|||
|
raise ValueError('sources must not be empty')
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
return _dijkstra_multisource(G, sources, weight, cutoff=cutoff)
|
|||
|
|
|||
|
|
|||
|
def multi_source_dijkstra(G, sources, target=None, cutoff=None,
|
|||
|
weight='weight'):
|
|||
|
"""Find shortest weighted paths and lengths from a given set of
|
|||
|
source nodes.
|
|||
|
|
|||
|
Uses Dijkstra's algorithm to compute the shortest paths and lengths
|
|||
|
between one of the source nodes and the given `target`, or all other
|
|||
|
reachable nodes if not specified, for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
sources : non-empty set of nodes
|
|||
|
Starting nodes for paths. If this is just a set containing a
|
|||
|
single node, then all paths computed by this function will start
|
|||
|
from that node. If there are two or more nodes in the set, the
|
|||
|
computed paths may begin from any one of the start nodes.
|
|||
|
|
|||
|
target : node label, optional
|
|||
|
Ending node for path
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance, path : pair of dictionaries, or numeric and list
|
|||
|
If target is None, returns a tuple of two dictionaries keyed by node.
|
|||
|
The first dictionary stores distance from one of the source nodes.
|
|||
|
The second stores the path from one of the sources to that node.
|
|||
|
If target is not None, returns a tuple of (distance, path) where
|
|||
|
distance is the distance from source to target and path is a list
|
|||
|
representing the path from source to target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length, path = nx.multi_source_dijkstra(G, {0, 4})
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 1
|
|||
|
4: 0
|
|||
|
>>> path[1]
|
|||
|
[0, 1]
|
|||
|
>>> path[3]
|
|||
|
[4, 3]
|
|||
|
|
|||
|
>>> length, path = nx.multi_source_dijkstra(G, {0, 4}, 1)
|
|||
|
>>> length
|
|||
|
1
|
|||
|
>>> path
|
|||
|
[0, 1]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The weight function can be used to hide edges by returning None.
|
|||
|
So ``weight = lambda u, v, d: 1 if d['color']=="red" else None``
|
|||
|
will find the shortest red path.
|
|||
|
|
|||
|
Based on the Python cookbook recipe (119466) at
|
|||
|
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/119466
|
|||
|
|
|||
|
This algorithm is not guaranteed to work if edge weights
|
|||
|
are negative or are floating point numbers
|
|||
|
(overflows and roundoff errors can cause problems).
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
ValueError
|
|||
|
If `sources` is empty.
|
|||
|
NodeNotFound
|
|||
|
If any of `sources` is not in `G`.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
multi_source_dijkstra_path()
|
|||
|
multi_source_dijkstra_path_length()
|
|||
|
|
|||
|
"""
|
|||
|
if not sources:
|
|||
|
raise ValueError('sources must not be empty')
|
|||
|
if target in sources:
|
|||
|
return (0, [target])
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
paths = {source: [source] for source in sources} # dictionary of paths
|
|||
|
dist = _dijkstra_multisource(G, sources, weight, paths=paths,
|
|||
|
cutoff=cutoff, target=target)
|
|||
|
if target is None:
|
|||
|
return (dist, paths)
|
|||
|
try:
|
|||
|
return (dist[target], paths[target])
|
|||
|
except KeyError:
|
|||
|
raise nx.NetworkXNoPath("No path to {}.".format(target))
|
|||
|
|
|||
|
|
|||
|
def _dijkstra(G, source, weight, pred=None, paths=None, cutoff=None,
|
|||
|
target=None):
|
|||
|
"""Uses Dijkstra's algorithm to find shortest weighted paths from a
|
|||
|
single source.
|
|||
|
|
|||
|
This is a convenience function for :func:`_dijkstra_multisource`
|
|||
|
with all the arguments the same, except the keyword argument
|
|||
|
`sources` set to ``[source]``.
|
|||
|
|
|||
|
"""
|
|||
|
return _dijkstra_multisource(G, [source], weight, pred=pred, paths=paths,
|
|||
|
cutoff=cutoff, target=target)
|
|||
|
|
|||
|
|
|||
|
def _dijkstra_multisource(G, sources, weight, pred=None, paths=None,
|
|||
|
cutoff=None, target=None):
|
|||
|
"""Uses Dijkstra's algorithm to find shortest weighted paths
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
sources : non-empty iterable of nodes
|
|||
|
Starting nodes for paths. If this is just an iterable containing
|
|||
|
a single node, then all paths computed by this function will
|
|||
|
start from that node. If there are two or more nodes in this
|
|||
|
iterable, the computed paths may begin from any one of the start
|
|||
|
nodes.
|
|||
|
|
|||
|
weight: function
|
|||
|
Function with (u, v, data) input that returns that edges weight
|
|||
|
|
|||
|
pred: dict of lists, optional(default=None)
|
|||
|
dict to store a list of predecessors keyed by that node
|
|||
|
If None, predecessors are not stored.
|
|||
|
|
|||
|
paths: dict, optional (default=None)
|
|||
|
dict to store the path list from source to each node, keyed by node.
|
|||
|
If None, paths are not stored.
|
|||
|
|
|||
|
target : node label, optional
|
|||
|
Ending node for path. Search is halted when target is found.
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : dictionary
|
|||
|
A mapping from node to shortest distance to that node from one
|
|||
|
of the source nodes.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If any of `sources` is not in `G`.
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
The optional predecessor and path dictionaries can be accessed by
|
|||
|
the caller through the original pred and paths objects passed
|
|||
|
as arguments. No need to explicitly return pred or paths.
|
|||
|
|
|||
|
"""
|
|||
|
G_succ = G._succ if G.is_directed() else G._adj
|
|||
|
|
|||
|
push = heappush
|
|||
|
pop = heappop
|
|||
|
dist = {} # dictionary of final distances
|
|||
|
seen = {}
|
|||
|
# fringe is heapq with 3-tuples (distance,c,node)
|
|||
|
# use the count c to avoid comparing nodes (may not be able to)
|
|||
|
c = count()
|
|||
|
fringe = []
|
|||
|
for source in sources:
|
|||
|
if source not in G:
|
|||
|
raise nx.NodeNotFound("Source {} not in G".format(source))
|
|||
|
seen[source] = 0
|
|||
|
push(fringe, (0, next(c), source))
|
|||
|
while fringe:
|
|||
|
(d, _, v) = pop(fringe)
|
|||
|
if v in dist:
|
|||
|
continue # already searched this node.
|
|||
|
dist[v] = d
|
|||
|
if v == target:
|
|||
|
break
|
|||
|
for u, e in G_succ[v].items():
|
|||
|
cost = weight(v, u, e)
|
|||
|
if cost is None:
|
|||
|
continue
|
|||
|
vu_dist = dist[v] + cost
|
|||
|
if cutoff is not None:
|
|||
|
if vu_dist > cutoff:
|
|||
|
continue
|
|||
|
if u in dist:
|
|||
|
if vu_dist < dist[u]:
|
|||
|
raise ValueError('Contradictory paths found:',
|
|||
|
'negative weights?')
|
|||
|
elif u not in seen or vu_dist < seen[u]:
|
|||
|
seen[u] = vu_dist
|
|||
|
push(fringe, (vu_dist, next(c), u))
|
|||
|
if paths is not None:
|
|||
|
paths[u] = paths[v] + [u]
|
|||
|
if pred is not None:
|
|||
|
pred[u] = [v]
|
|||
|
elif vu_dist == seen[u]:
|
|||
|
if pred is not None:
|
|||
|
pred[u].append(v)
|
|||
|
|
|||
|
# The optional predecessor and path dictionaries can be accessed
|
|||
|
# by the caller via the pred and paths objects passed as arguments.
|
|||
|
return dist
|
|||
|
|
|||
|
|
|||
|
def dijkstra_predecessor_and_distance(G, source, cutoff=None, weight='weight'):
|
|||
|
"""Compute weighted shortest path length and predecessors.
|
|||
|
|
|||
|
Uses Dijkstra's Method to obtain the shortest weighted paths
|
|||
|
and return dictionaries of predecessors for each node and
|
|||
|
distance for each node from the `source`.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
pred, distance : dictionaries
|
|||
|
Returns two dictionaries representing a list of predecessors
|
|||
|
of a node and the distance to each node.
|
|||
|
Warning: If target is specified, the dicts are incomplete as they
|
|||
|
only contain information for the nodes along a path to target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The list of predecessors contains more than one element only when
|
|||
|
there are more than one shortest paths to the key node.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> import networkx as nx
|
|||
|
>>> G = nx.path_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> pred, dist = nx.dijkstra_predecessor_and_distance(G, 0)
|
|||
|
>>> sorted(pred.items())
|
|||
|
[(0, []), (1, [0]), (2, [1]), (3, [2]), (4, [3])]
|
|||
|
>>> sorted(dist.items())
|
|||
|
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
|
|||
|
|
|||
|
>>> pred, dist = nx.dijkstra_predecessor_and_distance(G, 0, 1)
|
|||
|
>>> sorted(pred.items())
|
|||
|
[(0, []), (1, [0])]
|
|||
|
>>> sorted(dist.items())
|
|||
|
[(0, 0), (1, 1)]
|
|||
|
"""
|
|||
|
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
pred = {source: []} # dictionary of predecessors
|
|||
|
return (pred, _dijkstra(G, source, weight, pred=pred, cutoff=cutoff))
|
|||
|
|
|||
|
|
|||
|
def all_pairs_dijkstra(G, cutoff=None, weight='weight'):
|
|||
|
"""Find shortest weighted paths and lengths between all nodes.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edge[u][v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Yields
|
|||
|
------
|
|||
|
(node, (distance, path)) : (node obj, (dict, dict))
|
|||
|
Each source node has two associated dicts. The first holds distance
|
|||
|
keyed by target and the second holds paths keyed by target.
|
|||
|
(See single_source_dijkstra for the source/target node terminology.)
|
|||
|
If desired you can apply `dict()` to this function to create a dict
|
|||
|
keyed by source node to the two dicts.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> len_path = dict(nx.all_pairs_dijkstra(G))
|
|||
|
>>> print(len_path[3][0][1])
|
|||
|
2
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('3 - {}: {}'.format(node, len_path[3][0][node]))
|
|||
|
3 - 0: 3
|
|||
|
3 - 1: 2
|
|||
|
3 - 2: 1
|
|||
|
3 - 3: 0
|
|||
|
3 - 4: 1
|
|||
|
>>> len_path[3][1][1]
|
|||
|
[3, 2, 1]
|
|||
|
>>> for n, (dist, path) in nx.all_pairs_dijkstra(G):
|
|||
|
... print(path[1])
|
|||
|
[0, 1]
|
|||
|
[1]
|
|||
|
[2, 1]
|
|||
|
[3, 2, 1]
|
|||
|
[4, 3, 2, 1]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The yielded dicts only have keys for reachable nodes.
|
|||
|
"""
|
|||
|
for n in G:
|
|||
|
dist, path = single_source_dijkstra(G, n, cutoff=cutoff, weight=weight)
|
|||
|
yield (n, (dist, path))
|
|||
|
|
|||
|
|
|||
|
def all_pairs_dijkstra_path_length(G, cutoff=None, weight='weight'):
|
|||
|
"""Compute shortest path lengths between all nodes in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : iterator
|
|||
|
(source, dictionary) iterator with dictionary keyed by target and
|
|||
|
shortest path length as the key value.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length = dict(nx.all_pairs_dijkstra_path_length(G))
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('1 - {}: {}'.format(node, length[1][node]))
|
|||
|
1 - 0: 1
|
|||
|
1 - 1: 0
|
|||
|
1 - 2: 1
|
|||
|
1 - 3: 2
|
|||
|
1 - 4: 3
|
|||
|
>>> length[3][2]
|
|||
|
1
|
|||
|
>>> length[2][2]
|
|||
|
0
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The dictionary returned only has keys for reachable node pairs.
|
|||
|
"""
|
|||
|
length = single_source_dijkstra_path_length
|
|||
|
for n in G:
|
|||
|
yield (n, length(G, n, cutoff=cutoff, weight=weight))
|
|||
|
|
|||
|
|
|||
|
def all_pairs_dijkstra_path(G, cutoff=None, weight='weight'):
|
|||
|
"""Compute shortest paths between all nodes in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
cutoff : integer or float, optional
|
|||
|
Depth to stop the search. Only return paths with length <= cutoff.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : dictionary
|
|||
|
Dictionary, keyed by source and target, of shortest paths.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> path = dict(nx.all_pairs_dijkstra_path(G))
|
|||
|
>>> print(path[0][4])
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
floyd_warshall(), all_pairs_bellman_ford_path()
|
|||
|
|
|||
|
"""
|
|||
|
path = single_source_dijkstra_path
|
|||
|
# TODO This can be trivially parallelized.
|
|||
|
for n in G:
|
|||
|
yield (n, path(G, n, cutoff=cutoff, weight=weight))
|
|||
|
|
|||
|
|
|||
|
def bellman_ford_predecessor_and_distance(G, source, target=None,
|
|||
|
weight='weight'):
|
|||
|
"""Compute shortest path lengths and predecessors on shortest paths
|
|||
|
in weighted graphs.
|
|||
|
|
|||
|
The algorithm has a running time of $O(mn)$ where $n$ is the number of
|
|||
|
nodes and $m$ is the number of edges. It is slower than Dijkstra but
|
|||
|
can handle negative edge weights.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
The algorithm works for all types of graphs, including directed
|
|||
|
graphs and multigraphs.
|
|||
|
|
|||
|
source: node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
pred, dist : dictionaries
|
|||
|
Returns two dictionaries keyed by node to predecessor in the
|
|||
|
path and to the distance from the source respectively.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXUnbounded
|
|||
|
If the (di)graph contains a negative cost (di)cycle, the
|
|||
|
algorithm raises an exception to indicate the presence of the
|
|||
|
negative cost (di)cycle. Note: any negative weight edge in an
|
|||
|
undirected graph is a negative cost cycle.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> import networkx as nx
|
|||
|
>>> G = nx.path_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0)
|
|||
|
>>> sorted(pred.items())
|
|||
|
[(0, []), (1, [0]), (2, [1]), (3, [2]), (4, [3])]
|
|||
|
>>> sorted(dist.items())
|
|||
|
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
|
|||
|
|
|||
|
>>> pred, dist = nx.bellman_ford_predecessor_and_distance(G, 0, 1)
|
|||
|
>>> sorted(pred.items())
|
|||
|
[(0, []), (1, [0]), (2, [1]), (3, [2]), (4, [3])]
|
|||
|
>>> sorted(dist.items())
|
|||
|
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
|
|||
|
|
|||
|
>>> import pytest
|
|||
|
>>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> G[1][2]['weight'] = -7
|
|||
|
>>> pytest.raises(nx.NetworkXUnbounded, \
|
|||
|
nx.bellman_ford_predecessor_and_distance, G, 0)
|
|||
|
<ExceptionInfo NetworkXUnbounded tblen=3>
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The dictionaries returned only have keys for nodes reachable from
|
|||
|
the source.
|
|||
|
|
|||
|
In the case where the (di)graph is not connected, if a component
|
|||
|
not containing the source contains a negative cost (di)cycle, it
|
|||
|
will not be detected.
|
|||
|
|
|||
|
In NetworkX v2.1 and prior, the source node had predecessor `[None]`.
|
|||
|
In NetworkX v2.2 this changed to the source node having predecessor `[]`
|
|||
|
"""
|
|||
|
if source not in G:
|
|||
|
raise nx.NodeNotFound("Node %s is not found in the graph" % source)
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
if any(weight(u, v, d) < 0 for u, v, d in nx.selfloop_edges(G, data=True)):
|
|||
|
raise nx.NetworkXUnbounded("Negative cost cycle detected.")
|
|||
|
|
|||
|
dist = {source: 0}
|
|||
|
pred = {source: []}
|
|||
|
|
|||
|
if len(G) == 1:
|
|||
|
return pred, dist
|
|||
|
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
|
|||
|
dist = _bellman_ford(G, [source], weight, pred=pred, dist=dist,
|
|||
|
target=target)
|
|||
|
return (pred, dist)
|
|||
|
|
|||
|
|
|||
|
def _bellman_ford(G, source, weight, pred=None, paths=None, dist=None,
|
|||
|
target=None):
|
|||
|
"""Relaxation loop for Bellman–Ford algorithm.
|
|||
|
|
|||
|
This is an implementation of the SPFA variant.
|
|||
|
See https://en.wikipedia.org/wiki/Shortest_Path_Faster_Algorithm
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source: list
|
|||
|
List of source nodes. The shortest path from any of the source
|
|||
|
nodes will be found if multiple sources are provided.
|
|||
|
|
|||
|
weight : function
|
|||
|
The weight of an edge is the value returned by the function. The
|
|||
|
function must accept exactly three positional arguments: the two
|
|||
|
endpoints of an edge and the dictionary of edge attributes for
|
|||
|
that edge. The function must return a number.
|
|||
|
|
|||
|
pred: dict of lists, optional (default=None)
|
|||
|
dict to store a list of predecessors keyed by that node
|
|||
|
If None, predecessors are not stored
|
|||
|
|
|||
|
paths: dict, optional (default=None)
|
|||
|
dict to store the path list from source to each node, keyed by node
|
|||
|
If None, paths are not stored
|
|||
|
|
|||
|
dist: dict, optional (default=None)
|
|||
|
dict to store distance from source to the keyed node
|
|||
|
If None, returned dist dict contents default to 0 for every node in the
|
|||
|
source list
|
|||
|
|
|||
|
target: node label, optional
|
|||
|
Ending node for path. Path lengths to other destinations may (and
|
|||
|
probably will) be incorrect.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
Returns a dict keyed by node to the distance from the source.
|
|||
|
Dicts for paths and pred are in the mutated input dicts by those names.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If any of `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXUnbounded
|
|||
|
If the (di)graph contains a negative cost (di)cycle, the
|
|||
|
algorithm raises an exception to indicate the presence of the
|
|||
|
negative cost (di)cycle. Note: any negative weight edge in an
|
|||
|
undirected graph is a negative cost cycle
|
|||
|
"""
|
|||
|
for s in source:
|
|||
|
if s not in G:
|
|||
|
raise nx.NodeNotFound("Source {} not in G".format(s))
|
|||
|
|
|||
|
if pred is None:
|
|||
|
pred = {v: [] for v in source}
|
|||
|
|
|||
|
if dist is None:
|
|||
|
dist = {v: 0 for v in source}
|
|||
|
|
|||
|
G_succ = G.succ if G.is_directed() else G.adj
|
|||
|
inf = float('inf')
|
|||
|
n = len(G)
|
|||
|
|
|||
|
count = {}
|
|||
|
q = deque(source)
|
|||
|
in_q = set(source)
|
|||
|
while q:
|
|||
|
u = q.popleft()
|
|||
|
in_q.remove(u)
|
|||
|
|
|||
|
# Skip relaxations if any of the predecessors of u is in the queue.
|
|||
|
if all(pred_u not in in_q for pred_u in pred[u]):
|
|||
|
dist_u = dist[u]
|
|||
|
for v, e in G_succ[u].items():
|
|||
|
dist_v = dist_u + weight(v, u, e)
|
|||
|
|
|||
|
if dist_v < dist.get(v, inf):
|
|||
|
if v not in in_q:
|
|||
|
q.append(v)
|
|||
|
in_q.add(v)
|
|||
|
count_v = count.get(v, 0) + 1
|
|||
|
if count_v == n:
|
|||
|
raise nx.NetworkXUnbounded(
|
|||
|
"Negative cost cycle detected.")
|
|||
|
count[v] = count_v
|
|||
|
dist[v] = dist_v
|
|||
|
pred[v] = [u]
|
|||
|
|
|||
|
elif dist.get(v) is not None and dist_v == dist.get(v):
|
|||
|
pred[v].append(u)
|
|||
|
|
|||
|
if paths is not None:
|
|||
|
dsts = [target] if target is not None else pred
|
|||
|
for dst in dsts:
|
|||
|
|
|||
|
path = [dst]
|
|||
|
cur = dst
|
|||
|
|
|||
|
while pred[cur]:
|
|||
|
cur = pred[cur][0]
|
|||
|
path.append(cur)
|
|||
|
|
|||
|
path.reverse()
|
|||
|
paths[dst] = path
|
|||
|
|
|||
|
return dist
|
|||
|
|
|||
|
|
|||
|
def bellman_ford_path(G, source, target, weight='weight'):
|
|||
|
"""Returns the shortest path from source to target in a weighted graph G.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node
|
|||
|
Starting node
|
|||
|
|
|||
|
target : node
|
|||
|
Ending node
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
path : list
|
|||
|
List of nodes in a shortest path.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXNoPath
|
|||
|
If no path exists between source and target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> print(nx.bellman_ford_path(G, 0, 4))
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
dijkstra_path(), bellman_ford_path_length()
|
|||
|
"""
|
|||
|
length, path = single_source_bellman_ford(G, source,
|
|||
|
target=target, weight=weight)
|
|||
|
return path
|
|||
|
|
|||
|
|
|||
|
def bellman_ford_path_length(G, source, target, weight='weight'):
|
|||
|
"""Returns the shortest path length from source to target
|
|||
|
in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
starting node for path
|
|||
|
|
|||
|
target : node label
|
|||
|
ending node for path
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length : number
|
|||
|
Shortest path length.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXNoPath
|
|||
|
If no path exists between source and target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> print(nx.bellman_ford_path_length(G,0,4))
|
|||
|
4
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
dijkstra_path_length(), bellman_ford_path()
|
|||
|
"""
|
|||
|
if source == target:
|
|||
|
return 0
|
|||
|
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
|
|||
|
length = _bellman_ford(G, [source], weight, target=target)
|
|||
|
|
|||
|
try:
|
|||
|
return length[target]
|
|||
|
except KeyError:
|
|||
|
raise nx.NetworkXNoPath(
|
|||
|
"node %s not reachable from %s" % (target, source))
|
|||
|
|
|||
|
|
|||
|
def single_source_bellman_ford_path(G, source, weight='weight'):
|
|||
|
"""Compute shortest path between source and all other reachable
|
|||
|
nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node
|
|||
|
Starting node for path.
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
paths : dictionary
|
|||
|
Dictionary of shortest path lengths keyed by target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G=nx.path_graph(5)
|
|||
|
>>> path=nx.single_source_bellman_ford_path(G,0)
|
|||
|
>>> path[4]
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra(), single_source_bellman_ford()
|
|||
|
|
|||
|
"""
|
|||
|
(length, path) = single_source_bellman_ford(
|
|||
|
G, source, weight=weight)
|
|||
|
return path
|
|||
|
|
|||
|
|
|||
|
def single_source_bellman_ford_path_length(G, source, weight='weight'):
|
|||
|
"""Compute the shortest path length between source and all other
|
|||
|
reachable nodes for a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length : iterator
|
|||
|
(target, shortest path length) iterator
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length = dict(nx.single_source_bellman_ford_path_length(G, 0))
|
|||
|
>>> length[4]
|
|||
|
4
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 3
|
|||
|
4: 4
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra(), single_source_bellman_ford()
|
|||
|
|
|||
|
"""
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
return _bellman_ford(G, [source], weight)
|
|||
|
|
|||
|
|
|||
|
def single_source_bellman_ford(G, source, target=None, weight='weight'):
|
|||
|
"""Compute shortest paths and lengths in a weighted graph G.
|
|||
|
|
|||
|
Uses Bellman-Ford algorithm for shortest paths.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
target : node label, optional
|
|||
|
Ending node for path
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance, path : pair of dictionaries, or numeric and list
|
|||
|
If target is None, returns a tuple of two dictionaries keyed by node.
|
|||
|
The first dictionary stores distance from one of the source nodes.
|
|||
|
The second stores the path from one of the sources to that node.
|
|||
|
If target is not None, returns a tuple of (distance, path) where
|
|||
|
distance is the distance from source to target and path is a list
|
|||
|
representing the path from source to target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length, path = nx.single_source_bellman_ford(G, 0)
|
|||
|
>>> print(length[4])
|
|||
|
4
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('{}: {}'.format(node, length[node]))
|
|||
|
0: 0
|
|||
|
1: 1
|
|||
|
2: 2
|
|||
|
3: 3
|
|||
|
4: 4
|
|||
|
>>> path[4]
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
>>> length, path = nx.single_source_bellman_ford(G, 0, 1)
|
|||
|
>>> length
|
|||
|
1
|
|||
|
>>> path
|
|||
|
[0, 1]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
single_source_dijkstra()
|
|||
|
single_source_bellman_ford_path()
|
|||
|
single_source_bellman_ford_path_length()
|
|||
|
"""
|
|||
|
if source == target:
|
|||
|
return (0, [source])
|
|||
|
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
|
|||
|
paths = {source: [source]} # dictionary of paths
|
|||
|
dist = _bellman_ford(G, [source], weight, paths=paths, target=target)
|
|||
|
if target is None:
|
|||
|
return (dist, paths)
|
|||
|
try:
|
|||
|
return (dist[target], paths[target])
|
|||
|
except KeyError:
|
|||
|
msg = "Node %s not reachable from %s" % (target, source)
|
|||
|
raise nx.NetworkXNoPath(msg)
|
|||
|
|
|||
|
|
|||
|
def all_pairs_bellman_ford_path_length(G, weight='weight'):
|
|||
|
""" Compute shortest path lengths between all nodes in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : iterator
|
|||
|
(source, dictionary) iterator with dictionary keyed by target and
|
|||
|
shortest path length as the key value.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length = dict(nx.all_pairs_bellman_ford_path_length(G))
|
|||
|
>>> for node in [0, 1, 2, 3, 4]:
|
|||
|
... print('1 - {}: {}'.format(node, length[1][node]))
|
|||
|
1 - 0: 1
|
|||
|
1 - 1: 0
|
|||
|
1 - 2: 1
|
|||
|
1 - 3: 2
|
|||
|
1 - 4: 3
|
|||
|
>>> length[3][2]
|
|||
|
1
|
|||
|
>>> length[2][2]
|
|||
|
0
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The dictionary returned only has keys for reachable node pairs.
|
|||
|
"""
|
|||
|
length = single_source_bellman_ford_path_length
|
|||
|
for n in G:
|
|||
|
yield (n, dict(length(G, n, weight=weight)))
|
|||
|
|
|||
|
|
|||
|
def all_pairs_bellman_ford_path(G, weight='weight'):
|
|||
|
""" Compute shortest paths between all nodes in a weighted graph.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
weight: string, optional (default='weight')
|
|||
|
Edge data key corresponding to the edge weight
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : dictionary
|
|||
|
Dictionary, keyed by source and target, of shortest paths.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> path = dict(nx.all_pairs_bellman_ford_path(G))
|
|||
|
>>> print(path[0][4])
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
floyd_warshall(), all_pairs_dijkstra_path()
|
|||
|
|
|||
|
"""
|
|||
|
path = single_source_bellman_ford_path
|
|||
|
# TODO This can be trivially parallelized.
|
|||
|
for n in G:
|
|||
|
yield (n, path(G, n, weight=weight))
|
|||
|
|
|||
|
|
|||
|
def goldberg_radzik(G, source, weight='weight'):
|
|||
|
"""Compute shortest path lengths and predecessors on shortest paths
|
|||
|
in weighted graphs.
|
|||
|
|
|||
|
The algorithm has a running time of $O(mn)$ where $n$ is the number of
|
|||
|
nodes and $m$ is the number of edges. It is slower than Dijkstra but
|
|||
|
can handle negative edge weights.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
The algorithm works for all types of graphs, including directed
|
|||
|
graphs and multigraphs.
|
|||
|
|
|||
|
source: node label
|
|||
|
Starting node for path
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
pred, dist : dictionaries
|
|||
|
Returns two dictionaries keyed by node to predecessor in the
|
|||
|
path and to the distance from the source respectively.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If `source` is not in `G`.
|
|||
|
|
|||
|
NetworkXUnbounded
|
|||
|
If the (di)graph contains a negative cost (di)cycle, the
|
|||
|
algorithm raises an exception to indicate the presence of the
|
|||
|
negative cost (di)cycle. Note: any negative weight edge in an
|
|||
|
undirected graph is a negative cost cycle.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> import networkx as nx
|
|||
|
>>> G = nx.path_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> pred, dist = nx.goldberg_radzik(G, 0)
|
|||
|
>>> sorted(pred.items())
|
|||
|
[(0, None), (1, 0), (2, 1), (3, 2), (4, 3)]
|
|||
|
>>> sorted(dist.items())
|
|||
|
[(0, 0), (1, 1), (2, 2), (3, 3), (4, 4)]
|
|||
|
|
|||
|
>>> import pytest
|
|||
|
>>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> G[1][2]['weight'] = -7
|
|||
|
>>> pytest.raises(nx.NetworkXUnbounded, nx.goldberg_radzik, G, 0)
|
|||
|
<ExceptionInfo NetworkXUnbounded tblen=3>
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
The dictionaries returned only have keys for nodes reachable from
|
|||
|
the source.
|
|||
|
|
|||
|
In the case where the (di)graph is not connected, if a component
|
|||
|
not containing the source contains a negative cost (di)cycle, it
|
|||
|
will not be detected.
|
|||
|
|
|||
|
"""
|
|||
|
if source not in G:
|
|||
|
raise nx.NodeNotFound("Node %s is not found in the graph" % source)
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
if any(weight(u, v, d) < 0 for u, v, d in nx.selfloop_edges(G, data=True)):
|
|||
|
raise nx.NetworkXUnbounded("Negative cost cycle detected.")
|
|||
|
|
|||
|
if len(G) == 1:
|
|||
|
return {source: None}, {source: 0}
|
|||
|
|
|||
|
if G.is_directed():
|
|||
|
G_succ = G.succ
|
|||
|
else:
|
|||
|
G_succ = G.adj
|
|||
|
|
|||
|
inf = float('inf')
|
|||
|
d = {u: inf for u in G}
|
|||
|
d[source] = 0
|
|||
|
pred = {source: None}
|
|||
|
|
|||
|
def topo_sort(relabeled):
|
|||
|
"""Topologically sort nodes relabeled in the previous round and detect
|
|||
|
negative cycles.
|
|||
|
"""
|
|||
|
# List of nodes to scan in this round. Denoted by A in Goldberg and
|
|||
|
# Radzik's paper.
|
|||
|
to_scan = []
|
|||
|
# In the DFS in the loop below, neg_count records for each node the
|
|||
|
# number of edges of negative reduced costs on the path from a DFS root
|
|||
|
# to the node in the DFS forest. The reduced cost of an edge (u, v) is
|
|||
|
# defined as d[u] + weight[u][v] - d[v].
|
|||
|
#
|
|||
|
# neg_count also doubles as the DFS visit marker array.
|
|||
|
neg_count = {}
|
|||
|
for u in relabeled:
|
|||
|
# Skip visited nodes.
|
|||
|
if u in neg_count:
|
|||
|
continue
|
|||
|
d_u = d[u]
|
|||
|
# Skip nodes without out-edges of negative reduced costs.
|
|||
|
if all(d_u + weight(u, v, e) >= d[v]
|
|||
|
for v, e in G_succ[u].items()):
|
|||
|
continue
|
|||
|
# Nonrecursive DFS that inserts nodes reachable from u via edges of
|
|||
|
# nonpositive reduced costs into to_scan in (reverse) topological
|
|||
|
# order.
|
|||
|
stack = [(u, iter(G_succ[u].items()))]
|
|||
|
in_stack = set([u])
|
|||
|
neg_count[u] = 0
|
|||
|
while stack:
|
|||
|
u, it = stack[-1]
|
|||
|
try:
|
|||
|
v, e = next(it)
|
|||
|
except StopIteration:
|
|||
|
to_scan.append(u)
|
|||
|
stack.pop()
|
|||
|
in_stack.remove(u)
|
|||
|
continue
|
|||
|
t = d[u] + weight(u, v, e)
|
|||
|
d_v = d[v]
|
|||
|
if t <= d_v:
|
|||
|
is_neg = t < d_v
|
|||
|
d[v] = t
|
|||
|
pred[v] = u
|
|||
|
if v not in neg_count:
|
|||
|
neg_count[v] = neg_count[u] + int(is_neg)
|
|||
|
stack.append((v, iter(G_succ[v].items())))
|
|||
|
in_stack.add(v)
|
|||
|
elif (v in in_stack and
|
|||
|
neg_count[u] + int(is_neg) > neg_count[v]):
|
|||
|
# (u, v) is a back edge, and the cycle formed by the
|
|||
|
# path v to u and (u, v) contains at least one edge of
|
|||
|
# negative reduced cost. The cycle must be of negative
|
|||
|
# cost.
|
|||
|
raise nx.NetworkXUnbounded(
|
|||
|
'Negative cost cycle detected.')
|
|||
|
to_scan.reverse()
|
|||
|
return to_scan
|
|||
|
|
|||
|
def relax(to_scan):
|
|||
|
"""Relax out-edges of relabeled nodes.
|
|||
|
"""
|
|||
|
relabeled = set()
|
|||
|
# Scan nodes in to_scan in topological order and relax incident
|
|||
|
# out-edges. Add the relabled nodes to labeled.
|
|||
|
for u in to_scan:
|
|||
|
d_u = d[u]
|
|||
|
for v, e in G_succ[u].items():
|
|||
|
w_e = weight(u, v, e)
|
|||
|
if d_u + w_e < d[v]:
|
|||
|
d[v] = d_u + w_e
|
|||
|
pred[v] = u
|
|||
|
relabeled.add(v)
|
|||
|
return relabeled
|
|||
|
|
|||
|
# Set of nodes relabled in the last round of scan operations. Denoted by B
|
|||
|
# in Goldberg and Radzik's paper.
|
|||
|
relabeled = set([source])
|
|||
|
|
|||
|
while relabeled:
|
|||
|
to_scan = topo_sort(relabeled)
|
|||
|
relabeled = relax(to_scan)
|
|||
|
|
|||
|
d = {u: d[u] for u in pred}
|
|||
|
return pred, d
|
|||
|
|
|||
|
|
|||
|
def negative_edge_cycle(G, weight='weight'):
|
|||
|
"""Returns True if there exists a negative edge cycle anywhere in G.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
negative_cycle : bool
|
|||
|
True if a negative edge cycle exists, otherwise False.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> import networkx as nx
|
|||
|
>>> G = nx.cycle_graph(5, create_using = nx.DiGraph())
|
|||
|
>>> print(nx.negative_edge_cycle(G))
|
|||
|
False
|
|||
|
>>> G[1][2]['weight'] = -7
|
|||
|
>>> print(nx.negative_edge_cycle(G))
|
|||
|
True
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
This algorithm uses bellman_ford_predecessor_and_distance() but finds
|
|||
|
negative cycles on any component by first adding a new node connected to
|
|||
|
every node, and starting bellman_ford_predecessor_and_distance on that
|
|||
|
node. It then removes that extra node.
|
|||
|
"""
|
|||
|
newnode = generate_unique_node()
|
|||
|
G.add_edges_from([(newnode, n) for n in G])
|
|||
|
|
|||
|
try:
|
|||
|
bellman_ford_predecessor_and_distance(G, newnode, weight)
|
|||
|
except nx.NetworkXUnbounded:
|
|||
|
return True
|
|||
|
finally:
|
|||
|
G.remove_node(newnode)
|
|||
|
return False
|
|||
|
|
|||
|
|
|||
|
def bidirectional_dijkstra(G, source, target, weight='weight'):
|
|||
|
r"""Dijkstra's algorithm for shortest paths using bidirectional search.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
source : node
|
|||
|
Starting node.
|
|||
|
|
|||
|
target : node
|
|||
|
Ending node.
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
length, path : number and list
|
|||
|
length is the distance from source to target.
|
|||
|
path is a list of nodes on a path from source to target.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NodeNotFound
|
|||
|
If either `source` or `target` is not in `G`.
|
|||
|
|
|||
|
NetworkXNoPath
|
|||
|
If no path exists between source and target.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> G = nx.path_graph(5)
|
|||
|
>>> length, path = nx.bidirectional_dijkstra(G, 0, 4)
|
|||
|
>>> print(length)
|
|||
|
4
|
|||
|
>>> print(path)
|
|||
|
[0, 1, 2, 3, 4]
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Edge weight attributes must be numerical.
|
|||
|
Distances are calculated as sums of weighted edges traversed.
|
|||
|
|
|||
|
In practice bidirectional Dijkstra is much more than twice as fast as
|
|||
|
ordinary Dijkstra.
|
|||
|
|
|||
|
Ordinary Dijkstra expands nodes in a sphere-like manner from the
|
|||
|
source. The radius of this sphere will eventually be the length
|
|||
|
of the shortest path. Bidirectional Dijkstra will expand nodes
|
|||
|
from both the source and the target, making two spheres of half
|
|||
|
this radius. Volume of the first sphere is `\pi*r*r` while the
|
|||
|
others are `2*\pi*r/2*r/2`, making up half the volume.
|
|||
|
|
|||
|
This algorithm is not guaranteed to work if edge weights
|
|||
|
are negative or are floating point numbers
|
|||
|
(overflows and roundoff errors can cause problems).
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
shortest_path
|
|||
|
shortest_path_length
|
|||
|
"""
|
|||
|
if source not in G or target not in G:
|
|||
|
msg = 'Either source {} or target {} is not in G'
|
|||
|
raise nx.NodeNotFound(msg.format(source, target))
|
|||
|
|
|||
|
if source == target:
|
|||
|
return (0, [source])
|
|||
|
push = heappush
|
|||
|
pop = heappop
|
|||
|
# Init: [Forward, Backward]
|
|||
|
dists = [{}, {}] # dictionary of final distances
|
|||
|
paths = [{source: [source]}, {target: [target]}] # dictionary of paths
|
|||
|
fringe = [[], []] # heap of (distance, node) for choosing node to expand
|
|||
|
seen = [{source: 0}, {target: 0}] # dict of distances to seen nodes
|
|||
|
c = count()
|
|||
|
# initialize fringe heap
|
|||
|
push(fringe[0], (0, next(c), source))
|
|||
|
push(fringe[1], (0, next(c), target))
|
|||
|
# neighs for extracting correct neighbor information
|
|||
|
if G.is_directed():
|
|||
|
neighs = [G.successors, G.predecessors]
|
|||
|
else:
|
|||
|
neighs = [G.neighbors, G.neighbors]
|
|||
|
# variables to hold shortest discovered path
|
|||
|
# finaldist = 1e30000
|
|||
|
finalpath = []
|
|||
|
dir = 1
|
|||
|
while fringe[0] and fringe[1]:
|
|||
|
# choose direction
|
|||
|
# dir == 0 is forward direction and dir == 1 is back
|
|||
|
dir = 1 - dir
|
|||
|
# extract closest to expand
|
|||
|
(dist, _, v) = pop(fringe[dir])
|
|||
|
if v in dists[dir]:
|
|||
|
# Shortest path to v has already been found
|
|||
|
continue
|
|||
|
# update distance
|
|||
|
dists[dir][v] = dist # equal to seen[dir][v]
|
|||
|
if v in dists[1 - dir]:
|
|||
|
# if we have scanned v in both directions we are done
|
|||
|
# we have now discovered the shortest path
|
|||
|
return (finaldist, finalpath)
|
|||
|
|
|||
|
for w in neighs[dir](v):
|
|||
|
if(dir == 0): # forward
|
|||
|
if G.is_multigraph():
|
|||
|
minweight = min((dd.get(weight, 1)
|
|||
|
for k, dd in G[v][w].items()))
|
|||
|
else:
|
|||
|
minweight = G[v][w].get(weight, 1)
|
|||
|
vwLength = dists[dir][v] + minweight # G[v][w].get(weight,1)
|
|||
|
else: # back, must remember to change v,w->w,v
|
|||
|
if G.is_multigraph():
|
|||
|
minweight = min((dd.get(weight, 1)
|
|||
|
for k, dd in G[w][v].items()))
|
|||
|
else:
|
|||
|
minweight = G[w][v].get(weight, 1)
|
|||
|
vwLength = dists[dir][v] + minweight # G[w][v].get(weight,1)
|
|||
|
|
|||
|
if w in dists[dir]:
|
|||
|
if vwLength < dists[dir][w]:
|
|||
|
raise ValueError(
|
|||
|
"Contradictory paths found: negative weights?")
|
|||
|
elif w not in seen[dir] or vwLength < seen[dir][w]:
|
|||
|
# relaxing
|
|||
|
seen[dir][w] = vwLength
|
|||
|
push(fringe[dir], (vwLength, next(c), w))
|
|||
|
paths[dir][w] = paths[dir][v] + [w]
|
|||
|
if w in seen[0] and w in seen[1]:
|
|||
|
# see if this path is better than than the already
|
|||
|
# discovered shortest path
|
|||
|
totaldist = seen[0][w] + seen[1][w]
|
|||
|
if finalpath == [] or finaldist > totaldist:
|
|||
|
finaldist = totaldist
|
|||
|
revpath = paths[1][w][:]
|
|||
|
revpath.reverse()
|
|||
|
finalpath = paths[0][w] + revpath[1:]
|
|||
|
raise nx.NetworkXNoPath("No path between %s and %s." % (source, target))
|
|||
|
|
|||
|
|
|||
|
def johnson(G, weight='weight'):
|
|||
|
r"""Uses Johnson's Algorithm to compute shortest paths.
|
|||
|
|
|||
|
Johnson's Algorithm finds a shortest path between each pair of
|
|||
|
nodes in a weighted graph even if negative weights are present.
|
|||
|
|
|||
|
Parameters
|
|||
|
----------
|
|||
|
G : NetworkX graph
|
|||
|
|
|||
|
weight : string or function
|
|||
|
If this is a string, then edge weights will be accessed via the
|
|||
|
edge attribute with this key (that is, the weight of the edge
|
|||
|
joining `u` to `v` will be ``G.edges[u, v][weight]``). If no
|
|||
|
such edge attribute exists, the weight of the edge is assumed to
|
|||
|
be one.
|
|||
|
|
|||
|
If this is a function, the weight of an edge is the value
|
|||
|
returned by the function. The function must accept exactly three
|
|||
|
positional arguments: the two endpoints of an edge and the
|
|||
|
dictionary of edge attributes for that edge. The function must
|
|||
|
return a number.
|
|||
|
|
|||
|
Returns
|
|||
|
-------
|
|||
|
distance : dictionary
|
|||
|
Dictionary, keyed by source and target, of shortest paths.
|
|||
|
|
|||
|
Raises
|
|||
|
------
|
|||
|
NetworkXError
|
|||
|
If given graph is not weighted.
|
|||
|
|
|||
|
Examples
|
|||
|
--------
|
|||
|
>>> import networkx as nx
|
|||
|
>>> graph = nx.DiGraph()
|
|||
|
>>> graph.add_weighted_edges_from([('0', '3', 3), ('0', '1', -5),
|
|||
|
... ('0', '2', 2), ('1', '2', 4), ('2', '3', 1)])
|
|||
|
>>> paths = nx.johnson(graph, weight='weight')
|
|||
|
>>> paths['0']['2']
|
|||
|
['0', '1', '2']
|
|||
|
|
|||
|
Notes
|
|||
|
-----
|
|||
|
Johnson's algorithm is suitable even for graphs with negative weights. It
|
|||
|
works by using the Bellman–Ford algorithm to compute a transformation of
|
|||
|
the input graph that removes all negative weights, allowing Dijkstra's
|
|||
|
algorithm to be used on the transformed graph.
|
|||
|
|
|||
|
The time complexity of this algorithm is $O(n^2 \log n + n m)$,
|
|||
|
where $n$ is the number of nodes and $m$ the number of edges in the
|
|||
|
graph. For dense graphs, this may be faster than the Floyd–Warshall
|
|||
|
algorithm.
|
|||
|
|
|||
|
See Also
|
|||
|
--------
|
|||
|
floyd_warshall_predecessor_and_distance
|
|||
|
floyd_warshall_numpy
|
|||
|
all_pairs_shortest_path
|
|||
|
all_pairs_shortest_path_length
|
|||
|
all_pairs_dijkstra_path
|
|||
|
bellman_ford_predecessor_and_distance
|
|||
|
all_pairs_bellman_ford_path
|
|||
|
all_pairs_bellman_ford_path_length
|
|||
|
|
|||
|
"""
|
|||
|
if not nx.is_weighted(G, weight=weight):
|
|||
|
raise nx.NetworkXError('Graph is not weighted.')
|
|||
|
|
|||
|
dist = {v: 0 for v in G}
|
|||
|
pred = {v: [] for v in G}
|
|||
|
weight = _weight_function(G, weight)
|
|||
|
|
|||
|
# Calculate distance of shortest paths
|
|||
|
dist_bellman = _bellman_ford(G, list(G), weight, pred=pred, dist=dist)
|
|||
|
|
|||
|
# Update the weight function to take into account the Bellman--Ford
|
|||
|
# relaxation distances.
|
|||
|
def new_weight(u, v, d):
|
|||
|
return weight(u, v, d) + dist_bellman[u] - dist_bellman[v]
|
|||
|
|
|||
|
def dist_path(v):
|
|||
|
paths = {v: [v]}
|
|||
|
_dijkstra(G, v, new_weight, paths=paths)
|
|||
|
return paths
|
|||
|
|
|||
|
return {v: dist_path(v) for v in G}
|