From f8c24e05a64a303baff8da1bc742d7d6a013f240 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 18:05:30 +0100 Subject: [PATCH 1/8] add parameter immutable to method complement --- src/sage/graphs/generic_graph.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 101952109c3..bbeb1397ccf 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19379,7 +19379,7 @@ def add_path(self, vertices): self.add_vertices(vertices) self.add_edges(zip(vertices[:-1], vertices[1:])) - def complement(self): + def complement(self, immutable=None): """ Return the complement of the (di)graph. @@ -19387,6 +19387,12 @@ def complement(self): that are not in the original graph. This is not well defined for graphs with multiple edges. + INPUT: + + - ``immutable`` -- boolean (default: ``None``); whether to return a + mutable or an immutable version of ``self``. By default (``None``), + the graph and its complement behave the same. + EXAMPLES:: sage: P = graphs.PetersenGraph() @@ -19436,8 +19442,9 @@ def complement(self): if self.name(): G.name("complement({})".format(self.name())) - - if self.is_immutable(): + if immutable is None: + immutable = self.is_immutable() + if immutable: return G.copy(immutable=True) return G From eb0781f5c0dcb2efb858cc95975aabbc0429ca03 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 18:07:37 +0100 Subject: [PATCH 2/8] add doctests to method complement --- src/sage/graphs/generic_graph.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index bbeb1397ccf..c02e2209d97 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19434,6 +19434,17 @@ def complement(self, immutable=None): Graph on 10 vertices sage: g.complement() Graph on 10 vertices + + Check the behovior of parameter `ìmmutable``:: + + sage: type(Graph().complement()._backend) + + sage: type(Graph().complement(immutable=True)._backend) + + sage: type(Graph(immutable=True).complement()._backend) + + sage: type(Graph(immutable=True).complement(immutable=False)._backend) + """ self._scream_if_not_simple() From df92cd547cd8d904ecabf1fb0af7a97ffa30f4af Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 18:23:09 +0100 Subject: [PATCH 3/8] fix the behavior of method to_simple --- src/sage/graphs/generic_graph.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c02e2209d97..e61029ff988 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19509,12 +19509,31 @@ def to_simple(self, to_undirected=True, keep_label='any', immutable=None): [(2, 3, 1), (3, 2, None)] sage: G.to_simple(to_undirected=False, keep_label='max').edges(sort=True) [(2, 3, 2), (3, 2, None)] + + TESTS: + + Check the behavior of parameter `ìmmutable``:: + + sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=True) + sage: H = G.to_simple() + sage: H.is_immutable() + True + sage: H.edges(labels=False) + [(0, 1)] + sage: H = G.to_simple(immutable=False) + sage: H.is_immutable() + False + sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=False) + sage: G.to_simple().is_immutable() + False + sage: G.to_simple(immutable=True).is_immutable() + True """ if to_undirected: from sage.graphs.graph import Graph - g = Graph(self) + g = Graph(self, immutable=False) else: - g = copy(self) + g = self.copy(immutable=False) g.allow_loops(False) g.allow_multiple_edges(False, keep_label=keep_label) if immutable is None: From 7ae68620c92f86e983d105ef7a8361274eb46667 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 18:30:27 +0100 Subject: [PATCH 4/8] add tests to add_clique, add_cycle, add_path --- src/sage/graphs/generic_graph.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index e61029ff988..c44dab69b67 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19271,7 +19271,16 @@ def add_clique(self, vertices, loops=False): sage: D.add_clique(range(4), loops=True) sage: D.is_clique(directed_clique=True, loops=True) True + + Immutable graph:: + + sage: Graph(immutable=True).add_clique([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ + if self.is_immutable(): + raise ValueError("graph is immutable; please change a copy instead (use function copy())") import itertools if loops: if self.is_directed(): @@ -19337,6 +19346,13 @@ def add_cycle(self, vertices): sage: G.add_cycle(['a', 'b', 'c']) sage: G.order(), G.size() (3, 3) + + Immutable graph:: + + sage: Graph(immutable=True).add_cycle([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ if vertices: self.add_path(vertices) @@ -19373,6 +19389,16 @@ def add_path(self, vertices): sage: D.add_path(list(range(4))) sage: D.edges(sort=True) [(0, 1, None), (1, 2, None), (2, 3, None)] + + TESTS: + + Immutable graph:: + + sage: Graph(immutable=True).add_path([]) + sage: Graph(immutable=True).add_path([1, 2, 3]) + Traceback (most recent call last): + ... + ValueError: graph is immutable; please change a copy instead (use function copy()) """ if not vertices: return From 597ffad4be4a72a1f8f6d64b6e7afc3e99f52c56 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 18:39:49 +0100 Subject: [PATCH 5/8] improve method union to avoid copy --- src/sage/graphs/generic_graph.py | 25 ++++++++++--------------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index c44dab69b67..ab2cc5b8e51 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19715,30 +19715,25 @@ def union(self, other, immutable=None): sage: D1.union(D2).weighted() or D2.union(D1).weighted() False """ - if (self._directed and not other._directed) or (not self._directed and other._directed): + if self._directed != other._directed: raise TypeError('both arguments must be of the same class') multiedges = self.allows_multiple_edges() or other.allows_multiple_edges() loops = self.allows_loops() or other.allows_loops() weighted = self.weighted() and other.weighted() + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() if self._directed: - from sage.graphs.digraph import DiGraph - G = DiGraph(multiedges=multiedges, loops=loops, weighted=weighted) + from sage.graphs.digraph import DiGraph as GT else: - from sage.graphs.graph import Graph - G = Graph(multiedges=multiedges, loops=loops, weighted=weighted) - G.add_vertices(self) - G.add_vertices(other) - G.add_edges(self.edge_iterator()) - G.add_edges(other.edge_iterator()) - - if immutable is None: - immutable = self.is_immutable() and other.is_immutable() - if immutable: - G = G.copy(immutable=True) + from sage.graphs.graph import Graph as GT - return G + from itertools import chain + return GT([chain(self, other), + chain(self.edge_iterator(), other.edge_iterator())], + format='vertices_and_edges', weighted=weighted, loops=loops, + multiedges=multiedges, immutable=immutable) def cartesian_product(self, other): r""" From 7e4375005c59a2edb2a2fda63300bb3f9af6ad2b Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 19:10:23 +0100 Subject: [PATCH 6/8] improve method disjoint_union --- src/sage/graphs/generic_graph.py | 45 +++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index ab2cc5b8e51..942b49f0a9c 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19616,8 +19616,27 @@ def disjoint_union(self, other, labels='pairs', immutable=None): Custom path disjoint_union Cycle graph: Graph on 5 vertices sage: J.vertices(sort=True) [(0, 'a'), (0, 'b'), (1, 0), (1, 1), (1, 2)] + + TESTS: + + Check the behavior of parameter `ìmmutable``:: + + sage: G = Graph([(0, 1)]) + sage: G.disjoint_union(G).is_immutable() + False + sage: G.disjoint_union(G, immutable=True).is_immutable() + True + sage: H = G.copy(immutable=True) + sage: H.disjoint_union(H).is_immutable() + True + sage: G.disjoint_union(G, immutable=False).is_immutable() + False + sage: H.disjoint_union(G).is_immutable() + False + sage: G.disjoint_union(G, immutable=True).is_immutable() + True """ - if (self._directed and not other._directed) or (not self._directed and other._directed): + if self._directed != other._directed: raise TypeError('both arguments must be of the same class') if labels not in ['pairs', 'integers']: @@ -19629,7 +19648,11 @@ def disjoint_union(self, other, labels='pairs', immutable=None): else: r_self = {v: (0, v) for v in self} r_other = {v: (1, v) for v in other} - G = self.relabel(r_self, inplace=False).union(other.relabel(r_other, inplace=False), immutable=immutable) + + from itertools import chain + vertices = chain(r_self.values(), r_other.values()) + edges = chain(((r_self[u], r_self[v], w) for u, v, w in self.edge_iterator()), + ((r_other[u], r_other[v], w) for u, v, w in other.edge_iterator())) a = self.name() if not a: @@ -19637,8 +19660,22 @@ def disjoint_union(self, other, labels='pairs', immutable=None): b = other.name() if not b: b = other._repr_() - G._name = '{} disjoint_union {}'.format(a, b) - return G + name = f"{a} disjoint_union {b}" + + multiedges = self.allows_multiple_edges() or other.allows_multiple_edges() + loops = self.allows_loops() or other.allows_loops() + weighted = self.weighted() and other.weighted() + if immutable is None: + immutable = self.is_immutable() and other.is_immutable() + + if self._directed: + from sage.graphs.digraph import DiGraph as GT + else: + from sage.graphs.graph import Graph as GT + + return GT([vertices, edges], format='vertices_and_edges', + weighted=weighted, loops=loops, multiedges=multiedges, + name=name, immutable=immutable) def union(self, other, immutable=None): """ From 61d9b90113f4eb07027be48c52e9a04c8349acdc Mon Sep 17 00:00:00 2001 From: dcoudert Date: Sat, 4 Jan 2025 19:20:51 +0100 Subject: [PATCH 7/8] #39280: typo --- src/sage/graphs/generic_graph.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index 942b49f0a9c..5f5ba9b69e2 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19461,7 +19461,7 @@ def complement(self, immutable=None): sage: g.complement() Graph on 10 vertices - Check the behovior of parameter `ìmmutable``:: + Check the behavior of parameter `ìmmutable``:: sage: type(Graph().complement()._backend) From f75189b2cfe70aa9852461bed026ee7a0a47ae28 Mon Sep 17 00:00:00 2001 From: dcoudert Date: Wed, 8 Jan 2025 11:50:36 +0100 Subject: [PATCH 8/8] #39280: add missing backticks --- src/sage/graphs/generic_graph.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/graphs/generic_graph.py b/src/sage/graphs/generic_graph.py index fb14afc6e0b..9375f9418b3 100644 --- a/src/sage/graphs/generic_graph.py +++ b/src/sage/graphs/generic_graph.py @@ -19473,7 +19473,7 @@ def complement(self, immutable=None): sage: g.complement() Graph on 10 vertices - Check the behavior of parameter `ìmmutable``:: + Check the behavior of parameter ``immutable``:: sage: type(Graph().complement()._backend) @@ -19550,7 +19550,7 @@ def to_simple(self, to_undirected=True, keep_label='any', immutable=None): TESTS: - Check the behavior of parameter `ìmmutable``:: + Check the behavior of parameter ``immutable``:: sage: G = Graph([(0, 0), (0, 1)] * 2, loops=True, multiedges=True, immutable=True) sage: H = G.to_simple() @@ -19631,7 +19631,7 @@ def disjoint_union(self, other, labels='pairs', immutable=None): TESTS: - Check the behavior of parameter `ìmmutable``:: + Check the behavior of parameter ``immutable``:: sage: G = Graph([(0, 1)]) sage: G.disjoint_union(G).is_immutable()