diff --git a/src/sage/rings/ring_extension.py b/src/sage/rings/ring_extension.py index 074b71bdd3e..ea0e9a86058 100644 --- a/src/sage/rings/ring_extension.py +++ b/src/sage/rings/ring_extension.py @@ -199,9 +199,17 @@ def from_base_ring(self, r): return self.element_class(self, r) def _Hom_(self, other, category): - if isinstance(self, RingExtension_class) or isinstance(other, RingExtension_class): - from sage.rings.ring_extension_homset import RingExtensionHomset - return RingExtensionHomset(self, other, category) + from sage.rings.ring_extension_homset import RingExtensionHomset + return RingExtensionHomset(self, other, category) + + def hom(self, im_gens, codomain=None, base_map=None, check=True, category=None): + from sage.rings.ring_extension_homset import RingExtensionHomset + from sage.rings.ring_extension_morphism import RingExtensionHomomorphism + if codomain is None: + from sage.structure.sequence import Sequence + codomain = Sequence(im_gens).universe() + parent = RingExtensionHomset(self, codomain, category) + return RingExtensionHomomorphism(parent, im_gens, base_map, check) def _pushout_(self, other): r""" @@ -441,11 +449,11 @@ def _an_element_(self): elt = self._ring.an_element() return self.element_class(self, elt) - def gens(self): + def gens(self, base=None): return tuple([ self(x) for x in self._ring.gens() ]) - def ngens(self): - return len(self.gens()) + def ngens(self, base=None): + return len(self.gens(base)) def gen(self): r""" @@ -710,8 +718,17 @@ def modulus(self, var='x'): S = PolynomialRing(self._base, name=var) return S(coeffs) - def gens(self): - return (self(self._gen),) + def gens(self, base=None): + if base is None: + return (self(self._gen),) + gens = tuple([]) + b = self + while b is not base: + gens += b.gens() + if b is b.base_ring(): + raise ValueError("(%s) is not defined over (%s)" % (self, base)) + b = b.base_ring() + return gens - def _repr_(self): + def _repr_(self, base=None): return "%s in %s with defining polynomial %s over its base" % (self._type, self._name, self.modulus()) diff --git a/src/sage/rings/ring_extension_element.pyx b/src/sage/rings/ring_extension_element.pyx index 92c94b14991..3d8e88d970b 100644 --- a/src/sage/rings/ring_extension_element.pyx +++ b/src/sage/rings/ring_extension_element.pyx @@ -11,6 +11,8 @@ from sage.structure.element cimport CommutativeAlgebraElement from sage.structure.element cimport Element +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing cdef class RingExtensionElement(CommutativeAlgebraElement): @@ -225,10 +227,8 @@ cdef class RingExtensionElement(CommutativeAlgebraElement): cdef class RingExtensionWithBasisElement(RingExtensionElement): def _repr_(self): - parent = self._parent - names = parent._names - _, _, j = parent.vector_space() - coeffs = j(self) + names = self._parent._names + coeffs = self.vector() s = "" for i in range(len(names)): if coeffs[i].is_zero(): continue @@ -254,6 +254,38 @@ cdef class RingExtensionWithBasisElement(RingExtensionElement): if s == "": return "0" return s + def vector(self, base=None): + _, _, j = self._parent.vector_space(base) + return j(self) + + def polynomial(self, base=None, var='x'): + from sage.rings.ring_extension import RingExtensionWithGen + if base is None: + base = self._parent._base + degrees = [ ] + b = self._parent + degree = 1 + while b is not base: + if not isinstance(b, RingExtensionWithGen): + raise NotImplementedError + reldeg = b.relative_degree() + degree *= reldeg + degrees.append(reldeg) + if b is b.base_ring(): + raise ValueError("(%s) is not defined over (%s)" % (self, base)) + b = b.base_ring() + v = self.vector(base) + coeffs = { } + S = PolynomialRing(base, len(degrees), names=var) + for i in range(degree): + ii = ZZ(i) + exponents = [ ] + for j in range(len(degrees)): + ii, exponent = ii.quo_rem(degrees[j]) + exponents.append(exponent) + coeffs[tuple(exponents)] = v[i] + return S(coeffs) + def matrix(self, base=None): from sage.matrix.matrix_space import MatrixSpace parent = self._parent diff --git a/src/sage/rings/ring_extension_morphism.pxd b/src/sage/rings/ring_extension_morphism.pxd index f4a290cee42..a7698730d98 100644 --- a/src/sage/rings/ring_extension_morphism.pxd +++ b/src/sage/rings/ring_extension_morphism.pxd @@ -3,4 +3,7 @@ from sage.rings.morphism cimport RingHomomorphism cdef class RingExtensionHomomorphism(RingHomomorphism): cdef _backend_morphism + cdef _gens + cdef _im_gens + cdef _base_map cpdef Element _call_(self, x) diff --git a/src/sage/rings/ring_extension_morphism.pyx b/src/sage/rings/ring_extension_morphism.pyx index 1274a2e8d32..0913b76f6e7 100644 --- a/src/sage/rings/ring_extension_morphism.pyx +++ b/src/sage/rings/ring_extension_morphism.pyx @@ -63,7 +63,7 @@ cdef class RingExtensionHomomorphism(RingHomomorphism): sage: E2 = RingExtension(L,K) """ - def __init__(self, parent, defn, base_map=None, check=False): + def __init__(self, parent, defn, base_map=None, check=True): RingHomomorphism.__init__(self, parent) backend_domain = domain = self.domain() if isinstance(backend_domain, RingExtension_class): @@ -81,9 +81,24 @@ cdef class RingExtensionHomomorphism(RingHomomorphism): if backend.codomain() is not backend_codomain: raise TypeError("the codomain of the backend morphism is not correct") self._backend_morphism = backend + self._im_gens = None + self._base_map = False elif isinstance(defn, (list, tuple)): - if domain.ngens() != len(defn): - raise ValueError("the number of images does not match the number of generators") + # We figure out what is the base + if base_map is not None: + base = base_map.domain() + gens = domain.gens(base) + else: + base = domain + gens = tuple([]) + while True: + if len(gens) == len(defn): + break + if len(gens) > len(defn) or base is base.base_ring(): + raise ValueError("the number of images does not match the number of generators") + gens += base.gens() + base = base.base_ring() + # We construct the backend morphism im_gens = [ codomain(x) for x in defn ] backend_bases = [ backend_domain ] b = backend_domain.base_ring() @@ -95,18 +110,49 @@ cdef class RingExtensionHomomorphism(RingHomomorphism): for current_domain in backend_bases: current_im_gens = [ ] for x in current_domain.gens(): - pol = codomain(backend_codomain(x)).polynomial() + pol = domain(backend_domain(x)).polynomial(base) if base_map is not None: pol = pol.map_coefficients(base_map) y = pol(im_gens) if isinstance(codomain, RingExtension_class): y = y._backend() current_im_gens.append(y) - current_morphism = current_domain.Hom(backend_codomain)(current_im_gens, base_map=current_morphism, check=check) + current_morphism = current_domain.hom(current_im_gens, base_map=current_morphism, check=check) + # We check that everything went well + if check: + for i in range(len(gens)): + if current_morphism(domain(gens[i])._backend()) != im_gens[i]._backend(): + raise ValueError("images do not define a valid homomorphism") + # instead of the following code, it would be better to write + # if backend_morphism(base_map) != current_morphism.restriction(base._backend()): + # raise ValueError("images do not define a valid homomorphism") + # but many things need to be implemented for that + current = base + while current is not current.base_ring(): + for g in current.gens(): + gg = domain(g) + x = gg._backend() + if base_map is None: + y = codomain(gg) + else: + y = codomain(base_map(g)) + if isinstance(codomain, RingExtension_class): + y = y._backend() + if current_morphism(x) != y: + raise ValueError("images do not define a valid homomorphism") + current = current.base_ring() self._backend_morphism = current_morphism + self._im_gens = im_gens[:domain.ngens()] + if base is domain.base_ring(): + self._base_map = base_map + else: + self._base_map = { + 'im_gens': defn[domain.ngens():], + 'base_map': base_map, + 'check': False + } else: raise TypeError - self._base_map = base_map cpdef Element _call_(self, x): if isinstance(self.domain(), RingExtension_class): @@ -120,8 +166,28 @@ cdef class RingExtensionHomomorphism(RingHomomorphism): return self._backend_morphism def base_map(self): + if self._base_map is False: + raise NotImplementedError + if isinstance(self._base_map, dict): + self._base_map = self.domain().base_ring().hom(**self._base_map) return self._base_map + def _repr_defn(self): + import re + s = "" + if self._im_gens is not None: + gens = self.domain().gens() + for i in range(len(gens)): + s += "%s |--> %s\n" % (gens[i], self._im_gens[i]) + if self._base_map is not None: + s += "with base map" + ss = self.base_map()._repr_defn() + ss = re.sub('\nwith base map:?$', '', ss, 0, re.MULTILINE) + if ss != "": s += ":\n" + ss + if s[-1] == "\n": + s = s[:-1] + return s + cdef _update_slots(self, dict _slots): self._backend_morphism = _slots['_backend_morphism'] RingHomomorphism._update_slots(self, _slots)