From d4a35ac1da3360df7a92d02fc0e7c898c3e0676a Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 12 Sep 2019 00:00:43 +0200 Subject: [PATCH 1/3] Add base_map to homomorphisms defined by images of generators --- .../lie_algebras/lie_algebra_element.pyx | 10 +- src/sage/algebras/lie_algebras/morphism.py | 79 ++++++++-- ...ite_dimensional_lie_algebras_with_basis.py | 26 +++- src/sage/categories/homset.py | 2 + src/sage/modules/fg_pid/fgp_morphism.py | 8 +- src/sage/rings/finite_rings/element_base.pyx | 12 +- src/sage/rings/finite_rings/integer_mod.pyx | 3 +- src/sage/rings/fraction_field_element.pyx | 17 ++- src/sage/rings/homset.py | 24 ++- src/sage/rings/integer.pyx | 2 +- .../rings/laurent_series_ring_element.pyx | 26 +++- src/sage/rings/morphism.pxd | 5 +- src/sage/rings/morphism.pyx | 86 ++++++++--- .../rings/multi_power_series_ring_element.py | 28 +++- src/sage/rings/number_field/maps.py | 12 +- src/sage/rings/number_field/morphism.py | 79 +++++----- .../number_field/number_field_element.pyx | 10 +- src/sage/rings/number_field/structure.py | 8 +- .../rings/polynomial/laurent_polynomial.pyx | 26 +++- .../polynomial/multi_polynomial_element.py | 19 ++- .../multi_polynomial_libsingular.pyx | 17 ++- src/sage/rings/polynomial/plural.pyx | 28 +++- .../rings/polynomial/polynomial_element.pyx | 24 ++- .../polynomial_quotient_ring_element.py | 27 +++- src/sage/rings/power_series_ring_element.pyx | 7 +- src/sage/rings/quotient_ring_element.py | 4 +- src/sage/rings/rational.pyx | 2 +- src/sage/rings/real_double.pyx | 2 +- src/sage/rings/real_mpfi.pyx | 2 +- src/sage/rings/real_mpfr.pyx | 2 +- src/sage/structure/element.pyx | 2 +- src/sage/structure/parent.pyx | 144 ++++++++++-------- src/sage/structure/parent_gens.pyx | 17 ++- 33 files changed, 536 insertions(+), 224 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index d056b6841eb..d54644647d8 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -82,7 +82,7 @@ cdef class LieAlgebraElement(IndexedFreeModuleElement): right = ( right).lift() return left * right - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` in ``codomain`` under the map that sends the generators of the parent of ``self`` @@ -119,8 +119,12 @@ cdef class LieAlgebraElement(IndexedFreeModuleElement): if not self: # If we are 0 return s names = self.parent().variable_names() - return codomain.sum(c * t._im_gens_(codomain, im_gens, names) - for t, c in self._monomial_coefficients.iteritems()) + if base_map is None: + return codomain.sum(c * t._im_gens_(codomain, im_gens, names) + for t, c in self._monomial_coefficients.iteritems()) + else: + return codomain.sum(base_map(c) * t._im_gens_(codomain, im_gens, names) + for t, c in self._monomial_coefficients.iteritems()) cpdef lift(self): """ diff --git a/src/sage/algebras/lie_algebras/morphism.py b/src/sage/algebras/lie_algebras/morphism.py index 32da21b40c3..bea2d600b43 100644 --- a/src/sage/algebras/lie_algebras/morphism.py +++ b/src/sage/algebras/lie_algebras/morphism.py @@ -35,6 +35,18 @@ class LieAlgebraHomomorphism_im_gens(Morphism): `x, y \in \mathfrak{g}`. Thus homomorphisms are completely determined by the image of the generators of `\mathfrak{g}`. + INPUT: + + - ``parent`` -- a homset between two Lie algebras + - ``im_gens`` -- the image of the generators of the domain + - ``base_map`` -- a homomorphism to apply to the coefficients. + It should be a map from the base ring of the domain to the + base ring of the codomain. + Note that if base_map is nontrivial then the result will + not be a morphism in the category of lie algebras over + the base ring. + - ``check`` -- whether to run checks on the validity of the defining data + EXAMPLES:: sage: L = LieAlgebra(QQ, 'x,y,z') @@ -51,7 +63,7 @@ class LieAlgebraHomomorphism_im_gens(Morphism): y |--> y z |--> z """ - def __init__(self, parent, im_gens, check=True): + def __init__(self, parent, im_gens, base_map=None, check=True): """ EXAMPLES:: @@ -73,6 +85,8 @@ def __init__(self, parent, im_gens, check=True): if check: if len(im_gens) != len(parent.domain().lie_algebra_generators()): raise ValueError("number of images must equal number of generators") + if base_map is not None and not (base_map.domain() is parent.domain().base_ring() and parent.codomain().base_ring().has_coerce_map_from(base_map.codomain())): + raise ValueError("Invalid base homomorphism") # TODO: Implement a (meaningful) _is_valid_homomorphism_() #if not parent.domain()._is_valid_homomorphism_(parent.codomain(), im_gens): # raise ValueError("relations do not all (canonically) map to 0 under map determined by images of generators.") @@ -81,6 +95,7 @@ def __init__(self, parent, im_gens, check=True): im_gens = copy.copy(im_gens) im_gens.set_immutable() self._im_gens = im_gens + self._base_map = base_map def _repr_type(self): """ @@ -116,6 +131,29 @@ def im_gens(self): """ return list(self._im_gens) + def base_map(self): + """ + Return the map on the base ring that is part of the defining + data for this morphism. May return ``None`` if a coercion is used. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: L. = LieAlgebra(K, {('X','Y'): {'Z':1}, ('X','Z'): {'W':1}}) + sage: M. = LieAlgebra(K, abelian=True) + sage: phi = L.morphism({X: A, Y: B}, base_map=cc) + sage: phi(X) + A + sage: phi(i*X) + -i*A + sage: phi.base_map() + Ring endomorphism of Number Field in i with defining polynomial x^2 + 1 + Defn: i |--> -i + """ + return self._base_map + def _richcmp_(self, other, op): """ Rich comparisons. @@ -136,7 +174,7 @@ def _richcmp_(self, other, op): sage: f != h True """ - return richcmp(self._im_gens, other._im_gens, op) + return richcmp((self._im_gens, self._base_map), (other._im_gens, other._base_map), op) def __hash__(self): """ @@ -151,7 +189,7 @@ def __hash__(self): sage: hash(phi) == hash(phi) True """ - return hash(self._im_gens) + return hash((self._im_gens, self._base_map)) def _repr_defn(self): """ @@ -169,8 +207,11 @@ def _repr_defn(self): z |--> z """ D = self.domain() - return '\n'.join('%s |--> %s' % (x, gen) - for gen, x in zip(self._im_gens, D.gens())) + s = '\n'.join('%s |--> %s' % (x, gen) + for gen, x in zip(self._im_gens, D.gens())) + if s and self._base_map is not None: + s += '\nwith map of base ring' + return s def _call_(self, x): """ @@ -188,7 +229,7 @@ def _call_(self, x): [x, [[x, z], [y, z]]] + [x, [[[x, z], z], y]] + [[x, y], [[x, z], z]] + [[x, [y, z]], [x, z]] """ - return x._im_gens_(self.codomain(), self.im_gens()) + return x._im_gens_(self.codomain(), self.im_gens(), base_map=self.base_map()) class LieAlgebraHomset(Homset): """ @@ -307,6 +348,12 @@ class LieAlgebraMorphism_from_generators(LieAlgebraHomomorphism_im_gens): in ``codomain`` of elements `X` of ``domain`` - ``codomain`` -- a Lie algebra (optional); this is inferred from the values of ``on_generators`` if not given + - ``base_map`` -- a homomorphism to apply to the coefficients. + It should be a map from the base ring of the domain to the + base ring of the codomain. + Note that if base_map is nontrivial then the result will + not be a morphism in the category of lie algebras over + the base ring. - ``check`` -- (default: ``True``) boolean; if ``False`` the values on the Lie brackets implied by ``on_generators`` will not be checked for contradictory values @@ -396,7 +443,7 @@ class LieAlgebraMorphism_from_generators(LieAlgebraHomomorphism_im_gens): Z |--> 0 W |--> 0 """ - def __init__(self, on_generators, domain=None, codomain=None, check=True): + def __init__(self, on_generators, domain=None, codomain=None, check=True, base_map=None, category=None): r""" Initialize ``self``. @@ -456,7 +503,13 @@ def __init__(self, on_generators, domain=None, codomain=None, check=True): if codomain not in LieAlgebras: raise TypeError("codomain %s is not a Lie algebra" % codomain) - parent = Hom(domain, codomain) + if base_map is not None and category is None: + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps + # We can't make any guarantee about the category of this morphism + # (in particular, it won't usually be linear over the base) + # so we default to a very lax category + category = SetsWithPartialMaps() + parent = Hom(domain, codomain, category=category) m = domain.module() cm = codomain.module() @@ -464,7 +517,7 @@ def __init__(self, on_generators, domain=None, codomain=None, check=True): im_gens = [Y.to_vector() for Y in on_generators.values()] if not im_gens: - LieAlgebraHomomorphism_im_gens.__init__(self, parent, [], check=check) + LieAlgebraHomomorphism_im_gens.__init__(self, parent, [], base_map=base_map, check=check) return # helper function to solve linear systems Ax = b, where both x and b @@ -520,7 +573,7 @@ def solve_linear_system(A, b, check): A = matrix(m.base_ring(), spanning_set) im_gens = solve_linear_system(A, im_gens, check) - LieAlgebraHomomorphism_im_gens.__init__(self, parent, im_gens, check=check) + LieAlgebraHomomorphism_im_gens.__init__(self, parent, im_gens, base_map=base_map, check=check) def _call_(self, x): """ @@ -554,4 +607,8 @@ def _call_(self, x): 1/3*A - 1/3*B + 2/3*C """ C = self.codomain() - return C.sum(c * self._im_gens[i] for i, c in x.to_vector().iteritems()) + if self._base_map is None: + return C.sum(c * self._im_gens[i] for i, c in x.to_vector().iteritems()) + else: + bh = self._base_map + return C.sum(bh(c) * self._im_gens[i] for i, c in x.to_vector().iteritems()) diff --git a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py index 5ebb3085bac..5ba4ed3869c 100644 --- a/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py +++ b/src/sage/categories/finite_dimensional_lie_algebras_with_basis.py @@ -1344,7 +1344,7 @@ def as_finite_dimensional_algebra(self): from sage.algebras.finite_dimensional_algebras.finite_dimensional_algebra import FiniteDimensionalAlgebra return FiniteDimensionalAlgebra(R, mats, names=self._names) - def morphism(self, on_generators, codomain=None, check=True): + def morphism(self, on_generators, codomain=None, base_map=None, check=True): r""" Return a Lie algebra morphism defined by images of a Lie generating subset of ``self``. @@ -1355,6 +1355,8 @@ def morphism(self, on_generators, codomain=None, check=True): in ``codomain`` of elements `X` of ``domain`` - ``codomain`` -- a Lie algebra (optional); this is inferred from the values of ``on_generators`` if not given + - ``base_map`` -- a homomorphism from the base ring to something + coercing into the codomain - ``check`` -- (default: ``True``) boolean; if ``False`` the values on the Lie brackets implied by ``on_generators`` will not be checked for contradictory values @@ -1391,10 +1393,30 @@ def morphism(self, on_generators, codomain=None, check=True): ... ValueError: this does not define a Lie algebra morphism; contradictory values for brackets of length 2 + + However, it is still possible to create a morphism that acts nontrivially + on the coefficients, even though it's not a Lie algebra morphism + (since it isn't linear):: + + sage: R. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: L. = LieAlgebra(K, {('X','Y'): {'Z':1}, ('X','Z'): {'W':1}}) + sage: M. = LieAlgebra(K, abelian=True) + sage: phi = L.morphism({X: A, Y: B}, base_map=cc) + sage: phi(X) + A + sage: phi(i*X) + -i*A + + Note that ``phi`` is not a morphism of Lie algebras over `K`:: + + sage: phi.category_for() + Category of sets with partial maps """ from sage.algebras.lie_algebras.morphism import LieAlgebraMorphism_from_generators return LieAlgebraMorphism_from_generators(on_generators, domain=self, - codomain=codomain, check=check) + codomain=codomain, base_map=base_map, check=check) class ElementMethods: def adjoint_matrix(self): # In #11111 (more or less) by using matrix of a morphism diff --git a/src/sage/categories/homset.py b/src/sage/categories/homset.py index 7199a9ab8b6..1a6a37b1ca9 100644 --- a/src/sage/categories/homset.py +++ b/src/sage/categories/homset.py @@ -922,6 +922,8 @@ def _element_constructor_(self, x, check=None, **options): try: call_with_keywords = self.__call_on_basis__ except AttributeError: + if 'base_map' in options: + raise NotImplementedError("base_map not supported for this Homset; you may need to specify a category") raise NotImplementedError("no keywords are implemented for constructing elements of {}".format(self)) options.setdefault("category", self.homset_category()) return call_with_keywords(**options) diff --git a/src/sage/modules/fg_pid/fgp_morphism.py b/src/sage/modules/fg_pid/fgp_morphism.py index 1df1db59fb5..1944b3777bc 100644 --- a/src/sage/modules/fg_pid/fgp_morphism.py +++ b/src/sage/modules/fg_pid/fgp_morphism.py @@ -24,7 +24,7 @@ from sage.categories.morphism import Morphism, is_Morphism from .fgp_module import DEBUG from sage.structure.richcmp import richcmp, op_NE - +from sage.misc.cachefunc import cached_method class FGP_Morphism(Morphism): """ @@ -132,6 +132,7 @@ def _repr_(self): self.domain().base_ring(), self.domain().invariants(), self.codomain().invariants(), list(self.im_gens())) + @cached_method def im_gens(self): """ Return tuple of the images of the generators of the domain @@ -146,10 +147,7 @@ def im_gens(self): sage: phi.im_gens() is phi.im_gens() True """ - try: return self.__im_gens - except AttributeError: pass - self.__im_gens = tuple([self(x) for x in self.domain().gens()]) - return self.__im_gens + return tuple([self(x) for x in self.domain().gens()]) def _richcmp_(self, right, op): """ diff --git a/src/sage/rings/finite_rings/element_base.pyx b/src/sage/rings/finite_rings/element_base.pyx index 8cb0dad3a19..3b35447debd 100755 --- a/src/sage/rings/finite_rings/element_base.pyx +++ b/src/sage/rings/finite_rings/element_base.pyx @@ -110,14 +110,14 @@ cdef class FinitePolyExtElement(FiniteRingElement): sage: k. = GF(64) sage: TestSuite(a).run() """ - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Used for applying homomorphisms of finite fields. EXAMPLES:: - sage: k. = FiniteField(73^2, 'a') - sage: K. = FiniteField(73^4, 'b') + sage: k. = FiniteField(73^2) + sage: K. = FiniteField(73^4) sage: phi = k.hom([ b^(73*73+1) ]) # indirect doctest sage: phi(0) 0 @@ -130,7 +130,11 @@ cdef class FinitePolyExtElement(FiniteRingElement): ## NOTE: see the note in sage/rings/number_field_element.pyx, ## in the comments for _im_gens_ there -- something analogous ## applies here. - return codomain(self.polynomial()(im_gens[0])) + f = self.polynomial() + if base_map is not None: + Cx = codomain['x'] + f = Cx([base_map(c) for c in f]) + return codomain(f(im_gens[0])) def minpoly(self,var='x',algorithm='pari'): """ diff --git a/src/sage/rings/finite_rings/integer_mod.pyx b/src/sage/rings/finite_rings/integer_mod.pyx index e522adda524..8268de346fe 100644 --- a/src/sage/rings/finite_rings/integer_mod.pyx +++ b/src/sage/rings/finite_rings/integer_mod.pyx @@ -444,7 +444,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): """ return sage.rings.finite_rings.integer_mod.mod, (self.lift(), self.modulus(), self.parent()) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` under the map that sends the generators of the parent to ``im_gens``. @@ -456,6 +456,7 @@ cdef class IntegerMod_abstract(FiniteRingElement): sage: a._im_gens_(R, (R(1),)) 2 """ + # The generators are irrelevant (Zmod(n) is its own base), so we ignore base_map return codomain._coerce_(self) def __mod__(self, modulus): diff --git a/src/sage/rings/fraction_field_element.pyx b/src/sage/rings/fraction_field_element.pyx index e7a625425cd..88a26461518 100644 --- a/src/sage/rings/fraction_field_element.pyx +++ b/src/sage/rings/fraction_field_element.pyx @@ -126,7 +126,7 @@ cdef class FractionFieldElement(FieldElement): if self.__denominator.is_zero(): raise ZeroDivisionError("fraction field element division by zero") - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ EXAMPLES:: @@ -147,9 +147,20 @@ cdef class FractionFieldElement(FieldElement): (a^2 + 2*a*b + b^2)/(a*b) sage: (x^2/y)._im_gens_(K, [a, a*b]) a/b + + :: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: R. = K[] + sage: F = R.fraction_field() + sage: phi = F.hom([F(b),F(a)], base_map=cc, category=Fields()) + sage: phi(i/a) + ((-i))/b """ - nnum = codomain.coerce(self.__numerator._im_gens_(codomain, im_gens)) - nden = codomain.coerce(self.__denominator._im_gens_(codomain, im_gens)) + nnum = codomain.coerce(self.__numerator._im_gens_(codomain, im_gens, base_map=base_map)) + nden = codomain.coerce(self.__denominator._im_gens_(codomain, im_gens, base_map=base_map)) return codomain.coerce(nnum/nden) cpdef reduce(self): diff --git a/src/sage/rings/homset.py b/src/sage/rings/homset.py index fd975ca96bd..2a10df21074 100644 --- a/src/sage/rings/homset.py +++ b/src/sage/rings/homset.py @@ -157,7 +157,7 @@ def _coerce_impl(self, x): except Exception: raise TypeError - def __call__(self, im_gens, check=True): + def __call__(self, im_gens, check=True, base_map=None): """ Create a homomorphism. @@ -170,6 +170,20 @@ def __call__(self, im_gens, check=True): To: Rational Field Defn: 1 |--> 1 + You can provide a morphism on the base:: + + sage: k = GF(9) + sage: z2 = k.gen() + sage: cc = k.frobenius_endomorphism() + sage: R. = k[] + sage: H = Hom(R, R, category=Rings()) + sage: phi = H([x^2], base_map=cc); phi + Ring endomorphism of Univariate Polynomial Ring in x over Finite Field in z2 of size 3^2 + Defn: x |--> x^2 + with map of base ring + sage: phi(z2*x) == z2^3 * x^2 + True + TESTS:: sage: H = Hom(ZZ, QQ) @@ -178,9 +192,11 @@ def __call__(self, im_gens, check=True): """ from sage.categories.map import Map if isinstance(im_gens, Map): + if base_map is not None: + raise ValueError("Cannot specify base_map when providing a map") return self._coerce_impl(im_gens) else: - return morphism.RingHomomorphism_im_gens(self, im_gens, check=check) + return morphism.RingHomomorphism_im_gens(self, im_gens, base_map=base_map, check=check) def natural_map(self): """ @@ -264,7 +280,7 @@ class RingHomset_quo_ring(RingHomset_generic): sage: phi == loads(dumps(phi)) True """ - def __call__(self, im_gens, check=True): + def __call__(self, im_gens, base_map=None, check=True): """ Create a homomorphism. @@ -285,7 +301,7 @@ def __call__(self, im_gens, check=True): return morphism.RingHomomorphism_from_quotient(self, im_gens._phi()) try: pi = self.domain().cover() - phi = pi.domain().hom(im_gens, check=check) + phi = pi.domain().hom(im_gens, base_map=base_map, check=check) return morphism.RingHomomorphism_from_quotient(self, phi) except (NotImplementedError, ValueError): try: diff --git a/src/sage/rings/integer.pyx b/src/sage/rings/integer.pyx index 430fd2b9c9f..ee45ba72e07 100644 --- a/src/sage/rings/integer.pyx +++ b/src/sage/rings/integer.pyx @@ -789,7 +789,7 @@ cdef class Integer(sage.structure.element.EuclideanDomainElement): """ return mpz_get_pyintlong(self.value) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of self under the map that sends the generators of the parent to im_gens. Since ZZ maps canonically in the category diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 6ed117011db..7bfe74412e3 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -274,8 +274,30 @@ cdef class LaurentSeries(AlgebraElement): """ return bool(self.__u) - def _im_gens_(self, codomain, im_gens): - return codomain(self(im_gens[0])) + def _im_gens_(self, codomain, im_gens, base_map=None): + """ + Return the image of this series under the map that sends the generators of + the parent to im_gens. + + EXAMPLES:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: R. = LaurentSeriesRing(K) + sage: z = t^-1 + i*t + sage: z._im_gens_(R, [t^2]) + t^-2 + i*t^2 + + The argument base_map is not yet supported, because it isn't over power series:: + + sage: cc = K.hom([i]) + sage: z._im_gens_(R, [t^2], base_map=cc) + Traceback (most recent call last): + ... + NotImplementedError: + """ + x = im_gens[0] + return codomain(self.__u._im_gens_(codomain, im_gens, base_map=base_map) * x**self.__n) cdef __normalize(self): r""" diff --git a/src/sage/rings/morphism.pxd b/src/sage/rings/morphism.pxd index 18c3169d00b..62f1ee1f00e 100644 --- a/src/sage/rings/morphism.pxd +++ b/src/sage/rings/morphism.pxd @@ -16,10 +16,11 @@ cdef class RingHomomorphism(RingMap): cdef Morphism _lift cdef class RingHomomorphism_im_gens(RingHomomorphism): - cdef __im_gens + cdef _im_gens + cdef _base_map cdef class RingHomomorphism_from_base(RingHomomorphism): - cdef __underlying + cdef _underlying cdef class RingHomomorphism_cover(RingHomomorphism): pass diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index e68c30f141e..98e7308b97d 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1014,7 +1014,7 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): """ A ring homomorphism determined by the images of generators. """ - def __init__(self, parent, im_gens, check=True): + def __init__(self, parent, im_gens, check=True, base_map=None): """ EXAMPLES:: @@ -1038,6 +1038,16 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): ... TypeError: images do not define a valid homomorphism + You can give a map of the base ring:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: R. = K[] + sage: z = 1 + i*t + (3+4*i)*t^2 + sage: z._im_gens_(R, [t^2], base_map=cc) + (-4*i + 3)*t^4 - i*t^2 + 1 + There is a check option, but it may be ignored in some cases -- it's purpose isn't so you can lie to Sage, but to sometimes speed up creation of a homomorphism:: @@ -1063,7 +1073,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): import copy im_gens = copy.copy(im_gens) im_gens.set_immutable() - self.__im_gens = im_gens + self._im_gens = im_gens + self._base_map = base_map def im_gens(self): """ @@ -1087,7 +1098,31 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: f.im_gens() [x, x + y] """ - return list(self.__im_gens) + return list(self._im_gens) + + def base_map(self): + """ + Return the map on the base ring that is part of the defining + data for this morphism. May return ``None`` if a coercion is used. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: S. = K[] + sage: phi = S.hom([y^2], base_map=cc, category=S.category()) + sage: phi + Ring endomorphism of Univariate Polynomial Ring in y over Number Field in i with defining polynomial x^2 + 1 + Defn: y |--> y^2 + with map of base ring + sage: phi(y) + y^2 + sage: phi(i*y) + -i*y^2 + sage: phi.base_map() + """ + return self._base_map cdef _update_slots(self, dict _slots): """ @@ -1105,7 +1140,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: g(y) x + y """ - self.__im_gens = _slots['__im_gens'] + self._im_gens = _slots['__im_gens'] # double underscores for legacy pickles + self._base_map = _slots.get('_base_map') RingHomomorphism._update_slots(self, _slots) cdef dict _extra_slots(self): @@ -1125,7 +1161,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): x + y """ slots = RingHomomorphism._extra_slots(self) - slots['__im_gens'] = self.__im_gens + slots['__im_gens'] = self._im_gens + slots['_base_map'] = self._base_map return slots cpdef _richcmp_(self, other, int op): @@ -1192,9 +1229,11 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): # Generic comparison return RingMap._richcmp_(self, other, op) # Check equality using the images of the generators. - self_im = self.__im_gens - other_im = (other).__im_gens - return richcmp(self_im, other_im, op) + self_im = self._im_gens + other_im = (other)._im_gens + self_hom = self._base_map + other_hom = (other)._base_map + return richcmp((self_im, self_hom), (other_im, other_hom), op) def __hash__(self): """ @@ -1211,7 +1250,7 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: {s: 1}[s] 1 """ - return hash(self.__im_gens) + return hash((self._im_gens, self._base_map)) def _repr_defn(self): """ @@ -1225,9 +1264,12 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): y |--> x + y """ D = self.domain() - ig = self.__im_gens - return '\n'.join(['%s |--> %s'%(D.gen(i), ig[i]) for\ + ig = self._im_gens + s = '\n'.join(['%s |--> %s'%(D.gen(i), ig[i]) for\ i in range(D.ngens())]) + if s and self._base_map is not None: + s += '\nwith map of base ring' + return s cpdef Element _call_(self, x): """ @@ -1239,7 +1281,7 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: f(x+2*y+3*z) # indirect doctest 2*x + 3*y + 2*z """ - return x._im_gens_(self.codomain(), self.im_gens()) + return x._im_gens_(self.codomain(), self.im_gens(), base_map=self.base_map()) cdef class RingHomomorphism_from_base(RingHomomorphism): @@ -1356,7 +1398,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): raise ValueError("The given homomorphism has to have the codomain %s"%parent.codomain().base()) if parent.domain().construction()[0] != parent.codomain().construction()[0]: raise ValueError(f"domain ({parent.domain()}) and codomain ({parent.codomain()}) must have the same functorial construction over their base rings") - self.__underlying = underlying + self._underlying = underlying def underlying_map(self): """ @@ -1373,7 +1415,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): sage: g.underlying_map() == f True """ - return self.__underlying + return self._underlying cdef _update_slots(self, dict _slots): """ @@ -1402,7 +1444,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): sage: psi(x*t) 2*z*t """ - self.__underlying = _slots['__underlying'] + self._underlying = _slots['__underlying'] # double underscore for legacy pickles RingHomomorphism._update_slots(self, _slots) cdef dict _extra_slots(self): @@ -1433,7 +1475,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): 2*z*t """ slots = RingHomomorphism._extra_slots(self) - slots['__underlying'] = self.__underlying + slots['__underlying'] = self._underlying return slots cpdef _richcmp_(self, other, int op): @@ -1488,8 +1530,8 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): if not isinstance(other, RingHomomorphism_from_base): # Generic comparison return RingMap._richcmp_(self, other, op) - self_underlying = self.__underlying - other_underlying = (other).__underlying + self_underlying = self._underlying + other_underlying = (other)._underlying return richcmp(self_underlying, other_underlying, op) def _repr_defn(self): @@ -1514,7 +1556,7 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): Defn: x |--> x + y y |--> x - y """ - U = repr(self.__underlying).split('\n') + U = repr(self._underlying).split('\n') return 'Induced from base ring by\n'+'\n'.join(U) cpdef Element _call_(self, x): @@ -1530,15 +1572,15 @@ cdef class RingHomomorphism_from_base(RingHomomorphism): """ P = self.codomain() try: - return P(dict([(a, self.__underlying(b)) for a,b in x.dict().items()])) + return P(dict([(a, self._underlying(b)) for a,b in x.dict().items()])) except Exception: pass try: - return P([self.__underlying(b) for b in x]) + return P([self._underlying(b) for b in x]) except Exception: pass try: - return P(self.__underlying(x.numerator()))/P(self.__underlying(x.denominator())) + return P(self._underlying(x.numerator()))/P(self._underlying(x.denominator())) except Exception: raise TypeError("invalid argument %s" % repr(x)) diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index bee5b0710c5..cb8ef6e2804 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -455,9 +455,22 @@ def __call__(self, *x, **kwds): sage: s = M.hom([u, u+v]) sage: s(M.one()) 1 + + Since :trac:`26105` you can specify a map on the base ring:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: R. = PowerSeriesRing(K) + sage: f = s^2 + i*s*t + (3+4*i)*s^3 + R.O(4); f + s^2 + (i)*s*t + (4*i + 3)*s^3 + O(s, t)^4 + sage: f(t, s, base_map=cc) + (-i)*s*t + t^2 + (-4*i + 3)*t^3 + O(s, t)^4 """ if len(x) != self.parent().ngens(): raise ValueError("Number of arguments does not match number of variables in parent.") + if kwds: + return self._subs_formal(*x, **kwds) sub_dict = {} valn_list = [] @@ -467,7 +480,7 @@ def __call__(self, *x, **kwds): except (AttributeError, TypeError): # Input does not coerce to parent ring of self # attempt formal substitution - return self._subs_formal(*x,**kwds) + return self._subs_formal(*x, **kwds) if xi.valuation() == 0 and self.prec() is not infinity: raise TypeError("Substitution defined only for elements of positive valuation, unless self has infinite precision.") elif xi.valuation() > 0: @@ -538,8 +551,11 @@ def _subs_formal(self, *x, **kwds): return self y = 0 + base_map = kwds.get('base_map') for m, c in iteritems(self.dict()): - y += c*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) + if base_map is not None: + c = base_map(c) + y += c*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) if self.prec() == infinity: return y else: @@ -617,7 +633,7 @@ def _latex_(self): 'prec':self._prec} - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Returns the image of this series under the map that sends the generators to ``im_gens``. This is used internally for computing @@ -637,7 +653,11 @@ def _im_gens_(self, codomain, im_gens): sage: phi(a+b+3*a*b^2 + A.O(5)) # indirect doctest x + 2*y + 12*x*y^2 + O(x, y)^5 """ - return codomain(self(*im_gens)) + if base_map is None: + # __call__ might be faster if codomain coerces into the base ring + return codomain(self(*im_gens)) + else: + return codomain(self._subs_formal(*im_gens, base_map=base_map)) def __getitem__(self,n): """ diff --git a/src/sage/rings/number_field/maps.py b/src/sage/rings/number_field/maps.py index 57172271d3d..f60add31e33 100644 --- a/src/sage/rings/number_field/maps.py +++ b/src/sage/rings/number_field/maps.py @@ -553,8 +553,8 @@ def __init__(self, V, L, from_V, from_K): From: Vector space of dimension 4 over Rational Field To: Number Field in a with defining polynomial x^2 + 3 over its base field """ - self.__from_V = from_V - self.__from_K = from_K + self._from_V = from_V + self._from_K = from_K NumberFieldIsomorphism.__init__(self, Hom(V, L)) def _call_(self, x): @@ -568,7 +568,7 @@ def _call_(self, x): sage: fr(to(a)), fr(to(b)) # indirect doctest (a, b) """ - return self.__from_K(self.__from_V(x)) + return self._from_K(self._from_V(x)) class MapRelativeNumberFieldToVectorSpace(NumberFieldIsomorphism): r""" @@ -614,8 +614,8 @@ def __init__(self, L, V, to_K, to_V): From: Number Field in a with defining polynomial x^2 + 3 over its base field To: Vector space of dimension 4 over Rational Field """ - self.__to_K = to_K - self.__to_V = to_V + self._to_K = to_K + self._to_V = to_V NumberFieldIsomorphism.__init__(self, Hom(L, V)) def _call_(self, x): @@ -629,4 +629,4 @@ def _call_(self, x): sage: to(fr(V([1,3,0,1/17]))) # indirect doctest (1, 3, 0, 1/17) """ - return self.__to_V(self.__to_K(x)) + return self._to_V(self._to_K(x)) diff --git a/src/sage/rings/number_field/morphism.py b/src/sage/rings/number_field/morphism.py index a9d3f0b678c..b740969ba91 100644 --- a/src/sage/rings/number_field/morphism.py +++ b/src/sage/rings/number_field/morphism.py @@ -6,6 +6,7 @@ """ from sage.misc.cachefunc import cached_method +from sage.misc.superseded import deprecation from sage.rings.homset import RingHomset_generic from sage.rings.morphism import RingHomomorphism_im_gens, RingHomomorphism @@ -387,7 +388,7 @@ class RelativeNumberFieldHomset(NumberFieldHomset): sage: phi(phi(phi(cuberoot2 + zeta3))) cuberoot2 + zeta3 """ - def __call__(self, im_gen, base_hom=None, check=True): + def __call__(self, im_gen, base_map=None, base_hom=None, check=True): r""" Create a homomorphism in this homset from the given data, which can be: @@ -420,10 +421,21 @@ def __call__(self, im_gen, base_hom=None, check=True): Relative number field endomorphism of Number Field in b with defining polynomial x^4 - 2 over its base field Defn: b |--> -a*b a |--> -a - + + You can specify a map on the base field:: + + sage: R. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: L. = K.extension(x^2-17) + sage: cc = K.hom([-i]) + sage: phi = L.hom([-b],base_map=cc); phi + Relative number field endomorphism of Number Field in b with defining polynomial x^2 - 17 over its base field + Defn: b |--> -b + i |--> -i + Using check=False, it is possible to construct homomorphisms into fields such as CC where calculations are only approximate. - + sage: K. = QuadraticField(-7) sage: f = K.hom([CC(sqrt(-7))], check=False) sage: x = polygen(K) @@ -435,6 +447,9 @@ def __call__(self, im_gen, base_hom=None, check=True): Defn: b |--> 2.30833860703888 + 0.573085617291335*I a |--> -8.88178419700125e-16 + 2.64575131106459*I """ + if base_hom is not None: + deprecation(26105, "Use base_map rather than base_hom") + base_map = base_hom if isinstance(im_gen, NumberFieldHomomorphism_im_gens): # Then it must be a homomorphism from the corresponding # absolute number field @@ -450,13 +465,13 @@ def __call__(self, im_gen, base_hom=None, check=True): return RelativeNumberFieldHomomorphism_from_abs(self, abs_hom) if isinstance(im_gen, RelativeNumberFieldHomomorphism_from_abs): return self._coerce_impl(im_gen) - if base_hom is None: - base_hom = self.default_base_hom() + if base_map is None: + base_map = self.default_base_hom() if isinstance(im_gen, (list, tuple)) and len(im_gen) == 1: im_gen = im_gen[0] if check: im_gen = self.codomain()(im_gen) - return self._from_im(im_gen, base_hom, check=check) + return self._from_im(im_gen, base_map=base_map, check=check) def _coerce_impl(self, x): r""" @@ -480,7 +495,7 @@ def _coerce_impl(self, x): return RelativeNumberFieldHomomorphism_from_abs(self, x.abs_hom()) raise TypeError - def _from_im(self, im_gen, base_hom, check=True): + def _from_im(self, im_gen, base_map, check=True): """ Return the homomorphism that acts on the base as given and sends the generator of the domain to im_gen. @@ -498,14 +513,15 @@ def _from_im(self, im_gen, base_hom, check=True): from_K, to_K = K.structure() a = from_K(K.gen()) # We just have to figure out where a goes to - # under the morphism defined by im_gen and base_hom. + # under the morphism defined by im_gen and base_map. L = self.codomain() R = L['x'] - f = R([base_hom(x) for x in a.list()]) + f = R([base_map(x) for x in a.list()]) b = f(im_gen) abs_hom = K.hom([b], check=check) return RelativeNumberFieldHomomorphism_from_abs(self, abs_hom) + @cached_method def default_base_hom(self): r""" Pick an embedding of the base field of self into the codomain of this @@ -521,16 +537,12 @@ def default_base_hom(self): To: Number Field in c with defining polynomial x^4 + 80*x^2 + 36 Defn: b |--> 1/12*c^3 + 43/6*c """ - try: - return self.__default_base_hom - except AttributeError: - pass v = self.domain().base_field().embeddings(self.codomain()) if len(v) == 0: raise ValueError("no way to map base field to codomain.") - self.__default_base_hom = v[0] return v[0] + @cached_method def list(self): """ Return a list of all the elements of self (for which the domain @@ -568,18 +580,11 @@ def list(self): b |--> z^5 + z^3 - z ] """ - try: - return self.__list - except AttributeError: - pass D = self.domain() C = self.codomain() D_abs = D.absolute_field('a') v = [self(f, check=False) for f in D_abs.Hom(C).list()] - v = Sequence(v, universe=self, check=False, immutable=True, cr=v!=[]) - self.__list = v - return v - + return Sequence(v, universe=self, check=False, immutable=True, cr=v!=[]) class RelativeNumberFieldHomomorphism_from_abs(RingHomomorphism): r""" @@ -600,12 +605,11 @@ def __init__(self, parent, abs_hom): """ RingHomomorphism.__init__(self, parent) - self.__abs_hom = abs_hom + self._abs_hom = abs_hom K = abs_hom.domain() from_K, to_K = K.structure() - self.__K = K - self.__from_K = from_K - self.__to_K = to_K + self._from_K = from_K + self._to_K = to_K def abs_hom(self): r""" @@ -620,7 +624,7 @@ def abs_hom(self): To: Number Field in a with defining polynomial x^3 + 2 over its base field Defn: a |--> a - b """ - return self.__abs_hom + return self._abs_hom def _repr_type(self): r""" @@ -634,6 +638,7 @@ def _repr_type(self): """ return "Relative number field" + @cached_method def im_gens(self): r""" Return the images of the generators under this map. @@ -644,15 +649,9 @@ def im_gens(self): sage: K.hom(a, K).im_gens() [a, b] """ - try: - return self.__im_gens - except AttributeError: - pass D = self.domain() C = self.codomain() - v = Sequence([self(x) for x in D.gens()], universe=C, check=False, immutable=True) - self.__im_gens = v - return v + return Sequence([self(x) for x in D.gens()], universe=C, check=False, immutable=True) def _richcmp_(self, other, op): """ @@ -696,7 +695,7 @@ def _call_(self, x): sage: K.hom(a*b, K)(17 + 3*a + 2*b) # indirect doctest 3*b*a + 2*b + 17 """ - return self.__abs_hom(self.__to_K(x)) + return self._abs_hom(self._to_K(x)) class CyclotomicFieldHomset(NumberFieldHomset): @@ -760,6 +759,7 @@ def _coerce_impl(self, x): return CyclotomicFieldHomomorphism_im_gens(self, x.im_gens()) raise TypeError + @cached_method def list(self): """ Return a list of all the elements of self (for which the domain @@ -785,11 +785,6 @@ def list(self): sage: Hom(CyclotomicField(11), L).list() [] """ - try: - return self.__list - except AttributeError: - pass - D = self.domain() C = self.codomain() z = D.gen() @@ -802,9 +797,7 @@ def list(self): else: w = C.zeta(n) v = [self([w**k], check=False) for k in Zmod(n) if k.is_unit()] - v = Sequence(v, universe=self, check=False, immutable=True, cr=v!=[]) - self.__list = v - return v + return Sequence(v, universe=self, check=False, immutable=True, cr=v!=[]) class CyclotomicFieldHomomorphism_im_gens(NumberFieldHomomorphism_im_gens): pass diff --git a/src/sage/rings/number_field/number_field_element.pyx b/src/sage/rings/number_field/number_field_element.pyx index 0aad530e920..576860eec6c 100644 --- a/src/sage/rings/number_field/number_field_element.pyx +++ b/src/sage/rings/number_field/number_field_element.pyx @@ -456,7 +456,7 @@ cdef class NumberFieldElement(FieldElement): K = self.number_field() return str(x).replace(x.parent().variable_name(), K.variable_name()) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ This is used in computing homomorphisms between number fields. @@ -469,6 +469,7 @@ cdef class NumberFieldElement(FieldElement): b^2 + 1 sage: (a+1)._im_gens_(m, [b^2]) b^2 + 1 + """ # NOTE -- if you ever want to change this so relative number # fields are in terms of a root of a poly. The issue is that @@ -477,7 +478,12 @@ cdef class NumberFieldElement(FieldElement): # gives the image of gen, which need not be a generator for # the absolute field. The morphism has to be *over* the # relative element. - return codomain(self.polynomial()(im_gens[0])) + f = self.polynomial() + # The current implementation won't productively use base_map + # since the coefficients of f are in QQ. + if base_map is not None: + f = f.change_ring(base_map) + return codomain(f(im_gens[0])) def _latex_(self): """ diff --git a/src/sage/rings/number_field/structure.py b/src/sage/rings/number_field/structure.py index ab010944728..91e6db4a5ba 100644 --- a/src/sage/rings/number_field/structure.py +++ b/src/sage/rings/number_field/structure.py @@ -293,9 +293,9 @@ def create_structure(self, field): assert other_to_field(gen) == field(field.base_field().gen()) # to go from right to left, we first define a map from Q(gen) to other - base_hom = field.base_field().hom([gen], other) + base_map = field.base_field().hom([gen], other) # and extend it to a map from field - field_to_other = field.Hom(other)([other.gen()], base_hom=base_hom, check=True) + field_to_other = field.Hom(other)([other.gen()], base_map=base_map, check=True) return field_to_other, other_to_field @@ -372,12 +372,12 @@ def create_structure(self, field): gf = g*f base = other.base_field() base_to_field = base.Hom(field)([h(gf(other.gen(1)))]) - other_to_field = other.Hom(field)([h(gf(other.gen()))], base_hom=base_to_field) + other_to_field = other.Hom(field)([h(gf(other.gen()))], base_map=base_to_field) # And its inverse, essentially the same construction: f_g_ = f_*g_ base = field.base_field() base_to_other = base.Hom(other)([f_g_(h_(field.gen(1)))]) - field_to_other = field.Hom(other)([f_g_(h_(field.gen()))], base_hom=base_to_other) + field_to_other = field.Hom(other)([f_g_(h_(field.gen()))], base_map=base_to_other) return field_to_other, other_to_field diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 4f2958202c8..af6a2411cf3 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -377,10 +377,11 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): """ return not self.__u.is_zero() - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ - Return the image of ``self`` under the morphism defined by - ``im_gens`` in ``codomain``. + Return the image of this element under the morphism defined by + ``im_gens`` in ``codomain``, where elements of the + base ring are mapped by ``base_map``. EXAMPLES:: @@ -391,8 +392,23 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 17/4 sage: 4 + 1/4 17/4 - """ - return codomain(self(im_gens[0])) + + You can specify a map on the base ring:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: R. = LaurentPolynomialRing(K) + sage: H = Hom(R, R) + sage: phi = H([t^-2], base_map=cc) + sage: phi(i*t) + -i*t^-2 + """ + x = im_gens[0] + u = self.__u + if base_map is not None: + u = u.change_ring(base_map) + return codomain(u(x) * x**self.__n) cpdef __normalize(self): r""" diff --git a/src/sage/rings/polynomial/multi_polynomial_element.py b/src/sage/rings/polynomial/multi_polynomial_element.py index 3f722d4c91f..6ddd5e71bc3 100644 --- a/src/sage/rings/polynomial/multi_polynomial_element.py +++ b/src/sage/rings/polynomial/multi_polynomial_element.py @@ -194,21 +194,34 @@ def _richcmp_(self, right, op): return self.__element.rich_compare(right.__element, op, self.parent().term_order().sortkey) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ EXAMPLES:: sage: R. = PolynomialRing(QQbar, 2) sage: f = R.hom([y,x], R) - sage: f(x^2 + 3*y^5) + sage: f(x^2 + 3*y^5) # indirect doctest 3*x^5 + y^2 + + You can specify a map on the base ring:: + + sage: F. = ZZ[] + sage: F = F.fraction_field(); x,y = F(x),F(y) + sage: cc = F.hom([y,x]) + sage: R. = F[] + sage: phi = R.hom([w,z], base_map=cc, category=Rings()) + sage: phi(w/x) + 1/y*z """ n = self.parent().ngens() if n == 0: return codomain._coerce_(self) y = codomain(0) + if base_map is None: + # Just use conversion + base_map = codomain for (m,c) in iteritems(self.element().dict()): - y += codomain(c)*prod([ im_gens[i]**m[i] for i in range(n) if m[i] ]) + y += base_map(c)*prod([ im_gens[i]**m[i] for i in range(n) if m[i] ]) return y def number_of_terms(self): diff --git a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx index e4cc7591584..41aaf38870e 100644 --- a/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx +++ b/src/sage/rings/polynomial/multi_polynomial_libsingular.pyx @@ -5107,7 +5107,7 @@ cdef class MPolynomial_libsingular(MPolynomial): """ return unpickle_MPolynomial_libsingular, (self._parent, self.dict()) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ INPUT: @@ -5126,14 +5126,27 @@ cdef class MPolynomial_libsingular(MPolynomial): sage: h = R.hom([0,0,0,u], S) # indirect doctest sage: h((a+d)^3) u^3 + + You can specify a map on the base ring:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: R. = K[] + sage: phi = R.hom([y,x], base_map=cc, category=Rings()) + sage: phi(x + i*y) + (-i)*x + y """ #TODO: very slow n = self.parent().ngens() if n == 0: return codomain._coerce_(self) y = codomain(0) + if base_map is None: + # Just use conversion + base_map = codomain for (m,c) in self.dict().iteritems(): - y += codomain(c)*mul([ im_gens[i]**m[i] for i in range(n) if m[i]]) + y += base_map(c)*mul([ im_gens[i]**m[i] for i in range(n) if m[i]]) return y diff --git a/src/sage/rings/polynomial/plural.pyx b/src/sage/rings/polynomial/plural.pyx index 989bb54991c..72829792ee0 100644 --- a/src/sage/rings/polynomial/plural.pyx +++ b/src/sage/rings/polynomial/plural.pyx @@ -2221,7 +2221,7 @@ cdef class NCPolynomial_plural(RingElement): p = pNext(p) return pd - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` in codomain under the map that sends the images of the generators of the parent of ``self`` to the @@ -2235,24 +2235,40 @@ cdef class NCPolynomial_plural(RingElement): EXAMPLES:: - sage: A. = FreeAlgebra(GF(389), 3) + sage: A. = FreeAlgebra(GF(9), 3) sage: R = A.g_algebra(relations={y*x:-x*y + z}, order='lex') sage: R.inject_variables() Defining x, z, y - sage: B. = FreeAlgebra(GF(389), 3) + sage: B. = FreeAlgebra(GF(9), 3) sage: S = B.g_algebra({b*a:2*a*b, c*a:-2*a*c}) sage: S.inject_variables() Defining a, b, c sage: (x*y - x^2*z)._im_gens_(S, [a*b, b, a*b*c]) - -2*a^2*b^3 + 2*a^2*b^2*c + a^2*b^3 - a^2*b^2*c sage: -(a*b)*(a*b)*b+(a*b)*(a*b*c) - -2*a^2*b^3 + 2*a^2*b^2*c + a^2*b^3 - a^2*b^2*c + + sage: z2 = GF(9).gen() + sage: phi = R.hom([a*b, b, a*b*c], check=False) + sage: phi(x*y - x^2*z) + a^2*b^3 - a^2*b^2*c + sage: phi(x*y - z2*x^2*z) + (z2)*a^2*b^3 - a^2*b^2*c + sage: phi = R.hom([a*b, b, a*b*c], base_map=GF(9).frobenius_endomorphism(), category=Algebras(GF(9)), check=False) + sage: phi(x*y - x^2*z) + a^2*b^3 - a^2*b^2*c + sage: phi(x*y - z2*x^2*z) + (-z2 + 1)*a^2*b^3 - a^2*b^2*c + sage: z2^3 + 2*z2 + 1 """ if self.is_zero(): return codomain.zero() from sage.misc.misc_c import prod d = self.dict() - return sum(prod(im_gens[i]**val for i, val in enumerate(t))*codomain(d[t]) for t in d) + if base_map is None: + base_map = codomain + return sum(prod(im_gens[i]**val for i, val in enumerate(t))*base_map(d[t]) for t in d) cdef long _hash_c(self): diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index cbe40aeb1a1..adb82cb0904 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -1257,8 +1257,12 @@ cdef class Polynomial(CommutativeAlgebraElement): raise TypeError("cannot coerce nonconstant polynomial to int") return int(self.get_coeff_c(0)) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ + Return the image of this element under the morphism defined by + ``im_gens`` in ``codomain``, where elements of the + base ring are mapped by ``base_map``. + EXAMPLES:: sage: R. = ZZ[] @@ -1271,18 +1275,26 @@ cdef class Polynomial(CommutativeAlgebraElement): Defn: x |--> 5 sage: f(x) 5 - sage: f(x^2 + 3) + sage: f(x^2 + 3) # indirect doctest 28 + + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: S. = K[] + sage: phi = S.hom([y^2], base_map=cc, category=Rings()) + sage: phi(i*y) + -i*y^2 """ a = im_gens[0] - P = a.parent() d = self.degree() if d == -1: - return P.zero() # Special case: 0 should always coerce to 0 - result = P._coerce_(self.get_unsafe(d)) + return codomain.zero() # Special case: 0 should always coerce to 0 + if base_map is None: + base_map = codomain.coerce_map_from(self.base_ring()) + result = base_map(self.get_unsafe(d)) i = d - 1 while i >= 0: - result = result * a + P._coerce_(self.get_unsafe(i)) + result = result * a + base_map(self.get_unsafe(i)) i -= 1 return result diff --git a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py index 3994c569a4f..56b2adb339e 100644 --- a/src/sage/rings/polynomial/polynomial_quotient_ring_element.py +++ b/src/sage/rings/polynomial/polynomial_quotient_ring_element.py @@ -154,8 +154,27 @@ def __init__(self, parent, polynomial, check=True): polynomial = R self._polynomial = polynomial - def _im_gens_(self, codomain, im_gens): - return self._polynomial._im_gens_(codomain, im_gens) + def _im_gens_(self, codomain, im_gens, base_map=None): + """ + Return the image of this element under the morphism defined by + ``im_gens`` in ``codomain``, where elements of the + base ring are mapped by ``base_map``. + + EXAMPLES:: + + sage: Zx. = ZZ[] + sage: K. = NumberField(x^2 + 1) + sage: cc = K.hom([-i]) + sage: S. = K[] + sage: Q. = S.quotient(y^2*(y-1)*(y-i)) + sage: T. = S.quotient(y*(y+1)) + sage: phi = Q.hom([t+1], base_map=cc, category=Rings()) + sage: phi(q) + t + 1 + sage: phi(i*q) + -i*t - i + """ + return self._polynomial._im_gens_(codomain, im_gens, base_map=base_map) def __hash__(self): return hash(self._polynomial) @@ -534,8 +553,8 @@ def field_extension(self, names): if number_field_rel.is_RelativeNumberField(F): - base_hom = F.base_field().hom([R.base_ring().gen()]) - g = F.Hom(R)(x, base_hom) + base_map = F.base_field().hom([R.base_ring().gen()]) + g = F.Hom(R)(x, base_map) else: g = F.hom([x], R, check=False) diff --git a/src/sage/rings/power_series_ring_element.pyx b/src/sage/rings/power_series_ring_element.pyx index 2871d57f9ec..e100b14b4e8 100644 --- a/src/sage/rings/power_series_ring_element.pyx +++ b/src/sage/rings/power_series_ring_element.pyx @@ -245,7 +245,7 @@ cdef class PowerSeries(AlgebraElement): """ return bool(self.__is_gen) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of this series under the map that sends the generators to ``im_gens``. This is used internally for computing @@ -258,7 +258,10 @@ cdef class PowerSeries(AlgebraElement): sage: f._im_gens_(ZZ, [3]) 13 """ - return codomain(self(im_gens[0])) + if base_map is None: + return codomain(self(im_gens[0])) + else: + raise NotImplementedError cpdef base_extend(self, R): """ diff --git a/src/sage/rings/quotient_ring_element.py b/src/sage/rings/quotient_ring_element.py index 00ba54b8741..013e61fb241 100644 --- a/src/sage/rings/quotient_ring_element.py +++ b/src/sage/rings/quotient_ring_element.py @@ -416,7 +416,7 @@ def _div_(self, right): "a multiple of the denominator.") return P(XY[0]) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` in ``codomain`` under the map that sends ``self.parent().gens()`` to ``im_gens``. @@ -451,7 +451,7 @@ def _im_gens_(self, codomain, im_gens): sage: f(xbar/ybar) t """ - return self.lift()._im_gens_(codomain, im_gens) + return self.lift()._im_gens_(codomain, im_gens, base_map=base_map) def __int__(self): """ diff --git a/src/sage/rings/rational.pyx b/src/sage/rings/rational.pyx index 4c837528fd6..874cdfae6a5 100644 --- a/src/sage/rings/rational.pyx +++ b/src/sage/rings/rational.pyx @@ -1101,7 +1101,7 @@ cdef class Rational(sage.structure.element.FieldElement): mathml(abs(self.numer())), mathml(self.denom())) return t - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` under the homomorphism from the rational field to ``codomain``. diff --git a/src/sage/rings/real_double.pyx b/src/sage/rings/real_double.pyx index 5a25dd47830..266af51d8ef 100644 --- a/src/sage/rings/real_double.pyx +++ b/src/sage/rings/real_double.pyx @@ -1118,7 +1118,7 @@ cdef class RealDoubleElement(FieldElement): """ return hash(self._value) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` under the homomorphism from the rational field to ``codomain``. diff --git a/src/sage/rings/real_mpfi.pyx b/src/sage/rings/real_mpfi.pyx index c6d9bc4dda6..41cd7397eb2 100644 --- a/src/sage/rings/real_mpfi.pyx +++ b/src/sage/rings/real_mpfi.pyx @@ -1415,7 +1415,7 @@ cdef class RealIntervalFieldElement(RingElement): """ return hash(self.str(16)) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` under the homomorphism from the rational field to ``codomain``. diff --git a/src/sage/rings/real_mpfr.pyx b/src/sage/rings/real_mpfr.pyx index d98409902a5..2d1e8f6a948 100644 --- a/src/sage/rings/real_mpfr.pyx +++ b/src/sage/rings/real_mpfr.pyx @@ -1776,7 +1776,7 @@ cdef class RealNumber(sage.structure.element.RingElement): """ return hash(float(self)) - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` under the homomorphism from the rational field to ``codomain``. diff --git a/src/sage/structure/element.pyx b/src/sage/structure/element.pyx index cb1e1bf2501..c9adde95809 100644 --- a/src/sage/structure/element.pyx +++ b/src/sage/structure/element.pyx @@ -608,7 +608,7 @@ cdef class Element(SageObject): pass return res - def _im_gens_(self, codomain, im_gens): + def _im_gens_(self, codomain, im_gens, base_map=None): """ Return the image of ``self`` in codomain under the map that sends the images of the generators of the parent of ``self`` to the diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index f8c4ae85616..6843929a38c 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1263,90 +1263,102 @@ cdef class Parent(sage.structure.category_object.CategoryObject): from sage.categories.homset import Hom return Hom(self, codomain, category) - def hom(self, im_gens, codomain=None, check=None): - r""" - Return the unique homomorphism from self to codomain that - sends ``self.gens()`` to the entries of ``im_gens``. - Raises a TypeError if there is no such homomorphism. + def hom(self, im_gens, codomain=None, check=None, base_map=None, category=None): + r""" + Return the unique homomorphism from self to codomain that + sends ``self.gens()`` to the entries of ``im_gens``. + Raises a TypeError if there is no such homomorphism. - INPUT: + INPUT: - - ``im_gens`` -- the images in the codomain of the generators - of this object under the homomorphism + - ``im_gens`` -- the images in the codomain of the generators + of this object under the homomorphism - - ``codomain`` -- the codomain of the homomorphism + - ``codomain`` -- the codomain of the homomorphism - - ``check`` -- whether to verify that the images of generators - extend to define a map (using only canonical coercions). + - ``base_map`` -- a map from the base ring to the codomain. + If not given, coercion is used. - OUTPUT: + - ``check`` -- whether to verify that the images of generators + extend to define a map (using only canonical coercions). - A homomorphism self --> codomain + OUTPUT: - .. NOTE:: + A homomorphism self --> codomain - As a shortcut, one can also give an object X instead of - ``im_gens``, in which case return the (if it exists) - natural map to X. + .. NOTE:: - EXAMPLES: + As a shortcut, one can also give an object X instead of + ``im_gens``, in which case return the (if it exists) + natural map to X. - Polynomial Ring: We first illustrate construction of a few - homomorphisms involving a polynomial ring:: + EXAMPLES: - sage: R. = PolynomialRing(ZZ) - sage: f = R.hom([5], QQ) - sage: f(x^2 - 19) - 6 + Polynomial Ring: We first illustrate construction of a few + homomorphisms involving a polynomial ring:: - sage: R. = PolynomialRing(QQ) - sage: f = R.hom([5], GF(7)) - Traceback (most recent call last): - ... - ValueError: relations do not all (canonically) map to 0 under map determined by images of generators + sage: R. = PolynomialRing(ZZ) + sage: f = R.hom([5], QQ) + sage: f(x^2 - 19) + 6 - sage: R. = PolynomialRing(GF(7)) - sage: f = R.hom([3], GF(49,'a')) - sage: f - Ring morphism: - From: Univariate Polynomial Ring in x over Finite Field of size 7 - To: Finite Field in a of size 7^2 - Defn: x |--> 3 - sage: f(x+6) - 2 - sage: f(x^2+1) - 3 + sage: R. = PolynomialRing(QQ) + sage: f = R.hom([5], GF(7)) + Traceback (most recent call last): + ... + ValueError: relations do not all (canonically) map to 0 under map determined by images of generators + + sage: R. = PolynomialRing(GF(7)) + sage: f = R.hom([3], GF(49,'a')) + sage: f + Ring morphism: + From: Univariate Polynomial Ring in x over Finite Field of size 7 + To: Finite Field in a of size 7^2 + Defn: x |--> 3 + sage: f(x+6) + 2 + sage: f(x^2+1) + 3 - Natural morphism:: + Natural morphism:: - sage: f = ZZ.hom(GF(5)) - sage: f(7) - 2 - sage: f - Natural morphism: - From: Integer Ring - To: Finite Field of size 5 + sage: f = ZZ.hom(GF(5)) + sage: f(7) + 2 + sage: f + Natural morphism: + From: Integer Ring + To: Finite Field of size 5 - There might not be a natural morphism, in which case a - ``TypeError`` is raised:: + There might not be a natural morphism, in which case a + ``TypeError`` is raised:: - sage: QQ.hom(ZZ) - Traceback (most recent call last): - ... - TypeError: natural coercion morphism from Rational Field to Integer Ring not defined - """ - if isinstance(im_gens, Parent): - return self.Hom(im_gens).natural_map() - from sage.structure.sequence import Sequence_generic, Sequence - if codomain is None: - im_gens = Sequence(im_gens) - codomain = im_gens.universe() - if isinstance(im_gens, Sequence_generic): + sage: QQ.hom(ZZ) + Traceback (most recent call last): + ... + TypeError: natural coercion morphism from Rational Field to Integer Ring not defined + """ + if isinstance(im_gens, Parent): + return self.Hom(im_gens).natural_map() + from sage.structure.sequence import Sequence_generic, Sequence + if codomain is None: + im_gens = Sequence(im_gens) + codomain = im_gens.universe() + if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) - if check is None: - return self.Hom(codomain)(im_gens) - else: - return self.Hom(codomain)(im_gens, check=check) + kwds = {} + if check is not None: + kwds['check'] = check + if base_map is not None: + if category is None: + from sage.categories.sets_with_partial_maps import SetsWithPartialMaps + # It might be possible to be more precise with the category here + # by taking the meet of base_map.category_for() with the domain and + # codomain's categories. But it's not clear that this is always correct + # so we conservatively choose just SetsWithPartialMaps + category = SetsWithPartialMaps() + kwds['base_map'] = base_map + return self.Hom(codomain, category=category)(im_gens, **kwds) ################################################################################# # New Coercion support functionality diff --git a/src/sage/structure/parent_gens.pyx b/src/sage/structure/parent_gens.pyx index 414bb1221e5..49d93bbced6 100644 --- a/src/sage/structure/parent_gens.pyx +++ b/src/sage/structure/parent_gens.pyx @@ -207,10 +207,11 @@ cdef class ParentWithGens(ParentWithBase): # Morphisms of objects with generators ################################################################################# - def hom(self, im_gens, codomain=None, check=True): + def hom(self, im_gens, codomain=None, base_map=None, category=None, check=True): r""" Return the unique homomorphism from self to codomain that - sends ``self.gens()`` to the entries of ``im_gens``. + sends ``self.gens()`` to the entries of ``im_gens`` + and induces the map ``base_map`` on the base ring. Raises a TypeError if there is no such homomorphism. INPUT: @@ -220,6 +221,9 @@ cdef class ParentWithGens(ParentWithBase): - ``codomain`` - the codomain of the homomorphism + - ``base_map`` - a map from the base ring of the domain into something + that coerces into the codomain. + - ``check`` - whether to verify that the images of generators extend to define a map (using only canonical coercions). @@ -284,14 +288,19 @@ cdef class ParentWithGens(ParentWithBase): TypeError: natural coercion morphism from Rational Field to Integer Ring not defined """ if self._element_constructor is not None: - return parent.Parent.hom(self, im_gens, codomain, check) + return parent.Parent.hom(self, im_gens, codomain, base_map=base_map, category=category, check=check) if isinstance(im_gens, parent.Parent): return self.Hom(im_gens).natural_map() if codomain is None: from sage.structure.all import Sequence im_gens = Sequence(im_gens) codomain = im_gens.universe() - return self.Hom(codomain)(im_gens, check=check) + kwds = {} + if check is not None: + kwds['check'] = check + if base_map is not None: + kwds['base_map'] = base_map + return self.Hom(codomain, category=category)(im_gens, **kwds) cdef class localvars: From 1f78dd4c51b965187c3004eb5b743b41efe4e347 Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 12 Sep 2019 00:44:18 +0200 Subject: [PATCH 2/3] Fix some doctests --- src/sage/rings/laurent_series_ring_element.pyx | 2 +- src/sage/rings/morphism.pyx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/laurent_series_ring_element.pyx b/src/sage/rings/laurent_series_ring_element.pyx index 7bfe74412e3..8eb08413a01 100644 --- a/src/sage/rings/laurent_series_ring_element.pyx +++ b/src/sage/rings/laurent_series_ring_element.pyx @@ -294,7 +294,7 @@ cdef class LaurentSeries(AlgebraElement): sage: z._im_gens_(R, [t^2], base_map=cc) Traceback (most recent call last): ... - NotImplementedError: + NotImplementedError """ x = im_gens[0] return codomain(self.__u._im_gens_(codomain, im_gens, base_map=base_map) * x**self.__n) diff --git a/src/sage/rings/morphism.pyx b/src/sage/rings/morphism.pyx index 98e7308b97d..fc17a3dc1b2 100644 --- a/src/sage/rings/morphism.pyx +++ b/src/sage/rings/morphism.pyx @@ -1121,6 +1121,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism): sage: phi(i*y) -i*y^2 sage: phi.base_map() + Ring endomorphism of Number Field in i with defining polynomial x^2 + 1 + Defn: i |--> -i """ return self._base_map From 84704ba5472818a3efe97f6ef0784f4e383c512f Mon Sep 17 00:00:00 2001 From: David Roe Date: Thu, 12 Sep 2019 10:24:39 +0200 Subject: [PATCH 3/3] Fix doctests and py3, incorporate small reviewer suggestion --- src/sage/algebras/lie_algebras/lie_algebra_element.pyx | 8 +++----- src/sage/algebras/lie_algebras/morphism.py | 10 +++++----- src/sage/quivers/representation.py | 4 ++-- src/sage/rings/multi_power_series_ring_element.py | 6 +++--- src/sage/structure/parent.pyx | 4 +++- 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx index d54644647d8..d519d37b618 100644 --- a/src/sage/algebras/lie_algebras/lie_algebra_element.pyx +++ b/src/sage/algebras/lie_algebras/lie_algebra_element.pyx @@ -120,11 +120,9 @@ cdef class LieAlgebraElement(IndexedFreeModuleElement): return s names = self.parent().variable_names() if base_map is None: - return codomain.sum(c * t._im_gens_(codomain, im_gens, names) - for t, c in self._monomial_coefficients.iteritems()) - else: - return codomain.sum(base_map(c) * t._im_gens_(codomain, im_gens, names) - for t, c in self._monomial_coefficients.iteritems()) + base_map = lambda x: x + return codomain.sum(base_map(c) * t._im_gens_(codomain, im_gens, names) + for t, c in self._monomial_coefficients.iteritems()) cpdef lift(self): """ diff --git a/src/sage/algebras/lie_algebras/morphism.py b/src/sage/algebras/lie_algebras/morphism.py index bea2d600b43..2ccf85263d7 100644 --- a/src/sage/algebras/lie_algebras/morphism.py +++ b/src/sage/algebras/lie_algebras/morphism.py @@ -23,6 +23,7 @@ from sage.structure.richcmp import richcmp from sage.matrix.constructor import matrix from itertools import combinations +from six import iteritems # TODO: Refactor out common functionality with RingHomomorphism_im_gens class LieAlgebraHomomorphism_im_gens(Morphism): @@ -607,8 +608,7 @@ def _call_(self, x): 1/3*A - 1/3*B + 2/3*C """ C = self.codomain() - if self._base_map is None: - return C.sum(c * self._im_gens[i] for i, c in x.to_vector().iteritems()) - else: - bh = self._base_map - return C.sum(bh(c) * self._im_gens[i] for i, c in x.to_vector().iteritems()) + bh = self._base_map + if bh is None: + bh = lambda t: t + return C.sum(bh(c) * self._im_gens[i] for i, c in iteritems(x.to_vector())) diff --git a/src/sage/quivers/representation.py b/src/sage/quivers/representation.py index 60a6b680ee3..6512e879fc5 100644 --- a/src/sage/quivers/representation.py +++ b/src/sage/quivers/representation.py @@ -2425,7 +2425,7 @@ def algebraic_dual(self, basis=False): from sage.quivers.homspace import QuiverHomSpace return QuiverHomSpace(self, self._semigroup.free_module(self.base_ring())).left_module(basis) - def Hom(self, codomain): + def Hom(self, codomain, category=None): """ Return the hom space from ``self`` to ``codomain``. @@ -2438,7 +2438,7 @@ def Hom(self, codomain): Dimension 2 QuiverHomSpace """ from sage.quivers.homspace import QuiverHomSpace - return QuiverHomSpace(self, codomain) + return QuiverHomSpace(self, codomain, category=category) def direct_sum(self, modules, return_maps=False): """ diff --git a/src/sage/rings/multi_power_series_ring_element.py b/src/sage/rings/multi_power_series_ring_element.py index cb8ef6e2804..111a0bf525d 100644 --- a/src/sage/rings/multi_power_series_ring_element.py +++ b/src/sage/rings/multi_power_series_ring_element.py @@ -552,10 +552,10 @@ def _subs_formal(self, *x, **kwds): y = 0 base_map = kwds.get('base_map') + if base_map is None: + base_map = lambda t: t for m, c in iteritems(self.dict()): - if base_map is not None: - c = base_map(c) - y += c*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) + y += base_map(c)*prod([x[i]**m[i] for i in range(n) if m[i] != 0]) if self.prec() == infinity: return y else: diff --git a/src/sage/structure/parent.pyx b/src/sage/structure/parent.pyx index 6843929a38c..50ca7fa616c 100644 --- a/src/sage/structure/parent.pyx +++ b/src/sage/structure/parent.pyx @@ -1346,6 +1346,7 @@ cdef class Parent(sage.structure.category_object.CategoryObject): codomain = im_gens.universe() if isinstance(im_gens, Sequence_generic): im_gens = list(im_gens) + # Not all homsets accept catgory/check/base_map as arguments kwds = {} if check is not None: kwds['check'] = check @@ -1358,7 +1359,8 @@ cdef class Parent(sage.structure.category_object.CategoryObject): # so we conservatively choose just SetsWithPartialMaps category = SetsWithPartialMaps() kwds['base_map'] = base_map - return self.Hom(codomain, category=category)(im_gens, **kwds) + Hom_kwds = {} if category is None else {'category': category} + return self.Hom(codomain, **Hom_kwds)(im_gens, **kwds) ################################################################################# # New Coercion support functionality