Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
trac #32800: weights in distance, closeness_centrality, shortest_path
Browse files Browse the repository at this point in the history
  • Loading branch information
dcoudert committed Oct 30, 2021
1 parent f716a0b commit 6281b32
Showing 1 changed file with 40 additions and 47 deletions.
87 changes: 40 additions & 47 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -14657,7 +14657,7 @@ def cluster_transitivity(self):

### Distance

def distance(self, u, v, by_weight=False):
def distance(self, u, v, by_weight=False, weight_function=None, check_weight=True):
"""
Return the (directed) distance from ``u`` to ``v`` in the (di)graph.

Expand All @@ -14674,6 +14674,15 @@ def distance(self, u, v, by_weight=False):
shortest path. If ``True``, the distance is the sum of edge labels
(which are assumed to be numbers).

- ``weight_function`` -- function (default: ``None``); a function that
takes as input an edge ``(u, v, l)`` and outputs its weight. If not
``None``, ``by_weight`` is automatically set to ``True``. If ``None``
and ``by_weight`` is ``True``, we use the edge label ``l``, if ``l``
is not ``None``, else ``1`` as a weight.

- ``check_weight`` -- boolean (default: ``True``); whether to check that
the ``weight_function`` outputs a number for each edge.

EXAMPLES::

sage: G = graphs.CycleGraph(9)
Expand All @@ -14693,7 +14702,9 @@ def distance(self, u, v, by_weight=False):
sage: G.distance(0, 3, by_weight=True)
3
"""
return self.shortest_path_length(u, v, by_weight=by_weight)
return self.shortest_path_length(u, v, by_weight=by_weight,
weight_function=weight_function,
check_weight=check_weight)

def distance_all_pairs(self, by_weight=False, algorithm=None,
weight_function=None, check_weight=True):
Expand Down Expand Up @@ -15558,11 +15569,9 @@ def centrality_closeness(self, vert=None, by_weight=False, algorithm=None,
....: for ci, cj in itertools.combinations(c, 2):
....: assert(sum(abs(ci[v] - cj[v]) for v in g if g.degree(v)) < 1e-12)
"""
if weight_function is not None:
by_weight=True
elif by_weight:
def weight_function(e):
return 1 if e[2] is None else e[2]
by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
weight_function=weight_function,
check_weight=check_weight)

onlyone = False
if vert in self:
Expand All @@ -15576,21 +15585,13 @@ def weight_function(e):
if algorithm is None:
if not by_weight:
algorithm = 'BFS'
else:
for e in self.edge_iterator():
try:
if float(weight_function(e)) < 0:
algorithm = 'Johnson_Boost'
break
except (ValueError, TypeError):
raise ValueError("the weight function cannot find the" +
" weight of " + str(e))
weight_function = None
elif any(float(weight_function(e)) < 0 for e in self.edge_iterator()):
algorithm = 'Johnson_Boost'
if algorithm is None:
algorithm = 'Dijkstra_Boost'

if algorithm == 'NetworkX':
if by_weight and check_weight:
self._check_weight_function(weight_function)
import networkx
if by_weight:
if self.is_directed():
Expand Down Expand Up @@ -15629,15 +15630,17 @@ def weight_function(e):
distances = None
if algorithm in ["Floyd-Warshall-Cython",
"Floyd-Warshall-Python"]:
distances = self.shortest_path_all_pairs(by_weight,algorithm,
weight_function,
check_weight)[0]
distances = self.shortest_path_all_pairs(algorithm=algorithm,
by_weight=by_weight,
weight_function=weight_function,
check_weight=False)[0]

for v in v_iter:
if distances is None:
distv = self.shortest_path_lengths(v, by_weight, algorithm,
weight_function,
check_weight)
distv = self.shortest_path_lengths(v, algorithm=algorithm,
by_weight=by_weight,
weight_function=weight_function,
check_weight=False)
else:
distv = distances[v]
try:
Expand Down Expand Up @@ -15906,38 +15909,28 @@ def shortest_path(self, u, v, by_weight=False, algorithm=None,
raise ValueError("vertex '{}' is not in the (di)graph".format(u))
if not self.has_vertex(v):
raise ValueError("vertex '{}' is not in the (di)graph".format(v))
if u == v:
return [u]

if weight_function is not None:
by_weight = True
by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
weight_function=weight_function,
check_weight=check_weight)

if algorithm is None:
algorithm = 'Dijkstra_Bid' if by_weight else 'BFS_Bid'
elif algorithm in ['BFS', 'BFS_Bid']:
if by_weight:
raise ValueError("the '{}' algorithm does not work on weighted graphs".format(algorithm))
# We don't want the default weight function
weight_function = None

if algorithm in ['BFS', 'Dijkstra_NetworkX', 'Bellman-Ford_Boost']:
all_paths = self.shortest_paths(u, by_weight, algorithm, weight_function, check_weight)
all_paths = self.shortest_paths(u, algorithm=algorithm, by_weight=by_weight,
weight_function=weight_function, check_weight=False)
if v in all_paths:
return all_paths[v]
return []

if u == v: # to avoid a NetworkX bug
return [u]

if by_weight:
if algorithm == 'BFS_Bid':
raise ValueError("the 'BFS_Bid' algorithm does not "
"work on weighted graphs")

if not weight_function:
def weight_function(e):
return 1 if e[2] is None else e[2]

if check_weight:
self._check_weight_function(weight_function)
else:
def weight_function(e):
return 1

if algorithm == "Dijkstra_Bid":
elif algorithm == "Dijkstra_Bid":
return self._backend.bidirectional_dijkstra(u, v, weight_function)
elif algorithm == "Dijkstra_Bid_NetworkX":
import networkx
Expand Down

0 comments on commit 6281b32

Please sign in to comment.