passagemath-objects 10.6.41__cp312-cp312-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_objects/__init__.py +3 -0
- passagemath_objects-10.6.41.dist-info/DELVEWHEEL +2 -0
- passagemath_objects-10.6.41.dist-info/METADATA +115 -0
- passagemath_objects-10.6.41.dist-info/RECORD +281 -0
- passagemath_objects-10.6.41.dist-info/WHEEL +5 -0
- passagemath_objects-10.6.41.dist-info/top_level.txt +3 -0
- passagemath_objects.libs/libgmp-10-79b4110c7ea2b760f16cfef97e8a8a34.dll +0 -0
- sage/all__sagemath_objects.py +46 -0
- sage/arith/all__sagemath_objects.py +5 -0
- sage/arith/long.pxd +411 -0
- sage/arith/numerical_approx.cp312-win_amd64.pyd +0 -0
- sage/arith/numerical_approx.pxd +35 -0
- sage/arith/numerical_approx.pyx +75 -0
- sage/arith/power.cp312-win_amd64.pyd +0 -0
- sage/arith/power.pxd +31 -0
- sage/arith/power.pyx +127 -0
- sage/categories/action.cp312-win_amd64.pyd +0 -0
- sage/categories/action.pxd +29 -0
- sage/categories/action.pyx +641 -0
- sage/categories/algebra_functor.py +745 -0
- sage/categories/all__sagemath_objects.py +33 -0
- sage/categories/basic.py +71 -0
- sage/categories/cartesian_product.py +295 -0
- sage/categories/category.py +3401 -0
- sage/categories/category_cy_helper.cp312-win_amd64.pyd +0 -0
- sage/categories/category_cy_helper.pxd +8 -0
- sage/categories/category_cy_helper.pyx +322 -0
- sage/categories/category_singleton.cp312-win_amd64.pyd +0 -0
- sage/categories/category_singleton.pxd +3 -0
- sage/categories/category_singleton.pyx +342 -0
- sage/categories/category_types.py +637 -0
- sage/categories/category_with_axiom.py +2885 -0
- sage/categories/covariant_functorial_construction.py +703 -0
- sage/categories/facade_sets.py +228 -0
- sage/categories/functor.cp312-win_amd64.pyd +0 -0
- sage/categories/functor.pxd +7 -0
- sage/categories/functor.pyx +691 -0
- sage/categories/homset.py +1338 -0
- sage/categories/homsets.py +364 -0
- sage/categories/isomorphic_objects.py +73 -0
- sage/categories/map.cp312-win_amd64.pyd +0 -0
- sage/categories/map.pxd +34 -0
- sage/categories/map.pyx +2112 -0
- sage/categories/morphism.cp312-win_amd64.pyd +0 -0
- sage/categories/morphism.pxd +14 -0
- sage/categories/morphism.pyx +895 -0
- sage/categories/objects.py +167 -0
- sage/categories/primer.py +1696 -0
- sage/categories/pushout.py +4834 -0
- sage/categories/quotients.py +64 -0
- sage/categories/realizations.py +200 -0
- sage/categories/sets_cat.py +3228 -0
- sage/categories/sets_with_partial_maps.py +52 -0
- sage/categories/subobjects.py +64 -0
- sage/categories/subquotients.py +21 -0
- sage/categories/with_realizations.py +311 -0
- sage/cpython/__init__.py +28 -0
- sage/cpython/_py2_random.py +619 -0
- sage/cpython/all.py +3 -0
- sage/cpython/atexit.cp312-win_amd64.pyd +0 -0
- sage/cpython/atexit.pyx +269 -0
- sage/cpython/builtin_types.cp312-win_amd64.pyd +0 -0
- sage/cpython/builtin_types.pyx +7 -0
- sage/cpython/cython_metaclass.cp312-win_amd64.pyd +0 -0
- sage/cpython/cython_metaclass.h +117 -0
- sage/cpython/cython_metaclass.pxd +3 -0
- sage/cpython/cython_metaclass.pyx +130 -0
- sage/cpython/debug.cp312-win_amd64.pyd +0 -0
- sage/cpython/debug.pyx +302 -0
- sage/cpython/dict_del_by_value.cp312-win_amd64.pyd +0 -0
- sage/cpython/dict_del_by_value.pxd +9 -0
- sage/cpython/dict_del_by_value.pyx +191 -0
- sage/cpython/dict_internal.h +245 -0
- sage/cpython/getattr.cp312-win_amd64.pyd +0 -0
- sage/cpython/getattr.pxd +9 -0
- sage/cpython/getattr.pyx +439 -0
- sage/cpython/pycore_long.h +97 -0
- sage/cpython/pycore_long.pxd +10 -0
- sage/cpython/python_debug.h +44 -0
- sage/cpython/python_debug.pxd +47 -0
- sage/cpython/pyx_visit.h +13 -0
- sage/cpython/string.cp312-win_amd64.pyd +0 -0
- sage/cpython/string.pxd +76 -0
- sage/cpython/string.pyx +34 -0
- sage/cpython/string_impl.h +60 -0
- sage/cpython/type.cp312-win_amd64.pyd +0 -0
- sage/cpython/type.pxd +2 -0
- sage/cpython/type.pyx +40 -0
- sage/cpython/wrapperdescr.pxd +67 -0
- sage/ext/all__sagemath_objects.py +3 -0
- sage/ext/ccobject.h +64 -0
- sage/ext/cplusplus.pxd +17 -0
- sage/ext/mod_int.h +30 -0
- sage/ext/mod_int.pxd +24 -0
- sage/ext/stdsage.pxd +39 -0
- sage/groups/all__sagemath_objects.py +1 -0
- sage/groups/group.cp312-win_amd64.pyd +0 -0
- sage/groups/group.pxd +14 -0
- sage/groups/group.pyx +322 -0
- sage/groups/old.cp312-win_amd64.pyd +0 -0
- sage/groups/old.pxd +14 -0
- sage/groups/old.pyx +219 -0
- sage/libs/all__sagemath_objects.py +3 -0
- sage/libs/gmp/__init__.py +1 -0
- sage/libs/gmp/all.pxd +6 -0
- sage/libs/gmp/binop.pxd +23 -0
- sage/libs/gmp/misc.pxd +8 -0
- sage/libs/gmp/mpf.pxd +88 -0
- sage/libs/gmp/mpn.pxd +57 -0
- sage/libs/gmp/mpq.pxd +57 -0
- sage/libs/gmp/mpz.pxd +202 -0
- sage/libs/gmp/pylong.cp312-win_amd64.pyd +0 -0
- sage/libs/gmp/pylong.pxd +12 -0
- sage/libs/gmp/pylong.pyx +150 -0
- sage/libs/gmp/random.pxd +25 -0
- sage/libs/gmp/randomize.pxd +59 -0
- sage/libs/gmp/types.pxd +53 -0
- sage/libs/gmpxx.pxd +19 -0
- sage/misc/abstract_method.py +276 -0
- sage/misc/all__sagemath_objects.py +43 -0
- sage/misc/bindable_class.py +253 -0
- sage/misc/c3_controlled.cp312-win_amd64.pyd +0 -0
- sage/misc/c3_controlled.pxd +2 -0
- sage/misc/c3_controlled.pyx +1402 -0
- sage/misc/cachefunc.cp312-win_amd64.pyd +0 -0
- sage/misc/cachefunc.pxd +43 -0
- sage/misc/cachefunc.pyx +3781 -0
- sage/misc/call.py +188 -0
- sage/misc/classcall_metaclass.cp312-win_amd64.pyd +0 -0
- sage/misc/classcall_metaclass.pxd +14 -0
- sage/misc/classcall_metaclass.pyx +599 -0
- sage/misc/constant_function.cp312-win_amd64.pyd +0 -0
- sage/misc/constant_function.pyx +130 -0
- sage/misc/decorators.py +747 -0
- sage/misc/fast_methods.cp312-win_amd64.pyd +0 -0
- sage/misc/fast_methods.pxd +20 -0
- sage/misc/fast_methods.pyx +351 -0
- sage/misc/flatten.py +90 -0
- sage/misc/fpickle.cp312-win_amd64.pyd +0 -0
- sage/misc/fpickle.pyx +177 -0
- sage/misc/function_mangling.cp312-win_amd64.pyd +0 -0
- sage/misc/function_mangling.pxd +11 -0
- sage/misc/function_mangling.pyx +308 -0
- sage/misc/inherit_comparison.cp312-win_amd64.pyd +0 -0
- sage/misc/inherit_comparison.pxd +5 -0
- sage/misc/inherit_comparison.pyx +105 -0
- sage/misc/instancedoc.cp312-win_amd64.pyd +0 -0
- sage/misc/instancedoc.pyx +331 -0
- sage/misc/lazy_attribute.cp312-win_amd64.pyd +0 -0
- sage/misc/lazy_attribute.pyx +607 -0
- sage/misc/lazy_format.py +135 -0
- sage/misc/lazy_import.cp312-win_amd64.pyd +0 -0
- sage/misc/lazy_import.pyx +1299 -0
- sage/misc/lazy_import_cache.py +36 -0
- sage/misc/lazy_list.cp312-win_amd64.pyd +0 -0
- sage/misc/lazy_list.pxd +19 -0
- sage/misc/lazy_list.pyx +1187 -0
- sage/misc/lazy_string.cp312-win_amd64.pyd +0 -0
- sage/misc/lazy_string.pxd +7 -0
- sage/misc/lazy_string.pyx +546 -0
- sage/misc/misc.py +1066 -0
- sage/misc/misc_c.cp312-win_amd64.pyd +0 -0
- sage/misc/misc_c.pxd +3 -0
- sage/misc/misc_c.pyx +766 -0
- sage/misc/namespace_package.py +37 -0
- sage/misc/nested_class.cp312-win_amd64.pyd +0 -0
- sage/misc/nested_class.pxd +3 -0
- sage/misc/nested_class.pyx +394 -0
- sage/misc/persist.cp312-win_amd64.pyd +0 -0
- sage/misc/persist.pyx +1251 -0
- sage/misc/prandom.py +418 -0
- sage/misc/randstate.cp312-win_amd64.pyd +0 -0
- sage/misc/randstate.pxd +30 -0
- sage/misc/randstate.pyx +1059 -0
- sage/misc/repr.py +203 -0
- sage/misc/reset.cp312-win_amd64.pyd +0 -0
- sage/misc/reset.pyx +196 -0
- sage/misc/sage_ostools.cp312-win_amd64.pyd +0 -0
- sage/misc/sage_ostools.pyx +323 -0
- sage/misc/sage_timeit.py +276 -0
- sage/misc/sage_timeit_class.cp312-win_amd64.pyd +0 -0
- sage/misc/sage_timeit_class.pyx +120 -0
- sage/misc/sage_unittest.py +637 -0
- sage/misc/sageinspect.py +2768 -0
- sage/misc/session.cp312-win_amd64.pyd +0 -0
- sage/misc/session.pyx +392 -0
- sage/misc/superseded.py +557 -0
- sage/misc/test_nested_class.py +228 -0
- sage/misc/timing.py +264 -0
- sage/misc/unknown.py +222 -0
- sage/misc/verbose.py +253 -0
- sage/misc/weak_dict.cp312-win_amd64.pyd +0 -0
- sage/misc/weak_dict.pxd +15 -0
- sage/misc/weak_dict.pyx +1231 -0
- sage/modules/all__sagemath_objects.py +1 -0
- sage/modules/module.cp312-win_amd64.pyd +0 -0
- sage/modules/module.pxd +5 -0
- sage/modules/module.pyx +329 -0
- sage/rings/all__sagemath_objects.py +3 -0
- sage/rings/integer_fake.h +22 -0
- sage/rings/integer_fake.pxd +55 -0
- sage/sets/all__sagemath_objects.py +3 -0
- sage/sets/pythonclass.cp312-win_amd64.pyd +0 -0
- sage/sets/pythonclass.pxd +9 -0
- sage/sets/pythonclass.pyx +247 -0
- sage/structure/__init__.py +13 -0
- sage/structure/all.py +30 -0
- sage/structure/category_object.cp312-win_amd64.pyd +0 -0
- sage/structure/category_object.pxd +28 -0
- sage/structure/category_object.pyx +1087 -0
- sage/structure/coerce.cp312-win_amd64.pyd +0 -0
- sage/structure/coerce.pxd +44 -0
- sage/structure/coerce.pyx +2107 -0
- sage/structure/coerce_actions.cp312-win_amd64.pyd +0 -0
- sage/structure/coerce_actions.pxd +27 -0
- sage/structure/coerce_actions.pyx +988 -0
- sage/structure/coerce_dict.cp312-win_amd64.pyd +0 -0
- sage/structure/coerce_dict.pxd +51 -0
- sage/structure/coerce_dict.pyx +1557 -0
- sage/structure/coerce_exceptions.py +23 -0
- sage/structure/coerce_maps.cp312-win_amd64.pyd +0 -0
- sage/structure/coerce_maps.pxd +28 -0
- sage/structure/coerce_maps.pyx +718 -0
- sage/structure/debug_options.cp312-win_amd64.pyd +0 -0
- sage/structure/debug_options.pxd +6 -0
- sage/structure/debug_options.pyx +54 -0
- sage/structure/dynamic_class.py +541 -0
- sage/structure/element.cp312-win_amd64.pyd +0 -0
- sage/structure/element.pxd +272 -0
- sage/structure/element.pyx +4772 -0
- sage/structure/element_wrapper.cp312-win_amd64.pyd +0 -0
- sage/structure/element_wrapper.pxd +12 -0
- sage/structure/element_wrapper.pyx +582 -0
- sage/structure/factorization.py +1422 -0
- sage/structure/factorization_integer.py +105 -0
- sage/structure/factory.cp312-win_amd64.pyd +0 -0
- sage/structure/factory.pyx +786 -0
- sage/structure/formal_sum.py +489 -0
- sage/structure/gens_py.py +73 -0
- sage/structure/global_options.py +1743 -0
- sage/structure/indexed_generators.py +863 -0
- sage/structure/list_clone.cp312-win_amd64.pyd +0 -0
- sage/structure/list_clone.pxd +65 -0
- sage/structure/list_clone.pyx +1867 -0
- sage/structure/list_clone_demo.cp312-win_amd64.pyd +0 -0
- sage/structure/list_clone_demo.pyx +248 -0
- sage/structure/list_clone_timings.py +179 -0
- sage/structure/list_clone_timings_cy.cp312-win_amd64.pyd +0 -0
- sage/structure/list_clone_timings_cy.pyx +86 -0
- sage/structure/mutability.cp312-win_amd64.pyd +0 -0
- sage/structure/mutability.pxd +21 -0
- sage/structure/mutability.pyx +348 -0
- sage/structure/nonexact.py +69 -0
- sage/structure/parent.cp312-win_amd64.pyd +0 -0
- sage/structure/parent.pxd +112 -0
- sage/structure/parent.pyx +3093 -0
- sage/structure/parent_base.cp312-win_amd64.pyd +0 -0
- sage/structure/parent_base.pxd +13 -0
- sage/structure/parent_base.pyx +44 -0
- sage/structure/parent_gens.cp312-win_amd64.pyd +0 -0
- sage/structure/parent_gens.pxd +22 -0
- sage/structure/parent_gens.pyx +377 -0
- sage/structure/parent_old.cp312-win_amd64.pyd +0 -0
- sage/structure/parent_old.pxd +25 -0
- sage/structure/parent_old.pyx +294 -0
- sage/structure/proof/__init__.py +1 -0
- sage/structure/proof/all.py +243 -0
- sage/structure/proof/proof.py +300 -0
- sage/structure/richcmp.cp312-win_amd64.pyd +0 -0
- sage/structure/richcmp.pxd +213 -0
- sage/structure/richcmp.pyx +495 -0
- sage/structure/sage_object.cp312-win_amd64.pyd +0 -0
- sage/structure/sage_object.pxd +3 -0
- sage/structure/sage_object.pyx +988 -0
- sage/structure/sage_object_test.py +19 -0
- sage/structure/sequence.py +937 -0
- sage/structure/set_factories.py +1178 -0
- sage/structure/set_factories_example.py +527 -0
- sage/structure/support_view.py +179 -0
- sage/structure/test_factory.py +56 -0
- sage/structure/unique_representation.py +1359 -0
sage/categories/map.pyx
ADDED
|
@@ -0,0 +1,2112 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-objects
|
|
2
|
+
r"""
|
|
3
|
+
Base class for maps
|
|
4
|
+
|
|
5
|
+
AUTHORS:
|
|
6
|
+
|
|
7
|
+
- Robert Bradshaw: initial implementation
|
|
8
|
+
|
|
9
|
+
- Sebastien Besnier (2014-05-5): :class:`FormalCompositeMap` contains
|
|
10
|
+
a list of Map instead of only two Map. See :issue:`16291`.
|
|
11
|
+
|
|
12
|
+
- Sebastian Oehms (2019-01-19): :meth:`section` added to :class:`FormalCompositeMap`.
|
|
13
|
+
See :issue:`27081`.
|
|
14
|
+
"""
|
|
15
|
+
# ****************************************************************************
|
|
16
|
+
# Copyright (C) 2008 Robert Bradshaw <robertwb@math.washington.edu>
|
|
17
|
+
#
|
|
18
|
+
# This program is free software: you can redistribute it and/or modify
|
|
19
|
+
# it under the terms of the GNU General Public License as published by
|
|
20
|
+
# the Free Software Foundation, either version 2 of the License, or
|
|
21
|
+
# (at your option) any later version.
|
|
22
|
+
# https://www.gnu.org/licenses/
|
|
23
|
+
# ****************************************************************************
|
|
24
|
+
|
|
25
|
+
from sage.categories import homset
|
|
26
|
+
import weakref
|
|
27
|
+
from sage.ext.stdsage cimport HAS_DICTIONARY
|
|
28
|
+
from sage.arith.power cimport generic_power
|
|
29
|
+
from sage.sets.pythonclass cimport Set_PythonType
|
|
30
|
+
from sage.misc.constant_function import ConstantFunction
|
|
31
|
+
from sage.structure.element cimport parent
|
|
32
|
+
from cpython.object cimport PyObject_RichCompare
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def unpickle_map(_class, parent, _dict, _slots):
|
|
36
|
+
"""
|
|
37
|
+
Auxiliary function for unpickling a map.
|
|
38
|
+
|
|
39
|
+
TESTS::
|
|
40
|
+
|
|
41
|
+
sage: R.<x,y> = QQ[]
|
|
42
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
43
|
+
sage: f == loads(dumps(f)) # indirect doctest
|
|
44
|
+
True
|
|
45
|
+
"""
|
|
46
|
+
# should we use slots?
|
|
47
|
+
# from element.pyx
|
|
48
|
+
cdef Map mor = _class.__new__(_class)
|
|
49
|
+
mor._set_parent(parent)
|
|
50
|
+
mor._update_slots(_slots)
|
|
51
|
+
if HAS_DICTIONARY(mor):
|
|
52
|
+
mor.__dict__ = _dict
|
|
53
|
+
return mor
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def is_Map(x):
|
|
57
|
+
"""
|
|
58
|
+
Auxiliary function: Is the argument a map?
|
|
59
|
+
|
|
60
|
+
EXAMPLES::
|
|
61
|
+
|
|
62
|
+
sage: R.<x,y> = QQ[]
|
|
63
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
64
|
+
sage: from sage.categories.map import is_Map
|
|
65
|
+
sage: is_Map(f)
|
|
66
|
+
doctest:warning...
|
|
67
|
+
DeprecationWarning: The function is_Map is deprecated; use 'isinstance(..., Map)' instead.
|
|
68
|
+
See https://github.com/sagemath/sage/issues/38103 for details.
|
|
69
|
+
True
|
|
70
|
+
"""
|
|
71
|
+
from sage.misc.superseded import deprecation_cython
|
|
72
|
+
deprecation_cython(38103, "The function is_Map is deprecated; use 'isinstance(..., Map)' instead.")
|
|
73
|
+
return isinstance(x, Map)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
cdef class Map(Element):
|
|
77
|
+
"""
|
|
78
|
+
Basic class for all maps.
|
|
79
|
+
|
|
80
|
+
.. NOTE::
|
|
81
|
+
|
|
82
|
+
The call method is of course not implemented in this base class. This must
|
|
83
|
+
be done in the sub classes, by overloading ``_call_`` and possibly also
|
|
84
|
+
``_call_with_args``.
|
|
85
|
+
|
|
86
|
+
EXAMPLES:
|
|
87
|
+
|
|
88
|
+
Usually, instances of this class will not be constructed directly, but
|
|
89
|
+
for example like this::
|
|
90
|
+
|
|
91
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
92
|
+
sage: X.<x> = ZZ[]
|
|
93
|
+
sage: Y = ZZ
|
|
94
|
+
sage: phi = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
95
|
+
sage: phi(x^2+2*x-1)
|
|
96
|
+
-1
|
|
97
|
+
sage: R.<x,y> = QQ[]
|
|
98
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
99
|
+
sage: f(x^2+2*x-1)
|
|
100
|
+
x^2 + 2*x*y + y^2 + 2*x + 2*y - 1
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(self, parent, codomain=None):
|
|
104
|
+
"""
|
|
105
|
+
INPUT:
|
|
106
|
+
|
|
107
|
+
There can be one or two arguments of this init method. If it is one argument,
|
|
108
|
+
it must be a hom space. If it is two arguments, it must be two parent structures
|
|
109
|
+
that will be domain and codomain of the map-to-be-created.
|
|
110
|
+
|
|
111
|
+
TESTS::
|
|
112
|
+
|
|
113
|
+
sage: from sage.categories.map import Map
|
|
114
|
+
|
|
115
|
+
Using a hom space::
|
|
116
|
+
|
|
117
|
+
sage: Map(Hom(QQ, ZZ, Rings()))
|
|
118
|
+
Generic map:
|
|
119
|
+
From: Rational Field
|
|
120
|
+
To: Integer Ring
|
|
121
|
+
|
|
122
|
+
Using domain and codomain::
|
|
123
|
+
|
|
124
|
+
sage: Map(QQ['x'], SymmetricGroup(6)) # needs sage.groups
|
|
125
|
+
Generic map:
|
|
126
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
127
|
+
To: Symmetric group of order 6! as a permutation group
|
|
128
|
+
"""
|
|
129
|
+
if codomain is not None:
|
|
130
|
+
if isinstance(parent, type):
|
|
131
|
+
parent = Set_PythonType(parent)
|
|
132
|
+
parent = homset.Hom(parent, codomain)
|
|
133
|
+
elif not isinstance(parent, homset.Homset):
|
|
134
|
+
raise TypeError("parent (=%s) must be a Homspace" % parent)
|
|
135
|
+
Element.__init__(self, parent)
|
|
136
|
+
D = parent.domain()
|
|
137
|
+
C = parent.codomain()
|
|
138
|
+
self._category_for = parent.homset_category()
|
|
139
|
+
self._codomain = C
|
|
140
|
+
self.domain = ConstantFunction(D)
|
|
141
|
+
self.codomain = ConstantFunction(C)
|
|
142
|
+
self._is_coercion = False
|
|
143
|
+
if D.is_exact() and C.is_exact():
|
|
144
|
+
self._coerce_cost = 10 # default value.
|
|
145
|
+
else:
|
|
146
|
+
self._coerce_cost = 10000 # inexact morphisms are bad.
|
|
147
|
+
|
|
148
|
+
def __copy__(self):
|
|
149
|
+
"""
|
|
150
|
+
Return copy, with strong references to domain and codomain.
|
|
151
|
+
|
|
152
|
+
.. NOTE::
|
|
153
|
+
|
|
154
|
+
To implement copying on sub-classes, do not override this method, but
|
|
155
|
+
implement cdef methods ``_extra_slots()`` returning a dictionary and
|
|
156
|
+
``_update_slots()`` using this dictionary to fill the cdef or cpdef
|
|
157
|
+
slots of the subclass.
|
|
158
|
+
|
|
159
|
+
EXAMPLES::
|
|
160
|
+
|
|
161
|
+
sage: phi = QQ['x']._internal_coerce_map_from(ZZ)
|
|
162
|
+
sage: phi.domain
|
|
163
|
+
<weakref at ...; to 'sage.rings.integer_ring.IntegerRing_class' at ...>
|
|
164
|
+
sage: type(phi)
|
|
165
|
+
<class 'sage.categories.map.FormalCompositeMap'>
|
|
166
|
+
sage: psi = copy(phi) # indirect doctest
|
|
167
|
+
sage: psi
|
|
168
|
+
Composite map:
|
|
169
|
+
From: Integer Ring
|
|
170
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
171
|
+
Defn: Natural morphism:
|
|
172
|
+
From: Integer Ring
|
|
173
|
+
To: Rational Field
|
|
174
|
+
then
|
|
175
|
+
Polynomial base injection morphism:
|
|
176
|
+
From: Rational Field
|
|
177
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
178
|
+
sage: psi.domain
|
|
179
|
+
The constant function (...) -> Integer Ring
|
|
180
|
+
sage: psi(3)
|
|
181
|
+
3
|
|
182
|
+
"""
|
|
183
|
+
cdef Map out = Element.__copy__(self)
|
|
184
|
+
# Element.__copy__ updates the __dict__, but not the slots.
|
|
185
|
+
# Let's do this now, but with strong references.
|
|
186
|
+
out._parent = self.parent() # self._parent might be None
|
|
187
|
+
out._update_slots(self._extra_slots())
|
|
188
|
+
return out
|
|
189
|
+
|
|
190
|
+
def parent(self):
|
|
191
|
+
r"""
|
|
192
|
+
Return the homset containing this map.
|
|
193
|
+
|
|
194
|
+
.. NOTE::
|
|
195
|
+
|
|
196
|
+
The method :meth:`_make_weak_references`, that is used for the maps
|
|
197
|
+
found by the coercion system, needs to remove the usual strong
|
|
198
|
+
reference from the coercion map to the homset containing it. As long
|
|
199
|
+
as the user keeps strong references to domain and codomain of the map,
|
|
200
|
+
we will be able to reconstruct the homset. However, a strong reference
|
|
201
|
+
to the coercion map does not prevent the domain from garbage collection!
|
|
202
|
+
|
|
203
|
+
EXAMPLES::
|
|
204
|
+
|
|
205
|
+
sage: Q = QuadraticField(-5) # needs sage.rings.number_field
|
|
206
|
+
sage: phi = CDF._internal_convert_map_from(Q) # needs sage.rings.number_field
|
|
207
|
+
sage: print(phi.parent()) # needs sage.rings.number_field
|
|
208
|
+
Set of field embeddings
|
|
209
|
+
from Number Field in a with defining polynomial x^2 + 5
|
|
210
|
+
with a = 2.236067977499790?*I
|
|
211
|
+
to Complex Double Field
|
|
212
|
+
|
|
213
|
+
We now demonstrate that the reference to the coercion map `\phi` does
|
|
214
|
+
not prevent `Q` from being garbage collected::
|
|
215
|
+
|
|
216
|
+
sage: import gc
|
|
217
|
+
sage: del Q # needs sage.rings.number_field
|
|
218
|
+
sage: _ = gc.collect()
|
|
219
|
+
sage: phi.parent() # needs sage.rings.number_field
|
|
220
|
+
Traceback (most recent call last):
|
|
221
|
+
...
|
|
222
|
+
ValueError: This map is in an invalid state,
|
|
223
|
+
the domain has been garbage collected
|
|
224
|
+
|
|
225
|
+
You can still obtain copies of the maps used by the coercion system with
|
|
226
|
+
strong references::
|
|
227
|
+
|
|
228
|
+
sage: # needs sage.rings.number_field
|
|
229
|
+
sage: Q = QuadraticField(-5)
|
|
230
|
+
sage: phi = CDF.convert_map_from(Q)
|
|
231
|
+
sage: print(phi.parent())
|
|
232
|
+
Set of field embeddings
|
|
233
|
+
from Number Field in a with defining polynomial x^2 + 5
|
|
234
|
+
with a = 2.236067977499790?*I
|
|
235
|
+
to Complex Double Field
|
|
236
|
+
sage: import gc
|
|
237
|
+
sage: del Q
|
|
238
|
+
sage: _ = gc.collect()
|
|
239
|
+
sage: phi.parent()
|
|
240
|
+
Set of field embeddings
|
|
241
|
+
from Number Field in a with defining polynomial x^2 + 5
|
|
242
|
+
with a = 2.236067977499790?*I
|
|
243
|
+
to Complex Double Field
|
|
244
|
+
"""
|
|
245
|
+
if self._parent is None:
|
|
246
|
+
D = self.domain()
|
|
247
|
+
C = self._codomain
|
|
248
|
+
if C is None or D is None:
|
|
249
|
+
raise ValueError("This map is in an invalid state, the domain has been garbage collected")
|
|
250
|
+
return homset.Hom(D, C, self._category_for)
|
|
251
|
+
return self._parent
|
|
252
|
+
|
|
253
|
+
def _make_weak_references(self):
|
|
254
|
+
"""
|
|
255
|
+
Only store weak references to domain and codomain of this map.
|
|
256
|
+
|
|
257
|
+
.. NOTE::
|
|
258
|
+
|
|
259
|
+
This method is internally used on maps that are used for coercions
|
|
260
|
+
or conversions between parents. Without using this method, some objects
|
|
261
|
+
would stay alive indefinitely as soon as they are involved in a coercion
|
|
262
|
+
or conversion.
|
|
263
|
+
|
|
264
|
+
.. SEEALSO::
|
|
265
|
+
|
|
266
|
+
:meth:`_make_strong_references`
|
|
267
|
+
|
|
268
|
+
EXAMPLES::
|
|
269
|
+
|
|
270
|
+
sage: Q = QuadraticField(-5) # needs sage.rings.number_field
|
|
271
|
+
sage: phi = CDF._internal_convert_map_from(Q) # needs sage.rings.number_field
|
|
272
|
+
|
|
273
|
+
By :issue:`14711`, maps used in the coercion and conversion system
|
|
274
|
+
use *weak* references to domain and codomain, in contrast to other
|
|
275
|
+
maps::
|
|
276
|
+
|
|
277
|
+
sage: phi.domain # needs sage.rings.number_field
|
|
278
|
+
<weakref at ...; to '...NumberField_quadratic_with_category' at ...>
|
|
279
|
+
sage: phi._make_strong_references() # needs sage.rings.number_field
|
|
280
|
+
sage: print(phi.domain) # needs sage.rings.number_field
|
|
281
|
+
The constant function (...) -> Number Field in a
|
|
282
|
+
with defining polynomial x^2 + 5 with a = 2.236067977499790?*I
|
|
283
|
+
|
|
284
|
+
Now, as there is a strong reference, `Q` cannot be garbage collected::
|
|
285
|
+
|
|
286
|
+
sage: # needs sage.rings.number_field
|
|
287
|
+
sage: import gc
|
|
288
|
+
sage: _ = gc.collect()
|
|
289
|
+
sage: C = Q.__class__.__base__
|
|
290
|
+
sage: x = None
|
|
291
|
+
sage: numberQuadFields = len([x for x in gc.get_objects()
|
|
292
|
+
....: if isinstance(x, C)])
|
|
293
|
+
sage: del Q, x
|
|
294
|
+
sage: _ = gc.collect()
|
|
295
|
+
sage: numberQuadFields == len([x for x in gc.get_objects()
|
|
296
|
+
....: if isinstance(x, C)])
|
|
297
|
+
True
|
|
298
|
+
|
|
299
|
+
However, if we now make the references weak again, the number field can
|
|
300
|
+
be garbage collected, which of course makes the map and its parents
|
|
301
|
+
invalid. This is why :meth:`_make_weak_references` should only be used
|
|
302
|
+
if one really knows what one is doing::
|
|
303
|
+
|
|
304
|
+
sage: # needs sage.rings.number_field
|
|
305
|
+
sage: phi._make_weak_references()
|
|
306
|
+
sage: _ = gc.collect()
|
|
307
|
+
sage: numberQuadFields == len([x for x in gc.get_objects()
|
|
308
|
+
....: if isinstance(x, C)]) + 1
|
|
309
|
+
True
|
|
310
|
+
sage: phi
|
|
311
|
+
Defunct map
|
|
312
|
+
"""
|
|
313
|
+
if not isinstance(self.domain, ConstantFunction):
|
|
314
|
+
return
|
|
315
|
+
self.domain = weakref.ref(self.domain())
|
|
316
|
+
# Save the category before clearing the parent.
|
|
317
|
+
self._category_for = self._parent.homset_category()
|
|
318
|
+
self._parent = None
|
|
319
|
+
|
|
320
|
+
def _make_strong_references(self):
|
|
321
|
+
"""
|
|
322
|
+
Store strong references to domain and codomain of this map.
|
|
323
|
+
|
|
324
|
+
.. NOTE::
|
|
325
|
+
|
|
326
|
+
By default, maps keep strong references to domain and codomain,
|
|
327
|
+
preventing them thus from garbage collection. However, in Sage's
|
|
328
|
+
coercion system, these strong references are replaced by weak
|
|
329
|
+
references, since otherwise some objects would stay alive indefinitely
|
|
330
|
+
as soon as they are involved in a coercion or conversion.
|
|
331
|
+
|
|
332
|
+
.. SEEALSO::
|
|
333
|
+
|
|
334
|
+
:meth:`_make_weak_references`
|
|
335
|
+
|
|
336
|
+
EXAMPLES::
|
|
337
|
+
|
|
338
|
+
sage: Q = QuadraticField(-5) # needs sage.rings.number_field
|
|
339
|
+
sage: phi = CDF._internal_convert_map_from(Q) # needs sage.rings.number_field
|
|
340
|
+
|
|
341
|
+
By :issue:`14711`, maps used in the coercion and conversion system
|
|
342
|
+
use *weak* references to domain and codomain, in contrast to other
|
|
343
|
+
maps::
|
|
344
|
+
|
|
345
|
+
sage: phi.domain # needs sage.rings.number_field
|
|
346
|
+
<weakref at ...; to '...NumberField_quadratic_with_category' at ...>
|
|
347
|
+
sage: phi._make_strong_references() # needs sage.rings.number_field
|
|
348
|
+
sage: print(phi.domain) # needs sage.rings.number_field
|
|
349
|
+
The constant function (...) -> Number Field in a
|
|
350
|
+
with defining polynomial x^2 + 5 with a = 2.236067977499790?*I
|
|
351
|
+
|
|
352
|
+
Now, as there is a strong reference, `Q` cannot be garbage collected::
|
|
353
|
+
|
|
354
|
+
sage: # needs sage.rings.number_field
|
|
355
|
+
sage: import gc
|
|
356
|
+
sage: _ = gc.collect()
|
|
357
|
+
sage: C = Q.__class__.__base__
|
|
358
|
+
sage: x = None
|
|
359
|
+
sage: numberQuadFields = len([x for x in gc.get_objects()
|
|
360
|
+
....: if isinstance(x, C)])
|
|
361
|
+
sage: del Q, x
|
|
362
|
+
sage: _ = gc.collect()
|
|
363
|
+
sage: numberQuadFields == len([x for x in gc.get_objects()
|
|
364
|
+
....: if isinstance(x, C)])
|
|
365
|
+
True
|
|
366
|
+
|
|
367
|
+
However, if we now make the references weak again, the number field can
|
|
368
|
+
be garbage collected, which of course makes the map and its parents
|
|
369
|
+
invalid. This is why :meth:`_make_weak_references` should only be used
|
|
370
|
+
if one really knows what one is doing::
|
|
371
|
+
|
|
372
|
+
sage: # needs sage.rings.number_field
|
|
373
|
+
sage: phi._make_weak_references()
|
|
374
|
+
sage: _ = gc.collect()
|
|
375
|
+
sage: numberQuadFields == len([x for x in gc.get_objects()
|
|
376
|
+
....: if isinstance(x, C)]) + 1
|
|
377
|
+
True
|
|
378
|
+
sage: phi
|
|
379
|
+
Defunct map
|
|
380
|
+
sage: phi._make_strong_references()
|
|
381
|
+
Traceback (most recent call last):
|
|
382
|
+
...
|
|
383
|
+
RuntimeError: The domain of this map became garbage collected
|
|
384
|
+
sage: phi.parent()
|
|
385
|
+
Traceback (most recent call last):
|
|
386
|
+
...
|
|
387
|
+
ValueError: This map is in an invalid state, the domain has been garbage collected
|
|
388
|
+
"""
|
|
389
|
+
if isinstance(self.domain, ConstantFunction):
|
|
390
|
+
return
|
|
391
|
+
D = self.domain()
|
|
392
|
+
C = self._codomain
|
|
393
|
+
if D is None or C is None:
|
|
394
|
+
raise RuntimeError("The domain of this map became garbage collected")
|
|
395
|
+
self.domain = ConstantFunction(D)
|
|
396
|
+
self._parent = homset.Hom(D, C, self._category_for)
|
|
397
|
+
|
|
398
|
+
cdef _update_slots(self, dict slots):
|
|
399
|
+
"""
|
|
400
|
+
Set various attributes of this map to implement unpickling.
|
|
401
|
+
|
|
402
|
+
INPUT:
|
|
403
|
+
|
|
404
|
+
- ``slots`` -- dictionary of slots to be updated;
|
|
405
|
+
the dictionary must have the keys ``'_domain'`` and
|
|
406
|
+
``'_codomain'``, and may have the keys ``'_repr_type_str'``
|
|
407
|
+
and ``'_is_coercion'``
|
|
408
|
+
|
|
409
|
+
TESTS:
|
|
410
|
+
|
|
411
|
+
Since it is a ``cdef``d method, it is tested using a dummy python method.
|
|
412
|
+
::
|
|
413
|
+
|
|
414
|
+
sage: # needs sage.rings.real_mpfr
|
|
415
|
+
sage: from sage.categories.map import Map
|
|
416
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
417
|
+
sage: f._update_slots_test({"_domain": RR, "_codomain": QQ}) # indirect doctest
|
|
418
|
+
sage: f.domain()
|
|
419
|
+
Real Field with 53 bits of precision
|
|
420
|
+
sage: f.codomain()
|
|
421
|
+
Rational Field
|
|
422
|
+
sage: f._repr_type_str
|
|
423
|
+
sage: f._update_slots_test({"_repr_type_str": "bla", "_domain": RR, "_codomain": QQ})
|
|
424
|
+
sage: f._repr_type_str
|
|
425
|
+
'bla'
|
|
426
|
+
"""
|
|
427
|
+
# todo: the following can break during unpickling of complex
|
|
428
|
+
# objects with circular references! In that case, _slots might
|
|
429
|
+
# contain incomplete objects.
|
|
430
|
+
self.domain = ConstantFunction(slots['_domain'])
|
|
431
|
+
self._codomain = slots['_codomain']
|
|
432
|
+
self.codomain = ConstantFunction(self._codomain)
|
|
433
|
+
|
|
434
|
+
# Several pickles exist without the following, so these are
|
|
435
|
+
# optional
|
|
436
|
+
self._repr_type_str = slots.get('_repr_type_str')
|
|
437
|
+
self._is_coercion = slots.get('_is_coercion')
|
|
438
|
+
|
|
439
|
+
def _update_slots_test(self, _slots):
|
|
440
|
+
"""
|
|
441
|
+
A Python method to test the cdef _update_slots method.
|
|
442
|
+
|
|
443
|
+
TESTS::
|
|
444
|
+
|
|
445
|
+
sage: # needs sage.rings.real_mpfr
|
|
446
|
+
sage: from sage.categories.map import Map
|
|
447
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
448
|
+
sage: f._update_slots_test({"_domain": RR, "_codomain": QQ})
|
|
449
|
+
sage: f.domain()
|
|
450
|
+
Real Field with 53 bits of precision
|
|
451
|
+
sage: f.codomain()
|
|
452
|
+
Rational Field
|
|
453
|
+
sage: f._repr_type_str
|
|
454
|
+
sage: f._update_slots_test({"_repr_type_str": "bla", "_domain": RR, "_codomain": QQ})
|
|
455
|
+
sage: f._repr_type_str
|
|
456
|
+
'bla'
|
|
457
|
+
"""
|
|
458
|
+
self._update_slots(_slots)
|
|
459
|
+
|
|
460
|
+
cdef dict _extra_slots(self):
|
|
461
|
+
"""
|
|
462
|
+
Return a dict with attributes to pickle and copy this map.
|
|
463
|
+
"""
|
|
464
|
+
return dict(
|
|
465
|
+
_domain=self.domain(),
|
|
466
|
+
_codomain=self._codomain,
|
|
467
|
+
_is_coercion=self._is_coercion,
|
|
468
|
+
_repr_type_str=self._repr_type_str)
|
|
469
|
+
|
|
470
|
+
def _extra_slots_test(self):
|
|
471
|
+
"""
|
|
472
|
+
A Python method to test the cdef _extra_slots method.
|
|
473
|
+
|
|
474
|
+
TESTS::
|
|
475
|
+
|
|
476
|
+
sage: from sage.categories.map import Map
|
|
477
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
478
|
+
sage: f._extra_slots_test()
|
|
479
|
+
{'_codomain': Integer Ring,
|
|
480
|
+
'_domain': Rational Field,
|
|
481
|
+
'_is_coercion': False,
|
|
482
|
+
'_repr_type_str': None}
|
|
483
|
+
"""
|
|
484
|
+
return self._extra_slots()
|
|
485
|
+
|
|
486
|
+
def __reduce__(self):
|
|
487
|
+
"""
|
|
488
|
+
TESTS::
|
|
489
|
+
|
|
490
|
+
sage: from sage.categories.map import Map
|
|
491
|
+
sage: f = Map(Hom(QQ, ZZ, Rings())); f
|
|
492
|
+
Generic map:
|
|
493
|
+
From: Rational Field
|
|
494
|
+
To: Integer Ring
|
|
495
|
+
sage: loads(dumps(f)) # indirect doctest
|
|
496
|
+
Generic map:
|
|
497
|
+
From: Rational Field
|
|
498
|
+
To: Integer Ring
|
|
499
|
+
"""
|
|
500
|
+
if HAS_DICTIONARY(self):
|
|
501
|
+
_dict = self.__dict__
|
|
502
|
+
else:
|
|
503
|
+
_dict = {}
|
|
504
|
+
return unpickle_map, (type(self), self.parent(), _dict, self._extra_slots())
|
|
505
|
+
|
|
506
|
+
def _repr_type(self):
|
|
507
|
+
"""
|
|
508
|
+
Return a string describing the specific type of this map, to be used when printing ``self``.
|
|
509
|
+
|
|
510
|
+
.. NOTE::
|
|
511
|
+
|
|
512
|
+
By default, the string ``'Generic'`` is returned. Subclasses may overload this method.
|
|
513
|
+
|
|
514
|
+
EXAMPLES::
|
|
515
|
+
|
|
516
|
+
sage: from sage.categories.map import Map
|
|
517
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
518
|
+
sage: print(f._repr_type())
|
|
519
|
+
Generic
|
|
520
|
+
sage: R.<x,y> = QQ[]
|
|
521
|
+
sage: phi = R.hom([x+y, x-y], R)
|
|
522
|
+
sage: print(phi._repr_type())
|
|
523
|
+
Ring
|
|
524
|
+
"""
|
|
525
|
+
if self._repr_type_str is None:
|
|
526
|
+
return "Generic"
|
|
527
|
+
else:
|
|
528
|
+
return self._repr_type_str
|
|
529
|
+
|
|
530
|
+
def _repr_defn(self):
|
|
531
|
+
"""
|
|
532
|
+
Return a string describing the definition of ``self``, to be used when printing ``self``.
|
|
533
|
+
|
|
534
|
+
.. NOTE::
|
|
535
|
+
|
|
536
|
+
By default, the empty string is returned. Subclasses may overload this method.
|
|
537
|
+
|
|
538
|
+
EXAMPLES::
|
|
539
|
+
|
|
540
|
+
sage: from sage.categories.map import Map
|
|
541
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
542
|
+
sage: f._repr_defn() == ''
|
|
543
|
+
True
|
|
544
|
+
sage: R.<x,y> = QQ[]
|
|
545
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
546
|
+
sage: print(f._repr_defn())
|
|
547
|
+
x |--> x + y
|
|
548
|
+
y |--> x - y
|
|
549
|
+
"""
|
|
550
|
+
return ""
|
|
551
|
+
|
|
552
|
+
def _repr_(self):
|
|
553
|
+
"""
|
|
554
|
+
.. NOTE::
|
|
555
|
+
|
|
556
|
+
The string representation is based on the strings returned by
|
|
557
|
+
:meth:`_repr_defn` and :meth:`_repr_type`, as well as the domain
|
|
558
|
+
and the codomain.
|
|
559
|
+
|
|
560
|
+
A map that has been subject to :meth:`_make_weak_references` has
|
|
561
|
+
probably been used internally in the coercion system. Hence, it
|
|
562
|
+
may become defunct by garbage collection of the domain. In this
|
|
563
|
+
case, a warning is printed accordingly.
|
|
564
|
+
|
|
565
|
+
EXAMPLES::
|
|
566
|
+
|
|
567
|
+
sage: from sage.categories.map import Map
|
|
568
|
+
sage: Map(Hom(QQ, ZZ, Rings())) # indirect doctest
|
|
569
|
+
Generic map:
|
|
570
|
+
From: Rational Field
|
|
571
|
+
To: Integer Ring
|
|
572
|
+
sage: R.<x,y> = QQ[]
|
|
573
|
+
sage: R.hom([x+y, x-y], R)
|
|
574
|
+
Ring endomorphism of Multivariate Polynomial Ring in x, y over Rational Field
|
|
575
|
+
Defn: x |--> x + y
|
|
576
|
+
y |--> x - y
|
|
577
|
+
|
|
578
|
+
TESTS::
|
|
579
|
+
|
|
580
|
+
sage: # needs sage.rings.number_field
|
|
581
|
+
sage: Q = QuadraticField(-5)
|
|
582
|
+
sage: phi = CDF._internal_coerce_map_from(Q); phi
|
|
583
|
+
(map internal to coercion system -- copy before use)
|
|
584
|
+
Composite map:
|
|
585
|
+
From: Number Field in a with defining polynomial x^2 + 5 with a = 2.236067977499790?*I
|
|
586
|
+
To: Complex Double Field
|
|
587
|
+
sage: del Q
|
|
588
|
+
sage: import gc
|
|
589
|
+
sage: _ = gc.collect()
|
|
590
|
+
sage: phi
|
|
591
|
+
Defunct map
|
|
592
|
+
"""
|
|
593
|
+
D = self.domain()
|
|
594
|
+
if D is None:
|
|
595
|
+
return "Defunct map"
|
|
596
|
+
s = "%s map:" % self._repr_type()
|
|
597
|
+
s += "\n From: %s" % D
|
|
598
|
+
s += "\n To: %s" % self._codomain
|
|
599
|
+
if isinstance(self.domain, ConstantFunction):
|
|
600
|
+
d = self._repr_defn()
|
|
601
|
+
if d != '':
|
|
602
|
+
s += "\n Defn: %s" % ('\n '.join(d.split('\n')))
|
|
603
|
+
else:
|
|
604
|
+
d = "(map internal to coercion system -- copy before use)"
|
|
605
|
+
s = d + "\n" + s
|
|
606
|
+
return s
|
|
607
|
+
|
|
608
|
+
def _default_repr_(self):
|
|
609
|
+
D = self.domain()
|
|
610
|
+
if D is None:
|
|
611
|
+
return "Defunct map"
|
|
612
|
+
s = "%s map:" % self._repr_type()
|
|
613
|
+
s += "\n From: %s" % D
|
|
614
|
+
s += "\n To: %s" % self._codomain
|
|
615
|
+
d = self._repr_defn()
|
|
616
|
+
if d != '':
|
|
617
|
+
s += "\n Defn: %s" % ('\n '.join(d.split('\n')))
|
|
618
|
+
return s
|
|
619
|
+
|
|
620
|
+
def domains(self):
|
|
621
|
+
"""
|
|
622
|
+
Iterate over the domains of the factors of a (composite) map.
|
|
623
|
+
|
|
624
|
+
This default implementation simply yields the domain of this map.
|
|
625
|
+
|
|
626
|
+
.. SEEALSO:: :meth:`FormalCompositeMap.domains`
|
|
627
|
+
|
|
628
|
+
EXAMPLES::
|
|
629
|
+
|
|
630
|
+
sage: list(QQ.coerce_map_from(ZZ).domains())
|
|
631
|
+
[Integer Ring]
|
|
632
|
+
"""
|
|
633
|
+
yield self.domain()
|
|
634
|
+
|
|
635
|
+
def category_for(self):
|
|
636
|
+
"""
|
|
637
|
+
Return the category ``self`` is a morphism for.
|
|
638
|
+
|
|
639
|
+
.. NOTE::
|
|
640
|
+
|
|
641
|
+
This is different from the category of maps to which this
|
|
642
|
+
map belongs *as an object*.
|
|
643
|
+
|
|
644
|
+
EXAMPLES::
|
|
645
|
+
|
|
646
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
647
|
+
sage: X.<x> = ZZ[]
|
|
648
|
+
sage: Y = ZZ
|
|
649
|
+
sage: phi = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
650
|
+
sage: phi.category_for()
|
|
651
|
+
Category of rings
|
|
652
|
+
sage: phi.category()
|
|
653
|
+
Category of homsets of unital magmas and additive unital additive magmas
|
|
654
|
+
sage: R.<x,y> = QQ[]
|
|
655
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
656
|
+
sage: f.category_for()
|
|
657
|
+
Join of Category of unique factorization domains
|
|
658
|
+
and Category of algebras with basis over
|
|
659
|
+
(number fields and quotient fields and metric spaces)
|
|
660
|
+
and Category of commutative algebras over
|
|
661
|
+
(number fields and quotient fields and metric spaces)
|
|
662
|
+
and Category of infinite sets
|
|
663
|
+
sage: f.category()
|
|
664
|
+
Category of endsets of unital magmas
|
|
665
|
+
and right modules over (number fields and quotient fields and metric spaces)
|
|
666
|
+
and left modules over (number fields and quotient fields and metric spaces)
|
|
667
|
+
|
|
668
|
+
|
|
669
|
+
FIXME: find a better name for this method
|
|
670
|
+
"""
|
|
671
|
+
if self._category_for is None:
|
|
672
|
+
# This can happen if the map is the result of unpickling.
|
|
673
|
+
# We have initialised self._parent, but could not set
|
|
674
|
+
# self._category_for at that moment, because it could
|
|
675
|
+
# happen that the parent was not fully constructed and
|
|
676
|
+
# did not know its category yet.
|
|
677
|
+
self._category_for = self._parent.homset_category()
|
|
678
|
+
return self._category_for
|
|
679
|
+
|
|
680
|
+
def __call__(self, x, *args, **kwds):
|
|
681
|
+
"""
|
|
682
|
+
Apply this map to ``x``.
|
|
683
|
+
|
|
684
|
+
IMPLEMENTATION:
|
|
685
|
+
|
|
686
|
+
- To implement the call method in a subclass of Map, implement
|
|
687
|
+
:meth:`_call_` and possibly also :meth:`_call_with_args` and
|
|
688
|
+
:meth:`pushforward`.
|
|
689
|
+
- If the parent of ``x`` cannot be coerced into the domain of
|
|
690
|
+
``self``, then the method ``pushforward`` is called with ``x``
|
|
691
|
+
and the other given arguments, provided it is implemented.
|
|
692
|
+
In that way, ``self`` could be applied to objects like ideals
|
|
693
|
+
or sub-modules.
|
|
694
|
+
- If there is no coercion and if ``pushforward`` is not implemented
|
|
695
|
+
or fails, ``_call_`` is called after conversion into the domain
|
|
696
|
+
(which may be possible even when there is no coercion); if there
|
|
697
|
+
are additional arguments (or keyword arguments),
|
|
698
|
+
:meth:`_call_with_args` is called instead. Note that the
|
|
699
|
+
positional arguments after ``x`` are passed as a tuple to
|
|
700
|
+
:meth:`_call_with_args` and the named arguments are passed
|
|
701
|
+
as a dictionary.
|
|
702
|
+
|
|
703
|
+
INPUT:
|
|
704
|
+
|
|
705
|
+
- ``x`` -- an element coercible to the domain of ``self``; also objects
|
|
706
|
+
like ideals are supported in some cases
|
|
707
|
+
|
|
708
|
+
OUTPUT:
|
|
709
|
+
|
|
710
|
+
an element (or ideal, etc.)
|
|
711
|
+
|
|
712
|
+
EXAMPLES::
|
|
713
|
+
|
|
714
|
+
sage: R.<x,y> = QQ[]; phi = R.hom([y, x])
|
|
715
|
+
sage: phi(y) # indirect doctest
|
|
716
|
+
x
|
|
717
|
+
|
|
718
|
+
We take the image of an ideal::
|
|
719
|
+
|
|
720
|
+
sage: I = ideal(x, y); I
|
|
721
|
+
Ideal (x, y) of Multivariate Polynomial Ring in x, y over Rational Field
|
|
722
|
+
sage: phi(I)
|
|
723
|
+
Ideal (y, x) of Multivariate Polynomial Ring in x, y over Rational Field
|
|
724
|
+
|
|
725
|
+
TESTS:
|
|
726
|
+
|
|
727
|
+
We test that the map can be applied to something that converts
|
|
728
|
+
(but not coerces) into the domain and can *not* be dealt with
|
|
729
|
+
by :meth:`pushforward` (see :issue:`10496`)::
|
|
730
|
+
|
|
731
|
+
sage: D = {(0, 2): -1, (0, 0): -1, (1, 1): 7, (2, 0): 1/3}
|
|
732
|
+
sage: phi(D)
|
|
733
|
+
-x^2 + 7*x*y + 1/3*y^2 - 1
|
|
734
|
+
|
|
735
|
+
We test what happens if the argument can't be converted into
|
|
736
|
+
the domain::
|
|
737
|
+
|
|
738
|
+
sage: from sage.categories.map import Map
|
|
739
|
+
sage: f = Map(Hom(ZZ, QQ, Rings()))
|
|
740
|
+
sage: f(1/2)
|
|
741
|
+
Traceback (most recent call last):
|
|
742
|
+
...
|
|
743
|
+
TypeError: 1/2 fails to convert into the map's domain Integer Ring,
|
|
744
|
+
but a `pushforward` method is not properly implemented
|
|
745
|
+
|
|
746
|
+
We test that the default call method really works as described
|
|
747
|
+
above (that was fixed in :issue:`10496`)::
|
|
748
|
+
|
|
749
|
+
sage: class FOO(Map):
|
|
750
|
+
....: def _call_(self, x):
|
|
751
|
+
....: print("_call_ {}".format(parent(x)))
|
|
752
|
+
....: return self.codomain()(x)
|
|
753
|
+
....: def _call_with_args(self, x, args=(), kwds={}):
|
|
754
|
+
....: print("_call_with_args {}".format(parent(x)))
|
|
755
|
+
....: return self.codomain()(x)^kwds.get('exponent', 1)
|
|
756
|
+
....: def pushforward(self, x, exponent=1):
|
|
757
|
+
....: print("pushforward {}".format(parent(x)))
|
|
758
|
+
....: return self.codomain()(1/x)^exponent
|
|
759
|
+
sage: f = FOO(ZZ, QQ)
|
|
760
|
+
sage: f(1/1) #indirect doctest
|
|
761
|
+
pushforward Rational Field
|
|
762
|
+
1
|
|
763
|
+
|
|
764
|
+
``_call_`` and ``_call_with_args_`` are used *after* coercion::
|
|
765
|
+
|
|
766
|
+
sage: f(int(1))
|
|
767
|
+
_call_ Integer Ring
|
|
768
|
+
1
|
|
769
|
+
sage: f(int(2), exponent=2)
|
|
770
|
+
_call_with_args Integer Ring
|
|
771
|
+
4
|
|
772
|
+
|
|
773
|
+
``pushforward`` is called without conversion::
|
|
774
|
+
|
|
775
|
+
sage: f(1/2)
|
|
776
|
+
pushforward Rational Field
|
|
777
|
+
2
|
|
778
|
+
sage: f(1/2, exponent=2)
|
|
779
|
+
pushforward Rational Field
|
|
780
|
+
4
|
|
781
|
+
|
|
782
|
+
If the argument does not coerce into the domain, and if
|
|
783
|
+
``pushforward`` fails, ``_call_`` is tried after conversion::
|
|
784
|
+
|
|
785
|
+
sage: g = FOO(QQ, ZZ)
|
|
786
|
+
sage: g(SR(3)) # needs sage.symbolic
|
|
787
|
+
pushforward Symbolic Ring
|
|
788
|
+
_call_ Rational Field
|
|
789
|
+
3
|
|
790
|
+
sage: g(SR(3), exponent=2) # needs sage.symbolic
|
|
791
|
+
pushforward Symbolic Ring
|
|
792
|
+
_call_with_args Rational Field
|
|
793
|
+
9
|
|
794
|
+
|
|
795
|
+
If conversion fails as well, an error is raised::
|
|
796
|
+
|
|
797
|
+
sage: h = FOO(ZZ, ZZ)
|
|
798
|
+
sage: h(2/3)
|
|
799
|
+
Traceback (most recent call last):
|
|
800
|
+
...
|
|
801
|
+
TypeError: 2/3 fails to convert into the map's domain Integer Ring,
|
|
802
|
+
but a `pushforward` method is not properly implemented
|
|
803
|
+
"""
|
|
804
|
+
P = parent(x)
|
|
805
|
+
cdef Parent D = self.domain()
|
|
806
|
+
if P is D: # we certainly want to call _call_/with_args
|
|
807
|
+
if not args and not kwds:
|
|
808
|
+
return self._call_(x)
|
|
809
|
+
return self._call_with_args(x, args, kwds)
|
|
810
|
+
# Is there coercion?
|
|
811
|
+
converter = D._internal_coerce_map_from(P)
|
|
812
|
+
if converter is None:
|
|
813
|
+
try:
|
|
814
|
+
return self.pushforward(x, *args, **kwds)
|
|
815
|
+
except (AttributeError, TypeError, NotImplementedError):
|
|
816
|
+
pass
|
|
817
|
+
try:
|
|
818
|
+
x = D(x)
|
|
819
|
+
except (TypeError, NotImplementedError):
|
|
820
|
+
raise TypeError("%s fails to convert into the map's domain %s, but a `pushforward` method is not properly implemented" % (x, D))
|
|
821
|
+
else:
|
|
822
|
+
x = converter(x)
|
|
823
|
+
if not args and not kwds:
|
|
824
|
+
return self._call_(x)
|
|
825
|
+
return self._call_with_args(x, args, kwds)
|
|
826
|
+
|
|
827
|
+
cpdef Element _call_(self, x):
|
|
828
|
+
"""
|
|
829
|
+
Call method with a single argument, not implemented in the base class.
|
|
830
|
+
|
|
831
|
+
TESTS::
|
|
832
|
+
|
|
833
|
+
sage: from sage.categories.map import Map
|
|
834
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
835
|
+
sage: f(1/2) # indirect doctest
|
|
836
|
+
Traceback (most recent call last):
|
|
837
|
+
...
|
|
838
|
+
NotImplementedError: <class 'sage.categories.map.Map'>
|
|
839
|
+
"""
|
|
840
|
+
raise NotImplementedError(type(self))
|
|
841
|
+
|
|
842
|
+
cpdef Element _call_with_args(self, x, args=(), kwds={}):
|
|
843
|
+
"""
|
|
844
|
+
Call method with multiple arguments, not implemented in the base class.
|
|
845
|
+
|
|
846
|
+
TESTS::
|
|
847
|
+
|
|
848
|
+
sage: from sage.categories.map import Map
|
|
849
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
850
|
+
sage: f(1/2, 2, foo='bar') # indirect doctest
|
|
851
|
+
Traceback (most recent call last):
|
|
852
|
+
...
|
|
853
|
+
NotImplementedError: _call_with_args not overridden to accept arguments for <class 'sage.categories.map.Map'>
|
|
854
|
+
"""
|
|
855
|
+
if not args and not kwds:
|
|
856
|
+
return self(x)
|
|
857
|
+
raise NotImplementedError("_call_with_args not overridden "
|
|
858
|
+
f"to accept arguments for {type(self)}")
|
|
859
|
+
|
|
860
|
+
def __mul__(self, right):
|
|
861
|
+
r"""
|
|
862
|
+
The multiplication * operator is operator composition.
|
|
863
|
+
|
|
864
|
+
IMPLEMENTATION:
|
|
865
|
+
|
|
866
|
+
If you want to change the behaviour of composition for
|
|
867
|
+
derived classes, please overload :meth:`_composition_`
|
|
868
|
+
(but not :meth:`_composition`!) of the left factor.
|
|
869
|
+
|
|
870
|
+
INPUT:
|
|
871
|
+
|
|
872
|
+
- ``self`` -- Map
|
|
873
|
+
- ``right`` -- Map
|
|
874
|
+
|
|
875
|
+
OUTPUT:
|
|
876
|
+
|
|
877
|
+
The map `x \mapsto self(right(x))`.
|
|
878
|
+
|
|
879
|
+
EXAMPLES::
|
|
880
|
+
|
|
881
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
882
|
+
sage: X.<x> = ZZ[]
|
|
883
|
+
sage: Y = ZZ
|
|
884
|
+
sage: Z = QQ
|
|
885
|
+
sage: phi_xy = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
886
|
+
sage: phi_yz = SetMorphism(Hom(Y, Z, CommutativeAdditiveMonoids()), lambda y: QQ(y)/2)
|
|
887
|
+
sage: phi_yz * phi_xy
|
|
888
|
+
Composite map:
|
|
889
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
890
|
+
To: Rational Field
|
|
891
|
+
Defn: Generic morphism:
|
|
892
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
893
|
+
To: Integer Ring
|
|
894
|
+
then
|
|
895
|
+
Generic morphism:
|
|
896
|
+
From: Integer Ring
|
|
897
|
+
To: Rational Field
|
|
898
|
+
|
|
899
|
+
If ``right`` is a ring homomorphism given by the images of
|
|
900
|
+
generators, then it is attempted to form the composition
|
|
901
|
+
accordingly. Only if this fails, or if the result does not
|
|
902
|
+
belong to the given homset, a formal composite map is
|
|
903
|
+
returned (as above).
|
|
904
|
+
::
|
|
905
|
+
|
|
906
|
+
sage: R.<x,y> = QQ[]
|
|
907
|
+
sage: S.<a,b> = QQ[]
|
|
908
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
909
|
+
sage: f = R.hom([a+b, a-b])
|
|
910
|
+
sage: g = S.hom([x+y, x-y])
|
|
911
|
+
sage: f*g
|
|
912
|
+
Ring endomorphism of Multivariate Polynomial Ring in a, b over Rational Field
|
|
913
|
+
Defn: a |--> 2*a
|
|
914
|
+
b |--> 2*b
|
|
915
|
+
sage: h = SetMorphism(Hom(S, QQ, Rings()), lambda p: p.lc())
|
|
916
|
+
sage: h*f
|
|
917
|
+
Composite map:
|
|
918
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
919
|
+
To: Rational Field
|
|
920
|
+
Defn: Ring morphism:
|
|
921
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
922
|
+
To: Multivariate Polynomial Ring in a, b over Rational Field
|
|
923
|
+
Defn: x |--> a + b
|
|
924
|
+
y |--> a - b
|
|
925
|
+
then
|
|
926
|
+
Generic morphism:
|
|
927
|
+
From: Multivariate Polynomial Ring in a, b over Rational Field
|
|
928
|
+
To: Rational Field
|
|
929
|
+
"""
|
|
930
|
+
if not isinstance(right, Map):
|
|
931
|
+
raise TypeError("right (=%s) must be a map to multiply it by %s" % (right, self))
|
|
932
|
+
if right.codomain() != self.domain():
|
|
933
|
+
raise TypeError("self (=%s) domain must equal right (=%s) codomain" % (self, right))
|
|
934
|
+
return self._composition(right)
|
|
935
|
+
|
|
936
|
+
def _composition(self, right):
|
|
937
|
+
"""
|
|
938
|
+
Composition of maps, which generically returns a :class:`CompositeMap`.
|
|
939
|
+
|
|
940
|
+
INPUT:
|
|
941
|
+
|
|
942
|
+
- ``self`` -- a Map in some ``Hom(Y, Z, category_left)``
|
|
943
|
+
- ``right`` -- a Map in some ``Hom(X, Y, category_right)``
|
|
944
|
+
|
|
945
|
+
OUTPUT:
|
|
946
|
+
|
|
947
|
+
Returns the composition of ``self`` and ``right`` as a
|
|
948
|
+
morphism in ``Hom(X, Z, category)`` where ``category`` is the
|
|
949
|
+
meet of ``category_left`` and ``category_right``.
|
|
950
|
+
|
|
951
|
+
EXAMPLES::
|
|
952
|
+
|
|
953
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
954
|
+
sage: X.<x> = ZZ[]
|
|
955
|
+
sage: Y = ZZ
|
|
956
|
+
sage: Z = QQ
|
|
957
|
+
sage: phi_xy = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
958
|
+
sage: phi_yz = SetMorphism(Hom(Y, Z, CommutativeAdditiveMonoids()), lambda y: QQ(y)/2)
|
|
959
|
+
sage: phi_yz._composition(phi_xy)
|
|
960
|
+
Composite map:
|
|
961
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
962
|
+
To: Rational Field
|
|
963
|
+
Defn: Generic morphism:
|
|
964
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
965
|
+
To: Integer Ring
|
|
966
|
+
then
|
|
967
|
+
Generic morphism:
|
|
968
|
+
From: Integer Ring
|
|
969
|
+
To: Rational Field
|
|
970
|
+
sage: phi_yz.category_for()
|
|
971
|
+
Category of commutative additive monoids
|
|
972
|
+
"""
|
|
973
|
+
category = self.category_for()._meet_(right.category_for())
|
|
974
|
+
H = homset.Hom(right.domain(), self._codomain, category)
|
|
975
|
+
return self._composition_(right, H)
|
|
976
|
+
|
|
977
|
+
def _composition_(self, right, homset):
|
|
978
|
+
"""
|
|
979
|
+
INPUT:
|
|
980
|
+
|
|
981
|
+
- ``self``, ``right`` -- maps
|
|
982
|
+
- ``homset`` -- a homset
|
|
983
|
+
|
|
984
|
+
ASSUMPTION:
|
|
985
|
+
|
|
986
|
+
The codomain of ``right`` is contained in the domain of ``self``.
|
|
987
|
+
This assumption is not verified.
|
|
988
|
+
|
|
989
|
+
OUTPUT:
|
|
990
|
+
|
|
991
|
+
Returns a formal composite map, the composition of ``right``
|
|
992
|
+
followed by ``self``, as a morphism in ``homset``.
|
|
993
|
+
|
|
994
|
+
Classes deriving from :class:`Map` are encouraged to override
|
|
995
|
+
this whenever meaningful. This is the case, e.g., for ring
|
|
996
|
+
homomorphisms.
|
|
997
|
+
|
|
998
|
+
EXAMPLES::
|
|
999
|
+
|
|
1000
|
+
sage: Rx.<x> = ZZ['x']
|
|
1001
|
+
sage: Ry.<y> = ZZ['y']
|
|
1002
|
+
sage: Rz.<z> = ZZ['z']
|
|
1003
|
+
sage: phi_xy = Rx.hom([y+1]); phi_xy
|
|
1004
|
+
Ring morphism:
|
|
1005
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1006
|
+
To: Univariate Polynomial Ring in y over Integer Ring
|
|
1007
|
+
Defn: x |--> y + 1
|
|
1008
|
+
sage: phi_yz = Ry.hom([z+1]); phi_yz
|
|
1009
|
+
Ring morphism:
|
|
1010
|
+
From: Univariate Polynomial Ring in y over Integer Ring
|
|
1011
|
+
To: Univariate Polynomial Ring in z over Integer Ring
|
|
1012
|
+
Defn: y |--> z + 1
|
|
1013
|
+
sage: phi_xz = phi_yz._composition_(phi_xy, Hom(Rx, Rz, Monoids()))
|
|
1014
|
+
sage: phi_xz
|
|
1015
|
+
Composite map:
|
|
1016
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1017
|
+
To: Univariate Polynomial Ring in z over Integer Ring
|
|
1018
|
+
Defn: Ring morphism:
|
|
1019
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1020
|
+
To: Univariate Polynomial Ring in y over Integer Ring
|
|
1021
|
+
Defn: x |--> y + 1
|
|
1022
|
+
then
|
|
1023
|
+
Ring morphism:
|
|
1024
|
+
From: Univariate Polynomial Ring in y over Integer Ring
|
|
1025
|
+
To: Univariate Polynomial Ring in z over Integer Ring
|
|
1026
|
+
Defn: y |--> z + 1
|
|
1027
|
+
sage: phi_xz.category_for()
|
|
1028
|
+
Category of monoids
|
|
1029
|
+
|
|
1030
|
+
TESTS:
|
|
1031
|
+
|
|
1032
|
+
This illustrates that it is not tested whether the maps can actually
|
|
1033
|
+
be composed, i.e., whether codomain and domain match.
|
|
1034
|
+
::
|
|
1035
|
+
|
|
1036
|
+
sage: R.<x,y> = QQ[]
|
|
1037
|
+
sage: S.<a,b> = QQ[]
|
|
1038
|
+
sage: f_R = R.hom([x+y, x-y], R)
|
|
1039
|
+
sage: f_S = S.hom([a+b, a-b], S)
|
|
1040
|
+
sage: foo_bar = f_R._composition_(f_S, Hom(S, R, Monoids()))
|
|
1041
|
+
sage: foo_bar(a)
|
|
1042
|
+
2*x
|
|
1043
|
+
|
|
1044
|
+
However, it is tested when attempting to compose the maps in
|
|
1045
|
+
the usual multiplicative notation::
|
|
1046
|
+
|
|
1047
|
+
sage: f_R*f_S
|
|
1048
|
+
Traceback (most recent call last):
|
|
1049
|
+
...
|
|
1050
|
+
TypeError: self (=Ring endomorphism of Multivariate Polynomial Ring in x, y over Rational Field
|
|
1051
|
+
Defn: x |--> x + y
|
|
1052
|
+
y |--> x - y) domain must equal right (=Ring endomorphism of Multivariate Polynomial Ring in a, b over Rational Field
|
|
1053
|
+
Defn: a |--> a + b
|
|
1054
|
+
b |--> a - b) codomain
|
|
1055
|
+
"""
|
|
1056
|
+
return FormalCompositeMap(homset, right, self)
|
|
1057
|
+
|
|
1058
|
+
def pre_compose(self, right):
|
|
1059
|
+
"""
|
|
1060
|
+
INPUT:
|
|
1061
|
+
|
|
1062
|
+
- ``self`` -- a Map in some ``Hom(Y, Z, category_left)``
|
|
1063
|
+
- ``left`` -- a Map in some ``Hom(X, Y, category_right)``
|
|
1064
|
+
|
|
1065
|
+
Returns the composition of ``right`` followed by ``self`` as a
|
|
1066
|
+
morphism in ``Hom(X, Z, category)`` where ``category`` is the
|
|
1067
|
+
meet of ``category_left`` and ``category_right``.
|
|
1068
|
+
|
|
1069
|
+
EXAMPLES::
|
|
1070
|
+
|
|
1071
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1072
|
+
sage: X.<x> = ZZ[]
|
|
1073
|
+
sage: Y = ZZ
|
|
1074
|
+
sage: Z = QQ
|
|
1075
|
+
sage: phi_xy = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
1076
|
+
sage: phi_yz = SetMorphism(Hom(Y, Z, Monoids()), lambda y: QQ(y**2))
|
|
1077
|
+
sage: phi_xz = phi_yz.pre_compose(phi_xy); phi_xz
|
|
1078
|
+
Composite map:
|
|
1079
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1080
|
+
To: Rational Field
|
|
1081
|
+
Defn: Generic morphism:
|
|
1082
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1083
|
+
To: Integer Ring
|
|
1084
|
+
then
|
|
1085
|
+
Generic morphism:
|
|
1086
|
+
From: Integer Ring
|
|
1087
|
+
To: Rational Field
|
|
1088
|
+
sage: phi_xz.category_for()
|
|
1089
|
+
Category of monoids
|
|
1090
|
+
"""
|
|
1091
|
+
D = self.domain()
|
|
1092
|
+
if D is not right.codomain():
|
|
1093
|
+
right = right.extend_codomain(D)
|
|
1094
|
+
return self._composition(right)
|
|
1095
|
+
|
|
1096
|
+
def post_compose(self, left):
|
|
1097
|
+
"""
|
|
1098
|
+
INPUT:
|
|
1099
|
+
|
|
1100
|
+
- ``self`` -- a Map in some ``Hom(X, Y, category_right)``
|
|
1101
|
+
- ``left`` -- a Map in some ``Hom(Y, Z, category_left)``
|
|
1102
|
+
|
|
1103
|
+
Returns the composition of ``self`` followed by ``left`` as a
|
|
1104
|
+
morphism in ``Hom(X, Z, category)`` where ``category`` is the
|
|
1105
|
+
meet of ``category_left`` and ``category_right``.
|
|
1106
|
+
|
|
1107
|
+
Caveat: see the current restrictions on :meth:`Category.meet`
|
|
1108
|
+
|
|
1109
|
+
EXAMPLES::
|
|
1110
|
+
|
|
1111
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1112
|
+
sage: X.<x> = ZZ[]
|
|
1113
|
+
sage: Y = ZZ
|
|
1114
|
+
sage: Z = QQ
|
|
1115
|
+
sage: phi_xy = SetMorphism(Hom(X, Y, Rings()), lambda p: p[0])
|
|
1116
|
+
sage: phi_yz = SetMorphism(Hom(Y, Z, Monoids()), lambda y: QQ(y**2))
|
|
1117
|
+
sage: phi_xz = phi_xy.post_compose(phi_yz); phi_xz
|
|
1118
|
+
Composite map:
|
|
1119
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1120
|
+
To: Rational Field
|
|
1121
|
+
Defn: Generic morphism:
|
|
1122
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1123
|
+
To: Integer Ring
|
|
1124
|
+
then
|
|
1125
|
+
Generic morphism:
|
|
1126
|
+
From: Integer Ring
|
|
1127
|
+
To: Rational Field
|
|
1128
|
+
sage: phi_xz.category_for()
|
|
1129
|
+
Category of monoids
|
|
1130
|
+
"""
|
|
1131
|
+
return left._composition(self)
|
|
1132
|
+
|
|
1133
|
+
def extend_domain(self, new_domain):
|
|
1134
|
+
r"""
|
|
1135
|
+
INPUT:
|
|
1136
|
+
|
|
1137
|
+
- ``self`` -- a member of Hom(Y, Z)
|
|
1138
|
+
- ``new_codomain`` -- an object X such that there is a canonical coercion
|
|
1139
|
+
`\phi` in Hom(X, Y)
|
|
1140
|
+
|
|
1141
|
+
OUTPUT:
|
|
1142
|
+
|
|
1143
|
+
An element of Hom(X, Z) obtained by composing self with `\phi`. If
|
|
1144
|
+
no canonical `\phi` exists, a :exc:`TypeError` is raised.
|
|
1145
|
+
|
|
1146
|
+
EXAMPLES::
|
|
1147
|
+
|
|
1148
|
+
sage: # needs sage.rings.complex_double
|
|
1149
|
+
sage: mor = CDF.coerce_map_from(RDF)
|
|
1150
|
+
sage: mor.extend_domain(QQ)
|
|
1151
|
+
Composite map:
|
|
1152
|
+
From: Rational Field
|
|
1153
|
+
To: Complex Double Field
|
|
1154
|
+
Defn: Native morphism:
|
|
1155
|
+
From: Rational Field
|
|
1156
|
+
To: Real Double Field
|
|
1157
|
+
then
|
|
1158
|
+
Native morphism:
|
|
1159
|
+
From: Real Double Field
|
|
1160
|
+
To: Complex Double Field
|
|
1161
|
+
sage: mor.extend_domain(ZZ['x'])
|
|
1162
|
+
Traceback (most recent call last):
|
|
1163
|
+
...
|
|
1164
|
+
TypeError: No coercion from Univariate Polynomial Ring in x over Integer Ring
|
|
1165
|
+
to Real Double Field
|
|
1166
|
+
"""
|
|
1167
|
+
D = self.domain()
|
|
1168
|
+
if D is None:
|
|
1169
|
+
raise ValueError("This map became defunct by garbage collection")
|
|
1170
|
+
cdef Map connecting = D._internal_coerce_map_from(new_domain)
|
|
1171
|
+
if connecting is None:
|
|
1172
|
+
raise TypeError("No coercion from %s to %s" % (new_domain, D))
|
|
1173
|
+
elif connecting.codomain() is not D:
|
|
1174
|
+
raise RuntimeError("BUG: coerce_map_from should always return a map to self (%s)" % D)
|
|
1175
|
+
else:
|
|
1176
|
+
return self.pre_compose(connecting.__copy__())
|
|
1177
|
+
|
|
1178
|
+
def extend_codomain(self, new_codomain):
|
|
1179
|
+
r"""
|
|
1180
|
+
INPUT:
|
|
1181
|
+
|
|
1182
|
+
- ``self`` -- a member of Hom(X, Y)
|
|
1183
|
+
- ``new_codomain`` -- an object Z such that there is a canonical coercion
|
|
1184
|
+
`\phi` in Hom(Y, Z)
|
|
1185
|
+
|
|
1186
|
+
OUTPUT:
|
|
1187
|
+
|
|
1188
|
+
An element of Hom(X, Z) obtained by composing ``self`` with `\phi`. If
|
|
1189
|
+
no canonical `\phi` exists, a :exc:`TypeError` is raised.
|
|
1190
|
+
|
|
1191
|
+
EXAMPLES::
|
|
1192
|
+
|
|
1193
|
+
sage: mor = QQ.coerce_map_from(ZZ)
|
|
1194
|
+
sage: mor.extend_codomain(RDF)
|
|
1195
|
+
Composite map:
|
|
1196
|
+
From: Integer Ring
|
|
1197
|
+
To: Real Double Field
|
|
1198
|
+
Defn: Natural morphism:
|
|
1199
|
+
From: Integer Ring
|
|
1200
|
+
To: Rational Field
|
|
1201
|
+
then
|
|
1202
|
+
Native morphism:
|
|
1203
|
+
From: Rational Field
|
|
1204
|
+
To: Real Double Field
|
|
1205
|
+
sage: mor.extend_codomain(GF(7))
|
|
1206
|
+
Traceback (most recent call last):
|
|
1207
|
+
...
|
|
1208
|
+
TypeError: No coercion from Rational Field to Finite Field of size 7
|
|
1209
|
+
"""
|
|
1210
|
+
cdef Map connecting = new_codomain._internal_coerce_map_from(self._codomain)
|
|
1211
|
+
if connecting is None:
|
|
1212
|
+
raise TypeError("No coercion from %s to %s" % (self._codomain, new_codomain))
|
|
1213
|
+
elif connecting.domain() is not self._codomain:
|
|
1214
|
+
raise RuntimeError("BUG: coerce_map_from should always return a map from its input (%s)" % new_codomain)
|
|
1215
|
+
else:
|
|
1216
|
+
return self.post_compose(connecting.__copy__())
|
|
1217
|
+
|
|
1218
|
+
def is_surjective(self):
|
|
1219
|
+
"""
|
|
1220
|
+
Tell whether the map is surjective (not implemented in the base class).
|
|
1221
|
+
|
|
1222
|
+
TESTS::
|
|
1223
|
+
|
|
1224
|
+
sage: from sage.categories.map import Map
|
|
1225
|
+
sage: f = Map(Hom(QQ, ZZ, Rings()))
|
|
1226
|
+
sage: f.is_surjective()
|
|
1227
|
+
Traceback (most recent call last):
|
|
1228
|
+
...
|
|
1229
|
+
NotImplementedError: <class 'sage.categories.map.Map'>
|
|
1230
|
+
"""
|
|
1231
|
+
raise NotImplementedError(type(self))
|
|
1232
|
+
|
|
1233
|
+
cpdef _pow_int(self, n):
|
|
1234
|
+
"""
|
|
1235
|
+
TESTS::
|
|
1236
|
+
|
|
1237
|
+
sage: R.<x> = ZZ['x']
|
|
1238
|
+
sage: phi = R.hom([x+1]); phi
|
|
1239
|
+
Ring endomorphism of Univariate Polynomial Ring in x over Integer Ring
|
|
1240
|
+
Defn: x |--> x + 1
|
|
1241
|
+
|
|
1242
|
+
sage: phi^0
|
|
1243
|
+
Identity endomorphism of Univariate Polynomial Ring in x over Integer Ring
|
|
1244
|
+
|
|
1245
|
+
sage: phi^2 == phi*phi
|
|
1246
|
+
True
|
|
1247
|
+
|
|
1248
|
+
sage: S.<y> = QQ[]
|
|
1249
|
+
sage: psi = R.hom([y^2])
|
|
1250
|
+
sage: psi^1
|
|
1251
|
+
Ring morphism:
|
|
1252
|
+
From: Univariate Polynomial Ring in x over Integer Ring
|
|
1253
|
+
To: Univariate Polynomial Ring in y over Rational Field
|
|
1254
|
+
Defn: x |--> y^2
|
|
1255
|
+
sage: psi^2
|
|
1256
|
+
Traceback (most recent call last):
|
|
1257
|
+
...
|
|
1258
|
+
TypeError: self must be an endomorphism
|
|
1259
|
+
|
|
1260
|
+
sage: # needs sage.rings.number_field
|
|
1261
|
+
sage: K.<a> = NumberField(x^4 - 5*x + 5)
|
|
1262
|
+
sage: C5.<z> = CyclotomicField(5)
|
|
1263
|
+
sage: tau = K.hom([z - z^2]); tau
|
|
1264
|
+
Ring morphism:
|
|
1265
|
+
From: Number Field in a with defining polynomial x^4 - 5*x + 5
|
|
1266
|
+
To: Cyclotomic Field of order 5 and degree 4
|
|
1267
|
+
Defn: a |--> -z^2 + z
|
|
1268
|
+
sage: tau^-1
|
|
1269
|
+
Ring morphism:
|
|
1270
|
+
From: Cyclotomic Field of order 5 and degree 4
|
|
1271
|
+
To: Number Field in a with defining polynomial x^4 - 5*x + 5
|
|
1272
|
+
Defn: z |--> 3/11*a^3 + 4/11*a^2 + 9/11*a - 14/11
|
|
1273
|
+
"""
|
|
1274
|
+
if self.domain() is not self._codomain and n != 1 and n != -1:
|
|
1275
|
+
raise TypeError("self must be an endomorphism")
|
|
1276
|
+
if n == 0:
|
|
1277
|
+
from sage.categories.morphism import IdentityMorphism
|
|
1278
|
+
return IdentityMorphism(self._parent)
|
|
1279
|
+
return generic_power(self, n)
|
|
1280
|
+
|
|
1281
|
+
def section(self):
|
|
1282
|
+
"""
|
|
1283
|
+
Return a section of ``self``.
|
|
1284
|
+
|
|
1285
|
+
.. NOTE::
|
|
1286
|
+
|
|
1287
|
+
By default, it returns ``None``. You may override it in subclasses.
|
|
1288
|
+
|
|
1289
|
+
TESTS::
|
|
1290
|
+
|
|
1291
|
+
sage: R.<x,y> = QQ[]
|
|
1292
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1293
|
+
sage: print(f.section())
|
|
1294
|
+
None
|
|
1295
|
+
|
|
1296
|
+
sage: f = QQ.coerce_map_from(ZZ); f
|
|
1297
|
+
Natural morphism:
|
|
1298
|
+
From: Integer Ring
|
|
1299
|
+
To: Rational Field
|
|
1300
|
+
sage: ff = f.section(); ff
|
|
1301
|
+
Generic map:
|
|
1302
|
+
From: Rational Field
|
|
1303
|
+
To: Integer Ring
|
|
1304
|
+
sage: ff(4/2)
|
|
1305
|
+
2
|
|
1306
|
+
sage: parent(ff(4/2)) is ZZ
|
|
1307
|
+
True
|
|
1308
|
+
sage: ff(1/2)
|
|
1309
|
+
Traceback (most recent call last):
|
|
1310
|
+
...
|
|
1311
|
+
TypeError: no conversion of this rational to integer
|
|
1312
|
+
"""
|
|
1313
|
+
return None
|
|
1314
|
+
|
|
1315
|
+
def __hash__(self):
|
|
1316
|
+
"""
|
|
1317
|
+
Return the hash of this map.
|
|
1318
|
+
|
|
1319
|
+
TESTS::
|
|
1320
|
+
|
|
1321
|
+
sage: f = sage.rings.morphism.RingMap(ZZ.Hom(ZZ))
|
|
1322
|
+
sage: type(f)
|
|
1323
|
+
<class 'sage.rings.morphism.RingMap'>
|
|
1324
|
+
sage: hash(f) == hash(f)
|
|
1325
|
+
True
|
|
1326
|
+
sage: {f: 1}[f]
|
|
1327
|
+
1
|
|
1328
|
+
"""
|
|
1329
|
+
D = self.domain()
|
|
1330
|
+
if D is None:
|
|
1331
|
+
raise ValueError("This map became defunct by garbage collection")
|
|
1332
|
+
return hash((self.domain(), self._codomain))
|
|
1333
|
+
|
|
1334
|
+
cdef class Section(Map):
|
|
1335
|
+
"""
|
|
1336
|
+
A formal section of a map.
|
|
1337
|
+
|
|
1338
|
+
.. NOTE::
|
|
1339
|
+
|
|
1340
|
+
Call methods are not implemented for the base class ``Section``.
|
|
1341
|
+
|
|
1342
|
+
EXAMPLES::
|
|
1343
|
+
|
|
1344
|
+
sage: from sage.categories.map import Section
|
|
1345
|
+
sage: R.<x,y> = ZZ[]
|
|
1346
|
+
sage: S.<a,b> = QQ[]
|
|
1347
|
+
sage: f = R.hom([a+b, a-b])
|
|
1348
|
+
sage: sf = Section(f); sf
|
|
1349
|
+
Section map:
|
|
1350
|
+
From: Multivariate Polynomial Ring in a, b over Rational Field
|
|
1351
|
+
To: Multivariate Polynomial Ring in x, y over Integer Ring
|
|
1352
|
+
sage: sf(a)
|
|
1353
|
+
Traceback (most recent call last):
|
|
1354
|
+
...
|
|
1355
|
+
NotImplementedError: <class 'sage.categories.map.Section'>
|
|
1356
|
+
"""
|
|
1357
|
+
|
|
1358
|
+
def __init__(self, map):
|
|
1359
|
+
"""
|
|
1360
|
+
INPUT:
|
|
1361
|
+
|
|
1362
|
+
- ``map`` -- a map
|
|
1363
|
+
|
|
1364
|
+
TESTS::
|
|
1365
|
+
|
|
1366
|
+
sage: from sage.categories.map import Section
|
|
1367
|
+
sage: R.<x,y> = QQ[]
|
|
1368
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1369
|
+
sage: sf = Section(f); sf
|
|
1370
|
+
Section map:
|
|
1371
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1372
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1373
|
+
"""
|
|
1374
|
+
from sage.categories.homset import Hom
|
|
1375
|
+
from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
|
|
1376
|
+
Map.__init__(self, Hom(map.codomain(), map.domain(), SetsWithPartialMaps()))
|
|
1377
|
+
self._inverse = map # TODO: Use this attribute somewhere!
|
|
1378
|
+
|
|
1379
|
+
cdef dict _extra_slots(self):
|
|
1380
|
+
"""
|
|
1381
|
+
Helper for pickling and copying.
|
|
1382
|
+
|
|
1383
|
+
TESTS::
|
|
1384
|
+
|
|
1385
|
+
sage: from sage.categories.map import Section
|
|
1386
|
+
sage: R.<x,y> = QQ[]
|
|
1387
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1388
|
+
sage: sf = Section(f)
|
|
1389
|
+
sage: copy(sf) # indirect doctest
|
|
1390
|
+
Section map:
|
|
1391
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1392
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1393
|
+
"""
|
|
1394
|
+
slots = Map._extra_slots(self)
|
|
1395
|
+
slots['_inverse'] = self._inverse
|
|
1396
|
+
return slots
|
|
1397
|
+
|
|
1398
|
+
cdef _update_slots(self, dict _slots):
|
|
1399
|
+
"""
|
|
1400
|
+
Helper for pickling and copying.
|
|
1401
|
+
|
|
1402
|
+
TESTS::
|
|
1403
|
+
|
|
1404
|
+
sage: from sage.categories.map import Section
|
|
1405
|
+
sage: R.<x,y> = QQ[]
|
|
1406
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1407
|
+
sage: sf = Section(f)
|
|
1408
|
+
sage: copy(sf) # indirect doctest
|
|
1409
|
+
Section map:
|
|
1410
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1411
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1412
|
+
"""
|
|
1413
|
+
Map._update_slots(self, _slots)
|
|
1414
|
+
self._inverse = _slots['_inverse']
|
|
1415
|
+
|
|
1416
|
+
def _repr_type(self):
|
|
1417
|
+
"""
|
|
1418
|
+
Return a string describing the type of this map (which is "Section").
|
|
1419
|
+
|
|
1420
|
+
TESTS::
|
|
1421
|
+
|
|
1422
|
+
sage: from sage.categories.map import Section
|
|
1423
|
+
sage: R.<x,y> = QQ[]
|
|
1424
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1425
|
+
sage: sf = Section(f)
|
|
1426
|
+
sage: sf # indirect doctest
|
|
1427
|
+
Section map:
|
|
1428
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1429
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1430
|
+
"""
|
|
1431
|
+
return "Section"
|
|
1432
|
+
|
|
1433
|
+
def inverse(self):
|
|
1434
|
+
"""
|
|
1435
|
+
Return inverse of ``self``.
|
|
1436
|
+
|
|
1437
|
+
TESTS::
|
|
1438
|
+
|
|
1439
|
+
sage: from sage.categories.map import Section
|
|
1440
|
+
sage: R.<x,y> = QQ[]
|
|
1441
|
+
sage: f = R.hom([x+y, x-y], R)
|
|
1442
|
+
sage: sf = Section(f)
|
|
1443
|
+
sage: sf.inverse()
|
|
1444
|
+
Ring endomorphism of Multivariate Polynomial Ring in x, y over Rational Field
|
|
1445
|
+
Defn: x |--> x + y
|
|
1446
|
+
y |--> x - y
|
|
1447
|
+
"""
|
|
1448
|
+
return self._inverse
|
|
1449
|
+
|
|
1450
|
+
|
|
1451
|
+
cdef class FormalCompositeMap(Map):
|
|
1452
|
+
"""
|
|
1453
|
+
Formal composite maps.
|
|
1454
|
+
|
|
1455
|
+
A formal composite map is formed by two maps, so that the codomain of the
|
|
1456
|
+
first map is contained in the domain of the second map.
|
|
1457
|
+
|
|
1458
|
+
.. NOTE::
|
|
1459
|
+
|
|
1460
|
+
When calling a composite with additional arguments, these arguments are
|
|
1461
|
+
*only* passed to the second underlying map.
|
|
1462
|
+
|
|
1463
|
+
EXAMPLES::
|
|
1464
|
+
|
|
1465
|
+
sage: R.<x> = QQ[]
|
|
1466
|
+
sage: S.<a> = QQ[]
|
|
1467
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1468
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1469
|
+
sage: g = S.hom([2*x])
|
|
1470
|
+
sage: f*g
|
|
1471
|
+
Composite map:
|
|
1472
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1473
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1474
|
+
Defn: Ring morphism:
|
|
1475
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1476
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1477
|
+
Defn: a |--> 2*x
|
|
1478
|
+
then
|
|
1479
|
+
Generic morphism:
|
|
1480
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
1481
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1482
|
+
sage: g*f
|
|
1483
|
+
Composite map:
|
|
1484
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
1485
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1486
|
+
Defn: Generic morphism:
|
|
1487
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
1488
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1489
|
+
then
|
|
1490
|
+
Ring morphism:
|
|
1491
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1492
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1493
|
+
Defn: a |--> 2*x
|
|
1494
|
+
sage: (f*g)(2*a^2+5)
|
|
1495
|
+
5*a^2
|
|
1496
|
+
sage: (g*f)(2*x^2+5)
|
|
1497
|
+
20*x^2
|
|
1498
|
+
"""
|
|
1499
|
+
|
|
1500
|
+
def __init__(self, parent, first, second=None):
|
|
1501
|
+
"""
|
|
1502
|
+
INPUT:
|
|
1503
|
+
|
|
1504
|
+
- ``parent`` -- a homset
|
|
1505
|
+
- ``first`` -- a map or a list of maps
|
|
1506
|
+
- ``second`` -- a map or None
|
|
1507
|
+
|
|
1508
|
+
.. NOTE::
|
|
1509
|
+
|
|
1510
|
+
The intended use is of course that the codomain of the
|
|
1511
|
+
first map is contained in the domain of the second map,
|
|
1512
|
+
so that the two maps can be composed, and that the
|
|
1513
|
+
composition belongs to ``parent``. However, none of
|
|
1514
|
+
these conditions is verified in the init method.
|
|
1515
|
+
|
|
1516
|
+
The user is advised to compose two maps ``f`` and ``g``
|
|
1517
|
+
in multiplicative notation, ``g*f``, since this will in
|
|
1518
|
+
some cases return a more efficient map object than a
|
|
1519
|
+
formal composite map.
|
|
1520
|
+
|
|
1521
|
+
TESTS::
|
|
1522
|
+
|
|
1523
|
+
sage: R.<x,y> = QQ[]
|
|
1524
|
+
sage: S.<a,b> = QQ[]
|
|
1525
|
+
sage: f = R.hom([a+b, a-b])
|
|
1526
|
+
sage: g = S.hom([x+y, x-y])
|
|
1527
|
+
sage: H = Hom(R, R, Rings())
|
|
1528
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1529
|
+
sage: m = FormalCompositeMap(H, f, g); m
|
|
1530
|
+
Composite map:
|
|
1531
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1532
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1533
|
+
Defn: Ring morphism:
|
|
1534
|
+
From: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1535
|
+
To: Multivariate Polynomial Ring in a, b over Rational Field
|
|
1536
|
+
Defn: x |--> a + b
|
|
1537
|
+
y |--> a - b
|
|
1538
|
+
then
|
|
1539
|
+
Ring morphism:
|
|
1540
|
+
From: Multivariate Polynomial Ring in a, b over Rational Field
|
|
1541
|
+
To: Multivariate Polynomial Ring in x, y over Rational Field
|
|
1542
|
+
Defn: a |--> x + y
|
|
1543
|
+
b |--> x - y
|
|
1544
|
+
sage: m(x), m(y)
|
|
1545
|
+
(2*x, 2*y)
|
|
1546
|
+
"""
|
|
1547
|
+
Map.__init__(self, parent)
|
|
1548
|
+
|
|
1549
|
+
if isinstance(first, (list, tuple)):
|
|
1550
|
+
self.__list = first
|
|
1551
|
+
self._coerce_cost = sum((<Map>f)._coerce_cost for f in first)
|
|
1552
|
+
return
|
|
1553
|
+
|
|
1554
|
+
self.__list = []
|
|
1555
|
+
if isinstance(first, FormalCompositeMap):
|
|
1556
|
+
self.__list += (<FormalCompositeMap>first).__list
|
|
1557
|
+
else:
|
|
1558
|
+
self.__list += [first]
|
|
1559
|
+
|
|
1560
|
+
if isinstance(second, FormalCompositeMap):
|
|
1561
|
+
self.__list += (<FormalCompositeMap>second).__list
|
|
1562
|
+
else:
|
|
1563
|
+
self.__list += [second]
|
|
1564
|
+
self._coerce_cost = (<Map>first)._coerce_cost + (<Map>second)._coerce_cost
|
|
1565
|
+
|
|
1566
|
+
def __copy__(self):
|
|
1567
|
+
"""
|
|
1568
|
+
Since :meth:`_extra_slots` would return the uncopied constituents
|
|
1569
|
+
of this composite map, we cannot rely on the default copying method
|
|
1570
|
+
of maps.
|
|
1571
|
+
|
|
1572
|
+
TESTS::
|
|
1573
|
+
|
|
1574
|
+
sage: copy(QQ['q,t'].coerce_map_from(int)) # indirect doctest
|
|
1575
|
+
Composite map:
|
|
1576
|
+
From: Set of Python objects of class 'int'
|
|
1577
|
+
To: Multivariate Polynomial Ring in q, t over Rational Field
|
|
1578
|
+
Defn: Native morphism:
|
|
1579
|
+
From: Set of Python objects of class 'int'
|
|
1580
|
+
To: Rational Field
|
|
1581
|
+
then
|
|
1582
|
+
Polynomial base injection morphism:
|
|
1583
|
+
From: Rational Field
|
|
1584
|
+
To: Multivariate Polynomial Ring in q, t over Rational Field
|
|
1585
|
+
"""
|
|
1586
|
+
return FormalCompositeMap(self.parent(), [f.__copy__() for f in self.__list])
|
|
1587
|
+
|
|
1588
|
+
cdef _update_slots(self, dict _slots):
|
|
1589
|
+
"""
|
|
1590
|
+
Used in pickling and copying.
|
|
1591
|
+
|
|
1592
|
+
TESTS::
|
|
1593
|
+
|
|
1594
|
+
sage: R.<x,y> = QQ[]
|
|
1595
|
+
sage: S.<a,b> = QQ[]
|
|
1596
|
+
sage: f = R.hom([a+b, a-b])
|
|
1597
|
+
sage: g = S.hom([x+y, x-y])
|
|
1598
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1599
|
+
sage: H = Hom(R, R, Rings())
|
|
1600
|
+
sage: m = FormalCompositeMap(H, f, g)
|
|
1601
|
+
sage: m == loads(dumps(m)) # indirect doctest
|
|
1602
|
+
True
|
|
1603
|
+
"""
|
|
1604
|
+
self.__list = _slots['__list']
|
|
1605
|
+
Map._update_slots(self, _slots)
|
|
1606
|
+
|
|
1607
|
+
cdef dict _extra_slots(self):
|
|
1608
|
+
"""
|
|
1609
|
+
Used in pickling and copying.
|
|
1610
|
+
|
|
1611
|
+
TESTS::
|
|
1612
|
+
|
|
1613
|
+
sage: R.<x,y> = QQ[]
|
|
1614
|
+
sage: S.<a,b> = QQ[]
|
|
1615
|
+
sage: f = R.hom([a+b, a-b])
|
|
1616
|
+
sage: g = S.hom([x+y, x-y])
|
|
1617
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1618
|
+
sage: H = Hom(R, R, Rings())
|
|
1619
|
+
sage: m = FormalCompositeMap(H, f, g)
|
|
1620
|
+
sage: m == loads(dumps(m)) # indirect doctest
|
|
1621
|
+
True
|
|
1622
|
+
"""
|
|
1623
|
+
slots = Map._extra_slots(self)
|
|
1624
|
+
slots['__list'] = self.__list
|
|
1625
|
+
return slots
|
|
1626
|
+
|
|
1627
|
+
def __richcmp__(self, other, int op):
|
|
1628
|
+
"""
|
|
1629
|
+
TESTS::
|
|
1630
|
+
|
|
1631
|
+
sage: R.<x,y> = QQ[]
|
|
1632
|
+
sage: S.<a,b> = QQ[]
|
|
1633
|
+
sage: f = R.hom([a+b, a-b])
|
|
1634
|
+
sage: g = S.hom([x+y, x-y])
|
|
1635
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1636
|
+
sage: H = Hom(R, R, Rings())
|
|
1637
|
+
sage: m = FormalCompositeMap(H, f, g)
|
|
1638
|
+
sage: m == loads(dumps(m))
|
|
1639
|
+
True
|
|
1640
|
+
|
|
1641
|
+
sage: m == None
|
|
1642
|
+
False
|
|
1643
|
+
sage: m == 2
|
|
1644
|
+
False
|
|
1645
|
+
"""
|
|
1646
|
+
if type(self) is not type(other):
|
|
1647
|
+
return NotImplemented
|
|
1648
|
+
left = (<FormalCompositeMap>self).__list
|
|
1649
|
+
right = (<FormalCompositeMap>other).__list
|
|
1650
|
+
return PyObject_RichCompare(left, right, op)
|
|
1651
|
+
|
|
1652
|
+
def __hash__(self):
|
|
1653
|
+
"""
|
|
1654
|
+
Return the hash of this map.
|
|
1655
|
+
|
|
1656
|
+
TESTS::
|
|
1657
|
+
|
|
1658
|
+
sage: R.<x,y> = QQ[]
|
|
1659
|
+
sage: S.<a,b> = QQ[]
|
|
1660
|
+
sage: f = R.hom([a+b, a-b])
|
|
1661
|
+
sage: g = S.hom([x+y, x-y])
|
|
1662
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1663
|
+
sage: H = Hom(R, R, Rings())
|
|
1664
|
+
sage: m = FormalCompositeMap(H, f, g)
|
|
1665
|
+
sage: hash(m) == hash(m)
|
|
1666
|
+
True
|
|
1667
|
+
sage: {m: 1}[m]
|
|
1668
|
+
1
|
|
1669
|
+
sage: n = FormalCompositeMap(Hom(S, S, Rings()), g, f)
|
|
1670
|
+
sage: hash(m) == hash(n)
|
|
1671
|
+
False
|
|
1672
|
+
sage: len({m: 1, n: 2}.keys())
|
|
1673
|
+
2
|
|
1674
|
+
"""
|
|
1675
|
+
return hash(tuple(self.__list))
|
|
1676
|
+
|
|
1677
|
+
def __getitem__(self, i):
|
|
1678
|
+
r"""
|
|
1679
|
+
Return the `i`-th map of the formal composition.
|
|
1680
|
+
|
|
1681
|
+
If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
|
|
1682
|
+
f_1 \circ f_0`, then ``self[i]`` gives `f_i`. Support
|
|
1683
|
+
negative indices as ``list.__getitem__``. Raise an error if
|
|
1684
|
+
the index does not match, in the same way as
|
|
1685
|
+
``list.__getitem__``.
|
|
1686
|
+
|
|
1687
|
+
EXAMPLES::
|
|
1688
|
+
|
|
1689
|
+
sage: from sage.categories.map import Map
|
|
1690
|
+
sage: f = Map(ZZ, QQ)
|
|
1691
|
+
sage: g = Map(QQ, ZZ)
|
|
1692
|
+
sage: (f*g)[0]
|
|
1693
|
+
Generic map:
|
|
1694
|
+
From: Rational Field
|
|
1695
|
+
To: Integer Ring
|
|
1696
|
+
sage: (f*g)[1]
|
|
1697
|
+
Generic map:
|
|
1698
|
+
From: Integer Ring
|
|
1699
|
+
To: Rational Field
|
|
1700
|
+
sage: (f*g)[-1]
|
|
1701
|
+
Generic map:
|
|
1702
|
+
From: Integer Ring
|
|
1703
|
+
To: Rational Field
|
|
1704
|
+
sage: (f*g)[-2]
|
|
1705
|
+
Generic map:
|
|
1706
|
+
From: Rational Field
|
|
1707
|
+
To: Integer Ring
|
|
1708
|
+
sage: (f*g)[-3]
|
|
1709
|
+
Traceback (most recent call last):
|
|
1710
|
+
...
|
|
1711
|
+
IndexError: list index out of range
|
|
1712
|
+
sage: (f*g)[2]
|
|
1713
|
+
Traceback (most recent call last):
|
|
1714
|
+
...
|
|
1715
|
+
IndexError: list index out of range
|
|
1716
|
+
"""
|
|
1717
|
+
return self.__list[i]
|
|
1718
|
+
|
|
1719
|
+
cpdef Element _call_(self, x):
|
|
1720
|
+
"""
|
|
1721
|
+
Call with a single argument.
|
|
1722
|
+
|
|
1723
|
+
TESTS::
|
|
1724
|
+
|
|
1725
|
+
sage: R.<x> = QQ[]
|
|
1726
|
+
sage: S.<a> = QQ[]
|
|
1727
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1728
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1729
|
+
sage: g = S.hom([2*x])
|
|
1730
|
+
sage: (g*f)((x+1)^2), (f*g)((a+1)^2) # indirect doctest
|
|
1731
|
+
(4*x^2, a^2)
|
|
1732
|
+
"""
|
|
1733
|
+
for f in self.__list:
|
|
1734
|
+
x = f._call_(x)
|
|
1735
|
+
return x
|
|
1736
|
+
|
|
1737
|
+
cpdef Element _call_with_args(self, x, args=(), kwds={}):
|
|
1738
|
+
"""
|
|
1739
|
+
Additional arguments are only passed to the last applied map.
|
|
1740
|
+
|
|
1741
|
+
TESTS::
|
|
1742
|
+
|
|
1743
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1744
|
+
sage: R.<x> = QQ[]
|
|
1745
|
+
sage: def foo(x, *args, **kwds):
|
|
1746
|
+
....: print('foo called with {} {}'.format(args, kwds))
|
|
1747
|
+
....: return x
|
|
1748
|
+
sage: def bar(x, *args, **kwds):
|
|
1749
|
+
....: print('bar called with {} {}'.format(args, kwds))
|
|
1750
|
+
....: return x
|
|
1751
|
+
sage: f = SetMorphism(Hom(R, R, Rings()), foo)
|
|
1752
|
+
sage: b = SetMorphism(Hom(R, R, Rings()), bar)
|
|
1753
|
+
sage: c = b*f
|
|
1754
|
+
sage: c(2, 'hello world', test=1) # indirect doctest
|
|
1755
|
+
foo called with () {}
|
|
1756
|
+
bar called with ('hello world',) {'test': 1}
|
|
1757
|
+
2
|
|
1758
|
+
sage: c = f*b
|
|
1759
|
+
sage: c(2, 'hello world', test=1)
|
|
1760
|
+
bar called with () {}
|
|
1761
|
+
foo called with ('hello world',) {'test': 1}
|
|
1762
|
+
2
|
|
1763
|
+
"""
|
|
1764
|
+
for f in self.__list[:-1]:
|
|
1765
|
+
x = f._call_(x)
|
|
1766
|
+
return self.__list[-1]._call_with_args(x, args, kwds)
|
|
1767
|
+
|
|
1768
|
+
def _repr_type(self):
|
|
1769
|
+
"""
|
|
1770
|
+
Return a string describing the type of ``self``, namely "Composite".
|
|
1771
|
+
|
|
1772
|
+
TESTS::
|
|
1773
|
+
|
|
1774
|
+
sage: R.<x> = QQ[]
|
|
1775
|
+
sage: S.<a> = QQ[]
|
|
1776
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1777
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1778
|
+
sage: g = S.hom([2*x])
|
|
1779
|
+
sage: f*g # indirect doctest
|
|
1780
|
+
Composite map:
|
|
1781
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1782
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1783
|
+
Defn: Ring morphism:
|
|
1784
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1785
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1786
|
+
Defn: a |--> 2*x
|
|
1787
|
+
then
|
|
1788
|
+
Generic morphism:
|
|
1789
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
1790
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1791
|
+
"""
|
|
1792
|
+
return "Composite"
|
|
1793
|
+
|
|
1794
|
+
def _repr_defn(self):
|
|
1795
|
+
"""
|
|
1796
|
+
Return a string describing the definition of ``self``.
|
|
1797
|
+
|
|
1798
|
+
The return value is obtained from the string representations
|
|
1799
|
+
of the two constituents.
|
|
1800
|
+
|
|
1801
|
+
TESTS::
|
|
1802
|
+
|
|
1803
|
+
sage: R.<x> = QQ[]
|
|
1804
|
+
sage: S.<a> = QQ[]
|
|
1805
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1806
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1807
|
+
sage: g = S.hom([2*x])
|
|
1808
|
+
sage: f*g # indirect doctest
|
|
1809
|
+
Composite map:
|
|
1810
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1811
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1812
|
+
Defn: Ring morphism:
|
|
1813
|
+
From: Univariate Polynomial Ring in a over Rational Field
|
|
1814
|
+
To: Univariate Polynomial Ring in x over Rational Field
|
|
1815
|
+
Defn: a |--> 2*x
|
|
1816
|
+
then
|
|
1817
|
+
Generic morphism:
|
|
1818
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
1819
|
+
To: Univariate Polynomial Ring in a over Rational Field
|
|
1820
|
+
"""
|
|
1821
|
+
s = " %s" % (self.__list[0])
|
|
1822
|
+
for f in self.__list[1:]:
|
|
1823
|
+
s += "\nthen\n %s" % f
|
|
1824
|
+
return s
|
|
1825
|
+
|
|
1826
|
+
def first(self):
|
|
1827
|
+
r"""
|
|
1828
|
+
Return the first map in the formal composition.
|
|
1829
|
+
|
|
1830
|
+
If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
|
|
1831
|
+
f_1 \circ f_0`, then ``self.first()`` returns `f_0`. We have
|
|
1832
|
+
``self == self.then() * self.first()``.
|
|
1833
|
+
|
|
1834
|
+
EXAMPLES::
|
|
1835
|
+
|
|
1836
|
+
sage: R.<x> = QQ[]
|
|
1837
|
+
sage: S.<a> = QQ[]
|
|
1838
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1839
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1840
|
+
sage: g = S.hom([2*x])
|
|
1841
|
+
sage: fg = f * g
|
|
1842
|
+
sage: fg.first() == g
|
|
1843
|
+
True
|
|
1844
|
+
sage: fg == fg.then() * fg.first()
|
|
1845
|
+
True
|
|
1846
|
+
"""
|
|
1847
|
+
return self.__list[0]
|
|
1848
|
+
|
|
1849
|
+
def then(self):
|
|
1850
|
+
r"""
|
|
1851
|
+
Return the tail of the list of maps.
|
|
1852
|
+
|
|
1853
|
+
If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
|
|
1854
|
+
f_1 \circ f_0`, then ``self.first()`` returns `f_n \circ
|
|
1855
|
+
f_{n-1} \circ \cdots \circ f_1`. We have ``self ==
|
|
1856
|
+
self.then() * self.first()``.
|
|
1857
|
+
|
|
1858
|
+
EXAMPLES::
|
|
1859
|
+
|
|
1860
|
+
sage: R.<x> = QQ[]
|
|
1861
|
+
sage: S.<a> = QQ[]
|
|
1862
|
+
sage: from sage.categories.morphism import SetMorphism
|
|
1863
|
+
sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
|
|
1864
|
+
sage: g = S.hom([2*x])
|
|
1865
|
+
sage: (f*g).then() == f
|
|
1866
|
+
True
|
|
1867
|
+
|
|
1868
|
+
sage: f = QQ.coerce_map_from(ZZ)
|
|
1869
|
+
sage: f = f.extend_domain(ZZ).extend_codomain(QQ)
|
|
1870
|
+
sage: f.then()
|
|
1871
|
+
Composite map:
|
|
1872
|
+
From: Integer Ring
|
|
1873
|
+
To: Rational Field
|
|
1874
|
+
Defn: Natural morphism:
|
|
1875
|
+
From: Integer Ring
|
|
1876
|
+
To: Rational Field
|
|
1877
|
+
then
|
|
1878
|
+
Identity endomorphism of Rational Field
|
|
1879
|
+
"""
|
|
1880
|
+
if len(self.__list) == 2:
|
|
1881
|
+
return self.__list[1]
|
|
1882
|
+
domain = self.__list[0].codomain()
|
|
1883
|
+
codomain = self.codomain()
|
|
1884
|
+
H = homset.Hom(domain, codomain, category=self._category_for)
|
|
1885
|
+
return FormalCompositeMap(H, self.__list[1:])
|
|
1886
|
+
|
|
1887
|
+
def is_injective(self):
|
|
1888
|
+
"""
|
|
1889
|
+
Tell whether ``self`` is injective.
|
|
1890
|
+
|
|
1891
|
+
It raises :exc:`NotImplementedError` if it cannot be determined.
|
|
1892
|
+
|
|
1893
|
+
EXAMPLES::
|
|
1894
|
+
|
|
1895
|
+
sage: # needs sage.modules
|
|
1896
|
+
sage: V1 = QQ^2
|
|
1897
|
+
sage: V2 = QQ^3
|
|
1898
|
+
sage: phi1 = (QQ^1).hom(Matrix([[1, 1]]), V1)
|
|
1899
|
+
sage: phi2 = V1.hom(Matrix([[1, 2, 3], [4, 5, 6]]), V2)
|
|
1900
|
+
|
|
1901
|
+
If both constituents are injective, the composition is injective::
|
|
1902
|
+
|
|
1903
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1904
|
+
sage: c1 = FormalCompositeMap(Hom(QQ^1, V2, phi1.category_for()), # needs sage.modules
|
|
1905
|
+
....: phi1, phi2)
|
|
1906
|
+
sage: c1.is_injective() # needs sage.modules
|
|
1907
|
+
True
|
|
1908
|
+
|
|
1909
|
+
If it cannot be determined whether the composition is injective,
|
|
1910
|
+
an error is raised::
|
|
1911
|
+
|
|
1912
|
+
sage: psi1 = V2.hom(Matrix([[1, 2], [3, 4], [5, 6]]), V1) # needs sage.modules
|
|
1913
|
+
sage: c2 = FormalCompositeMap(Hom(V1, V1, phi2.category_for()), # needs sage.modules
|
|
1914
|
+
....: phi2, psi1)
|
|
1915
|
+
sage: c2.is_injective() # needs sage.modules
|
|
1916
|
+
Traceback (most recent call last):
|
|
1917
|
+
...
|
|
1918
|
+
NotImplementedError: not enough information to deduce injectivity
|
|
1919
|
+
|
|
1920
|
+
If the first map is surjective and the second map is not injective,
|
|
1921
|
+
then the composition is not injective::
|
|
1922
|
+
|
|
1923
|
+
sage: psi2 = V1.hom([[1], [1]], QQ^1) # needs sage.modules
|
|
1924
|
+
sage: c3 = FormalCompositeMap(Hom(V2, QQ^1, phi2.category_for()), # needs sage.modules
|
|
1925
|
+
....: psi2, psi1)
|
|
1926
|
+
sage: c3.is_injective() # needs sage.modules
|
|
1927
|
+
False
|
|
1928
|
+
|
|
1929
|
+
TESTS:
|
|
1930
|
+
|
|
1931
|
+
Check that :issue:`23205` has been resolved::
|
|
1932
|
+
|
|
1933
|
+
sage: f = QQ.hom(QQbar) * ZZ.hom(QQ) # needs sage.rings.number_field
|
|
1934
|
+
sage: f.is_injective() # needs sage.rings.number_field
|
|
1935
|
+
True
|
|
1936
|
+
"""
|
|
1937
|
+
try:
|
|
1938
|
+
# we try the category first
|
|
1939
|
+
# as of 2017-06, the MRO of this class does not get patched to
|
|
1940
|
+
# include the category's MorphismMethods (because it is a Cython
|
|
1941
|
+
# class); therefore, we cannot simply call "super" but need to
|
|
1942
|
+
# invoke the category method explicitly
|
|
1943
|
+
return self.getattr_from_category('is_injective')()
|
|
1944
|
+
except (AttributeError, NotImplementedError):
|
|
1945
|
+
pass
|
|
1946
|
+
|
|
1947
|
+
injectives = []
|
|
1948
|
+
for f in self.__list:
|
|
1949
|
+
if f.is_injective():
|
|
1950
|
+
injectives.append(f)
|
|
1951
|
+
else:
|
|
1952
|
+
break
|
|
1953
|
+
else:
|
|
1954
|
+
return True
|
|
1955
|
+
|
|
1956
|
+
if all(f.is_surjective() for f in injectives):
|
|
1957
|
+
return False
|
|
1958
|
+
|
|
1959
|
+
raise NotImplementedError("not enough information to deduce injectivity")
|
|
1960
|
+
|
|
1961
|
+
def is_surjective(self):
|
|
1962
|
+
"""
|
|
1963
|
+
Tell whether ``self`` is surjective.
|
|
1964
|
+
|
|
1965
|
+
It raises :exc:`NotImplementedError` if it cannot be determined.
|
|
1966
|
+
|
|
1967
|
+
EXAMPLES::
|
|
1968
|
+
|
|
1969
|
+
sage: from sage.categories.map import FormalCompositeMap
|
|
1970
|
+
sage: V3 = QQ^3 # needs sage.modules
|
|
1971
|
+
sage: V2 = QQ^2 # needs sage.modules
|
|
1972
|
+
sage: V1 = QQ^1 # needs sage.modules
|
|
1973
|
+
|
|
1974
|
+
If both maps are surjective, the composition is surjective::
|
|
1975
|
+
|
|
1976
|
+
sage: # needs sage.modules
|
|
1977
|
+
sage: phi32 = V3.hom(Matrix([[1, 2], [3, 4], [5, 6]]), V2)
|
|
1978
|
+
sage: phi21 = V2.hom(Matrix([[1], [1]]), V1)
|
|
1979
|
+
sage: c_phi = FormalCompositeMap(Hom(V3, V1, phi32.category_for()),
|
|
1980
|
+
....: phi32, phi21)
|
|
1981
|
+
sage: c_phi.is_surjective()
|
|
1982
|
+
True
|
|
1983
|
+
|
|
1984
|
+
If the second map is not surjective, the composition is not
|
|
1985
|
+
surjective::
|
|
1986
|
+
|
|
1987
|
+
sage: FormalCompositeMap(Hom(V3, V1, phi32.category_for()), # needs sage.modules
|
|
1988
|
+
....: phi32,
|
|
1989
|
+
....: V2.hom(Matrix([[0], [0]]), V1)).is_surjective()
|
|
1990
|
+
False
|
|
1991
|
+
|
|
1992
|
+
If the second map is an isomorphism and the first map is not
|
|
1993
|
+
surjective, then the composition is not surjective::
|
|
1994
|
+
|
|
1995
|
+
sage: FormalCompositeMap(Hom(V2, V1, phi32.category_for()), # needs sage.modules
|
|
1996
|
+
....: V2.hom(Matrix([[0], [0]]), V1),
|
|
1997
|
+
....: V1.hom(Matrix([[1]]), V1)).is_surjective()
|
|
1998
|
+
False
|
|
1999
|
+
|
|
2000
|
+
Otherwise, surjectivity of the composition cannot be determined::
|
|
2001
|
+
|
|
2002
|
+
sage: FormalCompositeMap(Hom(V2, V1, phi32.category_for()), # needs sage.modules
|
|
2003
|
+
....: V2.hom(Matrix([[1, 1], [1, 1]]), V2),
|
|
2004
|
+
....: V2.hom(Matrix([[1], [1]]), V1)).is_surjective()
|
|
2005
|
+
Traceback (most recent call last):
|
|
2006
|
+
...
|
|
2007
|
+
NotImplementedError: not enough information to deduce surjectivity
|
|
2008
|
+
"""
|
|
2009
|
+
try:
|
|
2010
|
+
# we try the category first
|
|
2011
|
+
# as of 2017-06, the MRO of this class does not get patched to
|
|
2012
|
+
# include the category's MorphismMethods (because it is a Cython
|
|
2013
|
+
# class); therefore, we cannot simply call "super" but need to
|
|
2014
|
+
# invoke the category method explicitly
|
|
2015
|
+
return self.getattr_from_category('is_surjective')()
|
|
2016
|
+
except (AttributeError, NotImplementedError):
|
|
2017
|
+
pass
|
|
2018
|
+
|
|
2019
|
+
surjectives = []
|
|
2020
|
+
for f in self.__list[::-1]:
|
|
2021
|
+
if f.is_surjective():
|
|
2022
|
+
surjectives.append(f)
|
|
2023
|
+
else:
|
|
2024
|
+
break
|
|
2025
|
+
else:
|
|
2026
|
+
return True
|
|
2027
|
+
|
|
2028
|
+
if all(f.is_injective() for f in surjectives):
|
|
2029
|
+
return False
|
|
2030
|
+
|
|
2031
|
+
raise NotImplementedError("not enough information to deduce surjectivity")
|
|
2032
|
+
|
|
2033
|
+
def domains(self):
|
|
2034
|
+
"""
|
|
2035
|
+
Iterate over the domains of the factors of this map.
|
|
2036
|
+
|
|
2037
|
+
(This is useful in particular to check for loops in coercion maps.)
|
|
2038
|
+
|
|
2039
|
+
.. SEEALSO:: :meth:`Map.domains`
|
|
2040
|
+
|
|
2041
|
+
EXAMPLES::
|
|
2042
|
+
|
|
2043
|
+
sage: f = QQ.coerce_map_from(ZZ)
|
|
2044
|
+
sage: g = MatrixSpace(QQ, 2, 2).coerce_map_from(QQ) # needs sage.modules
|
|
2045
|
+
sage: list((g * f).domains()) # needs sage.modules
|
|
2046
|
+
[Integer Ring, Rational Field]
|
|
2047
|
+
"""
|
|
2048
|
+
for f in self.__list:
|
|
2049
|
+
yield f.domain()
|
|
2050
|
+
|
|
2051
|
+
def section(self):
|
|
2052
|
+
"""
|
|
2053
|
+
Compute a section map from sections of the factors of
|
|
2054
|
+
``self`` if they have been implemented.
|
|
2055
|
+
|
|
2056
|
+
EXAMPLES::
|
|
2057
|
+
|
|
2058
|
+
sage: P.<x> = QQ[]
|
|
2059
|
+
sage: incl = P.coerce_map_from(ZZ)
|
|
2060
|
+
sage: sect = incl.section(); sect
|
|
2061
|
+
Composite map:
|
|
2062
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
2063
|
+
To: Integer Ring
|
|
2064
|
+
Defn: Generic map:
|
|
2065
|
+
From: Univariate Polynomial Ring in x over Rational Field
|
|
2066
|
+
To: Rational Field
|
|
2067
|
+
then
|
|
2068
|
+
Generic map:
|
|
2069
|
+
From: Rational Field
|
|
2070
|
+
To: Integer Ring
|
|
2071
|
+
sage: p = x + 5; q = x + 2
|
|
2072
|
+
sage: sect(p-q)
|
|
2073
|
+
3
|
|
2074
|
+
|
|
2075
|
+
the following example has been attached to :meth:`_integer_`
|
|
2076
|
+
of :class:`sage.rings.polynomial.polynomial_element.Polynomial`
|
|
2077
|
+
before (see comment there)::
|
|
2078
|
+
|
|
2079
|
+
sage: k = GF(47)
|
|
2080
|
+
sage: R.<x> = PolynomialRing(k)
|
|
2081
|
+
sage: R.coerce_map_from(ZZ).section()
|
|
2082
|
+
Composite map:
|
|
2083
|
+
From: Univariate Polynomial Ring in x over Finite Field of size 47
|
|
2084
|
+
To: Integer Ring
|
|
2085
|
+
Defn: Generic map:
|
|
2086
|
+
From: Univariate Polynomial Ring in x over Finite Field of size 47
|
|
2087
|
+
To: Finite Field of size 47
|
|
2088
|
+
then
|
|
2089
|
+
Lifting map:
|
|
2090
|
+
From: Finite Field of size 47
|
|
2091
|
+
To: Integer Ring
|
|
2092
|
+
sage: ZZ(R(45)) # indirect doctest
|
|
2093
|
+
45
|
|
2094
|
+
sage: ZZ(3*x + 45) # indirect doctest
|
|
2095
|
+
Traceback (most recent call last):
|
|
2096
|
+
...
|
|
2097
|
+
TypeError: 3*x + 45 is not a constant polynomial
|
|
2098
|
+
"""
|
|
2099
|
+
sections = []
|
|
2100
|
+
for m in reversed(list(self)):
|
|
2101
|
+
try:
|
|
2102
|
+
sec = m.section()
|
|
2103
|
+
except TypeError:
|
|
2104
|
+
return None
|
|
2105
|
+
if sec is None:
|
|
2106
|
+
return None
|
|
2107
|
+
sections.append(sec)
|
|
2108
|
+
|
|
2109
|
+
from sage.categories.homset import Hom
|
|
2110
|
+
from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
|
|
2111
|
+
H = Hom(self.codomain(), self.domain(), category=SetsWithPartialMaps())
|
|
2112
|
+
return FormalCompositeMap(H, sections)
|