Skip to content

Commit

Permalink
Trac #23184: Use the right category for DefaultConvertMaps, rather th…
Browse files Browse the repository at this point in the history
…an SetsWithPartialMaps

Currently, the category for `DefaultConvertMap`s is
`SetsWithPartialMaps()`.
{{{
sage: type(QQ[['x']].coerce_map_from(QQ))
<type 'sage.structure.coerce_maps.DefaultConvertMap_unique'>
sage: QQ[['x']].coerce_map_from(QQ).category_for()
Category of sets with partial maps
}}}
In contrast,
{{{
sage: QQ.hom(QQ[['x']]).category_for()
Category of euclidean domains
}}}

URL: https://trac.sagemath.org/23184
Reported by: roed
Ticket author(s): David Roe, Julian Rüth
Reviewer(s): Julian Rüth, David Roe
  • Loading branch information
Release Manager authored and vbraun committed Jun 11, 2017
2 parents 39d5735 + 5eccb52 commit 8266e46
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 31 deletions.
6 changes: 4 additions & 2 deletions src/sage/categories/homset.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,14 +707,16 @@ def __bool__(self):

__nonzero__ = __bool__

def _generic_convert_map(self, S):
def _generic_convert_map(self, S, category=None):
"""
Return a generic map from a given homset to ``self``.
INPUT:
- ``S`` -- a homset
- ``category`` -- a category
OUTPUT:
A map (by default: a Call morphism) from ``S`` to ``self``.
Expand Down Expand Up @@ -771,7 +773,7 @@ def _generic_convert_map(self, S):
from sage.categories.homset import Hom
return CallMorphism(Hom(S, self))
else:
return Parent._generic_convert_map(self, S)
return Parent._generic_convert_map(self, S, category)

def homset_category(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions src/sage/quivers/morphism.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,9 +271,9 @@ def __init__(self, domain, codomain, data={}):
for v in self._quiver:
if v in maps_dict:
if is_Map(maps_dict[v]):
if hasattr(maps_dict[v], 'matrix'):
try:
m = maps_dict[v].matrix()
else:
except (AttributeError, ValueError):
gens_images = [codomain._spaces[v].coordinate_vector(maps_dict[v](x))
for x in domain._spaces[v].gens()]
m = Matrix(self._base_ring, domain_dims[v], codomain_dims[v], gens_images)
Expand Down
8 changes: 4 additions & 4 deletions src/sage/rings/complex_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -426,22 +426,22 @@ def _coerce_map_from_(self, S):
return RRtoCC(RR, self) * RR._internal_coerce_map_from(S)
if is_ComplexField(S):
if self._prec <= S._prec:
return self._generic_convert_map(S)
return self._generic_coerce_map(S)
else:
return None
if S is complex:
if self._prec <= 53:
return self._generic_convert_map(S)
return self._generic_coerce_map(S)
else:
return None
late_import()
if S is CDF:
if self._prec <= 53:
return self._generic_convert_map(S)
return self._generic_coerce_map(S)
else:
return None
if S in [AA, QQbar, CLF, RLF]:
return self._generic_convert_map(S)
return self._generic_coerce_map(S)
return self._coerce_map_via([CLF], S)

def _repr_(self):
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/complex_mpc.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ cdef class MPComplexField_class(sage.rings.ring.Field):

late_import()
if S in [AA, QQbar, CLF, RLF] or (S == CDF and self._prec <= 53):
return self._generic_convert_map(S)
return self._generic_coerce_map(S)

return self._coerce_map_via([CLF], S)

Expand Down
8 changes: 5 additions & 3 deletions src/sage/rings/number_field/number_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -6906,11 +6906,13 @@ def _coerce_map_from_(self, R):
<type 'sage.rings.number_field.number_field_element_quadratic.Q_to_quadratic_field_element'>
"""
if R in integer_types + (ZZ, QQ, self.base()):
return self._generic_convert_map(R)
if R in integer_types:
return self._generic_coerce_map(R)
elif R in (ZZ, QQ, self.base()):
return self._generic_coerce_map(R)
from sage.rings.number_field.order import is_NumberFieldOrder
if is_NumberFieldOrder(R) and self.has_coerce_map_from(R.number_field()):
return self._generic_convert_map(R)
return self._generic_coerce_map(R)
# R is not QQ by the above tests
if is_NumberField(R) and R.coerce_embedding() is not None:
if self.coerce_embedding() is not None:
Expand Down
8 changes: 5 additions & 3 deletions src/sage/rings/number_field/number_field_rel.py
Original file line number Diff line number Diff line change
Expand Up @@ -1054,11 +1054,13 @@ def _coerce_map_from_(self, R):
2/3
sage: c = a + b # no output
"""
if R in integer_types + (ZZ, QQ, self.base_field()):
return self._generic_convert_map(R)
if R in integer_types:
return self._generic_coerce_map(R)
elif R in (ZZ, QQ, self.base_field()):
return self._generic_coerce_map(R)
from sage.rings.number_field.order import is_NumberFieldOrder
if is_NumberFieldOrder(R) and R.number_field() is self:
return self._generic_convert_map(R)
return self._generic_coerce_map(R)
mor = self.base_field()._internal_coerce_map_from(R)
if mor is not None:
return self._internal_coerce_map_from(self.base_field()) * mor
Expand Down
2 changes: 1 addition & 1 deletion src/sage/rings/real_mpfr.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -711,7 +711,7 @@ cdef class RealField_class(sage.rings.ring.Field):
from sage.rings.qqbar import AA
from sage.rings.real_lazy import RLF
if S == AA or S is RLF:
return self._generic_convert_map(S)
return self._generic_coerce_map(S)
return self._coerce_map_via([RLF], S)

def __cmp__(self, other):
Expand Down
8 changes: 5 additions & 3 deletions src/sage/structure/coerce_maps.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ cdef class DefaultConvertMap(Map):
This morphism simply calls the codomain's element_constructor method,
passing in the codomain as the first argument.
"""
def __init__(self, domain, codomain, force_use=False):
def __init__(self, domain, codomain, category=None, force_use=False):
"""
TESTS:
Expand All @@ -37,8 +37,10 @@ cdef class DefaultConvertMap(Map):
"""
if not isinstance(domain, Parent):
domain = Set_PythonType(domain)
from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
parent = domain.Hom(codomain, category=SetsWithPartialMaps())
if category is None:
from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
category = SetsWithPartialMaps()
parent = domain.Hom(codomain, category=category)
Map.__init__(self, parent)
self._coerce_cost = 100
self._force_use = force_use
Expand Down
3 changes: 2 additions & 1 deletion src/sage/structure/parent.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ cdef class Parent(category_object.CategoryObject):
cdef public object _cache_an_element

# For internal use
cpdef _generic_convert_map(self, S)
cpdef _generic_convert_map(self, S, category=*)
cpdef _generic_coerce_map(self, S)
cdef discover_coerce_map_from(self, S)
cdef discover_convert_map_from(self, S)
cdef discover_action(self, S, op, bint self_on_left, self_el=*, S_el=*)
Expand Down
61 changes: 52 additions & 9 deletions src/sage/structure/parent.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -1560,8 +1560,8 @@ cdef class Parent(category_object.CategoryObject):
if isinstance(mor, map.Map):
if mor.codomain() is not self:
raise ValueError("Map's codomain must be self (%s) is not (%s)" % (self, mor.codomain()))
elif isinstance(mor, Parent) or isinstance(mor, type):
mor = self._generic_convert_map(mor)
elif isinstance(mor, (type, Parent)):
mor = self._generic_coerce_map(mor)
else:
raise TypeError("coercions must be parents or maps (got %s)" % type(mor))
D = mor.domain()
Expand Down Expand Up @@ -1771,7 +1771,7 @@ cdef class Parent(category_object.CategoryObject):
raise ValueError("embedding's domain must be self")
self._embedding = embedding
elif isinstance(embedding, Parent):
self._embedding = embedding._generic_convert_map(self)
self._embedding = embedding._generic_coerce_map(self)
elif embedding is not None:
raise TypeError("embedding must be a parent or map")
self._embedding._make_weak_references()
Expand Down Expand Up @@ -1803,7 +1803,40 @@ cdef class Parent(category_object.CategoryObject):
"""
return copy(self._embedding) # It might be overkill to make a copy here

cpdef _generic_convert_map(self, S):
cpdef _generic_coerce_map(self, S):
r"""
Returns a default coercion map based on the data provided to
:meth:`_populate_coercion_lists_`.
This method differs from :meth:`_generic_convert_map` only in setting
the category for the map to the meet of the category of this parent
and ``S``.
EXAMPLES::
sage: QQ['x']._generic_coerce_map(ZZ)
Conversion map:
From: Integer Ring
To: Univariate Polynomial Ring in x over Rational Field
TESTS:
We check that `trac`:23184 has been resolved::
sage: QQ['x', 'y']._generic_coerce_map(QQ).category_for()
Category of unique factorization domains
sage: QQ[['x']].coerce_map_from(QQ).category_for()
Category of euclidean domains
"""
if isinstance(S, type):
category = None
else:
category = self.category()._meet_(S.category())
return self._generic_convert_map(S, category=category)

cpdef _generic_convert_map(self, S, category=None):
r"""
Returns the default conversion map based on the data provided to
:meth:`_populate_coercion_lists_`.
Expand All @@ -1815,7 +1848,17 @@ cdef class Parent(category_object.CategoryObject):
``DefaultConvertMap`` or ``DefaultConvertMap_unique``
depending on whether or not init_no_parent is set.
EXAMPLES:
EXAMPLES::
sage: QQ['x']._generic_convert_map(SR)
Conversion via _polynomial_ method map:
From: Symbolic Ring
To: Univariate Polynomial Ring in x over Rational Field
sage: GF(11)._generic_convert_map(GF(7))
Conversion map:
From: Finite Field of size 7
To: Finite Field of size 11
"""
import coerce_maps
if self._convert_method_name is not None:
Expand All @@ -1833,9 +1876,9 @@ cdef class Parent(category_object.CategoryObject):
return coerce_maps.NamedConvertMap(S, self, self._convert_method_name)

if self._element_init_pass_parent:
return coerce_maps.DefaultConvertMap(S, self)
return coerce_maps.DefaultConvertMap(S, self, category=category)
else:
return coerce_maps.DefaultConvertMap_unique(S, self)
return coerce_maps.DefaultConvertMap_unique(S, self, category=category)

def _coerce_map_via(self, v, S):
"""
Expand Down Expand Up @@ -2054,7 +2097,7 @@ cdef class Parent(category_object.CategoryObject):
# non-unique parents
if debug.unique_parent_warnings:
print("Warning: non-unique parents %s" % (type(S)))
mor = self._generic_convert_map(S)
mor = self._generic_coerce_map(S)
self._coerce_from_hash.set(S, mor)
mor._make_weak_references()
return mor
Expand Down Expand Up @@ -2210,7 +2253,7 @@ cdef class Parent(category_object.CategoryObject):
from coerce_maps import DefaultConvertMap, DefaultConvertMap_unique, NamedConvertMap, CallableConvertMap

if user_provided_mor is True:
mor = self._generic_convert_map(S)
mor = self._generic_coerce_map(S)
elif isinstance(user_provided_mor, Map):
mor = <map.Map>user_provided_mor
elif callable(user_provided_mor):
Expand Down
14 changes: 12 additions & 2 deletions src/sage/structure/parent_old.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,17 @@ cdef class Parent(parent.Parent):
else:
return parent.Parent._an_element_(self)

cpdef _generic_convert_map(self, S):
cpdef _generic_convert_map(self, S, category=None):
r"""
Return a default conversion from ``S``.
EXAMPLES::
sage: R.<x,y>=QQ[]
sage: R._generic_convert_map(QQ).category_for()
Category of sets with partial maps
"""
if self._element_constructor is None:
if hasattr(self, '_element_constructor_'):
assert callable(self._element_constructor_)
Expand All @@ -391,4 +401,4 @@ cdef class Parent(parent.Parent):
if isinstance(S, type):
S = Set_PythonType(S)
return CallMorphism(Hom(S, self))
return parent.Parent._generic_convert_map(self, S)
return parent.Parent._generic_convert_map(self, S, category)

0 comments on commit 8266e46

Please sign in to comment.