| 257 | |
| 258 | If use_star is True (which is the default), we also use the |
| 259 | +/- eigenspaces for the star operator to find the dual free |
| 260 | module of self. If the self does not have a star involution, |
| 261 | use_star will automatically be set to True. |
| 262 | |
| 263 | EXAMPLES: |
| 264 | sage: M = ModularSymbols(11, 2) |
| 265 | sage: M.dual_free_module() |
| 266 | Vector space of dimension 3 over Rational Field |
| 267 | sage: Mpc = M.plus_submodule().cuspidal_submodule() |
| 268 | sage: Mcp = M.cuspidal_submodule().plus_submodule() |
| 269 | sage: Mcp.dual_free_module() == Mpc.dual_free_module() |
| 270 | True |
| 271 | sage: Mpc.dual_free_module() |
| 272 | Vector space of degree 3 and dimension 1 over Rational Field |
| 273 | Basis matrix: |
| 274 | [ 1 5/2 5] |
| 275 | |
| 276 | sage: M = ModularSymbols(35,2).cuspidal_submodule() |
| 277 | sage: M.dual_free_module(use_star=False) |
| 278 | Vector space of degree 9 and dimension 6 over Rational Field |
| 279 | Basis matrix: |
| 280 | [ 1 0 0 0 -1 0 0 4 -2] |
| 281 | [ 0 1 0 0 0 0 0 -1/2 1/2] |
| 282 | [ 0 0 1 0 0 0 0 -1/2 1/2] |
| 283 | [ 0 0 0 1 -1 0 0 1 0] |
| 284 | [ 0 0 0 0 0 1 0 -2 1] |
| 285 | [ 0 0 0 0 0 0 1 -2 1] |
| 286 | |
| 287 | sage: M = ModularSymbols(40,2) |
| 288 | sage: Mmc = M.minus_submodule().cuspidal_submodule() |
| 289 | sage: Mcm = M.cuspidal_submodule().minus_submodule() |
| 290 | sage: Mcm.dual_free_module() == Mmc.dual_free_module() |
| 291 | True |
| 292 | sage: Mcm.dual_free_module() |
| 293 | Vector space of degree 13 and dimension 3 over Rational Field |
| 294 | Basis matrix: |
| 295 | [ 0 1 0 0 0 0 1 0 -1 -1 1 -1 0] |
| 296 | [ 0 0 1 0 -1 0 -1 0 1 0 0 0 0] |
| 297 | [ 0 0 0 0 0 1 1 0 -1 0 0 0 0] |
| 298 | |
| 299 | sage: M = ModularSymbols(43).cuspidal_submodule() |
| 300 | sage: S = M[0].plus_submodule() + M[1].minus_submodule() |
| 301 | sage: S.dual_free_module(use_star=False) |
| 302 | Traceback (most recent call last): |
| 303 | ... |
| 304 | RuntimeError: Computation of embedded dual vector space failed (cut down to rank 6, but should have cut down to rank 3). |
| 305 | sage: S.dual_free_module().dimension() == S.dimension() |
| 306 | True |
259 | | if self.dimension() == 0: |
260 | | self.__dual_free_module = self.ambient_hecke_module().dual_free_module().zero_submodule() |
261 | | return self.__dual_free_module |
262 | | |
263 | | # ALGORITHM: Compute the char poly of each Hecke operator on the |
264 | | # submodule, then use it to cut out a submodule of the dual. If |
265 | | # the dimension cuts down to the dimension of self terminate |
266 | | # with success. If it stays bigger beyond the bound (Sturm) |
267 | | # bound, raise a RuntimeError exception. |
268 | | misc.verbose("computing") |
269 | | N = self.level() |
270 | | A = self.ambient_hecke_module() |
271 | | if A.dimension() == self.dimension(): |
272 | | self.__dual_free_module = A.free_module() |
273 | | return self.__dual_free_module |
| 311 | pass |
| 312 | |
| 313 | misc.verbose("computing") |
| 314 | |
| 315 | A = self.ambient_hecke_module() |
| 316 | |
| 317 | if self.dimension() == 0: |
| 318 | self.__dual_free_module = A.zero_submodule() |
| 319 | return self.__dual_free_module |
| 320 | |
| 321 | if A.dimension() == self.dimension(): |
| 322 | self.__dual_free_module = A.free_module() |
| 323 | return self.__dual_free_module |
| 324 | |
| 325 | # ALGORITHM: Compute the char poly of each Hecke operator on |
| 326 | # the submodule, then use it to cut out a submodule of the |
| 327 | # dual. If the dimension cuts down to the dimension of self |
| 328 | # terminate with success. If it stays larger beyond the Sturm |
| 329 | # bound, raise a RuntimeError exception. |
| 330 | |
| 331 | # In the case that the sign of self is not 1, we need to use |
| 332 | # the star involution as well as the Hecke operators in order |
| 333 | # to find the dual of self. |
| 334 | # |
| 335 | # Note that one needs to comment out the line caching the |
| 336 | # result of this computation below in order to get meaningful |
| 337 | # timings. |
| 338 | |
| 339 | # If the star involution doesn't make sense for self, then we |
| 340 | # can't use it. |
| 341 | if not hasattr(self, 'star_eigenvalues'): |
| 342 | use_star = False |
| 343 | |
| 344 | if use_star: |
| 345 | # If the star involution has both + and - eigenspaces on self, |
| 346 | # then we compute the dual on each eigenspace, then put them |
| 347 | # together. |
| 348 | if len(self.star_eigenvalues()) == 2: |
| 349 | V = self.plus_submodule(compute_dual = False).dual_free_module() + \ |
| 350 | self.minus_submodule(compute_dual = False).dual_free_module() |
| 351 | return V |
| 352 | |
| 353 | # At this point, we know that self is an eigenspace for star. |
| 354 | V = A.sign_submodule(self.sign()).dual_free_module() |
| 355 | else: |
291 | | if V.rank() == self.rank(): |
292 | | self.__dual_free_module = V |
293 | | return V |
294 | | else: |
295 | | # failed |
296 | | raise RuntimeError, "Computation of embedded dual vector space failed " + \ |
297 | | "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank()) |
| 358 | N = self.level() |
| 359 | p = 2 |
| 360 | if bound is None: |
| 361 | bound = A.hecke_bound() |
| 362 | while True: |
| 363 | if anemic: |
| 364 | while N % p == 0: p = arith.next_prime(p) |
| 365 | misc.verbose("using T_%s"%p) |
| 366 | f = self.hecke_polynomial(p) |
| 367 | T = A.dual_hecke_matrix(p) |
| 368 | V = T.kernel_on(V, poly=f, check=False) |
| 369 | if V.dimension() <= self.dimension(): |
| 370 | break |
| 371 | p = arith.next_prime(p) |
| 372 | if p > bound: |
| 373 | break |
| 374 | |
| 375 | if V.rank() == self.rank(): |
| 376 | self.__dual_free_module = V |
| 377 | return V |
| 378 | else: |
| 379 | # Failed to reduce V to the appropriate dimension |
| 380 | raise RuntimeError, "Computation of embedded dual vector space failed " + \ |
| 381 | "(cut down to rank %s, but should have cut down to rank %s)."%(V.rank(), self.rank()) |