def __init__(self, field=None): """ Create a linear feedback shift cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E LFSR cryptosystem over Finite Field of size 2 TESTS:: sage: E = LFSRCryptosystem(FiniteField(2)) sage: E == loads(dumps(E)) True TODO: Implement LFSR cryptosystem for arbitrary rings. The current implementation is limited to the finite field of 2 elements only because of the dependence on binary strings. """ if field is None: field = FiniteField(2) if field.cardinality() != 2: raise NotImplementedError("Not yet implemented.") S = BinaryStrings() P = PolynomialRing(FiniteField(2), 'x') SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def twin_prime_powers_difference_set(p, check=True): r""" Return a difference set on `GF(p) \times GF(p+2)`. The difference set is built from the following element of the cartesian product of finite fields `GF(p) \times GF(p+2)`: - `(x,0)` with any `x` - `(x,y)` with `x` and `y` squares - `(x,y)` with `x` and `y` non-squares For more information see :wikipedia:`Difference_set`. INPUT: - ``check`` -- boolean (default: ``True``). If ``True`` then the result of the computation is checked before being returned. This should not be needed but ensures that the output is correct. EXAMPLES:: sage: from sage.combinat.designs.difference_family import twin_prime_powers_difference_set sage: G,D = twin_prime_powers_difference_set(3) sage: G The cartesian product of (Finite Field of size 3, Finite Field of size 5) sage: D [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]] """ from sage.rings.finite_rings.constructor import FiniteField from sage.categories.cartesian_product import cartesian_product from itertools import product Fp = FiniteField(p, 'x') Fq = FiniteField(p + 2, 'x') Fpset = set(Fp) Fqset = set(Fq) Fp_squares = set(x**2 for x in Fpset) Fq_squares = set(x**2 for x in Fqset) # Pairs of squares, pairs of non-squares d = [] d.extend(product(Fp_squares.difference([0]), Fq_squares.difference([0]))) d.extend( product(Fpset.difference(Fp_squares), Fqset.difference(Fq_squares))) # All (x,0) d.extend((x, 0) for x in Fpset) G = cartesian_product([Fp, Fq]) if check and not is_difference_family(G, [d]): raise RuntimeError("twin_prime_powers_difference_set produced a wrong " "difference set with p={}. Please contact " "*****@*****.**".format(p)) return G, [d]
def __init__(self, p, use_database=True): """ TESTS:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(3) sage: PCL.polynomial(3) x^3 + 2*x + 1 sage: PCL = PseudoConwayLattice(5, use_database=False) sage: PCL.polynomial(12) x^12 + 2*x^11 + x^10 + 4*x^9 + 4*x^8 + 4*x^7 + x^6 + 4*x^5 + x^4 + 3*x + 2 sage: PCL.polynomial(6) x^6 + x^5 + 4*x^4 + 3*x^3 + 3*x^2 + 2*x + 2 sage: PCL.polynomial(11) x^11 + x^6 + 3*x^3 + 4*x + 3 """ self.p = p from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing self.ring = PolynomialRing(FiniteField(p), 'x') if use_database: C = sage.databases.conway.ConwayPolynomials() self.nodes = {n: self.ring(C.polynomial(p, n)) for n in C.degrees(p)} else: self.nodes = {}
def __classcall__(cls, *args, **kwds): """ Normalize input to ensure a unique representation. EXAMPLES:: sage: A = AffineSpace(2, GF(4,'a')) sage: AffineGroup(A) is AffineGroup(2,4) True sage: AffineGroup(A) is AffineGroup(2, GF(4,'a')) True sage: A = AffineGroup(2, QQ) sage: V = QQ^2 sage: A is AffineGroup(V) True """ if len(args) == 1: V = args[0] if isinstance(V, AffineGroup): return V try: degree = V.dimension_relative() except AttributeError: degree = V.dimension() ring = V.base_ring() if len(args) == 2: degree, ring = args from sage.rings.integer import is_Integer if is_Integer(ring): from sage.rings.finite_rings.constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) return super(AffineGroup, cls).__classcall__(cls, degree, ring)
def QuaternionMatrixGroupGF3(): r""" The quaternion group as a set of `2\times 2` matrices over `GF(3)`. OUTPUT: A matrix group consisting of `2\times 2` matrices with elements from the finite field of order 3. The group is the quaternion group, the nonabelian group of order 8 that is not isomorphic to the group of symmetries of a square (the dihedral group `D_4`). .. note:: This group is most easily available via ``groups.matrix.QuaternionGF3()``. EXAMPLES: The generators are the matrix representations of the elements commonly called `I` and `J`, while `K` is the product of `I` and `J`. :: sage: from sage.groups.misc_gps.misc_groups import QuaternionMatrixGroupGF3 sage: Q = QuaternionMatrixGroupGF3() sage: Q.order() 8 sage: aye = Q.gens()[0]; aye [1 1] [1 2] sage: jay = Q.gens()[1]; jay [2 1] [1 1] sage: kay = aye*jay; kay [0 2] [1 0] TESTS:: sage: groups.matrix.QuaternionGF3() Matrix group over Finite Field of size 3 with 2 generators: [[[1, 1], [1, 2]], [[2, 1], [1, 1]]] sage: Q = QuaternionMatrixGroupGF3() sage: QP = Q.as_permutation_group() sage: QP.is_isomorphic(QuaternionGroup()) True sage: H = DihedralGroup(4) sage: H.order() 8 sage: QP.is_abelian(), H.is_abelian() (False, False) sage: QP.is_isomorphic(H) False """ from sage.rings.finite_rings.constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace from sage.groups.matrix_gps.matrix_group import MatrixGroup MS = MatrixSpace(FiniteField(3), 2) aye = MS([1,1,1,2]) jay = MS([2,1,1,1]) return MatrixGroup([aye, jay])
def __init__(self, n): """ Args: n (int): The number of elements in the finite field. """ f = FiniteField(n) operations = {'+': lambda x, y: x + y, '*': lambda x, y: x * y} AlgebraicStructure.__init__(self, set(f), operations)
def test_generic_02(self): F = FiniteField(2**8, 'a') shares = [(1, 1), (2, 121), (3, 97), (4, 77), (5, 29)] points = [(F.fetch_int(x), F.fetch_int(y)) for x, y in shares] p = berlekamp_welsh(5, points) print(p) print([coeff.integer_representation() for coeff in p]) print(map(lambda x: x.integer_representation(), p))
def gfq_gap_to_sage(x, F): """ INPUT: - ``x`` - gap finite field element - ``F`` - Sage finite field OUTPUT: element of F EXAMPLES:: sage: x = gap('Z(13)') sage: F = GF(13, 'a') sage: F(x) 2 sage: F(gap('0*Z(13)')) 0 sage: F = GF(13^2, 'a') sage: x = gap('Z(13)') sage: F(x) 2 sage: x = gap('Z(13^2)^3') sage: F(x) 12*a + 11 sage: F.multiplicative_generator()^3 12*a + 11 AUTHOR: - David Joyner and William Stein """ from sage.rings.finite_rings.constructor import FiniteField s = str(x) if s[:2] == '0*': return F(0) i1 = s.index("(") i2 = s.index(")") q = eval(s[i1 + 1:i2].replace('^', '**')) if q == F.order(): K = F else: K = FiniteField(q, F.variable_name()) if s.find(')^') == -1: e = 1 else: e = int(s[i2 + 2:]) if F.degree() == 1: g = int(gap.eval('Int(Z(%s))' % q)) else: g = K.multiplicative_generator() return F(K(g**e))
def test_generic_01(self): F = FiniteField(2**8, 'a') #shares = [(1, 22), (2, 82), (3, 110), (4, 218), (5, 230)] points = [(1, 22), (2, 82), (3, 110), (4, 219)] points = [(F.fetch_int(x), F.fetch_int(y)) for x, y in shares] p = berlekamp_welsh(1, points) pint = ([coeff.integer_representation() for coeff in p]) #pint = (map(lambda x: x.integer_representation(), p)) print(p, pint) assert [42, 60] == pint
def test_01(self): order = 2**8 F = FiniteField(order, 'a') P = PolynomialRing(F, 'x') n = 7 deg = 2 poly = F.fetch_int(42) for i in range(1, deg + 1): poly += F.random_element() * P.gen()**i # evaluate polynomial at different points (shares) print(poly) points = [(F.fetch_int(i), poly(F.fetch_int(i))) for i in range(1, n + 1)] points[0] = (points[0][0], points[0][1] + F.fetch_int(9)) print(points) assert poly == berlekamp_welsh(deg, points)
def conway_polynomial(p, n): """ Return the Conway polynomial of degree `n` over ``GF(p)``. If the requested polynomial is not known, this function raises a ``RuntimeError`` exception. INPUT: - ``p`` -- prime number - ``n`` -- positive integer OUTPUT: - the Conway polynomial of degree `n` over the finite field ``GF(p)``, loaded from a table. .. NOTE:: The first time this function is called a table is read from disk, which takes a fraction of a second. Subsequent calls do not require reloading the table. See also the ``ConwayPolynomials()`` object, which is the table of Conway polynomials used by this function. EXAMPLES:: sage: conway_polynomial(2,5) x^5 + x^2 + 1 sage: conway_polynomial(101,5) x^5 + 2*x + 99 sage: conway_polynomial(97,101) Traceback (most recent call last): ... RuntimeError: requested Conway polynomial not in database. """ (p, n) = (int(p), int(n)) R = FiniteField(p)['x'] try: return R(sage.databases.conway.ConwayPolynomials()[p][n]) except KeyError: raise RuntimeError("requested Conway polynomial not in database.")
def check_consistency(self, n): """ Check that the pseudo-Conway polynomials of degree dividing `n` in this lattice satisfy the required compatibility conditions. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.check_consistency(6) sage: PCL.check_consistency(60) # long """ p = self.p K = FiniteField(p**n, modulus = self.polynomial(n), names='a') a = K.gen() for m in n.divisors(): assert (a**((p**n-1)//(p**m-1))).minimal_polynomial() == self.polynomial(m)
def _subfield(self, n): """ Return the unique subfield of degree `n` of ``self``. EXAMPLES:: sage: F = GF(3).algebraic_closure() sage: F._subfield(4) Finite Field in z4 of size 3^4 """ if n == 1: return self.base_ring() else: from sage.rings.finite_rings.constructor import FiniteField return FiniteField(self.base_ring().cardinality()**n, name=self.variable_name() + str(n), modulus=self._get_polynomial(n), check_irreducible=False)
def BIBD_5q_5_for_q_prime_power(q): r""" Returns a `(5q,5,1)`-BIBD with `q\equiv 1\pmod 4` a prime power. See Theorem 24 [ClaytonSmith]_. INPUT: - ``q`` (integer) -- a prime power such that `q\equiv 1\pmod 4`. EXAMPLES:: sage: from sage.combinat.designs.bibd import BIBD_5q_5_for_q_prime_power sage: for q in [25, 45, 65, 85, 125, 145, 185, 205, 305, 405, 605]: # long time ....: _ = BIBD_5q_5_for_q_prime_power(q/5) # long time """ from sage.rings.arith import is_prime_power from sage.rings.finite_rings.constructor import FiniteField if q % 4 != 1 or not is_prime_power(q): raise ValueError("q is not a prime power or q%4!=1.") d = (q - 1) / 4 B = [] F = FiniteField(q, 'x') a = F.primitive_element() L = {b: i for i, b in enumerate(F)} for b in L: B.append([i * q + L[b] for i in range(5)]) for i in range(5): for j in range(d): B.append([ i * q + L[b], ((i + 1) % 5) * q + L[a**j + b], ((i + 1) % 5) * q + L[-a**j + b], ((i + 4) % 5) * q + L[a**(j + d) + b], ((i + 4) % 5) * q + L[-a**(j + d) + b], ]) return B
def __init__(self, field=None): """ Create a shrinking generator cryptosystem. INPUT: A string monoid over a binary alphabet. OUTPUT: EXAMPLES:: sage: E = ShrinkingGeneratorCryptosystem() sage: E Shrinking generator cryptosystem over Finite Field of size 2 """ if field is None: field = FiniteField(2) if field.cardinality() != 2: raise NotImplementedError("Not yet implemented.") S = BinaryStrings() P = PolynomialRing(field, 'x') SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def polynomial(self, name=None): """ Returns the polynomial ``name``. EXAMPLES:: sage: k.<a> = GF(3) sage: k.polynomial() x """ if name is None: name = self.variable_name() try: return self.__polynomial[name] except AttributeError: from sage.rings.finite_rings.constructor import FiniteField R = FiniteField(self.characteristic())[name] f = self[name]([0,1]) try: self.__polynomial[name] = f except (KeyError, AttributeError): self.__polynomial = {} self.__polynomial[name] = f return f
def __init__(self, n=7, k=3, order=2**8): r""" Sharmir secret sharing. EXAMPLES:: sage: from sage.crypto.smc.shamir_ss import ShamirSS sage: sss = ShamirSS() sage: secret = 42 sage: shares = sss.share(secret) sage: secret == sss.reconstruct(shares) True """ self._k = k # threshold self._n = n # number shares self._order = order # order of field from sage.rings.finite_rings.constructor import FiniteField self._F = FiniteField(self._order, 'a') if not self._F.is_prime_field() and not hasattr(self._F, 'fetch_int'): raise TypeError("field order not supported") from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing self._P = PolynomialRing(self._F, 'x')
#print "r =", r #print "ss=", ss #print "rr=", rr #print "-----------------" assert r == s and m == ss and m == rr, "ERROR!!!" ### main def parseargs(): """ Parse the commandline arguments """ parser = argparse.ArgumentParser(description='') parser.add_argument('-m', '--manual', action='store_true', help='Run test cases manually.') return parser.parse_args() if __name__ == '__main__': args = parseargs() F = FiniteField(7) simple_test(F, 6, 3) F = FiniteField(257) simple_test(F, 4, 3) simple_test(F, 256, 90) F = FiniteField(256, 'a') simple_test(F, 15, 7)
def difference_family(v, k, l=1, existence=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. Let `G` be a finite Abelian group. For a given subset `D` of `G`, we define `\Delta D` to be the multi-set of differences `\Delta D = \{x - y; x \in D, y \in D, x \not= y\}`. A `(G,k,\lambda)`-*difference family* is a collection of `k`-subsets of `G`, `D = \{D_1, D_2, \ldots, D_b\}` such that the union of the difference sets `\Delta D_i` for `i=1,...b`, seen as a multi-set, contains each element of `G \backslash \{0\}` exactly `\lambda`-times. When there is only one block, i.e. `\lambda(v - 1) = k(k-1)`, then a `(G,k,\lambda)`-difference family is also called a *difference set*. See also :wikipedia:`Difference_set`. If there is no such difference family, an ``EmptySetError`` is raised and if there is no construction at the moment ``NotImplementedError`` is raised. EXAMPLES:: sage: K,D = designs.difference_family(73,4) sage: D [[0, 1, 8, 64], [0, 25, 54, 67], [0, 41, 36, 69], [0, 3, 24, 46], [0, 2, 16, 55], [0, 50, 35, 61]] sage: K,D = designs.difference_family(337,7) sage: D [[1, 175, 295, 64, 79, 8, 52], [326, 97, 125, 307, 142, 249, 102], [121, 281, 310, 330, 123, 294, 226], [17, 279, 297, 77, 332, 136, 210], [150, 301, 103, 164, 55, 189, 49], [35, 59, 215, 218, 69, 280, 135], [289, 25, 331, 298, 252, 290, 200], [191, 62, 66, 92, 261, 180, 159]] For `k=6,7` we look at the set of small prime powers for which a construction is available:: sage: def prime_power_mod(r,m): ....: k = m+r ....: while True: ....: if is_prime_power(k): ....: yield k ....: k += m sage: from itertools import islice sage: l6 = {True:[], False: [], Unknown: []} sage: for q in islice(prime_power_mod(1,30), 60): ....: l6[designs.difference_family(q,6,existence=True)].append(q) sage: l6[True] [31, 121, 151, 181, 211, ..., 3061, 3121, 3181] sage: l6[Unknown] [61] sage: l6[False] [] sage: l7 = {True: [], False: [], Unknown: []} sage: for q in islice(prime_power_mod(1,42), 60): ....: l7[designs.difference_family(q,7,existence=True)].append(q) sage: l7[True] [337, 421, 463, 883, 1723, 3067, 3319, 3529, 3823, 3907, 4621, 4957, 5167] sage: l7[Unknown] [43, 127, 169, 211, ..., 4999, 5041, 5209] sage: l7[False] [] Other constructions for `\lambda > 1`:: sage: for v in xrange(2,100): ....: constructions = [] ....: for k in xrange(2,10): ....: for l in xrange(2,10): ....: if designs.difference_family(v,k,l,existence=True): ....: constructions.append((k,l)) ....: _ = designs.difference_family(v,k,l) ....: if constructions: ....: print "%2d: %s"%(v, ', '.join('(%d,%d)'%(k,l) for k,l in constructions)) 2: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 3: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 4: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 5: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 7: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 8: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 9: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 11: (3,2), (4,3), (4,6), (5,2), (5,3), (5,4), (6,5), (7,6), (8,7), (9,8) 13: (3,2), (4,3), (5,4), (5,5), (6,5), (7,6), (8,7), (9,8) 15: (4,6), (5,6), (7,3) 16: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 17: (3,2), (4,3), (5,4), (5,5), (6,5), (7,6), (8,7), (9,8) 19: (3,2), (4,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,4), (9,5), (9,6), (9,7), (9,8) 21: (4,3), (6,3), (6,5) 22: (4,2), (6,5), (7,4), (8,8) 23: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 25: (3,2), (4,3), (5,4), (6,5), (7,6), (7,7), (8,7), (9,8) 27: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 28: (3,2), (6,5) 29: (3,2), (4,3), (5,4), (6,5), (7,3), (7,6), (8,4), (8,6), (8,7), (9,8) 31: (3,2), (4,2), (4,3), (5,2), (5,4), (6,5), (7,6), (8,7), (9,8) 32: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 33: (5,5), (6,5) 34: (4,2) 35: (5,2), (8,4) 37: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,2), (9,3), (9,8) 39: (6,5) 40: (3,2) 41: (3,2), (4,3), (5,4), (6,3), (6,5), (7,6), (8,7), (9,8) 43: (3,2), (4,2), (4,3), (5,4), (6,5), (7,2), (7,3), (7,6), (8,4), (8,7), (9,8) 46: (4,2), (6,2) 47: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 49: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,3), (9,8) 51: (5,2), (6,3) 53: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 55: (9,4) 57: (7,3) 59: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 61: (3,2), (4,3), (5,4), (6,2), (6,3), (6,5), (7,6), (8,7), (9,8) 64: (3,2), (4,3), (5,4), (6,5), (7,2), (7,6), (8,7), (9,8) 67: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 71: (3,2), (4,3), (5,2), (5,4), (6,5), (7,3), (7,6), (8,4), (8,7), (9,8) 73: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 75: (5,2) 79: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 81: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 83: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 85: (7,2), (7,3), (8,2) 89: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 97: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,3), (9,8) TESTS: Check more of the Wilson constructions from [Wi72]_:: sage: Q5 = [241, 281,421,601,641, 661, 701, 821,881] sage: Q9 = [73, 1153, 1873, 2017] sage: Q15 = [76231] sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457] sage: Q8 = [1009, 3137, 3697] sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: ....: for q in Q: ....: assert designs.difference_family(q,k,1,existence=True) is True ....: _ = designs.difference_family(q,k,1) Check Singer difference sets:: sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) sage: for q in range(2,10): ....: if is_prime_power(q): ....: for d in [2,3,4]: ....: v,k,l = sgp(q,d) ....: assert designs.difference_family(v,k,l,existence=True) is True ....: _ = designs.difference_family(v,k,l) Check twin primes difference sets:: sage: for p in [3,5,7,9,11]: ....: v = p*(p+2); k = (v-1)/2; lmbda = (k-1)/2 ....: G,D = designs.difference_family(v,k,lmbda) Check the database: sage: from sage.combinat.designs.database import DF sage: for v,k,l in DF: ....: df = designs.difference_family(v,k,l,check=True) .. TODO:: Implement recursive constructions from Buratti "Recursive for difference matrices and relative difference families" (1998) and Jungnickel "Composition theorems for difference families and regular planes" (1978) """ from block_design import are_hyperplanes_in_projective_geometry_parameters from database import DF if (v, k, l) in DF: if existence: return True vv, blocks = DF[v, k, l].iteritems().next() # Build the group from sage.rings.finite_rings.integer_mod_ring import Zmod if len(vv) == 1: G = Zmod(vv[0]) else: from sage.categories.cartesian_product import cartesian_product G = cartesian_product([Zmod(i) for i in vv]) df = [[G(i) for i in b] for b in blocks] if check: assert is_difference_family( G, df, v=v, k=k, l=l), "Sage built an invalid ({},{},{})-DF!".format(v, k, l) return G, df e = k * (k - 1) t = l * (v - 1) // e # number of blocks D = None factorization = arith.factor(v) if len(factorization) == 1: # i.e. is v a prime power from sage.rings.finite_rings.constructor import GF G = K = GF(v, 'z') x = K.multiplicative_generator() if l == (k - 1): if existence: return True return K, K.cyclotomic_cosets(x**((v - 1) // k))[1:] if t == 1: # some of the difference set constructions VI.18.48 from the # Handbook of combinatorial designs # q = 3 mod 4 if v % 4 == 3 and k == (v - 1) // 2: if existence: return True D = K.cyclotomic_cosets(x**2, [1]) # q = 4t^2 + 1, t odd elif v % 8 == 5 and k == (v - 1) // 4 and arith.is_square( (v - 1) // 4): if existence: return True D = K.cyclotomic_cosets(x**4, [1]) # q = 4t^2 + 9, t odd elif v % 8 == 5 and k == (v + 3) // 4 and arith.is_square( (v - 9) // 4): if existence: return True D = K.cyclotomic_cosets(x**4, [1]) D[0].insert(0, K.zero()) if D is None and l == 1: one = K.one() # Wilson (1972), Theorem 9 if k % 2 == 1: m = (k - 1) // 2 xx = x**m to_coset = { x**i * xx**j: i for i in xrange(m) for j in xrange((v - 1) / m) } r = x**((v - 1) // k) # primitive k-th root of unity if len(set(to_coset[r**j - one] for j in xrange(1, m + 1))) == m: if existence: return True B = [r**j for j in xrange(k) ] # = H^((k-1)t) whose difference is # H^(mt) (r^i - 1, i=1,..,m) # Now pick representatives a translate of R for by a set of # representatives of H^m / H^(mt) D = [[x**(i * m) * b for b in B] for i in xrange(t)] # Wilson (1972), Theorem 10 else: m = k // 2 xx = x**m to_coset = { x**i * xx**j: i for i in xrange(m) for j in xrange((v - 1) / m) } r = x**((v - 1) // (k - 1)) # primitive (k-1)-th root of unity if (all(to_coset[r**j - one] != 0 for j in xrange(1, m)) and len(set(to_coset[r**j - one] for j in xrange(1, m))) == m - 1): if existence: return True B = [K.zero()] + [r**j for j in xrange(k - 1)] D = [[x**(i * m) * b for b in B] for i in xrange(t)] # Wilson (1972), Theorem 11 if D is None and k == 6: r = x**((v - 1) // 3) # primitive cube root of unity r2 = r * r xx = x**5 to_coset = { x**i * xx**j: i for i in xrange(5) for j in xrange((v - 1) / 5) } for c in to_coset: if c == 1 or c == r or c == r2: continue if len( set(to_coset[elt] for elt in (r - 1, c * (r - 1), c - 1, c - r, c - r**2))) == 5: if existence: return True B = [one, r, r**2, c, c * r, c * r**2] D = [[x**(i * 5) * b for b in B] for i in xrange(t)] break # Twin prime powers construction (see :wikipedia:`Difference_set`) # # i.e. v = p(p+2) where p and p+2 are prime powers # k = (v-1)/2 # lambda = (k-1)/2 elif (len(factorization) == 2 and abs(pow(*factorization[0]) - pow(*factorization[1])) == 2 and k == (v - 1) // 2 and (l is None or 2 * l == (v - 1) // 2 - 1)): # A difference set can be built from the set of elements # (x,y) in GF(p) x GF(p+2) such that: # # - either y=0 # - x and y with x and y squares # - x and y with x and y non-squares if existence: return True from sage.rings.finite_rings.constructor import FiniteField from sage.categories.cartesian_product import cartesian_product from itertools import product p, q = pow(*factorization[0]), pow(*factorization[1]) if p > q: p, q = q, p Fp = FiniteField(p, 'x') Fq = FiniteField(q, 'x') Fpset = set(Fp) Fqset = set(Fq) Fp_squares = set(x**2 for x in Fpset) Fq_squares = set(x**2 for x in Fqset) # Pairs of squares, pairs of non-squares d = [] d.extend( product(Fp_squares.difference([0]), Fq_squares.difference([0]))) d.extend( product(Fpset.difference(Fp_squares), Fqset.difference(Fq_squares))) # All (x,0) d.extend((x, 0) for x in Fpset) G = cartesian_product([Fp, Fq]) D = [d] if D is None and are_hyperplanes_in_projective_geometry_parameters( v, k, l): _, (q, d) = are_hyperplanes_in_projective_geometry_parameters( v, k, l, True) if existence: return True else: G, D = singer_difference_set(q, d) if D is None: if existence: return Unknown raise NotImplementedError("No constructions for these parameters") if check and not is_difference_family(G, D, verbose=False): raise RuntimeError return G, D
def polynomial(self, n): r""" Return the pseudo-Conway polynomial of degree `n` in this lattice. INPUT: - ``n`` -- positive integer OUTPUT: - a pseudo-Conway polynomial of degree `n` for the prime `p`. ALGORITHM: Uses an algorithm described in [HL99]_, modified to find pseudo-Conway polynomials rather than Conway polynomials. The major difference is that we stop as soon as we find a primitive polynomial. REFERENCE: .. [HL99] L. Heath and N. Loehr (1999). New algorithms for generating Conway polynomials over finite fields. Proceedings of the tenth annual ACM-SIAM symposium on discrete algorithms, pp. 429-437. EXAMPLES:: sage: from sage.rings.finite_rings.conway_polynomials import PseudoConwayLattice sage: PCL = PseudoConwayLattice(2, use_database=False) sage: PCL.polynomial(3) x^3 + x + 1 sage: PCL.polynomial(4) x^4 + x^3 + 1 sage: PCL.polynomial(60) x^60 + x^59 + x^58 + x^55 + x^54 + x^53 + x^52 + x^51 + x^48 + x^46 + x^45 + x^42 + x^41 + x^39 + x^38 + x^37 + x^35 + x^32 + x^31 + x^30 + x^28 + x^24 + x^22 + x^21 + x^18 + x^17 + x^16 + x^15 + x^14 + x^10 + x^8 + x^7 + x^5 + x^3 + x^2 + x + 1 """ if n in self.nodes: return self.nodes[n] p = self.p if n == 1: f = self.ring.gen() - FiniteField(p).multiplicative_generator() self.nodes[1] = f return f # Work in an arbitrary field K of order p**n. K = FiniteField(p**n, names='a') # TODO: something like the following # gcds = [n.gcd(d) for d in self.nodes.keys()] # xi = { m: (...) for m in gcds } xi = {q: self.polynomial(n//q).any_root(K, -n//q, assume_squarefree=True) for q in n.prime_divisors()} # The following is needed to ensure that in the concrete instantiation # of the "new" extension all previous choices are compatible. _frobenius_shift(K, xi) # Construct a compatible element having order the lcm of orders q, x = xi.popitem() v = p**(n//q) - 1 for q, xitem in xi.iteritems(): w = p**(n//q) - 1 g, alpha, beta = v.xgcd(w) x = x**beta * xitem**alpha v = v.lcm(w) r = p**n - 1 # Get the missing part of the order to be primitive g = r // v # Iterate through g-th roots of x until a primitive one is found z = x.nth_root(g) root = K.multiplicative_generator()**v while z.multiplicative_order() != r: z *= root # The following should work but tries to create a huge list # whose length overflows Python's ints for large parameters #Z = x.nth_root(g, all=True) #for z in Z: # if z.multiplicative_order() == r: # break f = z.minimal_polynomial() self.nodes[n] = f return f
def OA_from_Vmt(m, t, V): r""" Returns an Orthogonal Array from a V(m,t) *Definition* Let `q` be a prime power and let `q=mt+1` for `m,t` integers. Let `\omega` be a primitive element of `\mathbb{F}_q`. A `V(m,t)` vector is a vector `(a_1,\dots,a_{m+1}` for which, for each `1\leq k < m`, the differences .. MATH:: \{a_{i+k}-a_i:1\leq i \leq m+1,i+k\neq m+2\} represent the `m` cyclotomic classes of `\mathbb{F}_{mt+1}` (compute subscripts modulo `m+2`). In other words, for fixed `k`, is `a_{i+k}-a_i=\omega^{mx+\alpha}` and `a_{j+k}-a_j=\omega^{my+\beta}` then `\alpha\not\equiv\beta \mod{m}` *Construction of a quasi-difference matrix from a `V(m,t)` vector* Starting with a `V(m,t)` vector `(a_1,\dots,a_{m+1})`, form a single column of length `m+2` whose first entry is empty, and whose remaining entries are `(a_1,\dots,a_{m+1})`. Form `t` columns by multiplying this column by the `t` th roots, i.e. the powers of `\omega^m`. From each of these `t` columns, form `m+2` columns by taking the `m+2` cyclic shifts of the column. The result is a `(a,m+2;1,0;t)-QDM`. For more information, refer to the Handbook of Combinatorial Designs [DesignHandbook]_. INPUT: - ``m,t`` (integers) - ``V`` -- the vector `V(m,t)`. .. SEEALSO:: :func:`OA_from_quasi_difference_matrix` EXAMPLES:: sage: _ = designs.orthogonal_array(6,46) # indirect doctest """ from sage.rings.finite_rings.constructor import FiniteField q = m * t + 1 Fq = FiniteField(q) w = Fq.primitive_element() # Cyclic shift of a list cyclic_shift = lambda l, i: l[-i:] + l[:-i] M = [] wm = w**m for i in range(t): L = [None] for e in V: L.append(e * wm**i) for ii in range(m + 2): M.append(cyclic_shift(L, ii)) M.append([0] * q) M = zip(*M) M = OA_from_quasi_difference_matrix(M, Fq, add_col=False) return M
def ButterflyGraph(self, n, vertices='strings'): """ Returns a n-dimensional butterfly graph. The vertices consist of pairs (v,i), where v is an n-dimensional tuple (vector) with binary entries (or a string representation of such) and i is an integer in [0..n]. A directed edge goes from (v,i) to (w,i+1) if v and w are identical except for possibly v[i] != w[i]. A butterfly graph has `(2^n)(n+1)` vertices and `n2^{n+1}` edges. INPUT: - ``vertices`` - 'strings' (default) or 'vectors', specifying whether the vertices are zero-one strings or actually tuples over GF(2). EXAMPLES:: sage: digraphs.ButterflyGraph(2).edges(labels=False) [(('00', 0), ('00', 1)), (('00', 0), ('10', 1)), (('00', 1), ('00', 2)), (('00', 1), ('01', 2)), (('01', 0), ('01', 1)), (('01', 0), ('11', 1)), (('01', 1), ('00', 2)), (('01', 1), ('01', 2)), (('10', 0), ('00', 1)), (('10', 0), ('10', 1)), (('10', 1), ('10', 2)), (('10', 1), ('11', 2)), (('11', 0), ('01', 1)), (('11', 0), ('11', 1)), (('11', 1), ('10', 2)), (('11', 1), ('11', 2))] sage: digraphs.ButterflyGraph(2,vertices='vectors').edges(labels=False) [(((0, 0), 0), ((0, 0), 1)), (((0, 0), 0), ((1, 0), 1)), (((0, 0), 1), ((0, 0), 2)), (((0, 0), 1), ((0, 1), 2)), (((0, 1), 0), ((0, 1), 1)), (((0, 1), 0), ((1, 1), 1)), (((0, 1), 1), ((0, 0), 2)), (((0, 1), 1), ((0, 1), 2)), (((1, 0), 0), ((0, 0), 1)), (((1, 0), 0), ((1, 0), 1)), (((1, 0), 1), ((1, 0), 2)), (((1, 0), 1), ((1, 1), 2)), (((1, 1), 0), ((0, 1), 1)), (((1, 1), 0), ((1, 1), 1)), (((1, 1), 1), ((1, 0), 2)), (((1, 1), 1), ((1, 1), 2))] """ # We could switch to Sage integers to handle arbitrary n. if vertices == 'strings': if n >= 31: raise NotImplementedError( "vertices='strings' is only valid for n<=30.") from sage.graphs.generic_graph_pyx import binary butterfly = {} for v in xrange(2**n): for i in range(n): w = v w ^= (1 << i) # push 1 to the left by i and xor with w bv = binary(v) bw = binary(w) # pad and reverse the strings padded_bv = ('0' * (n - len(bv)) + bv)[::-1] padded_bw = ('0' * (n - len(bw)) + bw)[::-1] butterfly[(padded_bv, i)] = [(padded_bv, i + 1), (padded_bw, i + 1)] elif vertices == 'vectors': from sage.modules.free_module import VectorSpace from sage.rings.finite_rings.constructor import FiniteField from copy import copy butterfly = {} for v in VectorSpace(FiniteField(2), n): for i in xrange(n): w = copy(v) w[i] += 1 # Flip the ith bit # We must call tuple since vectors are mutable. To obtain # a vector from the tuple t, just call vector(t). butterfly[(tuple(v), i)] = [(tuple(v), i + 1), (tuple(w), i + 1)] else: raise NotImplementedError( "vertices must be 'strings' or 'vectors'.") return DiGraph(butterfly)
def mutually_orthogonal_latin_squares(n, k=None, partitions=False): r""" Returns `k` Mutually Orthogonal `n\times n` Latin Squares (MOLS). For more information on Latin Squares and MOLS, see :mod:`~sage.combinat.designs.latin_squares` or the :wikipedia:`Latin_square`, or even the :wikipedia:`Wikipedia entry on MOLS <Graeco-Latin_square#Mutually_orthogonal_Latin_squares>`. INPUT: - ``n`` (integer) -- size of the latin square. - ``k`` (integer) -- returns `k` MOLS. If set to ``None`` (default), returns the maximum number of MOLS that Sage can build. .. WARNING:: This has no reason to be the maximum number of `n\times n` MOLS, just the best Sage can do ! - ``partition`` (boolean) -- a Latin Square can be seen as 3 partitions of the `n^2` cells of the array into `n` sets of size `n`, respectively : * The partition of rows * The partition of columns * The partition of number (cells numbered with 0, cells numbered with 1, ...) These partitions have the additional property that any two sets from different partitions intersect on exactly one element. When ``partition`` is set to ``True``, this function returns a list of `k+2` partitions satisfying this intersection property instead of the `k+2` MOLS (though the data is exactly the same in both cases). EXAMPLES:: sage: designs.mutually_orthogonal_latin_squares(5) [ [0 1 2 3 4] [0 1 2 3 4] [0 1 2 3 4] [0 1 2 3 4] [3 0 1 4 2] [4 3 0 2 1] [1 2 4 0 3] [2 4 3 1 0] [4 3 0 2 1] [1 2 4 0 3] [2 4 3 1 0] [3 0 1 4 2] [1 2 4 0 3] [2 4 3 1 0] [3 0 1 4 2] [4 3 0 2 1] [2 4 3 1 0], [3 0 1 4 2], [4 3 0 2 1], [1 2 4 0 3] ] sage: designs.mutually_orthogonal_latin_squares(7,3) [ [0 1 2 3 4 5 6] [0 1 2 3 4 5 6] [0 1 2 3 4 5 6] [4 0 3 1 6 2 5] [5 6 0 4 2 1 3] [6 4 1 0 5 3 2] [5 6 0 4 2 1 3] [6 4 1 0 5 3 2] [1 3 5 2 0 6 4] [6 4 1 0 5 3 2] [1 3 5 2 0 6 4] [2 5 4 6 3 0 1] [1 3 5 2 0 6 4] [2 5 4 6 3 0 1] [3 2 6 5 1 4 0] [2 5 4 6 3 0 1] [3 2 6 5 1 4 0] [4 0 3 1 6 2 5] [3 2 6 5 1 4 0], [4 0 3 1 6 2 5], [5 6 0 4 2 1 3] ] sage: designs.mutually_orthogonal_latin_squares(5,2,partitions=True) [[[0, 1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11, 12, 13, 14], [15, 16, 17, 18, 19], [20, 21, 22, 23, 24]], [[0, 5, 10, 15, 20], [1, 6, 11, 16, 21], [2, 7, 12, 17, 22], [3, 8, 13, 18, 23], [4, 9, 14, 19, 24]], [[0, 6, 12, 18, 24], [1, 7, 14, 15, 23], [2, 9, 13, 16, 20], [3, 5, 11, 19, 22], [4, 8, 10, 17, 21]], [[0, 7, 13, 19, 21], [1, 9, 10, 18, 22], [2, 8, 11, 15, 24], [3, 6, 14, 17, 20], [4, 5, 12, 16, 23]]] TESTS:: sage: designs.mutually_orthogonal_latin_squares(5,5) Traceback (most recent call last): ... ValueError: There exist at most n-1 MOLS of size n. """ from sage.rings.finite_rings.constructor import FiniteField from sage.combinat.designs.block_design import AffineGeometryDesign from sage.rings.arith import is_prime_power from sage.matrix.constructor import Matrix from sage.rings.arith import factor if k is not None and k >= n: raise ValueError("There exist at most n-1 MOLS of size n.") if is_prime_power(n): if k is None: k = n - 1 # Section 6.4.1 of [Stinson2004] Fp = FiniteField(n, 'x') B = AffineGeometryDesign(2, 1, Fp).blocks() parallel_classes = [[] for _ in range(k + 2)] for b in B: for p in parallel_classes: if (not p) or all(i not in p[0] for i in b): p.append(b) break if partitions: return parallel_classes coord = {v: i for i, L in enumerate(parallel_classes[0]) for v in L} coord = { v: (coord[v], i) for i, L in enumerate(parallel_classes[1]) for v in L } matrices = [] for P in parallel_classes[2:]: matrices.append( Matrix({coord[v]: i for i, L in enumerate(P) for v in L})) return matrices else: # Theorem 6.33 of [Stinson2004], MacNeish's theorem. subcases = [p**i for p, i in factor(n)] s = min(subcases) - 1 if k is None: k = s elif k > s: raise NotImplementedError("I don't know how to build these MOLS.") subcalls = [mutually_orthogonal_latin_squares(p, k) for p in subcases] matrices = [ latin_square_product(*[sc[i] for sc in subcalls]) for i in range(k) ] if partitions: partitions = [[[i * n + j for j in range(n)] for i in range(n)], [[j * n + i for j in range(n)] for i in range(n)]] for m in matrices: partition = [[] for i in range(n)] for i in range(n): for j in range(n): partition[m[i, j]].append(i * n + j) partitions.append(partition) return partitions else: return matrices
def TaylorTwographDescendantSRG(q, clique_partition=None): r""" constructing the descendant graph of the Taylor's two-graph for `U_3(q)`, `q` odd This is a strongly regular graph with parameters `(v,k,\lambda,\mu)=(q^3, (q^2+1)(q-1)/2, (q-1)^3/4-1, (q^2+1)(q-1)/4)` obtained as a two-graph descendant of the :func:`Taylor's two-graph <sage.combinat.designs.twographs.taylor_twograph>` `T`. This graph admits a partition into cliques of size `q`, which are useful in :func:`TaylorTwographSRG <sage.graphs.generators.classical_geometries.TaylorTwographSRG>`, a strongly regular graph on `q^3+1` vertices in the Seidel switching class of `T`, for which we need `(q^2+1)/2` cliques. The cliques are the `q^2` lines on `v_0` of the projective plane containing the unital for `U_3(q)`, and intersecting the unital (i.e. the vertices of the graph and the point we remove) in `q+1` points. This is all taken from §7E of [BvL84]_. INPUT: - ``q`` -- a power of an odd prime number - ``clique_partition`` -- if ``True``, return `q^2-1` cliques of size `q` with empty pairwise intersection. (Removing all of them leaves a clique, too), and the point removed from the unital. EXAMPLES:: sage: g=graphs.TaylorTwographDescendantSRG(3); g Taylor two-graph descendant SRG: Graph on 27 vertices sage: g.is_strongly_regular(parameters=True) (27, 10, 1, 5) sage: from sage.combinat.designs.twographs import taylor_twograph sage: T = taylor_twograph(3) # long time sage: g.is_isomorphic(T.descendant(T.ground_set()[1])) # long time True sage: g=graphs.TaylorTwographDescendantSRG(5) # not tested (long time) sage: g.is_strongly_regular(parameters=True) # not tested (long time) (125, 52, 15, 26) TESTS:: sage: g,l,_=graphs.TaylorTwographDescendantSRG(3,clique_partition=True) sage: all(map(lambda x: g.is_clique(x), l)) True sage: graphs.TaylorTwographDescendantSRG(4) Traceback (most recent call last): ... ValueError: q must be an odd prime power sage: graphs.TaylorTwographDescendantSRG(6) Traceback (most recent call last): ... ValueError: q must be an odd prime power """ from sage.rings.arith import is_prime_power p, k = is_prime_power(q, get_data=True) if k == 0 or p == 2: raise ValueError('q must be an odd prime power') from sage.schemes.projective.projective_space import ProjectiveSpace from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector from sage.rings.finite_rings.integer_mod import mod from __builtin__ import sum Fq = FiniteField(q**2, 'a') PG = map(tuple, ProjectiveSpace(2, Fq)) def S(x, y): return sum(map(lambda j: x[j] * y[2 - j]**q, xrange(3))) V = filter(lambda x: S(x, x) == 0, PG) # the points of the unital v0 = V[0] V.remove(v0) if mod(q, 4) == 1: G = Graph( [V, lambda y, z: not (S(v0, y) * S(y, z) * S(z, v0)).is_square()], loops=False) else: G = Graph( [V, lambda y, z: (S(v0, y) * S(y, z) * S(z, v0)).is_square()], loops=False) G.name("Taylor two-graph descendant SRG") if clique_partition: lines = map(lambda x: filter(lambda t: t[0] + x * t[1] == 0, V), filter(lambda z: z != 0, Fq)) return (G, lines, v0) else: return G
def v_4_1_BIBD(v, check=True): r""" Returns a `(v,4,1)`-BIBD. A `(v,4,1)`-BIBD is an edge-decomposition of the complete graph `K_v` into copies of `K_4`. For more information, see :meth:`BalancedIncompleteBlockDesign`. It exists if and only if `v\equiv 1,4 \pmod {12}`. See page 167 of [Stinson2004]_ for the construction details. .. SEEALSO:: * :meth:`BalancedIncompleteBlockDesign` INPUT: - ``v`` (integer) -- number of points. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: from sage.combinat.designs.bibd import v_4_1_BIBD # long time sage: for n in range(13,100): # long time ....: if n%12 in [1,4]: # long time ....: _ = v_4_1_BIBD(n, check = True) # long time """ from sage.rings.finite_rings.constructor import FiniteField k = 4 if v == 0: return [] if v <= 12 or v % 12 not in [1, 4]: raise ValueError( "A K_4-decomposition of K_v exists iif v=2,4 mod 12, v>12 or v==0") # Step 1. Base cases. if v == 13: from block_design import ProjectivePlaneDesign return ProjectivePlaneDesign(3).blocks() if v == 16: from block_design import AffineGeometryDesign return AffineGeometryDesign(2, 1, FiniteField(4, 'x')).blocks() if v == 25: return [[0, 1, 17, 22], [0, 2, 11, 21], [0, 3, 15, 18], [0, 4, 7, 13], [0, 5, 12, 14], [0, 6, 19, 23], [0, 8, 16, 24], [0, 9, 10, 20], [1, 2, 3, 4], [1, 5, 6, 7], [1, 8, 12, 15], [1, 9, 13, 16], [1, 10, 11, 14], [1, 18, 20, 23], [1, 19, 21, 24], [2, 5, 15, 24], [2, 6, 9, 17], [2, 7, 14, 18], [2, 8, 22, 23], [2, 10, 12, 13], [2, 16, 19, 20], [3, 5, 16, 22], [3, 6, 11, 20], [3, 7, 12, 19], [3, 8, 9, 14], [3, 10, 17, 24], [3, 13, 21, 23], [4, 5, 10, 23], [4, 6, 8, 21], [4, 9, 18, 24], [4, 11, 15, 16], [4, 12, 17, 20], [4, 14, 19, 22], [5, 8, 13, 20], [5, 9, 11, 19], [5, 17, 18, 21], [6, 10, 15, 22], [6, 12, 16, 18], [6, 13, 14, 24], [7, 8, 11, 17], [7, 9, 15, 23], [7, 10, 16, 21], [7, 20, 22, 24], [8, 10, 18, 19], [9, 12, 21, 22], [11, 12, 23, 24], [11, 13, 18, 22], [13, 15, 17, 19], [14, 15, 20, 21], [14, 16, 17, 23]] if v == 28: return [[0, 1, 23, 26], [0, 2, 10, 11], [0, 3, 16, 18], [0, 4, 15, 20], [0, 5, 8, 9], [0, 6, 22, 25], [0, 7, 14, 21], [0, 12, 17, 27], [0, 13, 19, 24], [1, 2, 24, 27], [1, 3, 11, 12], [1, 4, 17, 19], [1, 5, 14, 16], [1, 6, 9, 10], [1, 7, 20, 25], [1, 8, 15, 22], [1, 13, 18, 21], [2, 3, 21, 25], [2, 4, 12, 13], [2, 5, 18, 20], [2, 6, 15, 17], [2, 7, 19, 22], [2, 8, 14, 26], [2, 9, 16, 23], [3, 4, 22, 26], [3, 5, 7, 13], [3, 6, 14, 19], [3, 8, 20, 23], [3, 9, 15, 27], [3, 10, 17, 24], [4, 5, 23, 27], [4, 6, 7, 8], [4, 9, 14, 24], [4, 10, 16, 21], [4, 11, 18, 25], [5, 6, 21, 24], [5, 10, 15, 25], [5, 11, 17, 22], [5, 12, 19, 26], [6, 11, 16, 26], [6, 12, 18, 23], [6, 13, 20, 27], [7, 9, 17, 18], [7, 10, 26, 27], [7, 11, 23, 24], [7, 12, 15, 16], [8, 10, 18, 19], [8, 11, 21, 27], [8, 12, 24, 25], [8, 13, 16, 17], [9, 11, 19, 20], [9, 12, 21, 22], [9, 13, 25, 26], [10, 12, 14, 20], [10, 13, 22, 23], [11, 13, 14, 15], [14, 17, 23, 25], [14, 18, 22, 27], [15, 18, 24, 26], [15, 19, 21, 23], [16, 19, 25, 27], [16, 20, 22, 24], [17, 20, 21, 26]] if v == 37: return [[0, 1, 3, 24], [0, 2, 23, 36], [0, 4, 26, 32], [0, 5, 9, 31], [0, 6, 11, 15], [0, 7, 17, 25], [0, 8, 20, 27], [0, 10, 18, 30], [0, 12, 19, 29], [0, 13, 14, 16], [0, 21, 34, 35], [0, 22, 28, 33], [1, 2, 4, 25], [1, 5, 27, 33], [1, 6, 10, 32], [1, 7, 12, 16], [1, 8, 18, 26], [1, 9, 21, 28], [1, 11, 19, 31], [1, 13, 20, 30], [1, 14, 15, 17], [1, 22, 35, 36], [1, 23, 29, 34], [2, 3, 5, 26], [2, 6, 28, 34], [2, 7, 11, 33], [2, 8, 13, 17], [2, 9, 19, 27], [2, 10, 22, 29], [2, 12, 20, 32], [2, 14, 21, 31], [2, 15, 16, 18], [2, 24, 30, 35], [3, 4, 6, 27], [3, 7, 29, 35], [3, 8, 12, 34], [3, 9, 14, 18], [3, 10, 20, 28], [3, 11, 23, 30], [3, 13, 21, 33], [3, 15, 22, 32], [3, 16, 17, 19], [3, 25, 31, 36], [4, 5, 7, 28], [4, 8, 30, 36], [4, 9, 13, 35], [4, 10, 15, 19], [4, 11, 21, 29], [4, 12, 24, 31], [4, 14, 22, 34], [4, 16, 23, 33], [4, 17, 18, 20], [5, 6, 8, 29], [5, 10, 14, 36], [5, 11, 16, 20], [5, 12, 22, 30], [5, 13, 25, 32], [5, 15, 23, 35], [5, 17, 24, 34], [5, 18, 19, 21], [6, 7, 9, 30], [6, 12, 17, 21], [6, 13, 23, 31], [6, 14, 26, 33], [6, 16, 24, 36], [6, 18, 25, 35], [6, 19, 20, 22], [7, 8, 10, 31], [7, 13, 18, 22], [7, 14, 24, 32], [7, 15, 27, 34], [7, 19, 26, 36], [7, 20, 21, 23], [8, 9, 11, 32], [8, 14, 19, 23], [8, 15, 25, 33], [8, 16, 28, 35], [8, 21, 22, 24], [9, 10, 12, 33], [9, 15, 20, 24], [9, 16, 26, 34], [9, 17, 29, 36], [9, 22, 23, 25], [10, 11, 13, 34], [10, 16, 21, 25], [10, 17, 27, 35], [10, 23, 24, 26], [11, 12, 14, 35], [11, 17, 22, 26], [11, 18, 28, 36], [11, 24, 25, 27], [12, 13, 15, 36], [12, 18, 23, 27], [12, 25, 26, 28], [13, 19, 24, 28], [13, 26, 27, 29], [14, 20, 25, 29], [14, 27, 28, 30], [15, 21, 26, 30], [15, 28, 29, 31], [16, 22, 27, 31], [16, 29, 30, 32], [17, 23, 28, 32], [17, 30, 31, 33], [18, 24, 29, 33], [18, 31, 32, 34], [19, 25, 30, 34], [19, 32, 33, 35], [20, 26, 31, 35], [20, 33, 34, 36], [21, 27, 32, 36]] # Step 2 : this is function PBD_4_5_8_9_12 PBD = PBD_4_5_8_9_12((v - 1) / (k - 1), check=False) # Step 3 : Theorem 7.20 bibd = BIBD_from_PBD(PBD, v, k, check=False) if check: _check_pbd(bibd, v, [k]) return bibd
def UnitaryPolarGraph(m, q, algorithm="gap"): r""" Returns the Unitary Polar Graph `U(m,q)`. For more information on Unitary Polar graphs, see the `page of Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/srghub.html>`_. INPUT: - ``m,q`` (integers) -- `q` must be a prime power. - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP library interface, computing totally singular subspaces, which is faster for large examples (especially with `q>2`). Otherwise it is done directly. EXAMPLES:: sage: G = graphs.UnitaryPolarGraph(4,2); G Unitary Polar Graph U(4, 2); GQ(4, 2): Graph on 45 vertices sage: G.is_strongly_regular(parameters=True) (45, 12, 3, 3) sage: graphs.UnitaryPolarGraph(5,2).is_strongly_regular(parameters=True) (165, 36, 3, 9) sage: graphs.UnitaryPolarGraph(6,2) # not tested (long time) Unitary Polar Graph U(6, 2): Graph on 693 vertices TESTS:: sage: graphs.UnitaryPolarGraph(4,3, algorithm="gap").is_strongly_regular(parameters=True) (280, 36, 8, 4) sage: graphs.UnitaryPolarGraph(4,3).is_strongly_regular(parameters=True) (280, 36, 8, 4) sage: graphs.UnitaryPolarGraph(4,3, algorithm="foo") Traceback (most recent call last): ... ValueError: unknown algorithm! """ if algorithm == "gap": from sage.libs.gap.libgap import libgap G = _polar_graph(m, q**2, libgap.GeneralUnitaryGroup(m, q)) elif algorithm == None: # slow on large examples from sage.schemes.projective.projective_space import ProjectiveSpace from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module_element import free_module_element as vector from __builtin__ import sum as psum Fq = FiniteField(q**2, 'a') PG = map(vector, ProjectiveSpace(m - 1, Fq)) map(lambda x: x.set_immutable(), PG) def P(x, y): return psum(map(lambda j: x[j] * y[m - 1 - j]**q, xrange(m))) == 0 V = filter(lambda x: P(x, x), PG) G = Graph( [ V, lambda x, y: # bottleneck is here, of course: P(x, y) ], loops=False) else: raise ValueError("unknown algorithm!") G.relabel() G.name("Unitary Polar Graph U" + str((m, q))) if m == 4: G.name(G.name() + '; GQ' + str((q**2, q))) if m == 5: G.name(G.name() + '; GQ' + str((q**2, q**3))) return G
def SymplecticPolarGraph(d, q, algorithm=None): r""" Returns the Symplectic Polar Graph `Sp(d,q)`. The Symplectic Polar Graph `Sp(d,q)` is built from a projective space of dimension `d-1` over a field `F_q`, and a symplectic form `f`. Two vertices `u,v` are made adjacent if `f(u,v)=0`. See the page `on symplectic graphs on Andries Brouwer's website <http://www.win.tue.nl/~aeb/graphs/Sp.html>`_. INPUT: - ``d,q`` (integers) -- note that only even values of `d` are accepted by the function. - ``algorithm`` -- if set to 'gap' then the computation is carried via GAP library interface, computing totally singular subspaces, which is faster for `q>3`. Otherwise it is done directly. EXAMPLES: Computation of the spectrum of `Sp(6,2)`:: sage: g = graphs.SymplecticGraph(6,2) doctest:...: DeprecationWarning: SymplecticGraph is deprecated. Please use sage.graphs.generators.classical_geometries.SymplecticPolarGraph instead. See http://trac.sagemath.org/19136 for details. sage: g.is_strongly_regular(parameters=True) (63, 30, 13, 15) sage: set(g.spectrum()) == {-5, 3, 30} True The parameters of `Sp(4,q)` are the same as of `O(5,q)`, but they are not isomorphic if `q` is odd:: sage: G = graphs.SymplecticPolarGraph(4,3) sage: G.is_strongly_regular(parameters=True) (40, 12, 2, 4) sage: O=graphs.OrthogonalPolarGraph(5,3) sage: O.is_strongly_regular(parameters=True) (40, 12, 2, 4) sage: O.is_isomorphic(G) False sage: graphs.SymplecticPolarGraph(6,4,algorithm="gap").is_strongly_regular(parameters=True) # not tested (long time) (1365, 340, 83, 85) TESTS:: sage: graphs.SymplecticPolarGraph(4,4,algorithm="gap").is_strongly_regular(parameters=True) (85, 20, 3, 5) sage: graphs.SymplecticPolarGraph(4,4).is_strongly_regular(parameters=True) (85, 20, 3, 5) sage: graphs.SymplecticPolarGraph(4,4,algorithm="blah") Traceback (most recent call last): ... ValueError: unknown algorithm! """ if d < 1 or d % 2 != 0: raise ValueError("d must be even and greater than 2") if algorithm == "gap": # faster for larger (q>3) fields from sage.libs.gap.libgap import libgap G = _polar_graph(d, q, libgap.SymplecticGroup(d, q)) elif algorithm == None: # faster for small (q<4) fields from sage.rings.finite_rings.constructor import FiniteField from sage.modules.free_module import VectorSpace from sage.schemes.projective.projective_space import ProjectiveSpace from sage.matrix.constructor import identity_matrix, block_matrix, zero_matrix F = FiniteField(q, "x") M = block_matrix(F, 2, 2, [ zero_matrix(F, d / 2), identity_matrix(F, d / 2), -identity_matrix(F, d / 2), zero_matrix(F, d / 2) ]) V = VectorSpace(F, d) PV = list(ProjectiveSpace(d - 1, F)) G = Graph([[tuple(_) for _ in PV], lambda x, y: V(x) * (M * V(y)) == 0], loops=False) else: raise ValueError("unknown algorithm!") G.name("Symplectic Polar Graph Sp(" + str(d) + "," + str(q) + ")") G.relabel() return G
def HughesPlane(q2, check=True): r""" Return the Hughes projective plane of order ``q2``. Let `q` be an odd prime, the Hughes plane of order `q^2` is a finite projective plane of order `q^2` introduced by D. Hughes in [Hu57]_. Its construction is as follows. Let `K = GF(q^2)` be a finite field with `q^2` elements and `F = GF(q) \subset K` be its unique subfield with `q` elements. We define a twisted multiplication on `K` as .. MATH:: x \circ y = \begin{cases} x\ y & \text{if y is a square in K}\\ x^q\ y & \text{otherwise} \end{cases} The points of the Hughes plane are the triples `(x, y, z)` of points in `K^3 \backslash \{0,0,0\}` up to the equivalence relation `(x,y,z) \sim (x \circ k, y \circ k, z \circ k)` where `k \in K`. For `a = 1` or `a \in (K \backslash F)` we define a block `L(a)` as the set of triples `(x,y,z)` so that `x + a \circ y + z = 0`. The rest of the blocks are obtained by letting act the group `GL(3, F)` by its standard action. For more information, see :wikipedia:`Hughes_plane` and [We07]. .. SEEALSO:: :func:`DesarguesianProjectivePlaneDesign` to build the Desarguesian projective planes INPUT: - ``q2`` -- an even power of an odd prime number - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: H = designs.HughesPlane(9) sage: H (91,10,1)-Balanced Incomplete Block Design We prove in the following computations that the Desarguesian plane ``H`` is not Desarguesian. Let us consider the two triangles `(0,1,10)` and `(57, 70, 59)`. We show that the intersection points `D_{0,1} \cap D_{57,70}`, `D_{1,10} \cap D_{70,59}` and `D_{10,0} \cap D_{59,57}` are on the same line while `D_{0,70}`, `D_{1,59}` and `D_{10,57}` are not concurrent:: sage: blocks = H.blocks() sage: line = lambda p,q: (b for b in blocks if p in b and q in b).next() sage: b_0_1 = line(0, 1) sage: b_1_10 = line(1, 10) sage: b_10_0 = line(10, 0) sage: b_57_70 = line(57, 70) sage: b_70_59 = line(70, 59) sage: b_59_57 = line(59, 57) sage: set(b_0_1).intersection(b_57_70) {2} sage: set(b_1_10).intersection(b_70_59) {73} sage: set(b_10_0).intersection(b_59_57) {60} sage: line(2, 73) == line(73, 60) True sage: b_0_57 = line(0, 57) sage: b_1_70 = line(1, 70) sage: b_10_59 = line(10, 59) sage: p = set(b_0_57).intersection(b_1_70) sage: q = set(b_1_70).intersection(b_10_59) sage: p == q False TESTS: Some wrong input:: sage: designs.HughesPlane(5) Traceback (most recent call last): ... EmptySetError: No Hughes plane of non-square order exists. sage: designs.HughesPlane(16) Traceback (most recent call last): ... EmptySetError: No Hughes plane of even order exists. Check that it works for non-prime `q`:: sage: designs.HughesPlane(3**4) # not tested - 10 secs (6643,82,1)-Balanced Incomplete Block Design """ if not q2.is_square(): raise EmptySetError("No Hughes plane of non-square order exists.") if q2 % 2 == 0: raise EmptySetError("No Hughes plane of even order exists.") q = q2.sqrt() K = FiniteField(q2, prefix='x', conway=True) F = FiniteField(q, prefix='y', conway=True) A = q3_minus_one_matrix(F) A = A.change_ring(K) m = K.list() V = VectorSpace(K, 3) zero = K.zero() one = K.one() points = [(x, y, one) for x in m for y in m] + \ [(x, one, zero) for x in m] + \ [(one, zero, zero)] relabel = {tuple(p): i for i, p in enumerate(points)} blcks = [] for a in m: if a not in F or a == 1: # build L(a) aa = ~a l = [] l.append(V((-a, one, zero))) for x in m: y = -aa * (x + one) if not y.is_square(): y *= aa**(q - 1) l.append(V((x, y, one))) # compute the orbit of L(a) blcks.append( [relabel[normalize_hughes_plane_point(p, q)] for p in l]) for i in range(q2 + q): l = [A * j for j in l] blcks.append( [relabel[normalize_hughes_plane_point(p, q)] for p in l]) from bibd import BalancedIncompleteBlockDesign return BalancedIncompleteBlockDesign(q2**2 + q2 + 1, blcks, check=check)
def DesarguesianProjectivePlaneDesign(n, check=True): r""" Return the Desarguesian projective plane of order ``n`` as a 2-design. The Desarguesian projective plane of order `n` can also be defined as the projective plane over a field of order `n`. For more information, have a look at :wikipedia:`Projective_plane`. INPUT: - ``n`` -- an integer which must be a power of a prime number - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. .. SEEALSO:: :func:`ProjectiveGeometryDesign` EXAMPLES:: sage: designs.DesarguesianProjectivePlaneDesign(2) (7,3,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(3) (13,4,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(4) (21,5,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(5) (31,6,1)-Balanced Incomplete Block Design sage: designs.DesarguesianProjectivePlaneDesign(6) Traceback (most recent call last): ... ValueError: the order of a finite field must be a prime power """ K = FiniteField(n, 'x') n2 = n**2 relabel = {x: i for i, x in enumerate(K)} Kiter = relabel # it is much faster to iterate throug a dict than through # the finite field K # we decompose the (equivalence class) of points [x:y:z] of the projective # plane into an affine plane, an affine line and a point. At the same time, # we relabel the points with the integers from 0 to n^2 + n as follows: # - the affine plane is the set of points [x:y:1] (i.e. the third coordinate # is non-zero) and gets relabeled from 0 to n^2-1 affine_plane = lambda x, y: relabel[x] + n * relabel[y] # - the affine line is the set of points [x:1:0] (i.e. the third coordinate is # zero but not the second one) and gets relabeld from n^2 to n^2 + n - 1 line_infinity = lambda x: n2 + relabel[x] # - the point is [1:0:0] and gets relabeld n^2 + n point_infinity = n2 + n blcks = [] # the n^2 lines of the form "x = sy + az" for s in Kiter: for a in Kiter: # points in the affine plane blcks.append([affine_plane(s * y + a, y) for y in Kiter]) # point at infinity blcks[-1].append(line_infinity(s)) # the n horizontals of the form "y = az" for a in Kiter: # points in the affine plane blcks.append([affine_plane(x, a) for x in Kiter]) # point at infinity blcks[-1].append(point_infinity) # the line at infinity "z = 0" blcks.append(range(n2, n2 + n + 1)) if check: from designs_pyx import is_projective_plane if not is_projective_plane(blcks): raise RuntimeError( 'There is a problem in the function DesarguesianProjectivePlane' ) from bibd import BalancedIncompleteBlockDesign return BalancedIncompleteBlockDesign(n2 + n + 1, blcks, check=check)
def difference_matrix(g, k, lmbda=1, existence=False, check=True): r""" Return a `(g,k,\lambda)`-difference matrix A matrix `M` is a `(g,k,\lambda)`-difference matrix if it has size `\lambda g\times k`, its entries belong to the group `G` of cardinality `g`, and for any two rows `R,R'` of `M` and `x\in G` there are exactly `\lambda` values `i` such that `R_i-R'_i=x`. INPUT: - ``k`` -- (integer) number of columns. If ``k=None`` it is set to the largest value available. - ``g`` -- (integer) cardinality of the group `G` - ``lmbda`` -- (integer; default: 1) -- number of times each element of `G` appears as a difference. - ``check`` -- (boolean) Whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. - ``existence`` (boolean) -- instead of building the design, return: - ``True`` -- meaning that Sage knows how to build the design - ``Unknown`` -- meaning that Sage does not know how to build the design, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the design does not exist. .. NOTE:: When ``k=None`` and ``existence=True`` the function returns an integer, i.e. the largest `k` such that we can build a `(g,k,\lambda)`-DM. EXAMPLES:: sage: G,M = designs.difference_matrix(25,10); G Finite Field in x of size 5^2 sage: designs.difference_matrix(993,None,existence=1) 32 Here we print for each `g` the maximum possible `k` for which Sage knows how to build a `(g,k,1)`-difference matrix:: sage: for g in range(2,30): ....: k_max = designs.difference_matrix(g=g,k=None,existence=True) ....: print "{:2} {}".format(g, k_max) ....: _ = designs.difference_matrix(g,k_max) 2 2 3 3 4 4 5 5 6 2 7 7 8 8 9 9 10 2 11 11 12 6 13 13 14 2 15 3 16 16 17 17 18 2 19 19 20 4 21 6 22 2 23 23 24 8 25 25 26 2 27 27 28 6 29 29 TESTS:: sage: designs.difference_matrix(10,12,1,existence=True) False sage: designs.difference_matrix(10,12,1) Traceback (most recent call last): ... EmptySetError: No (10,12,1)-Difference Matrix exists as k(=12)>g(=10) sage: designs.difference_matrix(10,9,1,existence=True) Unknown sage: designs.difference_matrix(10,9,1) Traceback (most recent call last): ... NotImplementedError: I don't know how to build a (10,9,1)-Difference Matrix! """ if lmbda == 1 and k is not None and k > g: if existence: return False raise EmptySetError( "No ({},{},{})-Difference Matrix exists as k(={})>g(={})".format( g, k, lmbda, k, g)) # Prime powers elif lmbda == 1 and is_prime_power(g): if k is None: if existence: return g else: k = g elif existence: return True F = FiniteField(g, 'x') F_set = list(F) F_k_set = F_set[:k] G = F M = [[x * y for y in F_k_set] for x in F_set] # Treat the case k=None # (find the max k such that there exists a DM) elif k is None: i = 2 while difference_matrix(g=g, k=i, lmbda=lmbda, existence=True): i += 1 return i - 1 # From the database elif (g, lmbda) in DM_constructions and DM_constructions[g, lmbda][0] >= k: if existence: return True _, f = DM_constructions[g, lmbda] G, M = f() M = [R[:k] for R in M] # Product construction elif find_product_decomposition(g, k, lmbda): if existence: return True (g1, lmbda1), (g2, lmbda2) = find_product_decomposition(g, k, lmbda) G1, M1 = difference_matrix(g1, k, lmbda1) G2, M2 = difference_matrix(g2, k, lmbda2) G, M = difference_matrix_product(k, M1, G1, lmbda1, M2, G2, lmbda2, check=False) else: if existence: return Unknown raise NotImplementedError( "I don't know how to build a ({},{},{})-Difference Matrix!".format( g, k, lmbda)) if check and not is_difference_matrix(M, G, k, lmbda, 1): raise RuntimeError( "Sage built something which is not a ({},{},{})-DM!".format( g, k, lmbda)) return G, M