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]
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])
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
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
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
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
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
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 = {}
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
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
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
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
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
def difference_family(v, k, l=1, existence=False, check=True): r""" Return a (``k``, ``l``)-difference family on an Abelian group of cardinality ``v``. Let `G` be a finite Abelian group. For a given subset `D` of `G`, we define `\Delta D` to be the multi-set of differences `\Delta D = \{x - y; x \in D, y \in D, x \not= y\}`. A `(G,k,\lambda)`-*difference family* is a collection of `k`-subsets of `G`, `D = \{D_1, D_2, \ldots, D_b\}` such that the union of the difference sets `\Delta D_i` for `i=1,...b`, seen as a multi-set, contains each element of `G \backslash \{0\}` exactly `\lambda`-times. When there is only one block, i.e. `\lambda(v - 1) = k(k-1)`, then a `(G,k,\lambda)`-difference family is also called a *difference set*. See also :wikipedia:`Difference_set`. If there is no such difference family, an ``EmptySetError`` is raised and if there is no construction at the moment ``NotImplementedError`` is raised. EXAMPLES:: sage: K,D = designs.difference_family(73,4) sage: D [[0, 1, 8, 64], [0, 25, 54, 67], [0, 41, 36, 69], [0, 3, 24, 46], [0, 2, 16, 55], [0, 50, 35, 61]] sage: K,D = designs.difference_family(337,7) sage: D [[1, 175, 295, 64, 79, 8, 52], [326, 97, 125, 307, 142, 249, 102], [121, 281, 310, 330, 123, 294, 226], [17, 279, 297, 77, 332, 136, 210], [150, 301, 103, 164, 55, 189, 49], [35, 59, 215, 218, 69, 280, 135], [289, 25, 331, 298, 252, 290, 200], [191, 62, 66, 92, 261, 180, 159]] For `k=6,7` we look at the set of small prime powers for which a construction is available:: sage: def prime_power_mod(r,m): ....: k = m+r ....: while True: ....: if is_prime_power(k): ....: yield k ....: k += m sage: from itertools import islice sage: l6 = {True:[], False: [], Unknown: []} sage: for q in islice(prime_power_mod(1,30), 60): ....: l6[designs.difference_family(q,6,existence=True)].append(q) sage: l6[True] [31, 121, 151, 181, 211, ..., 3061, 3121, 3181] sage: l6[Unknown] [61] sage: l6[False] [] sage: l7 = {True: [], False: [], Unknown: []} sage: for q in islice(prime_power_mod(1,42), 60): ....: l7[designs.difference_family(q,7,existence=True)].append(q) sage: l7[True] [337, 421, 463, 883, 1723, 3067, 3319, 3529, 3823, 3907, 4621, 4957, 5167] sage: l7[Unknown] [43, 127, 169, 211, ..., 4999, 5041, 5209] sage: l7[False] [] Other constructions for `\lambda > 1`:: sage: for v in xrange(2,100): ....: constructions = [] ....: for k in xrange(2,10): ....: for l in xrange(2,10): ....: if designs.difference_family(v,k,l,existence=True): ....: constructions.append((k,l)) ....: _ = designs.difference_family(v,k,l) ....: if constructions: ....: print "%2d: %s"%(v, ', '.join('(%d,%d)'%(k,l) for k,l in constructions)) 2: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 3: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 4: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 5: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 7: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 8: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 9: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 11: (3,2), (4,3), (4,6), (5,2), (5,3), (5,4), (6,5), (7,6), (8,7), (9,8) 13: (3,2), (4,3), (5,4), (5,5), (6,5), (7,6), (8,7), (9,8) 15: (4,6), (5,6), (7,3) 16: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 17: (3,2), (4,3), (5,4), (5,5), (6,5), (7,6), (8,7), (9,8) 19: (3,2), (4,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,4), (9,5), (9,6), (9,7), (9,8) 21: (4,3), (6,3), (6,5) 22: (4,2), (6,5), (7,4), (8,8) 23: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 25: (3,2), (4,3), (5,4), (6,5), (7,6), (7,7), (8,7), (9,8) 27: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 28: (3,2), (6,5) 29: (3,2), (4,3), (5,4), (6,5), (7,3), (7,6), (8,4), (8,6), (8,7), (9,8) 31: (3,2), (4,2), (4,3), (5,2), (5,4), (6,5), (7,6), (8,7), (9,8) 32: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 33: (5,5), (6,5) 34: (4,2) 35: (5,2), (8,4) 37: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,2), (9,3), (9,8) 39: (6,5) 40: (3,2) 41: (3,2), (4,3), (5,4), (6,3), (6,5), (7,6), (8,7), (9,8) 43: (3,2), (4,2), (4,3), (5,4), (6,5), (7,2), (7,3), (7,6), (8,4), (8,7), (9,8) 46: (4,2), (6,2) 47: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 49: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,3), (9,8) 51: (5,2), (6,3) 53: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 55: (9,4) 57: (7,3) 59: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 61: (3,2), (4,3), (5,4), (6,2), (6,3), (6,5), (7,6), (8,7), (9,8) 64: (3,2), (4,3), (5,4), (6,5), (7,2), (7,6), (8,7), (9,8) 67: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 71: (3,2), (4,3), (5,2), (5,4), (6,5), (7,3), (7,6), (8,4), (8,7), (9,8) 73: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 75: (5,2) 79: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 81: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 83: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 85: (7,2), (7,3), (8,2) 89: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,8) 97: (3,2), (4,3), (5,4), (6,5), (7,6), (8,7), (9,3), (9,8) TESTS: Check more of the Wilson constructions from [Wi72]_:: sage: Q5 = [241, 281,421,601,641, 661, 701, 821,881] sage: Q9 = [73, 1153, 1873, 2017] sage: Q15 = [76231] sage: Q4 = [13, 73, 97, 109, 181, 229, 241, 277, 337, 409, 421, 457] sage: Q8 = [1009, 3137, 3697] sage: for Q,k in [(Q4,4),(Q5,5),(Q8,8),(Q9,9),(Q15,15)]: ....: for q in Q: ....: assert designs.difference_family(q,k,1,existence=True) is True ....: _ = designs.difference_family(q,k,1) Check Singer difference sets:: sage: sgp = lambda q,d: ((q**(d+1)-1)//(q-1), (q**d-1)//(q-1), (q**(d-1)-1)//(q-1)) sage: for q in range(2,10): ....: if is_prime_power(q): ....: for d in [2,3,4]: ....: v,k,l = sgp(q,d) ....: assert designs.difference_family(v,k,l,existence=True) is True ....: _ = designs.difference_family(v,k,l) Check twin primes difference sets:: sage: for p in [3,5,7,9,11]: ....: v = p*(p+2); k = (v-1)/2; lmbda = (k-1)/2 ....: G,D = designs.difference_family(v,k,lmbda) Check the database: sage: from sage.combinat.designs.database import DF sage: for v,k,l in DF: ....: df = designs.difference_family(v,k,l,check=True) .. TODO:: Implement recursive constructions from Buratti "Recursive for difference matrices and relative difference families" (1998) and Jungnickel "Composition theorems for difference families and regular planes" (1978) """ from block_design import are_hyperplanes_in_projective_geometry_parameters from database import DF if (v, k, l) in DF: if existence: return True vv, blocks = DF[v, k, l].iteritems().next() # Build the group from sage.rings.finite_rings.integer_mod_ring import Zmod if len(vv) == 1: G = Zmod(vv[0]) else: from sage.categories.cartesian_product import cartesian_product G = cartesian_product([Zmod(i) for i in vv]) df = [[G(i) for i in b] for b in blocks] if check: assert is_difference_family( G, df, v=v, k=k, l=l), "Sage built an invalid ({},{},{})-DF!".format(v, k, l) return G, df e = k * (k - 1) t = l * (v - 1) // e # number of blocks D = None factorization = arith.factor(v) if len(factorization) == 1: # i.e. is v a prime power from sage.rings.finite_rings.constructor import GF G = K = GF(v, 'z') x = K.multiplicative_generator() if l == (k - 1): if existence: return True return K, K.cyclotomic_cosets(x**((v - 1) // k))[1:] if t == 1: # some of the difference set constructions VI.18.48 from the # Handbook of combinatorial designs # q = 3 mod 4 if v % 4 == 3 and k == (v - 1) // 2: if existence: return True D = K.cyclotomic_cosets(x**2, [1]) # q = 4t^2 + 1, t odd elif v % 8 == 5 and k == (v - 1) // 4 and arith.is_square( (v - 1) // 4): if existence: return True D = K.cyclotomic_cosets(x**4, [1]) # q = 4t^2 + 9, t odd elif v % 8 == 5 and k == (v + 3) // 4 and arith.is_square( (v - 9) // 4): if existence: return True D = K.cyclotomic_cosets(x**4, [1]) D[0].insert(0, K.zero()) if D is None and l == 1: one = K.one() # Wilson (1972), Theorem 9 if k % 2 == 1: m = (k - 1) // 2 xx = x**m to_coset = { x**i * xx**j: i for i in xrange(m) for j in xrange((v - 1) / m) } r = x**((v - 1) // k) # primitive k-th root of unity if len(set(to_coset[r**j - one] for j in xrange(1, m + 1))) == m: if existence: return True B = [r**j for j in xrange(k) ] # = H^((k-1)t) whose difference is # H^(mt) (r^i - 1, i=1,..,m) # Now pick representatives a translate of R for by a set of # representatives of H^m / H^(mt) D = [[x**(i * m) * b for b in B] for i in xrange(t)] # Wilson (1972), Theorem 10 else: m = k // 2 xx = x**m to_coset = { x**i * xx**j: i for i in xrange(m) for j in xrange((v - 1) / m) } r = x**((v - 1) // (k - 1)) # primitive (k-1)-th root of unity if (all(to_coset[r**j - one] != 0 for j in xrange(1, m)) and len(set(to_coset[r**j - one] for j in xrange(1, m))) == m - 1): if existence: return True B = [K.zero()] + [r**j for j in xrange(k - 1)] D = [[x**(i * m) * b for b in B] for i in xrange(t)] # Wilson (1972), Theorem 11 if D is None and k == 6: r = x**((v - 1) // 3) # primitive cube root of unity r2 = r * r xx = x**5 to_coset = { x**i * xx**j: i for i in xrange(5) for j in xrange((v - 1) / 5) } for c in to_coset: if c == 1 or c == r or c == r2: continue if len( set(to_coset[elt] for elt in (r - 1, c * (r - 1), c - 1, c - r, c - r**2))) == 5: if existence: return True B = [one, r, r**2, c, c * r, c * r**2] D = [[x**(i * 5) * b for b in B] for i in xrange(t)] break # Twin prime powers construction (see :wikipedia:`Difference_set`) # # i.e. v = p(p+2) where p and p+2 are prime powers # k = (v-1)/2 # lambda = (k-1)/2 elif (len(factorization) == 2 and abs(pow(*factorization[0]) - pow(*factorization[1])) == 2 and k == (v - 1) // 2 and (l is None or 2 * l == (v - 1) // 2 - 1)): # A difference set can be built from the set of elements # (x,y) in GF(p) x GF(p+2) such that: # # - either y=0 # - x and y with x and y squares # - x and y with x and y non-squares if existence: return True from sage.rings.finite_rings.constructor import FiniteField from sage.categories.cartesian_product import cartesian_product from itertools import product p, q = pow(*factorization[0]), pow(*factorization[1]) if p > q: p, q = q, p Fp = FiniteField(p, 'x') Fq = FiniteField(q, 'x') Fpset = set(Fp) Fqset = set(Fq) Fp_squares = set(x**2 for x in Fpset) Fq_squares = set(x**2 for x in Fqset) # Pairs of squares, pairs of non-squares d = [] d.extend( product(Fp_squares.difference([0]), Fq_squares.difference([0]))) d.extend( product(Fpset.difference(Fp_squares), Fqset.difference(Fq_squares))) # All (x,0) d.extend((x, 0) for x in Fpset) G = cartesian_product([Fp, Fq]) D = [d] if D is None and are_hyperplanes_in_projective_geometry_parameters( v, k, l): _, (q, d) = are_hyperplanes_in_projective_geometry_parameters( v, k, l, True) if existence: return True else: G, D = singer_difference_set(q, d) if D is None: if existence: return Unknown raise NotImplementedError("No constructions for these parameters") if check and not is_difference_family(G, D, verbose=False): raise RuntimeError return G, D
def 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