def random_element(self): r""" Return a random parking function of size `n`. The algorithm uses a circular parking space with `n+1` spots. Then all `n` cars can park and there remains one empty spot. Spots are then renumbered so that the empty spot is `0`. The probability distribution is uniform on the set of `(n+1)^{n-1}` parking functions of size `n`. EXAMPLES:: sage: pf = ParkingFunctions(8) sage: a = pf.random_element(); a # random [5, 7, 2, 4, 2, 5, 1, 3] sage: a in pf True """ n = self.n Zm = Zmod(n + 1) fun = [Zm(randint(0, n)) for i in range(n)] free = [Zm(j) for j in range(n + 1)] for car in fun: position = car while not (position in free): position += Zm.one() free.remove(position) return ParkingFunction([(i - free[0]).lift() for i in fun])
def random_element(self): r""" Return a random parking function of size `n`. The algorithm uses a circular parking space with `n+1` spots. Then all `n` cars can park and there remains one empty spot. Spots are then renumbered so that the empty spot is `0`. The probability distribution is uniform on the set of `(n+1)^{n-1}` parking functions of size `n`. EXAMPLES:: sage: pf = ParkingFunctions(8) sage: a = pf.random_element(); a # random [5, 7, 2, 4, 2, 5, 1, 3] sage: a in pf True """ n = self.n Zm = Zmod(n + 1) fun = [Zm(randint(0, n)) for i in range(n)] free = [Zm(j) for j in range(n + 1)] for car in fun: position = car while not(position in free): position += Zm.one() free.remove(position) return ParkingFunction([(i - free[0]).lift() for i in fun])
def accept(p, n, r, l, e, s): ''' This function is passed down to `sieve`. It accepts only if: (1) the order t of p in ℤ/l^e is a multiple of s; (2) gcd(nlcm, t / gcd(t, ngcd)) = 1. (3) gcd(s, φ(m)/s) = 1. (4) gcd(t/s, p) = 1. These three conditions imply the conditions (1)-(3) above assuming l is prime. ''' # Degrees of the ambient fields ngcd, nlcm = n # Generic case if l != 2 and p != l: m = l**e phi = (l - 1) * l**(e - 1) ord = Zmod(m)(p).multiplicative_order() return (ord % s.expand() == 0 and (phi // s.expand()).gcd(s.expand()) == 1 and (ord // (ord.gcd(ngcd))).gcd(nlcm) == 1 and (ord // s.expand()).gcd(p) == 1) # Special treatement for ℤ/2^x elif l == 2: return NotImplementedError else: return False
def accept(p, n, r, l, e, s): ''' This function is passed down to `sieve`. It accepts only if: (1) the order t of p in ℤ/l^e is a multiple of s; (2) gcd(nlcm, t / gcd(t, ngcd)) = 1. (3) gcd(s, φ(m)/s) = 1. (4) gcd(t/s, p) = 1. These three conditions imply the conditions (1)-(3) above assuming l is prime. ''' # Degrees of the ambient fields ngcd, nlcm = n # Generic case if l != 2 and p != l: m = l**e phi = (l - 1) * l**(e-1) ord = Zmod(m)(p).multiplicative_order() return (ord % s.expand() == 0 and (phi // s.expand()).gcd(s.expand()) == 1 and (ord // (ord.gcd(ngcd))).gcd(nlcm) == 1 and (ord // s.expand()).gcd(p) == 1) # Special treatement for ℤ/2^x elif l == 2: return NotImplementedError else: return False
def accept_noglue(p, n, r, l, e, s): ''' This function is passed down to `sieve`. It accepts only if: (1) the order of p in ℤ/l^e is a multiple of s; (3') λ(l^e) / s is coprime to s (λ is the Carmichael function). These two conditions imply the conditions (1)-(3) above assuming l is prime. No gluing can be performed on accepted values if condition (2) above has to be preserved. ''' # Generic case if l != 2 and p != l: m = l**e ord = (l - 1) * l**(e - 1) return ((ord // s.expand()).gcd(s.expand()) == 1 and # (3') all(Zmod(m)(p)**(ord // ell) != 1 for (ell, _) in s)) # (1) # Special treatement for ℤ/2^x elif l == 2: return ((e == 2 and p % 4 == 3) or (p != 2 and e - s[0][1] == 2 and # (3') Zmod(2**e)(p)**(s.expand() // 2))) # (1) else: return False
def image_mod_n(self): r""" Return the image of this group in `SL(2, \ZZ / N\ZZ)`. EXAMPLE:: sage: Gamma0(3).image_mod_n() Matrix group over Ring of integers modulo 3 with 2 generators: [[[2, 0], [0, 2]], [[1, 1], [0, 1]]] TEST:: sage: for n in [2..20]: ... for g in Gamma0(n).gamma_h_subgroups(): ... G = g.image_mod_n() ... assert G.order() == Gamma(n).index() / g.index() """ N = self.level() if N == 1: raise NotImplementedError, "Matrix groups over ring of integers modulo 1 not implemented" gens = [ matrix(Zmod(N), 2, 2, [x, 0, 0, Zmod(N)(1) / x]) for x in self._generators_for_H() ] gens += [matrix(Zmod(N), 2, [1, 1, 0, 1])] return MatrixGroup(gens)
def _make_integral_poly(exact_modulus, p, prec): """ Convert a defining polynomial into one with integral coefficients. INPUT: - ``exact_modulus`` -- a univariate polynomial - ``p`` -- a prime - ``prec`` -- the precision EXAMPLES:: sage: from sage.rings.padics.padic_extension_leaves import _make_integral_poly sage: R.<x> = QQ[] sage: f = _make_integral_poly(x^2 - 2, 5, 3); f x^2 - 2 sage: f.parent() Univariate Polynomial Ring in x over Integer Ring sage: f = _make_integral_poly(x^2 - 2/7, 5, 3); f x^2 + 89 sage: f.parent() Univariate Polynomial Ring in x over Integer Ring """ try: return exact_modulus.change_ring(ZZ) except TypeError: return exact_modulus.change_ring(Zmod(p**prec)).change_ring(ZZ)
def _make_integral_poly(prepoly, p, prec): """ Converts a defining polynomial into one with integral coefficients. INPUTS: - ``prepoly`` - a univariate polynomial or symbolic expression - ``p`` -- a prime - ``prec`` -- the precision EXAMPLES:: sage: from sage.rings.padics.padic_extension_leaves import _make_integral_poly sage: R.<x> = QQ[] sage: f = _make_integral_poly(x^2 - 2, 5, 3); f x^2 - 2 sage: f.parent() Univariate Polynomial Ring in x over Integer Ring sage: f = _make_integral_poly(x^2 - 2/7, 5, 3); f x^2 + 89 sage: f.parent() Univariate Polynomial Ring in x over Integer Ring """ try: Zpoly = prepoly.change_ring(ZZ) except AttributeError: # should be a symoblic expression Zpoly = prepoly.polynomial(QQ) except (TypeError, ValueError): Zpoly = prepoly.change_ring(QQ) if Zpoly.base_ring() is not ZZ: Zpoly = Zpoly.change_ring(Zmod(p**prec)).change_ring(ZZ) return Zpoly
def blift(LF, Li, p, S=None): r""" Search for a solution to the given list of inequalities. If found, lift the solution to an appropriate valuation. See Lemma 3.3.6 in [Molnar]_ INPUT: - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) - ``Li`` -- an integer, the bound on coefficients - ``p`` -- a prime OUTPUT: - boolean -- whether or not the lift is successful - integer -- the lift EXAMPLES:: sage: R.<b> = PolynomialRing(QQ) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import blift sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3) (True, 4) """ P = LF[0].parent() #Determine which inequalities are trivial, and scale the rest, so that we only lift #as many times as needed. keepScaledIneqs = [scale(P(coeff), Li, p) for coeff in LF if coeff != 0] keptVals = [i[2] for i in keepScaledIneqs if i[0]] if keptVals != []: #Determine the valuation to lift until. liftval = max(keptVals) else: #All inequalities are satisfied. return True, 1 if S is None: S = PolynomialRing(Zmod(p), 'b') keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]] #We need a solution for each polynomial on the left hand side of the inequalities, #so we need only find a solution for their gcd. g = gcd(keptScaledIneqs) rts = g.roots(multiplicities=False) for r in rts: #Recursively try to lift each root r_initial = QQ(r) newInput = P([r_initial, p]) LG = [F(newInput) for F in LF] lift, lifted = blift(LG, Li, p, S=S) if lift: #Lift successful. return True, r_initial + p * lifted #Lift non successful. return False, 0
def cyclotomic_cosets(q, n, t=None): r""" This method is deprecated. See the documentation in :func:`~sage.categories.rings.Rings.Finite.ParentMethods.cyclotomic_cosets`. INPUT: q,n,t positive integers (or t=None) Some type-checking of inputs is performed. OUTPUT: q-cyclotomic cosets mod n (or, if t is not None, the q-cyclotomic coset mod n containing t) EXAMPLES:: sage: cyclotomic_cosets(2,11) doctest:...: DeprecationWarning: cyclotomic_cosets(q,n,t) is deprecated. Use Zmod(n).cyclotomic_cosets(q) or Zmod(n).cyclotomic_cosets(q,[t]) instead. Be careful that this method returns elements of Zmod(n). See http://trac.sagemath.org/16464 for details. [[0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] sage: Zmod(11).cyclotomic_cosets(2) [[0], [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]] sage: cyclotomic_cosets(5,11) [[0], [1, 3, 4, 5, 9], [2, 6, 7, 8, 10]] sage: cyclotomic_cosets(5,11,3) [1, 3, 4, 5, 9] """ from sage.misc.superseded import deprecation deprecation( 16464, """cyclotomic_cosets(q,n,t) is deprecated. Use Zmod(n).cyclotomic_cosets(q) or Zmod(n).cyclotomic_cosets(q,[t]) instead. Be careful that this method returns elements of Zmod(n).""") from sage.rings.finite_rings.integer_mod_ring import Zmod if t is None: return [[x.lift() for x in cos] for cos in Zmod(n).cyclotomic_cosets(q)] else: return [x.lift() for x in Zmod(n).cyclotomic_cosets(q, [t])[0]]
def residue_ring(self, n): """ Returns the quotient of the ring of integers by the nth power of the maximal ideal. EXAMPLES:: sage: R = Zp(11) sage: R.residue_ring(3) Ring of integers modulo 1331 """ from sage.rings.finite_rings.integer_mod_ring import Zmod return Zmod(self.prime()**n)
def _GammaH_coset_helper(N, H): r""" Return a list of coset representatives for H in (Z / NZ)^*. EXAMPLE:: sage: from sage.modular.arithgroup.congroup_gammaH import _GammaH_coset_helper sage: _GammaH_coset_helper(108, [1, 107]) [1, 5, 7, 11, 13, 17, 19, 23, 25, 29, 31, 35, 37, 41, 43, 47, 49, 53] """ t = [Zmod(N)(1)] W = [Zmod(N)(h) for h in H] HH = [Zmod(N)(h) for h in H] k = euler_phi(N) for i in xrange(1, N): if gcd(i, N) != 1: continue if not i in W: t.append(t[0] * i) W = W + [i * h for h in HH] if len(W) == k: break return t
def linear_relation_new(self, List, Psi): # ~40% increase in speed for Npk = 2, 11, 0 sign = 0 for Phi in List: assert Phi.valuation() >= 0 assert Psi.valuation() >= 0 R = self.base() d = len(List) if d == 0: if Psi.is_zero(): return [None, R(1)] else: return [None, 0] M = Psi.precision_absolute() p = self.prime() V = R**d pM = p**M A = Matrix(Zmod(pM), [Phi.list_of_total_measures() for Phi in List]) b = vector(Zmod(pM), Psi.list_of_total_measures()) try: x = A.solve_left(b) return [V(x), R(-1)] except: return [V(0), 0]
def intmod_gap_to_sage(x): r""" INPUT: - x -- Gap integer mod ring element EXAMPLES:: sage: a = gap(Mod(3, 18)); a ZmodnZObj( 3, 18 ) sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b 3 sage: b.parent() Ring of integers modulo 18 sage: a = gap(Mod(3, 17)); a Z(17) sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b 3 sage: b.parent() Ring of integers modulo 17 sage: a = gap(Mod(0, 17)); a 0*Z(17) sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b 0 sage: b.parent() Ring of integers modulo 17 sage: a = gap(Mod(3, 65537)); a ZmodpZObj( 3, 65537 ) sage: b = sage.interfaces.gap.intmod_gap_to_sage(a); b 3 sage: b.parent() Ring of integers modulo 65537 """ from sage.rings.finite_rings.integer_mod import Mod from sage.rings.finite_rings.integer_mod_ring import Zmod s = str(x) m = re.search(r'Z\(([0-9]*)\)', s) if m: return gfq_gap_to_sage(x, Zmod(m.group(1))) m = re.match(r'Zmod[np]ZObj\( ([0-9]*), ([0-9]*) \)', s) if m: return Mod(m.group(1), m.group(2)) raise ValueError, "Unable to convert Gap element '%s'" % s
def list(self): """ Return a list of all the elements of self (for which the domain is a cyclotomic field). EXAMPLES:: sage: K.<z> = CyclotomicField(12) sage: G = End(K); G Automorphism group of Cyclotomic Field of order 12 and degree 4 sage: [g(z) for g in G] [z, z^3 - z, -z, -z^3 + z] sage: L.<a, b> = NumberField([x^2 + x + 1, x^4 + 1]) sage: L Number Field in a with defining polynomial x^2 + x + 1 over its base field sage: Hom(CyclotomicField(12), L)[3] Ring morphism: From: Cyclotomic Field of order 12 and degree 4 To: Number Field in a with defining polynomial x^2 + x + 1 over its base field Defn: zeta12 |--> -b^2*a sage: list(Hom(CyclotomicField(5), K)) [] sage: Hom(CyclotomicField(11), L).list() [] """ try: return self.__list except AttributeError: pass D = self.domain() C = self.codomain() z = D.gen() n = z.multiplicative_order() if not n.divides(C.zeta_order()): v =[] else: if D == C: w = z else: w = C.zeta(n) v = [self([w**k], check=False) for k in Zmod(n) if k.is_unit()] v = Sequence(v, universe=self, check=False, immutable=True, cr=v!=[]) self.__list = v return v
def __mod__(self, other): """ TESTS:: sage: R.<a,b,c> = PowerSeriesRing(ZZ) sage: f = -a^3*b*c^2 + a^2*b^2*c^4 - 12*a^3*b^3*c^3 + R.O(10) sage: g = f % 2; g a^3*b*c^2 + a^2*b^2*c^4 + O(a, b, c)^10 sage: g in R False sage: g in R.base_extend(Zmod(2)) True sage: g.polynomial() == f.polynomial() % 2 True """ if isinstance(other, (int, Integer, long)): return self.change_ring(Zmod(other)) raise NotImplementedError( "Mod on multivariate power series ring elements not defined except modulo an integer." )
def __init__(self, n, R, depth=None, basis=None): Module.__init__(self, base=R) if basis is not None: self._basis = copy(basis) self._n = n self._R = R if R.is_exact(): self._Rmod = self._R else: self._Rmod = Zmod(self._R.prime()**(self._R.precision_cap())) if depth is None: depth = n + 1 if depth != n + 1: if R.is_exact(): raise ValueError, "Trying to construct an over-convergent module with exact coefficients, how do you store p-adics ??" self._depth = depth self._PowerSeries = PowerSeriesRing(self._Rmod, default_prec=self._depth, name='z') self._powers = dict() self._populate_coercion_lists_()
def __init__(self, index=20, index_max=50, odd_probability=0.5): r""" Create an arithmetic subgroup testing object. INPUT: - ``index`` - the index of random subgroup to test - ``index_max`` - the maximum index for congruence subgroup to test EXAMPLES:: sage: from sage.modular.arithgroup.tests import Test sage: Test() Arithmetic subgroup testing class """ self.congroups = [] i = 1 self.odd_probability = odd_probability if index % 4: self.odd_probability = 0 while Gamma(i).index() < index_max: self.congroups.append(Gamma(i)) i += 1 i = 1 while Gamma0(i).index() < index_max: self.congroups.append(Gamma0(i)) i += 1 i = 2 while Gamma1(i).index() < index_max: self.congroups.append(Gamma1(i)) M = Zmod(i) U = [x for x in M if x.is_unit()] for j in range(1, len(U) - 1): self.congroups.append(GammaH(i, prandom.sample(U, j))) i += 1 self.index = index
def RandomBicubicPlanar(n): """ Return the graph of a random bipartite cubic map with `3 n` edges. INPUT: `n` -- an integer (at least `1`) OUTPUT: a graph with multiple edges (no embedding is provided) The algorithm used is described in [Schaeffer99]_. This samples a random rooted bipartite cubic map, chosen uniformly at random. First one creates a random binary tree with `n` vertices. Next one turns this into a blossoming tree (at random) and reads the contour word of this blossoming tree. Then one performs a rotation on this word so that this becomes a balanced word. There are three ways to do that, one is picked at random. Then a graph is build from the balanced word by iterated closure (adding edges). In the returned graph, the three edges incident to any given vertex are colored by the integers 0, 1 and 2. .. SEEALSO:: the auxiliary method :func:`blossoming_contour` EXAMPLES:: sage: n = randint(200, 300) sage: G = graphs.RandomBicubicPlanar(n) sage: G.order() == 2*n True sage: G.size() == 3*n True sage: G.is_bipartite() and G.is_planar() and G.is_regular(3) True sage: dic = {'red':[v for v in G.vertices() if v[0] == 'n'], ....: 'blue': [v for v in G.vertices() if v[0] != 'n']} sage: G.plot(vertex_labels=False,vertex_size=20,vertex_colors=dic) Graphics object consisting of ... graphics primitives .. PLOT:: :width: 300 px G = graphs.RandomBicubicPlanar(200) V0 = [v for v in G.vertices() if v[0] == 'n'] V1 = [v for v in G.vertices() if v[0] != 'n'] dic = {'red': V0, 'blue': V1} sphinx_plot(G.plot(vertex_labels=False,vertex_colors=dic)) REFERENCES: .. [Schaeffer99] Gilles Schaeffer, *Random Sampling of Large Planar Maps and Convex Polyhedra*, Annual ACM Symposium on Theory of Computing (Atlanta, GA, 1999) """ from sage.combinat.binary_tree import BinaryTrees from sage.rings.finite_rings.integer_mod_ring import Zmod if not n: raise ValueError("n must be at least 1") # first pick a random binary tree t = BinaryTrees(n).random_element() # next pick a random blossoming of this tree, compute its contour contour = blossoming_contour(t) + [('xb',)] # adding the final xb # first step : rotate the contour word to one of 3 balanced N = len(contour) double_contour = contour + contour pile = [] not_touched = [i for i in range(N) if contour[i][0] in ['x', 'xb']] for i, w in enumerate(double_contour): if w[0] == 'x' and i < N: pile.append(i) elif w[0] == 'xb' and (i % N) in not_touched: if pile: j = pile.pop() not_touched.remove(i % N) not_touched.remove(j) # random choice among 3 possibilities for a balanced word idx = not_touched[randint(0, 2)] w = contour[idx + 1:] + contour[:idx + 1] # second step : create the graph by closure from the balanced word G = Graph(multiedges=True) pile = [] Z3 = Zmod(3) colour = Z3.zero() not_touched = [i for i, v in enumerate(w) if v[0] in ['x', 'xb']] for i, v in enumerate(w): # internal edges if v[0] == 'i': colour += 1 if w[i + 1][0] == 'n': G.add_edge((w[i], w[i + 1], colour)) elif v[0] == 'n': colour += 2 elif v[0] == 'x': pile.append(i) elif v[0] == 'xb' and i in not_touched: if pile: j = pile.pop() G.add_edge((w[i + 1], w[j - 1], colour)) not_touched.remove(i) not_touched.remove(j) # there remains to add three edges to elements of "not_touched" # from a new vertex labelled "n" for i in not_touched: taken_colours = [edge[2] for edge in G.edges_incident(w[i - 1])] colour = [u for u in Z3 if u not in taken_colours][0] G.add_edge((('n', -1), w[i - 1], colour)) return G
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 singer_difference_set(q, d): r""" Return a difference set associated to the set of hyperplanes in a projective space of dimension `d` over `GF(q)`. A Singer difference set has parameters: .. MATH:: v = \frac{q^{d+1}-1}{q-1}, \quad k = \frac{q^d-1}{q-1}, \quad \lambda = \frac{q^{d-1}-1}{q-1}. The idea of the construction is as follows. One consider the finite field `GF(q^{d+1})` as a vector space of dimension `d+1` over `GF(q)`. The set of `GF(q)`-lines in `GF(q^{d+1})` is a projective plane and its set of hyperplanes form a balanced incomplete block design. Now, considering a multiplicative generator `z` of `GF(q^{d+1})`, we get a transitive action of a cyclic group on our projective plane from which it is possible to build a difference set. The construction is given in details in [Stinson2004]_, section 3.3. EXAMPLES:: sage: from sage.combinat.designs.difference_family import singer_difference_set, is_difference_family sage: G,D = singer_difference_set(3,2) sage: is_difference_family(G,D,verbose=True) It is a (13,4,1)-difference family True sage: G,D = singer_difference_set(4,2) sage: is_difference_family(G,D,verbose=True) It is a (21,5,1)-difference family True sage: G,D = singer_difference_set(3,3) sage: is_difference_family(G,D,verbose=True) It is a (40,13,4)-difference family True sage: G,D = singer_difference_set(9,3) sage: is_difference_family(G,D,verbose=True) It is a (820,91,10)-difference family True """ q = Integer(q) assert q.is_prime_power() assert d >= 2 from sage.rings.finite_rings.constructor import GF from sage.rings.finite_rings.conway_polynomials import conway_polynomial from sage.rings.finite_rings.integer_mod_ring import Zmod # build a polynomial c over GF(q) such that GF(q)[x] / (c(x)) is a # GF(q**(d+1)) and such that x is a multiplicative generator. p, e = q.factor()[0] c = conway_polynomial(p, e * (d + 1)) if e != 1: # i.e. q is not a prime, so we factorize c over GF(q) and pick # one of its factor K = GF(q, 'z') c = c.change_ring(K).factor()[0][0] else: K = GF(q) z = c.parent().gen() # Now we consider the GF(q)-subspace V spanned by (1,z,z^2,...,z^(d-1)) inside # GF(q^(d+1)). The multiplication by z is an automorphism of the # GF(q)-projective space built from GF(q^(d+1)). The difference family is # obtained by taking the integers i such that z^i belong to V. powers = [0] i = 1 x = z k = (q**d - 1) // (q - 1) while len(powers) < k: if x.degree() <= (d - 1): powers.append(i) x = (x * z).mod(c) i += 1 return Zmod((q**(d + 1) - 1) // (q - 1)), [powers]
def difference_family(v, k, l=1, existence=False, explain_construction=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. INPUT: - ``v,k,l`` -- parameters of the difference family. If ``l`` is not provided it is assumed to be ``1``. - ``existence`` -- if ``True``, then return either ``True`` if Sage knows how to build such design, ``Unknown`` if it does not and ``False`` if it knows that the design does not exist. - ``explain_construction`` -- instead of returning a difference family, returns a string that explains the construction used. - ``check`` -- boolean (default: ``True``). If ``True`` then the result of the computation is checked before being returned. This should not be needed but ensures that the output is correct. OUTPUT: A pair ``(G,D)`` made of a group `G` and a difference family `D` on that group. Or, if ``existence`` is ``True`` a troolean or if ``explain_construction`` is ``True`` a string. EXAMPLES:: sage: G,D = designs.difference_family(73,4) sage: G Finite Field of size 73 sage: D [[0, 1, 5, 18], [0, 3, 15, 54], [0, 9, 45, 16], [0, 27, 62, 48], [0, 8, 40, 71], [0, 24, 47, 67]] sage: print designs.difference_family(73, 4, explain_construction=True) The database contains a (73,4)-evenly distributed set sage: G,D = designs.difference_family(15,7,3) sage: G The cartesian product of (Finite Field of size 3, Finite Field of size 5) sage: D [[(1, 1), (1, 4), (2, 2), (2, 3), (0, 0), (1, 0), (2, 0)]] sage: print designs.difference_family(15,7,3,explain_construction=True) Twin prime powers difference family sage: print designs.difference_family(91,10,1,explain_construction=True) Singer difference set 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] [169, 337, 379, 421, 463, 547, 631, 673, 757, 841, 883, 967, ..., 4621, 4957, 5167] sage: l7[Unknown] [43, 127, 211, 2017, 2143, 2269, 2311, 2437, 2521, 2647, ..., 4999, 5041, 5209] sage: l7[False] [] List available constructions:: sage: for v in xrange(2,100): ....: constructions = [] ....: for k in xrange(2,10): ....: for l in xrange(1,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)) 3: (2,1) 4: (3,2) 5: (2,1), (4,3) 6: (5,4) 7: (2,1), (3,1), (3,2), (4,2), (6,5) 8: (7,6) 9: (2,1), (4,3), (8,7) 10: (9,8) 11: (2,1), (4,6), (5,2), (5,4), (6,3) 13: (2,1), (3,1), (3,2), (4,1), (4,3), (5,5), (6,5) 15: (3,1), (4,6), (5,6), (7,3) 16: (3,2), (5,4), (6,2) 17: (2,1), (4,3), (5,5), (8,7) 19: (2,1), (3,1), (3,2), (4,2), (6,5), (9,4), (9,8) 21: (3,1), (4,3), (5,1), (6,3), (6,5) 22: (4,2), (6,5), (7,4), (8,8) 23: (2,1) 25: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (7,7), (8,7) 27: (2,1), (3,1) 28: (3,2), (6,5) 29: (2,1), (4,3), (7,3), (7,6), (8,4), (8,6) 31: (2,1), (3,1), (3,2), (4,2), (5,2), (5,4), (6,1), (6,5) 33: (3,1), (5,5), (6,5) 34: (4,2) 35: (5,2) 37: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (9,2), (9,8) 39: (3,1), (6,5) 40: (3,2), (4,1) 41: (2,1), (4,3), (5,1), (5,4), (6,3), (8,7) 43: (2,1), (3,1), (3,2), (4,2), (6,5), (7,2), (7,3), (7,6), (8,4) 45: (3,1), (5,1) 46: (4,2), (6,2) 47: (2,1) 49: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3) 51: (3,1), (5,2), (6,3) 52: (4,1) 53: (2,1), (4,3) 55: (3,1), (9,4) 57: (3,1), (7,3), (8,1) 59: (2,1) 61: (2,1), (3,1), (3,2), (4,1), (4,3), (5,1), (5,4), (6,2), (6,3), (6,5) 63: (3,1) 64: (3,2), (4,1), (7,2), (7,6), (9,8) 65: (5,1) 67: (2,1), (3,1), (3,2), (6,5) 69: (3,1) 71: (2,1), (5,2), (5,4), (7,3), (7,6), (8,4) 73: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,1), (9,8) 75: (3,1), (5,2) 76: (4,1) 79: (2,1), (3,1), (3,2), (6,5) 81: (2,1), (3,1), (4,3), (5,1), (5,4), (8,7) 83: (2,1) 85: (4,1), (7,2), (7,3), (8,2) 89: (2,1), (4,3), (8,7) 91: (6,1), (7,1) 97: (2,1), (3,1), (3,2), (4,1), (4,3), (6,5), (8,7), (9,3) 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,EDS sage: for v,k,l in DF: ....: assert designs.difference_family(v,k,l,existence=True) is True ....: df = designs.difference_family(v,k,l,check=True) sage: for k in EDS: ....: for v in EDS[k]: ....: assert designs.difference_family(v,k,1,existence=True) is True ....: df = designs.difference_family(v,k,1,check=True) Check a failing construction (:trac:`17528`):: sage: designs.difference_family(9,3) Traceback (most recent call last): ... NotImplementedError: No construction available for (9,3,1)-difference family .. 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, EDS if (v,k,l) in DF: if existence: return True elif explain_construction: return "The database contains a ({},{},{})-difference family".format(v,k,l) vv, blocks = next(DF[v,k,l].iteritems()) # 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 and not is_difference_family(G, df, v=v, k=k, l=l): raise RuntimeError("There is an invalid ({},{},{})-difference " "family in the database... Please contact " "*****@*****.**".format(v,k,l)) return G,df elif l == 1 and k in EDS and v in EDS[k]: if existence: return True elif explain_construction: return "The database contains a ({},{})-evenly distributed set".format(v,k) from sage.rings.finite_rings.constructor import GF poly,B = EDS[k][v] if poly is None: # q is prime K = G = GF(v) else: K = G = GF(v,'a',modulus=poly) B = map(K,B) e = k*(k-1)/2 xe = G.multiplicative_generator()**e df = [[xe**j*b for b in B] for j in range((v-1)/(2*e))] if check and not is_difference_family(G, df, v=v, k=k, l=l): raise RuntimeError("There is an invalid ({},{})-evenly distributed " "set in the database... Please contact " "*****@*****.**".format(v,k,l)) return G,df e = k*(k-1) if (l*(v-1)) % e: if existence: return Unknown raise NotImplementedError("No construction available for ({},{},{})-difference family".format(v,k,l)) t = l*(v-1) // e # number of blocks # trivial construction if k == (v-1) and l == (v-2): from sage.rings.finite_rings.integer_mod_ring import Zmod G = Zmod(v) return G, [range(1,v)] factorization = arith.factor(v) D = None if len(factorization) == 1: # i.e. is v a prime power from sage.rings.finite_rings.constructor import GF G = K = GF(v,'z') if radical_difference_family(K, k, l, existence=True): if existence: return True elif explain_construction: return "Radical difference family on a finite field" else: D = radical_difference_family(K,k,l) elif l == 1 and k == 6 and df_q_6_1(K,existence=True): if existence: return True elif explain_construction: return "Wilson 1972 difference family made from the union of two cyclotomic cosets" else: D = df_q_6_1(K) # Twin prime powers construction # i.e. v = p(p+2) where p and p+2 are prime powers # k = (v-1)/2 # lambda = (k-1)/2 (ie 2l+1 = k) elif (k == (v-1)//2 and l == (k-1)//2 and len(factorization) == 2 and abs(pow(*factorization[0]) - pow(*factorization[1])) == 2): if existence: return True elif explain_construction: return "Twin prime powers difference family" else: p = pow(*factorization[0]) q = pow(*factorization[1]) if p > q: p,q = q,p G,D = twin_prime_powers_difference_set(p,check=False) 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 elif explain_construction: return "Singer difference set" 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,v=v,k=k,l=l,verbose=False): raise RuntimeError("There is a problem. Sage built the following " "difference family on G='{}' with parameters ({},{},{}):\n " "{}\nwhich seems to not be a difference family... " "Please contact [email protected]".format(G,v,k,l,D)) return G, D
def RandomBicubicPlanar(n): """ Return the graph of a random bipartite cubic map with `3 n` edges. INPUT: `n` -- an integer (at least `1`) OUTPUT: a graph with multiple edges (no embedding is provided) The algorithm used is described in [Schaeffer99]_. This samples a random rooted bipartite cubic map, chosen uniformly at random. First one creates a random binary tree with `n` vertices. Next one turns this into a blossoming tree (at random) and reads the contour word of this blossoming tree. Then one performs a rotation on this word so that this becomes a balanced word. There are three ways to do that, one is picked at random. Then a graph is build from the balanced word by iterated closure (adding edges). In the returned graph, the three edges incident to any given vertex are colored by the integers 0, 1 and 2. .. SEEALSO:: the auxiliary method :func:`blossoming_contour` EXAMPLES:: sage: n = randint(200, 300) sage: G = graphs.RandomBicubicPlanar(n) sage: G.order() == 2*n True sage: G.size() == 3*n True sage: G.is_bipartite() and G.is_planar() and G.is_regular(3) True sage: dic = {'red':[v for v in G.vertices() if v[0] == 'n'], ....: 'blue': [v for v in G.vertices() if v[0] != 'n']} sage: G.plot(vertex_labels=False,vertex_size=20,vertex_colors=dic) Graphics object consisting of ... graphics primitives .. PLOT:: :width: 300 px G = graphs.RandomBicubicPlanar(200) V0 = [v for v in G.vertices() if v[0] == 'n'] V1 = [v for v in G.vertices() if v[0] != 'n'] dic = {'red': V0, 'blue': V1} sphinx_plot(G.plot(vertex_labels=False,vertex_colors=dic)) REFERENCES: .. [Schaeffer99] Gilles Schaeffer, *Random Sampling of Large Planar Maps and Convex Polyhedra*, Annual ACM Symposium on Theory of Computing (Atlanta, GA, 1999) """ from sage.combinat.binary_tree import BinaryTrees from sage.rings.finite_rings.integer_mod_ring import Zmod if not n: raise ValueError("n must be at least 1") # first pick a random binary tree t = BinaryTrees(n).random_element() # next pick a random blossoming of this tree, compute its contour contour = blossoming_contour(t) + [('xb', )] # adding the final xb # first step : rotate the contour word to one of 3 balanced N = len(contour) double_contour = contour + contour pile = [] not_touched = [i for i in range(N) if contour[i][0] in ['x', 'xb']] for i, w in enumerate(double_contour): if w[0] == 'x' and i < N: pile.append(i) elif w[0] == 'xb' and (i % N) in not_touched: if pile: j = pile.pop() not_touched.remove(i % N) not_touched.remove(j) # random choice among 3 possibilities for a balanced word idx = not_touched[randint(0, 2)] w = contour[idx + 1:] + contour[:idx + 1] # second step : create the graph by closure from the balanced word G = Graph(multiedges=True) pile = [] Z3 = Zmod(3) colour = Z3.zero() not_touched = [i for i, v in enumerate(w) if v[0] in ['x', 'xb']] for i, v in enumerate(w): # internal edges if v[0] == 'i': colour += 1 if w[i + 1][0] == 'n': G.add_edge((w[i], w[i + 1], colour)) elif v[0] == 'n': colour += 2 elif v[0] == 'x': pile.append(i) elif v[0] == 'xb' and i in not_touched: if pile: j = pile.pop() G.add_edge((w[i + 1], w[j - 1], colour)) not_touched.remove(i) not_touched.remove(j) # there remains to add three edges to elements of "not_touched" # from a new vertex labelled "n" for i in not_touched: taken_colours = [edge[2] for edge in G.edges_incident(w[i - 1])] colour = [u for u in Z3 if u not in taken_colours][0] G.add_edge((('n', -1), w[i - 1], colour)) return G
def find_root_order(p, n, r=0, accept=accept, verbose=True): ''' Assuming that r divides n and p is prime, search for a small integer m such that: 1. the order of <p> ⊂ ℤ/m* is equal to r⋅o; 2. gcd(n, o) = 1; 3. ℤ/m* = <p^o> × G for some G ⊂ ℤ/m*; then return o and a set of generators for G, together with their respective orders. The first condition implies that the m-th roots of unity generate a superfield of F_{p^r}. The second condition is not strictly necessary, but it makes the algorithm much more efficient by ensuring that the superfield is generated over F_{p^n} by a polynomial with coefficients in F_p. When r strictly divides n, the weaker condition: 2'. gcd(n, r⋅o / gcd(n, r⋅o)) = 1; can be used to work in a smaller superfield. The third condition is needed so that the construction of `find_unique_orbit` applies. In his paper, Rains' gives a sufficient condition for (3), namely that 3'. gcd(r, φ(m)/r) = 1. It is easy to see that this condition is also necessary when ℤ/m* is cyclic, but there are easy counterexamples when it is not (e.g., take p=233, r=6, m=21). The integer m and the decomposition of ℤ/m are computed by the function `sieve` below, with acceptance criterion given by (1), (2) and (3'). To conclude: bounds on m, hard to tell. When r is prime, m must be prime, and under GRH the best bound is m ∈ O(r^{2.4 + ε}) [1]. Heuristically m ∈ O(r log r). Pinch [2] and Rains give some tabulations. Note: this algorithm loops forever if p=2 and 8|r. Rains proposes two fixes in his paper, which we haven't implemented yet. [1]: D. R. Heath-Brown, Zero-free regions for Dirichlet L-functions, and the least prime in an arithmetic progression [2]: R. G. E. Pinch. Recognizing elements of finite fields. ''' # For each prime power dividing r, find the smallest multiplier k. m = sieve(p, n, r, accept) if verbose: print "Using roots of unity of order {}".format(m) # Construct ℤ/m* as the product of the factors ℤ/f* R = Zmod(prod(f for f, _, _ in m)) crt = map(R, crt_basis([f for f, _, _ in m])) G = [(sum(crt[:i]) + R(-1 if f % 2 == 0 and f != 4 else (Zmod(f).unit_gens()[0]**s)) * crt[i] + sum(crt[i + 1:]), t) for i, (f, s, t) in enumerate(m)] assert (all(g**e == 1 for (g, e) in G)) ord = R(p).multiplicative_order() assert ord % r == 0 return ord // r, G
def blift(LF, Li, p, k, S=None, all_orbits=False): r""" Search for a solution to the given list of inequalities. If found, lift the solution to an appropriate valuation. See Lemma 3.3.6 in [Molnar]_ INPUT: - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) - ``Li`` -- an integer, the bound on coefficients - ``p`` -- a prime - ``k`` -- the scaling factor that makes the solution a ``p``-adic integer - ``S`` -- polynomial ring to use - ``all_orbits`` -- boolean; whether or not to use ``==`` in the inequalities to find all orbits OUTPUT: - boolean -- whether or not the lift is successful - integer -- the lift EXAMPLES:: sage: R.<b> = PolynomialRing(QQ) sage: from sage.dynamics.arithmetic_dynamics.endPN_minimal_model import blift sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341,\ ....: -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3, 2) [[True, 4]] """ P = LF[0].parent() #Determine which inequalities are trivial, and scale the rest, so that we only lift #as many times as needed. keepScaledIneqs = [scale(P(coeff), Li, p) for coeff in LF if coeff != 0] keptVals = [i[2] for i in keepScaledIneqs if i[0]] if keptVals != []: #Determine the valuation to lift until. liftval = max(keptVals) else: #All inequalities are satisfied. if all_orbits: return [[True, t] for t in range(p)] return [[True, 1]] if S is None: S = PolynomialRing(Zmod(p), 'b') keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]] #We need a solution for each polynomial on the left hand side of the inequalities, #so we need only find a solution for their gcd. g = gcd(keptScaledIneqs) rts = g.roots(multiplicities=False) good = [] for r in rts: #Recursively try to lift each root r_initial = QQ(r) newInput = P([r_initial, p]) LG = [F(newInput) for F in LF] new_good = blift(LG, Li, p, k, S=S) for lift, lifted in new_good: if lift: #Lift successful. if not all_orbits: return [[True, r_initial + p * lifted]] #only need up to SL(2,ZZ) equivalence #this helps control the size of the resulting coefficients if r_initial + p * lifted < p**k: good.append([True, r_initial + p * lifted]) else: new_r = r_initial + p * lifted - p**k while new_r > p**k: new_r -= p**k if [True, new_r] not in good: good.append([True, new_r]) if good: return good #Lift non successful. return [[False, 0]]
def mcfarland_1973_construction(q, s): r""" Return a difference set. The difference set returned has the following parameters .. MATH:: v = \frac{q^{s+1}(q^{s+1}+q-2)}{q-1}, k = \frac{q^s (q^{s+1}-1)}{q-1}, \lambda = \frac{q^s(q^s-1)}{q-1} This construction is due to [McF1973]_. INPUT: - ``q``, ``s`` - (integers) parameters for the difference set (see the above formulas for the expression of ``v``, ``k``, ``l`` in terms of ``q`` and ``s``) .. SEEALSO:: The function :func:`are_mcfarland_1973_parameters` makes the translation between the parameters `(q,s)` corresponding to a given triple `(v,k,\lambda)`. REFERENCES: .. [McF1973] Robert L. McFarland "A family of difference sets in non-cyclic groups" Journal of Combinatorial Theory (A) vol 15 (1973). http://dx.doi.org/10.1016/0097-3165(73)90031-9 EXAMPLES:: sage: from sage.combinat.designs.difference_family import ( ....: mcfarland_1973_construction, is_difference_family) sage: G,D = mcfarland_1973_construction(3, 1) sage: assert is_difference_family(G, D, 45, 12, 3) sage: G,D = mcfarland_1973_construction(2, 2) sage: assert is_difference_family(G, D, 64, 28, 12) """ from sage.rings.finite_rings.finite_field_constructor import GF from sage.modules.free_module import VectorSpace from sage.rings.finite_rings.integer_mod_ring import Zmod from sage.categories.cartesian_product import cartesian_product from itertools import izip r = (q**(s + 1) - 1) // (q - 1) F = GF(q, 'a') V = VectorSpace(F, s + 1) K = Zmod(r + 1) G = cartesian_product([F] * (s + 1) + [K]) D = [] for k, H in izip(K, V.subspaces(s)): for v in H: D.append(G((tuple(v) + (k, )))) return G, [D]
def v_5_1_BIBD(v, check=True): r""" Returns a `(v,5,1)`-BIBD. This method follows the constuction from [ClaytonSmith]_. INPUT: - ``v`` (integer) .. SEEALSO:: * :meth:`BalancedIncompleteBlockDesign` EXAMPLES:: sage: from sage.combinat.designs.bibd import v_5_1_BIBD sage: i = 0 sage: while i<200: ....: i += 20 ....: _ = v_5_1_BIBD(i+1) ....: _ = v_5_1_BIBD(i+5) """ v = int(v) assert (v > 1) assert (v % 20 == 5 or v % 20 == 1 ) # note: equivalent to (v-1)%4 == 0 and (v*(v-1))%20 == 0 # Lemma 27 if v % 5 == 0 and (v // 5) % 4 == 1 and is_prime_power(v // 5): bibd = BIBD_5q_5_for_q_prime_power(v // 5) # Lemma 28 elif v == 21: from sage.rings.finite_rings.integer_mod_ring import Zmod bibd = BIBD_from_difference_family(Zmod(21), [[0, 1, 4, 14, 16]], check=False) elif v == 41: from sage.rings.finite_rings.integer_mod_ring import Zmod bibd = BIBD_from_difference_family( Zmod(41), [[0, 1, 4, 11, 29], [0, 2, 8, 17, 22]], check=False) elif v == 61: from sage.rings.finite_rings.integer_mod_ring import Zmod bibd = BIBD_from_difference_family( Zmod(61), [[0, 1, 3, 13, 34], [0, 4, 9, 23, 45], [0, 6, 17, 24, 32]], check=False) elif v == 81: from sage.groups.additive_abelian.additive_abelian_group import AdditiveAbelianGroup D = [[(0, 0, 0, 1), (2, 0, 0, 1), (0, 0, 2, 1), (1, 2, 0, 2), (0, 1, 1, 1)], [(0, 0, 1, 0), (1, 1, 0, 2), (0, 2, 1, 0), (1, 2, 0, 1), (1, 1, 1, 0)], [(2, 2, 1, 1), (1, 2, 2, 2), (2, 0, 1, 2), (0, 1, 2, 1), (1, 1, 0, 0)], [(0, 2, 0, 2), (1, 1, 0, 1), (1, 2, 1, 2), (1, 2, 1, 0), (0, 2, 1, 1)]] bibd = BIBD_from_difference_family(AdditiveAbelianGroup([3] * 4), D, check=False) elif v == 161: # VI.16.16 of the Handbook of Combinatorial Designs, Second Edition D = [(0, 19, 34, 73, 80), (0, 16, 44, 71, 79), (0, 12, 33, 74, 78), (0, 13, 30, 72, 77), (0, 11, 36, 67, 76), (0, 18, 32, 69, 75), (0, 10, 48, 68, 70), (0, 3, 29, 52, 53)] from sage.rings.finite_rings.integer_mod_ring import Zmod bibd = BIBD_from_difference_family(Zmod(161), D, check=False) elif v == 281: from sage.rings.finite_rings.integer_mod_ring import Zmod D = [[3**(2 * a + 56 * b) for b in range(5)] for a in range(14)] bibd = BIBD_from_difference_family(Zmod(281), D, check=False) # Lemma 29 elif v == 165: bibd = BIBD_from_PBD(v_5_1_BIBD(41, check=False), 165, 5, check=False) elif v == 181: bibd = BIBD_from_PBD(v_5_1_BIBD(45, check=False), 181, 5, check=False) elif v in (201, 285, 301, 401, 421, 425): # Call directly the BIBD_from_TD function bibd = BIBD_from_TD(v, 5) # Lemma 30 elif v == 141: # VI.16.16 of the Handbook of Combinatorial Designs, Second Edition from sage.rings.finite_rings.integer_mod_ring import Zmod D = [(0, 33, 60, 92, 97), (0, 3, 45, 88, 110), (0, 18, 39, 68, 139), (0, 12, 67, 75, 113), (0, 1, 15, 84, 94), (0, 7, 11, 24, 30), (0, 36, 90, 116, 125)] bibd = BIBD_from_difference_family(Zmod(141), D, check=False) # Theorem 31.2 elif (v - 1) // 4 in [ 80, 81, 85, 86, 90, 91, 95, 96, 110, 111, 115, 116, 120, 121, 250, 251, 255, 256, 260, 261, 265, 266, 270, 271 ]: r = (v - 1) // 4 if r <= 96: k, t, u = 5, 16, r - 80 elif r <= 121: k, t, u = 10, 11, r - 110 else: k, t, u = 10, 25, r - 250 bibd = BIBD_from_PBD(PBD_from_TD(k, t, u), v, 5, check=False) else: r, s, t, u = _get_r_s_t_u(v) bibd = BIBD_from_PBD(PBD_from_TD(5, t, u), v, 5, check=False) if check: _check_pbd(bibd, v, [5]) return bibd