passagemath-objects 10.6.47__cp311-cp311-macosx_13_0_x86_64.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.
Files changed (280) hide show
  1. passagemath_objects/.dylibs/libgmp.10.dylib +0 -0
  2. passagemath_objects/__init__.py +3 -0
  3. passagemath_objects-10.6.47.dist-info/METADATA +115 -0
  4. passagemath_objects-10.6.47.dist-info/RECORD +280 -0
  5. passagemath_objects-10.6.47.dist-info/WHEEL +6 -0
  6. passagemath_objects-10.6.47.dist-info/top_level.txt +3 -0
  7. sage/all__sagemath_objects.py +37 -0
  8. sage/arith/all__sagemath_objects.py +5 -0
  9. sage/arith/long.pxd +411 -0
  10. sage/arith/numerical_approx.cpython-311-darwin.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-311-darwin.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-311-darwin.so +0 -0
  17. sage/categories/action.pxd +29 -0
  18. sage/categories/action.pyx +641 -0
  19. sage/categories/algebra_functor.py +745 -0
  20. sage/categories/all__sagemath_objects.py +33 -0
  21. sage/categories/basic.py +62 -0
  22. sage/categories/cartesian_product.py +295 -0
  23. sage/categories/category.py +3401 -0
  24. sage/categories/category_cy_helper.cpython-311-darwin.so +0 -0
  25. sage/categories/category_cy_helper.pxd +8 -0
  26. sage/categories/category_cy_helper.pyx +322 -0
  27. sage/categories/category_singleton.cpython-311-darwin.so +0 -0
  28. sage/categories/category_singleton.pxd +3 -0
  29. sage/categories/category_singleton.pyx +342 -0
  30. sage/categories/category_types.py +637 -0
  31. sage/categories/category_with_axiom.py +2876 -0
  32. sage/categories/covariant_functorial_construction.py +703 -0
  33. sage/categories/facade_sets.py +228 -0
  34. sage/categories/functor.cpython-311-darwin.so +0 -0
  35. sage/categories/functor.pxd +7 -0
  36. sage/categories/functor.pyx +691 -0
  37. sage/categories/homset.py +1338 -0
  38. sage/categories/homsets.py +364 -0
  39. sage/categories/isomorphic_objects.py +73 -0
  40. sage/categories/map.cpython-311-darwin.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-311-darwin.so +0 -0
  44. sage/categories/morphism.pxd +14 -0
  45. sage/categories/morphism.pyx +895 -0
  46. sage/categories/objects.py +167 -0
  47. sage/categories/primer.py +1696 -0
  48. sage/categories/pushout.py +4834 -0
  49. sage/categories/quotients.py +64 -0
  50. sage/categories/realizations.py +200 -0
  51. sage/categories/sets_cat.py +3290 -0
  52. sage/categories/sets_with_partial_maps.py +52 -0
  53. sage/categories/subobjects.py +64 -0
  54. sage/categories/subquotients.py +21 -0
  55. sage/categories/with_realizations.py +311 -0
  56. sage/cpython/__init__.py +19 -0
  57. sage/cpython/_py2_random.py +619 -0
  58. sage/cpython/all.py +3 -0
  59. sage/cpython/atexit.cpython-311-darwin.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-311-darwin.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-311-darwin.so +0 -0
  64. sage/cpython/cython_metaclass.h +117 -0
  65. sage/cpython/cython_metaclass.pxd +3 -0
  66. sage/cpython/cython_metaclass.pyx +130 -0
  67. sage/cpython/debug.cpython-311-darwin.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-311-darwin.so +0 -0
  70. sage/cpython/dict_del_by_value.pxd +9 -0
  71. sage/cpython/dict_del_by_value.pyx +191 -0
  72. sage/cpython/dict_internal.h +245 -0
  73. sage/cpython/getattr.cpython-311-darwin.so +0 -0
  74. sage/cpython/getattr.pxd +9 -0
  75. sage/cpython/getattr.pyx +439 -0
  76. sage/cpython/pycore_long.h +97 -0
  77. sage/cpython/pycore_long.pxd +10 -0
  78. sage/cpython/python_debug.h +44 -0
  79. sage/cpython/python_debug.pxd +47 -0
  80. sage/cpython/pyx_visit.h +13 -0
  81. sage/cpython/string.cpython-311-darwin.so +0 -0
  82. sage/cpython/string.pxd +76 -0
  83. sage/cpython/string.pyx +34 -0
  84. sage/cpython/string_impl.h +60 -0
  85. sage/cpython/type.cpython-311-darwin.so +0 -0
  86. sage/cpython/type.pxd +2 -0
  87. sage/cpython/type.pyx +40 -0
  88. sage/cpython/wrapperdescr.pxd +67 -0
  89. sage/ext/all__sagemath_objects.py +3 -0
  90. sage/ext/ccobject.h +64 -0
  91. sage/ext/cplusplus.pxd +17 -0
  92. sage/ext/mod_int.h +30 -0
  93. sage/ext/mod_int.pxd +24 -0
  94. sage/ext/stdsage.pxd +39 -0
  95. sage/groups/all__sagemath_objects.py +1 -0
  96. sage/groups/group.cpython-311-darwin.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-311-darwin.so +0 -0
  100. sage/groups/old.pxd +14 -0
  101. sage/groups/old.pyx +219 -0
  102. sage/libs/all__sagemath_objects.py +3 -0
  103. sage/libs/gmp/__init__.py +1 -0
  104. sage/libs/gmp/all.pxd +6 -0
  105. sage/libs/gmp/binop.pxd +23 -0
  106. sage/libs/gmp/misc.pxd +8 -0
  107. sage/libs/gmp/mpf.pxd +88 -0
  108. sage/libs/gmp/mpn.pxd +57 -0
  109. sage/libs/gmp/mpq.pxd +57 -0
  110. sage/libs/gmp/mpz.pxd +202 -0
  111. sage/libs/gmp/pylong.cpython-311-darwin.so +0 -0
  112. sage/libs/gmp/pylong.pxd +12 -0
  113. sage/libs/gmp/pylong.pyx +150 -0
  114. sage/libs/gmp/random.pxd +25 -0
  115. sage/libs/gmp/randomize.pxd +59 -0
  116. sage/libs/gmp/types.pxd +53 -0
  117. sage/libs/gmpxx.pxd +19 -0
  118. sage/misc/abstract_method.py +276 -0
  119. sage/misc/all__sagemath_objects.py +43 -0
  120. sage/misc/bindable_class.py +253 -0
  121. sage/misc/c3_controlled.cpython-311-darwin.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-311-darwin.so +0 -0
  125. sage/misc/cachefunc.pxd +43 -0
  126. sage/misc/cachefunc.pyx +3781 -0
  127. sage/misc/call.py +188 -0
  128. sage/misc/classcall_metaclass.cpython-311-darwin.so +0 -0
  129. sage/misc/classcall_metaclass.pxd +14 -0
  130. sage/misc/classcall_metaclass.pyx +599 -0
  131. sage/misc/constant_function.cpython-311-darwin.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-311-darwin.so +0 -0
  135. sage/misc/fast_methods.pxd +20 -0
  136. sage/misc/fast_methods.pyx +351 -0
  137. sage/misc/flatten.py +90 -0
  138. sage/misc/fpickle.cpython-311-darwin.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-311-darwin.so +0 -0
  141. sage/misc/function_mangling.pxd +11 -0
  142. sage/misc/function_mangling.pyx +308 -0
  143. sage/misc/inherit_comparison.cpython-311-darwin.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-311-darwin.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-311-darwin.so +0 -0
  149. sage/misc/lazy_attribute.pyx +607 -0
  150. sage/misc/lazy_format.py +135 -0
  151. sage/misc/lazy_import.cpython-311-darwin.so +0 -0
  152. sage/misc/lazy_import.pyx +1299 -0
  153. sage/misc/lazy_import_cache.py +36 -0
  154. sage/misc/lazy_list.cpython-311-darwin.so +0 -0
  155. sage/misc/lazy_list.pxd +19 -0
  156. sage/misc/lazy_list.pyx +1187 -0
  157. sage/misc/lazy_string.cpython-311-darwin.so +0 -0
  158. sage/misc/lazy_string.pxd +7 -0
  159. sage/misc/lazy_string.pyx +546 -0
  160. sage/misc/misc.py +1066 -0
  161. sage/misc/misc_c.cpython-311-darwin.so +0 -0
  162. sage/misc/misc_c.pxd +3 -0
  163. sage/misc/misc_c.pyx +766 -0
  164. sage/misc/namespace_package.py +37 -0
  165. sage/misc/nested_class.cpython-311-darwin.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-311-darwin.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-311-darwin.so +0 -0
  172. sage/misc/randstate.pxd +30 -0
  173. sage/misc/randstate.pyx +1059 -0
  174. sage/misc/repr.py +203 -0
  175. sage/misc/reset.cpython-311-darwin.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-311-darwin.so +0 -0
  178. sage/misc/sage_ostools.pyx +323 -0
  179. sage/misc/sage_timeit.py +275 -0
  180. sage/misc/sage_timeit_class.cpython-311-darwin.so +0 -0
  181. sage/misc/sage_timeit_class.pyx +120 -0
  182. sage/misc/sage_unittest.py +637 -0
  183. sage/misc/sageinspect.py +2768 -0
  184. sage/misc/session.cpython-311-darwin.so +0 -0
  185. sage/misc/session.pyx +392 -0
  186. sage/misc/superseded.py +557 -0
  187. sage/misc/test_nested_class.py +228 -0
  188. sage/misc/timing.py +264 -0
  189. sage/misc/unknown.py +222 -0
  190. sage/misc/verbose.py +253 -0
  191. sage/misc/weak_dict.cpython-311-darwin.so +0 -0
  192. sage/misc/weak_dict.pxd +15 -0
  193. sage/misc/weak_dict.pyx +1231 -0
  194. sage/modules/all__sagemath_objects.py +1 -0
  195. sage/modules/module.cpython-311-darwin.so +0 -0
  196. sage/modules/module.pxd +5 -0
  197. sage/modules/module.pyx +329 -0
  198. sage/rings/all__sagemath_objects.py +3 -0
  199. sage/rings/integer_fake.h +22 -0
  200. sage/rings/integer_fake.pxd +55 -0
  201. sage/sets/all__sagemath_objects.py +3 -0
  202. sage/sets/pythonclass.cpython-311-darwin.so +0 -0
  203. sage/sets/pythonclass.pxd +9 -0
  204. sage/sets/pythonclass.pyx +247 -0
  205. sage/structure/__init__.py +4 -0
  206. sage/structure/all.py +30 -0
  207. sage/structure/category_object.cpython-311-darwin.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-311-darwin.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-311-darwin.so +0 -0
  214. sage/structure/coerce_actions.pxd +27 -0
  215. sage/structure/coerce_actions.pyx +988 -0
  216. sage/structure/coerce_dict.cpython-311-darwin.so +0 -0
  217. sage/structure/coerce_dict.pxd +51 -0
  218. sage/structure/coerce_dict.pyx +1557 -0
  219. sage/structure/coerce_exceptions.py +23 -0
  220. sage/structure/coerce_maps.cpython-311-darwin.so +0 -0
  221. sage/structure/coerce_maps.pxd +28 -0
  222. sage/structure/coerce_maps.pyx +718 -0
  223. sage/structure/debug_options.cpython-311-darwin.so +0 -0
  224. sage/structure/debug_options.pxd +6 -0
  225. sage/structure/debug_options.pyx +54 -0
  226. sage/structure/dynamic_class.py +541 -0
  227. sage/structure/element.cpython-311-darwin.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-311-darwin.so +0 -0
  231. sage/structure/element_wrapper.pxd +12 -0
  232. sage/structure/element_wrapper.pyx +582 -0
  233. sage/structure/factorization.py +1422 -0
  234. sage/structure/factorization_integer.py +105 -0
  235. sage/structure/factory.cpython-311-darwin.so +0 -0
  236. sage/structure/factory.pyx +786 -0
  237. sage/structure/formal_sum.py +489 -0
  238. sage/structure/gens_py.py +73 -0
  239. sage/structure/global_options.py +1743 -0
  240. sage/structure/indexed_generators.py +863 -0
  241. sage/structure/list_clone.cpython-311-darwin.so +0 -0
  242. sage/structure/list_clone.pxd +65 -0
  243. sage/structure/list_clone.pyx +1867 -0
  244. sage/structure/list_clone_demo.cpython-311-darwin.so +0 -0
  245. sage/structure/list_clone_demo.pyx +248 -0
  246. sage/structure/list_clone_timings.py +179 -0
  247. sage/structure/list_clone_timings_cy.cpython-311-darwin.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-311-darwin.so +0 -0
  250. sage/structure/mutability.pxd +21 -0
  251. sage/structure/mutability.pyx +348 -0
  252. sage/structure/nonexact.py +69 -0
  253. sage/structure/parent.cpython-311-darwin.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-311-darwin.so +0 -0
  257. sage/structure/parent_base.pxd +13 -0
  258. sage/structure/parent_base.pyx +44 -0
  259. sage/structure/parent_gens.cpython-311-darwin.so +0 -0
  260. sage/structure/parent_gens.pxd +22 -0
  261. sage/structure/parent_gens.pyx +377 -0
  262. sage/structure/parent_old.cpython-311-darwin.so +0 -0
  263. sage/structure/parent_old.pxd +25 -0
  264. sage/structure/parent_old.pyx +294 -0
  265. sage/structure/proof/__init__.py +1 -0
  266. sage/structure/proof/all.py +243 -0
  267. sage/structure/proof/proof.py +300 -0
  268. sage/structure/richcmp.cpython-311-darwin.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-311-darwin.so +0 -0
  272. sage/structure/sage_object.pxd +3 -0
  273. sage/structure/sage_object.pyx +988 -0
  274. sage/structure/sage_object_test.py +19 -0
  275. sage/structure/sequence.py +937 -0
  276. sage/structure/set_factories.py +1178 -0
  277. sage/structure/set_factories_example.py +527 -0
  278. sage/structure/support_view.py +179 -0
  279. sage/structure/test_factory.py +56 -0
  280. sage/structure/unique_representation.py +1359 -0
@@ -0,0 +1,2106 @@
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
+ cpdef _pow_int(self, n):
1219
+ """
1220
+ TESTS::
1221
+
1222
+ sage: R.<x> = ZZ['x']
1223
+ sage: phi = R.hom([x+1]); phi
1224
+ Ring endomorphism of Univariate Polynomial Ring in x over Integer Ring
1225
+ Defn: x |--> x + 1
1226
+
1227
+ sage: phi^0
1228
+ Identity endomorphism of Univariate Polynomial Ring in x over Integer Ring
1229
+
1230
+ sage: phi^2 == phi*phi
1231
+ True
1232
+
1233
+ sage: S.<y> = QQ[]
1234
+ sage: psi = R.hom([y^2])
1235
+ sage: psi^1
1236
+ Ring morphism:
1237
+ From: Univariate Polynomial Ring in x over Integer Ring
1238
+ To: Univariate Polynomial Ring in y over Rational Field
1239
+ Defn: x |--> y^2
1240
+ sage: psi^2
1241
+ Traceback (most recent call last):
1242
+ ...
1243
+ TypeError: self must be an endomorphism
1244
+
1245
+ sage: # needs sage.rings.number_field
1246
+ sage: K.<a> = NumberField(x^4 - 5*x + 5)
1247
+ sage: C5.<z> = CyclotomicField(5)
1248
+ sage: tau = K.hom([z - z^2]); tau
1249
+ Ring morphism:
1250
+ From: Number Field in a with defining polynomial x^4 - 5*x + 5
1251
+ To: Cyclotomic Field of order 5 and degree 4
1252
+ Defn: a |--> -z^2 + z
1253
+ sage: tau^-1
1254
+ Ring morphism:
1255
+ From: Cyclotomic Field of order 5 and degree 4
1256
+ To: Number Field in a with defining polynomial x^4 - 5*x + 5
1257
+ Defn: z |--> 3/11*a^3 + 4/11*a^2 + 9/11*a - 14/11
1258
+ """
1259
+ if self.domain() is not self._codomain and n != 1 and n != -1:
1260
+ raise TypeError("self must be an endomorphism")
1261
+ if n == 0:
1262
+ from sage.categories.morphism import IdentityMorphism
1263
+ return IdentityMorphism(self._parent)
1264
+ return generic_power(self, n)
1265
+
1266
+ def section(self):
1267
+ """
1268
+ Return a section of ``self``.
1269
+
1270
+ .. NOTE::
1271
+
1272
+ By default, it returns ``None``. You may override it in subclasses.
1273
+
1274
+ TESTS::
1275
+
1276
+ sage: R.<x,y> = QQ[]
1277
+ sage: f = R.hom([x+y, x-y], R)
1278
+ sage: print(f.section())
1279
+ None
1280
+
1281
+ sage: f = QQ.coerce_map_from(ZZ); f
1282
+ Natural morphism:
1283
+ From: Integer Ring
1284
+ To: Rational Field
1285
+ sage: ff = f.section(); ff
1286
+ Generic map:
1287
+ From: Rational Field
1288
+ To: Integer Ring
1289
+ sage: ff(4/2)
1290
+ 2
1291
+ sage: parent(ff(4/2)) is ZZ
1292
+ True
1293
+ sage: ff(1/2)
1294
+ Traceback (most recent call last):
1295
+ ...
1296
+ TypeError: no conversion of this rational to integer
1297
+ """
1298
+ return None
1299
+
1300
+ def __hash__(self):
1301
+ """
1302
+ Return the hash of this map.
1303
+
1304
+ TESTS::
1305
+
1306
+ sage: f = sage.rings.morphism.RingMap(ZZ.Hom(ZZ))
1307
+ sage: type(f)
1308
+ <class 'sage.rings.morphism.RingMap'>
1309
+ sage: hash(f) == hash(f)
1310
+ True
1311
+ sage: {f: 1}[f]
1312
+ 1
1313
+ """
1314
+ D = self.domain()
1315
+ if D is None:
1316
+ raise ValueError("This map became defunct by garbage collection")
1317
+ return hash((self.domain(), self._codomain))
1318
+
1319
+ cdef class Section(Map):
1320
+ """
1321
+ A formal section of a map.
1322
+
1323
+ .. NOTE::
1324
+
1325
+ Call methods are not implemented for the base class ``Section``.
1326
+
1327
+ EXAMPLES::
1328
+
1329
+ sage: from sage.categories.map import Section
1330
+ sage: R.<x,y> = ZZ[]
1331
+ sage: S.<a,b> = QQ[]
1332
+ sage: f = R.hom([a+b, a-b])
1333
+ sage: sf = Section(f); sf
1334
+ Section map:
1335
+ From: Multivariate Polynomial Ring in a, b over Rational Field
1336
+ To: Multivariate Polynomial Ring in x, y over Integer Ring
1337
+ sage: sf(a)
1338
+ Traceback (most recent call last):
1339
+ ...
1340
+ NotImplementedError: <class 'sage.categories.map.Section'>
1341
+ """
1342
+
1343
+ def __init__(self, map):
1344
+ """
1345
+ INPUT:
1346
+
1347
+ - ``map`` -- a map
1348
+
1349
+ TESTS::
1350
+
1351
+ sage: from sage.categories.map import Section
1352
+ sage: R.<x,y> = QQ[]
1353
+ sage: f = R.hom([x+y, x-y], R)
1354
+ sage: sf = Section(f); sf
1355
+ Section map:
1356
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1357
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1358
+ """
1359
+ from sage.categories.homset import Hom
1360
+ from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
1361
+ Map.__init__(self, Hom(map.codomain(), map.domain(), SetsWithPartialMaps()))
1362
+ self._inverse = map # TODO: Use this attribute somewhere!
1363
+
1364
+ cdef dict _extra_slots(self):
1365
+ """
1366
+ Helper for pickling and copying.
1367
+
1368
+ TESTS::
1369
+
1370
+ sage: from sage.categories.map import Section
1371
+ sage: R.<x,y> = QQ[]
1372
+ sage: f = R.hom([x+y, x-y], R)
1373
+ sage: sf = Section(f)
1374
+ sage: copy(sf) # indirect doctest
1375
+ Section map:
1376
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1377
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1378
+ """
1379
+ slots = Map._extra_slots(self)
1380
+ slots['_inverse'] = self._inverse
1381
+ return slots
1382
+
1383
+ cdef _update_slots(self, dict _slots):
1384
+ """
1385
+ Helper for pickling and copying.
1386
+
1387
+ TESTS::
1388
+
1389
+ sage: from sage.categories.map import Section
1390
+ sage: R.<x,y> = QQ[]
1391
+ sage: f = R.hom([x+y, x-y], R)
1392
+ sage: sf = Section(f)
1393
+ sage: copy(sf) # indirect doctest
1394
+ Section map:
1395
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1396
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1397
+ """
1398
+ Map._update_slots(self, _slots)
1399
+ self._inverse = _slots['_inverse']
1400
+
1401
+ def _repr_type(self):
1402
+ """
1403
+ Return a string describing the type of this map (which is "Section").
1404
+
1405
+ TESTS::
1406
+
1407
+ sage: from sage.categories.map import Section
1408
+ sage: R.<x,y> = QQ[]
1409
+ sage: f = R.hom([x+y, x-y], R)
1410
+ sage: sf = Section(f)
1411
+ sage: sf # indirect doctest
1412
+ Section map:
1413
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1414
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1415
+ """
1416
+ return "Section"
1417
+
1418
+ def inverse(self):
1419
+ """
1420
+ Return inverse of ``self``.
1421
+
1422
+ TESTS::
1423
+
1424
+ sage: from sage.categories.map import Section
1425
+ sage: R.<x,y> = QQ[]
1426
+ sage: f = R.hom([x+y, x-y], R)
1427
+ sage: sf = Section(f)
1428
+ sage: sf.inverse()
1429
+ Ring endomorphism of Multivariate Polynomial Ring in x, y over Rational Field
1430
+ Defn: x |--> x + y
1431
+ y |--> x - y
1432
+ """
1433
+ return self._inverse
1434
+
1435
+
1436
+ cdef class FormalCompositeMap(Map):
1437
+ """
1438
+ Formal composite maps.
1439
+
1440
+ A formal composite map is formed by two maps, so that the codomain of the
1441
+ first map is contained in the domain of the second map.
1442
+
1443
+ .. NOTE::
1444
+
1445
+ When calling a composite with additional arguments, these arguments are
1446
+ *only* passed to the second underlying map.
1447
+
1448
+ EXAMPLES::
1449
+
1450
+ sage: R.<x> = QQ[]
1451
+ sage: S.<a> = QQ[]
1452
+ sage: from sage.categories.morphism import SetMorphism
1453
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1454
+ sage: g = S.hom([2*x])
1455
+ sage: f*g
1456
+ Composite map:
1457
+ From: Univariate Polynomial Ring in a over Rational Field
1458
+ To: Univariate Polynomial Ring in a over Rational Field
1459
+ Defn: Ring morphism:
1460
+ From: Univariate Polynomial Ring in a over Rational Field
1461
+ To: Univariate Polynomial Ring in x over Rational Field
1462
+ Defn: a |--> 2*x
1463
+ then
1464
+ Generic morphism:
1465
+ From: Univariate Polynomial Ring in x over Rational Field
1466
+ To: Univariate Polynomial Ring in a over Rational Field
1467
+ sage: g*f
1468
+ Composite map:
1469
+ From: Univariate Polynomial Ring in x over Rational Field
1470
+ To: Univariate Polynomial Ring in x over Rational Field
1471
+ Defn: Generic morphism:
1472
+ From: Univariate Polynomial Ring in x over Rational Field
1473
+ To: Univariate Polynomial Ring in a over Rational Field
1474
+ then
1475
+ Ring morphism:
1476
+ From: Univariate Polynomial Ring in a over Rational Field
1477
+ To: Univariate Polynomial Ring in x over Rational Field
1478
+ Defn: a |--> 2*x
1479
+ sage: (f*g)(2*a^2+5)
1480
+ 5*a^2
1481
+ sage: (g*f)(2*x^2+5)
1482
+ 20*x^2
1483
+ """
1484
+
1485
+ def __init__(self, parent, first, second=None):
1486
+ """
1487
+ INPUT:
1488
+
1489
+ - ``parent`` -- a homset
1490
+ - ``first`` -- a map or a list of maps
1491
+ - ``second`` -- a map or None
1492
+
1493
+ .. NOTE::
1494
+
1495
+ The intended use is of course that the codomain of the
1496
+ first map is contained in the domain of the second map,
1497
+ so that the two maps can be composed, and that the
1498
+ composition belongs to ``parent``. However, none of
1499
+ these conditions is verified in the init method.
1500
+
1501
+ The user is advised to compose two maps ``f`` and ``g``
1502
+ in multiplicative notation, ``g*f``, since this will in
1503
+ some cases return a more efficient map object than a
1504
+ formal composite map.
1505
+
1506
+ TESTS::
1507
+
1508
+ sage: R.<x,y> = QQ[]
1509
+ sage: S.<a,b> = QQ[]
1510
+ sage: f = R.hom([a+b, a-b])
1511
+ sage: g = S.hom([x+y, x-y])
1512
+ sage: H = Hom(R, R, Rings())
1513
+ sage: from sage.categories.map import FormalCompositeMap
1514
+ sage: m = FormalCompositeMap(H, f, g); m
1515
+ Composite map:
1516
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1517
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1518
+ Defn: Ring morphism:
1519
+ From: Multivariate Polynomial Ring in x, y over Rational Field
1520
+ To: Multivariate Polynomial Ring in a, b over Rational Field
1521
+ Defn: x |--> a + b
1522
+ y |--> a - b
1523
+ then
1524
+ Ring morphism:
1525
+ From: Multivariate Polynomial Ring in a, b over Rational Field
1526
+ To: Multivariate Polynomial Ring in x, y over Rational Field
1527
+ Defn: a |--> x + y
1528
+ b |--> x - y
1529
+ sage: m(x), m(y)
1530
+ (2*x, 2*y)
1531
+ """
1532
+ Map.__init__(self, parent)
1533
+
1534
+ if isinstance(first, (list, tuple)):
1535
+ self.__list = first
1536
+ self._coerce_cost = sum((<Map>f)._coerce_cost for f in first)
1537
+ return
1538
+
1539
+ self.__list = []
1540
+ if isinstance(first, FormalCompositeMap):
1541
+ self.__list += (<FormalCompositeMap>first).__list
1542
+ else:
1543
+ self.__list += [first]
1544
+
1545
+ if isinstance(second, FormalCompositeMap):
1546
+ self.__list += (<FormalCompositeMap>second).__list
1547
+ else:
1548
+ self.__list += [second]
1549
+ self._coerce_cost = (<Map>first)._coerce_cost + (<Map>second)._coerce_cost
1550
+
1551
+ def __copy__(self):
1552
+ """
1553
+ Since :meth:`_extra_slots` would return the uncopied constituents
1554
+ of this composite map, we cannot rely on the default copying method
1555
+ of maps.
1556
+
1557
+ TESTS::
1558
+
1559
+ sage: copy(QQ['q,t'].coerce_map_from(int)) # indirect doctest
1560
+ Composite map:
1561
+ From: Set of Python objects of class 'int'
1562
+ To: Multivariate Polynomial Ring in q, t over Rational Field
1563
+ Defn: Native morphism:
1564
+ From: Set of Python objects of class 'int'
1565
+ To: Rational Field
1566
+ then
1567
+ Polynomial base injection morphism:
1568
+ From: Rational Field
1569
+ To: Multivariate Polynomial Ring in q, t over Rational Field
1570
+ """
1571
+ return FormalCompositeMap(self.parent(), [f.__copy__() for f in self.__list])
1572
+
1573
+ cdef _update_slots(self, dict _slots):
1574
+ """
1575
+ Used in pickling and copying.
1576
+
1577
+ TESTS::
1578
+
1579
+ sage: R.<x,y> = QQ[]
1580
+ sage: S.<a,b> = QQ[]
1581
+ sage: f = R.hom([a+b, a-b])
1582
+ sage: g = S.hom([x+y, x-y])
1583
+ sage: from sage.categories.map import FormalCompositeMap
1584
+ sage: H = Hom(R, R, Rings())
1585
+ sage: m = FormalCompositeMap(H, f, g)
1586
+ sage: m == loads(dumps(m)) # indirect doctest
1587
+ True
1588
+ """
1589
+ self.__list = _slots['__list']
1590
+ Map._update_slots(self, _slots)
1591
+
1592
+ cdef dict _extra_slots(self):
1593
+ """
1594
+ Used in pickling and copying.
1595
+
1596
+ TESTS::
1597
+
1598
+ sage: R.<x,y> = QQ[]
1599
+ sage: S.<a,b> = QQ[]
1600
+ sage: f = R.hom([a+b, a-b])
1601
+ sage: g = S.hom([x+y, x-y])
1602
+ sage: from sage.categories.map import FormalCompositeMap
1603
+ sage: H = Hom(R, R, Rings())
1604
+ sage: m = FormalCompositeMap(H, f, g)
1605
+ sage: m == loads(dumps(m)) # indirect doctest
1606
+ True
1607
+ """
1608
+ slots = Map._extra_slots(self)
1609
+ slots['__list'] = self.__list
1610
+ return slots
1611
+
1612
+ def __richcmp__(self, other, int op):
1613
+ """
1614
+ TESTS::
1615
+
1616
+ sage: R.<x,y> = QQ[]
1617
+ sage: S.<a,b> = QQ[]
1618
+ sage: f = R.hom([a+b, a-b])
1619
+ sage: g = S.hom([x+y, x-y])
1620
+ sage: from sage.categories.map import FormalCompositeMap
1621
+ sage: H = Hom(R, R, Rings())
1622
+ sage: m = FormalCompositeMap(H, f, g)
1623
+ sage: m == loads(dumps(m))
1624
+ True
1625
+
1626
+ sage: m == None
1627
+ False
1628
+ sage: m == 2
1629
+ False
1630
+ """
1631
+ if type(self) is not type(other):
1632
+ return NotImplemented
1633
+ left = (<FormalCompositeMap>self).__list
1634
+ right = (<FormalCompositeMap>other).__list
1635
+ return PyObject_RichCompare(left, right, op)
1636
+
1637
+ def __hash__(self):
1638
+ """
1639
+ Return the hash of this map.
1640
+
1641
+ TESTS::
1642
+
1643
+ sage: R.<x,y> = QQ[]
1644
+ sage: S.<a,b> = QQ[]
1645
+ sage: f = R.hom([a+b, a-b])
1646
+ sage: g = S.hom([x+y, x-y])
1647
+ sage: from sage.categories.map import FormalCompositeMap
1648
+ sage: H = Hom(R, R, Rings())
1649
+ sage: m = FormalCompositeMap(H, f, g)
1650
+ sage: hash(m) == hash(m)
1651
+ True
1652
+ sage: {m: 1}[m]
1653
+ 1
1654
+ sage: n = FormalCompositeMap(Hom(S, S, Rings()), g, f)
1655
+ sage: hash(m) == hash(n)
1656
+ False
1657
+ sage: len({m: 1, n: 2}.keys())
1658
+ 2
1659
+ """
1660
+ return hash(tuple(self.__list))
1661
+
1662
+ def __getitem__(self, i):
1663
+ r"""
1664
+ Return the `i`-th map of the formal composition.
1665
+
1666
+ If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
1667
+ f_1 \circ f_0`, then ``self[i]`` gives `f_i`. Support
1668
+ negative indices as ``list.__getitem__``. Raise an error if
1669
+ the index does not match, in the same way as
1670
+ ``list.__getitem__``.
1671
+
1672
+ EXAMPLES::
1673
+
1674
+ sage: from sage.categories.map import Map
1675
+ sage: f = Map(ZZ, QQ)
1676
+ sage: g = Map(QQ, ZZ)
1677
+ sage: (f*g)[0]
1678
+ Generic map:
1679
+ From: Rational Field
1680
+ To: Integer Ring
1681
+ sage: (f*g)[1]
1682
+ Generic map:
1683
+ From: Integer Ring
1684
+ To: Rational Field
1685
+ sage: (f*g)[-1]
1686
+ Generic map:
1687
+ From: Integer Ring
1688
+ To: Rational Field
1689
+ sage: (f*g)[-2]
1690
+ Generic map:
1691
+ From: Rational Field
1692
+ To: Integer Ring
1693
+ sage: (f*g)[-3]
1694
+ Traceback (most recent call last):
1695
+ ...
1696
+ IndexError: list index out of range
1697
+ sage: (f*g)[2]
1698
+ Traceback (most recent call last):
1699
+ ...
1700
+ IndexError: list index out of range
1701
+ """
1702
+ return self.__list[i]
1703
+
1704
+ cpdef Element _call_(self, x):
1705
+ """
1706
+ Call with a single argument.
1707
+
1708
+ TESTS::
1709
+
1710
+ sage: R.<x> = QQ[]
1711
+ sage: S.<a> = QQ[]
1712
+ sage: from sage.categories.morphism import SetMorphism
1713
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1714
+ sage: g = S.hom([2*x])
1715
+ sage: (g*f)((x+1)^2), (f*g)((a+1)^2) # indirect doctest
1716
+ (4*x^2, a^2)
1717
+ """
1718
+ for f in self.__list:
1719
+ x = f._call_(x)
1720
+ return x
1721
+
1722
+ cpdef Element _call_with_args(self, x, args=(), kwds={}):
1723
+ """
1724
+ Additional arguments are only passed to the last applied map.
1725
+
1726
+ TESTS::
1727
+
1728
+ sage: from sage.categories.morphism import SetMorphism
1729
+ sage: R.<x> = QQ[]
1730
+ sage: def foo(x, *args, **kwds):
1731
+ ....: print('foo called with {} {}'.format(args, kwds))
1732
+ ....: return x
1733
+ sage: def bar(x, *args, **kwds):
1734
+ ....: print('bar called with {} {}'.format(args, kwds))
1735
+ ....: return x
1736
+ sage: f = SetMorphism(Hom(R, R, Rings()), foo)
1737
+ sage: b = SetMorphism(Hom(R, R, Rings()), bar)
1738
+ sage: c = b*f
1739
+ sage: c(2, 'hello world', test=1) # indirect doctest
1740
+ foo called with () {}
1741
+ bar called with ('hello world',) {'test': 1}
1742
+ 2
1743
+ sage: c = f*b
1744
+ sage: c(2, 'hello world', test=1)
1745
+ bar called with () {}
1746
+ foo called with ('hello world',) {'test': 1}
1747
+ 2
1748
+ """
1749
+ for f in self.__list[:-1]:
1750
+ x = f._call_(x)
1751
+ return self.__list[-1]._call_with_args(x, args, kwds)
1752
+
1753
+ def _repr_type(self):
1754
+ """
1755
+ Return a string describing the type of ``self``, namely "Composite".
1756
+
1757
+ TESTS::
1758
+
1759
+ sage: R.<x> = QQ[]
1760
+ sage: S.<a> = QQ[]
1761
+ sage: from sage.categories.morphism import SetMorphism
1762
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1763
+ sage: g = S.hom([2*x])
1764
+ sage: f*g # indirect doctest
1765
+ Composite map:
1766
+ From: Univariate Polynomial Ring in a over Rational Field
1767
+ To: Univariate Polynomial Ring in a over Rational Field
1768
+ Defn: Ring morphism:
1769
+ From: Univariate Polynomial Ring in a over Rational Field
1770
+ To: Univariate Polynomial Ring in x over Rational Field
1771
+ Defn: a |--> 2*x
1772
+ then
1773
+ Generic morphism:
1774
+ From: Univariate Polynomial Ring in x over Rational Field
1775
+ To: Univariate Polynomial Ring in a over Rational Field
1776
+ """
1777
+ return "Composite"
1778
+
1779
+ def _repr_defn(self):
1780
+ """
1781
+ Return a string describing the definition of ``self``.
1782
+
1783
+ The return value is obtained from the string representations
1784
+ of the two constituents.
1785
+
1786
+ TESTS::
1787
+
1788
+ sage: R.<x> = QQ[]
1789
+ sage: S.<a> = QQ[]
1790
+ sage: from sage.categories.morphism import SetMorphism
1791
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1792
+ sage: g = S.hom([2*x])
1793
+ sage: f*g # indirect doctest
1794
+ Composite map:
1795
+ From: Univariate Polynomial Ring in a over Rational Field
1796
+ To: Univariate Polynomial Ring in a over Rational Field
1797
+ Defn: Ring morphism:
1798
+ From: Univariate Polynomial Ring in a over Rational Field
1799
+ To: Univariate Polynomial Ring in x over Rational Field
1800
+ Defn: a |--> 2*x
1801
+ then
1802
+ Generic morphism:
1803
+ From: Univariate Polynomial Ring in x over Rational Field
1804
+ To: Univariate Polynomial Ring in a over Rational Field
1805
+ """
1806
+ s = " %s" % (self.__list[0])
1807
+ for f in self.__list[1:]:
1808
+ s += "\nthen\n %s" % f
1809
+ return s
1810
+
1811
+ def first(self):
1812
+ r"""
1813
+ Return the first map in the formal composition.
1814
+
1815
+ If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
1816
+ f_1 \circ f_0`, then ``self.first()`` returns `f_0`. We have
1817
+ ``self == self.then() * self.first()``.
1818
+
1819
+ EXAMPLES::
1820
+
1821
+ sage: R.<x> = QQ[]
1822
+ sage: S.<a> = QQ[]
1823
+ sage: from sage.categories.morphism import SetMorphism
1824
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1825
+ sage: g = S.hom([2*x])
1826
+ sage: fg = f * g
1827
+ sage: fg.first() == g
1828
+ True
1829
+ sage: fg == fg.then() * fg.first()
1830
+ True
1831
+ """
1832
+ return self.__list[0]
1833
+
1834
+ def then(self):
1835
+ r"""
1836
+ Return the tail of the list of maps.
1837
+
1838
+ If ``self`` represents `f_n \circ f_{n-1} \circ \cdots \circ
1839
+ f_1 \circ f_0`, then ``self.first()`` returns `f_n \circ
1840
+ f_{n-1} \circ \cdots \circ f_1`. We have ``self ==
1841
+ self.then() * self.first()``.
1842
+
1843
+ EXAMPLES::
1844
+
1845
+ sage: R.<x> = QQ[]
1846
+ sage: S.<a> = QQ[]
1847
+ sage: from sage.categories.morphism import SetMorphism
1848
+ sage: f = SetMorphism(Hom(R, S, Rings()), lambda p: p[0]*a^p.degree())
1849
+ sage: g = S.hom([2*x])
1850
+ sage: (f*g).then() == f
1851
+ True
1852
+
1853
+ sage: f = QQ.coerce_map_from(ZZ)
1854
+ sage: f = f.extend_domain(ZZ).extend_codomain(QQ)
1855
+ sage: f.then()
1856
+ Composite map:
1857
+ From: Integer Ring
1858
+ To: Rational Field
1859
+ Defn: Natural morphism:
1860
+ From: Integer Ring
1861
+ To: Rational Field
1862
+ then
1863
+ Identity endomorphism of Rational Field
1864
+ """
1865
+ if len(self.__list) == 2:
1866
+ return self.__list[1]
1867
+ domain = self.__list[0].codomain()
1868
+ codomain = self.codomain()
1869
+ H = homset.Hom(domain, codomain, category=self._category_for)
1870
+ return FormalCompositeMap(H, self.__list[1:])
1871
+
1872
+ def is_injective(self):
1873
+ """
1874
+ Tell whether ``self`` is injective.
1875
+
1876
+ It raises :exc:`NotImplementedError` if it cannot be determined.
1877
+
1878
+ EXAMPLES::
1879
+
1880
+ sage: # needs sage.modules
1881
+ sage: V1 = QQ^2
1882
+ sage: V2 = QQ^3
1883
+ sage: phi1 = (QQ^1).hom(Matrix([[1, 1]]), V1)
1884
+ sage: phi2 = V1.hom(Matrix([[1, 2, 3], [4, 5, 6]]), V2)
1885
+
1886
+ If both constituents are injective, the composition is injective::
1887
+
1888
+ sage: from sage.categories.map import FormalCompositeMap
1889
+ sage: c1 = FormalCompositeMap(Hom(QQ^1, V2, phi1.category_for()), # needs sage.modules
1890
+ ....: phi1, phi2)
1891
+ sage: c1.is_injective() # needs sage.modules
1892
+ True
1893
+
1894
+ If it cannot be determined whether the composition is injective,
1895
+ an error is raised::
1896
+
1897
+ sage: # needs sage.modules
1898
+ sage: psi1 = V2.hom(Matrix([[1, 2], [3, 4], [5, 6]]), V1)
1899
+ sage: c2_Sets = FormalCompositeMap(Hom(V1, V1, Sets()),
1900
+ ....: phi2, psi1)
1901
+ sage: c2_Sets.is_injective()
1902
+ Traceback (most recent call last):
1903
+ ...
1904
+ NotImplementedError: not enough information to deduce injectivity
1905
+
1906
+ Note that we invoke the injectivity test from the category first,
1907
+ which can determine injectivity in this case::
1908
+
1909
+ sage: c2 = FormalCompositeMap(Hom(V1, V1, phi2.category_for()), # needs sage.modules
1910
+ ....: phi2, psi1)
1911
+ sage: c2.is_injective() # needs sage.modules
1912
+ True
1913
+
1914
+ If the first map is surjective and the second map is not injective,
1915
+ then the composition is not injective::
1916
+
1917
+ sage: psi2 = V1.hom([[1], [1]], QQ^1) # needs sage.modules
1918
+ sage: c3 = FormalCompositeMap(Hom(V2, QQ^1, Sets()), # needs sage.modules
1919
+ ....: psi2, psi1)
1920
+ sage: c3.is_injective() # needs sage.modules
1921
+ False
1922
+
1923
+ TESTS:
1924
+
1925
+ Check that :issue:`23205` has been resolved::
1926
+
1927
+ sage: f = QQ.hom(QQbar) * ZZ.hom(QQ) # needs sage.rings.number_field
1928
+ sage: f.is_injective() # needs sage.rings.number_field
1929
+ True
1930
+ """
1931
+ try:
1932
+ # we try the category first
1933
+ # as of 2017-06, the MRO of this class does not get patched to
1934
+ # include the category's MorphismMethods (because it is a Cython
1935
+ # class); therefore, we cannot simply call "super" but need to
1936
+ # invoke the category method explicitly
1937
+ return self.getattr_from_category('is_injective')()
1938
+ except (AttributeError, NotImplementedError):
1939
+ pass
1940
+
1941
+ injectives = []
1942
+ for f in self.__list:
1943
+ if f.is_injective():
1944
+ injectives.append(f)
1945
+ else:
1946
+ break
1947
+ else:
1948
+ return True
1949
+
1950
+ if all(f.is_surjective() for f in injectives):
1951
+ return False
1952
+
1953
+ raise NotImplementedError("not enough information to deduce injectivity")
1954
+
1955
+ def is_surjective(self):
1956
+ """
1957
+ Tell whether ``self`` is surjective.
1958
+
1959
+ It raises :exc:`NotImplementedError` if it cannot be determined.
1960
+
1961
+ EXAMPLES::
1962
+
1963
+ sage: from sage.categories.map import FormalCompositeMap
1964
+ sage: V3 = QQ^3 # needs sage.modules
1965
+ sage: V2 = QQ^2 # needs sage.modules
1966
+ sage: V1 = QQ^1 # needs sage.modules
1967
+
1968
+ If both maps are surjective, the composition is surjective::
1969
+
1970
+ sage: # needs sage.modules
1971
+ sage: phi32 = V3.hom(Matrix([[1, 2], [3, 4], [5, 6]]), V2)
1972
+ sage: phi21 = V2.hom(Matrix([[1], [1]]), V1)
1973
+ sage: c_phi = FormalCompositeMap(Hom(V3, V1, phi32.category_for()),
1974
+ ....: phi32, phi21)
1975
+ sage: c_phi.is_surjective()
1976
+ True
1977
+
1978
+ If the second map is not surjective, the composition is not
1979
+ surjective::
1980
+
1981
+ sage: FormalCompositeMap(Hom(V3, V1, phi32.category_for()), # needs sage.modules
1982
+ ....: phi32,
1983
+ ....: V2.hom(Matrix([[0], [0]]), V1)).is_surjective()
1984
+ False
1985
+
1986
+ If the second map is an isomorphism and the first map is not
1987
+ surjective, then the composition is not surjective::
1988
+
1989
+ sage: FormalCompositeMap(Hom(V2, V1, phi32.category_for()), # needs sage.modules
1990
+ ....: V2.hom(Matrix([[0], [0]]), V1),
1991
+ ....: V1.hom(Matrix([[1]]), V1)).is_surjective()
1992
+ False
1993
+
1994
+ Otherwise, surjectivity of the composition cannot be determined::
1995
+
1996
+ sage: FormalCompositeMap(Hom(V2, V1, phi32.category_for()), # needs sage.modules
1997
+ ....: V2.hom(Matrix([[1, 1], [1, 1]]), V2),
1998
+ ....: V2.hom(Matrix([[1], [1]]), V1)).is_surjective()
1999
+ Traceback (most recent call last):
2000
+ ...
2001
+ NotImplementedError: not enough information to deduce surjectivity
2002
+ """
2003
+ try:
2004
+ # we try the category first
2005
+ # as of 2017-06, the MRO of this class does not get patched to
2006
+ # include the category's MorphismMethods (because it is a Cython
2007
+ # class); therefore, we cannot simply call "super" but need to
2008
+ # invoke the category method explicitly
2009
+ return self.getattr_from_category('is_surjective')()
2010
+ except (AttributeError, NotImplementedError):
2011
+ pass
2012
+
2013
+ surjectives = []
2014
+ for f in self.__list[::-1]:
2015
+ if f.is_surjective():
2016
+ surjectives.append(f)
2017
+ else:
2018
+ break
2019
+ else:
2020
+ return True
2021
+
2022
+ if all(f.is_injective() for f in surjectives):
2023
+ return False
2024
+
2025
+ raise NotImplementedError("not enough information to deduce surjectivity")
2026
+
2027
+ def domains(self):
2028
+ """
2029
+ Iterate over the domains of the factors of this map.
2030
+
2031
+ (This is useful in particular to check for loops in coercion maps.)
2032
+
2033
+ .. SEEALSO:: :meth:`Map.domains`
2034
+
2035
+ EXAMPLES::
2036
+
2037
+ sage: f = QQ.coerce_map_from(ZZ)
2038
+ sage: g = MatrixSpace(QQ, 2, 2).coerce_map_from(QQ) # needs sage.modules
2039
+ sage: list((g * f).domains()) # needs sage.modules
2040
+ [Integer Ring, Rational Field]
2041
+ """
2042
+ for f in self.__list:
2043
+ yield f.domain()
2044
+
2045
+ def section(self):
2046
+ """
2047
+ Compute a section map from sections of the factors of
2048
+ ``self`` if they have been implemented.
2049
+
2050
+ EXAMPLES::
2051
+
2052
+ sage: P.<x> = QQ[]
2053
+ sage: incl = P.coerce_map_from(ZZ)
2054
+ sage: sect = incl.section(); sect
2055
+ Composite map:
2056
+ From: Univariate Polynomial Ring in x over Rational Field
2057
+ To: Integer Ring
2058
+ Defn: Generic map:
2059
+ From: Univariate Polynomial Ring in x over Rational Field
2060
+ To: Rational Field
2061
+ then
2062
+ Generic map:
2063
+ From: Rational Field
2064
+ To: Integer Ring
2065
+ sage: p = x + 5; q = x + 2
2066
+ sage: sect(p-q)
2067
+ 3
2068
+
2069
+ the following example has been attached to :meth:`_integer_`
2070
+ of :class:`sage.rings.polynomial.polynomial_element.Polynomial`
2071
+ before (see comment there)::
2072
+
2073
+ sage: k = GF(47)
2074
+ sage: R.<x> = PolynomialRing(k)
2075
+ sage: R.coerce_map_from(ZZ).section()
2076
+ Composite map:
2077
+ From: Univariate Polynomial Ring in x over Finite Field of size 47
2078
+ To: Integer Ring
2079
+ Defn: Generic map:
2080
+ From: Univariate Polynomial Ring in x over Finite Field of size 47
2081
+ To: Finite Field of size 47
2082
+ then
2083
+ Lifting map:
2084
+ From: Finite Field of size 47
2085
+ To: Integer Ring
2086
+ sage: ZZ(R(45)) # indirect doctest
2087
+ 45
2088
+ sage: ZZ(3*x + 45) # indirect doctest
2089
+ Traceback (most recent call last):
2090
+ ...
2091
+ TypeError: 3*x + 45 is not a constant polynomial
2092
+ """
2093
+ sections = []
2094
+ for m in reversed(list(self)):
2095
+ try:
2096
+ sec = m.section()
2097
+ except TypeError:
2098
+ return None
2099
+ if sec is None:
2100
+ return None
2101
+ sections.append(sec)
2102
+
2103
+ from sage.categories.homset import Hom
2104
+ from sage.categories.sets_with_partial_maps import SetsWithPartialMaps
2105
+ H = Hom(self.codomain(), self.domain(), category=SetsWithPartialMaps())
2106
+ return FormalCompositeMap(H, sections)