def submodule_generated_by_images(self, M): """ Return the submodule of this ambient modular symbols space generated by the images under all degeneracy maps of M. The space M must have the same weight, sign, and group or character as this ambient space. EXAMPLES:: sage: ModularSymbols(6, 12).submodule_generated_by_images(ModularSymbols(1,12)) Modular Symbols subspace of dimension 12 of Modular Symbols space of dimension 22 for Gamma_0(6) of weight 12 with sign 0 over Rational Field """ S = self.zero_submodule() if self.level() % M.level() == 0: D = arith.divisors(self.level() // M.level()) elif M.level() % self.level() == 0: D = arith.divisors(M.level() // self.level()) else: D = [] for t in D: d = M.degeneracy_map(self, t) if d.codomain() != self: raise ArithmeticError, "incompatible spaces of modular symbols" S += d.image() if self.is_full_hecke_module(compute=False): S._is_full_hecke_module = True return S
def find_product_decomposition(g, k, lmbda=1): r""" Try to find a product decomposition construction for difference matrices. INPUT: - ``g,k,lmbda`` -- integers, parameters of the difference matrix OUTPUT: A pair of pairs ``(g1,lmbda),(g2,lmbda2)`` if Sage knows how to build `(g1,k,lmbda1)` and `(g2,k,lmbda2)` difference matrices and ``False`` otherwise. EXAMPLES:: sage: from sage.combinat.designs.difference_matrices import find_product_decomposition sage: find_product_decomposition(77,6) ((7, 1), (11, 1)) sage: find_product_decomposition(616,7) ((7, 1), (88, 1)) sage: find_product_decomposition(24,10) False """ for lmbda1 in divisors(lmbda): lmbda2 = lmbda//lmbda1 # To avoid infinite loop: # if lmbda1 == lmbda, then g1 should not be g # if lmbda2 == lmbda, then g2 should not be g if lmbda1 == lmbda: if lmbda2 == lmbda: div = divisors(g)[1:-1] else: div = divisors(g)[:-1] else: if lmbda2 == lmbda: div = divisors(g)[1:] else: div = divisors(g) for g1 in div: g2 = g//g1 if g1 > g2: break if (difference_matrix(g1,k,lmbda1,existence=True) and difference_matrix(g2,k,lmbda2,existence=True)): return (g1,lmbda1),(g2,lmbda2) return False
def AllCusps(N): r""" Return a list of CuspFamily objects corresponding to the cusps of `X_0(N)`. INPUT: - ``N`` - (integer): the level EXAMPLES:: sage: AllCusps(18) [(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)] sage: AllCusps(0) Traceback (most recent call last): ... ValueError: N must be positive """ N = ZZ(N) if N <= 0: raise ValueError("N must be positive") c = [] for d in divisors(N): n = num_cusps_of_width(N, d) if n == 1: c.append(CuspFamily(N, d)) elif n > 1: for i in xrange(n): c.append(CuspFamily(N, d, label=str(i + 1))) return c
def number_of_Gamma0_NFCusps(N): """ Returns the total number of orbits of cusps under the action of the congruence subgroup `\\Gamma_0(N)`. INPUT: - ``N`` -- a number field ideal. OUTPUT: ingeter -- the number of orbits of cusps under Gamma0(N)-action. EXAMPLES:: sage: k.<a> = NumberField(x^3 + 11) sage: N = k.ideal(2, a+1) sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: number_of_Gamma0_NFCusps(N) 4 sage: L = Gamma0_NFCusps(N) sage: len(L) == number_of_Gamma0_NFCusps(N) True """ k = N.number_field() # The number of Gamma0(N)-sub-orbits for each Gamma-orbit: from sage.rings.arith import divisors s = sum([len(list((d+N/d).invertible_residues_mod(k.unit_group().gens()))) \ for d in divisors(N)]) # There are h Gamma-orbits, with h class number of underlying number field. return s*k.class_number()
def _coset_reduction_data_second_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the second part of the reduction step (the second coordinate). INPUT: self OUTPUT: a dictionary v with keys the divisors of N such that v[d] is the subgroup {h in H : h = 1 (mod N/d)}. EXAMPLES:: sage: G = GammaH(240,[7,239]) sage: G._coset_reduction_data_second_coord() {1: [1], 2: [1], 3: [1], 4: [1], 5: [1, 49], 6: [1], 48: [1, 191], 8: [1], 80: [1, 7, 49, 103], 10: [1, 49], 12: [1], 15: [1, 49], 240: [1, 7, 49, 103, 137, 191, 233, 239], 40: [1, 7, 49, 103], 20: [1, 49], 24: [1, 191], 120: [1, 7, 49, 103, 137, 191, 233, 239], 60: [1, 49, 137, 233], 30: [1, 49, 137, 233], 16: [1]} sage: G = GammaH(1200,[-1,7]); G Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199] sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort() sage: K == divisors(1200) True """ H = G._list_of_elements_in_H() N = G.level() v = { 1: [1] , N: H } for d in [x for x in divisors(N) if x > 1 and x < N ]: N_over_d = N // d v[d] = [x for x in H if x % N_over_d == 1] return v
def AllCusps(N): r""" Return a list of CuspFamily objects corresponding to the cusps of `X_0(N)`. INPUT: - ``N`` - (integer): the level EXAMPLES:: sage: AllCusps(18) [(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)] """ try: N = ZZ(N) assert N>0 except TypeError: raise TypeError("N must be an integer") except AssertionError: raise AssertionError("N must be positive") c = [] for d in divisors(N): n = num_cusps_of_width(N, d) if n == 1: c.append(CuspFamily(N, d)) elif n > 1: for i in xrange(n): c.append(CuspFamily(N, d, label=str(i+1))) return c
def cardinality(self): """ Returns the number of integer necklaces with the evaluation e. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 Check to make sure that the count matches up with the number of Lyndon words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self.e le = list(evaluation) if len(le) == 0: return 0 n = sum(le) return sum([ euler_phi(j) * factorial(n / j) / prod([factorial(ni / j) for ni in evaluation]) for j in divisors(gcd(le)) ]) / n
def TD_find_product_decomposition(k,n): r""" Attempts to find a factorization of `n` in order to build a `TD(k,n)`. If Sage can build a `TD(k,n_1)` and a `TD(k,n_2)` such that `n=n_1\times n_2` then a `TD(k,n)` can be built (from the function :func:`transversal_design`). This method returns such a pair of integers if it exists, and ``None`` otherwise. INPUT: - ``k,n`` (integers) -- see above. .. SEEALSO:: :func:`TD_product` that actually build a product EXAMPLES:: sage: from sage.combinat.designs.orthogonal_arrays import TD_find_product_decomposition sage: TD_find_product_decomposition(6, 84) (7, 12) sage: TD1 = designs.transversal_design(6, 7) sage: TD2 = designs.transversal_design(6, 12) sage: from sage.combinat.designs.orthogonal_arrays import TD_product sage: TD = TD_product(6, TD1, 7, TD2, 12) """ from sage.rings.arith import divisors for n1 in divisors(n)[1:-1]: # we ignore 1 and n n2 = n//n1 if transversal_design(k, n1, existence = True) and transversal_design(k, n2, existence = True): return n1,n2 return None
def TD_find_product_decomposition(k, n): r""" Attempts to find a factorization of `n` in order to build a `TD(k,n)`. If Sage can build a `TD(k,n_1)` and a `TD(k,n_2)` such that `n=n_1\times n_2` then a `TD(k,n)` can be built (from the function :func:`transversal_design`). This method returns such a pair of integers if it exists, and ``None`` otherwise. INPUT: - ``k,n`` (integers) -- see above. .. SEEALSO:: :func:`TD_product` that actually build a product EXAMPLES:: sage: from sage.combinat.designs.orthogonal_arrays import TD_find_product_decomposition sage: TD_find_product_decomposition(6, 84) (7, 12) sage: TD1 = designs.transversal_design(6, 7) sage: TD2 = designs.transversal_design(6, 12) sage: from sage.combinat.designs.orthogonal_arrays import TD_product sage: TD = TD_product(6, TD1, 7, TD2, 12) """ from sage.rings.arith import divisors for n1 in divisors(n)[1:-1]: # we ignore 1 and n n2 = n // n1 if transversal_design(k, n1, existence=True) and transversal_design( k, n2, existence=True): return n1, n2 return None
def AllCusps(N): r""" Return a list of CuspFamily objects corresponding to the cusps of `X_0(N)`. INPUT: - ``N`` - (integer): the level EXAMPLES:: sage: AllCusps(18) [(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)] sage: AllCusps(0) Traceback (most recent call last): ... ValueError: N must be positive """ N = ZZ(N) if N <= 0: raise ValueError("N must be positive") c = [] for d in divisors(N): n = num_cusps_of_width(N, d) if n == 1: c.append(CuspFamily(N, d)) elif n > 1: for i in xrange(n): c.append(CuspFamily(N, d, label=str(i+1))) return c
def _coset_reduction_data_second_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the second part of the reduction step (the second coordinate). INPUT: self OUTPUT: a dictionary v with keys the divisors of N such that v[d] is the subgroup {h in H : h = 1 (mod N/d)}. EXAMPLES:: sage: G = GammaH(240,[7,239]) sage: G._coset_reduction_data_second_coord() {1: [1], 2: [1], 3: [1], 4: [1], 5: [1, 49], 6: [1], 48: [1, 191], 8: [1], 80: [1, 7, 49, 103], 10: [1, 49], 12: [1], 15: [1, 49], 240: [1, 7, 49, 103, 137, 191, 233, 239], 40: [1, 7, 49, 103], 20: [1, 49], 24: [1, 191], 120: [1, 7, 49, 103, 137, 191, 233, 239], 60: [1, 49, 137, 233], 30: [1, 49, 137, 233], 16: [1]} sage: G = GammaH(1200,[-1,7]); G Congruence Subgroup Gamma_H(1200) with H generated by [7, 1199] sage: K = G._coset_reduction_data_second_coord().keys() ; K.sort() sage: K == divisors(1200) True """ H = G._list_of_elements_in_H() N = G.level() v = { 1: [1] , N: H } for d in [x for x in divisors(N) if x > 1 and x < N ]: N_over_d = N // d v[d] = [x for x in H if x % N_over_d == 1] return v
def _b_power_k(self, k): r""" An expression involving moebius inversion in the powersum generators. For a positive value of ``k``, this expression is .. MATH:: \frac{1}{k} \sum_{d|k} \mu(d/k) p_d. INPUT: - ``k`` -- a positive integer OUTPUT: - an expression in the powersum basis of the symmetric functions EXAMPLES:: sage: st = SymmetricFunctions(QQ).st() sage: st._b_power_k(1) p[1] sage: st._b_power_k(2) -1/2*p[1] + 1/2*p[2] sage: st._b_power_k(6) 1/6*p[1] - 1/6*p[2] - 1/6*p[3] + 1/6*p[6] """ if k == 1: return self._p([1]) if k > 0: return ~k * self._p.sum(moebius(k/d)*self._p([d]) for d in divisors(k))
def test_Hecke_relations(a,b,C): r"""Testing Hecke relations for the Fourier coefficients in C INPUT: -''C'' -- dictionary of complex (Fourier coefficients) -''a'' -- integer -''b'' -- integer OUTPUT: -''diff'' -- real : |C(a)C(b)-C(ab)| if (a,b)=1 EXAMPLE:: sage: S=MaassWaveForms(Gamma0(1)) sage: R=mpmath.mpf(9.53369526135355755434423523592877032382125639510725198237579046413534) sage: Y=mpmath.mpf(0.85) sage: C=coefficients_for_Maass_waveforms(S,R,Y,10,20,12) sage: d=test_Hecke_relations(C,2,3); mppr(d) '9.29e-8' sage: C=coefficients_for_Maass_waveforms(S,R,Y,30,50,20) sage: d=test_Hecke_relations(C,2,3); mppr(d) '3.83e-43' """ c=gcd(Integer(a),Integer(b)) lhs=C[0][a]*C[0][b] rhs=mpmath.mpf(0) for d in divisors(c): rhs=rhs+C[0][Integer(a*b/d/d)] return abs(rhs-lhs)
def number_of_Gamma0_NFCusps(N): """ Returns the total number of orbits of cusps under the action of the congruence subgroup `\\Gamma_0(N)`. INPUT: - ``N`` -- a number field ideal. OUTPUT: ingeter -- the number of orbits of cusps under Gamma0(N)-action. EXAMPLES:: sage: k.<a> = NumberField(x^3 + 11) sage: N = k.ideal(2, a+1) sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: number_of_Gamma0_NFCusps(N) 4 sage: L = Gamma0_NFCusps(N) sage: len(L) == number_of_Gamma0_NFCusps(N) True """ k = N.number_field() # The number of Gamma0(N)-sub-orbits for each Gamma-orbit: from sage.rings.arith import divisors s = sum([len(list((d+N/d).invertible_residues_mod(k.unit_group().gens()))) \ for d in divisors(N)]) # There are h Gamma-orbits, with h class number of underlying number field. return s * k.class_number()
def AllCusps(N): r""" Return a list of CuspFamily objects corresponding to the cusps of `X_0(N)`. INPUT: - ``N`` - (integer): the level EXAMPLES:: sage: AllCusps(18) [(Inf), (c_{2}), (c_{3,1}), (c_{3,2}), (c_{6,1}), (c_{6,2}), (c_{9}), (0)] """ try: N = ZZ(N) assert N>0 except TypeError: raise TypeError, "N must be an integer" except AssertionError: raise AssertionError, "N must be positive" c = [] for d in divisors(N): n = num_cusps_of_width(N, d) if n == 1: c.append(CuspFamily(N, d)) elif n > 1: for i in xrange(n): c.append(CuspFamily(N, d, label=str(i+1))) return c
def _Weyl_law_consts(self): r""" Compute constants for the Weyl law on self._G OUTPUT: - tuple of real numbers EXAMPLES:: sage: M=MaassWaveForms(MySubgroup(Gamma0(1))) sage: M._Weyl_law_consts() (0, 2/pi, (log(pi) - log(2) + 2)/pi, 0, -2) """ import mpmath pi=mpmath.fp.pi ix=Integer(self._G.index()) nc=Integer(len(self._G.cusps())) if(self._G.is_congruence()): lvl=Integer(self._G.level()) else: lvl=0 n2=Integer(self._G.nu2()) n3=Integer(self._G.nu3()) c1=ix/Integer(12) c2=Integer(2)*nc/pi c3=nc*(Integer(2)-ln(Integer(2))+ln(pi))/pi if(lvl<>0): A=1 for q in divisors(lvl): num_prim_dc=0 DG=DirichletGroup(q) for chi in DG.list(): if(chi.is_primitive()): num_prim_dc=num_prim_dc+1 for m in divisors(lvl): if(lvl % (m*q) == 0 and m % q ==0 ): fak=(q*lvl)/gcd(m,lvl/m) A=A*Integer(fak)**num_prim_dc c4=-ln(A)/pi else: c4=Integer(0) # constant term c5=-ix/144+n2/8+n3*2/9-nc/4-1 return (c1,c2,c3,c4,c5)
def reduce_basis(self, long_etas): r""" Produce a more manageable basis via LLL-reduction. INPUT: - ``long_etas`` - a list of EtaGroupElement objects (which should all be of the same level) OUTPUT: - a new list of EtaGroupElement objects having hopefully smaller norm ALGORITHM: We define the norm of an eta-product to be the `L^2` norm of its divisor (as an element of the free `\ZZ`-module with the cusps as basis and the standard inner product). Applying LLL-reduction to this gives a basis of hopefully more tractable elements. Of course we'd like to use the `L^1` norm as this is just twice the degree, which is a much more natural invariant, but `L^2` norm is easier to work with! EXAMPLES:: sage: EtaGroup(4).reduce_basis([ EtaProduct(4, {1:8,2:24,4:-32}), EtaProduct(4, {1:8, 4:-8})]) [Eta product of level 4 : (eta_1)^8 (eta_4)^-8, Eta product of level 4 : (eta_1)^-8 (eta_2)^24 (eta_4)^-16] """ N = self.level() cusps = AllCusps(N) r = matrix(ZZ, [[et.order_at_cusp(c) for c in cusps] for et in long_etas]) V = FreeModule(ZZ, r.ncols()) A = V.submodule_with_basis([V(rw) for rw in r.rows()]) rred = r.LLL() short_etas = [] for shortvect in rred.rows(): bv = A.coordinates(shortvect) dict = {} for d in divisors(N): dict[d] = sum( [bv[i] * long_etas[i].r(d) for i in xrange(r.nrows())]) short_etas.append(self(dict)) return short_etas
def reduce_basis(self, long_etas): r""" Produce a more manageable basis via LLL-reduction. INPUT: - ``long_etas`` - a list of EtaGroupElement objects (which should all be of the same level) OUTPUT: - a new list of EtaGroupElement objects having hopefully smaller norm ALGORITHM: We define the norm of an eta-product to be the `L^2` norm of its divisor (as an element of the free `\ZZ`-module with the cusps as basis and the standard inner product). Applying LLL-reduction to this gives a basis of hopefully more tractable elements. Of course we'd like to use the `L^1` norm as this is just twice the degree, which is a much more natural invariant, but `L^2` norm is easier to work with! EXAMPLES:: sage: EtaGroup(4).reduce_basis([ EtaProduct(4, {1:8,2:24,4:-32}), EtaProduct(4, {1:8, 4:-8})]) [Eta product of level 4 : (eta_1)^8 (eta_4)^-8, Eta product of level 4 : (eta_1)^-8 (eta_2)^24 (eta_4)^-16] """ N = self.level() cusps = AllCusps(N) r = matrix(ZZ, [[et.order_at_cusp(c) for c in cusps] for et in long_etas]) V = FreeModule(ZZ, r.ncols()) A = V.submodule_with_basis([V(rw) for rw in r.rows()]) rred = r.LLL() short_etas = [] for shortvect in rred.rows(): bv = A.coordinates(shortvect) dict = {} for d in divisors(N): dict[d] = sum( [bv[i]*long_etas[i].r(d) for i in xrange(r.nrows())]) short_etas.append(self(dict)) return short_etas
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self._content le = list(evaluation) if not le: return 0 n = sum(le) return sum( euler_phi(j) * factorial(n / j) / prod(factorial(ni / j) for ni in evaluation) for j in divisors(gcd(le))) / n
def p1NFlist(N): """ Returns a list of the normalized elements of `\\mathbb{P}^1(R/N)`, where `N` is an integral ideal. INPUT: - ``N`` - integral ideal (the level or modulus). EXAMPLES:: sage: k.<a> = NumberField(x^2 + 23) sage: N = k.ideal(3) sage: from sage.modular.modsym.p1list_nf import p1NFlist, psi sage: len(p1NFlist(N))==psi(N) True """ k = N.number_field() L = [MSymbol(N, k(0), k(1), check=False)] #N.residues() = iterator through the residues mod N L = L + [MSymbol(N, k(1), r, check=False) for r in N.residues()] from sage.rings.arith import divisors for D in divisors(N): if not D.is_trivial() and D != N: #we find Dp ideal coprime to N, in inverse class to D if D.is_principal(): Dp = k.ideal(1) c = D.gens_reduced()[0] else: it = k.primes_of_degree_one_iter() Dp = it.next() while not Dp.is_coprime(N) or not (Dp * D).is_principal(): Dp = it.next() c = (D * Dp).gens_reduced()[0] #now we find all the (c,d)'s which have associated divisor D I = D + N / D for d in (N / D).residues(): if I.is_coprime(d): M = D.prime_to_idealM_part(N / D) u = (Dp * M).element_1_mod(N / D) d1 = u * d + (1 - u) L.append(MSymbol(N, c, d1, check=False).normalize()) return L
def p1NFlist(N): """ Returns a list of the normalized elements of `\\mathbb{P}^1(R/N)`, where `N` is an integral ideal. INPUT: - ``N`` - integral ideal (the level or modulus). EXAMPLES:: sage: k.<a> = NumberField(x^2 + 23) sage: N = k.ideal(3) sage: from sage.modular.modsym.p1list_nf import p1NFlist, psi sage: len(p1NFlist(N))==psi(N) True """ k = N.number_field() L = [MSymbol(N, k(0),k(1), check=False)] #N.residues() = iterator through the residues mod N L = L+[MSymbol(N, k(1), r, check=False) for r in N.residues()] from sage.rings.arith import divisors for D in divisors(N): if not D.is_trivial() and D!=N: #we find Dp ideal coprime to N, in inverse class to D if D.is_principal(): Dp = k.ideal(1) c = D.gens_reduced()[0] else: it = k.primes_of_degree_one_iter() Dp = it.next() while not Dp.is_coprime(N) or not (Dp*D).is_principal(): Dp = it.next() c = (D*Dp).gens_reduced()[0] #now we find all the (c,d)'s which have associated divisor D I = D + N/D for d in (N/D).residues(): if I.is_coprime(d): M = D.prime_to_idealM_part(N/D) u = (Dp*M).element_1_mod(N/D) d1 = u*d + (1-u) L.append(MSymbol(N, c, d1, check=False).normalize()) return L
def cardinality(self): r""" Return the number of integer necklaces with the evaluation ``content``. The formula for the number of necklaces of content `\alpha` a composition of `n` is: .. MATH:: \sum_{d|gcd(\alpha)} \phi(d) \binom{n/d}{\alpha_1/d, \ldots, \alpha_\ell/d}, where `\phi(d)` is the Euler `\phi` function. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 sage: Necklaces([0,3,2]).cardinality() 2 Check to make sure that the count matches up with the number of necklace words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2],[0,4,2],[2,0,4]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self._content le = list(evaluation) if not le: return 0 n = sum(le) return sum(euler_phi(j)*factorial(n/j) / prod(factorial(ni/j) for ni in evaluation) for j in divisors(gcd(le))) / n
def summand(part, n): """ Create the summand used in the Harrison count for a given partition. Args: part (tuple): A partition of `n` represented as a tuple. n (int): The integer for which `part` is a partition. Returns: int: The summand corresponding to the partition `part` of `n`. """ t = 1 count = list(cycle_count(part, n)) + (factorial(n)-n)*[0] for i in range(1,n+1): for j in range(1,n+1): s = sum([d*(count[d-1]) for d in divisors(lcm(i,j))]) t = t*(s**(count[i-1]*count[j-1]*gcd(i,j))) t = t*factorial(n)/(prod(factorial(count[d-1])*(d**(count[d-1])) for d in range(1,n+1))) return t
def _find_cusps(self): r""" Return an ordered list of inequivalent cusps for self, i.e. a set of representatives for the orbits of self on `\mathbb{P}^1(\QQ)`. These are returned in a reduced form; see self.reduce_cusp for the definition of reduced. ALGORITHM: Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on `\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d \sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d, N/d)`. EXAMPLES:: sage: Gamma0(90)._find_cusps() [0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity] sage: Gamma0(1).cusps() [Infinity] sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym') True """ N = self.level() s = [] for d in arith.divisors(N): w = arith.gcd(d, N // d) if w == 1: if d == 1: s.append(Cusp(1, 0)) elif d == N: s.append(Cusp(0, 1)) else: s.append(Cusp(1, d)) else: for a in xrange(1, w): if arith.gcd(a, w) == 1: while arith.gcd(a, d // w) != 1: a += w s.append(Cusp(a, d)) return sorted(s)
def _find_cusps(self): r""" Return an ordered list of inequivalent cusps for self, i.e. a set of representatives for the orbits of self on `\mathbb{P}^1(\QQ)`. These are returned in a reduced form; see self.reduce_cusp for the definition of reduced. ALGORITHM: Uses explicit formulae specific to `\Gamma_0(N)`: a reduced cusp on `\Gamma_0(N)` is always of the form `a/d` where `d | N`, and `a_1/d \sim a_2/d` if and only if `a_1 \cong a_2 \bmod {\rm gcd}(d, N/d)`. EXAMPLES:: sage: Gamma0(90)._find_cusps() [0, 1/45, 1/30, 1/18, 1/15, 1/10, 1/9, 2/15, 1/6, 1/5, 1/3, 11/30, 1/2, 2/3, 5/6, Infinity] sage: Gamma0(1).cusps() [Infinity] sage: Gamma0(180).cusps() == Gamma0(180).cusps(algorithm='modsym') True """ N = self.level() s = [] for d in arith.divisors(N): w = arith.gcd(d, N//d) if w == 1: if d == 1: s.append(Cusp(1,0)) elif d == N: s.append(Cusp(0,1)) else: s.append(Cusp(1,d)) else: for a in xrange(1, w): if arith.gcd(a, w) == 1: while arith.gcd(a, d//w) != 1: a += w s.append(Cusp(a,d)) return sorted(s)
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular. EXAMPLES:: sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 AUTHOR: - Jordi Quer """ if self.is_even(): return self.ncusps() N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in divisors(N) if d**2 <= N]: Nd = lcm(d,N//d) Hd = set([x%Nd for x in H]) if Nd - 1 not in Hd: summand = euler_phi(d)*euler_phi(N//d)//(2*len(Hd)) if d**2==N: c = c + summand else: c = c + 2*summand return c
def nregcusps(self): r""" Return the number of orbits of regular cusps for this subgroup. A cusp is regular if we may find a parabolic element generating the stabiliser of that cusp whose eigenvalues are both +1 rather than -1. If G contains -1, all cusps are regular. EXAMPLES:: sage: GammaH(20, [17]).nregcusps() 4 sage: GammaH(20, [17]).nirregcusps() 2 sage: GammaH(3212, [2045, 2773]).nregcusps() 1440 sage: GammaH(3212, [2045, 2773]).nirregcusps() 720 AUTHOR: - Jordi Quer """ if self.is_even(): return self.ncusps() N = self.level() H = self._list_of_elements_in_H() c = ZZ(0) for d in [d for d in divisors(N) if d**2 <= N]: Nd = lcm(d, N // d) Hd = set([x % Nd for x in H]) if Nd - 1 not in Hd: summand = euler_phi(d) * euler_phi(N // d) // (2 * len(Hd)) if d**2 == N: c = c + summand else: c = c + 2 * summand return c
def lift2smallest_field2(a): """ INPUT: a is an element of a finite field GF(q) OUTPUT: the element b of the smallest subfield F of GF(q) for which F(b)=a. EXAMPLES:: sage: from sage.coding.code_constructions import lift2smallest_field2 sage: FF.<z> = GF(3^4,"z") sage: a = z^40 sage: lift2smallest_field2(a) (2, Finite Field of size 3) sage: FF.<z> = GF(2^4,"z") sage: a = z^15 sage: lift2smallest_field2(a) (1, Finite Field of size 2) .. warning:: Since coercion (the FF(b) step) has a bug in it, this *only works* in the case when you *know* F is a prime field. AUTHORS: - David Joyner """ FF = a.parent() q = FF.order() if q.is_prime(): return a,FF p = q.factor()[0][0] k = q.factor()[0][1] for d in divisors(k): F = GF(p**d,"zz") for b in F: if FF(b) == a: return b, F
def lift2smallest_field2(a): """ INPUT: a is an element of a finite field GF(q) OUTPUT: the element b of the smallest subfield F of GF(q) for which F(b)=a. EXAMPLES:: sage: from sage.coding.code_constructions import lift2smallest_field2 sage: FF.<z> = GF(3^4,"z") sage: a = z^40 sage: lift2smallest_field2(a) (2, Finite Field of size 3) sage: FF.<z> = GF(2^4,"z") sage: a = z^15 sage: lift2smallest_field2(a) (1, Finite Field of size 2) .. warning:: Since coercion (the FF(b) step) has a bug in it, this *only works* in the case when you *know* F is a prime field. AUTHORS: - David Joyner """ FF = a.parent() q = FF.order() if q.is_prime(): return a,FF p = q.factor()[0][0] k = q.factor()[0][1] for d in divisors(k): F = GF(p**d,"zz") for b in F: if FF(b) == a: return b, F
def find_product_decomposition(k,n): r""" Look for a factorization of `n` in order to build an `OA(k,n)`. If Sage can build a `OA(k,n_1)` and a `OA(k,n_2)` such that `n=n_1\times n_2` then a `OA(k,n)` can be built by a product construction (which correspond to Wilson's construction with no truncated column). This function look for a pair of integers `(n_1,n_2)` with `n1 \leq n_2`, `n_1 \times n_2 = n` and such that both an `OA(k,n_1)` and an `OA(k,n_2)` are available. INPUT: - ``k,n`` (integers) -- see above. OUTPUT: A pair ``f,args`` such that ``f(*args)`` is an `OA(k,n)` or ``False`` if no product decomposition was found. EXAMPLES:: sage: from sage.combinat.designs.orthogonal_arrays_recursive import find_product_decomposition sage: f,args = find_product_decomposition(6, 84) sage: args (6, 7, 12, ()) sage: _ = f(*args) """ from sage.rings.arith import divisors for n1 in divisors(n)[1:-1]: # we ignore 1 and n n2 = n//n1 # n2 is decreasing along the loop if n2 < n1: break if orthogonal_array(k, n1, existence=True) and orthogonal_array(k, n2, existence=True): return simple_wilson_construction, (k,n1,n2,()) return False
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 _coset_reduction_data_first_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the first part of the reduction step (the first coordinate). INPUT: G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N). OUTPUT: A list v such that v[u] = (min(u*h: h in H), gcd(u,N) , an h such that h*u = min(u*h: h in H)). EXAMPLES:: sage: G = Gamma0(12) sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G) [(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1), (1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)] """ H = [int(x) for x in G._list_of_elements_in_H()] N = int(G.level()) # Get some useful fast functions for inverse and gcd inverse_mod = get_inverse_mod(N) # optimal inverse function gcd = get_gcd(N) # optimal gcd function # We will be filling this list in below. reduct_data = [0] * N # We can fill in 0 and all elements of H immediately reduct_data[0] = (0, N, 0) for u in H: reduct_data[u] = (1, 1, inverse_mod(u, N)) # Make a table of the reduction of H (mod N/d), one for each # divisor d. repr_H_mod_N_over_d = {} for d in divisors(N): # We special-case N == d because in this case, # 1 % N_over_d is 0 if N == d: repr_H_mod_N_over_d[d] = [1] break N_over_d = N // d # For each element of H, we look at its image mod # N_over_d. If we haven't yet seen it, add it on to # the end of z. w = [0] * N_over_d z = [1] for x in H: val = x % N_over_d if not w[val]: w[val] = 1 z.append(x) repr_H_mod_N_over_d[d] = z # Compute the rest of the tuples. The values left to process # are those where reduct_data has a 0. Note that several of # these values are processed on each loop below, so re-index # each time. while True: try: u = reduct_data.index(0) except ValueError: break d = gcd(u, N) for x in repr_H_mod_N_over_d[d]: reduct_data[(u * x) % N] = (u, d, inverse_mod(x, N)) return reduct_data
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: enumerate_totallyreal_fields_all(2, 10)[0][1].parent() Interface to the PARI C library sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0,0,0,0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.*B)**(1.*d/n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "="*80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n/d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0],pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0],t[1]] for t in T] j = i+1 for E in enumerate_totallyreal_fields_prim(n/d, int(math.floor((1.*B)**(1./d)/(1.*Sds[i][0])**(n*1./d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort(cmp=lambda x, y: cmp(x[0], y[0]) or cmp(x[1], y[1])) weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "="*80 print "Polynomials tested: {}".format(counts[0]) print ( "Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print "Irreducible polynomials: {}".format(counts[2]) print "Polynomials with nfdisc <= B: {}".format(counts[3]) for i in range(len(S)): print S[i] if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [map(ZZ, counts), [[ZZ(s[0]), map(QQ, s[1].reverse().Vec())] for s in S]] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px(map(QQ, s[1].list()))] for s in S]
def Gamma0_NFCusps(N): r""" Returns a list of inequivalent cusps for `\Gamma_0(N)`, i.e., a set of representatives for the orbits of ``self`` on `\mathbb{P}^1(k)`. INPUT: - ``N`` -- an integral ideal of the number field k (the level). OUTPUT: A list of inequivalent number field cusps. EXAMPLES: :: sage: k.<a> = NumberField(x^2 + 5) sage: N = k.ideal(3) sage: L = Gamma0_NFCusps(N) The cusps in the list are inequivalent: :: sage: all([not L[i].is_Gamma0_equivalent(L[j], N) for i, j in \ mrange([len(L), len(L)]) if i<j]) True We test that we obtain the right number of orbits: :: sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: len(L) == number_of_Gamma0_NFCusps(N) True Another example: :: sage: k.<a> = NumberField(x^4 - x^3 -21*x^2 + 17*x + 133) sage: N = k.ideal(5) sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: len(Gamma0_NFCusps(N)) == number_of_Gamma0_NFCusps(N) # long time (over 1 sec) True """ # We create L a list of three lists, which are different and each a list of # prime ideals, coprime to N, representing the ideal classes of k L = NFCusps_ideal_reps_for_levelN(N, nlists=3) Laux = L[1] + L[2] Lreps = list_of_representatives(N) Lcusps = [] k = N.number_field() for A in L[0]: #find B in inverse class: if A.is_trivial(): B = k.ideal(1) #B = k.unit_ideal() produces an error because we need fract ideal g = 1 else: Lbs = [P for P in Laux if (P * A).is_principal()] B = Lbs[0] g = (A * B).gens_reduced()[0] #for every divisor of N we have to find cusps from sage.rings.arith import divisors for d in divisors(N): #find delta prime coprime to B in inverse class of d*A #by searching in our list of auxiliary prime ideals Lds = [ P for P in Laux if (P * d * A).is_principal() and P.is_coprime(B) ] deltap = Lds[0] a = (deltap * d * A).gens_reduced()[0] I = d + N / d #especial case: A=B=d=<1>: if a.is_one() and I.is_trivial(): Lcusps.append(NFCusp(k, 0, 1, lreps=Lreps)) else: u = k.unit_group().gens() for b in I.invertible_residues_mod(u): #Note: if I trivial, invertible_residues_mod returns [1] #lift b to (R/a)star #we need the part of d which is coprime to I, call it M M = d.prime_to_idealM_part(I) deltAM = deltap * A * M u = (B * deltAM).element_1_mod(I) v = (I * B).element_1_mod(deltAM) newb = u * b + v #build AB-matrix: #----> extended gcd for k.ideal(a), k.ideal(newb) Y = k.ideal(newb).element_1_mod(k.ideal(a)) # if xa + yb = 1, cusp = y*g /a Lcusps.append(NFCusp(k, Y * g, a, lreps=Lreps)) return Lcusps
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 basis(self, reduce=True): r""" Produce a basis for the free abelian group of eta-products of level N (under multiplication), attempting to find basis vectors of the smallest possible degree. INPUT: - ``reduce`` - a boolean (default True) indicating whether or not to apply LLL-reduction to the calculated basis EXAMPLE:: sage: EtaGroup(5).basis() [Eta product of level 5 : (eta_1)^6 (eta_5)^-6] sage: EtaGroup(12).basis() [Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3, Eta product of level 12 : (eta_1)^-2 (eta_2)^3 (eta_3)^6 (eta_4)^-1 (eta_6)^-9 (eta_12)^3, Eta product of level 12 : (eta_1)^-3 (eta_2)^2 (eta_3)^1 (eta_4)^-1 (eta_6)^-2 (eta_12)^3, Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2, Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1] sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients [Eta product of level 12 : (eta_2)^24 (eta_12)^-24, Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144, Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8, Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4, Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5] ALGORITHM: An eta product of level `N` is uniquely determined by the integers `r_d` for `d | N` with `d < N`, since `\sum_{d | N} r_d = 0`. The valid `r_d` are those that satisfy two congruences modulo 24, and one congruence modulo 2 for every prime divisor of N. We beef up the congruences modulo 2 to congruences modulo 24 by multiplying by 12. To calculate the kernel of the ensuing map `\ZZ^m \to (\ZZ/24\ZZ)^n` we lift it arbitrarily to an integer matrix and calculate its Smith normal form. This gives a basis for the lattice. This lattice typically contains "large" elements, so by default we pass it to the reduce_basis() function which performs LLL-reduction to give a more manageable basis. """ N = self.level() divs = divisors(N)[:-1] s = len(divs) primedivs = prime_divisors(N) rows = [] for i in xrange(s): # generate a row of relation matrix row = [ Mod(divs[i], 24) - Mod(N, 24), Mod(N / divs[i], 24) - Mod(1, 24) ] for p in primedivs: row.append(Mod(12 * (N / divs[i]).valuation(p), 24)) rows.append(row) M = matrix(IntegerModRing(24), rows) Mlift = M.change_ring(ZZ) # now we compute elementary factors of Mlift S, U, V = Mlift.smith_form() good_vects = [] for i in xrange(U.nrows()): vect = U.row(i) nf = (i < S.ncols() and S[i, i]) or 0 good_vects.append((vect * 24 / gcd(nf, 24)).list()) for v in good_vects: v.append(-sum([r for r in v])) dicts = [] for v in good_vects: dicts.append({}) for i in xrange(s): dicts[-1][divs[i]] = v[i] dicts[-1][N] = v[-1] if reduce: return self.reduce_basis([self(d) for d in dicts]) else: return [self(d) for d in dicts]
def subgroups(self, check=False): r""" Compute all the subgroups of this abelian group (which must be finite). TODO: This is *many orders of magnitude* slower than Magma. INPUT: - check: if True, performs the same computation in GAP and checks that the number of subgroups generated is the same. (I don't know how to convert GAP's output back into Sage, so we don't actually compare the subgroups). ALGORITHM: If the group is cyclic, the problem is easy. Otherwise, write it as a direct product A x B, where B is cyclic. Compute the subgroups of A (by recursion). Now, for every subgroup C of A x B, let G be its *projection onto* A and H its *intersection with* B. Then there is a well-defined homomorphism f: G -> B/H that sends a in G to the class mod H of b, where (a,b) is any element of C lifting a; and every subgroup C arises from a unique triple (G, H, f). EXAMPLES:: sage: AbelianGroup([2,3]).subgroups() [Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0*f1^2], Multiplicative Abelian Group isomorphic to C2, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0], Multiplicative Abelian Group isomorphic to C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f1], Trivial Abelian Group, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by []] sage: len(AbelianGroup([2,4,8]).subgroups()) 81 """ if not self.is_finite(): raise ValueError, "Group must be finite" from sage.misc.misc import verbose v = self.invariants() if len(v) <= 1: if v == [] or v[0] == 1: return [self] else: return [ self.subgroup([self.gen(0)**i]) for i in divisors(v[0])[:-1] ] + [self.subgroup([])] A = AbelianGroup(v[:-1]) x = v[-1] Wsubs = A.subgroups() subgps = [] for G in Wsubs: verbose("G = subgp generated by %s" % G.gens()) verbose("invariants are:", [t.order() for t in G.gens()]) # G.invariants() doesn't work for H in divisors(x): # H = the subgroup of *index* H. its = [ xrange(0, H, H / gcd(H, G.gen(i).order())) for i in xrange(len(G.gens())) ] for f in cartesian_product_iterator(its): verbose("using hom from G to C_%s sending gens to %s" % (H, f)) new_sub = [] for a in xrange(len(G.gens())): new_sub.append(G.gen(a).list() + [f[a]]) if H != x: new_sub.append([0] * A.ngens() + [H]) subgps.append(self.subgroup_reduced(new_sub)) if check: from sage.interfaces.all import gap verbose("Running Gap cross-check") t = ZZ( gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v)) if t != len(subgps): raise ArithmeticError, "For %s Gap finds %s subgroups, I found %s" % ( v, t, len(subgps)) verbose("Gap check OK for %s: %s" % (v, t)) return subgps
def subgroups(self, check=False): r""" Compute all the subgroups of this abelian group (which must be finite). TODO: This is *many orders of magnitude* slower than Magma. INPUT: - check: if True, performs the same computation in GAP and checks that the number of subgroups generated is the same. (I don't know how to convert GAP's output back into Sage, so we don't actually compare the subgroups). ALGORITHM: If the group is cyclic, the problem is easy. Otherwise, write it as a direct product A x B, where B is cyclic. Compute the subgroups of A (by recursion). Now, for every subgroup C of A x B, let G be its *projection onto* A and H its *intersection with* B. Then there is a well-defined homomorphism f: G -> B/H that sends a in G to the class mod H of b, where (a,b) is any element of C lifting a; and every subgroup C arises from a unique triple (G, H, f). EXAMPLES:: sage: AbelianGroup([2,3]).subgroups() [Multiplicative Abelian Group isomorphic to C2 x C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0*f1^2], Multiplicative Abelian Group isomorphic to C2, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f0], Multiplicative Abelian Group isomorphic to C3, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by [f1], Trivial Abelian Group, which is the subgroup of Multiplicative Abelian Group isomorphic to C2 x C3 generated by []] sage: len(AbelianGroup([2,4,8]).subgroups()) 81 """ if not self.is_finite(): raise ValueError, "Group must be finite" from sage.misc.misc import verbose v = self.invariants() if len(v) <= 1: if v == [] or v[0] == 1: return [self] else: return [ self.subgroup([self.gen(0)**i]) for i in divisors(v[0])[:-1]] + [self.subgroup([])] A = AbelianGroup(v[:-1]) x = v[-1] Wsubs = A.subgroups() subgps = [] for G in Wsubs: verbose("G = subgp generated by %s" % G.gens()) verbose("invariants are:", [t.order() for t in G.gens()]) # G.invariants() doesn't work for H in divisors(x): # H = the subgroup of *index* H. its = [xrange(0, H, H/gcd(H, G.gen(i).order())) for i in xrange(len(G.gens()))] for f in cartesian_product_iterator(its): verbose("using hom from G to C_%s sending gens to %s" % (H,f)) new_sub = [] for a in xrange(len(G.gens())): new_sub.append(G.gen(a).list() + [f[a]]) if H != x: new_sub.append([0]*A.ngens() + [H]) subgps.append(self.subgroup_reduced(new_sub)) if check: from sage.interfaces.all import gap verbose("Running Gap cross-check") t = ZZ(gap.eval("Size(SubgroupsSolvableGroup(AbelianGroup(%s)))" % v)) if t != len(subgps): raise ArithmeticError, "For %s Gap finds %s subgroups, I found %s" % (v, t, len(subgps)) verbose("Gap check OK for %s: %s" % (v, t)) return subgps
def is_cycle(seq): length = len(seq) for n in divisors(length): if n < length and is_cycle_of_length(seq, n): return True return False
def additive_lift(self, forms, weight, with_character=False, is_integral=False): """ Borcherds additive lift to hermitian modular forms of degree `2`. This coinsides with Gritsenko's arithmetic lift after using the theta decomposition. INPUT: - ``forms`` -- A list of functions accepting an integer and returning a q-expansion. - ``weight`` -- A positive integer; The weight of the lift. - ``with_character`` -- A boolean (default: ``False``); Whether the lift has nontrivial character. - ``is_integral`` -- A boolean (default: ``False``); If ``True`` use rings of integral q-expansions over `\Z`. ALGORITHME: We use the explicite formulas in [D]. TESTS:: sage: from hermitianmodularforms.hermitianmodularformd2_fegenerators import HermitianModularFormD2AdditiveLift sage: HermitianModularFormD2AdditiveLift(4, [1,0,0], -3, 4).coefficients() {(2, 3, 2, 2): 720, (1, 1, 1, 1): 27, (1, 0, 0, 2): 270, (3, 3, 3, 3): 2943, (2, 1, 1, 3): 2592, (0, 0, 0, 2): 9, (2, 2, 2, 2): 675, (2, 3, 2, 3): 2160, (1, 1, 1, 2): 216, (3, 0, 0, 3): 8496, (2, 0, 0, 3): 2214, (1, 0, 0, 3): 720, (2, 1, 1, 2): 1080, (0, 0, 0, 1): 1, (3, 3, 2, 3): 4590, (3, 1, 1, 3): 4590, (1, 1, 1, 3): 459, (2, 0, 0, 2): 1512, (1, 0, 0, 1): 72, (0, 0, 0, 0): 1/240, (3, 4, 3, 3): 2808, (0, 0, 0, 3): 28, (3, 2, 2, 3): 4752, (2, 2, 2, 3): 1350} sage: HermitianModularFormD2AdditiveLift(4, [0,1,0], -3, 6).coefficients() {(2, 3, 2, 2): -19680, (1, 1, 1, 1): -45, (1, 0, 0, 2): -3690, (3, 3, 3, 3): -306225, (2, 1, 1, 3): -250560, (0, 0, 0, 2): 33, (2, 2, 2, 2): -13005, (2, 3, 2, 3): -153504, (1, 1, 1, 2): -1872, (3, 0, 0, 3): -1652640, (2, 0, 0, 3): -295290, (1, 0, 0, 3): -19680, (2, 1, 1, 2): -43920, (0, 0, 0, 1): 1, (3, 3, 2, 3): -948330, (3, 1, 1, 3): -1285290, (1, 1, 1, 3): -11565, (2, 0, 0, 2): -65520, (1, 0, 0, 1): -240, (0, 0, 0, 0): -1/504, (3, 4, 3, 3): -451152, (0, 0, 0, 3): 244, (3, 2, 2, 3): -839520, (2, 2, 2, 3): -108090} """ if with_character and self.__D % 4 != 0: raise ValueError( "Characters are only possible for even discriminants.") ## This will be needed if characters are implemented if with_character: if (Integer(self.__D / 4) % 4) in [-2, 2]: alpha = (-self.__D / 4, 1 / 2) else: alpha = (-self.__D / 8, 1 / 2) #minv = 1/2 if with_character else 1 R = self.power_series_ring() q = R.gen(0) (vv_expfactor, vv_basis) = self._additive_lift_vector_valued_basis() vvform = dict( (self._reduce_vector_valued_index(k), R(0)) for (k, _) in self. _semireduced_vector_valued_indices_with_discriminant_offset(1)) for (f, b) in zip(forms, vv_basis): ## We have to apply the scaling of exponents to the form f = R( f(self._qexp_precision()) ).add_bigoh(self._qexp_precision()) \ .subs({q : q**vv_expfactor}) if not f.is_zero(): for (k, e) in b.iteritems(): vvform[k] = vvform[k] + e * f ## the T = matrix(2,[*, t / 2, \bar t / 2, *] th fourier coefficients of the lift ## only depends on (- 4 * D * det(T), eps = gcd(T), \theta \cong t / eps) ## if m != 1 we consider 2*T maass_coeffs = dict() ## TODO: use divisor dictionaries if not with_character: ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = vv_expfactor // (-self.__D) for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iterator_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else: ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = (2 * vv_expfactor) // (-self.__D) if self.__D // 4 % 2 == 0: for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iter_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[0] + theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else: for eps in self._iterator_content(): for ( theta, offset ) in self._semireduced_vector_valued_indices_with_discriminant_offset( eps): for disc in self._iter_discriminant(eps, offset): maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps) ) lift_coeffs = dict() ## TODO: Check whether this is correct. Add the character as an argument. for ((a, b1, b2, c), eps, disc) in self.precision( ).iter_positive_forms_for_character_with_content_and_discriminant( for_character=with_character): (theta1, theta2) = self._reduce_vector_valued_index( (b1 / eps, b2 / eps)) theta = (eps * theta1, eps * theta2) try: lift_coeffs[(a, b1, b2, c)] = maass_coeffs[(disc, eps, theta)] except: raise RuntimeError( str((a, b1, b2, c)) + " ; " + str((disc, eps, theta))) # Eisenstein component for (_, _, _, c) in self.precision().iter_semidefinite_forms_for_character( for_character=with_character): if c != 0: lift_coeffs[(0, 0, 0, c)] = vvform[(0, 0)][0] * sigma(c, weight - 1) lift_coeffs[( 0, 0, 0, 0)] = -vvform[(0, 0)][0] * bernoulli(weight) / Integer(2 * weight) if is_integral: lift_coeffs[(0, 0, 0, 0)] = ZZ(lift_coeffs[(0, 0, 0, 0)]) return lift_coeffs
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False, return_pari_objects=True): r""" Enumerates *all* totally real fields of degree ``n`` with discriminant at most ``B``, primitive or otherwise. INPUT: - ``n`` -- integer, the degree - ``B`` -- integer, the discriminant bound - ``verbose`` -- boolean or nonnegative integer or string (default: 0) give a verbose description of the computations being performed. If ``verbose`` is set to ``2`` or more then it outputs some extra information. If ``verbose`` is a string then it outputs to a file specified by ``verbose`` - ``return_seqs`` -- (boolean, default False) If ``True``, then return the polynomials as sequences (for easier exporting to a file). This also returns a list of four numbers, as explained in the OUTPUT section below. - ``return_pari_objects`` -- (boolean, default: True) if both ``return_seqs`` and ``return_pari_objects`` are ``False`` then it returns the elements as Sage objects; otherwise it returns pari objects. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] sage: enumerate_totallyreal_fields_all(1, 10) [[1, x - 1]] TESTS: Each of the outputs must be elements of Sage if ``return_pari_objects`` is set to ``False``:: sage: enumerate_totallyreal_fields_all(2, 10) [[5, x^2 - x - 1], [8, x^2 - 2]] sage: enumerate_totallyreal_fields_all(2, 10)[0][1].parent() Interface to the PARI C library sage: enumerate_totallyreal_fields_all(2, 10, return_pari_objects=False)[0][1].parent() Univariate Polynomial Ring in x over Rational Field In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0, 0, 0, 0] if len(divisors(n)) > 4: raise ValueError("Only implemented for n = p*q with p,q prime") for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim( d, int(math.floor((1. * B)**(1. * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "=" * 80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(4): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] j = i + 1 for E in enumerate_totallyreal_fields_prim( n / d, int( math.floor((1. * B)**(1. / d) / (1. * Sds[i][0])**(n * 1. / d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append( [EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if isinstance(verbose, str): fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "=" * 80 print "Polynomials tested: {}".format(counts[0]) print( "Polynomials with discriminant with large enough square" " divisor: {}".format(counts[1])) print "Irreducible polynomials: {}".format(counts[2]) print "Polynomials with nfdisc <= B: {}".format(counts[3]) for i in range(len(S)): print S[i] if isinstance(verbose, str): fsock.close() sys.stdout = saveout # Make sure to return elements that belong to Sage if return_seqs: return [ map(ZZ, counts), [[ZZ(s[0]), map(QQ, s[1].reverse().Vec())] for s in S] ] elif return_pari_objects: return S else: Px = PolynomialRing(QQ, 'x') return [[ZZ(s[0]), Px(map(QQ, s[1].list()))] for s in S]
def wsum_p(m): # expansion of p_m in w-basis, for m > 0 return self._from_dict({Partition([d] * (m // d)): d for d in divisors(m)})
def skew_hadamard_matrix(n,existence=False, skew_normalize=True, check=True): r""" Tries to construct a skew Hadamard matrix A Hadamard matrix `H` is called skew if `H=S-I`, for `I` the identity matrix and `-S=S^\top`. Currently constructions from Section 14.1 of [Ha83]_ and few more exotic ones are implemented. INPUT: - ``n`` (integer) -- dimension of the matrix - ``existence`` (boolean) -- whether to build the matrix or merely query if a construction is available in Sage. When set to ``True``, the function returns: - ``True`` -- meaning that Sage knows how to build the matrix - ``Unknown`` -- meaning that Sage does not know how to build the matrix, but that the design may exist (see :mod:`sage.misc.unknown`). - ``False`` -- meaning that the matrix does not exist. - ``skew_normalize`` (boolean) -- whether to make the 1st row all-one, and adjust the 1st column accordingly. Set to ``True`` by default. - ``check`` (boolean) -- whether to check that output is correct before returning it. As this is expected to be useless (but we are cautious guys), you may want to disable it whenever you want speed. Set to ``True`` by default. EXAMPLES:: sage: from sage.combinat.matrices.hadamard_matrix import skew_hadamard_matrix sage: skew_hadamard_matrix(12).det() 2985984 sage: 12^6 2985984 sage: skew_hadamard_matrix(1) [1] sage: skew_hadamard_matrix(2) [ 1 1] [-1 1] TESTS:: sage: skew_hadamard_matrix(10,existence=True) False sage: skew_hadamard_matrix(12,existence=True) True sage: skew_hadamard_matrix(784,existence=True) True sage: skew_hadamard_matrix(10) Traceback (most recent call last): ... ValueError: A skew Hadamard matrix of order 10 does not exist sage: skew_hadamard_matrix(36) 36 x 36 dense matrix over Integer Ring... sage: skew_hadamard_matrix(36)==skew_hadamard_matrix(36,skew_normalize=False) False sage: skew_hadamard_matrix(52) 52 x 52 dense matrix over Integer Ring... sage: skew_hadamard_matrix(92) 92 x 92 dense matrix over Integer Ring... sage: skew_hadamard_matrix(816) # long time 816 x 816 dense matrix over Integer Ring... sage: skew_hadamard_matrix(100) Traceback (most recent call last): ... ValueError: A skew Hadamard matrix of order 100 is not yet implemented. sage: skew_hadamard_matrix(100,existence=True) Unknown REFERENCES: .. [Ha83] M. Hall, Combinatorial Theory, 2nd edition, Wiley, 1983 """ def true(): _skew_had_cache[n]=True return True M = None if existence and n in _skew_had_cache: return True if not(n % 4 == 0) and (n > 2): if existence: return False raise ValueError("A skew Hadamard matrix of order %s does not exist" % n) if n == 2: if existence: return true() M = matrix([[1, 1], [-1, 1]]) elif n == 1: if existence: return true() M = matrix([1]) elif is_prime_power(n - 1) and ((n - 1) % 4 == 3): if existence: return true() M = hadamard_matrix_paleyI(n, normalize=False) elif n % 8 == 0: if skew_hadamard_matrix(n//2,existence=True): # (Lemma 14.1.6 in [Ha83]_) if existence: return true() H = skew_hadamard_matrix(n//2,check=False) M = block_matrix([[H,H], [-H.T,H.T]]) else: # try Williamson construction (Lemma 14.1.5 in [Ha83]_) for d in divisors(n)[2:-2]: # skip 1, 2, n/2, and n n1 = n//d if is_prime_power(d - 1) and (d % 4 == 0) and (n1 % 4 == 0)\ and skew_hadamard_matrix(n1,existence=True): if existence: return true() H = skew_hadamard_matrix(n1, check=False)-I(n1) U = matrix(ZZ, d, lambda i, j: -1 if i==j==0 else\ 1 if i==j==1 or (i>1 and j-1==d-i)\ else 0) A = block_matrix([[matrix([0]), matrix(ZZ,1,d-1,[1]*(d-1))], [ matrix(ZZ,d-1,1,[-1]*(d-1)), _helper_payley_matrix(d-1,zero_position=0)]])+I(d) M = A.tensor_product(I(n1))+(U*A).tensor_product(H) break if M is None: # try Williamson-Goethals-Seidel construction if GS_skew_hadamard_smallcases(n, existence=True): if existence: return true() M = GS_skew_hadamard_smallcases(n) else: if existence: return Unknown raise ValueError("A skew Hadamard matrix of order %s is not yet implemented." % n) if skew_normalize: dd = diagonal_matrix(M[0]) M = dd*M*dd if check: assert is_hadamard_matrix(M, normalized=False, skew=True) if skew_normalize: from sage.modules.free_module_element import vector assert M[0]==vector([1]*n) _skew_had_cache[n]=True return M
def is_cycle(seq): length = len(seq) for n in divisors(length): if n < length and is_cycle_of_length(seq, n): return True return False
def cardinality(self): """ Returns the number of integer necklaces with the evaluation e. EXAMPLES:: sage: Necklaces([]).cardinality() 0 sage: Necklaces([2,2]).cardinality() 2 sage: Necklaces([2,3,2]).cardinality() 30 Check to make sure that the count matches up with the number of Lyndon words generated. :: sage: comps = [[],[2,2],[3,2,7],[4,2]]+Compositions(4).list() sage: ns = [ Necklaces(comp) for comp in comps] sage: all( [ n.cardinality() == len(n.list()) for n in ns] ) True """ evaluation = self.e le = list(evaluation) if len(le) == 0: return 0 n = sum(le) return sum([euler_phi(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): r""" Enumerates *all* totally real fields of degree `n` with discriminant `\le B`, primitive or otherwise. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). """ S = [] counts = [0, 0, 0] if len(divisors(n)) > 4: raise ValueError, "Only implemented for n = p*q with p,q prime" for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.0 * B) ** (1.0 * d / n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "=" * 80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), "t") T = enumerate_totallyreal_fields_rel(F, n / d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(3): counts[i] += T[0][i] S += [[t[0], pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0], t[1]] for t in T] j = i + 1 for E in enumerate_totallyreal_fields_prim( n / d, int(math.floor((1.0 * B) ** (1.0 / d) / (1.0 * Sds[i][0]) ** (n * 1.0 / d ** 2))) ): for EF in F.composite_fields(NumberField(ZZx(E[1]), "u")): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if type(verbose) == str: fsock = open(verbose, "w") sys.stdout = fsock # Else, print to screen print "=" * 80 print "Polynomials tested:", counts[0] print "Irreducible polynomials:", counts[1] print "Polynomials with nfdisc <= B:", counts[2] for i in range(len(S)): print S[i] if type(verbose) == str: fsock.close() sys.stdout = saveout if return_seqs: return [counts, [[s[0], s[1].reverse().Vec()] for s in S]] else: return S
def basis(self, reduce=True): r""" Produce a basis for the free abelian group of eta-products of level N (under multiplication), attempting to find basis vectors of the smallest possible degree. INPUT: - ``reduce`` - a boolean (default True) indicating whether or not to apply LLL-reduction to the calculated basis EXAMPLE:: sage: EtaGroup(5).basis() [Eta product of level 5 : (eta_1)^6 (eta_5)^-6] sage: EtaGroup(12).basis() [Eta product of level 12 : (eta_1)^2 (eta_2)^1 (eta_3)^2 (eta_4)^-1 (eta_6)^-7 (eta_12)^3, Eta product of level 12 : (eta_1)^-4 (eta_2)^2 (eta_3)^4 (eta_6)^-2, Eta product of level 12 : (eta_1)^-1 (eta_2)^3 (eta_3)^3 (eta_4)^-2 (eta_6)^-9 (eta_12)^6, Eta product of level 12 : (eta_1)^1 (eta_2)^-1 (eta_3)^-3 (eta_4)^-2 (eta_6)^7 (eta_12)^-2, Eta product of level 12 : (eta_1)^-6 (eta_2)^9 (eta_3)^2 (eta_4)^-3 (eta_6)^-3 (eta_12)^1] sage: EtaGroup(12).basis(reduce=False) # much bigger coefficients [Eta product of level 12 : (eta_2)^24 (eta_12)^-24, Eta product of level 12 : (eta_1)^-336 (eta_2)^576 (eta_3)^696 (eta_4)^-216 (eta_6)^-576 (eta_12)^-144, Eta product of level 12 : (eta_1)^-8 (eta_2)^-2 (eta_6)^2 (eta_12)^8, Eta product of level 12 : (eta_1)^1 (eta_2)^9 (eta_3)^13 (eta_4)^-4 (eta_6)^-15 (eta_12)^-4, Eta product of level 12 : (eta_1)^15 (eta_2)^-24 (eta_3)^-29 (eta_4)^9 (eta_6)^24 (eta_12)^5] ALGORITHM: An eta product of level `N` is uniquely determined by the integers `r_d` for `d | N` with `d < N`, since `\sum_{d | N} r_d = 0`. The valid `r_d` are those that satisfy two congruences modulo 24, and one congruence modulo 2 for every prime divisor of N. We beef up the congruences modulo 2 to congruences modulo 24 by multiplying by 12. To calculate the kernel of the ensuing map `\ZZ^m \to (\ZZ/24\ZZ)^n` we lift it arbitrarily to an integer matrix and calculate its Smith normal form. This gives a basis for the lattice. This lattice typically contains "large" elements, so by default we pass it to the reduce_basis() function which performs LLL-reduction to give a more manageable basis. """ N = self.level() divs = divisors(N)[:-1] s = len(divs) primedivs = prime_divisors(N) rows = [] for i in xrange(s): # generate a row of relation matrix row = [ Mod(divs[i], 24) - Mod(N, 24), Mod(N/divs[i], 24) - Mod(1, 24)] for p in primedivs: row.append( Mod(12*(N/divs[i]).valuation(p), 24)) rows.append(row) M = matrix(IntegerModRing(24), rows) Mlift = M.change_ring(ZZ) # now we compute elementary factors of Mlift S,U,V = Mlift.smith_form() good_vects = [] for i in xrange(U.nrows()): vect = U.row(i) nf = (i < S.ncols() and S[i,i]) or 0 good_vects.append((vect * 24/gcd(nf, 24)).list()) for v in good_vects: v.append(-sum([r for r in v])) dicts = [] for v in good_vects: dicts.append({}) for i in xrange(s): dicts[-1][divs[i]] = v[i] dicts[-1][N] = v[-1] if reduce: return self.reduce_basis([ self(d) for d in dicts]) else: return [self(d) for d in dicts]
def Gamma0_NFCusps(N): r""" Returns a list of inequivalent cusps for `\Gamma_0(N)`, i.e., a set of representatives for the orbits of ``self`` on `\mathbb{P}^1(k)`. INPUT: - ``N`` -- an integral ideal of the number field k (the level). OUTPUT: A list of inequivalent number field cusps. EXAMPLES: :: sage: k.<a> = NumberField(x^2 + 5) sage: N = k.ideal(3) sage: L = Gamma0_NFCusps(N) The cusps in the list are inequivalent: :: sage: all([not L[i].is_Gamma0_equivalent(L[j], N) for i, j in \ mrange([len(L), len(L)]) if i<j]) True We test that we obtain the right number of orbits: :: sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: len(L) == number_of_Gamma0_NFCusps(N) True Another example: :: sage: k.<a> = NumberField(x^4 - x^3 -21*x^2 + 17*x + 133) sage: N = k.ideal(5) sage: from sage.modular.cusps_nf import number_of_Gamma0_NFCusps sage: len(Gamma0_NFCusps(N)) == number_of_Gamma0_NFCusps(N) # long time (over 1 sec) True """ # We create L a list of three lists, which are different and each a list of # prime ideals, coprime to N, representing the ideal classes of k L = NFCusps_ideal_reps_for_levelN(N, nlists=3) Laux = L[1]+L[2] Lreps = list_of_representatives(N) Lcusps = [] k = N.number_field() for A in L[0]: #find B in inverse class: if A.is_trivial(): B = k.ideal(1) #B = k.unit_ideal() produces an error because we need fract ideal g = 1 else: Lbs = [P for P in Laux if (P*A).is_principal()] B = Lbs[0] g = (A*B).gens_reduced()[0] #for every divisor of N we have to find cusps from sage.rings.arith import divisors for d in divisors(N): #find delta prime coprime to B in inverse class of d*A #by searching in our list of auxiliary prime ideals Lds = [P for P in Laux if (P*d*A).is_principal() and P.is_coprime(B)] deltap = Lds[0] a = (deltap*d*A).gens_reduced()[0] I = d + N/d #especial case: A=B=d=<1>: if a.is_one() and I.is_trivial(): Lcusps.append(NFCusp(k, 0, 1, lreps=Lreps)) else: u = k.unit_group().gens() for b in I.invertible_residues_mod(u): #Note: if I trivial, invertible_residues_mod returns [1] #lift b to (R/a)star #we need the part of d which is coprime to I, call it M M = d.prime_to_idealM_part(I) deltAM = deltap*A*M u = (B*deltAM).element_1_mod(I) v = (I*B).element_1_mod(deltAM) newb = u*b + v #build AB-matrix: #----> extended gcd for k.ideal(a), k.ideal(newb) Y = k.ideal(newb).element_1_mod(k.ideal(a)) # if xa + yb = 1, cusp = y*g /a Lcusps.append(NFCusp(k, Y*g, a, lreps=Lreps)) return Lcusps
def _coset_reduction_data_first_coord(G): """ Compute data used for determining the canonical coset representative of an element of SL_2(Z) modulo G. This function specifically returns data needed for the first part of the reduction step (the first coordinate). INPUT: G -- a congruence subgroup Gamma_0(N), Gamma_1(N), or Gamma_H(N). OUTPUT: A list v such that v[u] = (min(u*h: h in H), gcd(u,N) , an h such that h*u = min(u*h: h in H)). EXAMPLES:: sage: G = Gamma0(12) sage: sage.modular.arithgroup.congroup_gammaH.GammaH_class._coset_reduction_data_first_coord(G) [(0, 12, 0), (1, 1, 1), (2, 2, 1), (3, 3, 1), (4, 4, 1), (1, 1, 5), (6, 6, 1), (1, 1, 7), (4, 4, 5), (3, 3, 7), (2, 2, 5), (1, 1, 11)] """ H = [ int(x) for x in G._list_of_elements_in_H() ] N = int(G.level()) # Get some useful fast functions for inverse and gcd inverse_mod = get_inverse_mod(N) # optimal inverse function gcd = get_gcd(N) # optimal gcd function # We will be filling this list in below. reduct_data = [0] * N # We can fill in 0 and all elements of H immediately reduct_data[0] = (0,N,0) for u in H: reduct_data[u] = (1, 1, inverse_mod(u, N)) # Make a table of the reduction of H (mod N/d), one for each # divisor d. repr_H_mod_N_over_d = {} for d in divisors(N): # We special-case N == d because in this case, # 1 % N_over_d is 0 if N == d: repr_H_mod_N_over_d[d] = [1] break N_over_d = N//d # For each element of H, we look at its image mod # N_over_d. If we haven't yet seen it, add it on to # the end of z. w = [0] * N_over_d z = [1] for x in H: val = x%N_over_d if not w[val]: w[val] = 1 z.append(x) repr_H_mod_N_over_d[d] = z # Compute the rest of the tuples. The values left to process # are those where reduct_data has a 0. Note that several of # these values are processed on each loop below, so re-index # each time. while True: try: u = reduct_data.index(0) except ValueError: break d = gcd(u, N) for x in repr_H_mod_N_over_d[d]: reduct_data[(u*x)%N] = (u, d, inverse_mod(x,N)) return reduct_data
def enumerate_totallyreal_fields_all(n, B, verbose=0, return_seqs=False): r""" Enumerates *all* totally real fields of degree `n` with discriminant `\le B`, primitive or otherwise. EXAMPLES:: sage: enumerate_totallyreal_fields_all(4, 2000) [[725, x^4 - x^3 - 3*x^2 + x + 1], [1125, x^4 - x^3 - 4*x^2 + 4*x + 1], [1600, x^4 - 6*x^2 + 4], [1957, x^4 - 4*x^2 - x + 1], [2000, x^4 - 5*x^2 + 5]] In practice most of these will be found by :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim`, which is guaranteed to return all primitive fields but often returns many non-primitive ones as well. For instance, only one of the five fields in the example above is primitive, but :func:`~sage.rings.number_field.totallyreal.enumerate_totallyreal_fields_prim` finds four out of the five (the exception being `x^4 - 6x^2 + 4`). TESTS: The following was fixed in :trac:`13101`:: sage: enumerate_totallyreal_fields_all(8, 10^6) # long time (about 2 s) [] """ S = [] counts = [0,0,0] if len(divisors(n)) > 4: raise ValueError, "Only implemented for n = p*q with p,q prime" for d in divisors(n): if d > 1 and d < n: Sds = enumerate_totallyreal_fields_prim(d, int(math.floor((1.*B)**(1.*d/n))), verbose=verbose) for i in range(len(Sds)): if verbose: print "="*80 print "Taking F =", Sds[i][1] F = NumberField(ZZx(Sds[i][1]), 't') T = enumerate_totallyreal_fields_rel(F, n/d, B, verbose=verbose, return_seqs=return_seqs) if return_seqs: for i in range(3): counts[i] += T[0][i] S += [[t[0],pari(t[1]).Polrev()] for t in T[1]] else: S += [[t[0],t[1]] for t in T] j = i+1 for E in enumerate_totallyreal_fields_prim(n/d, int(math.floor((1.*B)**(1./d)/(1.*Sds[i][0])**(n*1./d**2)))): for EF in F.composite_fields(NumberField(ZZx(E[1]), 'u')): if EF.degree() == n and EF.disc() <= B: S.append([EF.disc(), pari(EF.absolute_polynomial())]) S += enumerate_totallyreal_fields_prim(n, B, verbose=verbose) S.sort() weed_fields(S) # Output. if verbose: saveout = sys.stdout if type(verbose) == str: fsock = open(verbose, 'w') sys.stdout = fsock # Else, print to screen print "="*80 print "Polynomials tested:", counts[0] print "Irreducible polynomials:", counts[1] print "Polynomials with nfdisc <= B:", counts[2] for i in range(len(S)): print S[i] if type(verbose) == str: fsock.close() sys.stdout = saveout if return_seqs: return [counts,[[s[0],s[1].reverse().Vec()] for s in S]] else: return S
def additive_lift(self, forms, weight, with_character = False, is_integral = False) : """ Borcherds additive lift to hermitian modular forms of degree `2`. This coinsides with Gritsenko's arithmetic lift after using the theta decomposition. INPUT: - ``forms`` -- A list of functions accepting an integer and returning a q-expansion. - ``weight`` -- A positive integer; The weight of the lift. - ``with_character`` -- A boolean (default: ``False``); Whether the lift has nontrivial character. - ``is_integral`` -- A boolean (default: ``False``); If ``True`` use rings of integral q-expansions over `\Z`. ALGORITHME: We use the explicite formulas in [D]. TESTS:: sage: from hermitianmodularforms.hermitianmodularformd2_fegenerators import HermitianModularFormD2AdditiveLift sage: HermitianModularFormD2AdditiveLift(4, [1,0,0], -3, 4).coefficients() {(2, 3, 2, 2): 720, (1, 1, 1, 1): 27, (1, 0, 0, 2): 270, (3, 3, 3, 3): 2943, (2, 1, 1, 3): 2592, (0, 0, 0, 2): 9, (2, 2, 2, 2): 675, (2, 3, 2, 3): 2160, (1, 1, 1, 2): 216, (3, 0, 0, 3): 8496, (2, 0, 0, 3): 2214, (1, 0, 0, 3): 720, (2, 1, 1, 2): 1080, (0, 0, 0, 1): 1, (3, 3, 2, 3): 4590, (3, 1, 1, 3): 4590, (1, 1, 1, 3): 459, (2, 0, 0, 2): 1512, (1, 0, 0, 1): 72, (0, 0, 0, 0): 1/240, (3, 4, 3, 3): 2808, (0, 0, 0, 3): 28, (3, 2, 2, 3): 4752, (2, 2, 2, 3): 1350} sage: HermitianModularFormD2AdditiveLift(4, [0,1,0], -3, 6).coefficients() {(2, 3, 2, 2): -19680, (1, 1, 1, 1): -45, (1, 0, 0, 2): -3690, (3, 3, 3, 3): -306225, (2, 1, 1, 3): -250560, (0, 0, 0, 2): 33, (2, 2, 2, 2): -13005, (2, 3, 2, 3): -153504, (1, 1, 1, 2): -1872, (3, 0, 0, 3): -1652640, (2, 0, 0, 3): -295290, (1, 0, 0, 3): -19680, (2, 1, 1, 2): -43920, (0, 0, 0, 1): 1, (3, 3, 2, 3): -948330, (3, 1, 1, 3): -1285290, (1, 1, 1, 3): -11565, (2, 0, 0, 2): -65520, (1, 0, 0, 1): -240, (0, 0, 0, 0): -1/504, (3, 4, 3, 3): -451152, (0, 0, 0, 3): 244, (3, 2, 2, 3): -839520, (2, 2, 2, 3): -108090} """ if with_character and self.__D % 4 != 0 : raise ValueError( "Characters are only possible for even discriminants." ) ## This will be needed if characters are implemented if with_character : if (Integer(self.__D / 4) % 4) in [-2,2] : alpha = (-self.__D / 4, 1/2) else : alpha = (-self.__D / 8, 1/2) #minv = 1/2 if with_character else 1 R = self.power_series_ring() q = R.gen(0) (vv_expfactor, vv_basis) = self._additive_lift_vector_valued_basis() vvform = dict((self._reduce_vector_valued_index(k), R(0)) for (k,_) in self._semireduced_vector_valued_indices_with_discriminant_offset(1)) for (f,b) in zip(forms, vv_basis) : ## We have to apply the scaling of exponents to the form f = R( f(self._qexp_precision()) ).add_bigoh(self._qexp_precision()) \ .subs({q : q**vv_expfactor}) if not f.is_zero() : for (k,e) in b.iteritems() : vvform[k] = vvform[k] + e * f ## the T = matrix(2,[*, t / 2, \bar t / 2, *] th fourier coefficients of the lift ## only depends on (- 4 * D * det(T), eps = gcd(T), \theta \cong t / eps) ## if m != 1 we consider 2*T maass_coeffs = dict() ## TODO: use divisor dictionaries if not with_character : ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = vv_expfactor // (- self.__D) for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iterator_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else : ## The factor for the exponent of the basis of vector valued forms ## and the factor D in the formula for the discriminant are combined ## here vv_expfactor = (2 * vv_expfactor) // (- self.__D) if self.__D // 4 % 2 == 0 : for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iter_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[0] + theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps)) else : for eps in self._iterator_content() : for (theta, offset) in self._semireduced_vector_valued_indices_with_discriminant_offset(eps) : for disc in self._iter_discriminant(eps, offset) : maass_coeffs[(disc, eps, theta)] = \ sum( a**(weight-1) * (1 if (theta[1] - 1) % 4 == 0 else -1) * vvform[self._reduce_vector_valued_index((theta[0]//a, theta[1]//a))][vv_expfactor * disc // a**2] for a in divisors(eps) ) lift_coeffs = dict() ## TODO: Check whether this is correct. Add the character as an argument. for ((a,b1,b2,c), eps, disc) in self.precision().iter_positive_forms_for_character_with_content_and_discriminant(for_character = with_character) : (theta1, theta2) = self._reduce_vector_valued_index((b1/eps, b2/eps)) theta = (eps * theta1, eps * theta2) try: lift_coeffs[(a,b1,b2,c)] = maass_coeffs[(disc, eps, theta)] except : raise RuntimeError(str((a,b1,b2,c)) + " ; " + str((disc, eps, theta))) # Eisenstein component for (_,_,_,c) in self.precision().iter_semidefinite_forms_for_character(for_character = with_character) : if c != 0 : lift_coeffs[(0,0,0,c)] = vvform[(0,0)][0] * sigma(c, weight - 1) lift_coeffs[(0,0,0,0)] = - vvform[(0,0)][0] * bernoulli(weight) / Integer(2 * weight) if is_integral : lift_coeffs[(0,0,0,0)] = ZZ(lift_coeffs[(0,0,0,0)]) return lift_coeffs