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 DuadicCodeOddPair(F, S1, S2): """ Constructs the "odd pair" of duadic codes associated to the "splitting" S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeOddPair(GF(q),S1,S2) ([11, 6] Cyclic Code over GF(3), [11, 6] Cyclic Code over GF(3)) This is consistent with Theorem 6.1.3 in [HP2003]_. """ from sage.misc.stopgap import stopgap stopgap( "The function DuadicCodeOddPair has several issues which may cause wrong results", 25896) from .cyclic_code import CyclicCode n = len(S1) + len(S2) + 1 if not _is_a_splitting(S1, S2, n): raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n)) q = F.order() k = Mod(q, n).multiplicative_order() FF = GF(q**k, "z") z = FF.gen() zeta = z**((q**k - 1) / n) P1 = PolynomialRing(FF, "x") x = P1.gen() g1 = prod([x - zeta**i for i in S1 + [0]]) g2 = prod([x - zeta**i for i in S2 + [0]]) j = sum([x**i / n for i in range(n)]) P2 = PolynomialRing(F, "x") x = P2.gen() coeffs1 = [ _lift2smallest_field(c)[0] for c in (g1 + j).coefficients(sparse=False) ] coeffs2 = [ _lift2smallest_field(c)[0] for c in (g2 + j).coefficients(sparse=False) ] gg1 = P2(coeffs1) gg2 = P2(coeffs2) gg1 = gcd(gg1, x**n - 1) gg2 = gcd(gg2, x**n - 1) C1 = CyclicCode(length=n, generator_pol=gg1) C2 = CyclicCode(length=n, generator_pol=gg2) return C1, C2
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.finite_field_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 DuadicCodeEvenPair(F, S1, S2): r""" Constructs the "even pair" of duadic codes associated to the "splitting" (see the docstring for ``_is_a_splitting`` for the definition) S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeEvenPair(GF(q),S1,S2) ([11, 5] Cyclic Code over GF(3), [11, 5] Cyclic Code over GF(3)) """ from sage.misc.stopgap import stopgap stopgap( "The function DuadicCodeEvenPair has several issues which may cause wrong results", 25896) from .cyclic_code import CyclicCode n = len(S1) + len(S2) + 1 if not _is_a_splitting(S1, S2, n): raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n)) q = F.order() k = Mod(q, n).multiplicative_order() FF = GF(q**k, "z") z = FF.gen() zeta = z**((q**k - 1) / n) P1 = PolynomialRing(FF, "x") x = P1.gen() g1 = prod([x - zeta**i for i in S1 + [0]]) g2 = prod([x - zeta**i for i in S2 + [0]]) P2 = PolynomialRing(F, "x") x = P2.gen() gg1 = P2( [_lift2smallest_field(c)[0] for c in g1.coefficients(sparse=False)]) gg2 = P2( [_lift2smallest_field(c)[0] for c in g2.coefficients(sparse=False)]) C1 = CyclicCode(length=n, generator_pol=gg1) C2 = CyclicCode(length=n, generator_pol=gg2) return C1, C2
def DuadicCodeOddPair(F, S1, S2): """ Constructs the "odd pair" of duadic codes associated to the "splitting" S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeOddPair(GF(q),S1,S2) (Linear code of length 11, dimension 6 over Finite Field of size 3, Linear code of length 11, dimension 6 over Finite Field of size 3) This is consistent with Theorem 6.1.3 in [HP]_. """ n = len(S1) + len(S2) + 1 if not is_a_splitting(S1, S2, n): raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n)) q = F.order() k = Mod(q, n).multiplicative_order() FF = GF(q**k, "z") z = FF.gen() zeta = z**((q**k - 1) / n) P1 = PolynomialRing(FF, "x") x = P1.gen() g1 = prod([x - zeta**i for i in S1 + [0]]) g2 = prod([x - zeta**i for i in S2 + [0]]) j = sum([x**i / n for i in range(n)]) P2 = PolynomialRing(F, "x") x = P2.gen() coeffs1 = [ lift2smallest_field(c)[0] for c in (g1 + j).coefficients(sparse=False) ] coeffs2 = [ lift2smallest_field(c)[0] for c in (g2 + j).coefficients(sparse=False) ] gg1 = P2(coeffs1) gg2 = P2(coeffs2) C1 = CyclicCodeFromGeneratingPolynomial(n, gg1) C2 = CyclicCodeFromGeneratingPolynomial(n, gg2) return C1, C2
def DuadicCodeOddPair(F,S1,S2): """ Constructs the "odd pair" of duadic codes associated to the "splitting" S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeOddPair(GF(q),S1,S2) ([11, 6] Cyclic Code over GF(3), [11, 6] Cyclic Code over GF(3)) This is consistent with Theorem 6.1.3 in [HP2003]_. """ from .cyclic_code import CyclicCode n = len(S1) + len(S2) + 1 if not _is_a_splitting(S1,S2,n): raise TypeError("%s, %s must be a splitting of %s."%(S1,S2,n)) q = F.order() k = Mod(q,n).multiplicative_order() FF = GF(q**k,"z") z = FF.gen() zeta = z**((q**k-1)/n) P1 = PolynomialRing(FF,"x") x = P1.gen() g1 = prod([x-zeta**i for i in S1+[0]]) g2 = prod([x-zeta**i for i in S2+[0]]) j = sum([x**i/n for i in range(n)]) P2 = PolynomialRing(F,"x") x = P2.gen() coeffs1 = [_lift2smallest_field(c)[0] for c in (g1+j).coefficients(sparse=False)] coeffs2 = [_lift2smallest_field(c)[0] for c in (g2+j).coefficients(sparse=False)] gg1 = P2(coeffs1) gg2 = P2(coeffs2) gg1 = gcd(gg1, x**n - 1) gg2 = gcd(gg2, x**n - 1) C1 = CyclicCode(length = n, generator_pol = gg1) C2 = CyclicCode(length = n, generator_pol = gg2) return C1,C2
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 + 4*x^11 + 2*x^10 + 4*x^9 + 2*x^8 + 2*x^7 + 4*x^6 + x^5 + 2*x^4 + 2*x^2 + 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 __init__(this, p, category=None): """ TESTS:: sage: from yacop.testing.testalgebra import Testclass1 sage: X=Testclass1(5) sage: X.monomial((2,1,8,0)) x^2*a*y^8 sage: X.monomial((3,0,-17,1)) x^3*b/y^17 sage: from yacop.utils.region import region sage: X._manual_test_left_action(region(tmax=20)) 1126 non-zero multiplications checked sage: X._manual_test_left_conj_action(region(tmax=20)) # long time 1148 non-zero multiplications checked """ # must admit the "category" keyword for suspendability R = PolynomialRing(FiniteField(p), "x,a,y,b") x, a, y, b = R.gens() I = R.ideal([a**2, b**2]) degs = lambda idx: (1, -1, 0) if (idx & 1) else (2, 0, 0) SteenrodAlgebraBase.__init__(this, R, [degs(n) for n in range(4)], I, SteenrodAlgebra(p), category=category)
def __init__(self, prime=2, level=1, base_ring=ZZ): r""" Create a supersingular module. EXAMPLES:: sage: SupersingularModule(3) Module of supersingular points on X_0(1)/F_3 over Integer Ring """ if not prime.is_prime(): raise ValueError("the argument prime must be a prime number") if prime.divides(level): raise ValueError( "the argument level must be coprime to the argument prime") if level != 1: raise NotImplementedError( "supersingular modules of level > 1 not yet implemented") self.__prime = prime self.__finite_field = FiniteField(prime**2, 'a') self.__level = level self.__hecke_matrices = {} hecke.HeckeModule_free_module.__init__(self, base_ring, prime * level, weight=2)
def _test_category_contains(self, tester=None, **options): """ Test the implicit __contains__ method of this category:: sage: from yacop.modules.projective_spaces import RealProjectiveSpace sage: M=RealProjectiveSpace() sage: M.category() Category of yacop left module algebras over mod 2 Steenrod algebra, milnor basis sage: M in M.category() True sage: M._test_category_contains() """ from sage.misc.lazy_format import LazyFormat from sage.rings.finite_rings.finite_field_constructor import FiniteField tester = self._tester(**options) tester.assertTrue( self in self.category(), LazyFormat("%s not contained in its category %s" % (self, self.category())), ) M = ModulesWithBasis(FiniteField(self.base_ring().characteristic())) tester.assertTrue(self in M, LazyFormat("%s not contained in %s" % (self, M)))
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.finite_field_constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) return super(AffineGroup, cls).__classcall__(cls, degree, ring)
def upper_bound_on_elliptic_factors(self, p=None, ellmax=2): r""" Return an upper bound (provably correct) on the number of elliptic curves of conductor equal to the level of this supersingular module. INPUT: - ``p`` -- (default: 997) prime to work modulo ALGORITHM: Currently we only use `T_2`. Function will be extended to use more Hecke operators later. The prime p is replaced by the smallest prime that does not divide the level. EXAMPLES:: sage: SupersingularModule(37).upper_bound_on_elliptic_factors() 2 (There are 4 elliptic curves of conductor 37, but only 2 isogeny classes.) """ from sage.misc.verbose import verbose # NOTE: The heuristic runtime is *very* roughly `p^2/(2\cdot 10^6)`. # ellmax -- (default: 2) use Hecke operators T_ell with ell <= ellmax if p is None: p = 997 while self.level() % p == 0: p = next_prime(p) ell = 2 t = self.hecke_matrix(ell).change_ring(FiniteField(p)) # TODO: temporarily try using sparse=False # turn this off when sparse rank is optimized. t = t.dense_matrix() B = ZZ(4 * ell).isqrt() bnd = 0 lower = -B upper = B + 1 for a in range(lower, upper): tm = verbose("computing T_%s - %s" % (ell, a)) if a == lower: c = a else: c = 1 for i in range(t.nrows()): t[i, i] += c tm = verbose("computing kernel", tm) # dim = t.kernel().dimension() dim = t.nullity() bnd += dim verbose('got dimension = %s; new bound = %s' % (dim, bnd), tm) return bnd
def DuadicCodeEvenPair(F, S1, S2): r""" Constructs the "even pair" of duadic codes associated to the "splitting" (see the docstring for ``is_a_splitting`` for the definition) S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeEvenPair(GF(q),S1,S2) (Linear code of length 11, dimension 5 over Finite Field of size 3, Linear code of length 11, dimension 5 over Finite Field of size 3) """ n = len(S1) + len(S2) + 1 if not is_a_splitting(S1, S2, n): raise TypeError("%s, %s must be a splitting of %s." % (S1, S2, n)) q = F.order() k = Mod(q, n).multiplicative_order() FF = GF(q**k, "z") z = FF.gen() zeta = z**((q**k - 1) / n) P1 = PolynomialRing(FF, "x") x = P1.gen() g1 = prod([x - zeta**i for i in S1 + [0]]) g2 = prod([x - zeta**i for i in S2 + [0]]) P2 = PolynomialRing(F, "x") x = P2.gen() gg1 = P2( [lift2smallest_field(c)[0] for c in g1.coefficients(sparse=False)]) gg2 = P2( [lift2smallest_field(c)[0] for c in g2.coefficients(sparse=False)]) C1 = CyclicCodeFromGeneratingPolynomial(n, gg1) C2 = CyclicCodeFromGeneratingPolynomial(n, gg2) return C1, C2
def szekeres_difference_set_pair(m, check=True): r""" Construct Szekeres `(2m+1,m,1)`-cyclic difference family Let `4m+3` be a prime power. Theorem 3 in [Sz69]_ contains a construction of a pair of *complementary difference sets* `A`, `B` in the subgroup `G` of the quadratic residues in `F_{4m+3}^*`. Namely `|A|=|B|=m`, `a\in A` whenever `a-1\in G`, `b\in B` whenever `b+1 \in G`. See also Theorem 2.6 in [SWW72]_ (there the formula for `B` is correct, as opposed to (4.2) in [Sz69]_, where the sign before `1` is wrong. In modern terminology, for `m>1` the sets `A` and `B` form a :func:`difference family<sage.combinat.designs.difference_family>` with parameters `(2m+1,m,1)`. I.e. each non-identity `g \in G` can be expressed uniquely as `xy^{-1}` for `x,y \in A` or `x,y \in B`. Other, specific to this construction, properties of `A` and `B` are: for `a` in `A` one has `a^{-1}` not in `A`, whereas for `b` in `B` one has `b^{-1}` in `B`. INPUT: - ``m`` (integer) -- dimension of the matrix - ``check`` (default: ``True``) -- whether to check `A` and `B` for correctness EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import szekeres_difference_set_pair sage: G,A,B=szekeres_difference_set_pair(6) sage: G,A,B=szekeres_difference_set_pair(7) REFERENCE: .. [Sz69] \G. Szekeres, Tournaments and Hadamard matrices, Enseignement Math. (2) 15(1969), 269-278 """ from sage.rings.finite_rings.finite_field_constructor import GF F = GF(4 * m + 3) t = F.multiplicative_generator()**2 G = F.cyclotomic_cosets(t, cosets=[F.one()])[0] sG = set(G) A = filter(lambda a: a - F.one() in sG, G) B = filter(lambda b: b + F.one() in sG, G) if check: from itertools import product, chain assert (len(A) == len(B) == m) if m > 1: assert (sG == set( [xy[0] / xy[1] for xy in chain(product(A, A), product(B, B))])) assert (all(F.one() / b + F.one() in sG for b in B)) assert (not any(F.one() / a - F.one() in sG for a in A)) return G, A, B
def DuadicCodeEvenPair(F,S1,S2): r""" Constructs the "even pair" of duadic codes associated to the "splitting" (see the docstring for ``_is_a_splitting`` for the definition) S1, S2 of n. .. warning:: Maybe the splitting should be associated to a sum of q-cyclotomic cosets mod n, where q is a *prime*. EXAMPLES:: sage: from sage.coding.code_constructions import _is_a_splitting sage: n = 11; q = 3 sage: C = Zmod(n).cyclotomic_cosets(q); C [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: S1 = C[1] sage: S2 = C[2] sage: _is_a_splitting(S1,S2,11) True sage: codes.DuadicCodeEvenPair(GF(q),S1,S2) ([11, 5] Cyclic Code over GF(3), [11, 5] Cyclic Code over GF(3)) """ from .cyclic_code import CyclicCode n = len(S1) + len(S2) + 1 if not _is_a_splitting(S1,S2,n): raise TypeError("%s, %s must be a splitting of %s."%(S1,S2,n)) q = F.order() k = Mod(q,n).multiplicative_order() FF = GF(q**k,"z") z = FF.gen() zeta = z**((q**k-1)/n) P1 = PolynomialRing(FF,"x") x = P1.gen() g1 = prod([x-zeta**i for i in S1+[0]]) g2 = prod([x-zeta**i for i in S2+[0]]) P2 = PolynomialRing(F,"x") x = P2.gen() gg1 = P2([_lift2smallest_field(c)[0] for c in g1.coefficients(sparse=False)]) gg2 = P2([_lift2smallest_field(c)[0] for c in g2.coefficients(sparse=False)]) C1 = CyclicCode(length = n, generator_pol = gg1) C2 = CyclicCode(length = n, generator_pol = gg2) return C1,C2
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 time """ 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 szekeres_difference_set_pair(m, check=True): r""" Construct Szekeres `(2m+1,m,1)`-cyclic difference family Let `4m+3` be a prime power. Theorem 3 in [Sz69]_ contains a construction of a pair of *complementary difference sets* `A`, `B` in the subgroup `G` of the quadratic residues in `F_{4m+3}^*`. Namely `|A|=|B|=m`, `a\in A` whenever `a-1\in G`, `b\in B` whenever `b+1 \in G`. See also Theorem 2.6 in [SWW72]_ (there the formula for `B` is correct, as opposed to (4.2) in [Sz69]_, where the sign before `1` is wrong. In modern terminology, for `m>1` the sets `A` and `B` form a :func:`difference family<sage.combinat.designs.difference_family>` with parameters `(2m+1,m,1)`. I.e. each non-identity `g \in G` can be expressed uniquely as `xy^{-1}` for `x,y \in A` or `x,y \in B`. Other, specific to this construction, properties of `A` and `B` are: for `a` in `A` one has `a^{-1}` not in `A`, whereas for `b` in `B` one has `b^{-1}` in `B`. INPUT: - ``m`` (integer) -- dimension of the matrix - ``check`` (default: ``True``) -- whether to check `A` and `B` for correctness EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import szekeres_difference_set_pair sage: G,A,B=szekeres_difference_set_pair(6) sage: G,A,B=szekeres_difference_set_pair(7) REFERENCE: .. [Sz69] \G. Szekeres, Tournaments and Hadamard matrices, Enseignement Math. (2) 15(1969), 269-278 """ from sage.rings.finite_rings.finite_field_constructor import GF F = GF(4*m+3) t = F.multiplicative_generator()**2 G = F.cyclotomic_cosets(t, cosets=[F.one()])[0] sG = set(G) A = filter(lambda a: a-F.one() in sG, G) B = filter(lambda b: b+F.one() in sG, G) if check: from itertools import product, chain assert(len(A)==len(B)==m) if m>1: assert(sG==set([xy[0]/xy[1] for xy in chain(product(A,A), product(B,B))])) assert(all(F.one()/b+F.one() in sG for b in B)) assert(not any(F.one()/a-F.one() in sG for a in A)) return G,A,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() SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def steenrod_algebra_intersect(algebras): """ TESTS:: sage: from yacop.categories.utils import steenrod_algebra_intersect sage: A = SteenrodAlgebra(3) sage: B = SteenrodAlgebra(3,profile=((1,),(2,2))) sage: C = SteenrodAlgebra(3,profile=((),(1,2))) sage: steenrod_algebra_intersect((A,B)) sub-Hopf algebra of mod 3 Steenrod algebra, milnor basis, profile function ([1], [2, 2]) sage: steenrod_algebra_intersect((A,GF(3),A)) Finite Field of size 3 """ from sage.algebras.steenrod.steenrod_algebra import SteenrodAlgebra for dummy in (0,): A0 = algebras[0] if not all(A.characteristic() == A0.characteristic() for A in algebras): break for A in algebras: if not hasattr(A, "is_generic"): # this algebra is not a Steenrod algebra return FiniteField(A.characteristic()) if not all(A.is_generic() == A0.is_generic() for A in algebras): break rtrunc = +Infinity if all(A._truncation_type > 0 for A in algebras) else 0 isgen = A0.is_generic() profiles = ( [A._profile for A in algebras] if isgen else [(A._profile, ()) for A in algebras] ) proflen = max( [ 0, ] + [len(p[0]) for p in profiles] + [len(p[1]) for p in profiles] ) nprof0 = [ min(A.profile(i, component=0) for A in algebras) for i in range(1, proflen + 1) ] nprof1 = [ min(A.profile(i, component=1) for A in algebras) for i in range(0, proflen) ] prof = (nprof0, nprof1) if isgen else nprof0 # return prof res = SteenrodAlgebra( A0.prime(), generic=isgen, profile=prof, truncation_type=rtrunc ) return res raise ValueError("algebras not compatible")
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() SymmetricKeyCryptosystem.__init__(self, S, S, None) self._field = field
def BIBD_5q_5_for_q_prime_power(q): r""" Return 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.finite_rings.finite_field_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 category_meet(self, other): """ TESTS:: sage: from yacop.categories.utils import category_meet sage: from yacop.categories.left_modules import YacopLeftModules sage: from yacop.categories.right_modules import YacopRightModules sage: A3=YacopLeftModules(SteenrodAlgebra(3)) sage: category_meet(A3,A3) Category of yacop left modules over mod 3 Steenrod algebra, milnor basis sage: category_meet(A3,Modules(GF(3))) Category of vector spaces over Finite Field of size 3 sage: A2=YacopLeftModules(SteenrodAlgebra(2)) sage: B2=YacopLeftModules(SteenrodAlgebra(2,profile=(2,1,1))) sage: category_meet(A2,B2) Category of yacop left modules over sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [2, 1, 1] sage: A2=YacopRightModules(SteenrodAlgebra(2)) sage: B2=YacopRightModules(SteenrodAlgebra(2,profile=(2,1,1))) sage: category_meet(A2,B2) Category of yacop right modules over sub-Hopf algebra of mod 2 Steenrod algebra, milnor basis, profile function [2, 1, 1] """ import yacop.categories oR = other.base_ring() B = steenrod_algebra_intersect((self.base_ring(), oR)) if not hasattr(B, "is_generic"): return Modules(FiniteField(B.characteristic())) is_algebra = self._yacop_has_multiplication() and other._yacop_has_multiplication() is_right = self._yacop_has_right_action() and other._yacop_has_right_action() is_left = self._yacop_has_left_action() and other._yacop_has_left_action() is_bimod = is_left and is_right if is_algebra: if is_bimod: return yacop.categories.bimodules.YacopBimoduleAlgebras(B) elif is_right: return yacop.categories.right_modules.YacopRightModuleAlgebras(B) else: return yacop.categories.left_modules.YacopLeftModuleAlgebras(B) else: if is_bimod: return yacop.categories.bimodules.YacopBimodules(B) elif is_right: return yacop.categories.right_modules.YacopRightModules(B) else: return yacop.categories.left_modules.YacopLeftModules(B)
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 _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.finite_field_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 generate_wp_candidates(self, p, ideal_p, **kwargs): eps = self.eps q = self.q for a, b in product(range(q), repeat=2): if (p**2 * a**2 - b**2 * eps - p) % (q) == 0: verbose('Found a=%s, b=%s' % (a, b)) break c = (self.GFq(p)**-1 * b * eps).lift() d = a a, b, c, d = lift(matrix(ZZ, 2, 2, [p * a, b, c, d]), q).list() Fp = FiniteField(p) if c % p == 0: c += a * q d += b * q assert c % p != 0 r = (Fp(-a) * Fp(c * q)**-1).lift() a += q * c * r b += q * d * r ans = matrix(ZZ, 2, 2, [a, b, p * c, p * d]) ans.set_immutable() yield ans
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.finite_field_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 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 n = Integer(n) 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 six.iteritems(xi): 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 BCHCode(n, delta, F, b=0): r""" A 'Bose-Chaudhuri-Hockenghem code' (or BCH code for short) is the largest possible cyclic code of length n over field F=GF(q), whose generator polynomial has zeros (which contain the set) `Z = \{a^{b},a^{b+1}, ..., a^{b+delta-2}\}`, where a is a primitive `n^{th}` root of unity in the splitting field `GF(q^m)`, b is an integer `0\leq b\leq n-delta+1` and m is the multiplicative order of q modulo n. (The integers `b,...,b+delta-2` typically lie in the range `1,...,n-1`.) The integer `delta \geq 1` is called the "designed distance". The length n of the code and the size q of the base field must be relatively prime. The generator polynomial is equal to the least common multiple of the minimal polynomials of the elements of the set `Z` above. Special cases are b=1 (resulting codes are called 'narrow-sense' BCH codes), and `n=q^m-1` (known as 'primitive' BCH codes). It may happen that several values of delta give rise to the same BCH code. The largest one is called the Bose distance of the code. The true minimum distance, d, of the code is greater than or equal to the Bose distance, so `d\geq delta`. EXAMPLES:: sage: FF.<a> = GF(3^2,"a") sage: x = PolynomialRing(FF,"x").gen() sage: L = [b.minpoly() for b in [a,a^2,a^3]]; g = LCM(L) sage: f = x^(8)-1 sage: g.divides(f) True sage: C = codes.CyclicCode(8,g); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3),1); C Linear code of length 8, dimension 4 over Finite Field of size 3 sage: C.minimum_distance() 4 sage: C = codes.BCHCode(8,3,GF(3)); C Linear code of length 8, dimension 5 over Finite Field of size 3 sage: C.minimum_distance() 3 sage: C = codes.BCHCode(26, 5, GF(5), b=1); C Linear code of length 26, dimension 10 over Finite Field of size 5 """ q = F.order() R = IntegerModRing(n) m = R(q).multiplicative_order() FF = GF(q ** m, "z") z = FF.gen() e = z.multiplicative_order() / n a = z ** e # order n P = PolynomialRing(F, "x") x = P.gen() L1 = [] for coset in R.cyclotomic_cosets(q, range(b, b + delta - 1)): L1.extend(P((a ** j).minpoly()) for j in coset) g = P(LCM(L1)) if not (g.divides(x ** n - 1)): raise ValueError("BCH codes does not exist with the given input.") return CyclicCodeFromGeneratingPolynomial(n, g)
def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, 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 - ``point_coordinates`` (boolean) -- whether to label the points with their homogeneous coordinates (default) or with integers. - ``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, 'a') 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 B = BalancedIncompleteBlockDesign(n2+n+1, blcks, check=check) if point_coordinates: zero = K.zero() one = K.one() d = {affine_plane(x,y): (x,y,one) for x in Kiter for y in Kiter} d.update({line_infinity(x): (x,one,zero) for x in Kiter}) d[n2+n]=(one,zero,zero) B.relabel(d) return B
def normalize_args_vectorspace(*args, **kwds): """ Normalize the arguments that relate to a vector space. INPUT: Something that defines an affine space. For example * An affine space itself: - ``A`` -- affine space * A vector space: - ``V`` -- a vector space * Degree and base ring: - ``degree`` -- integer. The degree of the affine group, that is, the dimension of the affine space the group is acting on. - ``ring`` -- a ring or an integer. The base ring of the affine space. If an integer is given, it must be a prime power and the corresponding finite field is constructed. - ``var='a'`` -- optional keyword argument to specify the finite field generator name in the case where ``ring`` is a prime power. OUTPUT: A pair ``(degree, ring)``. TESTS:: sage: from sage.groups.matrix_gps.named_group import normalize_args_vectorspace sage: A = AffineSpace(2, GF(4,'a')); A Affine Space of dimension 2 over Finite Field in a of size 2^2 sage: normalize_args_vectorspace(A) (2, Finite Field in a of size 2^2) sage: normalize_args_vectorspace(2,4) # shorthand (2, Finite Field in a of size 2^2) sage: V = ZZ^3; V Ambient free module of rank 3 over the principal ideal domain Integer Ring sage: normalize_args_vectorspace(V) (3, Integer Ring) sage: normalize_args_vectorspace(2, QQ) (2, Rational Field) """ from sage.rings.all import ZZ if len(args) == 1: V = args[0] try: degree = V.dimension_relative() except AttributeError: degree = V.dimension() ring = V.base_ring() if len(args) == 2: degree, ring = args try: ring = ZZ(ring) from sage.rings.finite_rings.finite_field_constructor import FiniteField var = kwds.get('var', 'a') ring = FiniteField(ring, var) except (ValueError, TypeError): pass return (ZZ(degree), ring)
def GDD_4_2(q, existence=False, check=True): r""" Return a `(2q,\{4\},\{2\})`-GDD for `q` a prime power with `q\equiv 1\pmod{6}`. This method implements Lemma VII.5.17 from [BJL99] (p.495). INPUT: - ``q`` (integer) - ``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. - ``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. EXAMPLE:: sage: from sage.combinat.designs.group_divisible_designs import GDD_4_2 sage: GDD_4_2(7,existence=True) True sage: GDD_4_2(7) Group Divisible Design on 14 points of type 2^7 sage: GDD_4_2(8,existence=True) Unknown sage: GDD_4_2(8) Traceback (most recent call last): ... NotImplementedError """ if q <= 1 or q % 6 != 1 or not is_prime_power(q): if existence: return Unknown raise NotImplementedError if existence: return True from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF G = GF(q, "x") w = G.primitive_element() e = w ** ((q - 1) // 3) # A first parallel class is defined. G acts on it, which yields all others. first_class = [[(0, 0), (1, w ** i), (1, e * w ** i), (1, e * e * w ** i)] for i in range((q - 1) // 6)] label = {p: i for i, p in enumerate(G)} classes = [[[2 * label[x[1] + g] + (x[0] + j) % 2 for x in S] for S in first_class] for g in G for j in range(2)] return GroupDivisibleDesign( 2 * q, groups=[[i, i + 1] for i in range(0, 2 * q, 2)], blocks=sum(classes, []), K=[4], G=[2], check=check, copy=False, )
def __init__(self,q,level,info_magma = None,grouptype = None,magma = None, compute_presentation = True): from sage.modular.arithgroup.congroup_gamma import Gamma_constructor assert grouptype in ['SL2','PSL2'] self._grouptype = grouptype self._compute_presentation = compute_presentation self.magma = magma self.F = QQ self.q = ZZ(q) self.discriminant = ZZ(1) self.level = ZZ(level/self.q) if self.level != 1 and compute_presentation: raise NotImplementedError self._Gamma = Gamma_constructor(self.q) self._Gamma_farey = self._Gamma.farey_symbol() self.F_units = [] self._prec_inf = -1 self.B = MatrixSpace(QQ,2,2) self._O_discriminant = ZZ.ideal(self.level * self.q) # Here we initialize the non-split Cartan, properly self.GFq = FiniteField(self.q) if not self.GFq(-1).is_square(): self.eps = ZZ(-1) else: self.eps = ZZ(2) while self.GFq(self.eps).is_square(): self.eps += 1 epsinv = (self.GFq(self.eps)**-1).lift() N = self.level q = self.q self.Obasis = [matrix(ZZ,2,2,v) for v in [[1,0,0,1], [0,q,0,0], [0,N*epsinv,N,0], [0,0,0,q]]] x = QQ['x'].gen() K = FiniteField(self.q**2,'z',modulus = x*x - self.eps) x = K.primitive_element() x1 = x while x1.multiplicative_order() != self.q+1 or x1.norm() != 1: x1 *= x a, b = x1.polynomial().list() # represents a+b*sqrt(eps) a = a.lift() b = b.lift() self.extra_matrix = self.B(lift(matrix(ZZ,2,2,[a,b,b*self.eps,a]),self.q)) self.extra_matrix_inverse = ~self.extra_matrix if compute_presentation: self.Ugens = [] self._gens = [] temp_relation_words = [] I = SL2Z([1,0,0,1]) E = SL2Z([-1,0,0,-1]) minus_one = [] for i,g in enumerate(self._Gamma_farey.generators()): newg = self.B([g.a(),g.b(),g.c(),g.d()]) if newg == I: continue self.Ugens.append(newg) self._gens.append(self.element_class(self,quaternion_rep = newg, word_rep = [i+1],check = False)) if g.matrix()**2 == I.matrix(): temp_relation_words.append([i+1, i+1]) if minus_one is not None: temp_relation_words.append([-i-1]+minus_one) else: minus_one = [i+1] elif g.matrix()**2 == E.matrix(): temp_relation_words.append([i+1,i+1,i+1,i+1]) if minus_one is not None: temp_relation_words.append([-i-1,-i-1]+minus_one) else: minus_one = [i+1, i+1] elif g.matrix()**3 == I.matrix(): temp_relation_words.append([i+1, i+1, i+1]) elif g.matrix()**3 == E.matrix(): temp_relation_words.append([i+1, i+1, i+1, i+1, i+1, i+1]) if minus_one is not None: temp_relation_words.append([-i-1, -i-1, -i-1]+minus_one) else: minus_one = [i+1, i+1, i+1] else: assert g.matrix()**24 != I.matrix() # The extra matrix is added i = len(self.Ugens) self.extra_matrix_index = i self.Ugens.append(self.extra_matrix) self._gens.append(self.element_class(self,quaternion_rep = self.Ugens[i], word_rep = [i+1],check = False)) # The new relations are also added w = self._get_word_rep_initial(self.extra_matrix**(self.q+1)) temp_relation_words.append( w + ([-i-1] * (self.q+1))) for j,g in enumerate(self.Ugens[:-1]): g1 = self.extra_matrix_inverse * g * self.extra_matrix w = self._get_word_rep_initial(g1) new_rel = w + [-i-1, -j-1, i+1] temp_relation_words.append(new_rel) self.F_unit_offset = len(self.Ugens) if minus_one is not None: self.minus_one_long = syllables_to_tietze(minus_one) self._relation_words = [] for rel in temp_relation_words: sign = multiply_out(rel, self.Ugens, self.B(1)) if sign == self.B(1) or 'P' in grouptype: self._relation_words.append(rel) else: assert sign == self.B(-1) newrel = rel + self.minus_one sign = multiply_out(newrel, self.Ugens, self.B(1)) assert sign == self.B(1) self._relation_words.append(newrel) ArithGroup_generic.__init__(self) Parent.__init__(self)
def v_4_1_rbibd(v, existence=False): r""" Return a `(v,4,1)`-RBIBD. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the design or only answer whether it exists. .. SEEALSO:: - :meth:`IncidenceStructure.is_resolvable` - :func:`resolvable_balanced_incomplete_block_design` .. NOTE:: A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This function, however, only implements a construction of `(v,4,1)`-BIBD such that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a from [BJL99]_). EXAMPLE:: sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4) sage: rBIBD.is_resolvable() True sage: rBIBD.is_t_design(return_parameters=True) (True, (2, 28, 4, 1)) TESTS:: sage: for q in prime_powers(2,30): ....: if (3*q+1)%12 == 4: ....: _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4) # indirect doctest """ # Volume 1, VII.7.5.a from [BJL99]_ if v % 3 != 1 or not is_prime_power((v - 1) // 3): if existence: return Unknown raise NotImplementedError( "I don't know how to build a ({},{},1)-RBIBD!".format(v, 4)) from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v - 1) // 3 nn = (q - 1) // 4 G = GF(q, 'x') w = G.primitive_element() e = w**(nn) assert e**2 == -1 first_class = [[(w**i, j), (-w**i, j), (e * w**i, j + 1), (-e * w**i, j + 1)] for i in range(nn) for j in range(3)] first_class.append([(0, 0), (0, 1), (0, 2), 'inf']) label = {p: i for i, p in enumerate(G)} classes = [[[ v - 1 if x == 'inf' else (x[1] % 3) * q + label[x[0] + g] for x in S ] for S in first_class] for g in G] BIBD = BalancedIncompleteBlockDesign(v, blocks=sum(classes, []), k=4, check=True, copy=False) BIBD._classes = classes assert BIBD.is_resolvable() return BIBD
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 [HL1999]_, 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. 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 n = Integer(n) 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.items(): 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 hadamard_matrix_paleyI(n, normalize=True): """ Implements the Paley type I construction. The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a prime `p` (see [Hora]_). INPUT: - ``n`` -- the matrix size - ``normalize`` (boolean) -- whether to normalize the result. EXAMPLES: We note that this method by default returns a normalised Hadamard matrix :: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI sage: hadamard_matrix_paleyI(4) [ 1 1 1 1] [ 1 -1 1 -1] [ 1 -1 -1 1] [ 1 1 -1 -1] Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with `S=-S^\top` :: sage: M=hadamard_matrix_paleyI(4, normalize=False); M [ 1 1 1 1] [-1 1 1 -1] [-1 -1 1 1] [-1 1 -1 1] sage: S=M-identity_matrix(4); -S==S.T True TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3] sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True) ....: for n in test_cases) True sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True) ....: for n in test_cases) True """ p = n - 1 if not(is_prime_power(p) and (p % 4 == 3)): raise ValueError("The order %s is not covered by the Paley type I construction." % n) from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(p,'x') K_list = list(K) K_list.insert(0,K.zero()) H = matrix(ZZ, [[(1 if (x-y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(n): H[i,0] = -1 H[0,i] = 1 if normalize: for i in range(n): H[i,i] = -1 H = normalise_hadamard(H) return H
def hadamard_matrix_paleyII(n): """ Implements the Paley type II construction. The Paley type II case corresponds to the case `p \cong 1 \mod{4}` for a prime `p` (see [Hora]_). EXAMPLES:: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12).det() 2985984 sage: 12^6 2985984 We note that the method returns a normalised Hadamard matrix :: sage: sage.combinat.matrices.hadamard_matrix.hadamard_matrix_paleyII(12) [ 1 1| 1 1| 1 1| 1 1| 1 1| 1 1] [ 1 -1|-1 1|-1 1|-1 1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 -1| 1 1|-1 -1|-1 -1| 1 1] [ 1 1|-1 -1| 1 -1|-1 1|-1 1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1| 1 -1| 1 1|-1 -1|-1 -1] [ 1 1| 1 -1|-1 -1| 1 -1|-1 1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1| 1 1| 1 -1| 1 1|-1 -1] [ 1 1|-1 1| 1 -1|-1 -1| 1 -1|-1 1] [-----+-----+-----+-----+-----+-----] [ 1 -1|-1 -1|-1 -1| 1 1| 1 -1| 1 1] [ 1 1|-1 1|-1 1| 1 -1|-1 -1| 1 -1] [-----+-----+-----+-----+-----+-----] [ 1 -1| 1 1|-1 -1|-1 -1| 1 1| 1 -1] [ 1 1| 1 -1|-1 1|-1 1| 1 -1|-1 -1] TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import (hadamard_matrix_paleyII, is_hadamard_matrix) sage: test_cases = [2*(x+1) for x in range(50) if is_prime_power(x) and x%4==1] sage: all(is_hadamard_matrix(hadamard_matrix_paleyII(n),normalized=True,verbose=True) ....: for n in test_cases) True """ q = n//2 - 1 if not(n%2==0 and is_prime_power(q) and (q % 4 == 1)): raise ValueError("The order %s is not covered by the Paley type II construction." % n) from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(q,'x') K_list = list(K) K_list.insert(0,K.zero()) H = matrix(ZZ, [[(1 if (x-y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(q+1): H[0,i] = 1 H[i,0] = 1 H[i,i] = 0 tr = { 0: matrix(2,2,[ 1,-1,-1,-1]), 1: matrix(2,2,[ 1, 1, 1,-1]), -1: matrix(2,2,[-1,-1,-1, 1])} H = block_matrix(q+1,q+1,[tr[v] for r in H for v in r]) return normalise_hadamard(H)
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: next(b for b in blocks if p in b and q in b) 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') F = FiniteField(q, prefix='y') 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 v_4_1_rbibd(v,existence=False): r""" Return a `(v,4,1)`-RBIBD. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the design or only answer whether it exists. .. SEEALSO:: - :meth:`IncidenceStructure.is_resolvable` - :func:`resolvable_balanced_incomplete_block_design` .. NOTE:: A resolvable `(v,4,1)`-BIBD exists whenever `1\equiv 4\pmod(12)`. This function, however, only implements a construction of `(v,4,1)`-BIBD such that `v=3q+1\equiv 1\pmod{3}` where `q` is a prime power (see VII.7.5.a from [BJL99]_). EXAMPLE:: sage: rBIBD = designs.resolvable_balanced_incomplete_block_design(28,4) sage: rBIBD.is_resolvable() True sage: rBIBD.is_t_design(return_parameters=True) (True, (2, 28, 4, 1)) TESTS:: sage: for q in prime_powers(2,30): ....: if (3*q+1)%12 == 4: ....: _ = designs.resolvable_balanced_incomplete_block_design(3*q+1,4) # indirect doctest """ # Volume 1, VII.7.5.a from [BJL99]_ if v%3 != 1 or not is_prime_power((v-1)//3): if existence: return Unknown raise NotImplementedError("I don't know how to build a ({},{},1)-RBIBD!".format(v,4)) from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//3 nn = (q-1)//4 G = GF(q,'x') w = G.primitive_element() e = w**(nn) assert e**2 == -1 first_class = [[(w**i,j),(-w**i,j),(e*w**i,j+1),(-e*w**i,j+1)] for i in range(nn) for j in range(3)] first_class.append([(0,0),(0,1),(0,2),'inf']) label = {p:i for i,p in enumerate(G)} classes = [[[v-1 if x=='inf' else (x[1]%3)*q+label[x[0]+g] for x in S] for S in first_class] for g in G] BIBD = BalancedIncompleteBlockDesign(v, blocks = sum(classes,[]), k=4, check=True, copy=False) BIBD._classes = classes assert BIBD.is_resolvable() return BIBD
def hadamard_matrix_paleyI(n, normalize=True): """ Implements the Paley type I construction. The Paley type I case corresponds to the case `p \cong 3 \mod{4}` for a prime `p` (see [Hora]_). INPUT: - ``n`` -- the matrix size - ``normalize`` (boolean) -- whether to normalize the result. EXAMPLES: We note that this method by default returns a normalised Hadamard matrix :: sage: from sage.combinat.matrices.hadamard_matrix import hadamard_matrix_paleyI sage: hadamard_matrix_paleyI(4) [ 1 1 1 1] [ 1 -1 1 -1] [ 1 -1 -1 1] [ 1 1 -1 -1] Otherwise, it returns a skew Hadamard matrix `H`, i.e. `H=S+I`, with `S=-S^\top` :: sage: M=hadamard_matrix_paleyI(4, normalize=False); M [ 1 1 1 1] [-1 1 1 -1] [-1 -1 1 1] [-1 1 -1 1] sage: S=M-identity_matrix(4); -S==S.T True TESTS:: sage: from sage.combinat.matrices.hadamard_matrix import is_hadamard_matrix sage: test_cases = [x+1 for x in range(100) if is_prime_power(x) and x%4==3] sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n),normalized=True,verbose=True) ....: for n in test_cases) True sage: all(is_hadamard_matrix(hadamard_matrix_paleyI(n,normalize=False),verbose=True) ....: for n in test_cases) True """ p = n - 1 if not (is_prime_power(p) and (p % 4 == 3)): raise ValueError( "The order %s is not covered by the Paley type I construction." % n) from sage.rings.finite_rings.finite_field_constructor import FiniteField K = FiniteField(p, 'x') K_list = list(K) K_list.insert(0, K.zero()) H = matrix(ZZ, [[(1 if (x - y).is_square() else -1) for x in K_list] for y in K_list]) for i in range(n): H[i, 0] = -1 H[0, i] = 1 if normalize: for i in range(n): H[i, i] = -1 H = normalise_hadamard(H) return H
def kirkman_triple_system(v, existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: def to_name(r_s_t): ....: r, s, t = r_s_t ....: return ' ' + names[r] + names[s] + names[t] + ' ' sage: rows = [' '.join(('Day {}'.format(i) for i in range(1,8)))] sage: rows.extend(' '.join(map(to_name,row)) for row in zip(*classes)) sage: print('\n'.join(rows)) Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 07e 18e 29e 3ae 4be 5ce 6de 139 24a 35b 46c 05d 167 028 26b 03c 14d 257 368 049 15a 458 569 06a 01b 12c 23d 347 acd 7bd 78c 89d 79a 8ab 9bc TESTS:: sage: for i in range(3,300,6): ....: _ = designs.kirkman_triple_system(i) """ if v % 6 != 3: if existence: return False raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) if existence: return False elif v == 3: return BalancedIncompleteBlockDesign(3, [[0, 1, 2]], k=3, lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v - 1) // 2) % 6 == 1 and is_prime_power((v - 1) // 2): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v - 1) // 2 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t + 1) / 2, a) assert 2 * a**m == a**t + 1 # First parallel class first_class = [[(0, 1), (0, 2), 'inf']] b0 = K.one() b1 = a**t b2 = a**m first_class.extend([(b0 * a**i, 1), (b1 * a**i, 1), (b2 * a**i, 2)] for i in list(range(t)) + list(range(2 * t, 3 * t)) + list(range(4 * t, 5 * t))) b0 = a**(m + t) b1 = a**(m + 3 * t) b2 = a**(m + 5 * t) first_class.extend([[(b0 * a**i, 2), (b1 * a**i, 2), (b2 * a**i, 2)] for i in range(t)]) # Action of K on the points action = lambda v, x: (v + x[0], x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p, x): i + (x - 1) * q for i, p in enumerate(K) for x in [1, 2]} relabel['inf'] = 2 * q classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v // 3) % 6 == 1 and is_prime_power(v // 3): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = v // 3 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 A0 = [(0, 0), (0, 1), (0, 2)] B = [[(a**i, j), (a**(i + 2 * t), j), (a**(i + 4 * t), j)] for j in range(3) for i in range(t)] A = [[(a**i, 0), (a**(i + 2 * t), 1), (a**(i + 4 * t), 2)] for i in range(6 * t)] # Action of K on the points action = lambda v, x: (v + x[0], x[1]) # relabel to integer relabel = {(p, j): i + j * q for i, p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2 * t] + A[3 * t:4 * t] + A[5 * t:6 * t] # Classes classes = [[[relabel[action(p, x)] for x in tr] for tr in B0] for p in K] for i in list(range(t)) + list(range(2 * t, 3 * t)) + list( range(4 * t, 5 * t)): classes.append([[relabel[action(p, x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v: i for i, v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v: i for i, v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i, b in enumerate(B): if 8 in b: j = min(b) del B[i] B.insert(0, j) break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i, b in enumerate(B): if 14 in b: j = min(b) del B[i] B.insert(0, j) break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v - 1) // 2)] gdd = {4: gdd4, 7: gdd7} for B in PBD_4_7((v - 1) // 2, check=False): for i, classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2 * B[x // 2] + x % 2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i, classs in enumerate(classes): classs.append([2 * i, 2 * i + 1, v - 1]) KTS = BalancedIncompleteBlockDesign( v, blocks=[tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy=False) KTS._classes = classes assert KTS.is_resolvable() return KTS
def kirkman_triple_system(v,existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: to_name = lambda (r,s,t): ' '+names[r]+names[s]+names[t]+' ' sage: rows = [' '.join(('Day {}'.format(i) for i in range(1,8)))] sage: rows.extend(' '.join(map(to_name,row)) for row in zip(*classes)) sage: print '\n'.join(rows) Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 07e 18e 29e 3ae 4be 5ce 6de 139 24a 35b 46c 05d 167 028 26b 03c 14d 257 368 049 15a 458 569 06a 01b 12c 23d 347 acd 7bd 78c 89d 79a 8ab 9bc TESTS:: sage: for i in range(3,300,6): ....: _ = designs.kirkman_triple_system(i) """ if v%6 != 3: if existence: return False raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) if existence: return False elif v == 3: return BalancedIncompleteBlockDesign(3,[[0,1,2]],k=3,lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v-1)//2)%6 == 1 and is_prime_power((v-1)//2): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v-1)//2 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t+1)/2, a) assert 2*a**m == a**t+1 # First parallel class first_class = [[(0,1),(0,2),'inf']] b0 = K.one(); b1 = a**t; b2 = a**m first_class.extend([(b0*a**i,1),(b1*a**i,1),(b2*a**i,2)] for i in range(t)+range(2*t,3*t)+range(4*t,5*t)) b0 = a**(m+t); b1=a**(m+3*t); b2=a**(m+5*t) first_class.extend([[(b0*a**i,2),(b1*a**i,2),(b2*a**i,2)] for i in range(t)]) # Action of K on the points action = lambda v,x : (v+x[0],x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p,x): i+(x-1)*q for i,p in enumerate(K) for x in [1,2]} relabel['inf'] = 2*q classes = [[[relabel[action(p,x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v//3)%6 == 1 and is_prime_power(v//3): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = v//3 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 A0 = [(0,0),(0,1),(0,2)] B = [[(a**i,j),(a**(i+2*t),j),(a**(i+4*t),j)] for j in range(3) for i in range(t)] A = [[(a**i,0),(a**(i+2*t),1),(a**(i+4*t),2)] for i in range(6*t)] # Action of K on the points action = lambda v,x: (v+x[0],x[1]) # relabel to integer relabel = {(p,j): i+j*q for i,p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t] # Classes classes = [[[relabel[action(p,x)] for x in tr] for tr in B0] for p in K] for i in range(t)+range(2*t,3*t)+range(4*t,5*t): classes.append([[relabel[action(p,x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v:i for i,v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v:i for i,v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i,b in enumerate(B): if 8 in b: j = min(b); del B[i]; B.insert(0,j); break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i,b in enumerate(B): if 14 in b: j = min(b); del B[i]; B.insert(0,j); break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v-1)/2)] gdd = {4:gdd4, 7: gdd7} for B in PBD_4_7((v-1)//2,check=False): for i,classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2*B[x//2]+x%2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i,classs in enumerate(classes): classs.append([2*i,2*i+1,v-1]) KTS = BalancedIncompleteBlockDesign(v, blocks = [tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy =False) KTS._classes = classes assert KTS.is_resolvable() return KTS
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: next(b for b in blocks if p in b and q in b) 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') F = FiniteField(q, prefix='y') 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 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
def self_orthogonal_binary_codes(n, k, b=2, parent=None, BC=None, equal=False, in_test=None): """ Returns a Python iterator which generates a complete set of representatives of all permutation equivalence classes of self-orthogonal binary linear codes of length in ``[1..n]`` and dimension in ``[1..k]``. INPUT: - ``n`` - Integer, maximal length - ``k`` - Integer, maximal dimension - ``b`` - Integer, requires that the generators all have weight divisible by ``b`` (if ``b=2``, all self-orthogonal codes are generated, and if ``b=4``, all doubly even codes are generated). Must be an even positive integer. - ``parent`` - Used in recursion (default: ``None``) - ``BC`` - Used in recursion (default: ``None``) - ``equal`` - If ``True`` generates only [n, k] codes (default: ``False``) - ``in_test`` - Used in recursion (default: ``None``) EXAMPLES: Generate all self-orthogonal codes of length up to 7 and dimension up to 3:: sage: for B in codes.databases.self_orthogonal_binary_codes(7,3): ....: print(B) [2, 1] linear code over GF(2) [4, 2] linear code over GF(2) [6, 3] linear code over GF(2) [4, 1] linear code over GF(2) [6, 2] linear code over GF(2) [6, 2] linear code over GF(2) [7, 3] linear code over GF(2) [6, 1] linear code over GF(2) Generate all doubly-even codes of length up to 7 and dimension up to 3:: sage: for B in codes.databases.self_orthogonal_binary_codes(7,3,4): ....: print(B); print(B.generator_matrix()) [4, 1] linear code over GF(2) [1 1 1 1] [6, 2] linear code over GF(2) [1 1 1 1 0 0] [0 1 0 1 1 1] [7, 3] linear code over GF(2) [1 0 1 1 0 1 0] [0 1 0 1 1 1 0] [0 0 1 0 1 1 1] Generate all doubly-even codes of length up to 7 and dimension up to 2:: sage: for B in codes.databases.self_orthogonal_binary_codes(7,2,4): ....: print(B); print(B.generator_matrix()) [4, 1] linear code over GF(2) [1 1 1 1] [6, 2] linear code over GF(2) [1 1 1 1 0 0] [0 1 0 1 1 1] Generate all self-orthogonal codes of length equal to 8 and dimension equal to 4:: sage: for B in codes.databases.self_orthogonal_binary_codes(8, 4, equal=True): ....: print(B); print(B.generator_matrix()) [8, 4] linear code over GF(2) [1 0 0 1 0 0 0 0] [0 1 0 0 1 0 0 0] [0 0 1 0 0 1 0 0] [0 0 0 0 0 0 1 1] [8, 4] linear code over GF(2) [1 0 0 1 1 0 1 0] [0 1 0 1 1 1 0 0] [0 0 1 0 1 1 1 0] [0 0 0 1 0 1 1 1] Since all the codes will be self-orthogonal, b must be divisible by 2:: sage: list(codes.databases.self_orthogonal_binary_codes(8, 4, 1, equal=True)) Traceback (most recent call last): ... ValueError: b (1) must be a positive even integer. """ from sage.rings.finite_rings.finite_field_constructor import FiniteField from sage.matrix.constructor import Matrix d = int(b) if d != b or d % 2 == 1 or d <= 0: raise ValueError("b (%s) must be a positive even integer." % b) from .linear_code import LinearCode from .binary_code import BinaryCode, BinaryCodeClassifier if k < 1 or n < 2: return if equal: in_test = lambda M: (M.ncols() - M.nrows()) <= (n - k) out_test = lambda C: (C.dimension() == k) and (C.length() == n) else: in_test = lambda M: True out_test = lambda C: True if BC is None: BC = BinaryCodeClassifier() if parent is None: for j in range(d, n + 1, d): M = Matrix(FiniteField(2), [[1] * j]) if in_test(M): for N in self_orthogonal_binary_codes(n, k, d, M, BC, in_test=in_test): if out_test(N): yield N else: C = LinearCode(parent) if out_test(C): yield C if k == parent.nrows(): return for nn in range(parent.ncols() + 1, n + 1): if in_test(parent): for child in BC.generate_children(BinaryCode(parent), nn, d): for N in self_orthogonal_binary_codes(n, k, d, child, BC, in_test=in_test): if out_test(N): yield N
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.matrix_gps.finitely_generated 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] [2 1] [1 2], [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.finite_field_constructor import FiniteField from sage.matrix.matrix_space import MatrixSpace MS = MatrixSpace(FiniteField(3), 2) aye = MS([1, 1, 1, 2]) jay = MS([2, 1, 1, 1]) return MatrixGroup([aye, jay])
def DesarguesianProjectivePlaneDesign(n, point_coordinates=True, 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 - ``point_coordinates`` (boolean) -- whether to label the points with their homogeneous coordinates (default) or with integers. - ``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, 'a') n2 = n**2 relabel = {x: i for i, x in enumerate(K)} Kiter = relabel # it is much faster to iterate through 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 relabeled from n^2 to n^2 + n - 1 line_infinity = lambda x: n2 + relabel[x] # - the point is [1:0:0] and gets relabeled 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 B = BalancedIncompleteBlockDesign(n2 + n + 1, blcks, check=check) if point_coordinates: zero = K.zero() one = K.one() d = {affine_plane(x, y): (x, y, one) for x in Kiter for y in Kiter} d.update({line_infinity(x): (x, one, zero) for x in Kiter}) d[n2 + n] = (one, zero, zero) B.relabel(d) return B
def BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=False): r""" Returns a `(n,k,1)`-BIBD from a maximal arc in a projective plane. This function implements a construction from Denniston [Denniston69]_, who describes a maximal :meth:`arc <sage.combinat.designs.bibd.BalancedIncompleteBlockDesign.arc>` in a :func:`Desarguesian Projective Plane <sage.combinat.designs.block_design.DesarguesianProjectivePlaneDesign>` of order `2^k`. From two powers of two `n,q` with `n<q`, it produces a `((n-1)(q+1)+1,n,1)`-BIBD. INPUT: - ``n,k`` (integers) -- must be powers of two (among other restrictions). - ``existence`` (boolean) -- whether to return the BIBD obtained through this construction (default), or to merely indicate with a boolean return value whether this method *can* build the requested BIBD. EXAMPLES: A `(232,8,1)`-BIBD:: sage: from sage.combinat.designs.bibd import BIBD_from_arc_in_desarguesian_projective_plane sage: from sage.combinat.designs.bibd import BalancedIncompleteBlockDesign sage: D = BIBD_from_arc_in_desarguesian_projective_plane(232,8) sage: BalancedIncompleteBlockDesign(232,D) (232,8,1)-Balanced Incomplete Block Design A `(120,8,1)`-BIBD:: sage: D = BIBD_from_arc_in_desarguesian_projective_plane(120,8) sage: BalancedIncompleteBlockDesign(120,D) (120,8,1)-Balanced Incomplete Block Design Other parameters:: sage: all(BIBD_from_arc_in_desarguesian_projective_plane(n,k,existence=True) ....: for n,k in ....: [(120, 8), (232, 8), (456, 8), (904, 8), (496, 16), ....: (976, 16), (1936, 16), (2016, 32), (4000, 32), (8128, 64)]) True Of course, not all can be built this way:: sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3,existence=True) False sage: BIBD_from_arc_in_desarguesian_projective_plane(7,3) Traceback (most recent call last): ... ValueError: This function cannot produce a (7,3,1)-BIBD REFERENCE: .. [Denniston69] R. H. F. Denniston, Some maximal arcs in finite projective planes. Journal of Combinatorial Theory 6, no. 3 (1969): 317-319. http://dx.doi.org/10.1016/S0021-9800(69)80095-5 """ q = (n-1)//(k-1)-1 if (k % 2 or q % 2 or q <= k or n != (k-1)*(q+1)+1 or not is_prime_power(k) or not is_prime_power(q)): if existence: return False raise ValueError("This function cannot produce a ({},{},1)-BIBD".format(n,k)) if existence: return True n = k # From now on, the code assumes the notations of [Denniston69] for n,q, so # that the BIBD returned by the method will have the requested parameters. from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF from sage.libs.gap.libgap import libgap from sage.matrix.constructor import Matrix K = GF(q,'a') one = K.one() # An irreducible quadratic form over K[X,Y] GO = libgap.GeneralOrthogonalGroup(-1,2,q) M = libgap.InvariantQuadraticForm(GO)['matrix'] M = Matrix(M) M = M.change_ring(K) Q = lambda xx,yy : M[0,0]*xx**2+(M[0,1]+M[1,0])*xx*yy+M[1,1]*yy**2 # Here, the additive subgroup H (of order n) of K mentioned in # [Denniston69] is the set of all elements of K of degree < log_n # (seeing elements of K as polynomials in 'a') K_iter = list(K) # faster iterations log_n = is_prime_power(n,get_data=True)[1] C = [(x,y,one) for x in K_iter for y in K_iter if Q(x,y).polynomial().degree() < log_n] from sage.combinat.designs.block_design import DesarguesianProjectivePlaneDesign return DesarguesianProjectivePlaneDesign(q).trace(C)._blocks