# How to extend ring homomorphism to polynomial ring (or its fraction field)

I have a homomorphism from a number field nf to the field of algebraic numbers QQbar:

nf, alpha, hom = QQbar(sqrt(2)).as_number_field_element()

I now work in the polynomial ring R over nf:

R.<x> = nf[]

f = x - alpha; f

How do I get a homomorphism from R to the polynomial ring over QQbar extending hom? For the moment, I can use

f.map_coefficients(hom)

Same question about the fraction field of R, e.g.,

g = f/(x+1)

Is there a more elegant way than calling

g.numerator().map_coefficients(hom)/g.denominator().map_coefficients(hom)

So basically, I'd like to extend my homomorphism hom to the polynomial ring and its field of fractions.

edit retag close merge delete

Sort by » oldest newest most voted

There are some tools to wrap things up a bit. Note that "map_coefficients" might still do funny things with the codomain parent: for instance, in your case I think you end up with polynomials over the Algebraic Real Field rather than QQbar.

   sage: from sage.structure.coerce_maps import CallableConvertMap
sage: H=CallableConvertMap(RR,QbarX,lambda self,f:QbarX(f.map_coefficients(hom)))


Making a homomorphism between the fields of fractions could be done similarly. I think the tools for this kind of stuff could be made more convenient and/or be documented in a more discoverable way.

In your particular case there might be a shortcut. It's not as general as good tools to naturally extend ring homomorphisms to modules and algebras over these bases. Given that your number field comes from QQbar, it should perhaps be constructed with an embedding into it as well. We can hack our way around that a little bit by registering the embedding ourselves (since this modifies the coercion graph at a node that might already have been used, it could lead to inconsistencies, so we have to take some extra steps to neutralize safety precautions. Furthermore, it modifies the behaviour of a possibly globally unique parent, so it's really a hack at this point. Embeddings should be supplied upon construction, not added later):

sage: nf, alpha, hom = QQbar(sqrt(2)).as_number_field_element()
sage: nf._unset_coercions_used()
sage: nf.register_embedding(hom)
sage: nfX.<x>=nf[]
sage: QbarX.<x>=QQbar[]
sage: 1/nfX.0 - QbarX.0
(-x^2 + 1)/x
sage: parent(1/nfX.0 - QbarX.0)
Fraction Field of Univariate Polynomial Ring in x over Algebraic Field
sage: QbarX.coerce_map_from(nfX)

Ring morphism:
From: Univariate Polynomial Ring in x over Number Field in a with defining polynomial y^2 - 2
To:   Univariate Polynomial Ring in x over Algebraic Field
Defn: Induced from base ring by
Composite map:
From: Number Field in a with defining polynomial y^2 - 2
To:   Algebraic Field
Defn:   Ring morphism:
From: Number Field in a with defining polynomial y^2 - 2
To:   Algebraic Real Field
Defn: a |--> 1.414213562373095?
then
Conversion map:
From: Algebraic Real Field
To:   Algebraic Field


Sage does have quite good tools available for discovering/constructing these kinds of induced maps, but unfortunately they are only available for registered coercion maps. It would be great if the same tools would also be usable with an explicitly given, non-standard set of homomorphisms for the construction of new maps.

more

It worked once, but then my code came accross another example and UniqueRepresentation gave me problems. So I ended up avoiding as_number_field_element alltogether and replacing it by

alpha_qqbar = QQbar(sqrt(2))
nf = NumberField(alpha_qqbar.minpoly(), 'alpha', embedding=alpha_qqbar)
hom = nf.coerce_embedding()
x1 = polygen(nf, 'x')
x2 = polygen(QQbar, 'x')
1/x1 - 1/x2


Good example for why we need a to be a little careful with the UniqueRepresentation thing. In this case, I think as_number_field_element can be patched to return the number field as you construct it. Also: the method is called as_*_element, so the first returned value should be the element, not the parent!

I now have performance problems with my above solution:

R.<x> = AA[]
v = QQbar.polynomial_root(AA.common_polynomial(x^5 - 2), CIF(RIF(RR(0.35496731310463009), RR(0.35496731310463014)), RIF(RR(1.0924770557774537), RR(1.0924770557774539))))
rho = 1/60*v^4 + 1/30*v^3 + 1/60*v^2 + 1/30*v + 1/15
mp = rho.minpoly()
%time NumberField(mp, 'rho', embedding=rho)
CPU times: user 1min 5s, sys: 64 ms, total: 1min 5s
Wall time: 1min 5s
Number Field in rho with defining polynomial x^5 - 1/3*x^4 + 1/30*x^3 - 1/600*x^2 + 1/24000*x - 1/2400000


Any ideas?

I think QQbar is designed to have performance problems, so to a large extent this is a feature. Apparently, there are no attempts to simplify the algebraic data on roots, so your description of "rho" is just horrible. You could improve it by hand:

sage: rho0=[v for v in mp.roots(QQbar) if v==rho]
sage: %time NumberField(mp, 'rho', embedding=rho0)
CPU times: user 11.5 ms, sys: 1.06 ms, total: 12.6 ms
Wall time: 11.5 ms
Number Field in rho with defining polynomial x^5 - 1/3*x^4 + 1/30*x^3 - 1/600*x^2 + 1/24000*x - 1/2400000