From 7d88f9adfb2aed5481793040a2d4b081a7003d29 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 12 Sep 2019 15:01:22 +0200 Subject: [PATCH] Working on p-adic free module isomorphisms --- src/sage/rings/number_field/number_field.py | 13 +- .../rings/padics/padic_extension_generic.py | 283 +++++++++++++++++- 2 files changed, 287 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index d156924bd10..4ff82b85c3a 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -8124,6 +8124,7 @@ def _order(self, gens, **kwds): self._order.set_cache(ret, gens) return ret + @cached_method def vector_space(self): """ Return a vector space V and isomorphisms self --> V and V --> self. @@ -8157,14 +8158,10 @@ def vector_space(self): sage: to_V(from_V(V([0,-1/7,0]))) (0, -1/7, 0) """ - try: - return self.__vector_space - except AttributeError: - V = QQ**self.degree() - from_V = maps.MapVectorSpaceToNumberField(V, self) - to_V = maps.MapNumberFieldToVectorSpace(self, V) - self.__vector_space = (V, from_V, to_V) - return self.__vector_space + V = QQ**self.degree() + from_V = maps.MapVectorSpaceToNumberField(V, self) + to_V = maps.MapNumberFieldToVectorSpace(self, V) + return (V, from_V, to_V) def absolute_vector_space(self): r""" diff --git a/src/sage/rings/padics/padic_extension_generic.py b/src/sage/rings/padics/padic_extension_generic.py index 0b04b58864b..7827d063b12 100644 --- a/src/sage/rings/padics/padic_extension_generic.py +++ b/src/sage/rings/padics/padic_extension_generic.py @@ -29,13 +29,17 @@ from sage.structure.richcmp import op_EQ from functools import reduce from sage.categories.morphism import Morphism +from sage.categories.map import Map from sage.categories.sets_with_partial_maps import SetsWithPartialMaps from sage.categories.integral_domains import IntegralDomains from sage.categories.euclidean_domains import EuclideanDomains from sage.categories.metric_spaces import MetricSpaces from sage.categories.fields import Fields from sage.categories.homset import Hom - +from sage.categories.modules_with_basis import ModulesWithBasis +from sage.misc.flatten import flatten +from sage.misc.cachefunc import cached_method +from sage.misc.richcmp import richcmp class pAdicExtensionGeneric(pAdicGeneric): def __init__(self, poly, prec, print_mode, names, element_class): @@ -560,6 +564,51 @@ def random_element(self): range(self.modulus().degree())], 0) + @cached_method(key=lambda (self, base, map): (base or self.base_ring(), map)) + def free_module(self, base=None, map=True): + """ + Returns a free module V over a specified base ring together with maps to and from V. + + INPUT: + + - ``base`` -- a subring `R` so that this ring/field is isomorphic + to a finite-rank free `R`-module `V`. + - ``maps`` -- boolean (default ``True``), whether to return + `R`-linear maps to and from `V`. + + OUTPUT: + + - A finite-rank free `R`-module `V` + - An `R`-module isomorphism ``from_V`` from `V` to this ring/field + (only included if ``maps`` is ``True``) + - An `R`-module isomorphism ``to_V`` from this ring/field to `V` + (only included if ``maps`` is ``True``) + + EXAMPLES:: + + + """ + B = self.base_ring() + A = B.base_ring() + d = self.relative_degree() + if base is B: + # May eventually want to take advantage of the fact that precision is flat + V = B**d + from_V = MapFreeModuleToOneStep + to_V = MapOneStepToFreeModule + elif base is A: + d *= B.relative_degree() + V = A**d + from_V = MapFreeModuleToTwoStep + to_V = MapTwoStepToFreeModule + else: + raise NotImplementedError + FromV = Hom(V, self) + ToV = Hom(self, V) + from_V = FromV.__make_element_class__(from_V)(FromV) + to_V = ToV.__make_element_class__(to_V)(ToV) + return V, from_V, to_V + #def unit_group(self): # raise NotImplementedError @@ -575,6 +624,238 @@ def random_element(self): #def zeta_order(self): # raise NotImplementedError +# We could have used morphisms in the category +# FiniteDimensionalModulesWithBasis over Qp(p) +# But this would require making p-adic fields +# part of this category which has a lot of side +# effects (adding methods which will show up +# in tab completion for example). For now we +# just stick with Map. +class pAdicModuleIsomorphism(Map): + r""" + A base class for various isomorphisms between p-adic rings/fields and free modules + + EXAMPLES:: + + sage: K. = Qq(125) + sage: V, fr, to = K.free_module() + sage: from sage.rings.padics.padic_extension_generic import pAdicModuleIsomorphism + sage: isinstance(fr, pAdicModuleIsomorphism) + True + """ + def _repr_type(self): + r""" + EXAMPLES:: + + sage: K. = Qq(125) + sage: V, fr, to = K.free_module() + sage: fr._repr_type() + 'Isomorphism' + """ + return "Isomorphism" + + def is_injective(self): + r""" + EXAMPLES:: + + sage: K. = Qq(125) + sage: V, fr, to = K.free_module() + sage: fr.is_injective() + True + """ + return True + + def is_surjective(self): + r""" + EXAMPLES:: + + sage: K. = Qq(125) + sage: V, fr, to = K.free_module() + sage: fr.is_surjective() + True + """ + return True + + def _richcmp_(self, other, op): + r""" + EXAMPLES:: + + sage: K. = Qq(125) + sage: V, fr, to = K.free_module() + sage: fr == fr + True + """ + # Equality depends only on the parent + return richcmp(op, 0) + +class MapFreeModuleToOneStep(pAdicModuleIsomorphism): + """ + The isomorphism from the underlying module of a one-step p-adic extension + to the extension. + + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapFreeModuleToOneStep + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToOneStep(V, L) + sage: TestSuite(phi).run() + """ + def __init__(self, V, L): + pAdicModuleIsomorphism.__init__(self, Hom(V, L)) + + def _call_(self, x): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapFreeModuleToOneStep + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToOneStep(V, L) + sage: v = V([1,2,3]) + sage: phi(v) + (3*a^2 + 2*a + 1) + O(5^20) + """ + return self.codomain()(list(x)) + + def _call_with_args(self, x, args=(), kwds={}): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapOneStepToFreeModule + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToOneStep(V, L) + sage: v = V([1,2,3]) + sage: phi(v, 7) + (3*a^2 + 2*a + 1) + O(5^7) + """ + return self.codomain()(list(x), *args, **kwds) + +class MapOneStepToFreeModule(pAdicModuleIsomorphism): + """ + The isomorphism from a one-step p-adic extension to its underlying free module + + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapOneStepToFreeModule + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapOneStepToFreeModule(L, V) + sage: TestSuite(phi).run() + """ + def __init__(self, L, V): + pAdicModuleIsomorphism.__init__(self, Hom(L, V)) + + def _call_(self, x): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapOneStepToFreeModule + sage: Q5 = Qp(5) + sage: R. = ZZ[] + sage: L. = Q5.extension(x^3 - 5) + sage: V = Q5^3 + sage: phi = MapOneStepToFreeModule(L, V) + sage: a = 1 + pi^2 + O(pi^11) + sage: phi(a) + (1 + O(5^4), O(5^4), 1 + O(5^3)) + """ + V = self.codomain() + return self.codomain()(x.polynomial().padded_list(V.rank())) + +class MapFreeModuleToTwoStep(pAdicModuleIsomorphism): + """ + The isomorphism from the underlying module of a two-step p-adic extension + to the extension. + + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapFreeModuleToTwoStep + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToTwoStep(V, L) + sage: TestSuite(phi).run() + """ + def __init__(self, V, L): + pAdicModuleIsomorphism.__init__(self, Hom(V, L)) + + def _call_(self, x): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapFreeModuleToTwoStep + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToTwoStep(V, L) + sage: v = V([1,2,3]) + sage: phi(v) + (3*a^2 + 2*a + 1) + O(5^20) + """ + L = self.codomain() + d = L.relative_degree() + U = L.base_ring() + x = list(x) + n = len(x) + v = [U(x[i:i+d]) for i in range(0,n,d)] + return L(v) + + def _call_with_args(self, x, args=(), kwds={}): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapTwoStepToFreeModule + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapFreeModuleToTwoStep(V, L) + sage: v = V([1,2,3]) + sage: phi(v, 7) + (3*a^2 + 2*a + 1) + O(5^7) + """ + return self.codomain()(self._call_(x), *args, **kwds) + +class MapTwoStepToFreeModule(pAdicModuleIsomorphism): + """ + The isomorphism from a two-step p-adic extension to its underlying free module + + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapTwoStepToFreeModule + sage: L. = Qq(125) + sage: Q5 = L.base_ring() + sage: V = Q5^3 + sage: phi = MapTwoStepToFreeModule(L, V) + sage: TestSuite(phi).run() + """ + def __init__(self, L, V): + pAdicModuleIsomorphism.__init__(self, Hom(L, V)) + + def _call_(self, x): + """ + EXAMPLES:: + + sage: from sage.rings.padics.padic_extension_generic import MapTwoStepToFreeModule + sage: Q5 = Qp(5) + sage: R. = ZZ[] + sage: L. = Q5.extension(x^3 - 5) + sage: V = Q5^3 + sage: phi = MapTwoStepToFreeModule(L, V) + sage: a = 1 + pi^2 + O(pi^11) + sage: phi(a) + (1 + O(5^4), O(5^4), 1 + O(5^3)) + """ + e = self.domain().relative_degree() + f = self.domain().base_ring().relative_degree() + v = flatten([c.polynomial().padded_list(f) for c in x.polynomial().padded_list(e)]) + return self.codomain()(v) + class DefPolyConversion(Morphism): """ Conversion map between p-adic rings/fields with the same defining polynomial.