From c0a18032f71897554c59583314116c3c46c085dd Mon Sep 17 00:00:00 2001 From: Michele Borassi Date: Thu, 16 Jul 2015 15:33:40 +0200 Subject: [PATCH] Modified allow_multiple_edges, and removed simplify --- src/sage/graphs/generic_graph.py | 90 +++++++++++++++++++++++++------ src/sage/graphs/spanning_tree.pyx | 48 +---------------- 2 files changed, 77 insertions(+), 61 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c275639a585..29526d922e4 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -2494,15 +2494,27 @@ def allows_multiple_edges(self): """ return self._backend.multiple_edges(None) - def allow_multiple_edges(self, new, check=True): + def allow_multiple_edges(self, new, check=True, keep_label='any'): """ Changes whether multiple edges are permitted in the (di)graph. INPUT: - - ``new`` - boolean. + - ``new`` (boolean): if ``True``, the new graph will allow multiple + edges. - EXAMPLES:: + - ``check`` (boolean): if ``True`` and ``new`` is ``False``, we remove + all multiple edges from the graph. + + - ``keep_label`` (``'any','min','max'``): used only if ``new`` is + ``False`` and ``check`` is ``True``. If there are multiple edges with + different labels, this variable defines which label should be kept: + any label (``'any'``), the smallest label (``'min'``), or the largest + (``'max'``). + + EXAMPLES: + + The standard behavior with undirected graphs:: sage: G = Graph(multiedges=True,sparse=True); G Multi-graph on 0 vertices @@ -2510,17 +2522,33 @@ def allow_multiple_edges(self, new, check=True): False sage: G.allows_multiple_edges() True - sage: G.add_edges([(0,1)]*3) + sage: G.add_edges([(0,1,1), (0,1,2), (0,1,3)]) sage: G.has_multiple_edges() True sage: G.multiple_edges() - [(0, 1, None), (0, 1, None), (0, 1, None)] + [(0, 1, 1), (0, 1, 2), (0, 1, 3)] sage: G.allow_multiple_edges(False); G Graph on 2 vertices sage: G.has_multiple_edges() False sage: G.edges() - [(0, 1, None)] + [(0, 1, 1)] + + If we ask for the minimum label:: + + sage: G = Graph([(0, 1, 1), (0, 1, 2), (0, 1, 3)], multiedges=True,sparse=True) + sage: G.allow_multiple_edges(False, keep_label='min') + sage: G.edges() + [(0, 1, 1)] + + If we ask for the maximum label:: + + sage: G = Graph([(0, 1, 1), (0, 1, 2), (0, 1, 3)], multiedges=True,sparse=True) + sage: G.allow_multiple_edges(False, keep_label='max') + sage: G.edges() + [(0, 1, 3)] + + The standard behavior with digraphs:: sage: D = DiGraph(multiedges=True,sparse=True); D Multi-digraph on 0 vertices @@ -2540,15 +2568,28 @@ def allow_multiple_edges(self, new, check=True): sage: D.edges() [(0, 1, None)] """ - seen = set() + seen = dict() + + if not keep_label in ['any','min','max']: + raise ValueError("Variable keep_label must be 'any', 'min', or 'max'") # TODO: this should be much faster for c_graphs, but for now we just do this if self.allows_multiple_edges() and new is False and check: for u,v,l in self.multiple_edges(): - if (u,v) in seen: - self.delete_edge(u,v,l) + if not (u,v) in seen: + # This is the first time we see this edge + seen[(u,v)] = l else: - seen.add((u,v)) + # This edge has already been seen: we have to remove + # something from the graph. + oldl = seen[(u,v)] + if ((keep_label=='min' and loldl)): + # Keep the new edge, delete the old one + self.delete_edge((u,v,oldl)) + seen[(u,v)] = l + else: + # Delete the new edge + self.delete_edge((u,v,l)) self._backend.multiple_edges(new) @@ -14614,10 +14655,22 @@ def complement(self): return G.copy(immutable=True) return G - def to_simple(self): + def to_simple(self, to_undirected=True, keep_label='any'): """ - Returns a simple version of itself (i.e., undirected and loops and - multiple edges are removed). + Returns a simple version of itself. + + In particular, loops and multiple edges are removed, and the graph might + optionally be converted to an undirected graph. + + INPUT: + + - ``to_undirected`` - boolean - if ``True``, the graph is also converted + to an undirected graph. + + - ``keep_label`` (``'any','min','max'``): if there are multiple edges + with different labels, this variable defines which label should be + kept: any label (``'any'``), the smallest label (``'min'``), or the + largest (``'max'``). EXAMPLES:: @@ -14634,10 +14687,17 @@ def to_simple(self): False sage: H.allows_multiple_edges() False + sage: G.to_simple(to_undirected=False, keep_label='min').edges() + [(2, 3, 1), (3, 2, None)] + sage: G.to_simple(to_undirected=False, keep_label='max').edges() + [(2, 3, 2), (3, 2, None)] """ - g=self.to_undirected() + if to_undirected: + g=self.to_undirected() + else: + g=copy(self) g.allow_loops(False) - g.allow_multiple_edges(False) + g.allow_multiple_edges(False, keep_label=keep_label) return g def disjoint_union(self, other, verbose_relabel=None, labels="pairs"): diff --git a/src/sage/graphs/spanning_tree.pyx b/src/sage/graphs/spanning_tree.pyx index cd4ba428a97..af9d04d58cf 100644 --- a/src/sage/graphs/spanning_tree.pyx +++ b/src/sage/graphs/spanning_tree.pyx @@ -73,51 +73,6 @@ Methods include "sage/ext/interrupt.pxi" -cpdef simplify(g): - r""" - Removes all self-loops and multiple edges from the input graph ``g``. - - The graph ``g`` is not copied for performance reasons. If ``g`` is labeled - and edge ``(u,v)`` has many labels, only the minimum label will be kept. - - EXAMPLE:: - - sage: from sage.graphs.spanning_tree import simplify - sage: g = Graph([[1,1,1],[1,2,2],[1,2,4],[2,3,3],[2,3,1]], multiedges=True, loops=True) - sage: simplify(g) - sage: g.edges() - [(1, 2, 2), (2, 3, 1)] - """ - g.allow_loops(False) - # If there are multiple edges from u to v, retain the edge of - # minimum weight among all such edges. - if g.allows_multiple_edges(): - # If there are multiple edges from u to v, retain only the - # start and end vertices of such edges. Let a and b be the - # start and end vertices, respectively, of a weighted edge - # (a, b, w) having weight w. Then there are multiple weighted - # edges from a to b if and only if the set uniqE has the - # tuple (a, b) as an element. - uniqE = set() - for u, v, _ in iter(g.multiple_edges(to_undirected=True)): - uniqE.add((u, v)) - # Let (u, v) be an element in uniqE. Then there are multiple - # weighted edges from u to v. Let W be a list of all edge - # weights of multiple edges from u to v, sorted in - # nondecreasing order. If w is the first element in W, then - # (u, v, w) is an edge of minimum weight (there may be - # several edges of minimum weight) among all weighted edges - # from u to v. If i >= 2 is the i-th element in W, delete the - # multiple weighted edge (u, v, i). - for u, v in uniqE: - W = sorted(g.edge_label(u, v)) - for w in W[1:]: - g.delete_edge(u, v, w) - # all multiple edges should now be removed; check this! - assert g.multiple_edges() == [] - g.allow_multiple_edges(False) - - cpdef kruskal(G, wfunction=None, bint check=False): r""" Minimum spanning tree using Kruskal's algorithm. @@ -324,7 +279,8 @@ cpdef kruskal(G, wfunction=None, bint check=False): # G is a tree return G.edges() g = G.copy() - simplify(g) + g.allow_loops(False) + g.allow_multiple_edges(False, keep_label='min') # G is assumed to be connected, undirected, and with at least a vertex