From e63a3f5c111d52a988078b16075be95d956a9e03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 28 Sep 2023 20:01:40 +0200 Subject: [PATCH 1/2] a few details in combinat (designs) --- .../designs/group_divisible_designs.py | 86 +++++++++---------- src/sage/combinat/designs/resolvable_bibd.py | 3 + src/sage/combinat/designs/twographs.py | 16 ++-- src/sage/combinat/matrices/dlxcpp.py | 9 +- src/sage/combinat/tuple.py | 5 +- 5 files changed, 63 insertions(+), 56 deletions(-) diff --git a/src/sage/combinat/designs/group_divisible_designs.py b/src/sage/combinat/designs/group_divisible_designs.py index e1d90155920..b589489cfec 100644 --- a/src/sage/combinat/designs/group_divisible_designs.py +++ b/src/sage/combinat/designs/group_divisible_designs.py @@ -24,19 +24,20 @@ --------- """ -#***************************************************************************** +# **************************************************************************** # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.arith.misc import is_prime_power -from sage.misc.unknown import Unknown +from sage.misc.unknown import Unknown from .incidence_structures import IncidenceStructure -def group_divisible_design(v,K,G,existence=False,check=False): + +def group_divisible_design(v, K, G, existence=False, check=False): r""" Return a `(v,K,G)`-Group Divisible Design. @@ -90,36 +91,31 @@ def group_divisible_design(v,K,G,existence=False,check=False): blocks = None # from a (v+1,k,1)-BIBD - if (len(G) == 1 and - len(K) == 1 and - G[0]+1 in K): + if len(G) == 1 == len(K) and G[0] + 1 in K: from .bibd import balanced_incomplete_block_design k = K[0] if existence: - return balanced_incomplete_block_design(v+1,k,existence=True) - BIBD = balanced_incomplete_block_design(v+1,k) + return balanced_incomplete_block_design(v + 1, k, existence=True) + BIBD = balanced_incomplete_block_design(v + 1, k) groups = [[x for x in S if x != v] for S in BIBD if v in S] - d = {p:i for i,p in enumerate(sum(groups,[]))} + d = {p: i for i, p in enumerate(sum(groups, []))} d[v] = v BIBD.relabel(d) - groups = [list(range((k-1)*i,(k-1)*(i+1))) for i in range(v//(k-1))] + groups = [list(range((k - 1) * i, (k - 1) * (i + 1))) + for i in range(v // (k - 1))] blocks = [S for S in BIBD if v not in S] # (v,{4},{2})-GDD - elif (v % 2 == 0 and - K == [4] and - G == [2] and - GDD_4_2(v//2,existence=True)): + elif (v % 2 == 0 and K == [4] and + G == [2] and GDD_4_2(v // 2, existence=True)): if existence: return True - return GDD_4_2(v//2,check=check) + return GDD_4_2(v // 2, check=check) # From a TD(k,g) - elif (len(G) == 1 and - len(K) == 1 and - K[0]*G[0] == v): + elif (len(G) == 1 == len(K) and K[0] * G[0] == v): from .orthogonal_arrays import transversal_design - return transversal_design(k=K[0],n=G[0],existence=existence) + return transversal_design(k=K[0], n=G[0], existence=existence) if blocks: return GroupDivisibleDesign(v, @@ -134,7 +130,8 @@ def group_divisible_design(v,K,G,existence=False,check=False): return Unknown raise NotImplementedError -def GDD_4_2(q,existence=False,check=True): + +def GDD_4_2(q, existence=False, check=True): r""" Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. @@ -180,26 +177,27 @@ def GDD_4_2(q,existence=False,check=True): return True from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - G = GF(q,'x') + G = GF(q, 'x') w = G.primitive_element() e = w**((q - 1) // 3) # A first parallel class is defined. G acts on it, which yields all others. - first_class = [[(0,0),(1,w**i),(1,e*w**i),(1,e*e*w**i)] + first_class = [[(0, 0), (1, w**i), (1, e * w**i), (1, e * e * w**i)] for i in range((q - 1) // 6)] - label = {p:i for i,p in enumerate(G)} - classes = [[[2*label[x[1]+g]+(x[0]+j) % 2 for x in S] + label = {p: i for i, p in enumerate(G)} + classes = [[[2 * label[x[1] + g] + (x[0] + j) % 2 for x in S] for S in first_class] for g in G for j in range(2)] - return GroupDivisibleDesign(2*q, - groups=[[i,i+1] for i in range(0,2*q,2)], - blocks=sum(classes,[]), - K=[4], - G=[2], - check=check, - copy=False) + return GroupDivisibleDesign(2 * q, + groups=[[i, i + 1] for i in range(0, 2 * q, 2)], + blocks=sum(classes, []), + K=[4], + G=[2], + check=check, + copy=False) + class GroupDivisibleDesign(IncidenceStructure): r""" @@ -261,9 +259,9 @@ class GroupDivisibleDesign(IncidenceStructure): sage: GDD = GroupDivisibleDesign('abcdefghiklm',None,D) sage: sorted(GDD.groups()) [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i'], ['k', 'l', 'm']] - """ - def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, check=True, copy=True,**kwds): + def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, + check=True, copy=True, **kwds): r""" Constructor function @@ -286,8 +284,7 @@ def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, check=True, check=False, **kwds) - if (groups is None or - (copy is False and self._point_to_index is None)): + if (groups is None or (copy is False and self._point_to_index is None)): self._groups = groups elif self._point_to_index is None: self._groups = [g[:] for g in groups] @@ -295,7 +292,9 @@ def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, check=True, self._groups = [[self._point_to_index[x] for x in g] for g in groups] if check or groups is None: - is_gdd = is_group_divisible_design(self._groups,self._blocks,self.num_points(),G,K,lambd,verbose=1) + is_gdd = is_group_divisible_design(self._groups, self._blocks, + self.num_points(), G, K, + lambd, verbose=1) assert is_gdd if groups is None: self._groups = is_gdd[1] @@ -337,7 +336,7 @@ def groups(self): def __repr__(self): r""" - Returns a string that describes self + Return a string that describes ``self``. EXAMPLES:: @@ -347,11 +346,10 @@ def __repr__(self): sage: GDD = GroupDivisibleDesign(40,groups,TD); GDD Group Divisible Design on 40 points of type 10^4 """ + group_sizes = [len(g) for g in self._groups] - group_sizes = [len(_) for _ in self._groups] - - gdd_type = ["{}^{}".format(s,group_sizes.count(s)) - for s in sorted(set(group_sizes))] + gdd_type = ("{}^{}".format(s, group_sizes.count(s)) + for s in sorted(set(group_sizes))) gdd_type = ".".join(gdd_type) if not gdd_type: @@ -359,4 +357,4 @@ def __repr__(self): v = self.num_points() - return "Group Divisible Design on {} points of type {}".format(v,gdd_type) + return "Group Divisible Design on {} points of type {}".format(v, gdd_type) diff --git a/src/sage/combinat/designs/resolvable_bibd.py b/src/sage/combinat/designs/resolvable_bibd.py index 4af0bed4bb6..12ca7b11c30 100644 --- a/src/sage/combinat/designs/resolvable_bibd.py +++ b/src/sage/combinat/designs/resolvable_bibd.py @@ -140,6 +140,7 @@ def resolvable_balanced_incomplete_block_design(v,k,existence=False): return Unknown raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,3)) + def kirkman_triple_system(v,existence=False): r""" Return a Kirkman Triple System on `v` points. @@ -437,6 +438,7 @@ def v_4_1_rbibd(v,existence=False): assert BIBD.is_resolvable() return BIBD + def PBD_4_7(v,check=True, existence=False): r""" Return a `(v,\{4,7\})`-PBD @@ -683,6 +685,7 @@ def PBD_4_7(v,check=True, existence=False): check=check, copy=False) + def PBD_4_7_from_Y(gdd,check=True): r""" Return a `(3v+1,\{4,7\})`-PBD from a `(v,\{4,5,7\},\NN-\{3,6,10\})`-GDD. diff --git a/src/sage/combinat/designs/twographs.py b/src/sage/combinat/designs/twographs.py index 5c9a90afbcd..54d684cb9d2 100644 --- a/src/sage/combinat/designs/twographs.py +++ b/src/sage/combinat/designs/twographs.py @@ -59,6 +59,7 @@ from sage.combinat.designs.incidence_structures import IncidenceStructure from itertools import combinations + class TwoGraph(IncidenceStructure): r""" Two-graphs class. @@ -199,9 +200,10 @@ def taylor_twograph(q): from sage.graphs.generators.classical_geometries import TaylorTwographSRG return TaylorTwographSRG(q).twograph() -def is_twograph(T): + +def is_twograph(T) -> bool: r""" - Checks that the incidence system `T` is a two-graph + Check whether the incidence system `T` is a two-graph. INPUT: @@ -237,7 +239,7 @@ def is_twograph(T): return False # A structure for a fast triple existence check - v_to_blocks = {v:set() for v in range(T.num_points())} + v_to_blocks = {v: set() for v in range(T.num_points())} for B in T._blocks: B = frozenset(B) for x in B: @@ -248,8 +250,8 @@ def has_triple(x_y_z): return bool(v_to_blocks[x] & v_to_blocks[y] & v_to_blocks[z]) # Check that every quadruple contains an even number of triples - for quad in combinations(range(T.num_points()),4): - if sum(map(has_triple,combinations(quad,3))) % 2 == 1: + for quad in combinations(range(T.num_points()), 4): + if sum(map(has_triple, combinations(quad, 3))) % 2 == 1: return False return True @@ -296,10 +298,10 @@ def twograph_descendant(G, v, name=None): sage: twograph_descendant(p, 5, name=True) descendant of Petersen graph at 5: Graph on 9 vertices """ - G = G.seidel_switching(G.neighbors(v),inplace=False) + G = G.seidel_switching(G.neighbors(v), inplace=False) G.delete_vertex(v) if name: - G.name('descendant of '+G.name()+' at '+str(v)) + G.name('descendant of ' + G.name() + ' at ' + str(v)) else: G.name('') return G diff --git a/src/sage/combinat/matrices/dlxcpp.py b/src/sage/combinat/matrices/dlxcpp.py index e235f885188..fce16e7d2ad 100644 --- a/src/sage/combinat/matrices/dlxcpp.py +++ b/src/sage/combinat/matrices/dlxcpp.py @@ -1,7 +1,7 @@ """ Dancing links C++ wrapper """ -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2008 Carlo Hamalainen , # # Distributed under the terms of the GNU General Public License (GPL) @@ -13,14 +13,15 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** # OneExactCover and AllExactCovers are almost exact copies of the # functions with the same name in sage/combinat/dlx.py by Tom Boothby. from .dancing_links import dlx_solver + def DLXCPP(rows): """ Solves the Exact Cover problem by using the Dancing Links algorithm @@ -85,6 +86,7 @@ def DLXCPP(rows): while x.search(): yield x.get_solution() + def AllExactCovers(M): """ Solves the exact cover problem on the matrix M (treated as a dense @@ -112,6 +114,7 @@ def AllExactCovers(M): for s in DLXCPP(rows): yield [M.row(i) for i in s] + def OneExactCover(M): """ Solves the exact cover problem on the matrix M (treated as a dense diff --git a/src/sage/combinat/tuple.py b/src/sage/combinat/tuple.py index 95ae68b040c..8336fb4cbb6 100644 --- a/src/sage/combinat/tuple.py +++ b/src/sage/combinat/tuple.py @@ -15,13 +15,14 @@ # # https://www.gnu.org/licenses/ # **************************************************************************** +from itertools import product, combinations_with_replacement from sage.arith.misc import binomial +from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets from sage.rings.integer_ring import ZZ from sage.structure.parent import Parent from sage.structure.unique_representation import UniqueRepresentation -from sage.categories.finite_enumerated_sets import FiniteEnumeratedSets -from itertools import product, combinations_with_replacement + class Tuples(Parent, UniqueRepresentation): """ From be29cd1213d561a73764a7e52aeaed11b58a86ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Fri, 29 Sep 2023 17:54:30 +0200 Subject: [PATCH 2/2] suggested details --- src/sage/combinat/designs/group_divisible_designs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/sage/combinat/designs/group_divisible_designs.py b/src/sage/combinat/designs/group_divisible_designs.py index b589489cfec..9a17d42f114 100644 --- a/src/sage/combinat/designs/group_divisible_designs.py +++ b/src/sage/combinat/designs/group_divisible_designs.py @@ -113,7 +113,7 @@ def group_divisible_design(v, K, G, existence=False, check=False): return GDD_4_2(v // 2, check=check) # From a TD(k,g) - elif (len(G) == 1 == len(K) and K[0] * G[0] == v): + elif len(G) == 1 == len(K) and K[0] * G[0] == v: from .orthogonal_arrays import transversal_design return transversal_design(k=K[0], n=G[0], existence=existence) @@ -176,8 +176,8 @@ def GDD_4_2(q, existence=False, check=True): if existence: return True - from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF - G = GF(q, 'x') + from sage.rings.finite_rings.finite_field_constructor import FiniteField + G = FiniteField(q, 'x') w = G.primitive_element() e = w**((q - 1) // 3) @@ -284,7 +284,7 @@ def __init__(self, points, groups, blocks, G=None, K=None, lambd=1, check=False, **kwds) - if (groups is None or (copy is False and self._point_to_index is None)): + if groups is None or (copy is False and self._point_to_index is None): self._groups = groups elif self._point_to_index is None: self._groups = [g[:] for g in groups]