def graded_dimension(self, k): r""" Return the dimension of the ``k``-th graded piece of ``self``. The `k`-th graded part of a free Lie algebra on `n` generators has dimension .. MATH:: \frac{1}{k} \sum_{d \mid k} \mu(d) n^{k/d}, where `\mu` is the Mobius function. REFERENCES: [MKO1998]_ EXAMPLES:: sage: L = LieAlgebra(QQ, 'x', 3) sage: H = L.Hall() sage: [H.graded_dimension(i) for i in range(1, 11)] [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880] sage: H.graded_dimension(0) 0 """ if k == 0: return 0 from sage.arith.all import moebius s = len(self.lie_algebra_generators()) k = ZZ(k) # Make sure we have something that is in ZZ return sum(moebius(d) * s**(k // d) for d in k.divisors()) // k
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.linear_combination( (self._p([d]), moebius(k // d)) for d in divisors(k))
def cardinality(self): """ Returns the number of Lyndon words with the evaluation e. EXAMPLES:: sage: LyndonWords([]).cardinality() 0 sage: LyndonWords([2,2]).cardinality() 1 sage: LyndonWords([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: lws = [LyndonWords(comp) for comp in comps] sage: all(lw.cardinality() == len(lw.list()) for lw in lws) True """ evaluation = self._e le = builtins.list(evaluation) if len(evaluation) == 0: return 0 n = sum(evaluation) return sum([ moebius(j) * factorial(n / j) / prod([factorial(ni / j) for ni in evaluation]) for j in divisors(gcd(le)) ]) / n
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.linear_combination((self._p([d]),moebius(k//d)) for d in divisors(k))
def _cycle_type(self, s): """ EXAMPLES:: sage: cis = species.PartitionSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]] sage: cis = species.PermutationSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[3, 1, 1, 1], [2, 2, 1, 1], [1, 1, 1, 1, 1, 1]] sage: cis = species.SetSpecies().cycle_index_series() sage: [cis._cycle_type(p) for p in Partitions(3)] [[1], [1], [1]] """ if s == []: return self._card(0) res = [] for k in range(1, self._upper_bound_for_longest_cycle(s)+1): e = 0 for d in divisors(k): m = moebius(d) if m == 0: continue u = s.power(k/d) e += m*self.count(u) res.extend([k]*int(e/k)) res.reverse() return Partition(res)
def cardinality(self): """ Returns the number of Lyndon words with the evaluation e. EXAMPLES:: sage: LyndonWords([]).cardinality() 0 sage: LyndonWords([2,2]).cardinality() 1 sage: LyndonWords([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: lws = [LyndonWords(comp) for comp in comps] sage: all(lw.cardinality() == len(lw.list()) for lw in lws) True """ evaluation = self._e le = builtins.list(evaluation) if len(evaluation) == 0: return 0 n = sum(evaluation) return sum([moebius(j)*factorial(n/j) / prod([factorial(ni/j) for ni in evaluation]) for j in divisors(gcd(le))])/n
def cardinality(self): """ Return the number of Lyndon words with the evaluation e. EXAMPLES:: sage: LyndonWords([]).cardinality() 0 sage: LyndonWords([2,2]).cardinality() 1 sage: LyndonWords([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: lws = [LyndonWords(comp) for comp in comps] sage: all(lw.cardinality() == len(lw.list()) for lw in lws) True """ evaluation = self._e le = list(evaluation) if not evaluation: return Integer(0) n = sum(evaluation) return sum( moebius(j) * multinomial([ni // j for ni in evaluation]) for j in divisors(gcd(le))) // n
def cardinality(self): """ TESTS:: sage: [ LyndonWords(3,i).cardinality() for i in range(1, 11) ] [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880] """ if self._k == 0: return Integer(1) else: s = Integer(0) for d in divisors(self._k): s += moebius(d)*(self._n**(self._k/d)) return s/self._k
def cardinality(self): """ TESTS:: sage: [ LyndonWords(3,i).cardinality() for i in range(1, 11) ] [3, 3, 8, 18, 48, 116, 312, 810, 2184, 5880] """ if self._k == 0: return Integer(1) else: s = Integer(0) for d in divisors(self._k): s += moebius(d) * (self._n**(self._k / d)) return s / self._k
def dimension_new_cusp_forms(self, k=2, p=0): r""" Return the dimension of the space of new (or `p`-new) weight `k` cusp forms for this congruence subgroup. INPUT: - `k` -- an integer (default: 2), the weight. Not fully implemented for `k = 1`. - `p` -- integer (default: 0); if nonzero, compute the `p`-new subspace. OUTPUT: Integer ALGORITHM: This comes from the formula given in Theorem 1 of http://www.math.ubc.ca/~gerg/papers/downloads/DSCFN.pdf EXAMPLES:: sage: Gamma0(11000).dimension_new_cusp_forms() 240 sage: Gamma0(11000).dimension_new_cusp_forms(k=1) 0 sage: Gamma0(22).dimension_new_cusp_forms(k=4) 3 sage: Gamma0(389).dimension_new_cusp_forms(k=2,p=17) 32 TESTS:: sage: L = [1213, 1331, 2169, 2583, 2662, 2745, 3208, ....: 3232, 3465, 3608, 4040, 4302, 4338] sage: all(Gamma0(N).dimension_new_cusp_forms(2)==100 for N in L) True """ from sage.arith.all import moebius from sage.functions.other import floor N = self.level() k = ZZ(k) if not (p == 0 or N % p): return (self.dimension_cusp_forms(k) - 2 * self.restrict(N // p).dimension_new_cusp_forms(k)) if k < 2 or k % 2: return ZZ.zero() factors = list(N.factor()) def s0(q, a): # function s_0^# if a == 1: return 1 - 1 / q elif a == 2: return 1 - 1 / q - 1 / q**2 else: return (1 - 1 / q) * (1 - 1 / q**2) def vinf(q, a): # function v_oo^# if a % 2: return 0 elif a == 2: return q - 2 else: return q**(a / 2 - 2) * (q - 1)**2 def v2(q, a): # function v_2^# if q % 4 == 1: if a == 2: return -1 else: return 0 elif q % 4 == 3: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 def v3(q, a): # function v_3^# if q % 3 == 1: if a == 2: return -1 else: return 0 elif q % 3 == 2: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 res = (k - 1) / 12 * N * prod(s0(q, a) for q, a in factors) res -= prod(vinf(q, a) for q, a in factors) / ZZ(2) res += ( (1 - k) / 4 + floor(k / 4)) * prod(v2(q, a) for q, a in factors) res += ( (1 - k) / 3 + floor(k / 3)) * prod(v3(q, a) for q, a in factors) if k == 2: res += moebius(N) return res
def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of Eisenstein series forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of Eisenstein series of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: - William Stein - Cohen--Oesterle algorithm - Jordi Quer - algorithm based on GammaH subgroups - David Loeffler (2009) - code refactoring EXAMPLES: The following two computations use different algorithms: :: sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] So do these: :: sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] """ from all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_eis(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if eps.is_trivial(): return Gamma0(N).dimension_eis(k) # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid: if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_eis(k) return dim//phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle j = 2-k # We use the Cohen-Oesterle formula in a subtle way to # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on # computing with modular forms). alpha = -ZZ( K(Gamma0(N).index()*(j-1)/ZZ(12)) + CohenOesterle(eps,j) ) if k == 1: return alpha else: return alpha - self.dimension_cusp_forms(k, eps) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis")
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method: :: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples: :: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] """ from all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if K.characteristic() != 0: raise NotImplementedError('dimension_cusp_forms() is only implemented for rings of characteristic 0') if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k%2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: try: n = self.dimension_cusp_forms(1) if n == 0: return ZZ(0) else: # never happens at present raise NotImplementedError("Computations of dimensions of spaces of weight 1 cusp forms not implemented at present") except NotImplementedError: raise # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N,(eps**d).kernel()) dim = dim + moebius(d)*G.dimension_cusp_forms(k) return dim//phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k-1)/ZZ(12)) + CohenOesterle(eps,k) ) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms")
def dimension_cusp_forms(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of cusp forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of forms of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). Ignored for weight 1. EXAMPLES: We compute the same dimension in two different ways :: sage: K = CyclotomicField(3) sage: eps = DirichletGroup(7*43,K).0^2 sage: G = Gamma1(7*43) Via Cohen--Oesterle:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps) 28 Via Quer's method:: sage: Gamma1(7*43).dimension_cusp_forms(2, eps, algorithm="Quer") 28 Some more examples:: sage: G.<eps> = DirichletGroup(9) sage: [Gamma1(9).dimension_cusp_forms(k, eps) for k in [1..10]] [0, 0, 1, 0, 3, 0, 5, 0, 7, 0] sage: [Gamma1(9).dimension_cusp_forms(k, eps^2) for k in [1..10]] [0, 0, 0, 2, 0, 4, 0, 6, 0, 8] In weight 1, we can sometimes rule out cusp forms existing via Riemann-Roch, but if this does not work, we trigger computation of the cusp forms space via Schaeffer's algorithm:: sage: chi = [u for u in DirichletGroup(40) if u(-1) == -1 and u(21) == 1][0] sage: Gamma1(40).dimension_cusp_forms(1, chi) 0 sage: G = DirichletGroup(57); chi = (G.0) * (G.1)^6 sage: Gamma1(57).dimension_cusp_forms(1, chi) 1 """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_cusp_forms(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if K.characteristic() != 0: raise NotImplementedError( 'dimension_cusp_forms() is only implemented for rings of characteristic 0' ) if eps.is_trivial(): return Gamma0(N).dimension_cusp_forms(k) if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0 and eps.is_odd()): return ZZ(0) if k == 1: from sage.modular.modform.weight1 import dimension_wt1_cusp_forms return dimension_wt1_cusp_forms(eps) # now the main part if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N, (eps**d).kernel()) dim = dim + moebius(d) * G.dimension_cusp_forms(k) return dim // phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle return ZZ( K(Gamma0(N).index() * (k - 1) / ZZ(12)) + CohenOesterle(eps, k)) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_cusp_forms")
def dimension_eis(self, k=2, eps=None, algorithm="CohenOesterle"): r""" Return the dimension of the space of Eisenstein series forms for self, or the dimension of the subspace corresponding to the given character if one is supplied. INPUT: - ``k`` - an integer (default: 2), the weight. - ``eps`` - either None or a Dirichlet character modulo N, where N is the level of this group. If this is None, then the dimension of the whole space is returned; otherwise, the dimension of the subspace of Eisenstein series of character eps. - ``algorithm`` -- either "CohenOesterle" (the default) or "Quer". This specifies the method to use in the case of nontrivial character: either the Cohen--Oesterle formula as described in Stein's book, or by Möbius inversion using the subgroups GammaH (a method due to Jordi Quer). AUTHORS: - William Stein - Cohen--Oesterle algorithm - Jordi Quer - algorithm based on GammaH subgroups - David Loeffler (2009) - code refactoring EXAMPLES: The following two computations use different algorithms:: sage: [Gamma1(36).dimension_eis(1,eps) for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] sage: [Gamma1(36).dimension_eis(1,eps,algorithm="Quer") for eps in DirichletGroup(36)] [0, 4, 3, 0, 0, 2, 6, 0, 0, 2, 3, 0] So do these:: sage: [Gamma1(48).dimension_eis(3,eps) for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] sage: [Gamma1(48).dimension_eis(3,eps,algorithm="Quer") for eps in DirichletGroup(48)] [0, 12, 0, 4, 0, 8, 0, 4, 12, 0, 4, 0, 8, 0, 4, 0] """ from .all import Gamma0 # first deal with special cases if eps is None: return GammaH_class.dimension_eis(self, k) N = self.level() K = eps.base_ring() eps = DirichletGroup(N, K)(eps) if eps.is_trivial(): return Gamma0(N).dimension_eis(k) # Note case of k = 0 and trivial character already dealt with separately, so k <= 0 here is valid: if (k <= 0) or ((k % 2) == 1 and eps.is_even()) or ((k % 2) == 0 and eps.is_odd()): return ZZ(0) if algorithm == "Quer": n = eps.order() dim = ZZ(0) for d in n.divisors(): G = GammaH_constructor(N, (eps**d).kernel()) dim = dim + moebius(d) * G.dimension_eis(k) return dim // phi(n) elif algorithm == "CohenOesterle": from sage.modular.dims import CohenOesterle j = 2 - k # We use the Cohen-Oesterle formula in a subtle way to # compute dim M_k(N,eps) (see Ch. 6 of William Stein's book on # computing with modular forms). alpha = -ZZ( K(Gamma0(N).index() * (j - 1) / ZZ(12)) + CohenOesterle(eps, j)) if k == 1: return alpha else: return alpha - self.dimension_cusp_forms(k, eps) else: #algorithm not in ["CohenOesterle", "Quer"]: raise ValueError("Unrecognised algorithm in dimension_eis")
def dimension_new_cusp_forms(self, k=2, p=0): r""" Return the dimension of the space of new (or `p`-new) weight `k` cusp forms for this congruence subgroup. INPUT: - `k` -- an integer (default: 2), the weight. Not fully implemented for `k = 1`. - `p` -- integer (default: 0); if nonzero, compute the `p`-new subspace. OUTPUT: Integer ALGORITHM: This comes from the formula given in Theorem 1 of http://www.math.ubc.ca/~gerg/papers/downloads/DSCFN.pdf EXAMPLES:: sage: Gamma0(11000).dimension_new_cusp_forms() 240 sage: Gamma0(11000).dimension_new_cusp_forms(k=1) 0 sage: Gamma0(22).dimension_new_cusp_forms(k=4) 3 sage: Gamma0(389).dimension_new_cusp_forms(k=2,p=17) 32 TESTS:: sage: L = [1213, 1331, 2169, 2583, 2662, 2745, 3208, ....: 3232, 3465, 3608, 4040, 4302, 4338] sage: all(Gamma0(N).dimension_new_cusp_forms(2)==100 for N in L) True """ from sage.arith.all import moebius from sage.functions.other import floor N = self.level() k = ZZ(k) if not(p == 0 or N % p): return (self.dimension_cusp_forms(k) - 2 * self.restrict(N // p).dimension_new_cusp_forms(k)) if k < 2 or k % 2: return ZZ.zero() factors = list(N.factor()) def s0(q, a): # function s_0^# if a == 1: return 1 - 1/q elif a == 2: return 1 - 1/q - 1/q**2 else: return (1 - 1/q) * (1 - 1/q**2) def vinf(q, a): # function v_oo^# if a % 2: return 0 elif a == 2: return q - 2 else: return q**(a/2 - 2) * (q - 1)**2 def v2(q, a): # function v_2^# if q % 4 == 1: if a == 2: return -1 else: return 0 elif q % 4 == 3: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 def v3(q, a): # function v_3^# if q % 3 == 1: if a == 2: return -1 else: return 0 elif q % 3 == 2: if a == 1: return -2 elif a == 2: return 1 else: return 0 elif a in (1, 2): return -1 elif a == 3: return 1 else: return 0 res = (k - 1) / 12 * N * prod(s0(q, a) for q, a in factors) res -= prod(vinf(q, a) for q, a in factors) / ZZ(2) res += ((1 - k)/4 + floor(k/4)) * prod(v2(q, a) for q, a in factors) res += ((1 - k)/3 + floor(k/3)) * prod(v3(q, a) for q, a in factors) if k == 2: res += moebius(N) return res