def CremonaRichmondConfiguration(): r""" Return the Cremona-Richmond configuration The Cremona-Richmond configuration is a set system whose incidence graph is equal to the :meth:`~sage.graphs.graph_generators.GraphGenerators.TutteCoxeterGraph`. It is a generalized quadrangle of parameters `(2,2)`. For more information, see the :wikipedia:`Cremona-Richmond_configuration`. EXAMPLE:: sage: H = designs.CremonaRichmondConfiguration(); H Incidence structure with 15 points and 15 blocks sage: g = graphs.TutteCoxeterGraph() sage: H.incidence_graph().is_isomorphic(g) True """ from sage.graphs.generators.smallgraphs import TutteCoxeterGraph from sage.combinat.designs.incidence_structures import IncidenceStructure g = TutteCoxeterGraph() H = IncidenceStructure([g.neighbors(v) for v in g.bipartite_sets()[0]]) H.relabel() return H
def CremonaRichmondConfiguration(): r""" Return the Cremona-Richmond configuration The Cremona-Richmond configuration is a set system whose incidence graph is equal to the :meth:`~sage.graphs.graph_generators.GraphGenerators.TutteCoxeterGraph`. It is a generalized quadrangle of parameters `(2,2)`. For more information, see the :wikipedia:`Cremona-Richmond_configuration`. EXAMPLES:: sage: H = designs.CremonaRichmondConfiguration(); H Incidence structure with 15 points and 15 blocks sage: g = graphs.TutteCoxeterGraph() sage: H.incidence_graph().is_isomorphic(g) True """ from sage.graphs.generators.smallgraphs import TutteCoxeterGraph from sage.combinat.designs.incidence_structures import IncidenceStructure g = TutteCoxeterGraph() H = IncidenceStructure([g.neighbors(v) for v in g.bipartite_sets()[0]]) H.relabel() return H
def __init__(self, points=None, blocks=None, incidence_matrix=None, name=None, check=False, copy=True): r""" Constructor of the class TESTS:: sage: from sage.combinat.designs.twographs import TwoGraph sage: TwoGraph([[1,2]]) Incidence structure with 2 points and 1 blocks sage: TwoGraph([[1,2]], check=True) Traceback (most recent call last): ... AssertionError: the structure is not a 2-graph! sage: p=graphs.PetersenGraph().twograph() sage: TwoGraph(p, check=True) Incidence structure with 10 points and 60 blocks """ IncidenceStructure.__init__(self, points=points, blocks=blocks, incidence_matrix=incidence_matrix, name=name, check=False, copy=copy) if check: # it is a very slow, O(|points|^4), test... assert is_twograph(self), "the structure is not a 2-graph!"
def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator='', timestamp=''): """ EXAMPLES: sage: C=CoveringDesign(5,4,3,4,range(5),[[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4]],4, 'Lexicographic Covering') sage: print C C(5,4,3) = 4 Method: Lexicographic Covering 0 1 2 3 0 1 2 4 0 1 3 4 0 2 3 4 """ self.__v = v self.__k = k self.__t = t self.__size = size if low_bd > 0: self.__low_bd = low_bd else: self.__low_bd = schonheim(v, k, t) self.__method = method self.__creator = creator self.__timestamp = timestamp self.__incidence_structure = IncidenceStructure(points, blocks)
def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator ='',timestamp=''): """ EXAMPLES:: sage: C=CoveringDesign(5,4,3,4,range(5),[[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4]],4, 'Lexicographic Covering') sage: print(C) C(5,4,3) = 4 Method: Lexicographic Covering 0 1 2 3 0 1 2 4 0 1 3 4 0 2 3 4 """ self.__v = v self.__k = k self.__t = t self.__size = size if low_bd > 0: self.__low_bd = low_bd else: self.__low_bd = schonheim(v,k,t) self.__method = method self.__creator = creator self.__timestamp = timestamp self.__incidence_structure = IncidenceStructure(points, blocks)
def underlying_hypergraph(self): r""" Return the underlying hypergraph of cocircuits of the vector configuration. Edges of the hypergraph correspond to zeros in a cocircuit. OUTPUT: A hypergraph EXAMPLES:: sage: from cn_hyperarr.vector_classes import * sage: vc = VectorConfiguration([[1,0,0],[0,1,0],[0,0,1],[1,1,0],[0,1,1],[1,1,1]]) sage: vc.underlying_hypergraph() Incidence structure with 6 points and 7 blocks For a set of three linearly dependent vectors in dimension three there is just one cocicuit and thus one block:: sage: vc = VectorConfiguration([[1,0,0],[0,1,0],[2,-1,0]]) sage: vc.underlying_hypergraph() Incidence structure with 3 points and 1 blocks """ coc = set([v[1] for v in self.three_dim_cocircuits()]) return IncidenceStructure(coc)
def Hadamard3Design(n): r""" Return the Hadamard 3-design with parameters `3-(n, \frac n 2, \frac n 4 - 1)`. This is the unique extension of the Hadamard `2`-design (see :meth:`HadamardDesign`). We implement the description from pp. 12 in [CvL]_. INPUT: - ``n`` (integer) -- a multiple of 4 such that `n>4`. EXAMPLES:: sage: designs.Hadamard3Design(12) Incidence structure with 12 points and 22 blocks We verify that any two blocks of the Hadamard `3`-design `3-(8, 4, 1)` design meet in `0` or `2` points. More generally, it is true that any two blocks of a Hadamard `3`-design meet in `0` or `\frac{n}{4}` points (for `n > 4`). :: sage: D = designs.Hadamard3Design(8) sage: N = D.incidence_matrix() sage: N.transpose()*N [4 2 2 2 2 2 2 2 2 2 2 2 2 0] [2 4 2 2 2 2 2 2 2 2 2 2 0 2] [2 2 4 2 2 2 2 2 2 2 2 0 2 2] [2 2 2 4 2 2 2 2 2 2 0 2 2 2] [2 2 2 2 4 2 2 2 2 0 2 2 2 2] [2 2 2 2 2 4 2 2 0 2 2 2 2 2] [2 2 2 2 2 2 4 0 2 2 2 2 2 2] [2 2 2 2 2 2 0 4 2 2 2 2 2 2] [2 2 2 2 2 0 2 2 4 2 2 2 2 2] [2 2 2 2 0 2 2 2 2 4 2 2 2 2] [2 2 2 0 2 2 2 2 2 2 4 2 2 2] [2 2 0 2 2 2 2 2 2 2 2 4 2 2] [2 0 2 2 2 2 2 2 2 2 2 2 4 2] [0 2 2 2 2 2 2 2 2 2 2 2 2 4] REFERENCES: .. [CvL] \P. Cameron, J. H. van Lint, Designs, graphs, codes and their links, London Math. Soc., 1991. """ if n == 1 or n == 4: raise ValueError("The Hadamard design with n = %s does not extend to a three design." % n) from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix, block_matrix H = hadamard_matrix(n) #assumed to be normalised. H1 = H.matrix_from_columns(range(1, n)) J = matrix(ZZ, n, n-1, [1]*(n-1)*n) A1 = (H1+J)/2 A2 = (J-H1)/2 A = block_matrix(1, 2, [A1, A2]) #the incidence matrix of the design. return IncidenceStructure(incidence_matrix=A, name="HadamardThreeDesign")
def __init__(self, points=None, blocks=None, incidence_matrix=None, name=None, check=False, copy=True): r""" Constructor of the class TESTS:: sage: from sage.combinat.designs.twographs import TwoGraph sage: TwoGraph([[1,2]]) Incidence structure with 2 points and 1 blocks sage: TwoGraph([[1,2]], check=True) Traceback (most recent call last): ... AssertionError: the structure is not a 2-graph! sage: p=graphs.PetersenGraph().twograph() sage: TwoGraph(p, check=True) Incidence structure with 10 points and 60 blocks """ IncidenceStructure.__init__(self, points=points, blocks=blocks, incidence_matrix=incidence_matrix, name=name, check=False, copy=copy) if check: # it is a very slow, O(|points|^4), test... assert is_twograph(self), "the structure is not a 2-graph!"
def __init__(self, edge_set, vertex_sizes, num_locs, initial_distribution=None): if (initial_distribution is None): self.even_dist = True else: self.even_dist = False self.initial_distribution = initial_distribution self.vertex_sizes = vertex_sizes assert len(edge_set.values()) > 0 assert len(list(edge_set.values())[0]) == 3 blocks = [k for (_, k, _) in edge_set.values()] firsts = [k_0[0] for k_0 in blocks] # Ensure that all elements are assigned to at most once # Assumes that all operations are assignments, which precludes # in-place operations assert len(set(firsts)) == len(firsts) self.hypergraph = IS(blocks) # Make sure the big vertex list only includes vertices in the # edge set ground_set = set(self.hypergraph.ground_set()) assert ground_set == (ground_set | set(vertex_sizes[0] + vertex_sizes[1] + vertex_sizes[2] + vertex_sizes[3])) self.edge_set = edge_set lhs = {l[0]: edge for (edge, (_, l, _)) in self.edge_set.items()} partial_order = {k: set([]) for k in self.edge_set.keys()} for name, (_, var_s, _) in self.edge_set.items(): for i in var_s[1:]: try: if lhs[i] != name: partial_order[name].add(lhs[i]) except KeyError: partial_order[name].add('_begin_') self.partial_order = DiGraph(partial_order).reverse() vertex_set = self.hypergraph.ground_set() self.output_size_calculators = { 2: { 'add': self.add_output_size, 'mul': self.mul_output_size } } self.vertices = {} self.init_vertices(vertex_set)
def HadamardDesign(n): """ As described in Section 1, p. 10, in [CvL]_. The input n must have the property that there is a Hadamard matrix of order `n+1` (and that a construction of that Hadamard matrix has been implemented...). EXAMPLES:: sage: designs.HadamardDesign(7) Incidence structure with 7 points and 7 blocks sage: print(designs.HadamardDesign(7)) Incidence structure with 7 points and 7 blocks For example, the Hadamard 2-design with `n = 11` is a design whose parameters are 2-(11, 5, 2). We verify that `NJ = 5J` for this design. :: sage: D = designs.HadamardDesign(11); N = D.incidence_matrix() sage: J = matrix(ZZ, 11, 11, [1]*11*11); N*J [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] [5 5 5 5 5 5 5 5 5 5 5] REFERENCES: - [CvL] P. Cameron, J. H. van Lint, Designs, graphs, codes and their links, London Math. Soc., 1991. """ from sage.combinat.matrices.hadamard_matrix import hadamard_matrix from sage.matrix.constructor import matrix H = hadamard_matrix(n + 1) #assumed to be normalised. H1 = H.matrix_from_columns(range(1, n + 1)) H2 = H1.matrix_from_rows(range(1, n + 1)) J = matrix(ZZ, n, n, [1] * n * n) MS = J.parent() A = MS((H2 + J) / 2) # convert -1's to 0's; coerce entries to ZZ # A is the incidence matrix of the block design return IncidenceStructure(incidence_matrix=A, name="HadamardDesign")
def CompleteUniform(self, n, k): r""" Return the complete `k`-uniform hypergraph on `n` points. INPUT: - ``k,n`` -- nonnegative integers with `k\leq n` EXAMPLES:: sage: h = hypergraphs.CompleteUniform(5,2); h Incidence structure with 5 points and 10 blocks sage: len(h.packing()) 2 """ from sage.combinat.designs.incidence_structures import IncidenceStructure from itertools import combinations return IncidenceStructure(list(combinations(range(n), k)))
class Problem: """ Highest level object for the tiling solver INPUT: - edge_set -- A dictionary containing tuples of (op_name, [inputs/outputs to operation] as sets, expression from AST) We use the list of inputs/outputs to feed into the construction of the hypergraph. -- NOTE -- Order for the inputs/outputs is important in the edge set, as the order informs how the cost function calculates cost. However, since we don't allow any variable to be assigned more than once, the fact that the IncidenceStructure modifies the order of the lists of inputs/outputs doesn't matter. Input matrices to an operation shouldn't be assigned later since they were assigned at initialization/being passed into the function. Output matrices shouldn't be assigned again either, obviously, meaning this set of inputs/outputs is guaranteed to be unique, and equality can be tested by vertex membership agnostic of the order. -- NOTE -- - vertex_sizes -- A tuple of lists of variable_names. The tuple is structured as (big_big, big_small, small_big, small_small) - num_locs -- Number of localities to be used in computation - (Optional) initial_distribution -- Dictionary for sets of localities each beginning distributed matrix is spread across """ def __init__(self, edge_set, vertex_sizes, num_locs, initial_distribution=None): if (initial_distribution is None): self.even_dist = True else: self.even_dist = False self.initial_distribution = initial_distribution self.vertex_sizes = vertex_sizes assert len(edge_set.values()) > 0 assert len(list(edge_set.values())[0]) == 3 blocks = [k for (_, k, _) in edge_set.values()] firsts = [k_0[0] for k_0 in blocks] # Ensure that all elements are assigned to at most once # Assumes that all operations are assignments, which precludes # in-place operations assert len(set(firsts)) == len(firsts) self.hypergraph = IS(blocks) # Make sure the big vertex list only includes vertices in the # edge set ground_set = set(self.hypergraph.ground_set()) assert ground_set == (ground_set | set(vertex_sizes[0] + vertex_sizes[1] + vertex_sizes[2] + vertex_sizes[3])) self.edge_set = edge_set lhs = {l[0]: edge for (edge, (_, l, _)) in self.edge_set.items()} partial_order = {k: set([]) for k in self.edge_set.keys()} for name, (_, var_s, _) in self.edge_set.items(): for i in var_s[1:]: try: if lhs[i] != name: partial_order[name].add(lhs[i]) except KeyError: partial_order[name].add('_begin_') self.partial_order = DiGraph(partial_order).reverse() vertex_set = self.hypergraph.ground_set() self.output_size_calculators = { 2: { 'add': self.add_output_size, 'mul': self.mul_output_size } } self.vertices = {} self.init_vertices(vertex_set) def init_vertices(self, vertex_set): print("Inside init_vertices") for level_set in self.partial_order.level_sets()[1:]: for edge in level_set: op, vars_, expr = self.edge_set[edge] output_size_func = self.output_size_calculators[len( vars_[1:])][op] operands = [] for k in vars_[1:]: if k in self.vertex_sizes[0]: size = MatrixSize.large_large elif k in self.vertex_sizes[1]: size = MatrixSize.large_small elif k in self.vertex_sizes[2]: size = MatrixSize.small_large elif k in self.vertex_sizes[3]: size = MatrixSize.small_small else: raise ValueError('Error retrieving matrix size') if not self.even_dist: if k in initial_distribution.keys(): operands.append((size, initial_distribution[k])) else: operands.append((size, None)) out_size = output_size_func(operands) if out_size[0] == MatrixSize.large_large: self.vertex_sizes[0].append(vars_[0]) elif out_size[0] == MatrixSize.large_small: self.vertex_sizes[1].append(vars_[0]) elif out_size[0] == MatrixSize.small_large: self.vertex_sizes[2].append(vars_[0]) elif out_size[0] == MatrixSize.small_small: self.vertex_sizes[3].append(vars_[0]) def add_output_size(self, operands): lhs = operands[0] rhs = operands[1] assert lhs[0] == rhs[0], "Add operation should have equal size" assert lhs[1] == rhs[1], "Add operation should have aligned tiles" return lhs def mul_output_size(self, operands): assert len(operands), 'Matrix multiplication takes two arguments' lhs_size = operands[0][0] rhs_size = operands[1][0] out_size = None if lhs_size == MatrixSize.large_large: if rhs_size == MatrixSize.large_large: out_size = MatrixSize.large_large elif rhs_size == MatrixSize.large_small: out_size = MatrixSize.large_small elif lhs_size == MatrixSize.large_small: if rhs_size == MatrixSize.small_large: out_size = MatrixSize.large_large elif rhs_size == MatrixSize.small_small: out_size = MatrixSize.large_small elif lhs_size == MatrixSize.small_large: if rhs_size == MatrixSize.large_large: out_size = MatrixSize.small_large elif rhs_size == MatrixSize.large_small: out_size = MatrixSize.small_small elif lhs_size == MatrixSize.small_small: if rhs_size == MatrixSize.small_large: out_size = MatrixSize.small_large elif rhs_size == MatrixSize.small_small: out_size = MatrixSize.small_small if out_size is None: raise ValueError('Matrix size mismatch {0}, {1}'.format( lhs_size, rhs_size)) else: # Copy tiling from LHS return (out_size, operands[0][1])
def AhrensSzekeresGeneralizedQuadrangleGraph(q, dual=False): r""" Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual Let `q` be an odd prime power. `AS(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.5 in [PT09]_. Its points are elements of `F_q^3`, and lines are sets of size `q` of the form * `\{ (\sigma, a, b) \mid \sigma\in F_q \}` * `\{ (a, \sigma, b) \mid \sigma\in F_q \}` * `\{ (c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma) \mid \sigma\in F_q \}`, where `a`, `b`, `c` are arbitrary elements of `F_q`. INPUT: - ``q`` -- a power of an odd prime number - ``dual`` -- if ``False`` (default), return the collinearity graph of `AS(q)`. Otherwise return the collinearity graph of the dual `AS(q)`. EXAMPLES:: sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5); g AS(5); GQ(4, 6): Graph on 125 vertices sage: g.is_strongly_regular(parameters=True) (125, 28, 3, 7) sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5,dual=True); g AS(5)*; GQ(6, 4): Graph on 175 vertices sage: g.is_strongly_regular(parameters=True) (175, 30, 5, 5) REFERENCE: .. [GQwiki] `Generalized quadrangle <http://en.wikipedia.org/wiki/Generalized_quadrangle>`__ .. [PT09] \S. Payne, J. A. Thas. Finite generalized quadrangles. European Mathematical Society, 2nd edition, 2009. """ from sage.combinat.designs.incidence_structures import IncidenceStructure p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q must be an odd prime power') F = FiniteField(q, 'a') L = [] for a in F: for b in F: L.append(tuple(map(lambda s: (s, a, b), F))) L.append(tuple(map(lambda s: (a, s, b), F))) for c in F: L.append(tuple(map(lambda s: (c*s**2 - b*s + a, -2*c*s + b, s), F))) if dual: G = IncidenceStructure(L).intersection_graph() G.name('AS('+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G
def UniformRandomUniform(self, n, k, m): r""" Return a uniformly sampled `k`-uniform hypergraph on `n` points with `m` hyperedges. - ``n`` -- number of nodes of the graph - ``k`` -- uniformity - ``m`` -- number of edges EXAMPLES:: sage: H = hypergraphs.UniformRandomUniform(52, 3, 17) sage: H Incidence structure with 52 points and 17 blocks sage: H.is_connected() False TESTS:: sage: hypergraphs.UniformRandomUniform(-52, 3, 17) Traceback (most recent call last): ... ValueError: number of vertices should be non-negative sage: hypergraphs.UniformRandomUniform(52.9, 3, 17) Traceback (most recent call last): ... ValueError: number of vertices should be an integer sage: hypergraphs.UniformRandomUniform(52, -3, 17) Traceback (most recent call last): ... ValueError: the uniformity should be non-negative sage: hypergraphs.UniformRandomUniform(52, I, 17) Traceback (most recent call last): ... ValueError: the uniformity should be an integer """ from sage.rings.integer import Integer from sage.combinat.subset import Subsets from sage.misc.prandom import sample # Construct the vertex set if n < 0: raise ValueError("number of vertices should be non-negative") try: nverts = Integer(n) except TypeError: raise ValueError("number of vertices should be an integer") vertices = range(nverts) # Construct the edge set if k < 0: raise ValueError("the uniformity should be non-negative") try: uniformity = Integer(k) except TypeError: raise ValueError("the uniformity should be an integer") all_edges = Subsets(vertices, uniformity) try: edges = sample(all_edges, m) except OverflowError: raise OverflowError( "binomial({}, {}) too large to be treated".format(n, k)) except ValueError: raise ValueError( "number of edges m must be between 0 and binomial({}, {})". format(n, k)) from sage.combinat.designs.incidence_structures import IncidenceStructure return IncidenceStructure(points=vertices, blocks=edges)
class CoveringDesign(SageObject): """ Covering design. INPUT: - ``v``, ``k``, ``t`` -- integer parameters of the covering design - ``size`` (integer) - ``points`` -- list of points (default points are `[0, ..., v-1]`) - ``blocks`` - ``low_bd`` (integer) -- lower bound for such a design - ``method``, ``creator``, ``timestamp`` -- database information """ def __init__(self, v=0, k=0, t=0, size=0, points=None, blocks=None, low_bd=0, method='', creator='', timestamp=''): """ EXAMPLES:: sage: C = CoveringDesign(5, 4, 3, 4, range(5), [[0, 1, 2, 3], ....: [0, 1, 2, 4], [0, 1, 3, 4], [0, 2, 3, 4]], 4, ....: 'Lexicographic Covering') sage: print(C) C(5, 4, 3) = 4 Method: Lexicographic Covering 0 1 2 3 0 1 2 4 0 1 3 4 0 2 3 4 """ self.__v = v self.__k = k self.__t = t self.__size = size if low_bd > 0: self.__low_bd = low_bd else: self.__low_bd = schonheim(v, k, t) self.__method = method self.__creator = creator self.__timestamp = timestamp if points is None: points = [] if blocks is None: blocks = [] self.__incidence_structure = IncidenceStructure(points, blocks) def __repr__(self): """ Return the representation of this covering design. This has the parameters but not the blocks. EXAMPLES:: sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C (7, 3, 2)-covering design of size 7 Lower bound: 7 Method: Projective Plane """ repr = ('(%d, %d, %d)-covering design of size %d\n' % (self.__v, self.__k, self.__t, self.__size)) repr += 'Lower bound: %d\n' % (self.__low_bd) if self.__creator: repr += 'Created by: %s\n' % (self.__creator) if self.__method: repr += 'Method: %s\n' % (self.__method) if self.__timestamp: repr += 'Submitted on: %s\n' % (self.__timestamp) return repr[:-1] def __str__(self): """ Return the string for this covering design. This has the parameters and the blocks in readable form. EXAMPLES:: sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: print(C) C(7, 3, 2) = 7 Method: Projective Plane 0 1 2 0 3 4 0 5 6 1 3 5 1 4 6 2 3 6 2 4 5 """ if self.__size == self.__low_bd: # check if covering is optimal repr = ('C(%d, %d, %d) = %d\n' % (self.__v, self.__k, self.__t, self.__size)) else: repr = ('%d <= C(%d, %d, %d) <= %d\n' % (self.__low_bd, self.__v, self.__k, self.__t, self.__size)) if self.__creator: repr += 'Created by: %s\n' % (self.__creator) if self.__method: repr += 'Method: %s\n' % (self.__method) if self.__timestamp: repr += 'Submitted on: %s\n' % (self.__timestamp) return repr + '\n'.join( ' '.join(str(k) for k in block) for block in self.__incidence_structure.blocks()) def is_covering(self): """ Check all `t`-sets are in fact covered by the blocks of ``self``. .. NOTE:: This is very slow and wasteful of memory. EXAMPLES:: sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.is_covering() True sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], ....: [2, 4, 6]], 0, 'not a covering') # last block altered sage: C.is_covering() False """ v = self.__v k = self.__k t = self.__t Svt = Combinations(range(v), t) Skt = Combinations(range(k), t) tset = {} # tables of t-sets: False = uncovered, True = covered for i in Svt: tset[tuple(i)] = False # mark all t-sets covered by each block for a in self.__incidence_structure.blocks(): for z in Skt: y = (a[x] for x in z) tset[tuple(y)] = True for i in Svt: if not tset[tuple(i)]: # uncovered return False return True # everything was covered def v(self): """ Return `v`, the number of points in the covering design. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.v() 7 """ return self.__v def k(self): """ Return `k`, the size of blocks of the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.k() 3 """ return self.__k def t(self): """ Return `t`, the size of sets which must be covered by the blocks of the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.t() 2 """ return self.__t def size(self): """ Return the number of blocks in the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.size() 7 """ return self.__size def low_bd(self): """ Return a lower bound for the number of blocks a covering design with these parameters could have. Typically this is the Schonheim bound, but for some parameters better bounds have been shown. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.low_bd() 7 """ return self.__low_bd def method(self): """ Return the method used to create the covering design. This field is optional, and is used in a database to give information about how coverings were constructed. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: C.method() 'Projective Plane' """ return self.__method def creator(self): """ Return the creator of the covering design This field is optional, and is used in a database to give attribution for the covering design It can refer to the person who submitted it, or who originally gave a construction EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], ....: [2, 4, 5]],0, 'Projective Plane', 'Gino Fano') sage: C.creator() 'Gino Fano' """ return self.__creator def timestamp(self): """ Return the time that the covering was submitted to the database EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]],0, 'Projective Plane', ....: 'Gino Fano', '1892-01-01 00:00:00') sage: C.timestamp() # No exact date known; in Fano's 1892 article '1892-01-01 00:00:00' """ return self.__timestamp def incidence_structure(self): """ Return the incidence structure of this design, without extra parameters. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C = CoveringDesign(7, 3, 2, 7, range(7), [[0, 1, 2], ....: [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], ....: [2, 3, 6], [2, 4, 5]], 0, 'Projective Plane') sage: D = C.incidence_structure() sage: D.ground_set() [0, 1, 2, 3, 4, 5, 6] sage: D.blocks() [[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]] """ return self.__incidence_structure
def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of the generalized quadrangle `T_2^*(q)`, or of its dual Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a `hyperoval <http://en.wikipedia.org/wiki/Oval_(projective_plane)#Even_q>`__ `O \subset \Pi`. The points of `T_2^*(q):=T_2^*(O)` are the points of `\Theta` outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` that meet `\Pi` in a point of `O`. INPUT: - ``q`` -- a power of two - ``dual`` -- if ``False`` (default), return the graph of `T_2^*(O)`. Otherwise return the graph of the dual `T_2^*(O)`. - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane meeting every line in 0 or 2 points) in the plane of points with 0th coordinate 0 in `PG(3,q)` over the field ``field``. Each point of ``hyperoval`` must be a length 4 vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and ``field`` are not specified, and constructed on the fly. In particular, ``hyperoval`` we build is the classical one, i.e. a conic with the point of intersection of its tangent lines. - ``field`` -- an instance of a finite field of order `q`, must be provided if ``hyperoval`` is provided. - ``check_hyperoval`` -- (default: ``True``) if ``True``, check ``hyperoval`` for correctness. EXAMPLES: using the built-in construction:: sage: g=graphs.T2starGeneralizedQuadrangleGraph(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4,dual=True); g T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) supplying your own hyperoval:: sage: F=GF(4,'b') sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) TESTS:: sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval """ from sage.combinat.designs.incidence_structures import IncidenceStructure from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG from sage.modules.free_module_element import free_module_element as vector p, k = is_prime_power(q,get_data=True) if k==0 or p!=2: raise ValueError('q must be a power of 2') if field is None: F = FiniteField(q, 'a') else: F = field Theta = PG(3, 1, F, point_coordinates=1) Pi = set(filter(lambda x: x[0]==F.zero(), Theta.ground_set())) if hyperoval is None: O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) O = set(O) else: map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) if check_hyperoval: if len(O) != q+2: raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): if not len(O.intersection(L)) in [0,2]: raise RuntimeError("incorrect hyperoval") L = map(lambda z: filter(lambda y: not y in O, z), filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) if dual: G = IncidenceStructure(L).intersection_graph() G.name('T2*(O,'+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('T2*(O,'+str(q)+'); GQ'+str((q-1,q+1))) return G
class CoveringDesign(SageObject): """ Covering design. INPUT: - ``v,k,t`` -- integer parameters of the covering design. - ``size`` (integer) - ``points`` -- list of points (default points are `[0,...v-1]`). - ``blocks`` - ``low_bd`` (integer) -- lower bound for such a design - ``method, creator, timestamp`` -- database information. """ def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator ='',timestamp=''): """ EXAMPLES:: sage: C=CoveringDesign(5,4,3,4,range(5),[[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4]],4, 'Lexicographic Covering') sage: print(C) C(5,4,3) = 4 Method: Lexicographic Covering 0 1 2 3 0 1 2 4 0 1 3 4 0 2 3 4 """ self.__v = v self.__k = k self.__t = t self.__size = size if low_bd > 0: self.__low_bd = low_bd else: self.__low_bd = schonheim(v,k,t) self.__method = method self.__creator = creator self.__timestamp = timestamp self.__incidence_structure = IncidenceStructure(points, blocks) def __repr__(self): """ A print method, giving the parameters and any other information about the covering (but not the blocks). EXAMPLES:: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C (7,3,2)-covering design of size 7 Lower bound: 7 Method: Projective Plane """ repr = '(%d,%d,%d)-covering design of size %d\n' % (self.__v, self.__k, self.__t, self.__size) repr += 'Lower bound: %d\n' % (self.__low_bd) if self.__creator != '': repr += 'Created by: %s\n' % (self.__creator) if self.__method != '': repr += 'Method: %s\n' % (self.__method) if self.__timestamp != '': repr += 'Submitted on: %s\n' % (self.__timestamp) return repr def __str__(self): """ A print method, displaying a covering design's parameters and blocks. OUTPUT: covering design parameters and blocks, in a readable form EXAMPLES:: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: print(C) C(7,3,2) = 7 Method: Projective Plane 0 1 2 0 3 4 0 5 6 1 3 5 1 4 6 2 3 6 2 4 5 """ if self.__size == self.__low_bd: # check if the covering is known to be optimal repr = 'C(%d,%d,%d) = %d\n'%(self.__v,self.__k,self.__t,self.__size) else: repr = '%d <= C(%d,%d,%d) <= %d\n'%(self.__low_bd,self.__v,self.__k,self.__t,self.__size); if self.__creator != '': repr += 'Created by: %s\n'%(self.__creator) if self.__method != '': repr += 'Method: %s\n'%(self.__method) if self.__timestamp != '': repr += 'Submitted on: %s\n'%(self.__timestamp) for i in range(self.__size): for j in range(self.__k): repr = repr + str(self.__incidence_structure.blocks()[i][j]) + ' ' repr += '\n' return repr def is_covering(self): """ Checks that all `t`-sets are in fact covered by the blocks of ``self`` .. NOTE:: This is very slow and wasteful of memory. EXAMPLES:: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.is_covering() True sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 6]],0, 'not a covering') # last block altered sage: C.is_covering() False """ v = self.__v k = self.__k t = self.__t Svt = Combinations(range(v),t) Skt = Combinations(range(k),t) tset = {} # tables of t-sets: False = uncovered, True = covered for i in Svt: tset[tuple(i)] = False # mark all t-sets covered by each block for a in self.__incidence_structure.blocks(): for z in Skt: y = [a[x] for x in z] tset[tuple(y)] = True for i in Svt: if not tset[tuple(i)]: # uncovered return False return True # everything was covered def v(self): """ Return `v`, the number of points in the covering design. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.v() 7 """ return self.__v def k(self): """ Return `k`, the size of blocks of the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.k() 3 """ return self.__k def t(self): """ Return `t`, the size of sets which must be covered by the blocks of the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.t() 2 """ return self.__t def size(self): """ Return the number of blocks in the covering design EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.size() 7 """ return self.__size def low_bd(self): """ Return a lower bound for the number of blocks a covering design with these parameters could have. Typically this is the Schonheim bound, but for some parameters better bounds have been shown. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.low_bd() 7 """ return self.__low_bd def method(self): """ Return the method used to create the covering design This field is optional, and is used in a database to give information about how coverings were constructed EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.method() 'Projective Plane' """ return self.__method def creator(self): """ Return the creator of the covering design This field is optional, and is used in a database to give attribution for the covering design It can refer to the person who submitted it, or who originally gave a construction EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano') sage: C.creator() 'Gino Fano' """ return self.__creator def timestamp(self): """ Return the time that the covering was submitted to the database EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano','1892-01-01 00:00:00') sage: C.timestamp() #Fano had an article in 1892, I don't know the date it appeared '1892-01-01 00:00:00' """ return self.__timestamp def incidence_structure(self): """ Return the incidence structure of a covering design, without all the extra parameters. EXAMPLES:: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: D = C.incidence_structure() sage: D.ground_set() [0, 1, 2, 3, 4, 5, 6] sage: D.blocks() [[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]] """ return self.__incidence_structure
def T2starGeneralizedQuadrangleGraph(q, dual=False, hyperoval=None, field=None, check_hyperoval=True): r""" Return the collinearity graph of the generalized quadrangle `T_2^*(q)`, or of its dual Let `q=2^k` and `\Theta=PG(3,q)`. `T_2^*(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.3 in [PT09]_. Fix a plane `\Pi \subset \Theta` and a `hyperoval <http://en.wikipedia.org/wiki/Oval_(projective_plane)#Even_q>`__ `O \subset \Pi`. The points of `T_2^*(q):=T_2^*(O)` are the points of `\Theta` outside `\Pi`, and the lines are the lines of `\Theta` outside `\Pi` that meet `\Pi` in a point of `O`. INPUT: - ``q`` -- a power of two - ``dual`` -- if ``False`` (default), return the graph of `T_2^*(O)`. Otherwise return the graph of the dual `T_2^*(O)`. - ``hyperoval`` -- a hyperoval (i.e. a complete 2-arc; a set of points in the plane meeting every line in 0 or 2 points) in the plane of points with 0th coordinate 0 in `PG(3,q)` over the field ``field``. Each point of ``hyperoval`` must be a length 4 vector over ``field`` with 1st non-0 coordinate equal to 1. By default, ``hyperoval`` and ``field`` are not specified, and constructed on the fly. In particular, ``hyperoval`` we build is the classical one, i.e. a conic with the point of intersection of its tangent lines. - ``field`` -- an instance of a finite field of order `q`, must be provided if ``hyperoval`` is provided. - ``check_hyperoval`` -- (default: ``True``) if ``True``, check ``hyperoval`` for correctness. EXAMPLES: using the built-in construction:: sage: g=graphs.T2starGeneralizedQuadrangleGraph(4); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4,dual=True); g T2*(O,4)*; GQ(5, 3): Graph on 96 vertices sage: g.is_strongly_regular(parameters=True) (96, 20, 4, 4) supplying your own hyperoval:: sage: F=GF(4,'b') sage: O=[vector(F,(0,0,0,1)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: g=graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F); g T2*(O,4); GQ(3, 5): Graph on 64 vertices sage: g.is_strongly_regular(parameters=True) (64, 18, 2, 6) TESTS:: sage: F=GF(4,'b') # repeating a point... sage: O=[vector(F,(0,1,0,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval size sage: O=[vector(F,(0,1,1,0)),vector(F,(0,0,1,0))]+map(lambda x: vector(F, (0,1,x^2,x)),F) sage: graphs.T2starGeneralizedQuadrangleGraph(4, hyperoval=O, field=F) Traceback (most recent call last): ... RuntimeError: incorrect hyperoval """ from sage.combinat.designs.incidence_structures import IncidenceStructure from sage.combinat.designs.block_design import ProjectiveGeometryDesign as PG from sage.modules.free_module_element import free_module_element as vector p, k = is_prime_power(q,get_data=True) if k==0 or p!=2: raise ValueError('q must be a power of 2') if field is None: F = FiniteField(q, 'a') else: F = field Theta = PG(3, 1, F, point_coordinates=1) Pi = set(filter(lambda x: x[0]==F.zero(), Theta.ground_set())) if hyperoval is None: O = filter(lambda x: x[1]+x[2]*x[3]==0 or (x[1]==1 and x[2]==0 and x[3]==0), Pi) O = set(O) else: map(lambda x: x.set_immutable(), hyperoval) O = set(hyperoval) if check_hyperoval: if len(O) != q+2: raise RuntimeError("incorrect hyperoval size") for L in Theta.blocks(): if set(L).issubset(Pi): if not len(O.intersection(L)) in [0,2]: raise RuntimeError("incorrect hyperoval") L = map(lambda z: filter(lambda y: not y in O, z), filter(lambda x: len(O.intersection(x)) == 1, Theta.blocks())) if dual: G = IncidenceStructure(L).intersection_graph() G.name('T2*(O,'+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('T2*(O,'+str(q)+'); GQ'+str((q-1,q+1))) return G
class CoveringDesign(SageObject): """ Covering design. INPUT: data -- parameters (v,k,t) size incidence structure (points and blocks) -- default points are $[0,...v-1]$ lower bound for such a design database information: creator, method, timestamp """ def __init__(self, v=0, k=0, t=0, size=0, points=[], blocks=[], low_bd=0, method='', creator='', timestamp=''): """ EXAMPLES: sage: C=CoveringDesign(5,4,3,4,range(5),[[0,1,2,3],[0,1,2,4],[0,1,3,4],[0,2,3,4]],4, 'Lexicographic Covering') sage: print C C(5,4,3) = 4 Method: Lexicographic Covering 0 1 2 3 0 1 2 4 0 1 3 4 0 2 3 4 """ self.__v = v self.__k = k self.__t = t self.__size = size if low_bd > 0: self.__low_bd = low_bd else: self.__low_bd = schonheim(v, k, t) self.__method = method self.__creator = creator self.__timestamp = timestamp self.__incidence_structure = IncidenceStructure(points, blocks) def __repr__(self): """ A print method, giving the parameters and any other information about the covering (but not the blocks). EXAMPLES: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C (7,3,2)-covering design of size 7 Lower bound: 7 Method: Projective Plane """ repr = '(%d,%d,%d)-covering design of size %d\n' % ( self.__v, self.__k, self.__t, self.__size) repr += 'Lower bound: %d\n' % (self.__low_bd) if self.__creator != '': repr += 'Created by: %s\n' % (self.__creator) if self.__method != '': repr += 'Method: %s\n' % (self.__method) if self.__timestamp != '': repr += 'Submitted on: %s\n' % (self.__timestamp) return repr def __str__(self): """ A print method, displaying a covering design's parameters and blocks. OUTPUT: covering design parameters and blocks, in a readable form EXAMPLES: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: print C C(7,3,2) = 7 Method: Projective Plane 0 1 2 0 3 4 0 5 6 1 3 5 1 4 6 2 3 6 2 4 5 """ if self.__size == self.__low_bd: # check if the covering is known to be optimal repr = 'C(%d,%d,%d) = %d\n' % (self.__v, self.__k, self.__t, self.__size) else: repr = '%d <= C(%d,%d,%d) <= %d\n' % ( self.__low_bd, self.__v, self.__k, self.__t, self.__size) if self.__creator != '': repr += 'Created by: %s\n' % (self.__creator) if self.__method != '': repr += 'Method: %s\n' % (self.__method) if self.__timestamp != '': repr += 'Submitted on: %s\n' % (self.__timestamp) for i in range(self.__size): for j in range(self.__k): repr = repr + str( self.__incidence_structure.blocks()[i][j]) + ' ' repr += '\n' return repr def is_covering(self): """ Checks that all t-sets are in fact covered. INPUT: putative covering design OUTPUT: True if all t-sets are in at least one block NOTES: This is very slow and wasteful of memory. A faster Cython version will be added soon. EXAMPLES: sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.is_covering() True sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 6]],0, 'not a covering') # last block altered sage: C.is_covering() False """ v = self.__v k = self.__k t = self.__t Svt = Combinations(range(v), t) Skt = Combinations(range(k), t) tset = {} # tables of t-sets: False = uncovered, True = covered for i in Svt: tset[tuple(i)] = False # mark all t-sets covered by each block for a in self.__incidence_structure.blocks(): for z in Skt: y = [a[x] for x in z] tset[tuple(y)] = True for i in Svt: if tset[tuple(i)] == False: # uncovered return False return True # everything was covered def v(self): """ Return v, the number of points in the covering design EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.v() 7 """ return self.__v def k(self): """ Return k, the size of blocks of the covering design EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.k() 3 """ return self.__k def t(self): """ Return t, the size of sets which must be covered by the blocks of the covering design EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.t() 2 """ return self.__t def size(self): """ Return the number of blocks in the covering design EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.size() 7 """ return self.__size def low_bd(self): """ Return a lower bound for the number of blocks a covering design with these parameters could have. Typically this is the Schonheim bound, but for some parameters better bounds have been shown. EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.low_bd() 7 """ return self.__low_bd def method(self): """ Return the method used to create the covering design This field is optional, and is used in a database to give information about how coverings were constructed EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: C.method() 'Projective Plane' """ return self.__method def creator(self): """ Return the creator of the covering design This field is optional, and is used in a database to give attribution for the covering design It can refer to the person who submitted it, or who originally gave a construction EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano') sage: C.creator() 'Gino Fano' """ return self.__creator def timestamp(self): """ Return the time that the covering was submitted to the database EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane','Gino Fano','1892-01-01 00:00:00') sage: C.timestamp() #Fano had an article in 1892, I don't know the date it appeared '1892-01-01 00:00:00' """ return self.__timestamp def incidence_structure(self): """ Return the incidence structure of a covering design, without all the extra parameters. EXAMPLES: sage: from sage.combinat.designs.covering_design import CoveringDesign sage: C=CoveringDesign(7,3,2,7,range(7),[[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]],0, 'Projective Plane') sage: D = C.incidence_structure() sage: D.points() [0, 1, 2, 3, 4, 5, 6] sage: D.blocks() [[0, 1, 2], [0, 3, 4], [0, 5, 6], [1, 3, 5], [1, 4, 6], [2, 3, 6], [2, 4, 5]] """ return self.__incidence_structure
def AhrensSzekeresGeneralizedQuadrangleGraph(q, dual=False): r""" Return the collinearity graph of the generalized quadrangle `AS(q)`, or of its dual Let `q` be an odd prime power. `AS(q)` is a generalized quadrangle [GQwiki]_ of order `(q-1,q+1)`, see 3.1.5 in [PT09]_. Its points are elements of `F_q^3`, and lines are sets of size `q` of the form * `\{ (\sigma, a, b) \mid \sigma\in F_q \}` * `\{ (a, \sigma, b) \mid \sigma\in F_q \}` * `\{ (c \sigma^2 - b \sigma + a, -2 c \sigma + b, \sigma) \mid \sigma\in F_q \}`, where `a`, `b`, `c` are arbitrary elements of `F_q`. INPUT: - ``q`` -- a power of an odd prime number - ``dual`` -- if ``False`` (default), return the collinearity graph of `AS(q)`. Otherwise return the collinearity graph of the dual `AS(q)`. EXAMPLES:: sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5); g AS(5); GQ(4, 6): Graph on 125 vertices sage: g.is_strongly_regular(parameters=True) (125, 28, 3, 7) sage: g=graphs.AhrensSzekeresGeneralizedQuadrangleGraph(5,dual=True); g AS(5)*; GQ(6, 4): Graph on 175 vertices sage: g.is_strongly_regular(parameters=True) (175, 30, 5, 5) REFERENCE: .. [GQwiki] `Generalized quadrangle <http://en.wikipedia.org/wiki/Generalized_quadrangle>`__ .. [PT09] S. Payne, J. A. Thas. Finite generalized quadrangles. European Mathematical Society, 2nd edition, 2009. """ from sage.combinat.designs.incidence_structures import IncidenceStructure p, k = is_prime_power(q,get_data=True) if k==0 or p==2: raise ValueError('q must be an odd prime power') F = FiniteField(q, 'a') L = [] for a in F: for b in F: L.append(tuple(map(lambda s: (s, a, b), F))) L.append(tuple(map(lambda s: (a, s, b), F))) for c in F: L.append(tuple(map(lambda s: (c*s**2 - b*s + a, -2*c*s + b, s), F))) if dual: G = IncidenceStructure(L).intersection_graph() G.name('AS('+str(q)+')*; GQ'+str((q+1,q-1))) else: G = IncidenceStructure(L).dual().intersection_graph() G.name('AS('+str(q)+'); GQ'+str((q-1,q+1))) return G