Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Coerce maps """ from __future__ import print_function, absolute_import
import re import types
from .parent import Set_PythonType from sage.structure.parent cimport Parent from sage.structure.element cimport Element
cdef object BuiltinMethodType = type(repr)
# COERCE TODO: remove or integrate better (as this bit is only checked on an error) cdef bint print_warnings = 0
cdef class DefaultConvertMap(Map): """ This morphism simply calls the codomain's element_constructor method, passing in the codomain as the first argument.
EXAMPLES::
sage: QQ[['x']].coerce_map_from(QQ) Coercion map: From: Rational Field To: Power Series Ring in x over Rational Field """ def __init__(self, domain, codomain, category=None): """ TESTS:
Maps of this type are morphisms in the category of sets with partial maps (see :trac:`15618`)::
sage: f = GF(11).convert_map_from(GF(7)); f Conversion map: From: Finite Field of size 7 To: Finite Field of size 11 sage: f.parent() Set of Morphisms from Finite Field of size 7 to Finite Field of size 11 in Category of sets with partial maps
Test that :trac:`23211` is resolved::
sage: f._is_coercion False sage: QQ[['x']].coerce_map_from(QQ)._is_coercion True """ raise RuntimeError("BUG in coercion model, no element constructor for {}".format(type(codomain)))
def _repr_type(self): r""" Return a printable type for this morphism.
EXAMPLES::
sage: f = GF(11).convert_map_from(GF(7)) sage: f._repr_type() 'Conversion'
"""
cpdef Element _call_(self, x): """ Create an element of the codomain from a single element of the domain.
EXAMPLES::
sage: f = QQ[['x']].coerce_map_from(QQ) sage: f(2/3).parent() Power Series Ring in x over Rational Field """ cdef Parent C = self._codomain try: return C._element_constructor(C, x) print(type(C), C) raise
cpdef Element _call_with_args(self, x, args=(), kwds={}): """ Create an element of the codomain from an element of the domain, with extra arguments.
EXAMPLES::
sage: f = QQ[['x']].coerce_map_from(QQ) sage: f(2/3, 4) 2/3 + O(x^4) """ cdef Parent C = self._codomain try: if len(args) == 0: if len(kwds) == 0: # This line is apparently never used in any tests (hivert, 2009-04-28) return C._element_constructor(C, x) else: return C._element_constructor(C, x, **kwds) else: if len(kwds) == 0: return C._element_constructor(C, x, *args) else: return C._element_constructor(C, x, *args, **kwds) except Exception: if print_warnings: print(type(C), C) print(type(C._element_constructor), C._element_constructor) raise
cdef class DefaultConvertMap_unique(DefaultConvertMap): """ This morphism simply defers action to the codomain's element_constructor method, WITHOUT passing in the codomain as the first argument.
This is used for creating elements that don't take a parent as the first argument to their __init__ method, for example, Integers, Rationals, Algebraic Reals... all have a unique parent. It is also used when the element_constructor is a bound method (whose self argument is assumed to be bound to the codomain). """ cpdef Element _call_(self, x): print(type(C), C) print(type(C._element_constructor), C._element_constructor)
cpdef Element _call_with_args(self, x, args=(), kwds={}): return C._element_constructor(x) else: else: else: print(type(C), C) print(type(C._element_constructor), C._element_constructor)
cdef class NamedConvertMap(Map): """ This is used for creating elements via the _xxx_ methods.
For example, many elements implement an _integer_ method to convert to ZZ, or a _rational_ method to convert to QQ. """
def __init__(self, domain, codomain, method_name): """ EXAMPLES::
sage: from sage.structure.coerce_maps import NamedConvertMap sage: var('t') t sage: mor = NamedConvertMap(SR, QQ['t'], '_polynomial_') sage: mor(t^2/4+1) 1/4*t^2 + 1 sage: mor = NamedConvertMap(SR, GF(7)[['t']], '_polynomial_') sage: mor(t^2/4+1) 1 + 2*t^2 """
cdef dict _extra_slots(self): """ Helper for copying and pickling.
EXAMPLES::
sage: from sage.structure.coerce_maps import NamedConvertMap sage: var('t') t sage: phi = NamedConvertMap(SR, QQ['t'], '_polynomial_') sage: psi = copy(phi) # indirect doctest sage: psi Conversion via _polynomial_ method map: From: Symbolic Ring To: Univariate Polynomial Ring in t over Rational Field sage: phi == psi # todo: comparison not implemented True sage: psi(t^2/4+1) 1/4*t^2 + 1 sage: psi(t^2/4+1) == phi(t^2/4+1) True """
cdef _update_slots(self, dict _slots): """ Helper for copying and pickling.
EXAMPLES::
sage: from sage.structure.coerce_maps import NamedConvertMap sage: var('t') t sage: phi = NamedConvertMap(SR, QQ['t'], '_polynomial_') sage: psi = copy(phi) # indirect doctest sage: psi Conversion via _polynomial_ method map: From: Symbolic Ring To: Univariate Polynomial Ring in t over Rational Field sage: phi == psi # todo: comparison not implemented True sage: psi(t^2/4+1) 1/4*t^2 + 1 sage: psi(t^2/4+1) == phi(t^2/4+1) True """
cpdef Element _call_(self, x): """ EXAMPLES::
sage: from sage.structure.coerce_maps import NamedConvertMap sage: f = NamedConvertMap(GF(5), QQ, '_integer_'); f Conversion via _integer_ method map: From: Finite Field of size 5 To: Rational Field sage: f(19) 4 sage: f(19).parent() Rational Field """ except AttributeError: if print_warnings: print(type(x), x) print(type(C), C) print(self.method_name) raise TypeError("Cannot coerce {} to {}".format(x, C)) cdef Map m raise RuntimeError("BUG in coercion model: {} method of {} returned None".format(self.method_name, type(x))) raise TypeError
cpdef Element _call_with_args(self, x, args=(), kwds={}): """ EXAMPLES::
sage: from sage.structure.coerce_maps import NamedConvertMap sage: f = NamedConvertMap(SR, ZZ['x'], '_polynomial_') sage: f(x^2+1, check=True) x^2 + 1 """
# Perhaps this could be a method, extracting (<PyMethodDescrObject *>(<object>Parent).coerce_map_from).d_method.ml_meth and/or PyCFunction_GET_FUNCTION(method) # and constructing a CCallableConvertMap_class if it is bound to the codomain.
cdef class CallableConvertMap(Map): def __init__(self, domain, codomain, func, parent_as_first_arg=None): """ This lets one easily create maps from any callable object.
This is especially useful to create maps from bound methods.
EXAMPLES::
sage: from sage.structure.coerce_maps import CallableConvertMap sage: def foo(P, x): return x/2 sage: f = CallableConvertMap(ZZ, QQ, foo) sage: f(3) 3/2 sage: f Conversion via foo map: From: Integer Ring To: Rational Field
Create a homomorphism from $\RR$ to $\RR^+$ viewed as additive groups.
::
sage: f = CallableConvertMap(RR, RR, exp, parent_as_first_arg=False) sage: f(0) 1.00000000000000 sage: f(1) 2.71828182845905 sage: f(-3) 0.0497870683678639 """ # can't easily access self parent_as_first_arg = codomain is func.__self__ else:
cdef dict _extra_slots(self): """ Helper for copying and pickling.
EXAMPLES::
sage: from sage.structure.coerce_maps import CallableConvertMap sage: def foo(P, x): return x^2 sage: f = CallableConvertMap(ZZ, ZZ, foo) sage: g = copy(f) # indirect doctest sage: f == g # todo: comparison not implemented True sage: f(3) == g(3) True """
cdef _update_slots(self, dict _slots): """ Helper for copying and pickling.
EXAMPLES::
sage: from sage.structure.coerce_maps import CallableConvertMap sage: def foo(P, x): return x^2 sage: f = CallableConvertMap(ZZ, ZZ, foo) sage: g = copy(f) # indirect doctest sage: f == g # todo: comparison not implemented True sage: f(3) == g(3) True """
cpdef Element _call_(self, x): """ Because self._func may be anything we do a little bit of sanity checking (the return value must be an element with the correct parent).
TESTS::
sage: from sage.structure.coerce_maps import CallableConvertMap sage: def foo(P, x): return x sage: f = CallableConvertMap(ZZ, ZZ, foo) sage: f(0) 0 sage: f = CallableConvertMap(ZZ, QQ, foo) sage: f(0) Traceback (most recent call last): ... RuntimeError: BUG in coercion model: <function foo at ...> returned element with wrong parent (expected Rational Field got Integer Ring) sage: def foo(P, x): return None sage: f = CallableConvertMap(ZZ, QQ, foo) sage: f(0) Traceback (most recent call last): ... RuntimeError: BUG in coercion model: <function foo at ...> returned None """ cdef Element y else: print(self._func) print(C)
cpdef Element _call_with_args(self, x, args=(), kwds={}): """ TESTS::
sage: from sage.structure.coerce_maps import CallableConvertMap sage: def foo(P, x, y): return x or y sage: f = CallableConvertMap(ZZ, ZZ, foo) sage: f(0, 3) 3 sage: f = CallableConvertMap(ZZ, QQ, foo) sage: f(0, 3) Traceback (most recent call last): ... RuntimeError: BUG in coercion model: <function foo at ...> returned element with wrong parent (expected Rational Field got Integer Ring) sage: f(None, None) Traceback (most recent call last): ... RuntimeError: BUG in coercion model: <function foo at ...> returned None """ cdef Element y else: print(self._func) print(self._codomain)
cdef class CCallableConvertMap_class(Map): cdef Element (*_func)(Parent, object) cdef public _name
def __init__(self, domain, codomain, name): domain = Set_PythonType(domain)
cpdef Element _call_(self, x): """ TESTS::
sage: from sage.structure.coerce_maps import test_CCallableConvertMap sage: f = test_CCallableConvertMap(QQ, 'test') sage: f(1/3) -8/27 """
def _repr_type(self): """ EXAMPLES::
sage: from sage.structure.coerce_maps import test_CCallableConvertMap sage: test_CCallableConvertMap(ZZ, 'any name') Conversion via c call 'any name' map: From: Integer Ring To: Integer Ring sage: test_CCallableConvertMap(ZZ, None) Conversion via c call at 0x... map: From: Integer Ring To: Integer Ring """ else:
cdef Map CCallableConvertMap(domain, codomain, void* func, name): """ Use this to create a map from domain to codomain by calling func (which must be a function pointer taking a Parent and object, and returning an Element in the given Parent).
This is the c analogue of CallableConvertMap. """ # Cython doesn't yet accept function pointers as arguments, # change this when it does.
cpdef Element _ccall_test_function(codomain, x): """ For testing CCallableConvertMap_class. Returns x*x*x-x in the codomain.
TESTS::
sage: from sage.structure.coerce_maps import _ccall_test_function sage: _ccall_test_function(ZZ, 1) 0 sage: _ccall_test_function(ZZ, 2) 6 sage: _ccall_test_function(ZZ, -3) -24 """
def test_CCallableConvertMap(domain, name=None): """ For testing CCallableConvertMap_class.
TESTS::
sage: from sage.structure.coerce_maps import test_CCallableConvertMap sage: f = test_CCallableConvertMap(ZZ, 'test'); f Conversion via c call 'test' map: From: Integer Ring To: Integer Ring sage: f(3) 24 sage: f(9) 720 """
cdef class ListMorphism(Map):
cdef Map _real_morphism
def __init__(self, domain, Map real_morphism): if not isinstance(domain, Parent): domain = Set_PythonType(domain) Map.__init__(self, domain, real_morphism.codomain()) self._coerce_cost = real_morphism._coerce_cost + 3 self._real_morphism = real_morphism self._repr_type_str = "List"
cdef dict _extra_slots(self): slots = Map._extra_slots(self) slots['_real_morphism'] = self._real_morphism return slots
cdef _update_slots(self, dict _slots): self._real_morphism = _slots['_real_morphism'] Map._update_slots(self, _slots)
cpdef Element _call_(self, x): try: x = x._data except AttributeError: x = list(x) return self._real_morphism._call_(x)
cpdef Element _call_with_args(self, x, args=(), kwds={}): try: x = x._data except AttributeError: x = list(x) return self._real_morphism._call_with_args(x, args, kwds)
cdef class TryMap(Map): def __init__(self, Map morphism_preferred, Map morphism_backup, error_types=None): """ TESTS::
sage: sage.structure.coerce_maps.TryMap(RDF.coerce_map_from(QQ), RDF.coerce_map_from(ZZ)) Traceback (most recent call last): ... TypeError: incorrectly matching parent """ self._error_types = (ValueError, TypeError, AttributeError) else:
cdef dict _extra_slots(self): """ Helper for copying and pickling.
EXAMPLES::
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x: 1/x) sage: map2 = QQ.coerce_map_from(ZZ) sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,)) sage: cmap = copy(map) # indirect doctest sage: cmap == map # todo: comparison not implemented True sage: map(3) == cmap(3) True sage: map(0) == cmap(0) True """
cdef _update_slots(self, dict _slots): """ Helper for copying and pickling.
EXAMPLES::
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x: 1/x) sage: map2 = QQ.coerce_map_from(ZZ) sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,)) sage: cmap = copy(map) # indirect doctest sage: cmap == map # todo: comparison not implemented True sage: map(3) == cmap(3) True sage: map(0) == cmap(0) True """
cpdef Element _call_(self, x): """ EXAMPLES::
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x: 1/x) sage: map2 = QQ.coerce_map_from(ZZ) sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,)) sage: map(3) 1/3 sage: map(-7) -1/7 sage: map(0) 0 """
cpdef Element _call_with_args(self, x, args=(), kwds={}): """ EXAMPLES::
sage: map1 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x, y: y/x) sage: map2 = sage.structure.coerce_maps.CallableConvertMap(ZZ, QQ, lambda parent, x, y: 23/1) sage: map = sage.structure.coerce_maps.TryMap(map1, map2, error_types=(ZeroDivisionError,)) sage: map._call_with_args(3, (2,)) 2/3 sage: map._call_with_args(-7, (5,)) -5/7 sage: map._call_with_args(0, (1,)) 23 """ |