def is_represented_by_unit(a,c,p): #we can forget about powers of p, because they are units; then we can work with integers a, c, p = ZZ(a), ZZ(c), ZZ(p) vala = ZZ(0) valc = ZZ(0) while a % p == 0: vala += 1 a = ZZ(a / p) while c % p == 0: valc += 1 c = ZZ(c/p) aa = a.abs() # verbose('is_rep_by_unit aa=%s, cc=%s'%(aa,cc_mod_aa)) G = Zmod(aa) cc = G(c) Gp = G(p) res = [] sizeG = G.unit_group_order() expn = G.unit_group_exponent() # verbose('Group of order=%s'%sizeG) try: n0 = discrete_log(cc,Gp,sizeG,operation = '*') n0 = n0 % expn if n0 > expn/2: n0 -= expn res.append( p**ZZ(n0 + valc) ) except (ValueError,RuntimeError): pass try: n0 = discrete_log(cc,-Gp,sizeG,operation = '*') n0 = n0 % expn if n0 > expn/2: n0 -= expn res.append( ZZ(-1)**n0 * p**ZZ(n0 + valc) ) except (ValueError,RuntimeError): pass return res
def log(self, base): """ Return `x` such that `b^x = a`, where `x` is `a` and `b` is the base. INPUT: - ``self`` - finite field element - ``b`` - finite field element that generates the multiplicative group. OUTPUT: Integer `x` such that `a^x = b`, if it exists. Raises a ValueError exception if no such `x` exists. EXAMPLES:: sage: F = GF(17) sage: F(3^11).log(F(3)) 11 sage: F = GF(113) sage: F(3^19).log(F(3)) 19 sage: F = GF(next_prime(10000)) sage: F(23^997).log(F(23)) 997 :: sage: F = FiniteField(2^10, 'a') sage: g = F.gen() sage: b = g; a = g^37 sage: a.log(b) 37 sage: b^37; a a^8 + a^7 + a^4 + a + 1 a^8 + a^7 + a^4 + a + 1 AUTHORS: - David Joyner and William Stein (2005-11) """ from sage.groups.generic import discrete_log b = self.parent()(base) # TODO: This function is TERRIBLE! return discrete_log(self, b)
def log(self, base): """ Return `x` such that `b^x = a`, where `x` is `a` and `b` is the base. INPUT: - ``b`` -- finite field element that generates the multiplicative group. OUTPUT: Integer `x` such that `a^x = b`, if it exists. Raises a ``ValueError`` exception if no such `x` exists. EXAMPLES:: sage: F = GF(17) sage: F(3^11).log(F(3)) 11 sage: F = GF(113) sage: F(3^19).log(F(3)) 19 sage: F = GF(next_prime(10000)) sage: F(23^997).log(F(23)) 997 :: sage: F = FiniteField(2^10, 'a', impl='pari_mod') sage: g = F.gen() sage: b = g; a = g^37 sage: a.log(b) 37 sage: b^37; a a^8 + a^7 + a^4 + a + 1 a^8 + a^7 + a^4 + a + 1 AUTHORS: - David Joyner and William Stein (2005-11) """ from sage.groups.generic import discrete_log b = self.parent()(base) # TODO: This function is TERRIBLE! return discrete_log(self, b)
def kirkman_triple_system(v,existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: to_name = lambda (r,s,t): ' '+names[r]+names[s]+names[t]+' ' sage: rows = [join(('Day {}'.format(i) for i in range(1,8)), ' ')] sage: rows.extend(join(map(to_name,row), ' ') for row in zip(*classes)) sage: print join(rows,'\n') Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 07e 18e 29e 3ae 4be 5ce 6de 139 24a 35b 46c 05d 167 028 26b 03c 14d 257 368 049 15a 458 569 06a 01b 12c 23d 347 acd 7bd 78c 89d 79a 8ab 9bc TESTS:: sage: for i in range(3,300,6): ....: _ = designs.kirkman_triple_system(i) """ if v%6 != 3: if existence: return False raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) if existence: return False elif v == 3: return BalancedIncompleteBlockDesign(3,[[0,1,2]],k=3,lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v-1)//2)%6 == 1 and is_prime_power((v-1)//2): from sage.rings.finite_rings.constructor import FiniteField as GF q = (v-1)//2 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t+1)/2, a) assert 2*a**m == a**t+1 # First parallel class first_class = [[(0,1),(0,2),'inf']] b0 = K.one(); b1 = a**t; b2 = a**m first_class.extend([(b0*a**i,1),(b1*a**i,1),(b2*a**i,2)] for i in range(t)+range(2*t,3*t)+range(4*t,5*t)) b0 = a**(m+t); b1=a**(m+3*t); b2=a**(m+5*t) first_class.extend([[(b0*a**i,2),(b1*a**i,2),(b2*a**i,2)] for i in range(t)]) # Action of K on the points action = lambda v,x : (v+x[0],x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p,x): i+(x-1)*q for i,p in enumerate(K) for x in [1,2]} relabel['inf'] = 2*q classes = [[[relabel[action(p,x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v//3)%6 == 1 and is_prime_power(v//3): from sage.rings.finite_rings.constructor import FiniteField as GF q = v//3 K = GF(q,'x') a = K.primitive_element() t = (q-1)/6 A0 = [(0,0),(0,1),(0,2)] B = [[(a**i,j),(a**(i+2*t),j),(a**(i+4*t),j)] for j in range(3) for i in range(t)] A = [[(a**i,0),(a**(i+2*t),1),(a**(i+4*t),2)] for i in range(6*t)] # Action of K on the points action = lambda v,x: (v+x[0],x[1]) # relabel to integer relabel = {(p,j): i+j*q for i,p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2*t] + A[3*t:4*t] + A[5*t:6*t] # Classes classes = [[[relabel[action(p,x)] for x in tr] for tr in B0] for p in K] for i in range(t)+range(2*t,3*t)+range(4*t,5*t): classes.append([[relabel[action(p,x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign(v,[tr for cl in classes for tr in cl],k=3,lambd=1,copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v:i for i,v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v:i for i,v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i,b in enumerate(B): if 8 in b: j = min(b); del B[i]; B.insert(0,j); break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i,b in enumerate(B): if 14 in b: j = min(b); del B[i]; B.insert(0,j); break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v-1)/2)] gdd = {4:gdd4, 7: gdd7} for B in PBD_4_7((v-1)//2,check=False): for i,classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2*B[x//2]+x%2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i,classs in enumerate(classes): classs.append([2*i,2*i+1,v-1]) KTS = BalancedIncompleteBlockDesign(v, blocks = [tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy =False) KTS._classes = classes assert KTS.is_resolvable() return KTS
def kirkman_triple_system(v, existence=False): r""" Return a Kirkman Triple System on `v` points. A Kirkman Triple System `KTS(v)` is a resolvable Steiner Triple System. It exists if and only if `v\equiv 3\pmod{6}`. INPUT: - `n` (integer) - ``existence`` (boolean; ``False`` by default) -- whether to build the `KTS(n)` or only answer whether it exists. .. SEEALSO:: :meth:`IncidenceStructure.is_resolvable` EXAMPLES: A solution to Kirkmman's original problem:: sage: kts = designs.kirkman_triple_system(15) sage: classes = kts.is_resolvable(1)[1] sage: names = '0123456789abcde' sage: def to_name(r_s_t): ....: r, s, t = r_s_t ....: return ' ' + names[r] + names[s] + names[t] + ' ' sage: rows = [' '.join(('Day {}'.format(i) for i in range(1,8)))] sage: rows.extend(' '.join(map(to_name,row)) for row in zip(*classes)) sage: print('\n'.join(rows)) Day 1 Day 2 Day 3 Day 4 Day 5 Day 6 Day 7 07e 18e 29e 3ae 4be 5ce 6de 139 24a 35b 46c 05d 167 028 26b 03c 14d 257 368 049 15a 458 569 06a 01b 12c 23d 347 acd 7bd 78c 89d 79a 8ab 9bc TESTS:: sage: for i in range(3,300,6): ....: _ = designs.kirkman_triple_system(i) """ if v % 6 != 3: if existence: return False raise ValueError("There is no KTS({}) as v!=3 mod(6)".format(v)) if existence: return False elif v == 3: return BalancedIncompleteBlockDesign(3, [[0, 1, 2]], k=3, lambd=1) elif v == 9: classes = [[[0, 1, 5], [2, 6, 7], [3, 4, 8]], [[1, 6, 8], [3, 5, 7], [0, 2, 4]], [[1, 4, 7], [0, 3, 6], [2, 5, 8]], [[4, 5, 6], [0, 7, 8], [1, 2, 3]]] KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS # Construction 1.1 from [Stinson91] (originally Theorem 6 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(2q+1) elif ((v - 1) // 2) % 6 == 1 and is_prime_power((v - 1) // 2): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = (v - 1) // 2 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 # m is the solution of a^m=(a^t+1)/2 from sage.groups.generic import discrete_log m = discrete_log((a**t + 1) / 2, a) assert 2 * a**m == a**t + 1 # First parallel class first_class = [[(0, 1), (0, 2), 'inf']] b0 = K.one() b1 = a**t b2 = a**m first_class.extend([(b0 * a**i, 1), (b1 * a**i, 1), (b2 * a**i, 2)] for i in list(range(t)) + list(range(2 * t, 3 * t)) + list(range(4 * t, 5 * t))) b0 = a**(m + t) b1 = a**(m + 3 * t) b2 = a**(m + 5 * t) first_class.extend([[(b0 * a**i, 2), (b1 * a**i, 2), (b2 * a**i, 2)] for i in range(t)]) # Action of K on the points action = lambda v, x: (v + x[0], x[1]) if len(x) == 2 else x # relabel to integer relabel = {(p, x): i + (x - 1) * q for i, p in enumerate(K) for x in [1, 2]} relabel['inf'] = 2 * q classes = [[[relabel[action(p, x)] for x in tr] for tr in first_class] for p in K] KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS # Construction 1.2 from [Stinson91] (originally Theorem 5 from [RCW71]) # # For all prime powers q=1 mod 6, there exists a KTS(3q) elif (v // 3) % 6 == 1 and is_prime_power(v // 3): from sage.rings.finite_rings.finite_field_constructor import FiniteField as GF q = v // 3 K = GF(q, 'x') a = K.primitive_element() t = (q - 1) // 6 A0 = [(0, 0), (0, 1), (0, 2)] B = [[(a**i, j), (a**(i + 2 * t), j), (a**(i + 4 * t), j)] for j in range(3) for i in range(t)] A = [[(a**i, 0), (a**(i + 2 * t), 1), (a**(i + 4 * t), 2)] for i in range(6 * t)] # Action of K on the points action = lambda v, x: (v + x[0], x[1]) # relabel to integer relabel = {(p, j): i + j * q for i, p in enumerate(K) for j in range(3)} B0 = [A0] + B + A[t:2 * t] + A[3 * t:4 * t] + A[5 * t:6 * t] # Classes classes = [[[relabel[action(p, x)] for x in tr] for tr in B0] for p in K] for i in list(range(t)) + list(range(2 * t, 3 * t)) + list( range(4 * t, 5 * t)): classes.append([[relabel[action(p, x)] for x in A[i]] for p in K]) KTS = BalancedIncompleteBlockDesign( v, [tr for cl in classes for tr in cl], k=3, lambd=1, copy=False) KTS._classes = classes return KTS else: # This is Lemma IX.6.4 from [BJL99]. # # This construction takes a (v,{4,7})-PBD. All points are doubled (x has # a copy x'), and an infinite point \infty is added. # # On all blocks of 2*4 points we "paste" a KTS(2*4+1) using the infinite # point, in such a way that all {x,x',infty} are set of the design. We # do the same for blocks with 2*7 points using a KTS(2*7+1). # # Note that the triples of points equal to {x,x',\infty} will be added # several times. # # As all those subdesigns are resolvable, each class of the KTS(n) is # obtained by considering a set {x,x',\infty} and all sets of all # parallel classes of the subdesign which contain this set. # We create the small KTS(n') we need, and relabel them such that # 01(n'-1),23(n'-1),... are blocks of the design. gdd4 = kirkman_triple_system(9) gdd7 = kirkman_triple_system(15) X = [B for B in gdd4 if 8 in B] for b in X: b.remove(8) X = sum(X, []) + [8] gdd4.relabel({v: i for i, v in enumerate(X)}) gdd4 = gdd4.is_resolvable(True)[1] # the relabeled classes X = [B for B in gdd7 if 14 in B] for b in X: b.remove(14) X = sum(X, []) + [14] gdd7.relabel({v: i for i, v in enumerate(X)}) gdd7 = gdd7.is_resolvable(True)[1] # the relabeled classes # The first parallel class contains 01(n'-1), the second contains # 23(n'-1), etc.. # Then remove the blocks containing (n'-1) for B in gdd4: for i, b in enumerate(B): if 8 in b: j = min(b) del B[i] B.insert(0, j) break gdd4.sort() for B in gdd4: B.pop(0) for B in gdd7: for i, b in enumerate(B): if 14 in b: j = min(b) del B[i] B.insert(0, j) break gdd7.sort() for B in gdd7: B.pop(0) # Pasting the KTS(n') without {x,x',\infty} blocks classes = [[] for i in range((v - 1) // 2)] gdd = {4: gdd4, 7: gdd7} for B in PBD_4_7((v - 1) // 2, check=False): for i, classs in enumerate(gdd[len(B)]): classes[B[i]].extend([[2 * B[x // 2] + x % 2 for x in BB] for BB in classs]) # The {x,x',\infty} blocks for i, classs in enumerate(classes): classs.append([2 * i, 2 * i + 1, v - 1]) KTS = BalancedIncompleteBlockDesign( v, blocks=[tr for cl in classes for tr in cl], k=3, lambd=1, check=True, copy=False) KTS._classes = classes assert KTS.is_resolvable() return KTS
def one_radical_difference_family(K, k): r""" Search for a radical difference family on ``K`` using dancing links algorithm. For the definition of radical difference family, see :func:`radical_difference_family`. Here, we consider only radical difference family with `\lambda = 1`. INPUT: - ``K`` -- a finite field of cardinality `q`. - ``k`` -- a positive integer so that `k(k-1)` divides `q-1`. OUTPUT: Either a difference family or ``None`` if it does not exist. ALGORITHM: The existence of a radical difference family is equivalent to a one dimensional tiling (or packing) problem in a cyclic group. This subsequent problem is solved by a call to the function :func:`one_cyclic_tiling`. Let `K^*` be the multiplicative group of the finite field `K`. A radical family has the form `\mathcal B = \{x_1 B, \ldots, x_k B\}`, where `B=\{x:x^{k}=1\}` (for `k` odd) or `B=\{x:x^{k-1}=1\}\cup \{0\}` (for `k` even). Equivalently, `K^*` decomposes as: .. MATH:: K^* = \Delta (x_1 B) \cup ... \cup \Delta (x_k B) = x_1 \Delta B \cup ... \cup x_k \Delta B We observe that `C=B\backslash 0` is a subgroup of the (cyclic) group `K^*`, that can thus be generated by some element `r`. Furthermore, we observe that `\Delta B` is always a union of cosets of `\pm C` (which is twice larger than `C`). .. MATH:: \begin{array}{llll} (k\text{ odd} ) & \Delta B &= \{r^i-r^j:r^i\neq r^j\} &= \pm C\cdot \{r^i-1: 0 < i \leq m\}\\ (k\text{ even}) & \Delta B &= \{r^i-r^j:r^i\neq r^j\}\cup C &= \pm C\cdot \{r^i-1: 0 < i < m\}\cup \pm C \end{array} where .. MATH:: (k\text{ odd})\ m = (k-1)/2 \quad \text{and} \quad (k\text{ even})\ m = k/2. Consequently, `\mathcal B = \{x_1 B, \ldots, x_k B\}` is a radical difference family if and only if `\{x_1 (\Delta B/(\pm C)), \ldots, x_k (\Delta B/(\pm C))\}` is a partition of the cyclic group `K^*/(\pm C)`. EXAMPLES:: sage: from sage.combinat.designs.difference_family import ( ....: one_radical_difference_family, ....: is_difference_family) sage: one_radical_difference_family(GF(13),4) [[0, 1, 3, 9]] The parameters that appear in [Bu95]_:: sage: df = one_radical_difference_family(GF(449), 8); df [[0, 1, 18, 25, 176, 324, 359, 444], [0, 9, 88, 162, 222, 225, 237, 404], [0, 11, 140, 198, 275, 357, 394, 421], [0, 40, 102, 249, 271, 305, 388, 441], [0, 49, 80, 93, 161, 204, 327, 433], [0, 70, 99, 197, 230, 362, 403, 435], [0, 121, 141, 193, 293, 331, 335, 382], [0, 191, 285, 295, 321, 371, 390, 392]] sage: is_difference_family(GF(449), df, 449, 8, 1) True """ q = K.cardinality() x = K.multiplicative_generator() e = k*(k-1) if q%e != 1: raise ValueError("q%e is not 1") # We define A by (see the function's documentation): # ΔB = C.A if k%2 == 1: m = (k-1) // 2 r = x ** ((q-1) // k) # k-th root of unity A = [r**i - 1 for i in range(1,m+1)] else: m = k // 2 r = x ** ((q-1) // (k-1)) # (k-1)-th root of unity A = [r**i - 1 for i in range(1,m)] A.append(K.one()) # instead of the complicated multiplicative group K^*/(±C) we use the # discrete logarithm to convert everything into the additive group Z/cZ c = m * (q-1) // e # cardinal of ±C from sage.groups.generic import discrete_log logA = [discrete_log(a,x)%c for a in A] # if two elments of A are equal modulo c then no tiling is possible if len(set(logA)) != m: return None # brute force tiling = one_cyclic_tiling(logA, c) if tiling is None: return None D = K.cyclotomic_cosets(r, [x**i for i in tiling]) if k%2 == 0: for d in D: d.insert(K.zero(),0) return D