From 6800b36db28020ac40dcdf3236c661a8db4197ce Mon Sep 17 00:00:00 2001 From: dcoudert Date: Tue, 19 Oct 2021 18:35:32 +0200 Subject: [PATCH] trac #32723: add method _get_weight_function and show usage --- src/sage/graphs/generic_graph.py | 102 +++++++++++++++++++++++++------ 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 2ab81f802b2..097d450f5c0 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -16181,6 +16181,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""" @@ -16324,21 +16399,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 " @@ -16440,14 +16507,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