PageRenderTime 54ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/psage/modform/paramodularforms/siegelmodularformg2_fegenerators.py

https://github.com/ohanar/psage
Python | 428 lines | 204 code | 79 blank | 145 comment | 52 complexity | 7b27e18d89cfccfa09e973e32d4dda27 MD5 | raw file
  1. r"""
  2. Generator functions for the fourier expansion of Siegel modular forms.
  3. AUTHORS:
  4. - Nils-Peter Skorupa
  5. Factory function SiegelModularForm().
  6. Code parts concerning the Maass lift are partly based on code
  7. written by David Gruenewald in August 2006 which in turn is
  8. based on the PARI/GP code by Nils-Peter Skoruppa from 2003.
  9. - Martin Raum (2009 - 07 - 28) Port to new framework.
  10. - Martin Raum (2010 - 03 - 16) Implement the vector valued case.
  11. REFERENCES:
  12. - [Sko] Nils-Peter Skoruppa, ...
  13. - [I-H] Tomoyoshi Ibukiyama and Shuichi Hayashida, ...
  14. """
  15. #===============================================================================
  16. #
  17. # Copyright (C) 2009 Martin Raum
  18. #
  19. # This program is free software; you can redistribute it and/or
  20. # modify it under the terms of the GNU General Public License
  21. # as published by the Free Software Foundation; either version 3
  22. # of the License, or (at your option) any later version.
  23. #
  24. # This program is distributed in the hope that it will be useful,
  25. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  26. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  27. # General Public License for more details.
  28. #
  29. # You should have received a copy of the GNU General Public License
  30. # along with this program; if not, see <http://www.gnu.org/licenses/>.
  31. #
  32. #===============================================================================
  33. from psage.modform.fourier_expansion_framework.monoidpowerseries.monoidpowerseries_lazyelement import EquivariantMonoidPowerSeries_lazy
  34. from psage.modform.paramodularforms.siegelmodularformg2_fourierexpansion import SiegelModularFormG2FourierExpansionRing
  35. from sage.combinat.partition import number_of_partitions
  36. from sage.misc.functional import isqrt
  37. from sage.misc.latex import latex
  38. from sage.modular.modform.constructor import ModularForms
  39. from sage.modular.modform.element import ModularFormElement
  40. from sage.rings.arith import bernoulli, sigma
  41. from sage.rings.integer import Integer
  42. from sage.rings.integer_ring import ZZ
  43. from sage.rings.power_series_ring import PowerSeriesRing
  44. from sage.rings.rational_field import QQ
  45. from sage.structure.sage_object import SageObject
  46. from psage.modform.paramodularforms.siegelmodularformg2_fourierexpansion import SiegelModularFormG2Filter_discriminant
  47. from psage.modform.paramodularforms import siegelmodularformg2_misc_cython
  48. #===============================================================================
  49. # SiegelModularFormG2MaassLift
  50. #===============================================================================
  51. def SiegelModularFormG2MaassLift(f, g, precision, is_integral = True, weight = None) :
  52. factory = SiegelModularFormG2Factory(precision)
  53. if is_integral :
  54. expansion_ring = SiegelModularFormG2FourierExpansionRing(ZZ)
  55. else :
  56. expansion_ring = SiegelModularFormG2FourierExpansionRing(QQ)
  57. if f == 0 :
  58. fexp = lambda p : 0
  59. if hasattr(f, "qexp") :
  60. fexp = lambda p : f.qexp(p)
  61. else :
  62. fexp = f
  63. if g == 0 :
  64. gexp = lambda p : 0
  65. if hasattr(g, "qexp") :
  66. gexp = lambda p : g.qexp(p)
  67. else :
  68. gexp = g
  69. if weight is None :
  70. try :
  71. weight = f.weight()
  72. except AttributeError :
  73. weight = g.weight() -2
  74. coefficients_factory = DelayedFactory_SMFG2_masslift( factory, fexp, gexp, weight )
  75. return EquivariantMonoidPowerSeries_lazy(expansion_ring, precision, coefficients_factory.getcoeff)
  76. #===============================================================================
  77. # DelayedFactory_SMFG2_masslift
  78. #===============================================================================
  79. class DelayedFactory_SMFG2_masslift :
  80. def __init__(self, factory, f, g, weight) :
  81. self.__factory = factory
  82. self.__f = f
  83. self.__g = g
  84. self.__weight = weight
  85. self.__series = None
  86. def getcoeff(self, key, **kwds) :
  87. (_, k) = key
  88. # for speed we ignore the character
  89. if self.__series is None :
  90. self.__series = \
  91. self.__factory.maass_form( self.__f, self.__g, self.__weight,
  92. is_integral = True)
  93. return self.__series[k]
  94. #===============================================================================
  95. # SiegelModularFormG2Factory
  96. #===============================================================================
  97. _siegel_modular_form_g2_factory_cache = dict()
  98. def SiegelModularFormG2Factory(precision) :
  99. if not isinstance(precision, SiegelModularFormG2Filter_discriminant) :
  100. precision = SiegelModularFormG2Filter_discriminant(precision)
  101. global _siegel_modular_form_g2_factory_cache
  102. try :
  103. return _siegel_modular_form_g2_factory_cache[precision]
  104. except KeyError :
  105. factory = SiegelModularFormG2Factory_class(precision)
  106. _siegel_modular_form_g2_factory_cache[precision] = factory
  107. return factory
  108. #===============================================================================
  109. # SiegelModularFormG2Factory_class
  110. #===============================================================================
  111. class SiegelModularFormG2Factory_class( SageObject ) :
  112. r"""
  113. A class producing dictionarys saving fourier expansions of Siegel
  114. modular forms of genus `2`.
  115. """
  116. def __init__(self, precision) :
  117. r"""
  118. INPUT:
  119. - ``prec`` -- a SiegelModularFormPrec, which will be the precision of
  120. every object returned by an instance of this class.
  121. """
  122. self.__precision = precision
  123. ## Conversion of power series is not expensive but powers of interger
  124. ## series are much cheaper then powers of rational series
  125. self._power_series_ring_ZZ = PowerSeriesRing(ZZ, 'q')
  126. self._power_series_ring = PowerSeriesRing(QQ, 'q')
  127. def power_series_ring(self) :
  128. return self._power_series_ring
  129. def integral_power_series_ring(self) :
  130. return self._power_series_ring_ZZ
  131. def _get_maass_form_qexp_prec(self) :
  132. r"""
  133. Return the precision of eta, needed to calculate the Maass lift in
  134. self.as_maass_spezial_form
  135. """
  136. return ( self.__precision.discriminant() + 1)//4 + 1
  137. def _divisor_dict(self) :
  138. r"""
  139. Return a dictionary of assigning to each `k < n` a list of its divisors.
  140. INPUT:
  141. - `n` -- a positive integer
  142. """
  143. try :
  144. return self.__divisor_dict
  145. except AttributeError :
  146. self.__divisor_dict = siegelmodularformg2_misc_cython.divisor_dict(self.__precision.discriminant())
  147. return self.__divisor_dict
  148. def _eta_power(self) :
  149. try :
  150. return self.__eta_power
  151. except AttributeError :
  152. qexp_prec = self._get_maass_form_qexp_prec()
  153. self.__eta_power = self.integral_power_series_ring() \
  154. ([number_of_partitions(n) for n in xrange(qexp_prec)], qexp_prec)**6
  155. return self.__eta_power
  156. def precision(self) :
  157. return self.__precision
  158. def _negative_fundamental_discriminants(self) :
  159. r"""
  160. Return a list of all negative fundamental discriminants.
  161. """
  162. try :
  163. return self.__negative_fundamental_discriminants
  164. except AttributeError :
  165. self.__negative_fundamental_discriminants = \
  166. siegelmodularformg2_misc_cython.negative_fundamental_discriminants(
  167. self.__precision.discriminant() )
  168. return self.__negative_fundamental_discriminants
  169. def maass_form( self, f, g, k = None, is_integral = False) :
  170. r"""
  171. Return the Siegel modular form `I(f,g)` (Notation as in [Sko]).
  172. INPUT:
  173. - `f` -- modular form of level `1`
  174. - `g` -- cusp form of level `1` and weight = ``weight of f + 2``
  175. - ``is_integral`` -- ``True`` if the result is garanteed to have integer
  176. coefficients
  177. """
  178. ## we introduce an abbreviations
  179. if is_integral :
  180. PS = self.integral_power_series_ring()
  181. else :
  182. PS = self.power_series_ring()
  183. fismodular = isinstance(f, ModularFormElement)
  184. gismodular = isinstance(g, ModularFormElement)
  185. ## We only check the arguments if f and g are ModularFormElements.
  186. ## Otherwise we trust in the user
  187. if fismodular and gismodular :
  188. assert( f.weight() + 2 == g.weight() | (f==0) | (g==0)), \
  189. "incorrect weights!"
  190. assert( g.q_expansion(1) == 0), "second argument is not a cusp form"
  191. qexp_prec = self._get_maass_form_qexp_prec()
  192. if qexp_prec is None : # there are no forms below prec
  193. return dict()
  194. if fismodular :
  195. k = f.weight()
  196. if f == f.parent()(0) :
  197. f = PS(0, qexp_prec)
  198. else :
  199. f = PS(f.qexp(qexp_prec), qexp_prec)
  200. elif f == 0 :
  201. f = PS(0, qexp_prec)
  202. else :
  203. f = PS(f(qexp_prec), qexp_prec)
  204. if gismodular :
  205. k = g.weight() - 2
  206. if g == g.parent()(0) :
  207. g = PS(0, qexp_prec)
  208. else :
  209. g = PS(g.qexp(qexp_prec), qexp_prec)
  210. elif g == 0 :
  211. g = PS(0, qexp_prec)
  212. else :
  213. g = PS(g(qexp_prec), qexp_prec)
  214. if k is None :
  215. raise ValueError, "if neither f nor g are not ModularFormElements " + \
  216. "you must pass k"
  217. fderiv = f.derivative().shift(1)
  218. f *= Integer(k/2)
  219. gfderiv = g - fderiv
  220. ## Form A and B - the Jacobi forms used in [Sko]'s I map.
  221. ## This is not necessary if we multiply Ifg0 and Ifg1 by etapow
  222. # (A0,A1,B0,B1) = (a0*etapow, a1*etapow, b0*etapow, b1*etapow)
  223. ## Calculate the image of the pair of modular forms (f,g) under
  224. ## [Sko]'s isomorphism I : M_{k} \oplus S_{k+2} -> J_{k,1}.
  225. # Multiplication of big polynomials may take > 60 GB, so wie have
  226. # to do it in small parts; This is only implemented for integral
  227. # coefficients.
  228. """
  229. Create the Jacobi form I(f,g) as in [Sko].
  230. It suffices to construct for all Jacobi forms phi only the part
  231. sum_{r=0,1;n} c_phi(r^2-4n) q^n zeta^r.
  232. When, in this code part, we speak of Jacobi form we only mean this part.
  233. We need to compute Ifg = \sum_{r=0,1; n} c(r^2-4n) q^n zeta^r up to
  234. 4n-r^2 <= Dtop, i.e. n < precision
  235. """
  236. ## Create the Jacobi forms A=a*etapow and B=b*etapow in stages.
  237. ## Recall a = sum_{s != r mod 2} s^2*(-1)^r*q^((s^2+r^2-1)/4)*zeta^r
  238. ## b = sum_{s != r mod 2} (-1)^r*q^((s^2+r^2-1)/4)*zeta^r
  239. ## r, s run over ZZ but with opposite parities.
  240. ## For r=0, we need s odd, (s^2-1)/4 < precision, with s=2t+1 hence t^2+t < precision.
  241. ## For r=1, we need s even, s^2/4 < precision, with s=2t hence t^2 < precision.
  242. ## we use a slightly overestimated ab_prec
  243. ab_prec = isqrt(qexp_prec + 1)
  244. a1dict = dict(); a0dict = dict()
  245. b1dict = dict(); b0dict = dict()
  246. for t in xrange(1, ab_prec + 1) :
  247. tmp = t**2
  248. a1dict[tmp] = -8*tmp
  249. b1dict[tmp] = -2
  250. tmp += t
  251. a0dict[tmp] = 8*tmp + 2
  252. b0dict[tmp] = 2
  253. b1dict[0] = -1
  254. a0dict[0] = 2; b0dict[0] = 2
  255. a1 = PS(a1dict); b1 = PS(b1dict)
  256. a0 = PS(a0dict); b0 = PS(b0dict)
  257. ## Finally: I(f,g) is given by the formula below:
  258. ## We multiply by etapow explecitely and save two multiplications
  259. # Ifg0 = k/2*f*A0 - fderiv*B0 + g*B0 + O(q^precision)
  260. # Ifg1 = k/2*f*A1 - fderiv*B1 + g*B1 + O(q^precision)
  261. Ifg0 = (self._eta_power() * (f*a0 + gfderiv*b0)).list()
  262. Ifg1 = (self._eta_power() * (f*a1 + gfderiv*b1)).list()
  263. if len(Ifg0) < qexp_prec :
  264. Ifg0 += [0]*(qexp_prec - len(Ifg0))
  265. if len(Ifg1) < qexp_prec :
  266. Ifg1 += [0]*(qexp_prec - len(Ifg1))
  267. ## For applying the Maass' lifting to genus 2 modular forms.
  268. ## we put the coefficients of Ifg into a dictionary Chi
  269. ## so that we can access the coefficient corresponding to
  270. ## discriminant D by going Chi[D].
  271. Cphi = dict([(0,0)])
  272. for i in xrange(qexp_prec) :
  273. Cphi[-4*i] = Ifg0[i]
  274. Cphi[1-4*i] = Ifg1[i]
  275. del Ifg0[:], Ifg1[:]
  276. """
  277. Create the Maas lift F := VI(f,g) as in [Sko].
  278. """
  279. ## The constant term is given by -Cphi[0]*B_{2k}/(4*k)
  280. ## (note in [Sko] this coeff has typos).
  281. ## For nonconstant terms,
  282. ## The Siegel coefficient of q^n * zeta^r * qdash^m is given
  283. ## by the formula \sum_{ a | gcd(n,r,m) } Cphi[D/a^2] where
  284. ## D = r^2-4*n*m is the discriminant.
  285. ## Hence in either case the coefficient
  286. ## is fully deterimined by the pair (D,gcd(n,r,m)).
  287. ## Put (D,t) -> \sum_{ a | t } Cphi[D/a^2]
  288. ## in a dictionary (hash table) maassc.
  289. maass_coeffs = dict()
  290. divisor_dict = self._divisor_dict()
  291. ## First calculate maass coefficients corresponding to strictly positive definite matrices:
  292. for disc in self._negative_fundamental_discriminants() :
  293. for s in xrange(1, isqrt((-self.__precision.discriminant()) // disc) + 1) :
  294. ## add (disc*s^2,t) as a hash key, for each t that divides s
  295. for t in divisor_dict[s] :
  296. maass_coeffs[(disc * s**2,t)] = \
  297. sum( a**(k-1) * Cphi[disc * s**2 / a**2]
  298. for a in divisor_dict[t] )
  299. ## Compute the coefficients of the Siegel form $F$:
  300. siegel_coeffs = dict()
  301. for (n,r,m), g in self.__precision.iter_positive_forms_with_content() :
  302. siegel_coeffs[(n,r,m)] = maass_coeffs[(r**2 - 4*m*n, g)]
  303. ## Secondly, deal with the singular part.
  304. ## Include the coeff corresponding to (0,0,0):
  305. ## maass_coeffs = {(0,0): -bernoulli(k)/(2*k)*Cphi[0]}
  306. siegel_coeffs[(0,0,0)] = -bernoulli(k)/(2*k)*Cphi[0]
  307. if is_integral :
  308. siegel_coeffs[(0,0,0)] = Integer(siegel_coeffs[(0,0,0)])
  309. ## Calculate the other discriminant-zero maass coefficients.
  310. ## Since sigma is quite cheap it is faster to estimate the bound and
  311. ## save the time for repeated calculation
  312. for i in xrange(1, self.__precision._indefinite_content_bound()) :
  313. ## maass_coeffs[(0,i)] = sigma(i, k-1) * Cphi[0]
  314. siegel_coeffs[(0,0,i)] = sigma(i, k-1) * Cphi[0]
  315. return siegel_coeffs
  316. def maass_eisensteinseries(self, k) :
  317. if not isinstance(k, (int, Integer)) :
  318. raise TypeError, "k must be an integer"
  319. if k % 2 != 0 or k < 4 :
  320. raise ValueError, "k must be even and greater than 4"
  321. return self.maass_form(ModularForms(1,k).eisenstein_subspace().gen(0), 0)
  322. ## TODO: Rework these functions
  323. def from_gram_matrix( self, gram, name = None) :
  324. raise NotImplementedError, "matrix argument not yet implemented"
  325. def _SiegelModularForm_borcherds_lift( self, f, prec = 100, name = None):
  326. raise NotImplementedError, "matrix argument not yet implemented"
  327. def _SiegelModularForm_yoshida_lift( self, f, g, prec = 100, name = None):
  328. raise NotImplementedError, "matrix argument not yet implemented"
  329. def _SiegelModularForm_from_weil_representation( self, gram, prec = 100, name = None):
  330. raise NotImplementedError, "matrix argument not yet implemented"
  331. def _SiegelModularForm_singular_weight( self, gram, prec = 100, name = None):
  332. raise NotImplementedError, "matrix argument not yet implemented"
  333. def _repr_(self) :
  334. return "Factory class for Siegel modular forms of genus 2 with precision %s" % \
  335. repr(self.__precision.discriminant())
  336. def _latex_(self) :
  337. return "Factory class for Siegel modular forms of genus $2$ with precision %s" % \
  338. latex(self.__precision.discriminant())