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

Commit 61c73bb

Browse files
committed
#14982 Make coercions that go through lazy fields more robust
Coercions into lazy fields are currently defined by a kind of universal property: anything that coerces into parents into which the lazy field coerces also coerces into the lazy field. Besides, one of the possible classes of lazy field elements is a wrapper around elements of other exact fields, and the coercion of such wrappers into other parents simply converts the wrapped elements. This is a bit fragile, both because there is no way to know for sure all the parents into which the lazy field coerces, and because that way to define and apply coercion maps may lead to infinite loops. This patch makes three changes to mitigate the issue. 1. Test for coercions into all (known) rings into which the lazy field coerces instead of just into the associated interval field. 2. Only consider direct (as opposed to composite) coercions. 3. Check that the conversion path does not already goes through the lazy field when converting a wrapper.
1 parent 47b6a0e commit 61c73bb

File tree

2 files changed

+61
-5
lines changed

2 files changed

+61
-5
lines changed

src/sage/categories/map.pyx

+25
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,17 @@ cdef class Map(Element):
591591
s += "\n Defn: %s"%('\n '.join(d.split('\n')))
592592
return s
593593

594+
def domains(self):
595+
"""
596+
Yield the domain of self. In general, iterate over the domains of
597+
the factors of a composite map.
598+
599+
EXAMPLES::
600+
sage: list(QQ.coerce_map_from(ZZ).domains())
601+
[Integer Ring]
602+
"""
603+
yield self.domain()
604+
594605
def category_for(self):
595606
"""
596607
Returns the category self is a morphism for.
@@ -1903,3 +1914,17 @@ cdef class FormalCompositeMap(Map):
19031914
if all(f.is_surjective() for f in without_bij):
19041915
return True
19051916
raise NotImplementedError("Not enough information to deduce surjectivity.")
1917+
1918+
def domains(self):
1919+
"""
1920+
Iterate over the domains of the factors of self.
1921+
1922+
EXAMPLES:::
1923+
1924+
sage: f = QQ.coerce_map_from(ZZ)
1925+
sage: g = MatrixSpace(QQ, 2, 2).coerce_map_from(QQ)
1926+
sage: list((g*f).domains())
1927+
[Integer Ring, Rational Field]
1928+
"""
1929+
for f in self.__list:
1930+
yield f.domain()

src/sage/rings/real_lazy.pyx

+36-5
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ from operator import add, sub, mul, div, pow, neg, inv
2626

2727
cdef canonical_coercion
2828
from sage.structure.element import canonical_coercion
29+
from sage.structure.all import parent
2930

31+
import sage.categories.map
3032
from sage.categories.morphism cimport Morphism
3133
from sage.rings.ring cimport Field
3234
import sage.rings.infinity
@@ -112,8 +114,20 @@ cdef class LazyField(Field):
112114
cpdef _coerce_map_from_(self, R):
113115
r"""
114116
The only things that coerce into this ring are exact rings that
115-
embed into `\RR` or `\CC` (depending on whether or not this field
116-
is real or complex).
117+
embed into `\RR` or `\CC` (depending on whether this field
118+
is real or complex), that is, exact rings that coerce into all
119+
rings into which this ring coerces.
120+
121+
.. NOTE::
122+
123+
The rings into which this ring coerces are currently the
124+
corresponding floating-point fields (RealField(p) or
125+
ComplexField(p)), machine-precision floating-point fields (RDF,
126+
CDF), and interval fields (RealIntervalField(p),
127+
ComplexIntervalField(p)). This method should be updated if a new
128+
parent is added that declares a coercion from RLF/CLF but not from
129+
one of these, otherwise coercions of elements of type LazyWrapper
130+
into the new ring might fail.
117131
118132
EXAMPLES::
119133
@@ -141,8 +155,18 @@ cdef class LazyField(Field):
141155
if R in [int, long]:
142156
from sage.structure.parent import Set_PythonType
143157
return LazyWrapperMorphism(Set_PythonType(R), self)
144-
elif self.interval_field().has_coerce_map_from(R) and R.is_exact():
145-
return LazyWrapperMorphism(R, self)
158+
elif R.is_exact():
159+
ivf = self.interval_field()
160+
mor = ivf.coerce_map_from(R)
161+
# Indirect coercions might lead to loops both in the coercion
162+
# discovery algorithm and when trying to convert LazyWrappers,
163+
# so we only consider direct coercions.
164+
if mor is not None and not isinstance(mor, sage.categories.map.FormalCompositeMap):
165+
mor = ivf._middle_field().coerce_map_from(R)
166+
if mor is not None and not isinstance(mor, sage.categories.map.FormalCompositeMap):
167+
return LazyWrapperMorphism(R, self)
168+
# We can skip the test for a coercion to RDF/CDF since RR/CC
169+
# already coerce into it.
146170

147171
def algebraic_closure(self):
148172
"""
@@ -1004,7 +1028,14 @@ cdef class LazyWrapper(LazyFieldElement):
10041028
sage: a.eval(ZZ).parent()
10051029
Integer Ring
10061030
"""
1007-
return R(self._value)
1031+
try:
1032+
mor = R.convert_map_from(parent(self._value))
1033+
except AttributeError:
1034+
return R(self._value)
1035+
if mor is not None and self.parent() not in mor.domains():
1036+
return mor(self._value)
1037+
else:
1038+
raise TypeError("unable to convert {} to an element of {}".format(self._value, R))
10081039

10091040
def __reduce__(self):
10101041
"""

0 commit comments

Comments
 (0)