def iter_positive_forms_with_content(self): if self.__disc is infinity: raise ValueError, "infinity is not a true filter index" if self.__reduced: for a in xrange(1, isqrt(self.__disc // 3) + 1): for b in xrange(a + 1): g = gcd(a, b) for c in xrange(a, (b**2 + (self.__disc - 1)) // (4 * a) + 1): yield (a, b, c), gcd(g, c) else: maxtrace = floor(5 * self.__disc / 15 + sqrt(self.__disc) / 2) for a in xrange(1, maxtrace + 1): for c in xrange(1, maxtrace - a + 1): g = gcd(a, c) Bu = isqrt(4 * a * c - 1) di = 4 * a * c - self.__disc if di >= 0: Bl = isqrt(di) + 1 else: Bl = 0 for b in xrange(-Bu, -Bl + 1): yield (a, b, c), gcd(g, b) for b in xrange(Bl, Bu + 1): yield (a, b, c), gcd(g, b) #! if self.__reduced raise StopIteration
def iter_positive_forms_with_content(self) : if self.__disc is infinity : raise ValueError, "infinity is not a true filter index" if self.__reduced : for a in xrange(1,isqrt(self.__disc // 3) + 1) : for b in xrange(a+1) : g = gcd(a, b) for c in xrange(a, (b**2 + (self.__disc - 1))//(4*a) + 1) : yield (a,b,c), gcd(g,c) else : maxtrace = floor(5*self.__disc / 15 + sqrt(self.__disc)/2) for a in xrange(1, maxtrace + 1) : for c in xrange(1, maxtrace - a + 1) : g = gcd(a,c) Bu = isqrt(4*a*c - 1) di = 4*a*c - self.__disc if di >= 0 : Bl = isqrt(di) + 1 else : Bl = 0 for b in xrange(-Bu, -Bl + 1) : yield (a,b,c), gcd(g,b) for b in xrange(Bl, Bu + 1) : yield (a,b,c), gcd(g,b) #! if self.__reduced raise StopIteration
def order_at_cusp(self, cusp): r""" Return the order of vanishing of self at the given cusp. INPUT: - ``cusp`` - a CuspFamily object OUTPUT: - an integer EXAMPLES:: sage: e = EtaProduct(2, {2:24, 1:-24}) sage: e.order_at_cusp(CuspFamily(2, 1)) # cusp at infinity 1 sage: e.order_at_cusp(CuspFamily(2, 2)) # cusp 0 -1 """ if not isinstance(cusp, CuspFamily): raise TypeError, "Argument (=%s) should be a CuspFamily" % cusp if cusp.level() != self.level(): raise ValueError, "Cusp not on right curve!" return 1 / ZZ(24) / gcd(cusp.width(), self.level() // cusp.width()) * sum([ ell * self.r(ell) / cusp.width() * (gcd(cusp.width(), self.level() // ell))**2 for ell in self._keys ])
def circle_image(A, B): G = Graphics() G += circle((0, 0), 1, color='grey') from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j, a) == 1: rational = Rational(j) / Rational(a) tmp[(rational.numerator(), rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j, b) == 1: rational = Rational(j) / Rational(b) tmp[(rational.numerator(), rational.denominator())] -= 1 C = ComplexField() for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]), exp(C(-.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="green") if tmp[val] < 0: G += text(str(abs(tmp[val])), exp(C(.2 + 2 * 3.14159 * I * val[0] / val[1])), fontsize=30, axes=False, color="blue") return G
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 `\mathbf{P}^1(\QQ)`. These are returned in a reduced form; see self.reduce_cusp for the definition of reduced. ALGORITHM: Lemma 3.2 in Cremona's 1997 book shows that for the action of Gamma1(N) on "signed projective space" `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2` if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod gcd(v_1, N)`. It follows that every orbit has a representative `u/v` with `v \le N` and `0 \le u \le gcd(v, N)`. We iterate through all pairs `(u,v)` satisfying this. Having found a set containing at least one of every equivalence class modulo Gamma1(N), we can be sure of picking up every class modulo GammaH(N) since this contains Gamma1(N); and the reduce_cusp call does the checking to make sure we don't get any duplicates. EXAMPLES:: sage: Gamma1(5)._find_cusps() [0, 2/5, 1/2, Infinity] sage: Gamma1(35)._find_cusps() [0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity] sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym') True sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym') True """ s = [] hashes = [] N = self.level() for d in xrange(1, 1 + N): w = N.gcd(d) M = int(w) if w > 1 else 2 for a in xrange(1, M): if gcd(a, w) != 1: continue while gcd(a, d) != 1: a += w c = self.reduce_cusp(Cusp(a, d)) h = hash(c) if not h in hashes: hashes.append(h) s.append(c) 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 `\mathbf{P}^1(\QQ)`. These are returned in a reduced form; see self.reduce_cusp for the definition of reduced. ALGORITHM: Lemma 3.2 in Cremona's 1997 book shows that for the action of Gamma1(N) on "signed projective space" `\Q^2 / (\Q_{\geq 0}^+)`, we have `u_1/v_1 \sim u_2 / v_2` if and only if `v_1 = v_2 \bmod N` and `u_1 = u_2 \bmod gcd(v_1, N)`. It follows that every orbit has a representative `u/v` with `v \le N` and `0 \le u \le gcd(v, N)`. We iterate through all pairs `(u,v)` satisfying this. Having found a set containing at least one of every equivalence class modulo Gamma1(N), we can be sure of picking up every class modulo GammaH(N) since this contains Gamma1(N); and the reduce_cusp call does the checking to make sure we don't get any duplicates. EXAMPLES:: sage: Gamma1(5)._find_cusps() [0, 2/5, 1/2, Infinity] sage: Gamma1(35)._find_cusps() [0, 2/35, 1/17, 1/16, 1/15, 1/14, 1/13, 1/12, 3/35, 1/11, 1/10, 1/9, 4/35, 1/8, 2/15, 1/7, 1/6, 6/35, 1/5, 3/14, 8/35, 1/4, 9/35, 4/15, 2/7, 3/10, 11/35, 1/3, 12/35, 5/14, 13/35, 2/5, 3/7, 16/35, 17/35, 1/2, 8/15, 4/7, 3/5, 9/14, 7/10, 5/7, 11/14, 4/5, 6/7, 9/10, 13/14, Infinity] sage: Gamma1(24)._find_cusps() == Gamma1(24).cusps(algorithm='modsym') True sage: GammaH(24, [13,17])._find_cusps() == GammaH(24,[13,17]).cusps(algorithm='modsym') True """ s = [] hashes = [] N = self.level() for d in xrange(1, 1+N): w = N.gcd(d) M = int(w) if w > 1 else 2 for a in xrange(1,M): if gcd(a, w) != 1: continue while gcd(a, d) != 1: a += w c = self.reduce_cusp(Cusp(a,d)) h = hash(c) if not h in hashes: hashes.append(h) s.append(c) return sorted(s)
def _goodness(n, R, p): """ Return the goodness of ``n`` for the sequence ``R`` and the prime ``p`` -- that is the largest non-``p`` prime power dividing ``period(n)``. INPUT: - ``n`` -- an integer - ``R`` -- an object in the class ``BinaryRecurrenceSequence`` - ``p`` -- a rational prime OUTPUT: - An integer which is the "goodness" of ``n``, i.e. the largest non-``p`` prime power dividing ``period(n)``. EXAMPLES:: sage: R = BinaryRecurrenceSequence(11,2) sage: sage.combinat.binary_recurrence_sequences._goodness(89,R,7) 11 sage: R = BinaryRecurrenceSequence(1,1) sage: sage.combinat.binary_recurrence_sequences._goodness(13,R,7) 4 sage: R.period(13) #the period of R mod 13 is divisible by 7 28 """ #The period of R mod ell K = R.period(n) return _largest_ppower_divisor(K / gcd(K, p))
def _test__jacobi_predicted_taylor_coefficients(fs, q_precision) : r""" Given a list of power series, which are the corrected Taylor coefficients of a Jacobi form, return the renormalized uncorrected ones, assuming that all but one `f` vanish. INPUT: - ``fs`` -- A list of power series. - ``q_precision`` -- An integer. OUPUT: - A list of power series. TESTS: See jacobi_form_by_taylor_expansion. """ from sage.rings.arith import gcd R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0) diff = lambda f: f.derivative().shift(1) normalize = lambda f: f / gcd(f.list()) if f != 0 else f diffnorm = lambda f,l: normalize(reduce(lambda a, g: g(a), l*[diff], f)) taylor_coefficients = list() allf = R(0) for f in fs : allf = f(q_precision) + diffnorm(allf, 1) taylor_coefficients.append(allf) return taylor_coefficients
def random_element(self, bound=100, *args, **kwds): r""" Return a random element of `{\rm SL}_2(\ZZ)` with entries whose absolute value is strictly less than bound (default 100). Additional arguments and keywords are passed to the random_element method of ZZ. (Algorithm: Generate a random pair of integers at most bound. If they are not coprime, throw them away and start again. If they are, find an element of `{\rm SL}_2(\ZZ)` whose bottom row is that, and left-multiply it by `\begin{pmatrix} 1 & w \\ 0 & 1\end{pmatrix}` for an integer `w` randomly chosen from a small enough range that the answer still has entries at most bound.) It is, unfortunately, not true that all elements of SL2Z with entries < bound appear with equal probability; those with larger bottom rows are favoured, because there are fewer valid possibilities for w. EXAMPLES:: sage: SL2Z.random_element() [60 13] [83 18] sage: SL2Z.random_element(5) [-1 3] [ 1 -4] Passes extra positional or keyword arguments through:: sage: SL2Z.random_element(5, distribution='1/n') [ 1 -4] [ 0 1] """ if bound <= 1: raise ValueError("bound must be greater than 1") c = ZZ.random_element(1-bound, bound, *args, **kwds) d = ZZ.random_element(1-bound, bound, *args, **kwds) if gcd(c,d) != 1: # try again return self.random_element(bound, *args, **kwds) else: a,b,c,d = lift_to_sl2z(c,d,0) whi = bound wlo = bound if c > 0: whi = min(whi, ((bound - a)/ZZ(c)).ceil()) wlo = min(wlo, ((bound + a)/ZZ(c)).ceil()) elif c < 0: whi = min(whi, ((bound + a)/ZZ(-c)).ceil()) wlo = min(wlo, ((bound - a)/ZZ(-c)).ceil()) if d > 0: whi = min(whi, ((bound - b)/ZZ(d)).ceil()) wlo = min(wlo, ((bound + b)/ZZ(d)).ceil()) elif d < 0: whi = min(whi, ((bound + b)/ZZ(-d)).ceil()) wlo = min(wlo, ((bound - b)/ZZ(-d)).ceil()) w = ZZ.random_element(1-wlo, whi, *args, **kwds) a += c*w b += d*w return self([a,b,c,d])
def eval(self, expansion, weight = None) : if weight is None : try : weight = expansion.weight() except AttributeError : raise ValueError, "weight must be defined for the Hecke action" precision = expansion.precision() if precision.is_infinite() : precision = expansion._bounding_precision() else : precision = precision._hecke_operator(self.__l) characters = expansion.non_zero_components() expansion_level = precision.level() if gcd(expansion_level, self.__l) != 1 : raise ValueError, "Level of expansion and of Hecke operator must be coprime." hecke_expansion = dict() for ch in characters : hecke_expansion[ch] = dict( (k, self.hecke_coeff(expansion, ch, k, weight, expansion_level)) for k in precision ) result = expansion.parent()._element_constructor_(hecke_expansion) result._set_precision(expansion.precision()._hecke_operator(self.__l)) return result
def random_element(self, bound=100, *args, **kwds): r""" Return a random element of `{\rm SL}_2(\ZZ)` with entries whose absolute value is strictly less than bound (default 100). Additional arguments and keywords are passed to the random_element method of ZZ. (Algorithm: Generate a random pair of integers at most bound. If they are not coprime, throw them away and start again. If they are, find an element of `{\rm SL}_2(\ZZ)` whose bottom row is that, and left-multiply it by `\begin{pmatrix} 1 & w \\ 0 & 1\end{pmatrix}` for an integer `w` randomly chosen from a small enough range that the answer still has entries at most bound.) It is, unfortunately, not true that all elements of SL2Z with entries < bound appear with equal probability; those with larger bottom rows are favoured, because there are fewer valid possibilities for w. EXAMPLES:: sage: SL2Z.random_element() [60 13] [83 18] sage: SL2Z.random_element(5) [-1 3] [ 1 -4] Passes extra positional or keyword arguments through:: sage: SL2Z.random_element(5, distribution='1/n') [ 1 -4] [ 0 1] """ if bound <= 1: raise ValueError("bound must be greater than 1") c = ZZ.random_element(1 - bound, bound, *args, **kwds) d = ZZ.random_element(1 - bound, bound, *args, **kwds) if gcd(c, d) != 1: # try again return self.random_element(bound, *args, **kwds) else: a, b, c, d = lift_to_sl2z(c, d, 0) whi = bound wlo = bound if c > 0: whi = min(whi, ((bound - a) / ZZ(c)).ceil()) wlo = min(wlo, ((bound + a) / ZZ(c)).ceil()) elif c < 0: whi = min(whi, ((bound + a) / ZZ(-c)).ceil()) wlo = min(wlo, ((bound - a) / ZZ(-c)).ceil()) if d > 0: whi = min(whi, ((bound - b) / ZZ(d)).ceil()) wlo = min(wlo, ((bound + b) / ZZ(d)).ceil()) elif d < 0: whi = min(whi, ((bound + b) / ZZ(-d)).ceil()) wlo = min(wlo, ((bound - b) / ZZ(-d)).ceil()) w = ZZ.random_element(1 - wlo, whi, *args, **kwds) a += c * w b += d * w return self([a, b, c, d])
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] """ try: N = ZZ(N) d = ZZ(d) assert N>0 assert d>0 assert ((N % d) == 0) except TypeError: raise TypeError, "N and d must be integers" except AssertionError: raise AssertionError, "N and d must be positive integers with d|N" return euler_phi(gcd(d, N//d))
def get_representatives( self, t, N) : r""" A helper function used in hecke_coeff that computes the right coset representatives of $\Gamma^0(t) \cap \Gamma_0(N) \ Gamma_0(N)$ where $\Gamma^0(t)$ is the subgroup of $SL(2,Z)$ where the upper right hand corner is divisible by $t$. NOTE We use the bijection $\Gamma^0(t)\SL(2,Z) \rightarrow P^1(\Z/t\Z)$ given by $A \mapsto [1:0]A$. """ if t == 1 : return [(1,0,0,1)] rep_list = [] for (x,y) in P1List(t): ## we know that (N, x, y) = 1 N1 = gcd(N, x) if N1 != 1 : x = x + t * N // N1 ## we calculate a pair c,d satisfying a minimality condition ## to make later multiplications cheaper (_, d, c) = Integer(x)._xgcd(Integer(N * y), minimal=True) #print (x, y, -N * c, d) rep_list.append((x, y, -N * c, d)) return rep_list
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] """ try: N = ZZ(N) d = ZZ(d) assert N > 0 assert d > 0 assert ((N % d) == 0) except TypeError: raise TypeError, "N and d must be integers" except AssertionError: raise AssertionError, "N and d must be positive integers with d|N" return euler_phi(gcd(d, N // d))
def _normalize_H(H, level): """ Normalize representatives for a given subgroup H of the units modulo level. NOTE: This function does *not* make any attempt to find a minimal set of generators for H. It simply normalizes the inputs for use in hashing. EXAMPLES:: sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7) [5] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14) Traceback (most recent call last): ... ArithmeticError: The generators [4, 18] must be units modulo 14 sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10) [7, 9] """ H = [ZZ(h) for h in H] for h in H: if gcd(h, level) > 1: raise ArithmeticError, 'The generators %s must be units modulo %s' % ( H, level) H = list(set([h % level for h in H])) H.sort() if 1 in H: H.remove(1) return H
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 get_representatives(self, t, N): r""" A helper function used in hecke_coeff that computes the right coset representatives of $\Gamma^0(t) \cap \Gamma_0(N) \ Gamma_0(N)$ where $\Gamma^0(t)$ is the subgroup of $SL(2,Z)$ where the upper right hand corner is divisible by $t$. NOTE We use the bijection $\Gamma^0(t)\SL(2,Z) \rightarrow P^1(\Z/t\Z)$ given by $A \mapsto [1:0]A$. """ if t == 1: return [(1, 0, 0, 1)] rep_list = [] for (x, y) in P1List(t): ## we know that (N, x, y) = 1 N1 = gcd(N, x) if N1 != 1: x = x + t * N // N1 ## we calculate a pair c,d satisfying a minimality condition ## to make later multiplications cheaper (_, d, c) = Integer(x)._xgcd(Integer(N * y), minimal=True) #print (x, y, -N * c, d) rep_list.append((x, y, -N * c, d)) return rep_list
def _list_subgroup(N, gens): r""" Given an integer ``N`` and a list of integers ``gens``, return a list of the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the elements of ``gens``. EXAMPLE:: sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3]) [1, 3, 4, 5, 9] """ H = set([1]) N = int(N) for g in gens: if gcd(g, N) != 1: raise ValueError, "gen (=%s) is not in (Z/%sZ)^*" % (g, N) gk = int(g) % N sbgrp = [gk] while not (gk in H): gk = (gk * g) % N sbgrp.append(gk) H = set([(x * h) % N for x in sbgrp for h in H]) H = list(H) H.sort() return H
def _normalize_H(H, level): """ Normalize representatives for a given subgroup H of the units modulo level. NOTE: This function does *not* make any attempt to find a minimal set of generators for H. It simply normalizes the inputs for use in hashing. EXAMPLES:: sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([23], 10) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([1,5], 7) [5] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([4,18], 14) Traceback (most recent call last): ... ArithmeticError: The generators [4, 18] must be units modulo 14 sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([3,17], 14) [3] sage: sage.modular.arithgroup.congroup_gammaH._normalize_H([-1,7,9], 10) [7, 9] """ H = [ZZ(h) for h in H] for h in H: if gcd(h, level) > 1: raise ArithmeticError, 'The generators %s must be units modulo %s'%(H, level) H = list(set([h%level for h in H])) H.sort() if 1 in H: H.remove(1) return H
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 order_at_cusp(self, cusp): r""" Return the order of vanishing of self at the given cusp. INPUT: - ``cusp`` - a CuspFamily object OUTPUT: - an integer EXAMPLES:: sage: e = EtaProduct(2, {2:24, 1:-24}) sage: e.order_at_cusp(CuspFamily(2, 1)) # cusp at infinity 1 sage: e.order_at_cusp(CuspFamily(2, 2)) # cusp 0 -1 """ if not isinstance(cusp, CuspFamily): raise TypeError("Argument (=%s) should be a CuspFamily" % cusp) if cusp.level() != self.level(): raise ValueError("Cusp not on right curve!") return 1/ZZ(24)/gcd(cusp.width(), self.level()//cusp.width()) * sum( [ell*self.r(ell)/cusp.width() * (gcd(cusp.width(), self.level()//ell))**2 for ell in self._keys] )
def _list_subgroup(N, gens): r""" Given an integer ``N`` and a list of integers ``gens``, return a list of the elements of the subgroup of `(\ZZ / N\ZZ)^\times` generated by the elements of ``gens``. EXAMPLE:: sage: sage.modular.arithgroup.congroup_gammaH._list_subgroup(11, [3]) [1, 3, 4, 5, 9] """ H = set([1]) N = int(N) for g in gens: if gcd(g, N) != 1: raise ValueError, "gen (=%s) is not in (Z/%sZ)^*"%(g,N) gk = int(g) % N sbgrp = [gk] while not (gk in H): gk = (gk * g)%N sbgrp.append(gk) H = set([(x*h)%N for x in sbgrp for h in H]) H = list(H) H.sort() return H
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] sage: num_cusps_of_width(4,8) Traceback (most recent call last): ... ValueError: N and d must be positive integers with d|N """ N = ZZ(N) d = ZZ(d) if N <= 0 or d <= 0 or (N % d) != 0: raise ValueError("N and d must be positive integers with d|N") return euler_phi(gcd(d, N//d))
def num_cusps_of_width(N, d): r""" Return the number of cusps on `X_0(N)` of width d. INPUT: - ``N`` - (integer): the level - ``d`` - (integer): an integer dividing N, the cusp width EXAMPLES:: sage: [num_cusps_of_width(18,d) for d in divisors(18)] [1, 1, 2, 2, 1, 1] sage: num_cusps_of_width(4,8) Traceback (most recent call last): ... ValueError: N and d must be positive integers with d|N """ N = ZZ(N) d = ZZ(d) if N <= 0 or d <= 0 or (N % d) != 0: raise ValueError("N and d must be positive integers with d|N") return euler_phi(gcd(d, N // d))
def eval(self, expansion, weight=None): if weight is None: try: weight = expansion.weight() except AttributeError: raise ValueError, "weight must be defined for the Hecke action" precision = expansion.precision() if precision.is_infinite(): precision = expansion._bounding_precision() else: precision = precision._hecke_operator(self.__l) characters = expansion.non_zero_components() expansion_level = precision.level() if gcd(expansion_level, self.__l) != 1: raise ValueError, "Level of expansion and of Hecke operator must be coprime." hecke_expansion = dict() for ch in characters: hecke_expansion[ch] = dict( (k, self.hecke_coeff(expansion, ch, k, weight, expansion_level)) for k in precision) result = expansion.parent()._element_constructor_(hecke_expansion) result._set_precision(expansion.precision()._hecke_operator(self.__l)) return result
def circle_drops(A,B): # Drops going around the unit circle for those A and B. # See http://user.math.uzh.ch/dehaye/thesis_students/Nicolas_Wider-Integrality_of_factorial_ratios.pdf # for longer description (not original, better references exist) marks = lcm(lcm(A),lcm(B)) tmp = [0 for i in range(marks)] for a in A: # print tmp for i in range(a): if gcd(i, a) == 1: tmp[i*marks/a] -= 1 for b in B: # print tmp for i in range(b): if gcd(i, b) == 1: tmp[i*marks/b] += 1 # print tmp return [sum(tmp[:j]) for j in range(marks)]
def __init__(self, parent, polys, check=True): """ The Python constructor. See :class:`SchemeMorphism_polynomial` for details. EXAMPLES:: sage: A2.<x,y> = AffineSpace(QQ,2) sage: H = A2.Hom(A2) sage: H([x-y, x*y]) Scheme endomorphism of Affine Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x, y) to (x - y, x*y) """ if check: if not isinstance(polys, (list, tuple)): raise TypeError, "polys (=%s) must be a list or tuple" % polys source_ring = parent.domain().coordinate_ring() target = parent.codomain().ambient_space() if len(polys) != target.ngens(): raise ValueError, "there must be %s polynomials" % target.ngens( ) try: polys = [source_ring(poly) for poly in polys] except TypeError: raise TypeError, "polys (=%s) must be elements of %s" % ( polys, source_ring) from sage.rings.quotient_ring import QuotientRing_generic if isinstance(source_ring, QuotientRing_generic): lift_polys = [f.lift() for f in polys] else: lift_polys = polys from sage.schemes.generic.projective_space import is_ProjectiveSpace if is_ProjectiveSpace(target): # if the codomain is a subscheme of projective space, # then we need to make sure that polys have no common # zeros if isinstance(source_ring, QuotientRing_generic): # if the coordinate ring of the domain is a # quotient by an ideal, we need to check that the # gcd of polys and the generators of the ideal is 1 gcd_polys = lift_polys + list( source_ring.defining_ideal().gens()) else: # if the domain is affine space, we just need to # check the gcd of polys gcd_polys = polys from sage.rings.arith import gcd if gcd(gcd_polys) != 1: raise ValueError, "polys (=%s) must not have common factors" % polys polys = Sequence(lift_polys) polys.set_immutable() # Todo: check that map is well defined (how?) self.__polys = polys SchemeMorphism.__init__(self, parent)
def blift(LF,Li,p,S=None): r""" Search for a solution to the given list of inequalities. If found, lift the solution to an appropriate valuation. See Lemma 3.3.6 in [Molnar, M.Sc. thesis] INPUT: - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) - ``Li`` -- an integer, the bound on coefficients - ``p`` -- a prime OUTPUT: - Boolean -- whether or not the lift is successful - integer -- the lift EXAMPLES:: sage: R.<b> = PolynomialRing(QQ) sage: from sage.schemes.projective.endPN_minimal_model import blift sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3) (True, 4) """ P = LF[0].parent() #Determine which inequalities are trivial, and scale the rest, so that we only lift #as many times as needed. keepScaledIneqs = [scale(P(coeff),Li,p) for coeff in LF if coeff != 0] keptVals = [i[2] for i in keepScaledIneqs if i[0]] if keptVals != []: #Determine the valuation to lift until. liftval = max(keptVals) else: #All inequalities are satisfied. return True,1 if S is None: S = PolynomialRing(Zmod(p),'b') keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]] #We need a solution for each polynomial on the left hand side of the inequalities, #so we need only find a solution for their gcd. g = gcd(keptScaledIneqs) rts = g.roots(multiplicities=False) for r in rts: #Recursively try to lift each root r_initial=QQ(r) newInput = P([r_initial,p]) LG = [F(newInput) for F in LF] lift,lifted = blift(LG,Li,p,S=S) if lift: #Lift successful. return True,r_initial+ p*lifted #Lift non successful. return False,0
def blift(LF, Li, p, S=None): r""" Search for a solution to the given list of inequalities. If found, lift the solution to an appropriate valuation. See Lemma 3.3.6 in [Molnar, M.Sc. thesis] INPUT: - ``LF`` -- a list of integer polynomials in one variable (the normalized coefficients) - ``Li`` -- an integer, the bound on coefficients - ``p`` -- a prime OUTPUT: - Boolean -- whether or not the lift is successful - integer -- the lift EXAMPLES:: sage: R.<b> = PolynomialRing(QQ) sage: from sage.schemes.projective.endPN_minimal_model import blift sage: blift([8*b^3 + 12*b^2 + 6*b + 1, 48*b^2 + 483*b + 117, 72*b + 1341, -24*b^2 + 411*b + 99, -144*b + 1233, -216*b], 2, 3) (True, 4) """ P = LF[0].parent() #Determine which inequalities are trivial, and scale the rest, so that we only lift #as many times as needed. keepScaledIneqs = [scale(P(coeff), Li, p) for coeff in LF if coeff != 0] keptVals = [i[2] for i in keepScaledIneqs if i[0]] if keptVals != []: #Determine the valuation to lift until. liftval = max(keptVals) else: #All inequalities are satisfied. return True, 1 if S is None: S = PolynomialRing(Zmod(p), 'b') keptScaledIneqs = [S(i[1]) for i in keepScaledIneqs if i[0]] #We need a solution for each polynomial on the left hand side of the inequalities, #so we need only find a solution for their gcd. g = gcd(keptScaledIneqs) rts = g.roots(multiplicities=False) for r in rts: #Recursively try to lift each root r_initial = QQ(r) newInput = P([r_initial, p]) LG = [F(newInput) for F in LF] lift, lifted = blift(LG, Li, p, S=S) if lift: #Lift successful. return True, r_initial + p * lifted #Lift non successful. return False, 0
def arith_prod_of_partitions(l1, l2): # Given two partitions `l_1` and `l_2`, we construct a new partition `l_1 \\boxtimes l_2` by # the following procedure: each pair of parts `a \\in l_1` and `b \\in l_2` contributes # `\\gcd (a, b)`` parts of size `\\lcm (a, b)` to `l_1 \\boxtimes l_2`. If `l_1` and `l_2` # are partitions of integers `n` and `m`, respectively, then `l_1 \\boxtimes l_2` is a # partition of `nm`. term_iterable = chain.from_iterable( repeat(lcm(pair), times=gcd(pair)) for pair in product(l1, l2) ) term_list = sorted(term_iterable, reverse=True) res = Partition(term_list) return res
def __contains__(self, f): if self.__disc is infinity: return True (a, b, c) = f disc = 4 * a * c - b**2 if disc == 0: return gcd([a, b, c]) < self._indefinite_content_bound() else: return disc < self.__disc
def __contains__(self, f) : if self.__disc is infinity : return True (a, b, c) = f disc = 4*a*c - b**2 if disc == 0 : return gcd([a, b, c]) < self._indefinite_content_bound() else : return disc < self.__disc
def circle_drops(A, B): # Drops going around the unit circle for those A and B. # See http://user.math.uzh.ch/dehaye/thesis_students/Nicolas_Wider-Integrality_of_factorial_ratios.pdf # for longer description (not original, better references exist) marks = lcm(lcm(A), lcm(B)) tmp = [0 for i in range(marks)] for a in A: # print tmp for i in range(a): if gcd(i, a) == 1: tmp[i * marks / a] -= 1 for b in B: # print tmp for i in range(b): if gcd(i, b) == 1: tmp[i * marks / b] += 1 # print tmp return [sum(tmp[:j]) for j in range(marks)]
def _new_group_from_level(self, level): r""" Return a new group of the same type (Gamma0, Gamma1, or GammaH) as self of the given level. In the case that self is of type GammaH, we take the largest H inside `(\ZZ/ \text{level}\ZZ)^\times` which maps to H, namely its inverse image under the natural reduction map. EXAMPLES:: sage: G = Gamma0(20) sage: G._new_group_from_level(4) Congruence Subgroup Gamma0(4) sage: G._new_group_from_level(40) Congruence Subgroup Gamma0(40) sage: G = Gamma1(10) sage: G._new_group_from_level(6) Traceback (most recent call last): ... ValueError: one level must divide the other sage: G = GammaH(50,[7]); G Congruence Subgroup Gamma_H(50) with H generated by [7] sage: G._new_group_from_level(25) Congruence Subgroup Gamma_H(25) with H generated by [7] sage: G._new_group_from_level(10) Congruence Subgroup Gamma0(10) sage: G._new_group_from_level(100) Congruence Subgroup Gamma_H(100) with H generated by [7, 57] """ from congroup_gamma0 import is_Gamma0 from congroup_gamma1 import is_Gamma1 from congroup_gammaH import is_GammaH from all import Gamma0, Gamma1, GammaH N = self.level() if (level % N) and (N % level): raise ValueError, "one level must divide the other" if is_Gamma0(self): return Gamma0(level) elif is_Gamma1(self): return Gamma1(level) elif is_GammaH(self): H = self._generators_for_H() if level > N: d = level // N diffs = [N * i for i in range(d)] newH = [h + diff for h in H for diff in diffs] return GammaH(level, [x for x in newH if gcd(level, x) == 1]) else: return GammaH(level, [h % level for h in H]) else: raise NotImplementedError
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 eval_twisted_symbol_on_Da(self, a): # rename! should this be in modsym? """ Returns `\Phi_{\chi}(\{a/p}-{\infty})` where `Phi` is the OMS and `\chi` is a the quadratic character corresponding to self INPUT: - ``a`` -- integer in range(p) OUTPUT: The distribution `\Phi_{\chi}(\{a/p\}-\{\infty\})`. EXAMPLES: sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve sage: E = EllipticCurve('57a') sage: p = 5 sage: prec = 4 sage: phi = ps_modsym_from_elliptic_curve(E) sage: ap = phi.Tq_eigenvalue(p,prec) sage: Phi = phi.p_stabilize_and_lift(p,ap = ap, M = prec, algorithm='stevens') sage: L = pAdicLseries(Phi) sage: L.eval_twisted_symbol_on_Da(1) (2 + 2*5 + 2*5^2 + 2*5^3 + O(5^4), 2 + 3*5 + 2*5^2 + O(5^3), 4*5 + O(5^2), 3 + O(5)) sage: from sage.modular.pollack_stevens.space import ps_modsym_from_elliptic_curve sage: E = EllipticCurve('40a4') sage: p = 7 sage: prec = 4 sage: phi = ps_modsym_from_elliptic_curve(E) sage: ap = phi.Tq_eigenvalue(p,prec) sage: Phi = phi.p_stabilize_and_lift(p,ap = ap, M = prec, algorithm='stevens') sage: L = pAdicLseries(Phi) sage: L.eval_twisted_symbol_on_Da(1) (4 + 6*7 + 3*7^2 + O(7^4), 2 + 7 + O(7^3), 4 + 6*7 + O(7^2), 6 + O(7)) """ symb = self.symb() p = symb.parent().prime() S0p = Sigma0(p) Dists = symb.parent().coefficient_module() M = Dists.precision_cap() p = Dists.prime() twisted_dist = Dists.zero_element() m_map = symb._map D = self._quadratic_twist for b in range(1, abs(D) + 1): if gcd(b, D) == 1: M1 = S0p([1, (b / abs(D)) % p**M, 0, 1]) new_dist = m_map(M1 * M2Z([a, 1, p, 0]))*M1 new_dist = new_dist.scale(kronecker(D, b)).normalize() twisted_dist = twisted_dist + new_dist #ans = ans + self.eval(M1 * M2Z[a, 1, p, 0])._right_action(M1)._lmul_(kronecker(D, b)).normalize() return twisted_dist.normalize()
def _kohnen_rho(self, t, a) : dt = (-1)**(t.nrows() // 2) * t.det() d = fundamental_discriminant(dt) eps = isqrt(dt // d) res = 1 for (p,e) in gcd(a, eps).factor() : pol = self._kohnen_rho_polynomial(t, p).coefficients() if e < len(pol) : res = res * pol[e] return res
def __init__(self, parent, polys, check=True): """ The Python constructor. See :class:`SchemeMorphism_polynomial` for details. EXAMPLES:: sage: A2.<x,y> = AffineSpace(QQ,2) sage: H = A2.Hom(A2) sage: H([x-y, x*y]) Scheme endomorphism of Affine Space of dimension 2 over Rational Field Defn: Defined on coordinates by sending (x, y) to (x - y, x*y) """ if check: if not isinstance(polys, (list, tuple)): raise TypeError, "polys (=%s) must be a list or tuple"%polys source_ring = parent.domain().coordinate_ring() target = parent.codomain().ambient_space() if len(polys) != target.ngens(): raise ValueError, "there must be %s polynomials"%target.ngens() try: polys = [source_ring(poly) for poly in polys] except TypeError: raise TypeError, "polys (=%s) must be elements of %s"%(polys,source_ring) from sage.rings.quotient_ring import QuotientRing_generic if isinstance(source_ring, QuotientRing_generic): lift_polys = [f.lift() for f in polys] else: lift_polys = polys from sage.schemes.generic.projective_space import is_ProjectiveSpace if is_ProjectiveSpace(target): # if the codomain is a subscheme of projective space, # then we need to make sure that polys have no common # zeros if isinstance(source_ring, QuotientRing_generic): # if the coordinate ring of the domain is a # quotient by an ideal, we need to check that the # gcd of polys and the generators of the ideal is 1 gcd_polys = lift_polys + list(source_ring.defining_ideal().gens()) else: # if the domain is affine space, we just need to # check the gcd of polys gcd_polys = polys from sage.rings.arith import gcd if gcd(gcd_polys) != 1: raise ValueError, "polys (=%s) must not have common factors"%polys polys = Sequence(lift_polys) polys.set_immutable() # Todo: check that map is well defined (how?) self.__polys = polys SchemeMorphism.__init__(self, parent)
def _kohnen_rho(self, t, a): dt = (-1)**(t.nrows() // 2) * t.det() d = fundamental_discriminant(dt) eps = isqrt(dt // d) res = 1 for (p, e) in gcd(a, eps).factor(): pol = self._kohnen_rho_polynomial(t, p).coefficients() if e < len(pol): res = res * pol[e] return res
def _test__jacobi_taylor_coefficients(expansion, weight, prec=None): r""" Compute the renormalized Taylor coefficients of a Jacobi form. INPUT: - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys. - ``weight`` -- An integer. - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion possibly ``None``. OUTPUT: - A list of power series in `q`. """ from sage.rings.arith import gcd if prec is None: prec = expansion.precision() jacobi_index = prec.jacobi_index() q_precision = prec.index() R = PowerSeriesRing(ZZ, 'q') q = R.gen(0) weak_prec = JacobiFormD1NNFilter(prec, reduced=False, weak_forms=True) indices = JacobiFormD1NNIndices(jacobi_index) projs = list() for pw in (range(0, 2 * jacobi_index + 1, 2) if weight % 2 == 0 else range(1, 2 * jacobi_index - 1, 2)): proj = dict((n, 0) for n in range(q_precision)) for (n, r) in weak_prec: ((nred, rred), sign) = indices.reduce((n, r)) try: proj[n] += (sign * r)**pw * expansion[(nred, rred)] except (KeyError, ValueError): pass projs.append(proj) gcd_projs = [gcd(proj.values()) for proj in projs] gcd_projs = [g if g != 0 else 1 for g in gcd_projs] projs = [sorted(proj.iteritems()) for proj in projs] projs = [ R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj for (proj, gcd_proj) in zip(projs, gcd_projs) ] return projs
def ncusps(self): r""" Return the number of cusps of this subgroup `\Gamma_0(N)`. EXAMPLES:: sage: [Gamma0(n).ncusps() for n in [1..19]] [1, 2, 2, 3, 2, 4, 2, 4, 4, 4, 2, 6, 2, 4, 4, 6, 2, 8, 2] sage: [Gamma0(n).ncusps() for n in prime_range(2,100)] [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2] """ n = self.level() return sum([arith.euler_phi(arith.gcd(d,n//d)) for d in n.divisors()])
def _mul_(self, other): """ Multiply this Hecke operator by another element of the same algebra. If the other element is of the form `T_m` for some m, we check whether the product is equal to `T_{mn}` and return that; if the product is not (easily seen to be) of the form `T_{mn}`, then we calculate the product of the two matrices and return a Hecke algebra element defined by that. EXAMPLES: We create the space of modular symbols of level `11` and weight `2`, then compute `T_2` and `T_3` on it, along with their composition. :: sage: M = ModularSymbols(11) sage: t2 = M.hecke_operator(2); t3 = M.hecke_operator(3) sage: t2*t3 # indirect doctest Hecke operator T_6 on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field sage: t3.matrix() * t2.matrix() [12 0 -2] [ 0 2 0] [ 0 0 2] sage: (t2*t3).matrix() [12 0 -2] [ 0 2 0] [ 0 0 2] When we compute `T_2^5` the result is not (easily seen to be) a Hecke operator of the form `T_n`, so it is returned as a Hecke module homomorphism defined as a matrix:: sage: t2**5 Hecke operator on Modular Symbols space of dimension 3 for Gamma_0(11) of weight 2 with sign 0 over Rational Field defined by: [243 0 -55] [ 0 -32 0] [ 0 0 -32] """ if isinstance(other, HeckeOperator) and other.parent() == self.parent(): n = None if arith.gcd(self.__n, other.__n) == 1: n = self.__n * other.__n else: P = set(arith.prime_divisors(self.domain().level())) if P.issubset(set(arith.prime_divisors(self.__n))) and \ P.issubset(set(arith.prime_divisors(other.__n))): n = self.__n * other.__n if n: return HeckeOperator(self.parent(), n) # otherwise return self.matrix_form() * other
def _iter_positive_forms_with_content_and_discriminant(self): if self.__disc is infinity: raise ValueError, "infinity is not a true filter index" if self.__reduced: for (l, (u, x)) in enumerate(self.__p1list): if u == 0: for a in xrange(self.__level, isqrt(self.__disc // 4) + 1, self.__level): frpa = 4 * a for b in xrange(a + 1): g = gcd(a // self.__level, b) bsq = b**2 for c in xrange(a, (b**2 + (self.__disc - 1)) // (4 * a) + 1): yield (((a, b, c), l), gcd(g, c), frpa * c - bsq) else: for a in xrange(1, isqrt(self.__disc // 3) + 1): frpa = 4 * a for b in xrange(a + 1): g = gcd(a, b) bsq = b**2 ## We need x**2 * a + x * b + c % N == 0 h = (-((x**2 + 1) * a + x * b)) % self.__level for c in xrange(a + h, (b**2 + (self.__disc - 1)) // (4 * a) + 1, self.__level): yield (((a, b, c), l), gcd(g, (x**2 * a + x * b + c) // self.__level), frpa * c - bsq) #! if self.__reduced else: raise NotImplementedError raise StopIteration
def basiclemma(self, M): """ Finds a number represented by self and coprime to M. EXAMPLES:: sage: Q = QuadraticForm(ZZ, 2, [2, 1, 3]) sage: Q.basiclemma(6) 71 """ a = self(self.basiclemmavec(M)) assert gcd(a, M) == 1 return a
def circle_image(A,B): G = Graphics() G += circle((0,0), 1 , color = 'grey') from collections import defaultdict tmp = defaultdict(int) for a in A: for j in range(a): if gcd(j,a) == 1: rational = Rational(j)/Rational(a) tmp[(rational.numerator(),rational.denominator())] += 1 for b in B: for j in range(b): if gcd(j,b) == 1: rational = Rational(j)/Rational(b) tmp[(rational.numerator(),rational.denominator())] -= 1 C = ComplexField() for val in tmp: if tmp[val] > 0: G += text(str(tmp[val]),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "green") if tmp[val] < 0: G += text(str(abs(tmp[val])),exp(C(2*3.14159*I*val[0]/val[1])), fontsize = 30, axes = False, color = "red") return G
def _test__jacobi_taylor_coefficients(expansion, weight, prec = None) : r""" Compute the renormalized Taylor coefficients of a Jacobi form. INPUT: - ``expansion`` -- A Fourier expansion or a dictionary with corresponding keys. - ``weight`` -- An integer. - ``prec`` -- A filter for Fourier expansions, of if ``expansion`` is a Fourier expansion possibly ``None``. OUTPUT: - A list of power series in `q`. """ from sage.rings.arith import gcd if prec is None : prec = expansion.precision() jacobi_index = prec.jacobi_index() q_precision = prec.index() R = PowerSeriesRing(ZZ, 'q'); q = R.gen(0) weak_prec = JacobiFormD1NNFilter(prec, reduced = False, weak_forms = True) indices = JacobiFormD1NNIndices(jacobi_index) projs = list() for pw in (range(0, 2 * jacobi_index + 1, 2) if weight % 2 == 0 else range(1, 2 * jacobi_index - 1, 2)) : proj = dict( (n, 0) for n in range(q_precision) ) for (n, r) in weak_prec : ((nred, rred), sign) = indices.reduce((n,r)) try : proj[n] += (sign * r)**pw * expansion[(nred, rred)] except (KeyError, ValueError) : pass projs.append(proj) gcd_projs = [gcd(proj.values()) for proj in projs] gcd_projs = [g if g != 0 else 1 for g in gcd_projs] projs = [sorted(proj.iteritems()) for proj in projs] projs = [ R([c for (_, c) in proj]).add_bigoh(q_precision) / gcd_proj for (proj, gcd_proj) in zip(projs, gcd_projs) ] return projs
def order_mod_n(k,n): r""" Return the (additive) order of ``k`` mod ``n`` which is ``n/gcd(k,n)``. EXAMPLES:: sage: order_mod_n(0,2) 1 sage: order_mod_n(1,2) 2 sage: [order_mod_n(i,4) for i in xrange(4)] 1 4 2 4 """ if k == 0: return 1 return n // gcd(k,n)
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 _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(self, t): ## We compute the rational diagonal form of t. Whenever a zero entry occures we ## find a primitive isotropic vector and apply a base change, such that t finally ## has the form diag(0,0,...,P) where P is positive definite. P will then be a ## LLL reduced. ot = t t = matrix(QQ, t) n = t.nrows() u = identity_matrix(QQ, n) for i in xrange(n): if t[i, i] < 0: return None elif t[i, i] == 0: ## get the isotropic vector v = u.column(i) v = v.denominator() * v v = v / gcd(list(v)) u = self._gln_lift(v, 0) t = u.transpose() * ot * u ts = self.reduce(t.submatrix(1, 1, n - 1, n - 1)) if ts is None: return None t.set_block(1, 1, ts[0]) t.set_immutable() return (t, 1) else: for j in xrange(i + 1, n): us = identity_matrix(QQ, n, n) us[i, j] = -t[i, j] / t[i, i] u = u * us t.add_multiple_of_row(j, i, -t[j, i] / t[i, i]) t.add_multiple_of_column(j, i, -t[i, j] / t[i, i]) u = ot.LLL_gram() t = u.transpose() * ot * u t.set_immutable() return (t, 1)
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 reduce(self, t) : ## We compute the rational diagonal form of t. Whenever a zero entry occures we ## find a primitive isotropic vector and apply a base change, such that t finally ## has the form diag(0,0,...,P) where P is positive definite. P will then be a ## LLL reduced. ot = t t = matrix(QQ, t) n = t.nrows() u = identity_matrix(QQ, n) for i in xrange(n) : if t[i,i] < 0 : return None elif t[i,i] == 0 : ## get the isotropic vector v = u.column(i) v = v.denominator() * v v = v / gcd(list(v)) u = self._gln_lift(v, 0) t = u.transpose() * ot * u ts = self.reduce(t.submatrix(1,1,n-1,n-1)) if ts is None : return None t.set_block(1, 1, ts[0]) t.set_immutable() return (t,1) else : for j in xrange(i + 1, n) : us = identity_matrix(QQ, n, n) us[i,j] = -t[i,j]/t[i,i] u = u * us t.add_multiple_of_row(j, i, -t[j,i]/t[i,i]) t.add_multiple_of_column(j, i, -t[i,j]/t[i,i]) u = ot.LLL_gram() t = u.transpose() * ot * u t.set_immutable() return (t, 1)
def hecke_operator(self, n): """ Return the `n`-th Hecke operator, for `n` any positive integer coprime to the level. EXAMPLES:: sage: T = ModularSymbols(Gamma1(5),3).anemic_hecke_algebra() sage: T.hecke_operator(2) Hecke operator T_2 on Modular Symbols space of dimension 4 for Gamma_1(5) of weight 3 with sign 0 and over Rational Field sage: T.hecke_operator(5) Traceback (most recent call last): ... IndexError: Hecke operator T_5 not defined in the anemic Hecke algebra """ n = int(n) if arith.gcd(self.module().level(), n) != 1: raise IndexError, "Hecke operator T_%s not defined in the anemic Hecke algebra" % n return self.module()._hecke_operator_class()(self, n)
def is_primitive(self): """ Checks if the form `ax^2 + bxy + cy^2` satisfies `\gcd(a,b,c)=1`, i.e., is primitive. EXAMPLES:: sage: Q = BinaryQF([6,3,9]) sage: Q.is_primitive() False sage: Q = BinaryQF([1,1,1]) sage: Q.is_primitive() True sage: Q = BinaryQF([2,2,2]) sage: Q.is_primitive() False sage: rqf = BinaryQF_reduced_representatives(-23*9) sage: [qf.is_primitive() for qf in rqf] [True, True, True, False, True, True, False, False, True] sage: rqf [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 3*x^2 + 3*x*y + 18*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 6*x^2 - 3*x*y + 9*y^2, 6*x^2 + 3*x*y + 9*y^2, 8*x^2 + 7*x*y + 8*y^2] sage: [qf for qf in rqf if qf.is_primitive()] [x^2 + x*y + 52*y^2, 2*x^2 - x*y + 26*y^2, 2*x^2 + x*y + 26*y^2, 4*x^2 - x*y + 13*y^2, 4*x^2 + x*y + 13*y^2, 8*x^2 + 7*x*y + 8*y^2] """ from sage.rings.arith import gcd return gcd([self._a, self._b, self._c]) == 1