Skip to content
This repository has been archived by the owner on Jan 30, 2023. It is now read-only.

Commit

Permalink
Fix _is_valid_homomorphism_
Browse files Browse the repository at this point in the history
  • Loading branch information
roed314 committed Sep 13, 2019
1 parent 84704ba commit c4f2a3f
Show file tree
Hide file tree
Showing 19 changed files with 182 additions and 60 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -646,7 +646,7 @@ def random_element(self, *args, **kwargs):
"""
return self(self.zero().vector().parent().random_element(*args, **kwargs))

def _is_valid_homomorphism_(self, other, im_gens):
def _is_valid_homomorphism_(self, other, im_gens, base_map=None):
"""
TESTS::
Expand Down Expand Up @@ -679,11 +679,13 @@ def _is_valid_homomorphism_(self, other, im_gens):
"""
assert len(im_gens) == self.degree()

if base_map is None:
base_map = lambda x: x
B = self.table()
for i,gi in enumerate(im_gens):
for j,gj in enumerate(im_gens):
eiej = B[j][i]
if (sum([other(im_gens[k]) * v for k,v in enumerate(eiej)])
if (sum([other(im_gens[k]) * base_map(v) for k,v in enumerate(eiej)])
!= other(gi) * other(gj)):
return False
return True
Expand Down
29 changes: 28 additions & 1 deletion src/sage/algebras/lie_algebras/morphism.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,33 @@ class LieAlgebraHomomorphism_im_gens(Morphism):
Defn: x |--> x
y |--> y
z |--> z
You can provide a base map, creating a semilinear map that (sometimes)
preserves the Lie bracket::
sage: R.<x> = ZZ[]
sage: K.<i> = NumberField(x^2 + 1)
sage: cc = K.hom([-i])
sage: L.<X,Y,Z,W> = LieAlgebra(K, {('X','Y'): {'Z':1}, ('X','Z'): {'W':1}})
sage: M.<A,B,C,D> = LieAlgebra(K, {('A','B'): {'C':1}, ('A','C'): {'D':1}})
sage: phi = L.morphism({X:A, Y:B, Z:C, W:D}, base_map=cc)
sage: phi(X)
A
sage: phi(i*X)
-i*A
sage: all(phi(x.bracket(y)) == phi(x).bracket(phi(y)) for x,y in cartesian_product_iterator([[X,Y,Z,W],[X,Y,Z,W]]))
True
However, if the structure constants are not fixed by the base map,
the Lie bracket may not be preserved::
sage: L.<X,Y,Z,W> = LieAlgebra(K, {('X','Y'): {'Z':i}, ('X','Z'): {'W':1}})
sage: M.<A,B,C,D> = LieAlgebra(K, {('A','B'): {'C':i}, ('A','C'): {'D':1}})
sage: phi = L.morphism({X:A, Y:B, Z:C, W:D}, base_map=cc)
sage: phi(X.bracket(Y))
-i*C
sage: phi(X).bracket(phi(Y))
i*C
"""
def __init__(self, parent, im_gens, base_map=None, check=True):
"""
Expand All @@ -89,7 +116,7 @@ def __init__(self, parent, im_gens, base_map=None, check=True):
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):
#if not parent.domain()._is_valid_homomorphism_(parent.codomain(), im_gens, base_map=base_map):
# raise ValueError("relations do not all (canonically) map to 0 under map determined by images of generators.")
if not im_gens.is_immutable():
import copy
Expand Down
15 changes: 11 additions & 4 deletions src/sage/rings/finite_rings/finite_field_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ cdef class FiniteField(Field):
if lim == <unsigned long>(-1):
raise NotImplementedError("iterating over all elements of a large finite field is not supported")

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
Return ``True`` if the map from self to codomain sending
``self.0`` to the unique element of ``im_gens`` is a valid field
Expand Down Expand Up @@ -430,12 +430,19 @@ cdef class FiniteField(Field):
...
TypeError: images do not define a valid homomorphism
"""
if self.characteristic() != codomain.characteristic():
raise ValueError("no map from %s to %s" % (self, codomain))
#if self.characteristic() != codomain.characteristic():
# raise ValueError("no map from %s to %s" % (self, codomain))
# When the base is not just Fp, we want to ensure that there's a
# coercion map from the base rather than just checking the characteristic
if base_map is None and not codomain.has_coerce_map_from(self.base_ring()):
return False
if len(im_gens) != 1:
raise ValueError("only one generator for finite fields")

return self.modulus()(im_gens[0]).is_zero()
f = self.modulus()
if base_map is not None:
f = f.change_ring(base_map)
return f(im_gens[0]).is_zero()

def _Hom_(self, codomain, category=None):
"""
Expand Down
14 changes: 8 additions & 6 deletions src/sage/rings/fraction_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -771,7 +771,7 @@ def gen(self, i=0):
r = self._element_class(self, x, one, coerce=False, reduce=False)
return r

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
Check if the homomorphism defined by sending generators of this
fraction field to ``im_gens`` in ``codomain`` is valid.
Expand Down Expand Up @@ -801,11 +801,13 @@ def _is_valid_homomorphism_(self, codomain, im_gens):
"""
if len(im_gens) != self.ngens():
return False
# it is enough to check if elements of the base ring coerce to
# the codomain
if codomain.has_coerce_map_from(self.base_ring()):
return True
return False
# It is very difficult to check that the image of any element
# is invertible. Checking that the image of each generator
# is a unit is not sufficient. So we just give up and check
# that elements of the base ring coerce to the codomain
if base is None and not codomain.has_coerce_map_from(self.base_ring()):
return False
return True

def random_element(self, *args, **kwds):
"""
Expand Down
8 changes: 6 additions & 2 deletions src/sage/rings/integer_ring.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
else:
raise ValueError("Unknown distribution for the integers: %s" % distribution)

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
r"""
Tests whether the map from `\ZZ` to codomain, which takes the
generator of `\ZZ` to ``im_gens[0]``, is a ring homomorphism.
Expand All @@ -799,8 +799,12 @@ cdef class IntegerRing_class(PrincipalIdealDomain):
sage: ZZ._is_valid_homomorphism_(ZZ.quotient_ring(8),[ZZ.quotient_ring(8)(1)])
True
"""
if base_map is None:
base_map = codomain.coerce_map_from(self)
if base_map is None:
return False
try:
return im_gens[0] == codomain.coerce(self.gen(0))
return im_gens[0] == base_map(self.gen(0))
except TypeError:
return False

Expand Down
20 changes: 15 additions & 5 deletions src/sage/rings/laurent_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -587,7 +587,7 @@ def _coerce_map_from_(self, P):
and A.has_coerce_map_from(P.base_ring())):
return True

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
EXAMPLES::
Expand All @@ -600,15 +600,25 @@ def _is_valid_homomorphism_(self, codomain, im_gens):
sage: f = R.hom(x+x^3,R)
sage: f(x^2)
x^2 + 2*x^4 + x^6
The image of the generator needs to be a unit::
sage: R.<x> = LaurentSeriesRing(ZZ)
sage: R._is_valid_homomorphism_(R, [2*x])
False
"""
## NOTE: There are no ring homomorphisms from the ring of
## all formal power series to most rings, e.g, the p-adic
## field, since you can always (mathematically!) construct
## some power series that doesn't converge.
## Note that 0 is not a *ring* homomorphism.
from .power_series_ring import is_PowerSeriesRing
if is_PowerSeriesRing(codomain) or is_LaurentSeriesRing(codomain):
return im_gens[0].valuation() > 0 and codomain.has_coerce_map_from(self.base_ring())
## NOTE: The above claim is wrong when the base ring is Z.
## See trac 28486.

if base_map is None and not codomain.has_coerce_map_from(self.base_ring()):
return False
## Note that 0 is not a *ring* homomorphism, and you cannot map to a power series ring
if is_LaurentSeriesRing(codomain):
return im_gens[0].valuation() > 0 and im_gens[0].is_unit()
return False

def characteristic(self):
Expand Down
3 changes: 2 additions & 1 deletion src/sage/rings/morphism.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1066,7 +1066,8 @@ cdef class RingHomomorphism_im_gens(RingHomomorphism):
if check:
if len(im_gens) != parent.domain().ngens():
raise ValueError("number of images must equal number of generators")
t = parent.domain()._is_valid_homomorphism_(parent.codomain(), im_gens)
tkwds = {} if base_map is None else {'base_map': base_map}
t = parent.domain()._is_valid_homomorphism_(parent.codomain(), im_gens, **tkwds)
if not t:
raise ValueError("relations do not all (canonically) map to 0 under map determined by images of generators")
if not im_gens.is_immutable():
Expand Down
26 changes: 22 additions & 4 deletions src/sage/rings/multi_power_series_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def _coerce_impl(self, f):
else:
return self._coerce_try(f,[self.base_ring()])

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
Replacement for method of PowerSeriesRing_generic.
Expand Down Expand Up @@ -668,6 +668,19 @@ def _is_valid_homomorphism_(self, codomain, im_gens):
sage: g = [S.random_element(10)*v for v in S.gens()]
sage: M._is_valid_homomorphism_(S,g)
True
You must either give a base map or there must be a coercion
from the base ring to the codomain::
sage: T.<t> = ZZ[]
sage: K.<i> = NumberField(t^2 + 1)
sage: Q8.<z> = CyclotomicField(8)
sage: X.<x> = PowerSeriesRing(Q8)
sage: M.<a,b,c> = PowerSeriesRing(K)
sage: M._is_valid_homomorphism_(X, [x,x,x+x^2]) # no coercion
False
sage: M._is_valid_homomorphism_(X, [x,x,x+x^2], base_map=K.hom([z^2]))
True
"""
try:
im_gens = [codomain(v) for v in im_gens]
Expand All @@ -676,17 +689,22 @@ def _is_valid_homomorphism_(self, codomain, im_gens):

if len(im_gens) is not self.ngens():
raise ValueError("You must specify the image of each generator.")
if base_map is None and not codomain.has_coerce_map_from(self.base_ring()):
return False
if all(v == 0 for v in im_gens):
return True
if is_MPowerSeriesRing(codomain) or is_PowerSeriesRing(codomain):
from .laurent_series_ring import is_LaurentSeriesRing
if is_PowerSeriesRing(codomain) or is_LaurentSeriesRing(codomain):
try:
B = all(v.valuation() > 0 or v.is_nilpotent() for v in im_gens)
except NotImplementedError:
B = all(v.valuation() > 0 for v in im_gens)
return B
if isinstance(codomain, CommutativeRing):
try:
return all(v.is_nilpotent() for v in im_gens)

except NotImplementedError:
pass
return False

def _coerce_map_from_(self, P):
"""
Expand Down
13 changes: 9 additions & 4 deletions src/sage/rings/number_field/number_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -3723,7 +3723,7 @@ def completely_split_primes(self, B = 200):
split_primes.append(p)
return split_primes

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
Return whether or not there is a homomorphism defined by the given
images of generators.
Expand All @@ -3748,12 +3748,17 @@ def _is_valid_homomorphism_(self, codomain, im_gens):
"""
if len(im_gens) != 1:
return False
# We need that elements of the base ring of the polynomial
# ring map canonically into codomain.
if not codomain.has_coerce_map_from(QQ):
if base_map is None and not codomain.has_coerce_map_from(QQ):
# We need that elements of the base ring of the polynomial
# ring map canonically into codomain.
return False
f = self.defining_polynomial()
try:
# The current implementation won't productively use base_map
# since the coefficients of f are in QQ, if there is a hom
# from QQ to codomain it's probably unique and just the coercion
if base_map is not None:
f = f.change_ring(base_map)
return codomain(f(im_gens[0])) == 0
except (TypeError, ValueError):
return False
Expand Down
15 changes: 11 additions & 4 deletions src/sage/rings/polynomial/laurent_polynomial_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -717,15 +717,22 @@ def ideal(self):
"""
raise NotImplementedError

def _is_valid_homomorphism_(self, codomain, im_gens):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
EXAMPLES::
sage: L.<x,y> = LaurentPolynomialRing(QQ)
sage: L._is_valid_homomorphism_(QQ, (1/2, 3/2))
sage: T.<t> = ZZ[]
sage: K.<i> = NumberField(t^2 + 1)
sage: L.<x,y> = LaurentPolynomialRing(K)
sage: L._is_valid_homomorphism_(K, (K(1/2), K(3/2)))
True
sage: Q5 = Qp(5); i5 = Q5(-1).sqrt()
sage: L._is_valid_homomorphism_(Q5, (Q5(1/2), Q5(3/2))) # no coercion
False
sage: L._is_valid_homomorphism_(Q5, (Q5(1/2), Q5(3/2)), base_map=K.hom([i5]))
True
"""
if not codomain.has_coerce_map_from(self.base_ring()):
if base_map is None and not codomain.has_coerce_map_from(self.base_ring()):
# we need that elements of the base ring
# canonically coerce into codomain.
return False
Expand Down
26 changes: 20 additions & 6 deletions src/sage/rings/polynomial/multi_polynomial_ring_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,26 @@ cdef class MPolynomialRing_base(sage.rings.ring.CommutativeRing):
def _ideal_class_(self, n=0):
return multi_polynomial_ideal.MPolynomialIdeal

def _is_valid_homomorphism_(self, codomain, im_gens):
# all that is needed is that elements of the base ring
# of the polynomial ring canonically coerce into codomain.
# Since poly rings are free, any image of the gen
# determines a homomorphism
return codomain.has_coerce_map_from(self._base)
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
EXAMPLES::
sage: T.<t> = ZZ[]
sage: K.<i> = NumberField(t^2 + 1)
sage: R.<x,y> = K[]
sage: Q5 = Qp(5); i5 = Q5(-1).sqrt()
sage: R._is_valid_homomorphism_(Q5, [Q5.teichmuller(2), Q5(6).log()]) # no coercion
False
sage: R._is_valid_homomorphism_(Q5, [Q5.teichmuller(2), Q5(6).log()], base_map=K.hom([i5]))
True
"""
if base_map is None:
# all that is needed is that elements of the base ring
# of the polynomial ring canonically coerce into codomain.
# Since poly rings are free, any image of the gen
# determines a homomorphism
return codomain.has_coerce_map_from(self._base)
return True

def _magma_init_(self, magma):
"""
Expand Down
27 changes: 22 additions & 5 deletions src/sage/rings/polynomial/polynomial_quotient_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -562,15 +562,32 @@ def _coerce_map_from_(self, R):
parent = Hom(R, self, category=self.category()._meet_(R.category()))
return parent.__make_element_class__(PolynomialQuotientRing_coercion)(R, self, category=parent.homset_category())

def _is_valid_homomorphism_(self, codomain, im_gens):
# We need that elements of the base ring of the polynomial
# ring map canonically into codomain.
if not codomain.has_coerce_map_from(self.base_ring()):
def _is_valid_homomorphism_(self, codomain, im_gens, base_map=None):
"""
EXAMPLES::
sage: T.<t> = ZZ[]
sage: K.<i> = NumberField(t^2 + 1)
sage: R.<x> = K[]
sage: S.<a> = R.quotient(x^2 - i)
sage: Q8.<z> = CyclotomicField(8)
sage: S._is_valid_homomorphism_(Q8, [z]) # no coercion from K to Q8
False
sage: S._is_valid_homomorphism_(Q8, [z], K.hom([z^2]))
True
sage: S._is_valid_homomorphism_(Q8, [1/z], K.hom([z^-2]))
True
"""
if base_map is None and not codomain.has_coerce_map_from(self.base_ring()):
# If no base_map given, we need that elements of the base ring
# of the polynomial ring map canonically into codomain.
return False

# We also need that the polynomial modulus maps to 0.
# We also need that the polynomial modulus maps to 0, after twisting by the base_map
f = self.modulus()
try:
if base_map is not None:
f = f.change_ring(base_map)
return codomain(f(im_gens[0])) == 0
except (TypeError, ValueError):
return False
Expand Down

0 comments on commit c4f2a3f

Please sign in to comment.