Skip to content

Commit

Permalink
Trac #32723: Helper function to avoid duplicated code when using weights
Browse files Browse the repository at this point in the history
Many functions with parameters `by_weight` and `weight_function` have
duplicated code for checking the parameters and setting the weight
function. Here we add a helper function `_get_weight_function` that
takes all related parameters as input and return proper values for
parameter `by_weight` and a valid weight function.

A next step will be to unify the usage of weights in graphs, as proposed
in  #13112, since we use either `use_edge_labels` or the combination of
`by_weight` and `weight_function`.

URL: https://trac.sagemath.org/32723
Reported by: dcoudert
Ticket author(s): David Coudert
Reviewer(s): Travis Scrimshaw
  • Loading branch information
Release Manager committed Oct 23, 2021
2 parents 1a9bb6d + 6800b36 commit 225ac12
Showing 1 changed file with 83 additions and 19 deletions.
102 changes: 83 additions & 19 deletions src/sage/graphs/generic_graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -16209,6 +16209,81 @@ def _check_weight_function(self, weight_function=None):
raise ValueError("the weight function cannot find the "
"weight of " + str(e))

def _get_weight_function(self, by_weight=False, weight_function=None, check_weight=True):
r"""
Return an edge weight function.

An edge weight function is a function that takes as input an edge and
outputs its weight.

This method is a helper function for methods using the weight of edges.
It either checks the validity of an input weight function, or returns a
valid weight function on the edges.

INPUT:

- ``by_weight`` -- boolean (default: ``False``); if ``True``, the edges
in the graph are weighted, otherwise all edges have weight 1

- ``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``); if ``True``, we check
that the weight_function outputs a number for each edge

EXAMPLES:

The default weight function outputs 1 for each edge::

sage: G = Graph([(0, 1, 1), (1, 2, 3), (2, 3, 2)])
sage: by_weight, weight_function = G._get_weight_function()
sage: by_weight
False
sage: [weight_function(e) for e in G.edges()]
[1, 1, 1]

The standard weight function outputs labels::

sage: G = Graph([(0, 1, 1), (1, 2, 3), (2, 3, 2)])
sage: by_weight, weight_function = G._get_weight_function(by_weight=True)
sage: by_weight
True
sage: [weight_function(e) for e in G.edges()]
[1, 3, 2]

However, it might be more complicated::

sage: G = Graph([(0, 1, {'name':'a', 'weight':1}), (1, 2, {'name': 'b', 'weight': 3}), (2, 3, {'name': 'c', 'weight': 2})])
sage: by_weight, weight_function = G._get_weight_function(weight_function=lambda e:e[2]['weight'])
sage: by_weight
True
sage: [weight_function(e) for e in G.edges()]
[1, 3, 2]

Numeric string as a weight in weight_function::

sage: G = Graph({0: {1: '123'}})
sage: by_weight, weight_function = G._get_weight_function(by_weight=True)
Traceback (most recent call last):
...
ValueError: the weight function cannot find the weight of (0, 1, '123')
"""
if weight_function is not None:
by_weight = True
if by_weight:
if weight_function is None:
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
return by_weight, weight_function

def shortest_paths(self, u, by_weight=False, algorithm=None,
weight_function=None, check_weight=True, cutoff=None):
r"""
Expand Down Expand Up @@ -16352,21 +16427,13 @@ def shortest_paths(self, u, by_weight=False, algorithm=None,
...
ValueError: ('Contradictory paths found:', 'negative weights?')
"""
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]
else:
def weight_function(e):
return 1
by_weight, weight_function = self._get_weight_function(by_weight=by_weight,
weight_function=weight_function,
check_weight=check_weight)

if algorithm is None and not by_weight:
algorithm = 'BFS'

if by_weight and check_weight:
self._check_weight_function(weight_function)

if algorithm == 'BFS':
if by_weight:
raise ValueError("the 'BFS' algorithm does not work on "
Expand Down Expand Up @@ -16468,14 +16535,11 @@ def _path_length(self, path, by_weight=False, weight_function=None):
return Infinity

if by_weight or weight_function is not None:
if weight_function is None:
def weight_function(e):
return 1 if e[2] is None else e[2]
wt = 0

for u, v in zip(path[:-1], path[1:]):
wt += weight_function((u, v, self.edge_label(u, v)))
return wt
_, weight_function = self._get_weight_function(by_weight=by_weight,
weight_function=weight_function,
check_weight=False)
return sum(weight_function((u, v, self.edge_label(u, v)))
for u, v in zip(path[:-1], path[1:]))
else:
return len(path) - 1

Expand Down

0 comments on commit 225ac12

Please sign in to comment.