Exemplo n.º 1
0
 def get_s(self):
     """Returns a single value of s as required for the Elligator map while there may even be two of them, in which case the returned values is the smaller of the two
     """ 
     try:
         return self._s
     except AttributeError:
         pass
     s = []
     d = self.__E.get_d()
     if is_square(-d):
         c = ( 2*(d-1) + 4*sqrt(-d) ) / ( 2*(d+1) )
         s_2 = 2/c
         if is_square(s_2):
             s.append(sqrt(s_2))
         
         c = ( 2*(d-1) - 4*sqrt(-d) ) / ( 2*(d+1) )
         s_2 = 2/c
         if is_square(s_2):
             s.append(sqrt(s_2))
     if len(s) == 0:
         return None
     elif len(s) == 1:
         return s[0]
     else:
         if s[0] < s[1]:
             return s[0]
         else: 
             return s[1]    
Exemplo n.º 2
0
 def random_point(self):
     while(1):
         x = self.base_ring().random_element()
         test = (x**2 - 1)/(self._d*(x**2) - 1)
         if is_square(test):
             y = sqrt(test)
             return self([x,y,1])    
Exemplo n.º 3
0
    def __init__(self, parent, rdict):
        r"""
        Create an eta product object. Usually called implicitly via
        EtaGroup_class.__call__ or the EtaProduct factory function.

        EXAMPLE::

            sage: EtaGroupElement(EtaGroup(8), {1:24, 2:-24})
            Eta product of level 8 : (eta_1)^24 (eta_2)^-24
            sage: g = _; g == loads(dumps(g))
            True
        """
        MultiplicativeGroupElement.__init__(self, parent)

        self._N = self.parent().level()
        N = self._N

        if isinstance(rdict, EtaGroupElement):
            rdict = rdict._rdict
            # Note: This is needed because the "x in G" test tries to call G(x)
            # and see if it returns an error. So sometimes this will be getting
            # called with rdict being an eta product, not a dictionary.

        if rdict == 1:
            rdict = {}
        # Check Ligozat criteria
        sumR = sumDR = sumNoverDr = 0
        prod = 1

        for d in rdict.keys():
            if N % d:
                raise ValueError("%s does not divide %s" % (d, N))

        for d in rdict.keys():
            if rdict[d] == 0:
                rdict.pop(d)
                continue
            sumR += rdict[d]
            sumDR += rdict[d] * d
            sumNoverDr += rdict[d] * N / d
            prod *= (N / d)**rdict[d]

        if sumR != 0:
            raise ValueError("sum r_d (=%s) is not 0" % sumR)
        if (sumDR % 24) != 0:
            raise ValueError("sum d r_d (=%s) is not 0 mod 24" % sumDR)
        if (sumNoverDr % 24) != 0:
            raise ValueError("sum (N/d) r_d (=%s) is not 0 mod 24" %
                             sumNoverDr)
        if not is_square(prod):
            raise ValueError("product (N/d)^(r_d) (=%s) is not a square" %
                             prod)

        self._sumDR = sumDR  # this is useful to have around
        self._rdict = rdict
        self._keys = rdict.keys()  # avoid factoring N every time
Exemplo n.º 4
0
 def n_random_points(self,n):
     v = []
     while(len(v) < n):
         x = self.base_ring().random_element()
         test = (x**2 - 1)/(self._d*(x**2) - 1)
         if is_square(test):
             y = sqrt(test)
             v.append(self([x,y,1]))
     v.sort()
     return v        
Exemplo n.º 5
0
    def __init__(self, parent, rdict):
        r"""
        Create an eta product object. Usually called implicitly via
        EtaGroup_class.__call__ or the EtaProduct factory function.

        EXAMPLE::

            sage: EtaGroupElement(EtaGroup(8), {1:24, 2:-24})
            Eta product of level 8 : (eta_1)^24 (eta_2)^-24
            sage: g = _; g == loads(dumps(g))
            True
        """
        MultiplicativeGroupElement.__init__(self, parent)

        self._N = self.parent().level()
        N = self._N

        if isinstance(rdict, EtaGroupElement):
            rdict = rdict._rdict
            # Note: This is needed because the "x in G" test tries to call G(x)
            # and see if it returns an error. So sometimes this will be getting
            # called with rdict being an eta product, not a dictionary.

        if rdict == 1:
            rdict = {}
        # Check Ligozat criteria
        sumR = sumDR = sumNoverDr = 0
        prod = 1

        for d in rdict.keys():
            if N % d:
                raise ValueError("%s does not divide %s" % (d, N))

        for d in rdict.keys():
            if rdict[d] == 0:
                rdict.pop(d)
                continue
            sumR += rdict[d]
            sumDR += rdict[d]*d
            sumNoverDr += rdict[d]*N/d
            prod *= (N/d)**rdict[d]

        if sumR != 0:
            raise ValueError("sum r_d (=%s) is not 0" % sumR)
        if (sumDR % 24) != 0:
            raise ValueError("sum d r_d (=%s) is not 0 mod 24" % sumDR)
        if (sumNoverDr % 24) != 0:
            raise ValueError("sum (N/d) r_d (=%s) is not 0 mod 24" % sumNoverDr)
        if not is_square(prod):
            raise ValueError("product (N/d)^(r_d) (=%s) is not a square" % prod)

        self._sumDR = sumDR # this is useful to have around
        self._rdict = rdict
        self._keys = rdict.keys() # avoid factoring N every time
Exemplo n.º 6
0
 def is_image_point(self, point):
     s = self._s
     c = self._c
     r = self._r
     x = point[0]
     y = point[1]
     eta = (y-1) / (2 * (y+1))
     if y + 1 == 0:
         return False
     if not is_square( (1 + eta * r)**2 - 1):
         return False
     if eta*r == -2:
         temp = 2*s*(c - 1)*self.chi(c)/r
         if not x == temp:
             return False
     return True   
Exemplo n.º 7
0
 def points(self, limit = False):
     try:
         return self.__points
     except AttributeError: pass
     
     if not self.base_ring().is_finite():
         raise TypeError("points() is defined only for Edwards curve over finite fields")
     v = []
     for x in self.base_ring():
         test = (x**2 - 1)/(self._d*(x**2) - 1)
         if is_square(test):
             y = sqrt(test)
             v.append(self([x,y,1]))
             v.append(self([x,-y,1]))
     v.sort()
     self.__points = Sequence(v,immutable = True)
     return self.__points
Exemplo n.º 8
0
 def __init__( self, q):
     """
     We initialize by a list of integers $[a_1,...,a_N]$. The
     lattice self is then $L = (L,\beta) = (G^{-1}\ZZ^n/\ZZ^n, G[x])$,
     where $G$ is the symmetric matrix
     $G=[a_1,...,a_n;*,a_{n+1},....;..;* ... * a_N]$,
     i.e.~the $a_j$ denote the elements above the diagonal of $G$.
     """
     self.__form = q
     # We compute the rank
     N = len(q)
     assert is_square( 1+8*N)
     n = Integer( (-1+isqrt(1+8*N))/2)
     self.__rank = n
     # We set up the Gram matrix
     self.__G = matrix( IntegerRing(), n, n)
     i = j = 0
     for a in q:
         self.__G[i,j] = self.__G[j,i] = Integer(a)
         if j < n-1:
             j += 1
         else:
             i += 1
             j = i
     # We compute the level
     Gi = self.__G**-1
     self.__Gi = Gi
     a = lcm( map( lambda x: x.denominator(), Gi.list()))
     I = Gi.diagonal()
     b =  lcm( map( lambda x: (x/2).denominator(), I))
     self.__level = lcm( a, b)
     # We define the undelying module and the ambient space
     self.__module = FreeModule( IntegerRing(), n)
     self.__space = self.__module.ambient_vector_space()
     # We compute a shadow vector
     self.__shadow_vector = self.__space([(a%2)/2 for a in self.__G.diagonal()])*Gi
     # We define a basis
     M = Matrix( IntegerRing(), n, n, 1)
     self.__basis = self.__module.basis()
     # We prepare a cache
     self.__dual_vectors = None
     self.__values = None
     self.__chi = {}
Exemplo n.º 9
0
    def an(self, use_database=False, descent_second_limit=12):
        r"""
        Returns the Birch and Swinnerton-Dyer conjectural order of `Sha`
        as a provably correct integer, unless the analytic rank is > 1,
        in which case this function returns a numerical value.

        INPUT:

            - ``use_database`` -- bool (default: ``False``); if ``True``, try to use any
              databases installed to lookup the analytic order of `Sha`, if
              possible.  The order of `Sha` is computed if it cannot be looked up.

            - ``descent_second_limit`` -- int (default: 12); limit to use on
              point searching for the quartic twist in the hard case

        This result is proved correct if the order of vanishing is 0
        and the Manin constant is <= 2.

        If the optional parameter ``use_database`` is ``True`` (default:
        ``False``), this function returns the analytic order of `Sha` as
        listed in Cremona's tables, if this curve appears in Cremona's
        tables.

        NOTE:

        If you come across the following error::

            sage: E = EllipticCurve([0, 0, 1, -34874, -2506691])
            sage: E.sha().an()
            Traceback (most recent call last):
            ...
            RuntimeError: Unable to compute the rank, hence generators, with certainty (lower bound=0, generators found=[]).  This could be because Sha(E/Q)[2] is nontrivial.
            Try increasing descent_second_limit then trying this command again.

        You can increase the ``descent_second_limit`` (in the above example,
        set to the default, 12) option to try again::

            sage: E.sha().an(descent_second_limit=16)  # long time (2s on sage.math, 2011)
            1

        EXAMPLES::

            sage: E = EllipticCurve([0, -1, 1, -10, -20])   # 11A  = X_0(11)
            sage: E.sha().an()
            1
            sage: E = EllipticCurve([0, -1, 1, 0, 0])       # X_1(11)
            sage: E.sha().an()
            1

            sage: EllipticCurve('14a4').sha().an()
            1
            sage: EllipticCurve('14a4').sha().an(use_database=True)   # will be faster if you have large Cremona database installed
            1

        The smallest conductor curve with nontrivial `Sha`::

            sage: E = EllipticCurve([1,1,1,-352,-2689])     # 66b3
            sage: E.sha().an()
            4

        The four optimal quotients with nontrivial `Sha` and conductor <= 1000::

            sage: E = EllipticCurve([0, -1, 1, -929, -10595])       # 571A
            sage: E.sha().an()
            4
            sage: E = EllipticCurve([1, 1, 0, -1154, -15345])       # 681B
            sage: E.sha().an()
            9
            sage: E = EllipticCurve([0, -1, 0, -900, -10098])       # 960D
            sage: E.sha().an()
            4
            sage: E = EllipticCurve([0, 1, 0, -20, -42])            # 960N
            sage: E.sha().an()
            4

        The smallest conductor curve of rank > 1::

            sage: E = EllipticCurve([0, 1, 1, -2, 0])       # 389A (rank 2)
            sage: E.sha().an()
            1.00000000000000

        The following are examples that require computation of the Mordell-Weil
        group and regulator::

            sage: E = EllipticCurve([0, 0, 1, -1, 0])                     # 37A  (rank 1)
            sage: E.sha().an()
            1

            sage: E = EllipticCurve("1610f3")
            sage: E.sha().an()
            4

        In this case the input curve is not minimal, and if this function did not
        transform it to be minimal, it would give nonsense::

            sage: E = EllipticCurve([0,-432*6^2])
            sage: E.sha().an()
            1

        See :trac:`10096`: this used to give the wrong result 6.0000
        before since the minimal model was not used::

            sage: E = EllipticCurve([1215*1216,0]) # non-minimal model
            sage: E.sha().an()  # long time (2s on sage.math, 2011)
            1.00000000000000
            sage: E.minimal_model().sha().an()  # long time (1s on sage.math, 2011)
            1.00000000000000
        """
        if hasattr(self, '__an'):
            return self.__an
        if use_database:
            d = self.Emin.database_curve()
            if hasattr(d, 'db_extra'):
                self.__an = Integer(round(float(d.db_extra[4])))
                return self.__an

        # it's critical to switch to the minimal model.
        E = self.Emin
        eps = E.root_number()
        if eps == 1:
            L1_over_omega = E.lseries().L_ratio()
            if L1_over_omega == 0: # order of vanishing is at least 2
                return self.an_numerical(use_database=use_database)
            T = E.torsion_subgroup().order()
            Sha = (L1_over_omega * T * T) / Q(E.tamagawa_product())
            try:
                Sha = Integer(Sha)
            except ValueError:
                raise RuntimeError("There is a bug in an, since the computed conjectural order of Sha is %s, which is not an integer."%Sha)
            if not arith.is_square(Sha):
                raise RuntimeError("There is a bug in an, since the computed conjectural order of Sha is %s, which is not a square."%Sha)
            E.__an = Sha
            self.__an = Sha
            return Sha

        else:  # rank > 0  (Not provably correct)
            L1, error_bound = E.lseries().deriv_at1(10*sqrt(E.conductor()) + 10)
            if abs(L1) < error_bound:
                s = self.an_numerical()
                E.__an = s
                self.__an = s
                return s

            regulator = E.regulator(use_database=use_database, descent_second_limit=descent_second_limit)
            T = E.torsion_subgroup().order()
            omega = E.period_lattice().omega()
            Sha = Integer(round ( (L1 * T * T) / (E.tamagawa_product() * regulator * omega) ))
            try:
                Sha = Integer(Sha)
            except ValueError:
                raise RuntimeError("There is a bug in an, since the computed conjectural order of Sha is %s, which is not an integer."%Sha)
            if not arith.is_square(Sha):
                raise RuntimeError("There is a bug in an, since the computed conjectural order of Sha is %s, which is not a square."%Sha)
            E.__an = Sha
            self.__an = Sha
            return Sha
Exemplo n.º 10
0
def radical_difference_set(K, k, l=1, existence=False, check=True):
    r"""
    Return a difference set made of a cyclotomic coset in the finite field
    ``K`` and with paramters ``k`` and ``l``.

    Most of these difference sets appear in chapter VI.18.48 of the Handbook of
    combinatorial designs.

    EXAMPLES::

        sage: from sage.combinat.designs.difference_family import radical_difference_set

        sage: D = radical_difference_set(GF(7), 3, 1); D
        [[1, 2, 4]]
        sage: sorted(x-y for x in D[0] for y in D[0] if x != y)
        [1, 2, 3, 4, 5, 6]

        sage: D = radical_difference_set(GF(16,'a'), 6, 2)
        sage: sorted(x-y for x in D[0] for y in D[0] if x != y)
        [1,
         1,
         a,
         a,
         a + 1,
         a + 1,
         a^2,
         a^2,
         ...
         a^3 + a^2 + a + 1,
         a^3 + a^2 + a + 1]

        sage: for (v,k,l) in [(3,2,1), (7,3,1), (7,4,2), (11,5,2), (11,6,3),
        ....:                 (13,4,1), (16,6,2), (19,9,4), (19,10,5)]:
        ....:
        ....:     assert radical_difference_set(GF(v,'a'), k, l, existence=True), "pb with v={} k={} l={}".format(v,k,l)
    """
    v = K.cardinality()
    one = K.one()
    x = K.multiplicative_generator()

    if l*(v-1) != k*(k-1):
        if existence:
            return False
        raise EmptySetError("l*(v-1) is not equal to k*(k-1)")

    # trivial case
    if (v-1) == k:
        if existence:
            return True
        return K.cyclotomic_cosets(x, [one])

    # q = 3 mod 4
    elif v%4 == 3 and k == (v-1)//2:
        if existence:
            return True
        D = K.cyclotomic_cosets(x**2, [one])

    # q = 3 mod 4
    elif v%4 == 3 and k == (v+1)//2:
        if existence:
            return True
        D = K.cyclotomic_cosets(x**2, [one])
        D[0].insert(0, K.zero())

    # 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, [one])

    # 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, [one])
        D[0].insert(0,K.zero())

    # one case with k-1 = (v-1)/3
    elif (v,k,l) == (16,6,2):
        if existence:
            return True
        D = K.cyclotomic_cosets(x**3, [one])
        D[0].insert(0,K.zero())

    # one case with k = (v-1)/8
    elif (v,k,l) == (73,9,1):
        if existence:
            return True
        D = K.cyclotomic_cosets(x**8, [one])

    else:
        if existence:
            return Unknown
        raise NotImplementedError("no radical difference set is "
                "implemented for the parameters (v,k,l) = ({},{},{}".format(v,k,l))

    if check and not is_difference_family(K, D, v, k, l):
        raise RuntimeError("Sage tried to build a cyclotomic coset with "
                "parameters ({},{},{}) but it seems that it failed! Please "
                "e-mail [email protected]".format(v,k,l))

    return D
Exemplo n.º 11
0
def difference_family(v, k, l=1, existence=False, check=True):
    r"""
    Return a (``k``, ``l``)-difference family on an Abelian group of size ``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, 151, 181, 211, ...,  3061, 3121, 3181]
        sage: l6[Unknown]
        [61, 121]
        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))
         4: (3,2)
         5: (4,3)
         7: (3,2), (6,5)
         8: (7,6)
         9: (4,3), (8,7)
        11: (5,2), (5,4)
        13: (3,2), (4,3), (6,5)
        15: (7,3)
        16: (3,2), (5,4)
        17: (4,3), (8,7)
        19: (3,2), (6,5), (9,4), (9,8)
        25: (3,2), (4,3), (6,5), (8,7)
        29: (4,3), (7,6)
        31: (3,2), (5,4), (6,5)
        37: (3,2), (4,3), (6,5), (9,2), (9,8)
        41: (4,3), (5,4), (8,7)
        43: (3,2), (6,5), (7,6)
        49: (3,2), (4,3), (6,5), (8,7)
        53: (4,3)
        61: (3,2), (4,3), (5,4), (6,5)
        64: (3,2), (7,6), (9,8)
        67: (3,2), (6,5)
        71: (5,4), (7,6)
        73: (3,2), (4,3), (6,5), (8,7), (9,8)
        79: (3,2), (6,5)
        81: (4,3), (5,4), (8,7)
        89: (4,3), (8,7)
        97: (3,2), (4,3), (6,5), (8,7)

    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)

    .. TODO::

        There is a slightly more general version of difference families where
        the stabilizers of the blocks are taken into account. A block is *short*
        if the stabilizer is not trivial. The more general version is called a
        *partial difference family*. It is still possible to construct BIBD from
        this more general version (see the chapter 16 in the Handbook
        [DesignHandbook]_).

        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)
    """
    if (l * (v - 1)) % (k * (k - 1)) != 0:
        if existence:
            return False
        raise EmptySetError(
            "A (v,%d,%d)-difference family may exist only if %d*(v-1) = mod %d"
            % (k, l, l, k * (k - 1)))

    from block_design import are_hyperplanes_in_projective_geometry_parameters
    from database import DF_constructions
    if (v, k, l) in DF_constructions:
        if existence:
            return True
        return DF_constructions[(v, k, l)]()

    e = k * (k - 1)
    t = l * (v - 1) // e  # number of blocks

    D = None

    if arith.is_prime_power(v):
        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

    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
Exemplo n.º 12
0
def radical_difference_set(K, k, l=1, existence=False, check=True):
    r"""
    Return a difference set made of a cyclotomic coset in the finite field
    ``K`` and with paramters ``k`` and ``l``.

    Most of these difference sets appear in chapter VI.18.48 of the Handbook of
    combinatorial designs.

    EXAMPLES::

        sage: from sage.combinat.designs.difference_family import radical_difference_set

        sage: D = radical_difference_set(GF(7), 3, 1); D
        [[1, 2, 4]]
        sage: sorted(x-y for x in D[0] for y in D[0] if x != y)
        [1, 2, 3, 4, 5, 6]

        sage: D = radical_difference_set(GF(16,'a'), 6, 2)
        sage: sorted(x-y for x in D[0] for y in D[0] if x != y)
        [1,
         1,
         a,
         a,
         a + 1,
         a + 1,
         a^2,
         a^2,
         ...
         a^3 + a^2 + a + 1,
         a^3 + a^2 + a + 1]

        sage: for k in range(2,50):
        ....:     for l in reversed(divisors(k*(k-1))):
        ....:         v = k*(k-1)//l + 1
        ....:         if is_prime_power(v) and radical_difference_set(GF(v,'a'),k,l,existence=True):
        ....:             _ = radical_difference_set(GF(v,'a'),k,l)
        ....:             print "{:3} {:3} {:3}".format(v,k,l)
          3   2   1
          4   3   2
          7   3   1
          5   4   3
          7   4   2
         13   4   1
         11   5   2
          7   6   5
         11   6   3
         16   6   2
          8   7   6
          9   8   7
         19   9   4
         37   9   2
         73   9   1
         11  10   9
         19  10   5
         23  11   5
         13  12  11
         23  12   6
         27  13   6
         27  14   7
         16  15  14
         31  15   7
        ...
         41  40  39
         79  40  20
         83  41  20
         43  42  41
         83  42  21
         47  46  45
         49  48  47
        197  49  12
    """
    v = K.cardinality()

    if l*(v-1) != k*(k-1):
        if existence:
            return False
        raise EmptySetError("l*(v-1) is not equal to k*(k-1)")

    # trivial case
    if (v-1) == k:
        if existence:
            return True
        add_zero = False

    # q = 3 mod 4
    elif v%4 == 3 and k == (v-1)//2:
        if existence:
            return True
        add_zero = False

    # q = 3 mod 4
    elif v%4 == 3 and k == (v+1)//2:
        if existence:
            return True
        add_zero = True

    # 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
        add_zero = False

    # 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
        add_zero = True

    # exceptional case 1
    elif (v,k,l) == (16,6,2):
        if existence:
            return True
        add_zero = True

    # exceptional case 2
    elif (v,k,l) == (73,9,1):
        if existence:
            return True
        add_zero = False

    # are there more ??
    else:
        x = K.multiplicative_generator()
        D = K.cyclotomic_cosets(x**((v-1)//k), [K.one()])
        if is_difference_family(K, D, v, k, l):
            print "**  You found a new example of radical difference set **\n"\
                  "**  for the parameters (v,k,l)=({},{},{}).            **\n"\
                  "**  Please contact [email protected]        **\n".format(v,k,l)
            if existence:
                return True
            add_zero = False

        else:
            D = K.cyclotomic_cosets(x**((v-1)//(k-1)), [K.one()])
            D[0].insert(0,K.zero())
            if is_difference_family(K, D, v, k, l):
                print "**  You found a new example of radical difference set **\n"\
                      "**  for the parameters (v,k,l)=({},{},{}).            **\n"\
                      "**  Please contact [email protected]        **\n".format(v,k,l)
                if existence:
                    return True
                add_zero = True

            elif existence:
                return False
            else:
                raise EmptySetError("no radical difference set exist "
                        "for the parameters (v,k,l) = ({},{},{}".format(v,k,l))

    x = K.multiplicative_generator()
    if add_zero:
        r = x**((v-1)//(k-1))
        D = K.cyclotomic_cosets(r, [K.one()])
        D[0].insert(0, K.zero())
    else:
        r = x**((v-1)//k)
        D = K.cyclotomic_cosets(r, [K.one()])

    if check and not is_difference_family(K, D, v, k, l):
        raise RuntimeError("Sage tried to build a radical difference set with "
                "parameters ({},{},{}) but it seems that it failed! Please "
                "e-mail [email protected]".format(v,k,l))

    return D
Exemplo n.º 13
0
def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False):
    r"""
    Return a Regular Symmetric Hadamard Matrix with Constant Diagonal.

    A Hadamard matrix is said to be *regular* if its rows all sum to the same
    value.

    When `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if
    `M` is a regular symmetric Hadamard matrix with constant diagonal
    `\delta\in\{-1,+1\}` and row values all equal to `\delta \epsilon
    \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in
    [BH12]_.

    INPUT:

    - ``n`` (integer) -- side of the matrix

    - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon`

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1)
        [ 1  1  1 -1]
        [ 1  1 -1  1]
        [ 1 -1  1  1]
        [-1  1  1  1]
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1)
        [ 1 -1 -1 -1]
        [-1  1 -1 -1]
        [-1 -1  1 -1]
        [-1 -1 -1  1]

    Other hardcoded values::

        sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]:
        ....:     print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)
        36 x 36 dense matrix over Integer Ring
        36 x 36 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        196 x 196 dense matrix over Integer Ring

    From two close prime powers::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1)
        64 x 64 dense matrix over Integer Ring

    Recursive construction::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1)
        144 x 144 dense matrix over Integer Ring

    REFERENCE:

    .. [BH12] A. Brouwer and W. Haemers,
      Spectra of graphs,
      Springer, 2012,
      http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf

    .. [HX10] W. Haemers and Q. Xiang,
      Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`,
      European Journal of Combinatorics,
      Volume 31, Issue 6, August 2010, Pages 1553-1559,
      http://dx.doi.org/10.1016/j.ejc.2009.07.009.
    """
    if existence and (n,e) in _rshcd_cache:
        return _rshcd_cache[n,e]

    from sage.graphs.strongly_regular_db import strongly_regular_graph

    def true():
        _rshcd_cache[n,e] = True
        return True

    M = None
    if abs(e) != 1:
        raise ValueError
    if n<0:
        if existence:
            return False
        raise ValueError
    elif n == 4:
        if existence:
            return true()
        if e == 1:
            M = J(4)-2*matrix(4,[[int(i+j == 3) for i in range(4)] for j in range(4)])
        else:
            M = -J(4)+2*I(4)
    elif n ==  36:
        if existence:
            return true()
        if e == 1:
            M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix()
            M = J(36) - 2*M
        else:
            M = strongly_regular_graph(36,14,4,6).adjacency_matrix()
            M =  -J(36) + 2*M + 2*I(36)
    elif n == 100:
        if existence:
            return true()
        if e == -1:
            M = strongly_regular_graph(100,44,18,20).adjacency_matrix()
            M = 2*M - J(100) + 2*I(100)
        else:
            M = strongly_regular_graph(100,45,20,20).adjacency_matrix()
            M = J(100) - 2*M
    elif n == 196 and e == 1:
        if existence:
            return true()
        M = strongly_regular_graph(196,91,42,42).adjacency_matrix()
        M = J(196) - 2*M
    elif (  e  == 1                 and
          n%16 == 0                 and
          is_square(n)              and
          is_prime_power(sqrt(n)-1) and
          is_prime_power(sqrt(n)+1)):
        if existence:
            return true()
        M = -rshcd_from_close_prime_powers(int(sqrt(n)))

    # Recursive construction: the kronecker product of two RSHCD is a RSHCD
    else:
        from itertools import product
        for n1,e1 in product(divisors(n)[1:-1],[-1,1]):
            e2 = e1*e
            n2 = n//n1
            if (regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1,existence=True) and
                regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2,existence=True)):
                if existence:
                    return true()
                M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1)
                M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2)
                M  = M1.tensor_product(M2)
                break

    if M is None:
        from sage.misc.unknown import Unknown
        _rshcd_cache[n,e] = Unknown
        if existence:
            return Unknown
        raise ValueError("I do not know how to build a {}-RSHCD".format((n,e)))

    assert M*M.transpose() == n*I(n)
    assert set(map(sum,M)) == {e*sqrt(n)}

    return M
Exemplo n.º 14
0
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
Exemplo n.º 15
0
def difference_family(v, k, l=1, existence=False, check=True):
    r"""
    Return a (``k``, ``l``)-difference family on an Abelian group of size ``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, 151, 181, 211, ...,  3061, 3121, 3181]
        sage: l6[Unknown]
        [61, 121]
        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))
         4: (3,2)
         5: (4,3)
         7: (3,2), (6,5)
         8: (7,6)
         9: (4,3), (8,7)
        11: (5,2), (5,4)
        13: (3,2), (4,3), (6,5)
        15: (7,3)
        16: (3,2), (5,4)
        17: (4,3), (8,7)
        19: (3,2), (6,5), (9,4), (9,8)
        25: (3,2), (4,3), (6,5), (8,7)
        29: (4,3), (7,6)
        31: (3,2), (5,4), (6,5)
        37: (3,2), (4,3), (6,5), (9,2), (9,8)
        41: (4,3), (5,4), (8,7)
        43: (3,2), (6,5), (7,6)
        49: (3,2), (4,3), (6,5), (8,7)
        53: (4,3)
        61: (3,2), (4,3), (5,4), (6,5)
        64: (3,2), (7,6), (9,8)
        67: (3,2), (6,5)
        71: (5,4), (7,6)
        73: (3,2), (4,3), (6,5), (8,7), (9,8)
        79: (3,2), (6,5)
        81: (4,3), (5,4), (8,7)
        89: (4,3), (8,7)
        97: (3,2), (4,3), (6,5), (8,7)

    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)

    .. TODO::

        There is a slightly more general version of difference families where
        the stabilizers of the blocks are taken into account. A block is *short*
        if the stabilizer is not trivial. The more general version is called a
        *partial difference family*. It is still possible to construct BIBD from
        this more general version (see the chapter 16 in the Handbook
        [DesignHandbook]_).

        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)
    """
    if (l * (v - 1)) % (k * (k - 1)) != 0:
        if existence:
            return False
        raise EmptySetError(
            "A (v,%d,%d)-difference family may exist only if %d*(v-1) = mod %d" % (k, l, l, k * (k - 1))
        )

    from block_design import are_hyperplanes_in_projective_geometry_parameters
    from database import DF_constructions

    if (v, k, l) in DF_constructions:
        if existence:
            return True
        return DF_constructions[(v, k, l)]()

    e = k * (k - 1)
    t = l * (v - 1) // e  # number of blocks

    D = None

    if arith.is_prime_power(v):
        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

    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
Exemplo n.º 16
0
def regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e,existence=False):
    r"""
    Return a Regular Symmetric Hadamard Matrix with Constant Diagonal.

    A Hadamard matrix is said to be *regular* if its rows all sum to the same
    value.

    When `\epsilon\in\{-1,+1\}`, we say that `M` is a `(n,\epsilon)-RSHCD` if
    `M` is a regular symmetric Hadamard matrix with constant diagonal
    `\delta\in\{-1,+1\}` and row values all equal to `\delta \epsilon
    \sqrt(n)`. For more information, see [HX10]_ or 10.5.1 in
    [BH12]_.

    INPUT:

    - ``n`` (integer) -- side of the matrix

    - ``e`` -- one of `-1` or `+1`, equal to the value of `\epsilon`

    EXAMPLES::

        sage: from sage.combinat.matrices.hadamard_matrix import regular_symmetric_hadamard_matrix_with_constant_diagonal
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,1)
        [ 1  1  1 -1]
        [ 1  1 -1  1]
        [ 1 -1  1  1]
        [-1  1  1  1]
        sage: regular_symmetric_hadamard_matrix_with_constant_diagonal(4,-1)
        [ 1 -1 -1 -1]
        [-1  1 -1 -1]
        [-1 -1  1 -1]
        [-1 -1 -1  1]

    Other hardcoded values::

        sage: for n,e in [(36,1),(36,-1),(100,1),(100,-1),(196, 1)]:
        ....:     print regular_symmetric_hadamard_matrix_with_constant_diagonal(n,e)
        36 x 36 dense matrix over Integer Ring
        36 x 36 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        100 x 100 dense matrix over Integer Ring
        196 x 196 dense matrix over Integer Ring

    From two close prime powers::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(64,-1)
        64 x 64 dense matrix over Integer Ring

    Recursive construction::

        sage: print regular_symmetric_hadamard_matrix_with_constant_diagonal(144,-1)
        144 x 144 dense matrix over Integer Ring

    REFERENCE:

    .. [BH12] A. Brouwer and W. Haemers,
      Spectra of graphs,
      Springer, 2012,
      http://homepages.cwi.nl/~aeb/math/ipm/ipm.pdf

    .. [HX10] W. Haemers and Q. Xiang,
      Strongly regular graphs with parameters `(4m^4,2m^4+m^2,m^4+m^2,m^4+m^2)` exist for all `m>1`,
      European Journal of Combinatorics,
      Volume 31, Issue 6, August 2010, Pages 1553-1559,
      http://dx.doi.org/10.1016/j.ejc.2009.07.009.
    """
    if existence and (n,e) in _rshcd_cache:
        return _rshcd_cache[n,e]

    from sage.graphs.strongly_regular_db import strongly_regular_graph

    def true():
        _rshcd_cache[n,e] = True
        return True

    M = None
    if abs(e) != 1:
        raise ValueError
    if n<0:
        if existence:
            return False
        raise ValueError
    elif n == 4:
        if existence:
            return true()
        if e == 1:
            M = J(4)-2*matrix(4,[[int(i+j == 3) for i in range(4)] for j in range(4)])
        else:
            M = -J(4)+2*I(4)
    elif n ==  36:
        if existence:
            return true()
        if e == 1:
            M = strongly_regular_graph(36, 15, 6, 6).adjacency_matrix()
            M = J(36) - 2*M
        else:
            M = strongly_regular_graph(36,14,4,6).adjacency_matrix()
            M =  -J(36) + 2*M + 2*I(36)
    elif n == 100:
        if existence:
            return true()
        if e == -1:
            M = strongly_regular_graph(100,44,18,20).adjacency_matrix()
            M = 2*M - J(100) + 2*I(100)
        else:
            M = strongly_regular_graph(100,45,20,20).adjacency_matrix()
            M = J(100) - 2*M
    elif n == 196 and e == 1:
        if existence:
            return true()
        M = strongly_regular_graph(196,91,42,42).adjacency_matrix()
        M = J(196) - 2*M
    elif (  e  == 1                 and
          n%16 == 0                 and
          is_square(n)              and
          is_prime_power(sqrt(n)-1) and
          is_prime_power(sqrt(n)+1)):
        if existence:
            return true()
        M = -rshcd_from_close_prime_powers(int(sqrt(n)))

    # Recursive construction: the kronecker product of two RSHCD is a RSHCD
    else:
        from itertools import product
        for n1,e1 in product(divisors(n)[1:-1],[-1,1]):
            e2 = e1*e
            n2 = n//n1
            if (regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1,existence=True) and
                regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2,existence=True)):
                if existence:
                    return true()
                M1 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n1,e1)
                M2 = regular_symmetric_hadamard_matrix_with_constant_diagonal(n2,e2)
                M  = M1.tensor_product(M2)
                break

    if M is None:
        from sage.misc.unknown import Unknown
        _rshcd_cache[n,e] = Unknown
        if existence:
            return Unknown
        raise ValueError("I do not know how to build a {}-RSHCD".format((n,e)))

    assert M*M.transpose() == n*I(n)
    assert set(map(sum,M)) == {e*sqrt(n)}

    return M