passagemath-objects 10.6.45__cp313-cp313-musllinux_1_2_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.

Potentially problematic release.


This version of passagemath-objects might be problematic. Click here for more details.

Files changed (280) hide show
  1. passagemath_objects/__init__.py +3 -0
  2. passagemath_objects-10.6.45.dist-info/METADATA +115 -0
  3. passagemath_objects-10.6.45.dist-info/RECORD +280 -0
  4. passagemath_objects-10.6.45.dist-info/WHEEL +5 -0
  5. passagemath_objects-10.6.45.dist-info/top_level.txt +3 -0
  6. passagemath_objects.libs/libgmp-0e7fc84e.so.10.5.0 +0 -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-313-x86_64-linux-musl.so +0 -0
  11. sage/arith/numerical_approx.pxd +35 -0
  12. sage/arith/numerical_approx.pyx +75 -0
  13. sage/arith/power.cpython-313-x86_64-linux-musl.so +0 -0
  14. sage/arith/power.pxd +31 -0
  15. sage/arith/power.pyx +127 -0
  16. sage/categories/action.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  41. sage/categories/map.pxd +34 -0
  42. sage/categories/map.pyx +2106 -0
  43. sage/categories/morphism.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  60. sage/cpython/atexit.pyx +269 -0
  61. sage/cpython/builtin_types.cpython-313-x86_64-linux-musl.so +0 -0
  62. sage/cpython/builtin_types.pyx +7 -0
  63. sage/cpython/cython_metaclass.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  68. sage/cpython/debug.pyx +302 -0
  69. sage/cpython/dict_del_by_value.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  97. sage/groups/group.pxd +14 -0
  98. sage/groups/group.pyx +322 -0
  99. sage/groups/old.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  122. sage/misc/c3_controlled.pxd +2 -0
  123. sage/misc/c3_controlled.pyx +1402 -0
  124. sage/misc/cachefunc.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  132. sage/misc/constant_function.pyx +130 -0
  133. sage/misc/decorators.py +747 -0
  134. sage/misc/fast_methods.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  139. sage/misc/fpickle.pyx +177 -0
  140. sage/misc/function_mangling.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  144. sage/misc/inherit_comparison.pxd +5 -0
  145. sage/misc/inherit_comparison.pyx +105 -0
  146. sage/misc/instancedoc.cpython-313-x86_64-linux-musl.so +0 -0
  147. sage/misc/instancedoc.pyx +331 -0
  148. sage/misc/lazy_attribute.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  166. sage/misc/nested_class.pxd +3 -0
  167. sage/misc/nested_class.pyx +394 -0
  168. sage/misc/persist.cpython-313-x86_64-linux-musl.so +0 -0
  169. sage/misc/persist.pyx +1251 -0
  170. sage/misc/prandom.py +418 -0
  171. sage/misc/randstate.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  176. sage/misc/reset.pyx +196 -0
  177. sage/misc/sage_ostools.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  208. sage/structure/category_object.pxd +28 -0
  209. sage/structure/category_object.pyx +1087 -0
  210. sage/structure/coerce.cpython-313-x86_64-linux-musl.so +0 -0
  211. sage/structure/coerce.pxd +44 -0
  212. sage/structure/coerce.pyx +2107 -0
  213. sage/structure/coerce_actions.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  228. sage/structure/element.pxd +272 -0
  229. sage/structure/element.pyx +4772 -0
  230. sage/structure/element_wrapper.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  248. sage/structure/list_clone_timings_cy.pyx +86 -0
  249. sage/structure/mutability.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  254. sage/structure/parent.pxd +112 -0
  255. sage/structure/parent.pyx +3093 -0
  256. sage/structure/parent_base.cpython-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.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-313-x86_64-linux-musl.so +0 -0
  269. sage/structure/richcmp.pxd +213 -0
  270. sage/structure/richcmp.pyx +495 -0
  271. sage/structure/sage_object.cpython-313-x86_64-linux-musl.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,4834 @@
1
+ # sage_setup: distribution = sagemath-objects
2
+ """
3
+ Coercion via construction functors
4
+ """
5
+ # ****************************************************************************
6
+ # Copyright (C) 2007-2014 Robert Bradshaw
7
+ # 2007-2018 David Roe
8
+ # 2009-2013 Simon King
9
+ # 2010 John Cremona
10
+ # 2010-2011 Mike Hansen
11
+ # 2012 Julian Rueth
12
+ # 2013-2016 Peter Bruin
13
+ # 2014 Wilfried Luebbe
14
+ # 2015 Benjamin Hackl
15
+ # 2015 Daniel Krenn
16
+ # 2016-2020 Frédéric Chapoton
17
+ # 2017 Jori Mäntysalo
18
+ # 2018 Vincent Delecroix
19
+ # 2020 Marc Mezzarobba
20
+ # 2020-2022 Matthias Koeppe
21
+ #
22
+ # This program is free software: you can redistribute it and/or modify
23
+ # it under the terms of the GNU General Public License as published by
24
+ # the Free Software Foundation, either version 2 of the License, or
25
+ # (at your option) any later version.
26
+ # https://www.gnu.org/licenses/
27
+ # ****************************************************************************
28
+
29
+ import operator
30
+ try:
31
+ from typing import Self # type: ignore (Python >= 3.11)
32
+ except ImportError:
33
+ from typing_extensions import Self # type: ignore (Python 3.10)
34
+
35
+ from sage.categories.functor import Functor, IdentityFunctor_generic
36
+ from sage.misc.lazy_import import lazy_import
37
+ from sage.structure.coerce_exceptions import CoercionException
38
+
39
+ lazy_import('sage.categories.commutative_additive_groups', 'CommutativeAdditiveGroups')
40
+ lazy_import('sage.categories.commutative_rings', 'CommutativeRings')
41
+ lazy_import('sage.categories.groups', 'Groups')
42
+ lazy_import('sage.categories.objects', 'Objects')
43
+ lazy_import('sage.categories.rings', 'Rings')
44
+
45
+ # TODO, think through the rankings, and override pushout where necessary.
46
+
47
+
48
+ class ConstructionFunctor(Functor):
49
+ """
50
+ Base class for construction functors.
51
+
52
+ A construction functor is a functorial algebraic construction,
53
+ such as the construction of a matrix ring over a given ring
54
+ or the fraction field of a given ring.
55
+
56
+ In addition to the class :class:`~sage.categories.functor.Functor`,
57
+ construction functors provide rules for combining and merging
58
+ constructions. This is an important part of Sage's coercion model,
59
+ namely the pushout of two constructions: When a polynomial ``p`` in
60
+ a variable ``x`` with integer coefficients is added to a rational
61
+ number ``q``, then Sage finds that the parents ``ZZ['x']`` and
62
+ ``QQ`` are obtained from ``ZZ`` by applying a polynomial ring
63
+ construction respectively the fraction field construction. Each
64
+ construction functor has an attribute ``rank``, and the rank of
65
+ the polynomial ring construction is higher than the rank of the
66
+ fraction field construction. This means that the pushout of ``QQ``
67
+ and ``ZZ['x']``, and thus a common parent in which ``p`` and ``q``
68
+ can be added, is ``QQ['x']``, since the construction functor with
69
+ a lower rank is applied first.
70
+
71
+ ::
72
+
73
+ sage: F1, R = QQ.construction()
74
+ sage: F1
75
+ FractionField
76
+ sage: R
77
+ Integer Ring
78
+ sage: F2, R = (ZZ['x']).construction()
79
+ sage: F2
80
+ Poly[x]
81
+ sage: R
82
+ Integer Ring
83
+ sage: F3 = F2.pushout(F1)
84
+ sage: F3
85
+ Poly[x](FractionField(...))
86
+ sage: F3(R)
87
+ Univariate Polynomial Ring in x over Rational Field
88
+ sage: from sage.categories.pushout import pushout
89
+ sage: P.<x> = ZZ[]
90
+ sage: pushout(QQ,P)
91
+ Univariate Polynomial Ring in x over Rational Field
92
+ sage: ((x+1) + 1/2).parent()
93
+ Univariate Polynomial Ring in x over Rational Field
94
+
95
+ When composing two construction functors, they are sometimes
96
+ merged into one, as is the case in the Quotient construction::
97
+
98
+ sage: Q15, R = (ZZ.quo(15*ZZ)).construction()
99
+ sage: Q15
100
+ QuotientFunctor
101
+ sage: Q35, R = (ZZ.quo(35*ZZ)).construction()
102
+ sage: Q35
103
+ QuotientFunctor
104
+ sage: Q15.merge(Q35)
105
+ QuotientFunctor
106
+ sage: Q15.merge(Q35)(ZZ)
107
+ Ring of integers modulo 5
108
+
109
+ Functors can not only be applied to objects, but also to morphisms in the
110
+ respective categories. For example::
111
+
112
+ sage: P.<x,y> = ZZ[]
113
+ sage: F = P.construction()[0]; F
114
+ MPoly[x,y]
115
+ sage: A.<a,b> = GF(5)[]
116
+ sage: f = A.hom([a + b, a - b], A)
117
+ sage: F(A)
118
+ Multivariate Polynomial Ring in x, y
119
+ over Multivariate Polynomial Ring in a, b over Finite Field of size 5
120
+ sage: F(f)
121
+ Ring endomorphism of Multivariate Polynomial Ring in x, y
122
+ over Multivariate Polynomial Ring in a, b over Finite Field of size 5
123
+ Defn: Induced from base ring by
124
+ Ring endomorphism of Multivariate Polynomial Ring in a, b
125
+ over Finite Field of size 5
126
+ Defn: a |--> a + b
127
+ b |--> a - b
128
+ sage: F(f)(F(A)(x)*a)
129
+ (a + b)*x
130
+ """
131
+ def __mul__(self, other):
132
+ """
133
+ Compose ``self`` and ``other`` to a composite construction
134
+ functor, unless one of them is the identity.
135
+
136
+ .. NOTE::
137
+
138
+ The product is in functorial notation, i.e., when applying the
139
+ product to an object, the second factor is applied first.
140
+
141
+ TESTS::
142
+
143
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
144
+ sage: I = IdentityConstructionFunctor()
145
+ sage: F = QQ.construction()[0]
146
+ sage: P = ZZ['t'].construction()[0]
147
+ sage: F*P
148
+ FractionField(Poly[t](...))
149
+ sage: P*F
150
+ Poly[t](FractionField(...))
151
+ sage: (F*P)(ZZ)
152
+ Fraction Field of Univariate Polynomial Ring in t over Integer Ring
153
+ sage: I*P is P
154
+ True
155
+ sage: F*I is F
156
+ True
157
+ """
158
+ if not isinstance(self, ConstructionFunctor) and not isinstance(other, ConstructionFunctor):
159
+ raise CoercionException("Non-constructive product")
160
+ if isinstance(other, IdentityConstructionFunctor):
161
+ return self
162
+ if isinstance(self, IdentityConstructionFunctor):
163
+ return other
164
+ return CompositeConstructionFunctor(other, self)
165
+
166
+ def pushout(self, other):
167
+ """
168
+ Composition of two construction functors, ordered by their ranks.
169
+
170
+ .. NOTE::
171
+
172
+ - This method seems not to be used in the coercion model.
173
+
174
+ - By default, the functor with smaller rank is applied first.
175
+
176
+ TESTS::
177
+
178
+ sage: F = QQ.construction()[0]
179
+ sage: P = ZZ['t'].construction()[0]
180
+ sage: F.pushout(P)
181
+ Poly[t](FractionField(...))
182
+ sage: P.pushout(F)
183
+ Poly[t](FractionField(...))
184
+ """
185
+ if self.rank > other.rank:
186
+ return self * other
187
+ else:
188
+ return other * self
189
+
190
+ def __eq__(self, other):
191
+ """
192
+ Equality here means that they are mathematically equivalent, though they may have
193
+ specific implementation data. This method will usually be overloaded in subclasses.
194
+ by default, only the types of the functors are compared. Also see the :meth:`merge` function.
195
+
196
+ TESTS::
197
+
198
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
199
+ sage: I = IdentityConstructionFunctor()
200
+ sage: F = QQ.construction()[0]
201
+ sage: P = ZZ['t'].construction()[0]
202
+ sage: I == F # indirect doctest
203
+ False
204
+ sage: I == I # indirect doctest
205
+ True
206
+ """
207
+ return type(self) is type(other)
208
+
209
+ def __ne__(self, other):
210
+ """
211
+ Check whether ``self`` is not equal to ``other``.
212
+
213
+ EXAMPLES::
214
+
215
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
216
+ sage: I = IdentityConstructionFunctor()
217
+ sage: F = QQ.construction()[0]
218
+ sage: P = ZZ['t'].construction()[0]
219
+ sage: I != F # indirect doctest
220
+ True
221
+ sage: I != I # indirect doctest
222
+ False
223
+ """
224
+ return not (self == other)
225
+
226
+ def __hash__(self):
227
+ """
228
+ Return the hash of ``self``.
229
+
230
+ EXAMPLES::
231
+
232
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
233
+ sage: I = IdentityConstructionFunctor()
234
+ sage: F = QQ.construction()[0]
235
+ sage: hash(I) == hash(F)
236
+ False
237
+ sage: hash(I) == hash(I)
238
+ True
239
+ """
240
+ return hash(repr(self))
241
+
242
+ def _repr_(self):
243
+ """
244
+ .. NOTE::
245
+
246
+ By default, it returns the name of the construction
247
+ functor's class. Usually, this method will be overloaded.
248
+
249
+ TESTS::
250
+
251
+ sage: F = QQ.construction()[0]
252
+ sage: F # indirect doctest
253
+ FractionField
254
+ sage: Q = ZZ.quo(2).construction()[0]
255
+ sage: Q # indirect doctest
256
+ QuotientFunctor
257
+ """
258
+ s = str(type(self))
259
+ import re
260
+ return re.sub(r"<.*'.*\.([^.]*)'>", "\\1", s)
261
+
262
+ def merge(self, other) -> Self | None:
263
+ """
264
+ Merge ``self`` with another construction functor, or return ``None``.
265
+
266
+ .. NOTE::
267
+
268
+ The default is to merge only if the two functors coincide. But this
269
+ may be overloaded for subclasses, such as the quotient functor.
270
+
271
+ EXAMPLES::
272
+
273
+ sage: F = QQ.construction()[0]
274
+ sage: P = ZZ['t'].construction()[0]
275
+ sage: F.merge(F)
276
+ FractionField
277
+ sage: F.merge(P)
278
+ sage: P.merge(F)
279
+ sage: P.merge(P)
280
+ Poly[t]
281
+ """
282
+ if self == other:
283
+ return self
284
+ else:
285
+ return None
286
+
287
+ def commutes(self, other):
288
+ """
289
+ Determine whether ``self`` commutes with another construction functor.
290
+
291
+ .. NOTE::
292
+
293
+ By default, ``False`` is returned in all cases (even if the two
294
+ functors are the same, since in this case :meth:`merge` will apply
295
+ anyway). So far there is no construction functor that overloads
296
+ this method. Anyway, this method only becomes relevant if two
297
+ construction functors have the same rank.
298
+
299
+ EXAMPLES::
300
+
301
+ sage: F = QQ.construction()[0]
302
+ sage: P = ZZ['t'].construction()[0]
303
+ sage: F.commutes(P)
304
+ False
305
+ sage: P.commutes(F)
306
+ False
307
+ sage: F.commutes(F)
308
+ False
309
+ """
310
+ return False
311
+
312
+ def expand(self):
313
+ """
314
+ Decompose ``self`` into a list of construction functors.
315
+
316
+ .. NOTE::
317
+
318
+ The default is to return the list only containing ``self``.
319
+
320
+ EXAMPLES::
321
+
322
+ sage: F = QQ.construction()[0]
323
+ sage: F.expand()
324
+ [FractionField]
325
+ sage: Q = ZZ.quo(2).construction()[0]
326
+ sage: Q.expand()
327
+ [QuotientFunctor]
328
+ sage: P = ZZ['t'].construction()[0]
329
+ sage: FP = F*P
330
+ sage: FP.expand()
331
+ [FractionField, Poly[t]]
332
+ """
333
+ return [self]
334
+
335
+ # See the pushout() function below for explanation.
336
+ coercion_reversed = False
337
+
338
+ def common_base(self, other_functor, self_bases, other_bases):
339
+ r"""
340
+ This function is called by :func:`pushout` when no common parent
341
+ is found in the construction tower.
342
+
343
+ .. NOTE::
344
+
345
+ The main use is for multivariate construction functors,
346
+ which use this function to implement recursion for
347
+ :func:`pushout`.
348
+
349
+ INPUT:
350
+
351
+ - ``other_functor`` -- a construction functor
352
+
353
+ - ``self_bases`` -- the arguments passed to this functor
354
+
355
+ - ``other_bases`` -- the arguments passed to the functor
356
+ ``other_functor``
357
+
358
+ OUTPUT:
359
+
360
+ Nothing, since a
361
+ :class:`~sage.structure.coerce_exceptions.CoercionException`
362
+ is raised.
363
+
364
+ .. NOTE::
365
+
366
+ Overload this function in derived class, see
367
+ e.e. :class:`MultivariateConstructionFunctor`.
368
+
369
+ TESTS::
370
+
371
+ sage: from sage.categories.pushout import pushout
372
+ sage: pushout(QQ, cartesian_product([ZZ])) # indirect doctest
373
+ Traceback (most recent call last):
374
+ ...
375
+ CoercionException: No common base ("join") found for
376
+ FractionField(Integer Ring) and The cartesian_product functorial construction(Integer Ring).
377
+ """
378
+ self._raise_common_base_exception_(
379
+ other_functor, self_bases, other_bases)
380
+
381
+ def _raise_common_base_exception_(self, other_functor,
382
+ self_bases, other_bases,
383
+ reason=None):
384
+ r"""
385
+ Raise a coercion exception.
386
+
387
+ INPUT:
388
+
389
+ - ``other_functor`` -- a functor
390
+
391
+ - ``self_bases`` -- the arguments passed to this functor
392
+
393
+ - ``other_bases`` -- the arguments passed to the functor
394
+ ``other_functor``
395
+
396
+ - ``reason`` -- string or ``None`` (default)
397
+
398
+ TESTS::
399
+
400
+ sage: from sage.categories.pushout import pushout
401
+ sage: pushout(QQ, cartesian_product([QQ])) # indirect doctest
402
+ Traceback (most recent call last):
403
+ ...
404
+ CoercionException: No common base ("join") found for
405
+ FractionField(Integer Ring) and The cartesian_product functorial construction(Rational Field).
406
+ """
407
+ if not isinstance(self_bases, (tuple, list)):
408
+ self_bases = (self_bases,)
409
+ if not isinstance(other_bases, (tuple, list)):
410
+ other_bases = (other_bases,)
411
+ if reason is None:
412
+ reason = '.'
413
+ else:
414
+ reason = ': ' + reason + '.'
415
+ raise CoercionException(
416
+ 'No common base ("join") found for %s(%s) and %s(%s)%s' %
417
+ (self, ', '.join(str(b) for b in self_bases),
418
+ other_functor, ', '.join(str(b) for b in other_bases),
419
+ reason))
420
+
421
+
422
+ class CompositeConstructionFunctor(ConstructionFunctor):
423
+ """
424
+ A Construction Functor composed by other Construction Functors.
425
+
426
+ INPUT:
427
+
428
+ - ``F1, F2,...`` -- a list of Construction Functors. The result is the
429
+ composition ``F1`` followed by ``F2`` followed by ...
430
+
431
+ EXAMPLES::
432
+
433
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
434
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0], ZZ['x'].construction()[0],
435
+ ....: QQ.construction()[0], ZZ['y'].construction()[0])
436
+ sage: F
437
+ Poly[y](FractionField(Poly[x](FractionField(...))))
438
+ sage: F == loads(dumps(F))
439
+ True
440
+ sage: F == CompositeConstructionFunctor(*F.all)
441
+ True
442
+ sage: F(GF(2)['t']) # needs sage.libs.ntl
443
+ Univariate Polynomial Ring in y
444
+ over Fraction Field of Univariate Polynomial Ring in x
445
+ over Fraction Field of Univariate Polynomial Ring in t
446
+ over Finite Field of size 2 (using ...)
447
+ """
448
+ def __init__(self, *args):
449
+ """
450
+ TESTS::
451
+
452
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
453
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0], ZZ['x'].construction()[0],
454
+ ....: QQ.construction()[0], ZZ['y'].construction()[0])
455
+ sage: F
456
+ Poly[y](FractionField(Poly[x](FractionField(...))))
457
+ sage: F == CompositeConstructionFunctor(*F.all)
458
+ True
459
+ """
460
+ self.all = []
461
+ for c in args:
462
+ if isinstance(c, list):
463
+ self.all += c
464
+ elif isinstance(c, CompositeConstructionFunctor):
465
+ self.all += c.all
466
+ else:
467
+ self.all.append(c)
468
+ Functor.__init__(self, self.all[0].domain(), self.all[-1].codomain())
469
+
470
+ def _apply_functor_to_morphism(self, f):
471
+ """
472
+ Apply the functor to an object of ``self``'s domain.
473
+
474
+ TESTS::
475
+
476
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
477
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
478
+ sage: R.<a,b> = QQ[]
479
+ sage: f = R.hom([a+b, a-b])
480
+ sage: F(f) # indirect doctest
481
+ Ring endomorphism of Univariate Polynomial Ring in y over Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
482
+ Defn: Induced from base ring by
483
+ Ring endomorphism of Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
484
+ Defn: Induced from base ring by
485
+ Ring endomorphism of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
486
+ Defn: Induced from base ring by
487
+ Ring endomorphism of Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
488
+ Defn: a |--> a + b
489
+ b |--> a - b
490
+ """
491
+ for c in self.all:
492
+ f = c(f)
493
+ return f
494
+
495
+ def _apply_functor(self, R):
496
+ """
497
+ Apply the functor to an object of ``self``'s domain.
498
+
499
+ TESTS::
500
+
501
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
502
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
503
+ sage: R.<a,b> = QQ[]
504
+ sage: F(R) # indirect doctest
505
+ Univariate Polynomial Ring in y over Fraction Field of Univariate Polynomial Ring in x over Fraction Field of Multivariate Polynomial Ring in a, b over Rational Field
506
+ """
507
+ for c in self.all:
508
+ R = c(R)
509
+ return R
510
+
511
+ def __eq__(self, other):
512
+ """
513
+ TESTS::
514
+
515
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
516
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
517
+ sage: F == loads(dumps(F)) # indirect doctest
518
+ True
519
+ """
520
+ if isinstance(other, CompositeConstructionFunctor):
521
+ return self.all == other.all
522
+ else:
523
+ return type(self) is type(other)
524
+
525
+ def __ne__(self, other):
526
+ """
527
+ Check whether ``self`` is not equal to ``other``.
528
+
529
+ EXAMPLES::
530
+
531
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
532
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
533
+ sage: F != loads(dumps(F)) # indirect doctest
534
+ False
535
+ """
536
+ return not (self == other)
537
+
538
+ __hash__ = ConstructionFunctor.__hash__
539
+
540
+ def __mul__(self, other):
541
+ """
542
+ Compose construction functors to a composite construction functor, unless one of them is the identity.
543
+
544
+ .. NOTE::
545
+
546
+ The product is in functorial notation, i.e., when applying the product to an object
547
+ then the second factor is applied first.
548
+
549
+ EXAMPLES::
550
+
551
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
552
+ sage: F1 = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0])
553
+ sage: F2 = CompositeConstructionFunctor(QQ.construction()[0],ZZ['y'].construction()[0])
554
+ sage: F1*F2
555
+ Poly[x](FractionField(Poly[y](FractionField(...))))
556
+ """
557
+ if isinstance(self, CompositeConstructionFunctor):
558
+ all = [other] + self.all
559
+ elif isinstance(other, IdentityConstructionFunctor):
560
+ return self
561
+ else:
562
+ all = other.all + [self]
563
+ return CompositeConstructionFunctor(*all)
564
+
565
+ def _repr_(self):
566
+ """
567
+ TESTS::
568
+
569
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
570
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],ZZ['x'].construction()[0],QQ.construction()[0],ZZ['y'].construction()[0])
571
+ sage: F # indirect doctest
572
+ Poly[y](FractionField(Poly[x](FractionField(...))))
573
+ """
574
+ s = "..."
575
+ for c in self.all:
576
+ s = "%s(%s)" % (c, s)
577
+ return s
578
+
579
+ def expand(self):
580
+ """
581
+ Return expansion of a CompositeConstructionFunctor.
582
+
583
+ .. NOTE::
584
+
585
+ The product over the list of components, as returned by
586
+ the ``expand()`` method, is equal to ``self``.
587
+
588
+ EXAMPLES::
589
+
590
+ sage: from sage.categories.pushout import CompositeConstructionFunctor
591
+ sage: F = CompositeConstructionFunctor(QQ.construction()[0],
592
+ ....: ZZ['x'].construction()[0],
593
+ ....: QQ.construction()[0],
594
+ ....: ZZ['y'].construction()[0])
595
+ sage: F
596
+ Poly[y](FractionField(Poly[x](FractionField(...))))
597
+ sage: prod(F.expand()) == F
598
+ True
599
+ """
600
+ return list(reversed(self.all))
601
+
602
+
603
+ class IdentityConstructionFunctor(ConstructionFunctor):
604
+ """
605
+ A construction functor that is the identity functor.
606
+
607
+ TESTS::
608
+
609
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
610
+ sage: I = IdentityConstructionFunctor()
611
+ sage: I(RR) is RR
612
+ True
613
+ sage: I == loads(dumps(I))
614
+ True
615
+ """
616
+ rank = -100
617
+
618
+ def __init__(self):
619
+ """
620
+ TESTS::
621
+
622
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
623
+ sage: I = IdentityConstructionFunctor()
624
+ sage: IdentityFunctor(Sets()) == I
625
+ True
626
+ sage: I(RR) is RR
627
+ True
628
+ """
629
+ from sage.categories.sets_cat import Sets
630
+ ConstructionFunctor.__init__(self, Sets(), Sets())
631
+
632
+ def _apply_functor(self, x):
633
+ """
634
+ Return the argument unaltered.
635
+
636
+ TESTS::
637
+
638
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
639
+ sage: I = IdentityConstructionFunctor()
640
+ sage: I(RR) is RR # indirect doctest
641
+ True
642
+ """
643
+ return x
644
+
645
+ def _apply_functor_to_morphism(self, f):
646
+ """
647
+ Return the argument unaltered.
648
+
649
+ TESTS::
650
+
651
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
652
+ sage: I = IdentityConstructionFunctor()
653
+ sage: f = ZZ['t'].hom(['x'],QQ['x'])
654
+ sage: I(f) is f # indirect doctest
655
+ True
656
+ """
657
+ return f
658
+
659
+ def __eq__(self, other):
660
+ """
661
+ TESTS::
662
+
663
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
664
+ sage: I = IdentityConstructionFunctor()
665
+ sage: I == IdentityFunctor(Sets()) # indirect doctest
666
+ True
667
+ sage: I == QQ.construction()[0]
668
+ False
669
+ """
670
+ c = (type(self) is type(other))
671
+ if not c:
672
+ if isinstance(other, IdentityFunctor_generic):
673
+ return True
674
+ return c
675
+
676
+ def __ne__(self, other):
677
+ """
678
+ Check whether ``self`` is not equal to ``other``.
679
+
680
+ EXAMPLES::
681
+
682
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
683
+ sage: I = IdentityConstructionFunctor()
684
+ sage: I != IdentityFunctor(Sets()) # indirect doctest
685
+ False
686
+ sage: I != QQ.construction()[0]
687
+ True
688
+ """
689
+ return not (self == other)
690
+
691
+ __hash__ = ConstructionFunctor.__hash__
692
+
693
+ def __mul__(self, other):
694
+ """
695
+ Compose construction functors to a composite construction functor, unless one of them is the identity.
696
+
697
+ .. NOTE::
698
+
699
+ The product is in functorial notation, i.e., when applying the
700
+ product to an object then the second factor is applied first.
701
+
702
+ TESTS::
703
+
704
+ sage: from sage.categories.pushout import IdentityConstructionFunctor
705
+ sage: I = IdentityConstructionFunctor()
706
+ sage: F = QQ.construction()[0]
707
+ sage: P = ZZ['t'].construction()[0]
708
+ sage: I*F is F # indirect doctest
709
+ True
710
+ sage: F*I is F
711
+ True
712
+ sage: I*P is P
713
+ True
714
+ sage: P*I is P
715
+ True
716
+ """
717
+ if isinstance(self, IdentityConstructionFunctor):
718
+ return other
719
+ else:
720
+ return self
721
+
722
+
723
+ class MultivariateConstructionFunctor(ConstructionFunctor):
724
+ """
725
+ An abstract base class for functors that take
726
+ multiple inputs (e.g. Cartesian products).
727
+
728
+ TESTS::
729
+
730
+ sage: from sage.categories.pushout import pushout
731
+ sage: A = cartesian_product((QQ['z'], QQ))
732
+ sage: B = cartesian_product((ZZ['t']['z'], QQ))
733
+ sage: pushout(A, B)
734
+ The Cartesian product of (Univariate Polynomial Ring in z over
735
+ Univariate Polynomial Ring in t over Rational Field,
736
+ Rational Field)
737
+ sage: A.construction()
738
+ (The cartesian_product functorial construction,
739
+ (Univariate Polynomial Ring in z over Rational Field, Rational Field))
740
+ sage: pushout(A, B)
741
+ The Cartesian product of (Univariate Polynomial Ring in z over Univariate Polynomial Ring in t over Rational Field, Rational Field)
742
+ """
743
+ def common_base(self, other_functor, self_bases, other_bases):
744
+ r"""
745
+ This function is called by :func:`pushout` when no common parent
746
+ is found in the construction tower.
747
+
748
+ INPUT:
749
+
750
+ - ``other_functor`` -- a construction functor
751
+
752
+ - ``self_bases`` -- the arguments passed to this functor
753
+
754
+ - ``other_bases`` -- the arguments passed to the functor
755
+ ``other_functor``
756
+
757
+ OUTPUT: a parent
758
+
759
+ If no common base is found a :class:`sage.structure.coerce_exceptions.CoercionException`
760
+ is raised.
761
+
762
+ .. NOTE::
763
+
764
+ Overload this function in derived class, see
765
+ e.g. :class:`MultivariateConstructionFunctor`.
766
+
767
+ TESTS::
768
+
769
+ sage: from sage.categories.pushout import pushout
770
+ sage: pushout(cartesian_product([ZZ]), QQ) # indirect doctest
771
+ Traceback (most recent call last):
772
+ ...
773
+ CoercionException: No common base ("join") found for
774
+ The cartesian_product functorial construction(Integer Ring) and FractionField(Integer Ring):
775
+ (Multivariate) functors are incompatible.
776
+ sage: pushout(cartesian_product([ZZ]), cartesian_product([ZZ, QQ])) # indirect doctest
777
+ Traceback (most recent call last):
778
+ ...
779
+ CoercionException: No common base ("join") found ...
780
+ """
781
+ if self != other_functor:
782
+ self._raise_common_base_exception_(
783
+ other_functor, self_bases, other_bases,
784
+ '(Multivariate) functors are incompatible')
785
+ if len(self_bases) != len(other_bases):
786
+ self._raise_common_base_exception_(
787
+ other_functor, self_bases, other_bases,
788
+ 'Functors need the same number of arguments')
789
+ from sage.structure.element import coercion_model
790
+ Z_bases = tuple(coercion_model.common_parent(S, O)
791
+ for S, O in zip(self_bases, other_bases))
792
+ return self(Z_bases)
793
+
794
+
795
+ class PolynomialFunctor(ConstructionFunctor):
796
+ """
797
+ Construction functor for univariate polynomial rings.
798
+
799
+ EXAMPLES::
800
+
801
+ sage: P = ZZ['t'].construction()[0]
802
+ sage: P(GF(3))
803
+ Univariate Polynomial Ring in t over Finite Field of size 3
804
+ sage: P == loads(dumps(P))
805
+ True
806
+ sage: R.<x,y> = GF(5)[]
807
+ sage: f = R.hom([x + 2*y, 3*x - y], R)
808
+ sage: P(f)((x+y) * P(R).0)
809
+ (-x + y)*t
810
+
811
+ By :issue:`9944`, the construction functor distinguishes sparse and
812
+ dense polynomial rings. Before, the following example failed::
813
+
814
+ sage: R.<x> = PolynomialRing(GF(5), sparse=True)
815
+ sage: F, B = R.construction()
816
+ sage: F(B) is R
817
+ True
818
+ sage: S.<x> = PolynomialRing(ZZ)
819
+ sage: R.has_coerce_map_from(S)
820
+ False
821
+ sage: S.has_coerce_map_from(R)
822
+ False
823
+ sage: S.0 + R.0
824
+ 2*x
825
+ sage: (S.0 + R.0).parent()
826
+ Univariate Polynomial Ring in x over Finite Field of size 5
827
+ sage: (S.0 + R.0).parent().is_sparse()
828
+ False
829
+ """
830
+ rank = 9
831
+
832
+ def __init__(self, var, multi_variate=False, sparse=False, implementation=None):
833
+ """
834
+ TESTS::
835
+
836
+ sage: from sage.categories.pushout import PolynomialFunctor
837
+ sage: P = PolynomialFunctor('x')
838
+ sage: P(GF(3))
839
+ Univariate Polynomial Ring in x over Finite Field of size 3
840
+
841
+ There is an optional parameter ``multi_variate``, but
842
+ apparently it is not used::
843
+
844
+ sage: Q = PolynomialFunctor('x',multi_variate=True)
845
+ sage: Q(ZZ)
846
+ Univariate Polynomial Ring in x over Integer Ring
847
+ sage: Q == P
848
+ True
849
+ """
850
+ from .rings import Rings
851
+ Functor.__init__(self, Rings(), Rings())
852
+ self.var = var
853
+ self.multi_variate = multi_variate
854
+ self.sparse = sparse
855
+ self.implementation = implementation
856
+
857
+ def _apply_functor(self, R):
858
+ """
859
+ Apply the functor to an object of ``self``'s domain.
860
+
861
+ TESTS::
862
+
863
+ sage: P = ZZ['x'].construction()[0]
864
+ sage: P(GF(3)) # indirect doctest
865
+ Univariate Polynomial Ring in x over Finite Field of size 3
866
+ """
867
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
868
+ kwds = {}
869
+ if self.implementation:
870
+ kwds['implementation'] = self.implementation
871
+ return PolynomialRing(R, self.var, sparse=self.sparse, **kwds)
872
+
873
+ def _apply_functor_to_morphism(self, f):
874
+ """
875
+ Apply the functor ``self`` to the morphism `f`.
876
+
877
+ TESTS::
878
+
879
+ sage: P = ZZ['x'].construction()[0]
880
+ sage: P(ZZ.hom(GF(3))) # indirect doctest
881
+ Ring morphism:
882
+ From: Univariate Polynomial Ring in x over Integer Ring
883
+ To: Univariate Polynomial Ring in x over Finite Field of size 3
884
+ Defn: Induced from base ring by
885
+ Natural morphism:
886
+ From: Integer Ring
887
+ To: Finite Field of size 3
888
+ """
889
+ from sage.rings.polynomial.polynomial_ring_homomorphism import (
890
+ PolynomialRingHomomorphism_from_base,
891
+ )
892
+ R = self._apply_functor(f.domain())
893
+ S = self._apply_functor(f.codomain())
894
+ return PolynomialRingHomomorphism_from_base(R.Hom(S), f)
895
+
896
+ def __eq__(self, other):
897
+ """
898
+ TESTS::
899
+
900
+ sage: from sage.categories.pushout import MultiPolynomialFunctor
901
+ sage: Q = MultiPolynomialFunctor(('x',),'lex')
902
+ sage: P = ZZ['x'].construction()[0]
903
+ sage: P
904
+ Poly[x]
905
+ sage: Q
906
+ MPoly[x]
907
+ sage: P == Q
908
+ True
909
+ sage: P == loads(dumps(P))
910
+ True
911
+ sage: P == QQ.construction()[0]
912
+ False
913
+ """
914
+ if isinstance(other, PolynomialFunctor):
915
+ return self.var == other.var
916
+ elif isinstance(other, MultiPolynomialFunctor):
917
+ return (other == self)
918
+ else:
919
+ return False
920
+
921
+ def __ne__(self, other):
922
+ """
923
+ Check whether ``self`` is not equal to ``other``.
924
+
925
+ EXAMPLES::
926
+
927
+ sage: from sage.categories.pushout import MultiPolynomialFunctor
928
+ sage: Q = MultiPolynomialFunctor(('x',),'lex')
929
+ sage: P = ZZ['x'].construction()[0]
930
+ sage: P != Q
931
+ False
932
+ sage: P != loads(dumps(P))
933
+ False
934
+ sage: P != QQ.construction()[0]
935
+ True
936
+ """
937
+ return not (self == other)
938
+
939
+ __hash__ = ConstructionFunctor.__hash__
940
+
941
+ def merge(self, other):
942
+ """
943
+ Merge ``self`` with another construction functor, or return ``None``.
944
+
945
+ .. NOTE::
946
+
947
+ Internally, the merging is delegated to the merging of
948
+ multipolynomial construction functors. But in effect,
949
+ this does the same as the default implementation, that
950
+ returns ``None`` unless the to-be-merged functors coincide.
951
+
952
+ EXAMPLES::
953
+
954
+ sage: P = ZZ['x'].construction()[0]
955
+ sage: Q = ZZ['y','x'].construction()[0]
956
+ sage: P.merge(Q)
957
+ sage: P.merge(P) is P
958
+ True
959
+ """
960
+ if isinstance(other, MultiPolynomialFunctor):
961
+ return other.merge(self)
962
+ elif self == other:
963
+ # i.e., they only differ in sparsity
964
+ if not self.sparse:
965
+ return self
966
+ return other
967
+ else:
968
+ return None
969
+
970
+ def _repr_(self):
971
+ """
972
+ TESTS::
973
+
974
+ sage: P = ZZ['x'].construction()[0]
975
+ sage: P # indirect doctest
976
+ Poly[x]
977
+ """
978
+ return "Poly[%s]" % self.var
979
+
980
+
981
+ class MultiPolynomialFunctor(ConstructionFunctor):
982
+ """
983
+ A constructor for multivariate polynomial rings.
984
+
985
+ EXAMPLES::
986
+
987
+ sage: P.<x,y> = ZZ[]
988
+ sage: F = P.construction()[0]; F
989
+ MPoly[x,y]
990
+ sage: A.<a,b> = GF(5)[]
991
+ sage: F(A)
992
+ Multivariate Polynomial Ring in x, y
993
+ over Multivariate Polynomial Ring in a, b over Finite Field of size 5
994
+ sage: f = A.hom([a+b, a-b], A)
995
+ sage: F(f)
996
+ Ring endomorphism of Multivariate Polynomial Ring in x, y
997
+ over Multivariate Polynomial Ring in a, b over Finite Field of size 5
998
+ Defn: Induced from base ring by
999
+ Ring endomorphism of Multivariate Polynomial Ring in a, b over Finite Field of size 5
1000
+ Defn: a |--> a + b
1001
+ b |--> a - b
1002
+ sage: F(f)(F(A)(x)*a)
1003
+ (a + b)*x
1004
+ """
1005
+
1006
+ rank = 9
1007
+
1008
+ def __init__(self, vars, term_order):
1009
+ """
1010
+ EXAMPLES::
1011
+
1012
+ sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
1013
+ sage: F
1014
+ MPoly[x,y]
1015
+ sage: F(ZZ)
1016
+ Multivariate Polynomial Ring in x, y over Integer Ring
1017
+ sage: F(CC) # needs sage.rings.real_mpfr
1018
+ Multivariate Polynomial Ring in x, y over Complex Field with 53 bits of precision
1019
+ """
1020
+ Functor.__init__(self, Rings(), Rings())
1021
+ self.vars = vars
1022
+ self.term_order = term_order
1023
+
1024
+ def _apply_functor(self, R):
1025
+ """
1026
+ Apply the functor to an object of ``self``'s domain.
1027
+
1028
+ EXAMPLES::
1029
+
1030
+ sage: R.<x,y,z> = QQ[]
1031
+ sage: F = R.construction()[0]; F
1032
+ MPoly[x,y,z]
1033
+ sage: type(F)
1034
+ <class 'sage.categories.pushout.MultiPolynomialFunctor'>
1035
+ sage: F(ZZ) # indirect doctest
1036
+ Multivariate Polynomial Ring in x, y, z over Integer Ring
1037
+ sage: F(RR) # indirect doctest # needs sage.rings.real_mpfr
1038
+ Multivariate Polynomial Ring in x, y, z over Real Field with 53 bits of precision
1039
+ """
1040
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
1041
+ return PolynomialRing(R, self.vars)
1042
+
1043
+ def __eq__(self, other):
1044
+ """
1045
+ EXAMPLES::
1046
+
1047
+ sage: F = ZZ['x,y,z'].construction()[0]
1048
+ sage: G = QQ['x,y,z'].construction()[0]
1049
+ sage: F == G
1050
+ True
1051
+ sage: G == loads(dumps(G))
1052
+ True
1053
+ sage: G = ZZ['x,y'].construction()[0]
1054
+ sage: F == G
1055
+ False
1056
+ """
1057
+ if isinstance(other, MultiPolynomialFunctor):
1058
+ return (self.vars == other.vars and
1059
+ self.term_order == other.term_order)
1060
+ elif isinstance(other, PolynomialFunctor):
1061
+ return self.vars == (other.var,)
1062
+ else:
1063
+ return False
1064
+
1065
+ def __ne__(self, other):
1066
+ """
1067
+ Check whether ``self`` is not equal to ``other``.
1068
+
1069
+ EXAMPLES::
1070
+
1071
+ sage: F = ZZ['x,y,z'].construction()[0]
1072
+ sage: G = QQ['x,y,z'].construction()[0]
1073
+ sage: F != G
1074
+ False
1075
+ sage: G != loads(dumps(G))
1076
+ False
1077
+ sage: G = ZZ['x,y'].construction()[0]
1078
+ sage: F != G
1079
+ True
1080
+ """
1081
+ return not (self == other)
1082
+
1083
+ __hash__ = ConstructionFunctor.__hash__
1084
+
1085
+ def __mul__(self, other):
1086
+ """
1087
+ If two MPoly functors are given in a row, form a single MPoly functor
1088
+ with all of the variables.
1089
+
1090
+ EXAMPLES::
1091
+
1092
+ sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
1093
+ sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
1094
+ sage: G*F
1095
+ MPoly[x,y,t]
1096
+ """
1097
+ if isinstance(other, IdentityConstructionFunctor):
1098
+ return self
1099
+ if isinstance(other, MultiPolynomialFunctor):
1100
+ if self.term_order != other.term_order:
1101
+ raise CoercionException("Incompatible term orders (%s,%s)." % (self.term_order, other.term_order))
1102
+ if set(self.vars).intersection(other.vars):
1103
+ raise CoercionException("Overlapping variables (%s,%s)" % (self.vars, other.vars))
1104
+ return MultiPolynomialFunctor(other.vars + self.vars, self.term_order)
1105
+ elif (isinstance(other, CompositeConstructionFunctor)
1106
+ and isinstance(other.all[-1], MultiPolynomialFunctor)):
1107
+ return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1])
1108
+ else:
1109
+ return CompositeConstructionFunctor(other, self)
1110
+
1111
+ def merge(self, other):
1112
+ """
1113
+ Merge ``self`` with another construction functor, or return ``None``.
1114
+
1115
+ EXAMPLES::
1116
+
1117
+ sage: F = sage.categories.pushout.MultiPolynomialFunctor(['x','y'], None)
1118
+ sage: G = sage.categories.pushout.MultiPolynomialFunctor(['t'], None)
1119
+ sage: F.merge(G) is None
1120
+ True
1121
+ sage: F.merge(F)
1122
+ MPoly[x,y]
1123
+ """
1124
+ if self == other:
1125
+ return self
1126
+ else:
1127
+ return None
1128
+
1129
+ def expand(self):
1130
+ """
1131
+ Decompose ``self`` into a list of construction functors.
1132
+
1133
+ EXAMPLES::
1134
+
1135
+ sage: F = QQ['x,y,z,t'].construction()[0]; F
1136
+ MPoly[x,y,z,t]
1137
+ sage: F.expand()
1138
+ [MPoly[t], MPoly[z], MPoly[y], MPoly[x]]
1139
+
1140
+ Now an actual use case::
1141
+
1142
+ sage: R.<x,y,z> = ZZ[]
1143
+ sage: S.<z,t> = QQ[]
1144
+ sage: x+t
1145
+ x + t
1146
+ sage: parent(x+t)
1147
+ Multivariate Polynomial Ring in x, y, z, t over Rational Field
1148
+ sage: T.<y,s> = QQ[]
1149
+ sage: x + s
1150
+ Traceback (most recent call last):
1151
+ ...
1152
+ TypeError: unsupported operand parent(s) for +:
1153
+ 'Multivariate Polynomial Ring in x, y, z over Integer Ring' and
1154
+ 'Multivariate Polynomial Ring in y, s over Rational Field'
1155
+ sage: R = PolynomialRing(ZZ, 'x', 50)
1156
+ sage: S = PolynomialRing(GF(5), 'x', 20)
1157
+ sage: R.gen(0) + S.gen(0)
1158
+ 2*x0
1159
+ """
1160
+ if len(self.vars) <= 1:
1161
+ return [self]
1162
+ else:
1163
+ return [MultiPolynomialFunctor((x,), self.term_order) for x in reversed(self.vars)]
1164
+
1165
+ def _repr_(self):
1166
+ """
1167
+ TESTS::
1168
+
1169
+ sage: QQ['x,y,z,t'].construction()[0]
1170
+ MPoly[x,y,z,t]
1171
+ """
1172
+ return "MPoly[%s]" % ','.join(self.vars)
1173
+
1174
+
1175
+ class InfinitePolynomialFunctor(ConstructionFunctor):
1176
+ r"""
1177
+ A Construction Functor for Infinite Polynomial Rings (see :mod:`~sage.rings.polynomial.infinite_polynomial_ring`).
1178
+
1179
+ AUTHOR:
1180
+
1181
+ -- Simon King
1182
+
1183
+ This construction functor is used to provide uniqueness of infinite polynomial rings as parent structures.
1184
+ As usual, the construction functor allows for constructing pushouts.
1185
+
1186
+ Another purpose is to avoid name conflicts of variables of the to-be-constructed infinite polynomial ring with
1187
+ variables of the base ring, and moreover to keep the internal structure of an Infinite Polynomial Ring as simple
1188
+ as possible: If variables `v_1,...,v_n` of the given base ring generate an *ordered* sub-monoid of the monomials
1189
+ of the ambient Infinite Polynomial Ring, then they are removed from the base ring and merged with the generators
1190
+ of the ambient ring. However, if the orders don't match, an error is raised, since there was a name conflict
1191
+ without merging.
1192
+
1193
+ EXAMPLES::
1194
+
1195
+ sage: A.<a,b> = InfinitePolynomialRing(ZZ['t'])
1196
+ sage: A.construction()
1197
+ [InfPoly{[a,b], "lex", "dense"},
1198
+ Univariate Polynomial Ring in t over Integer Ring]
1199
+ sage: type(_[0])
1200
+ <class 'sage.categories.pushout.InfinitePolynomialFunctor'>
1201
+ sage: B.<x,y,a_3,a_1> = PolynomialRing(QQ, order='lex')
1202
+ sage: B.construction()
1203
+ (MPoly[x,y,a_3,a_1], Rational Field)
1204
+ sage: A.construction()[0] * B.construction()[0]
1205
+ InfPoly{[a,b], "lex", "dense"}(MPoly[x,y](...))
1206
+
1207
+ Apparently the variables `a_1,a_3` of the polynomial ring are merged with the variables
1208
+ `a_0, a_1, a_2, ...` of the infinite polynomial ring; indeed, they form an ordered sub-structure.
1209
+ However, if the polynomial ring was given a different ordering, merging would not be allowed,
1210
+ resulting in a name conflict::
1211
+
1212
+ sage: R = PolynomialRing(QQ, names=['x','y','a_3','a_1'])
1213
+ sage: A.construction()[0] * R.construction()[0]
1214
+ Traceback (most recent call last):
1215
+ ...
1216
+ CoercionException: Incompatible term orders lex, degrevlex
1217
+
1218
+ In an infinite polynomial ring with generator `a_\ast`, the variable `a_3` will always be greater
1219
+ than the variable `a_1`. Hence, the orders are incompatible in the next example as well::
1220
+
1221
+ sage: R = PolynomialRing(QQ, names=['x','y','a_1','a_3'], order='lex')
1222
+ sage: A.construction()[0] * R.construction()[0]
1223
+ Traceback (most recent call last):
1224
+ ...
1225
+ CoercionException: Overlapping variables (('a', 'b'),['a_1', 'a_3'])
1226
+ are incompatible
1227
+
1228
+ Another requirement is that after merging the order of the remaining variables must be unique.
1229
+ This is not the case in the following example, since it is not clear whether the variables `x,y`
1230
+ should be greater or smaller than the variables `b_\ast`::
1231
+
1232
+ sage: R = PolynomialRing(QQ, names=['a_3','a_1','x','y'], order='lex')
1233
+ sage: A.construction()[0] * R.construction()[0]
1234
+ Traceback (most recent call last):
1235
+ ...
1236
+ CoercionException: Overlapping variables (('a', 'b'),['a_3', 'a_1'])
1237
+ are incompatible
1238
+
1239
+ Since the construction functors are actually used to construct infinite polynomial rings, the following
1240
+ result is no surprise::
1241
+
1242
+ sage: C.<a,b> = InfinitePolynomialRing(B); C
1243
+ Infinite polynomial ring in a, b
1244
+ over Multivariate Polynomial Ring in x, y over Rational Field
1245
+
1246
+ There is also an overlap in the next example::
1247
+
1248
+ sage: X.<w,x,y> = InfinitePolynomialRing(ZZ)
1249
+ sage: Y.<x,y,z> = InfinitePolynomialRing(QQ)
1250
+
1251
+ `X` and `Y` have an overlapping generators `x_\ast, y_\ast`. Since the default lexicographic order is
1252
+ used in both rings, it gives rise to isomorphic sub-monoids in both `X` and `Y`. They are merged in the
1253
+ pushout, which also yields a common parent for doing arithmetic::
1254
+
1255
+ sage: P = sage.categories.pushout.pushout(Y,X); P
1256
+ Infinite polynomial ring in w, x, y, z over Rational Field
1257
+ sage: w[2]+z[3]
1258
+ w_2 + z_3
1259
+ sage: _.parent() is P
1260
+ True
1261
+ """
1262
+
1263
+ # We do provide merging with polynomial rings. However, it seems that it is better
1264
+ # to have a greater rank, since we want to apply InfinitePolynomialFunctor *after*
1265
+ # [Multi]PolynomialFunctor, which have rank 9. But there is the MatrixFunctor, which
1266
+ # has rank 10. So, do fine tuning...
1267
+ rank = 9.5
1268
+
1269
+ def __init__(self, gens, order, implementation):
1270
+ """
1271
+ TESTS::
1272
+
1273
+ sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
1274
+ InfPoly{[a,b,x], "degrevlex", "sparse"}
1275
+ sage: F == loads(dumps(F))
1276
+ True
1277
+ """
1278
+ if not gens:
1279
+ raise ValueError("Infinite Polynomial Rings have at least one generator")
1280
+ ConstructionFunctor.__init__(self, Rings(), Rings())
1281
+ self._gens = tuple(gens)
1282
+ self._order = order
1283
+ self._imple = implementation
1284
+
1285
+ def _apply_functor_to_morphism(self, f):
1286
+ """
1287
+ Morphisms for infinite polynomial rings are not implemented yet.
1288
+
1289
+ TESTS::
1290
+
1291
+ sage: P.<x,y> = QQ[]
1292
+ sage: R.<alpha> = InfinitePolynomialRing(P)
1293
+ sage: f = P.hom([x+y,x-y],P)
1294
+ sage: R.construction()[0](f) # indirect doctest
1295
+ Traceback (most recent call last):
1296
+ ...
1297
+ NotImplementedError: morphisms for infinite polynomial rings are not implemented yet
1298
+ """
1299
+ raise NotImplementedError("morphisms for infinite polynomial rings are not implemented yet")
1300
+
1301
+ def _apply_functor(self, R):
1302
+ """
1303
+ Apply the functor to an object of ``self``'s domain.
1304
+
1305
+ TESTS::
1306
+
1307
+ sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F
1308
+ InfPoly{[a,b,x], "degrevlex", "sparse"}
1309
+ sage: F(QQ['t']) # indirect doctest
1310
+ Infinite polynomial ring in a, b, x over Univariate Polynomial Ring in t over Rational Field
1311
+ """
1312
+ from sage.rings.polynomial.infinite_polynomial_ring import (
1313
+ InfinitePolynomialRing,
1314
+ )
1315
+ return InfinitePolynomialRing(R, self._gens, order=self._order, implementation=self._imple)
1316
+
1317
+ def _repr_(self):
1318
+ """
1319
+ TESTS::
1320
+
1321
+ sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
1322
+ InfPoly{[a,b,x], "degrevlex", "sparse"}
1323
+ """
1324
+ return 'InfPoly{[%s], "%s", "%s"}' % (','.join(self._gens), self._order, self._imple)
1325
+
1326
+ def __eq__(self, other):
1327
+ """
1328
+ TESTS::
1329
+
1330
+ sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
1331
+ InfPoly{[a,b,x], "degrevlex", "sparse"}
1332
+ sage: F == loads(dumps(F)) # indirect doctest
1333
+ True
1334
+ sage: F == sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse')
1335
+ False
1336
+ """
1337
+ if isinstance(other, InfinitePolynomialFunctor):
1338
+ return (self._gens == other._gens and
1339
+ self._order == other._order and
1340
+ self._imple == other._imple)
1341
+ return False
1342
+
1343
+ def __ne__(self, other):
1344
+ """
1345
+ Check whether ``self`` is not equal to ``other``.
1346
+
1347
+ EXAMPLES::
1348
+
1349
+ sage: F = sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'degrevlex','sparse'); F # indirect doctest
1350
+ InfPoly{[a,b,x], "degrevlex", "sparse"}
1351
+ sage: F != loads(dumps(F)) # indirect doctest
1352
+ False
1353
+ sage: F != sage.categories.pushout.InfinitePolynomialFunctor(['a','b','x'],'deglex','sparse')
1354
+ True
1355
+ """
1356
+ return not (self == other)
1357
+
1358
+ __hash__ = ConstructionFunctor.__hash__
1359
+
1360
+ def __mul__(self, other):
1361
+ """
1362
+ Compose construction functors to a composite construction functor, unless one of them is the identity.
1363
+
1364
+ .. NOTE::
1365
+
1366
+ The product is in functorial notation, i.e., when applying the
1367
+ product to an object then the second factor is applied first.
1368
+
1369
+ TESTS::
1370
+
1371
+ sage: F1 = QQ['a','x_2','x_1','y_3','y_2'].construction()[0]; F1
1372
+ MPoly[a,x_2,x_1,y_3,y_2]
1373
+ sage: F2 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex').construction()[0]; F2
1374
+ InfPoly{[x,y], "degrevlex", "dense"}
1375
+ sage: F3 = InfinitePolynomialRing(QQ, ['x','y'],order='degrevlex',implementation='sparse').construction()[0]; F3
1376
+ InfPoly{[x,y], "degrevlex", "sparse"}
1377
+ sage: F2*F1
1378
+ InfPoly{[x,y], "degrevlex", "dense"}(Poly[a](...))
1379
+ sage: F3*F1
1380
+ InfPoly{[x,y], "degrevlex", "sparse"}(Poly[a](...))
1381
+ sage: F4 = sage.categories.pushout.FractionField()
1382
+ sage: F2*F4
1383
+ InfPoly{[x,y], "degrevlex", "dense"}(FractionField(...))
1384
+ """
1385
+ if isinstance(other, IdentityConstructionFunctor):
1386
+ return self
1387
+ if isinstance(other, self.__class__):
1388
+ INT = set(self._gens).intersection(other._gens)
1389
+ if INT:
1390
+ # if there is overlap of generators, it must only be at the ends, so that
1391
+ # the resulting order after the merging is unique
1392
+ if other._gens[-len(INT):] != self._gens[:len(INT)]:
1393
+ raise CoercionException("Overlapping variables (%s,%s) are incompatible" % (self._gens, other._gens))
1394
+ OUTGENS = list(other._gens) + list(self._gens[len(INT):])
1395
+ else:
1396
+ OUTGENS = list(other._gens) + list(self._gens)
1397
+ # the orders must coincide
1398
+ if self._order != other._order:
1399
+ return CompositeConstructionFunctor(other, self)
1400
+ # the implementations must coincide
1401
+ if self._imple != other._imple:
1402
+ return CompositeConstructionFunctor(other, self)
1403
+ return InfinitePolynomialFunctor(OUTGENS, self._order, self._imple)
1404
+
1405
+ # Polynomial Constructor
1406
+ # Idea: We merge into self, if the polynomial functor really provides a substructure,
1407
+ # even respecting the order. Note that, if the pushout is computed, only *one* variable
1408
+ # will occur in the polynomial constructor. Hence, any order is fine, which is exactly
1409
+ # what we need in order to have coercion maps for different orderings.
1410
+ if isinstance(other, (MultiPolynomialFunctor, PolynomialFunctor)):
1411
+ if isinstance(other, MultiPolynomialFunctor):
1412
+ othervars = other.vars
1413
+ else:
1414
+ othervars = [other.var]
1415
+
1416
+ OverlappingVars = []
1417
+ # The variable names of the MultiPolynomialFunctor
1418
+ # that can be interpreted as variables in self
1419
+
1420
+ RemainingVars = list(othervars)
1421
+ IsOverlap = False
1422
+ BadOverlap = False
1423
+ for x in othervars:
1424
+ if x.count('_') == 1:
1425
+ g, n = x.split('_')
1426
+ if n.isdigit():
1427
+ if g.isalnum(): # we can interpret x in any InfinitePolynomialRing
1428
+ if g in self._gens: # we can interpret x in self, hence, we will not use it as a variable anymore.
1429
+ RemainingVars.pop(RemainingVars.index(x))
1430
+ IsOverlap = True # some variables of other can be interpreted in self.
1431
+ if OverlappingVars:
1432
+ # Is OverlappingVars in the right order?
1433
+ g0, n0 = OverlappingVars[-1].split('_')
1434
+ i = self._gens.index(g)
1435
+ i0 = self._gens.index(g0)
1436
+ if i < i0: # wrong order
1437
+ BadOverlap = True
1438
+ if i == i0 and int(n) > int(n0): # wrong order
1439
+ BadOverlap = True
1440
+ OverlappingVars.append(x)
1441
+ elif IsOverlap: # The overlap must be on the right end of the variable list
1442
+ BadOverlap = True
1443
+ elif IsOverlap: # The overlap must be on the right end of the variable list
1444
+ BadOverlap = True
1445
+ elif IsOverlap: # The overlap must be on the right end of the variable list
1446
+ BadOverlap = True
1447
+ elif IsOverlap: # The overlap must be on the right end of the variable list
1448
+ BadOverlap = True
1449
+
1450
+ if BadOverlap: # the overlapping variables appear in the wrong order
1451
+ raise CoercionException("Overlapping variables (%s,%s) are incompatible" % (self._gens, OverlappingVars))
1452
+ if len(OverlappingVars) > 1: # multivariate, hence, the term order matters
1453
+ if other.term_order.name() != self._order:
1454
+ raise CoercionException("Incompatible term orders %s, %s" % (self._order, other.term_order.name()))
1455
+ # ok, the overlap is fine, we will return something.
1456
+ if RemainingVars: # we can only partially merge other into self
1457
+ if len(RemainingVars) > 1:
1458
+ return CompositeConstructionFunctor(MultiPolynomialFunctor(RemainingVars, term_order=other.term_order), self)
1459
+ return CompositeConstructionFunctor(PolynomialFunctor(RemainingVars[0]), self)
1460
+ return self
1461
+ return CompositeConstructionFunctor(other, self)
1462
+
1463
+ def merge(self, other):
1464
+ """
1465
+ Merge two construction functors of infinite polynomial rings, regardless of monomial order and implementation.
1466
+
1467
+ The purpose is to have a pushout (and thus, arithmetic) even in cases when the parents are isomorphic as
1468
+ rings, but not as ordered rings.
1469
+
1470
+ EXAMPLES::
1471
+
1472
+ sage: X.<x,y> = InfinitePolynomialRing(QQ, implementation='sparse')
1473
+ sage: Y.<x,y> = InfinitePolynomialRing(QQ, order='degrevlex')
1474
+ sage: X.construction()
1475
+ [InfPoly{[x,y], "lex", "sparse"}, Rational Field]
1476
+ sage: Y.construction()
1477
+ [InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
1478
+ sage: Y.construction()[0].merge(Y.construction()[0])
1479
+ InfPoly{[x,y], "degrevlex", "dense"}
1480
+ sage: y[3] + X(x[2])
1481
+ x_2 + y_3
1482
+ sage: _.parent().construction()
1483
+ [InfPoly{[x,y], "degrevlex", "dense"}, Rational Field]
1484
+ """
1485
+ # Merging is only done if the ranks of self and other are the same.
1486
+ # It may happen that other is a substructure of self up to the monomial order
1487
+ # and the implementation. And this is when we want to merge, in order to
1488
+ # provide multiplication for rings with different term orderings.
1489
+ if not isinstance(other, InfinitePolynomialFunctor):
1490
+ return None
1491
+ if set(other._gens).issubset(self._gens):
1492
+ return self
1493
+ return None
1494
+ try:
1495
+ OUT = self * other
1496
+ # The following happens if "other" has the same order type etc.
1497
+ if not isinstance(OUT, CompositeConstructionFunctor):
1498
+ return OUT
1499
+ except CoercionException:
1500
+ pass
1501
+ if isinstance(other, InfinitePolynomialFunctor):
1502
+ # We don't require that the orders coincide. This is a difference to self*other
1503
+ # We only merge if other's generators are an ordered subset of self's generators
1504
+ for g in other._gens:
1505
+ if g not in self._gens:
1506
+ return None
1507
+ # The sequence of variables is part of the ordering. It must coincide in both rings
1508
+ Ind = [self._gens.index(g) for g in other._gens]
1509
+ if sorted(Ind) != Ind:
1510
+ return None
1511
+ # OK, other merges into self. Now, choose the default dense implementation,
1512
+ # unless both functors refer to the sparse implementation
1513
+ if self._imple != other._imple:
1514
+ return InfinitePolynomialFunctor(self._gens, self._order, 'dense')
1515
+ return self
1516
+ return None
1517
+
1518
+ def expand(self):
1519
+ """
1520
+ Decompose the functor `F` into sub-functors, whose product returns `F`.
1521
+
1522
+ EXAMPLES::
1523
+
1524
+ sage: A = InfinitePolynomialRing(QQ, ['x','y'], order='degrevlex')
1525
+ sage: F = A.construction()[0]; F
1526
+ InfPoly{[x,y], "degrevlex", "dense"}
1527
+ sage: F.expand()
1528
+ [InfPoly{[y], "degrevlex", "dense"}, InfPoly{[x], "degrevlex", "dense"}]
1529
+ sage: A = InfinitePolynomialRing(QQ, ['x','y','z'], order='degrevlex')
1530
+ sage: F = A.construction()[0]; F
1531
+ InfPoly{[x,y,z], "degrevlex", "dense"}
1532
+ sage: F.expand()
1533
+ [InfPoly{[z], "degrevlex", "dense"},
1534
+ InfPoly{[y], "degrevlex", "dense"},
1535
+ InfPoly{[x], "degrevlex", "dense"}]
1536
+ sage: prod(F.expand())==F
1537
+ True
1538
+ """
1539
+ if len(self._gens) == 1:
1540
+ return [self]
1541
+ return [InfinitePolynomialFunctor((x,), self._order, self._imple)
1542
+ for x in reversed(self._gens)]
1543
+
1544
+
1545
+ class MatrixFunctor(ConstructionFunctor):
1546
+ """
1547
+ A construction functor for matrices over rings.
1548
+
1549
+ EXAMPLES::
1550
+
1551
+ sage: # needs sage.modules
1552
+ sage: MS = MatrixSpace(ZZ, 2, 3)
1553
+ sage: F = MS.construction()[0]; F
1554
+ MatrixFunctor
1555
+ sage: MS = MatrixSpace(ZZ, 2)
1556
+ sage: F = MS.construction()[0]; F
1557
+ MatrixFunctor
1558
+ sage: P.<x,y> = QQ[]
1559
+ sage: R = F(P); R
1560
+ Full MatrixSpace of 2 by 2 dense matrices
1561
+ over Multivariate Polynomial Ring in x, y over Rational Field
1562
+ sage: f = P.hom([x+y, x-y], P); F(f)
1563
+ Ring endomorphism
1564
+ of Full MatrixSpace of 2 by 2 dense matrices
1565
+ over Multivariate Polynomial Ring in x, y over Rational Field
1566
+ Defn: Induced from base ring by
1567
+ Ring endomorphism
1568
+ of Multivariate Polynomial Ring in x, y over Rational Field
1569
+ Defn: x |--> x + y
1570
+ y |--> x - y
1571
+ sage: M = R([x, y, x*y, x + y])
1572
+ sage: F(f)(M)
1573
+ [ x + y x - y]
1574
+ [x^2 - y^2 2*x]
1575
+ """
1576
+ rank = 10
1577
+
1578
+ def __init__(self, nrows, ncols, is_sparse=False):
1579
+ """
1580
+ TESTS::
1581
+
1582
+ sage: # needs sage.modules
1583
+ sage: from sage.categories.pushout import MatrixFunctor
1584
+ sage: F = MatrixFunctor(2, 3)
1585
+ sage: F == MatrixSpace(ZZ, 2, 3).construction()[0]
1586
+ True
1587
+ sage: F.codomain()
1588
+ Category of commutative additive groups
1589
+ sage: R = MatrixSpace(ZZ, 2, 2).construction()[0]
1590
+ sage: R.codomain()
1591
+ Category of rings
1592
+ sage: F(ZZ)
1593
+ Full MatrixSpace of 2 by 3 dense matrices over Integer Ring
1594
+ sage: F(ZZ) in F.codomain()
1595
+ True
1596
+ sage: R(GF(2))
1597
+ Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 2
1598
+ sage: R(GF(2)) in R.codomain()
1599
+ True
1600
+ """
1601
+ if nrows == ncols:
1602
+ Functor.__init__(self, Rings(), Rings()) # Algebras() takes a base ring
1603
+ else:
1604
+ # Functor.__init__(self, Rings(), MatrixAlgebras()) # takes a base ring
1605
+ Functor.__init__(self, Rings(), CommutativeAdditiveGroups()) # not a nice solution, but the best we can do.
1606
+ self.nrows = nrows
1607
+ self.ncols = ncols
1608
+ self.is_sparse = is_sparse
1609
+
1610
+ def _apply_functor(self, R):
1611
+ """
1612
+ Apply the functor to an object of ``self``'s domain.
1613
+
1614
+ TESTS:
1615
+
1616
+ The following is a test against a bug discussed at :issue:`8800`::
1617
+
1618
+ sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
1619
+ sage: F(RR) # indirect doctest # needs sage.modules
1620
+ Full MatrixSpace of 2 by 3 dense matrices over Real Field with 53 bits of precision
1621
+ sage: F(RR) in F.codomain() # needs sage.modules
1622
+ True
1623
+ """
1624
+ from sage.matrix.matrix_space import MatrixSpace
1625
+ return MatrixSpace(R, self.nrows, self.ncols, sparse=self.is_sparse)
1626
+
1627
+ def __eq__(self, other):
1628
+ """
1629
+ TESTS::
1630
+
1631
+ sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
1632
+ sage: F == loads(dumps(F)) # needs sage.modules
1633
+ True
1634
+ sage: F == MatrixSpace(ZZ, 2, 2).construction()[0] # needs sage.modules
1635
+ False
1636
+ """
1637
+ if isinstance(other, MatrixFunctor):
1638
+ return (self.nrows == other.nrows and self.ncols == other.ncols)
1639
+ return False
1640
+
1641
+ def __ne__(self, other):
1642
+ """
1643
+ Check whether ``self`` is not equal to ``other``.
1644
+
1645
+ EXAMPLES::
1646
+
1647
+ sage: F = MatrixSpace(ZZ, 2, 3).construction()[0] # needs sage.modules
1648
+ sage: F != loads(dumps(F)) # needs sage.modules
1649
+ False
1650
+ sage: F != MatrixSpace(ZZ, 2, 2).construction()[0] # needs sage.modules
1651
+ True
1652
+ """
1653
+ return not (self == other)
1654
+
1655
+ __hash__ = ConstructionFunctor.__hash__
1656
+
1657
+ def merge(self, other):
1658
+ """
1659
+ Merging is only happening if both functors are matrix functors of the same dimension.
1660
+
1661
+ The result is sparse if and only if both given functors are sparse.
1662
+
1663
+ EXAMPLES::
1664
+
1665
+ sage: # needs sage.modules
1666
+ sage: F1 = MatrixSpace(ZZ, 2, 2).construction()[0]
1667
+ sage: F2 = MatrixSpace(ZZ, 2, 3).construction()[0]
1668
+ sage: F3 = MatrixSpace(ZZ, 2, 2, sparse=True).construction()[0]
1669
+ sage: F1.merge(F2)
1670
+ sage: F1.merge(F3)
1671
+ MatrixFunctor
1672
+ sage: F13 = F1.merge(F3)
1673
+ sage: F13.is_sparse
1674
+ False
1675
+ sage: F1.is_sparse
1676
+ False
1677
+ sage: F3.is_sparse
1678
+ True
1679
+ sage: F3.merge(F3).is_sparse
1680
+ True
1681
+ """
1682
+ if self != other:
1683
+ return None
1684
+ else:
1685
+ return MatrixFunctor(self.nrows, self.ncols, self.is_sparse and other.is_sparse)
1686
+
1687
+
1688
+ class LaurentPolynomialFunctor(ConstructionFunctor):
1689
+ """
1690
+ Construction functor for Laurent polynomial rings.
1691
+
1692
+ EXAMPLES::
1693
+
1694
+ sage: L.<t> = LaurentPolynomialRing(ZZ)
1695
+ sage: F = L.construction()[0]
1696
+ sage: F
1697
+ LaurentPolynomialFunctor
1698
+ sage: F(QQ)
1699
+ Univariate Laurent Polynomial Ring in t over Rational Field
1700
+ sage: K.<x> = LaurentPolynomialRing(ZZ)
1701
+ sage: F(K)
1702
+ Univariate Laurent Polynomial Ring in t
1703
+ over Univariate Laurent Polynomial Ring in x over Integer Ring
1704
+ sage: P.<x,y> = ZZ[]
1705
+ sage: f = P.hom([x + 2*y, 3*x - y],P)
1706
+ sage: F(f)
1707
+ Ring endomorphism of Univariate Laurent Polynomial Ring in t
1708
+ over Multivariate Polynomial Ring in x, y over Integer Ring
1709
+ Defn: Induced from base ring by
1710
+ Ring endomorphism of Multivariate Polynomial Ring in x, y over Integer Ring
1711
+ Defn: x |--> x + 2*y
1712
+ y |--> 3*x - y
1713
+ sage: F(f)(x*F(P).gen()^-2 + y*F(P).gen()^3)
1714
+ (x + 2*y)*t^-2 + (3*x - y)*t^3
1715
+ """
1716
+ rank = 9
1717
+
1718
+ def __init__(self, var, multi_variate=False):
1719
+ """
1720
+ INPUT:
1721
+
1722
+ - ``var`` -- string or list of strings
1723
+ - ``multi_variate`` -- boolean (default: ``False``); if ``var`` is a
1724
+ string and ``True`` otherwise: If ``True``, application to a Laurent
1725
+ polynomial ring yields a multivariate Laurent polynomial ring.
1726
+
1727
+ TESTS::
1728
+
1729
+ sage: from sage.categories.pushout import LaurentPolynomialFunctor
1730
+ sage: F1 = LaurentPolynomialFunctor('t')
1731
+ sage: F2 = LaurentPolynomialFunctor('s', multi_variate=True)
1732
+ sage: F3 = LaurentPolynomialFunctor(['s','t'])
1733
+ sage: F1(F2(QQ))
1734
+ Univariate Laurent Polynomial Ring in t over
1735
+ Univariate Laurent Polynomial Ring in s over Rational Field
1736
+ sage: F2(F1(QQ)) # needs sage.modules
1737
+ Multivariate Laurent Polynomial Ring in t, s over Rational Field
1738
+ sage: F3(QQ) # needs sage.modules
1739
+ Multivariate Laurent Polynomial Ring in s, t over Rational Field
1740
+ """
1741
+ Functor.__init__(self, Rings(), Rings())
1742
+ if not isinstance(var, (str, tuple, list)):
1743
+ raise TypeError("variable name or list of variable names expected")
1744
+ self.var = var
1745
+ self.multi_variate = multi_variate or not isinstance(var, str)
1746
+
1747
+ def _apply_functor(self, R):
1748
+ """
1749
+ Apply the functor to an object of ``self``'s domain.
1750
+
1751
+ TESTS::
1752
+
1753
+ sage: from sage.categories.pushout import LaurentPolynomialFunctor
1754
+ sage: F1 = LaurentPolynomialFunctor('t')
1755
+ sage: F2 = LaurentPolynomialFunctor('s', multi_variate=True)
1756
+ sage: F3 = LaurentPolynomialFunctor(['s','t'])
1757
+ sage: F1(F2(QQ)) # indirect doctest
1758
+ Univariate Laurent Polynomial Ring in t over
1759
+ Univariate Laurent Polynomial Ring in s over Rational Field
1760
+ sage: F2(F1(QQ)) # needs sage.modules
1761
+ Multivariate Laurent Polynomial Ring in t, s over Rational Field
1762
+ sage: F3(QQ) # needs sage.modules
1763
+ Multivariate Laurent Polynomial Ring in s, t over Rational Field
1764
+ """
1765
+ from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
1766
+ from sage.rings.polynomial.laurent_polynomial_ring_base import (
1767
+ LaurentPolynomialRing_generic,
1768
+ )
1769
+ if self.multi_variate and isinstance(R, LaurentPolynomialRing_generic):
1770
+ return LaurentPolynomialRing(R.base_ring(), list(R.variable_names()) + [self.var])
1771
+ else:
1772
+ return LaurentPolynomialRing(R, self.var)
1773
+
1774
+ def __eq__(self, other):
1775
+ """
1776
+ TESTS::
1777
+
1778
+ sage: from sage.categories.pushout import LaurentPolynomialFunctor
1779
+ sage: F1 = LaurentPolynomialFunctor('t')
1780
+ sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
1781
+ sage: F3 = LaurentPolynomialFunctor(['s','t'])
1782
+ sage: F1 == F2
1783
+ True
1784
+ sage: F1 == loads(dumps(F1))
1785
+ True
1786
+ sage: F1 == F3
1787
+ False
1788
+ sage: F1 == QQ.construction()[0]
1789
+ False
1790
+ """
1791
+ if isinstance(other, LaurentPolynomialFunctor):
1792
+ return self.var == other.var
1793
+ return False
1794
+
1795
+ def __ne__(self, other):
1796
+ """
1797
+ Check whether ``self`` is not equal to ``other``.
1798
+
1799
+ EXAMPLES::
1800
+
1801
+ sage: from sage.categories.pushout import LaurentPolynomialFunctor
1802
+ sage: F1 = LaurentPolynomialFunctor('t')
1803
+ sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
1804
+ sage: F3 = LaurentPolynomialFunctor(['s','t'])
1805
+ sage: F1 != F2
1806
+ False
1807
+ sage: F1 != loads(dumps(F1))
1808
+ False
1809
+ sage: F1 != F3
1810
+ True
1811
+ sage: F1 != QQ.construction()[0]
1812
+ True
1813
+ """
1814
+ return not (self == other)
1815
+
1816
+ __hash__ = ConstructionFunctor.__hash__
1817
+
1818
+ def merge(self, other):
1819
+ """
1820
+ Two Laurent polynomial construction functors merge if the variable names coincide.
1821
+
1822
+ The result is multivariate if one of the arguments is multivariate.
1823
+
1824
+ EXAMPLES::
1825
+
1826
+ sage: from sage.categories.pushout import LaurentPolynomialFunctor
1827
+ sage: F1 = LaurentPolynomialFunctor('t')
1828
+ sage: F2 = LaurentPolynomialFunctor('t', multi_variate=True)
1829
+ sage: F1.merge(F2)
1830
+ LaurentPolynomialFunctor
1831
+ sage: F1.merge(F2)(LaurentPolynomialRing(GF(2), 'a')) # needs sage.modules
1832
+ Multivariate Laurent Polynomial Ring in a, t over Finite Field of size 2
1833
+ sage: F1.merge(F1)(LaurentPolynomialRing(GF(2), 'a'))
1834
+ Univariate Laurent Polynomial Ring in t over
1835
+ Univariate Laurent Polynomial Ring in a over Finite Field of size 2
1836
+ """
1837
+ if self == other or isinstance(other, PolynomialFunctor) and self.var == other.var:
1838
+ return LaurentPolynomialFunctor(self.var, (self.multi_variate or other.multi_variate))
1839
+ else:
1840
+ return None
1841
+
1842
+
1843
+ class VectorFunctor(ConstructionFunctor):
1844
+ """
1845
+ A construction functor for free modules over commutative rings.
1846
+
1847
+ EXAMPLES::
1848
+
1849
+ sage: # needs sage.modules
1850
+ sage: F = (ZZ^3).construction()[0]
1851
+ sage: F
1852
+ VectorFunctor
1853
+ sage: F(GF(2)['t']) # needs sage.libs.ntl
1854
+ Ambient free module of rank 3
1855
+ over the principal ideal domain Univariate Polynomial Ring in t
1856
+ over Finite Field of size 2 (using ...)
1857
+ """
1858
+ rank = 10 # ranking of functor, not rank of module.
1859
+ # This coincides with the rank of the matrix construction functor, but this is OK since they cannot both be applied in any order
1860
+
1861
+ def __init__(self, n=None, is_sparse=False, inner_product_matrix=None, *,
1862
+ with_basis='standard', basis_keys=None, name_mapping=None, latex_name_mapping=None):
1863
+ """
1864
+ INPUT:
1865
+
1866
+ - ``n`` -- the rank of the to-be-created modules (nonnegative integer)
1867
+ - ``is_sparse`` -- boolean (default: ``False``); create sparse
1868
+ implementation of modules
1869
+ - ``inner_product_matrix`` -- ``n`` by ``n`` matrix, used to compute inner products in the
1870
+ to-be-created modules
1871
+ - ``name_mapping``, ``latex_name_mapping`` -- Dictionaries from base rings to names
1872
+ - other keywords: see :func:`~sage.modules.free_module.FreeModule`
1873
+
1874
+ TESTS::
1875
+
1876
+ sage: # needs sage.modules
1877
+ sage: from sage.categories.pushout import VectorFunctor
1878
+ sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
1879
+ sage: F1.domain()
1880
+ Category of commutative rings
1881
+ sage: F1.codomain()
1882
+ Category of commutative additive groups
1883
+ sage: M1 = F1(ZZ)
1884
+ sage: M1.is_sparse()
1885
+ False
1886
+ sage: v = M1([3, 2, 1])
1887
+ sage: v * Matrix(3, 3, range(9)) * v.column()
1888
+ (96)
1889
+ sage: v.inner_product(v)
1890
+ 96
1891
+ sage: F2 = VectorFunctor(3, is_sparse=True)
1892
+ sage: M2 = F2(QQ); M2; M2.is_sparse()
1893
+ Sparse vector space of dimension 3 over Rational Field
1894
+ True
1895
+ """
1896
+ # Functor.__init__(self, Rings(), FreeModules()) # FreeModules() takes a base ring
1897
+ # Functor.__init__(self, Objects(), Objects()) # Object() makes no sense, since FreeModule raises an error, e.g., on Set(['a',1]).
1898
+ # FreeModule requires a commutative ring. Thus, we have
1899
+ Functor.__init__(self, CommutativeRings(), CommutativeAdditiveGroups())
1900
+ self.n = n
1901
+ self.is_sparse = is_sparse
1902
+ self.inner_product_matrix = inner_product_matrix
1903
+ self.with_basis = with_basis
1904
+ self.basis_keys = basis_keys
1905
+ if name_mapping is None:
1906
+ name_mapping = {}
1907
+ self.name_mapping = name_mapping
1908
+ if latex_name_mapping is None:
1909
+ latex_name_mapping = {}
1910
+ self.latex_name_mapping = latex_name_mapping
1911
+
1912
+ def _apply_functor(self, R):
1913
+ r"""
1914
+ Apply the functor to an object of ``self``'s domain.
1915
+
1916
+ TESTS::
1917
+
1918
+ sage: # needs sage.modules
1919
+ sage: from sage.categories.pushout import VectorFunctor, pushout
1920
+ sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
1921
+ sage: M1 = F1(ZZ) # indirect doctest
1922
+ sage: M1.is_sparse()
1923
+ False
1924
+ sage: v = M1([3, 2, 1])
1925
+ sage: v * Matrix(3, 3, range(9)) * v.column()
1926
+ (96)
1927
+ sage: v.inner_product(v)
1928
+ 96
1929
+ sage: F2 = VectorFunctor(3, is_sparse=True)
1930
+ sage: M2 = F2(QQ); M2; M2.is_sparse()
1931
+ Sparse vector space of dimension 3 over Rational Field
1932
+ True
1933
+ sage: v = M2([3, 2, 1])
1934
+ sage: v.inner_product(v)
1935
+ 14
1936
+
1937
+ sage: # needs sage.modules
1938
+ sage: M = FreeModule(ZZ, 4, with_basis=None, name='M')
1939
+ sage: latex(M)
1940
+ M
1941
+ sage: M_QQ = pushout(M, QQ)
1942
+ sage: latex(M_QQ)
1943
+ M \otimes \Bold{Q}
1944
+ """
1945
+ from sage.modules.free_module import FreeModule
1946
+ name = self.name_mapping.get(R, None)
1947
+ latex_name = self.latex_name_mapping.get(R, None)
1948
+ if name is None:
1949
+ for name in self.name_mapping.values():
1950
+ name = f'{name}_base_ext'
1951
+ break
1952
+ if latex_name is None:
1953
+ from sage.misc.latex import latex
1954
+ for latex_name in self.latex_name_mapping.values():
1955
+ latex_name = fr'{latex_name} \otimes {latex(R)}'
1956
+ break
1957
+ if name is None and latex_name is None:
1958
+ return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix,
1959
+ with_basis=self.with_basis, basis_keys=self.basis_keys)
1960
+ return FreeModule(R, self.n, sparse=self.is_sparse, inner_product_matrix=self.inner_product_matrix,
1961
+ with_basis=self.with_basis, basis_keys=self.basis_keys, name=name, latex_name=latex_name)
1962
+
1963
+ def _apply_functor_to_morphism(self, f):
1964
+ """
1965
+ This is not implemented yet.
1966
+
1967
+ TESTS::
1968
+
1969
+ sage: F = (ZZ^3).construction()[0] # needs sage.modules
1970
+ sage: P.<x,y> = ZZ[]
1971
+ sage: f = P.hom([x + 2*y, 3*x - y], P)
1972
+ sage: F(f) # indirect doctest # needs sage.modules
1973
+ Traceback (most recent call last):
1974
+ ...
1975
+ NotImplementedError: Cannot create induced morphisms of free modules yet
1976
+ """
1977
+ # TODO: Implement this!
1978
+ raise NotImplementedError("Cannot create induced morphisms of free modules yet")
1979
+
1980
+ def __eq__(self, other):
1981
+ """
1982
+ The rank and the inner product matrix are compared.
1983
+
1984
+ TESTS::
1985
+
1986
+ sage: # needs sage.modules
1987
+ sage: from sage.categories.pushout import VectorFunctor
1988
+ sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
1989
+ sage: F2 = (ZZ^3).construction()[0]
1990
+ sage: F1 == F2
1991
+ False
1992
+ sage: F1(QQ) == F2(QQ)
1993
+ False
1994
+ sage: F1 == loads(dumps(F1))
1995
+ True
1996
+ """
1997
+ if isinstance(other, VectorFunctor):
1998
+ return (self.n == other.n and
1999
+ self.inner_product_matrix == other.inner_product_matrix and
2000
+ self.with_basis == other.with_basis and
2001
+ self.basis_keys == other.basis_keys and
2002
+ self.name_mapping == other.name_mapping and
2003
+ self.latex_name_mapping == other.latex_name_mapping)
2004
+ return False
2005
+
2006
+ def __ne__(self, other):
2007
+ """
2008
+ Check whether ``self`` is not equal to ``other``.
2009
+
2010
+ EXAMPLES::
2011
+
2012
+ sage: # needs sage.modules
2013
+ sage: from sage.categories.pushout import VectorFunctor
2014
+ sage: F1 = VectorFunctor(3, inner_product_matrix=Matrix(3, 3, range(9)))
2015
+ sage: F2 = (ZZ^3).construction()[0]
2016
+ sage: F1 != F2
2017
+ True
2018
+ sage: F1(QQ) != F2(QQ)
2019
+ True
2020
+ sage: F1 != loads(dumps(F1))
2021
+ False
2022
+ """
2023
+ return not (self == other)
2024
+
2025
+ __hash__ = ConstructionFunctor.__hash__
2026
+
2027
+ def merge(self, other):
2028
+ """
2029
+ Two constructors of free modules merge, if the module ranks and the inner products coincide. If both
2030
+ have explicitly given inner product matrices, they must coincide as well.
2031
+
2032
+ EXAMPLES:
2033
+
2034
+ Two modules without explicitly given inner product allow coercion::
2035
+
2036
+ sage: M1 = QQ^3 # needs sage.modules
2037
+ sage: P.<t> = ZZ[]
2038
+ sage: M2 = FreeModule(P, 3) # needs sage.modules
2039
+ sage: M1([1,1/2,1/3]) + M2([t,t^2+t,3]) # indirect doctest # needs sage.modules
2040
+ (t + 1, t^2 + t + 1/2, 10/3)
2041
+
2042
+ If only one summand has an explicit inner product, the result will be provided
2043
+ with it::
2044
+
2045
+ sage: M3 = FreeModule(P, 3, inner_product_matrix=Matrix(3, 3, range(9))) # needs sage.modules
2046
+ sage: M1([1,1/2,1/3]) + M3([t,t^2+t,3]) # needs sage.modules
2047
+ (t + 1, t^2 + t + 1/2, 10/3)
2048
+ sage: (M1([1,1/2,1/3]) + M3([t,t^2+t,3])).parent().inner_product_matrix() # needs sage.modules
2049
+ [0 1 2]
2050
+ [3 4 5]
2051
+ [6 7 8]
2052
+
2053
+ If both summands have an explicit inner product (even if it is the standard
2054
+ inner product), then the products must coincide. The only difference between
2055
+ ``M1`` and ``M4`` in the following example is the fact that the default
2056
+ inner product was *explicitly* requested for ``M4``. It is therefore not
2057
+ possible to coerce with a different inner product::
2058
+
2059
+ sage: # needs sage.modules
2060
+ sage: M4 = FreeModule(QQ, 3, inner_product_matrix=Matrix(3, 3, 1))
2061
+ sage: M4 == M1
2062
+ True
2063
+ sage: M4.inner_product_matrix() == M1.inner_product_matrix()
2064
+ True
2065
+ sage: M4([1,1/2,1/3]) + M3([t,t^2+t,3]) # indirect doctest
2066
+ Traceback (most recent call last):
2067
+ ...
2068
+ TypeError: unsupported operand parent(s) for +:
2069
+ 'Ambient quadratic space of dimension 3 over Rational Field
2070
+ Inner product matrix:
2071
+ [1 0 0]
2072
+ [0 1 0]
2073
+ [0 0 1]' and
2074
+ 'Ambient free quadratic module of rank 3 over the integral domain
2075
+ Univariate Polynomial Ring in t over Integer Ring
2076
+ Inner product matrix:
2077
+ [0 1 2]
2078
+ [3 4 5]
2079
+ [6 7 8]'
2080
+
2081
+ Names are removed when they conflict::
2082
+
2083
+ sage: # needs sage.modules
2084
+ sage: from sage.categories.pushout import VectorFunctor, pushout
2085
+ sage: M_ZZx = FreeModule(ZZ['x'], 4, with_basis=None, name='M_ZZx')
2086
+ sage: N_ZZx = FreeModule(ZZ['x'], 4, with_basis=None, name='N_ZZx')
2087
+ sage: pushout(M_ZZx, QQ)
2088
+ Rank-4 free module M_ZZx_base_ext
2089
+ over the Univariate Polynomial Ring in x over Rational Field
2090
+ sage: pushout(M_ZZx, N_ZZx)
2091
+ Rank-4 free module
2092
+ over the Univariate Polynomial Ring in x over Integer Ring
2093
+ sage: pushout(pushout(M_ZZx, N_ZZx), QQ)
2094
+ Rank-4 free module
2095
+ over the Univariate Polynomial Ring in x over Rational Field
2096
+ """
2097
+ if not isinstance(other, VectorFunctor):
2098
+ return None
2099
+
2100
+ if self.with_basis != other.with_basis:
2101
+ return None
2102
+ else:
2103
+ with_basis = self.with_basis
2104
+
2105
+ if self.basis_keys != other.basis_keys:
2106
+ # TODO: If both are enumerated families, should we try to take the union of the families?
2107
+ return None
2108
+ else:
2109
+ basis_keys = self.basis_keys
2110
+
2111
+ is_sparse = self.is_sparse and other.is_sparse
2112
+
2113
+ if self.inner_product_matrix is None:
2114
+ inner_product_matrix = other.inner_product_matrix
2115
+ elif other.inner_product_matrix is None:
2116
+ inner_product_matrix = self.inner_product_matrix
2117
+ elif self.inner_product_matrix != other.inner_product_matrix:
2118
+ # At this point, we know that the user wants to take care of the inner product.
2119
+ # So, we only merge if both coincide:
2120
+ return None
2121
+ else:
2122
+ inner_product_matrix = None
2123
+
2124
+ if self.n != other.n:
2125
+ return None
2126
+ else:
2127
+ n = self.n
2128
+
2129
+ name_mapping = {}
2130
+ for base_ring, name in self.name_mapping.items():
2131
+ try:
2132
+ other_name = other.name_mapping[base_ring]
2133
+ except KeyError:
2134
+ name_mapping[base_ring] = name
2135
+ else:
2136
+ if name == other_name:
2137
+ name_mapping[base_ring] = name
2138
+
2139
+ latex_name_mapping = {}
2140
+ for base_ring, latex_name in self.latex_name_mapping.items():
2141
+ try:
2142
+ other_latex_name = other.latex_name_mapping[base_ring]
2143
+ except KeyError:
2144
+ latex_name_mapping[base_ring] = latex_name
2145
+ else:
2146
+ if latex_name == other_latex_name:
2147
+ latex_name_mapping[base_ring] = latex_name
2148
+
2149
+ return VectorFunctor(n, is_sparse, inner_product_matrix,
2150
+ with_basis=with_basis, basis_keys=basis_keys,
2151
+ name_mapping=name_mapping, latex_name_mapping=latex_name_mapping)
2152
+
2153
+
2154
+ class SubspaceFunctor(ConstructionFunctor):
2155
+ """
2156
+ Constructing a subspace of an ambient free module, given by a basis.
2157
+
2158
+ .. NOTE::
2159
+
2160
+ This construction functor keeps track of the basis. It can only be
2161
+ applied to free modules into which this basis coerces.
2162
+
2163
+ EXAMPLES::
2164
+
2165
+ sage: # needs sage.modules
2166
+ sage: M = ZZ^3
2167
+ sage: S = M.submodule([(1,2,3), (4,5,6)]); S
2168
+ Free module of degree 3 and rank 2 over Integer Ring
2169
+ Echelon basis matrix:
2170
+ [1 2 3]
2171
+ [0 3 6]
2172
+ sage: F = S.construction()[0]
2173
+ sage: F(GF(2)^3)
2174
+ Vector space of degree 3 and dimension 2 over Finite Field of size 2
2175
+ User basis matrix:
2176
+ [1 0 1]
2177
+ [0 1 0]
2178
+ """
2179
+ rank = 11 # ranking of functor, not rank of module
2180
+
2181
+ # The subspace construction returns an object admitting a coercion
2182
+ # map into the original, not vice versa.
2183
+ coercion_reversed = True
2184
+
2185
+ def __init__(self, basis):
2186
+ """
2187
+ INPUT:
2188
+
2189
+ - ``basis`` -- list of elements of a free module
2190
+
2191
+ TESTS::
2192
+
2193
+ sage: from sage.categories.pushout import SubspaceFunctor
2194
+ sage: M = ZZ^3 # needs sage.modules
2195
+ sage: F = SubspaceFunctor([M([1,2,3]), M([4,5,6])]) # needs sage.modules
2196
+ sage: F(GF(5)^3) # needs sage.modules
2197
+ Vector space of degree 3 and dimension 2 over Finite Field of size 5
2198
+ User basis matrix:
2199
+ [1 2 3]
2200
+ [4 0 1]
2201
+ """
2202
+ # Functor.__init__(self, FreeModules(), FreeModules()) # takes a base ring
2203
+ # Functor.__init__(self, Objects(), Objects()) # is too general
2204
+ # It seems that the category of commutative additive groups
2205
+ # currently is the smallest base ring free category that
2206
+ # contains in- and output
2207
+ Functor.__init__(self, CommutativeAdditiveGroups(), CommutativeAdditiveGroups())
2208
+ self.basis = basis
2209
+
2210
+ def _apply_functor(self, ambient):
2211
+ """
2212
+ Apply the functor to an object of ``self``'s domain.
2213
+
2214
+ TESTS::
2215
+
2216
+ sage: # needs sage.modules
2217
+ sage: M = ZZ^3
2218
+ sage: S = M.submodule([(1,2,3), (4,5,6)]); S
2219
+ Free module of degree 3 and rank 2 over Integer Ring
2220
+ Echelon basis matrix:
2221
+ [1 2 3]
2222
+ [0 3 6]
2223
+ sage: F = S.construction()[0]
2224
+ sage: F(GF(2)^3) # indirect doctest
2225
+ Vector space of degree 3 and dimension 2 over Finite Field of size 2
2226
+ User basis matrix:
2227
+ [1 0 1]
2228
+ [0 1 0]
2229
+ """
2230
+ return ambient.span_of_basis(self.basis)
2231
+
2232
+ def _apply_functor_to_morphism(self, f):
2233
+ """
2234
+ This is not implemented yet.
2235
+
2236
+ TESTS::
2237
+
2238
+ sage: # needs sage.modules
2239
+ sage: F = (ZZ^3).span([(1,2,3), (4,5,6)]).construction()[0]
2240
+ sage: P.<x,y> = ZZ[]
2241
+ sage: f = P.hom([x + 2*y, 3*x - y],P)
2242
+ sage: F(f) # indirect doctest
2243
+ Traceback (most recent call last):
2244
+ ...
2245
+ NotImplementedError: Cannot create morphisms of free sub-modules yet
2246
+ """
2247
+ raise NotImplementedError("Cannot create morphisms of free sub-modules yet")
2248
+
2249
+ def __eq__(self, other):
2250
+ """
2251
+ TESTS::
2252
+
2253
+ sage: # needs sage.modules
2254
+ sage: F1 = (GF(5)^3).span([(1,2,3),(4,5,6)]).construction()[0]
2255
+ sage: F2 = (ZZ^3).span([(1,2,3),(4,5,6)]).construction()[0]
2256
+ sage: F3 = (QQ^3).span([(1,2,3),(4,5,6)]).construction()[0]
2257
+ sage: F4 = (ZZ^3).span([(1,0,-1),(0,1,2)]).construction()[0]
2258
+ sage: F1 == loads(dumps(F1))
2259
+ True
2260
+
2261
+ The ``span`` method automatically transforms the given basis into
2262
+ echelon form. The bases look like that::
2263
+
2264
+ sage: # needs sage.modules
2265
+ sage: F1.basis
2266
+ [(1, 0, 4), (0, 1, 2)]
2267
+ sage: F2.basis
2268
+ [(1, 2, 3), (0, 3, 6)]
2269
+ sage: F3.basis
2270
+ [(1, 0, -1), (0, 1, 2)]
2271
+ sage: F4.basis
2272
+ [(1, 0, -1), (0, 1, 2)]
2273
+
2274
+
2275
+ The basis of ``F2`` is modulo 5 different from the other bases.
2276
+ So, we have::
2277
+
2278
+ sage: F1 != F2 != F3 # needs sage.modules
2279
+ True
2280
+
2281
+ The bases of ``F1``, ``F3`` and ``F4`` are the same modulo 5; however,
2282
+ there is no coercion from ``QQ^3`` to ``GF(5)^3``. Therefore, we have::
2283
+
2284
+ sage: F1 == F3 # needs sage.modules
2285
+ False
2286
+
2287
+ But there are coercions from ``ZZ^3`` to ``QQ^3`` and ``GF(5)^3``, thus::
2288
+
2289
+ sage: F1 == F4 == F3 # needs sage.modules
2290
+ True
2291
+ """
2292
+ if not isinstance(other, SubspaceFunctor):
2293
+ return False
2294
+
2295
+ # since comparing the basis involves constructing the pushout
2296
+ # of the ambient module, we cannot do:
2297
+ # c = (self.basis == other.basis)
2298
+ # Instead, we only test whether there are coercions.
2299
+ L = self.basis.universe()
2300
+ R = other.basis.universe()
2301
+ c = (L == R)
2302
+ if L.has_coerce_map_from(R):
2303
+ return tuple(self.basis) == tuple(L(x) for x in other.basis)
2304
+ elif R.has_coerce_map_from(L):
2305
+ return tuple(other.basis) == tuple(R(x) for x in self.basis)
2306
+ return c
2307
+
2308
+ def __ne__(self, other):
2309
+ """
2310
+ Check whether ``self`` is not equal to ``other``.
2311
+
2312
+ EXAMPLES::
2313
+
2314
+ sage: F1 = (GF(5)^3).span([(1,2,3),(4,5,6)]).construction()[0] # needs sage.modules
2315
+ sage: F1 != loads(dumps(F1)) # needs sage.modules
2316
+ False
2317
+ """
2318
+ return not (self == other)
2319
+
2320
+ __hash__ = ConstructionFunctor.__hash__
2321
+
2322
+ def merge(self, other):
2323
+ """
2324
+ Two Subspace Functors are merged into a construction functor of the sum of two subspaces.
2325
+
2326
+ EXAMPLES::
2327
+
2328
+ sage: # needs sage.modules
2329
+ sage: M = GF(5)^3
2330
+ sage: S1 = M.submodule([(1,2,3),(4,5,6)])
2331
+ sage: S2 = M.submodule([(2,2,3)])
2332
+ sage: F1 = S1.construction()[0]
2333
+ sage: F2 = S2.construction()[0]
2334
+ sage: F1.merge(F2)
2335
+ SubspaceFunctor
2336
+ sage: F1.merge(F2)(GF(5)^3) == S1 + S2
2337
+ True
2338
+ sage: F1.merge(F2)(GF(5)['t']^3)
2339
+ Free module of degree 3 and rank 3
2340
+ over Univariate Polynomial Ring in t over Finite Field of size 5
2341
+ User basis matrix:
2342
+ [1 0 0]
2343
+ [0 1 0]
2344
+ [0 0 1]
2345
+
2346
+ TESTS::
2347
+
2348
+ sage: # needs sage.modules
2349
+ sage: P.<t> = ZZ[]
2350
+ sage: S1 = (ZZ^3).submodule([(1,2,3), (4,5,6)])
2351
+ sage: S2 = (Frac(P)^3).submodule([(t,t^2,t^3+1), (4*t,0,1)])
2352
+ sage: v = S1([0,3,6]) + S2([2,0,1/(2*t)]); v # indirect doctest
2353
+ (2, 3, (-12*t - 1)/(-2*t))
2354
+ sage: v.parent()
2355
+ Vector space of degree 3 and dimension 3
2356
+ over Fraction Field of Univariate Polynomial Ring in t over Integer Ring
2357
+ User basis matrix:
2358
+ [1 0 0]
2359
+ [0 1 0]
2360
+ [0 0 1]
2361
+ """
2362
+ if isinstance(other, SubspaceFunctor):
2363
+ # in order to remove linear dependencies, and in
2364
+ # order to test compatibility of the base rings,
2365
+ # we try to construct a sample submodule
2366
+ if not other.basis:
2367
+ return self
2368
+ if not self.basis:
2369
+ return other
2370
+ try:
2371
+ P = pushout(self.basis[0].parent().ambient_module(),
2372
+ other.basis[0].parent().ambient_module())
2373
+ except CoercionException:
2374
+ return None
2375
+ try:
2376
+ # Use span instead of submodule because we want to
2377
+ # allow denominators.
2378
+ submodule = P.span
2379
+ except AttributeError:
2380
+ return None
2381
+ S = submodule(self.basis + other.basis).echelonized_basis()
2382
+ return SubspaceFunctor(S)
2383
+ else:
2384
+ return None
2385
+
2386
+
2387
+ class FractionField(ConstructionFunctor):
2388
+ """
2389
+ Construction functor for fraction fields.
2390
+
2391
+ EXAMPLES::
2392
+
2393
+ sage: F = QQ.construction()[0]
2394
+ sage: F
2395
+ FractionField
2396
+ sage: F.domain()
2397
+ Category of integral domains
2398
+ sage: F.codomain()
2399
+ Category of fields
2400
+ sage: F(GF(5)) is GF(5)
2401
+ True
2402
+ sage: F(ZZ['t'])
2403
+ Fraction Field of Univariate Polynomial Ring in t over Integer Ring
2404
+ sage: P.<x,y> = QQ[]
2405
+ sage: f = P.hom([x+2*y,3*x-y],P)
2406
+ sage: F(f)
2407
+ Ring endomorphism of
2408
+ Fraction Field of Multivariate Polynomial Ring in x, y over Rational Field
2409
+ Defn: x |--> x + 2*y
2410
+ y |--> 3*x - y
2411
+ sage: F(f)(1/x)
2412
+ 1/(x + 2*y)
2413
+ sage: F == loads(dumps(F))
2414
+ True
2415
+ """
2416
+ rank = 5
2417
+
2418
+ def __init__(self):
2419
+ """
2420
+ TESTS::
2421
+
2422
+ sage: from sage.categories.pushout import FractionField
2423
+ sage: F = FractionField()
2424
+ sage: F
2425
+ FractionField
2426
+ sage: F(ZZ['t'])
2427
+ Fraction Field of Univariate Polynomial Ring in t over Integer Ring
2428
+ """
2429
+ from sage.categories.fields import Fields
2430
+ from sage.categories.integral_domains import IntegralDomains
2431
+ Functor.__init__(self, IntegralDomains(), Fields())
2432
+
2433
+ def _apply_functor(self, R):
2434
+ """
2435
+ Apply the functor to an object of ``self``'s domain.
2436
+
2437
+ TESTS::
2438
+
2439
+ sage: F = QQ.construction()[0]
2440
+ sage: F(GF(5)['t']) # indirect doctest
2441
+ Fraction Field of Univariate Polynomial Ring in t
2442
+ over Finite Field of size 5
2443
+ """
2444
+ return R.fraction_field()
2445
+
2446
+
2447
+ class CompletionFunctor(ConstructionFunctor):
2448
+ """
2449
+ Completion of a ring with respect to a given prime (including infinity).
2450
+
2451
+ EXAMPLES::
2452
+
2453
+ sage: # needs sage.rings.padics
2454
+ sage: R = Zp(5)
2455
+ sage: R
2456
+ 5-adic Ring with capped relative precision 20
2457
+ sage: F1 = R.construction()[0]
2458
+ sage: F1
2459
+ Completion[5, prec=20]
2460
+ sage: F1(ZZ) is R
2461
+ True
2462
+ sage: F1(QQ)
2463
+ 5-adic Field with capped relative precision 20
2464
+
2465
+ sage: F2 = RR.construction()[0]
2466
+ sage: F2
2467
+ Completion[+Infinity, prec=53]
2468
+ sage: F2(QQ) is RR
2469
+ True
2470
+
2471
+ sage: P.<x> = ZZ[]
2472
+ sage: Px = P.completion(x) # currently the only implemented completion of P
2473
+ sage: Px
2474
+ Power Series Ring in x over Integer Ring
2475
+ sage: F3 = Px.construction()[0]
2476
+ sage: F3(GF(3)['x'])
2477
+ Power Series Ring in x over Finite Field of size 3
2478
+
2479
+ TESTS::
2480
+
2481
+ sage: # needs sage.rings.padics
2482
+ sage: R1.<a> = Zp(5, prec=20)[]
2483
+ sage: R2 = Qp(5, prec=40)
2484
+ sage: R2(1) + a
2485
+ (1 + O(5^20))*a + 1 + O(5^40)
2486
+ sage: 1/2 + a
2487
+ (1 + O(5^20))*a + 3 + 2*5 + 2*5^2 + 2*5^3 + 2*5^4 + 2*5^5 + 2*5^6 + 2*5^7 + 2*5^8 + 2*5^9 + 2*5^10 + 2*5^11 + 2*5^12 + 2*5^13 + 2*5^14 + 2*5^15 + 2*5^16 + 2*5^17 + 2*5^18 + 2*5^19 + O(5^20)
2488
+ """
2489
+ rank = 4
2490
+ _real_types = ['Interval', 'Ball', 'MPFR', 'RDF', 'RLF', 'RR']
2491
+ _dvr_types = [None, 'fixed-mod', 'floating-point', 'capped-abs', 'capped-rel', 'lattice-cap', 'lattice-float', 'relaxed']
2492
+
2493
+ def __init__(self, p, prec, extras=None):
2494
+ """
2495
+ INPUT:
2496
+
2497
+ - ``p`` -- prime number, the generator of a univariate polynomial ring,
2498
+ or ``+Infinity``
2499
+
2500
+ - ``prec`` -- integer; yielding the precision in bits. Note that
2501
+ if ``p`` is prime then the ``prec`` is the *capped* precision,
2502
+ while it is the *set* precision if ``p`` is ``+Infinity``.
2503
+ In the ``lattice-cap`` precision case, ``prec`` will be a tuple instead.
2504
+
2505
+ - ``extras`` -- dictionary (optional); information on how to print elements, etc.
2506
+ If 'type' is given as a key, the corresponding value should be a string among
2507
+ the following:
2508
+
2509
+ - 'RDF', 'Interval', 'RLF', or 'RR' for completions at infinity
2510
+
2511
+ - 'capped-rel', 'capped-abs', 'fixed-mod', 'lattice-cap' or 'lattice-float'
2512
+ for completions at a finite place or ideal of a DVR.
2513
+
2514
+ TESTS::
2515
+
2516
+ sage: from sage.categories.pushout import CompletionFunctor
2517
+ sage: F1 = CompletionFunctor(5, 100)
2518
+ sage: F1(QQ) # needs sage.rings.padics
2519
+ 5-adic Field with capped relative precision 100
2520
+ sage: F1(ZZ) # needs sage.rings.padics
2521
+ 5-adic Ring with capped relative precision 100
2522
+ sage: F1.type is None
2523
+ True
2524
+ sage: sorted(F1.extras.items())
2525
+ []
2526
+ sage: F2 = RR.construction()[0]
2527
+ sage: F2
2528
+ Completion[+Infinity, prec=53]
2529
+ sage: F2.type # needs sage.rings.real_mpfr
2530
+ 'MPFR'
2531
+ sage: F2.extras # needs sage.rings.real_mpfr
2532
+ {'rnd': 0, 'sci_not': False}
2533
+ """
2534
+ Functor.__init__(self, Rings(), Rings())
2535
+ self.p = p
2536
+ self.prec = prec
2537
+
2538
+ if extras is None:
2539
+ self.extras = {}
2540
+ self.type = None
2541
+ else:
2542
+ self.extras = dict(extras)
2543
+ self.type = self.extras.pop('type', None)
2544
+ from sage.rings.infinity import Infinity
2545
+ if self.p == Infinity:
2546
+ if self.type not in self._real_types:
2547
+ raise ValueError("completion type must be one of %s" % (", ".join(self._real_types)))
2548
+ elif self.type not in self._dvr_types:
2549
+ raise ValueError("completion type must be one of %s" % (", ".join(self._dvr_types[1:])))
2550
+
2551
+ def _repr_(self):
2552
+ """
2553
+ TESTS::
2554
+
2555
+ sage: Zp(7).construction() # indirect doctest # needs sage.rings.padics
2556
+ (Completion[7, prec=20], Integer Ring)
2557
+
2558
+ sage: RR.construction() # indirect doctest
2559
+ (Completion[+Infinity, prec=53], Rational Field)
2560
+ """
2561
+ return 'Completion[%s, prec=%s]' % (self.p, self.prec)
2562
+
2563
+ def _apply_functor(self, R):
2564
+ """
2565
+ Apply the functor to an object of ``self``'s domain.
2566
+
2567
+ TESTS::
2568
+
2569
+ sage: # needs sage.rings.padics
2570
+ sage: R = Zp(5)
2571
+ sage: F1 = R.construction()[0]
2572
+ sage: F1(ZZ) is R # indirect doctest
2573
+ True
2574
+ sage: F1(QQ)
2575
+ 5-adic Field with capped relative precision 20
2576
+ """
2577
+ try:
2578
+ if not self.extras:
2579
+ if self.type is None:
2580
+ try:
2581
+ return R.completion(self.p, self.prec)
2582
+ except TypeError:
2583
+ return R.completion(self.p, self.prec, {})
2584
+ else:
2585
+ return R.completion(self.p, self.prec, {'type': self.type})
2586
+ else:
2587
+ extras = self.extras.copy()
2588
+ if self.type is not None:
2589
+ extras['type'] = self.type
2590
+ return R.completion(self.p, self.prec, extras)
2591
+ except (NotImplementedError, AttributeError):
2592
+ if R.construction() is None:
2593
+ raise NotImplementedError("Completion is not implemented for %s" % R.__class__)
2594
+ F, BR = R.construction()
2595
+ M = self.merge(F) or F.merge(self)
2596
+ if M is not None:
2597
+ return M(BR)
2598
+ if self.commutes(F) or F.commutes(self):
2599
+ return F(self(BR))
2600
+ raise NotImplementedError("Don't know how to apply %s to %s" % (repr(self), repr(R)))
2601
+
2602
+ def __eq__(self, other):
2603
+ """
2604
+ .. NOTE::
2605
+
2606
+ Only the prime used in the completion is relevant to comparison
2607
+ of Completion functors, although the resulting rings also take
2608
+ the precision into account.
2609
+
2610
+ TESTS::
2611
+
2612
+ sage: # needs sage.rings.padics
2613
+ sage: R1 = Zp(5, prec=30)
2614
+ sage: R2 = Zp(5, prec=40)
2615
+ sage: F1 = R1.construction()[0]
2616
+ sage: F2 = R2.construction()[0]
2617
+ sage: F1 == loads(dumps(F1)) # indirect doctest
2618
+ True
2619
+ sage: F1 == F2
2620
+ True
2621
+ sage: F1(QQ) == F2(QQ)
2622
+ False
2623
+ sage: R3 = Zp(7)
2624
+ sage: F3 = R3.construction()[0]
2625
+ sage: F1 == F3
2626
+ False
2627
+ """
2628
+ if isinstance(other, CompletionFunctor):
2629
+ return self.p == other.p
2630
+ return False
2631
+
2632
+ def __ne__(self, other):
2633
+ """
2634
+ Check whether ``self`` is not equal to ``other``.
2635
+
2636
+ EXAMPLES::
2637
+
2638
+ sage: # needs sage.rings.padics
2639
+ sage: R1 = Zp(5, prec=30)
2640
+ sage: R2 = Zp(5, prec=40)
2641
+ sage: F1 = R1.construction()[0]
2642
+ sage: F2 = R2.construction()[0]
2643
+ sage: F1 != loads(dumps(F1)) # indirect doctest
2644
+ False
2645
+ sage: F1 != F2
2646
+ False
2647
+ sage: F1(QQ) != F2(QQ)
2648
+ True
2649
+ sage: R3 = Zp(7)
2650
+ sage: F3 = R3.construction()[0]
2651
+ sage: F1 != F3
2652
+ True
2653
+ """
2654
+ return not (self == other)
2655
+
2656
+ __hash__ = ConstructionFunctor.__hash__
2657
+
2658
+ def merge(self, other):
2659
+ """
2660
+ Two Completion functors are merged, if they are equal. If the precisions of
2661
+ both functors coincide, then a Completion functor is returned that results
2662
+ from updating the ``extras`` dictionary of ``self`` by ``other.extras``.
2663
+ Otherwise, if the completion is at infinity then merging does not increase
2664
+ the set precision, and if the completion is at a finite prime, merging
2665
+ does not decrease the capped precision.
2666
+
2667
+ EXAMPLES::
2668
+
2669
+ sage: # needs sage.rings.padics
2670
+ sage: R1.<a> = Zp(5, prec=20)[]
2671
+ sage: R2 = Qp(5, prec=40)
2672
+ sage: R2(1) + a # indirect doctest
2673
+ (1 + O(5^20))*a + 1 + O(5^40)
2674
+ sage: R3 = RealField(30)
2675
+ sage: R4 = RealField(50)
2676
+ sage: R3(1) + R4(1) # indirect doctest
2677
+ 2.0000000
2678
+ sage: (R3(1) + R4(1)).parent()
2679
+ Real Field with 30 bits of precision
2680
+
2681
+ TESTS:
2682
+
2683
+ We check that :issue:`12353` has been resolved::
2684
+
2685
+ sage: RIF(1) > RR(1) # needs sage.rings.real_interval_field
2686
+ Traceback (most recent call last):
2687
+ ...
2688
+ TypeError: unsupported operand parent(s) for >:
2689
+ 'Real Interval Field with 53 bits of precision' and 'Real Field with 53 bits of precision'
2690
+
2691
+ We check that various pushouts work::
2692
+
2693
+ sage: # needs sage.rings.real_interval_field sage.rings.real_mpfr
2694
+ sage: R0 = RealIntervalField(30)
2695
+ sage: R1 = RealIntervalField(30, sci_not=True)
2696
+ sage: R2 = RealIntervalField(53)
2697
+ sage: R3 = RealIntervalField(53, sci_not=True)
2698
+ sage: R4 = RealIntervalField(90)
2699
+ sage: R5 = RealIntervalField(90, sci_not=True)
2700
+ sage: R6 = RealField(30)
2701
+ sage: R7 = RealField(30, sci_not=True)
2702
+ sage: R8 = RealField(53, rnd='RNDD')
2703
+ sage: R9 = RealField(53, sci_not=True, rnd='RNDZ')
2704
+ sage: R10 = RealField(53, sci_not=True)
2705
+ sage: R11 = RealField(90, sci_not=True, rnd='RNDZ')
2706
+ sage: Rlist = [R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11]
2707
+ sage: from sage.categories.pushout import pushout
2708
+ sage: pushouts = [R0,R0,R0,R1,R0,R1,R0,R1,R0,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R1,R0,R1,R2,R2,R2,R3,R0,R1,R2,R3,R3,R3,R1,R1,R3,R3,R3,R3,R1,R1,R3,R3,R3,R3,R0,R1,R2,R3,R4,R4,R0,R1,R2,R3,R3,R5,R1,R1,R3,R3,R5,R5,R1,R1,R3,R3,R3,R5,R0,R1,R0,R1,R0,R1,R6,R6,R6,R7,R7,R7,R1,R1,R1,R1,R1,R1,R7,R7,R7,R7,R7,R7,R0,R1,R2,R3,R2,R3,R6,R7,R8,R9,R10,R9,R1,R1,R3,R3,R3,R3,R7,R7,R9,R9,R10,R9,R1,R1,R3,R3,R3,R3,R7,R7,R10,R10,R10,R10,R1,R1,R3,R3,R5,R5,R7,R7,R9,R9,R10,R11]
2709
+ sage: all(R is S for R, S in zip(pushouts, [pushout(a, b) for a in Rlist for b in Rlist]))
2710
+ True
2711
+
2712
+ ::
2713
+
2714
+ sage: # needs sage.rings.padics
2715
+ sage: P0 = ZpFM(5, 10)
2716
+ sage: P1 = ZpFM(5, 20)
2717
+ sage: P2 = ZpCR(5, 10)
2718
+ sage: P3 = ZpCR(5, 20)
2719
+ sage: P4 = ZpCA(5, 10)
2720
+ sage: P5 = ZpCA(5, 20)
2721
+ sage: P6 = Qp(5, 10)
2722
+ sage: P7 = Qp(5, 20)
2723
+ sage: Plist = [P2,P3,P4,P5,P6,P7]
2724
+ sage: from sage.categories.pushout import pushout
2725
+ sage: pushouts = [P2,P3,P4,P5,P6,P7,P3,P3,P5,P5,P7,P7,P4,P5,P4,P5,P6,P7,
2726
+ ....: P5,P5,P5,P5,P7,P7,P6,P7,P6,P7,P6,P7,P7,P7,P7,P7,P7,P7]
2727
+ sage: all(P is Q
2728
+ ....: for P, Q in zip(pushouts, [pushout(a, b) for a in Plist for b in Plist]))
2729
+ True
2730
+ """
2731
+ if self == other: # both are Completion functors with the same p
2732
+ from sage.rings.infinity import Infinity
2733
+ if self.p == Infinity:
2734
+ new_prec = min(self.prec, other.prec)
2735
+ new_type = self._real_types[min(self._real_types.index(self.type),
2736
+ self._real_types.index(other.type))]
2737
+ new_scinot = max(self.extras.get('sci_not', 0),
2738
+ other.extras.get('sci_not', 0))
2739
+ new_rnd = min(self.extras.get('rnd', 0),
2740
+ other.extras.get('rnd', 0))
2741
+ return CompletionFunctor(self.p, new_prec,
2742
+ {'type': new_type,
2743
+ 'sci_not': new_scinot,
2744
+ 'rnd': new_rnd})
2745
+ else:
2746
+ new_type = self._dvr_types[min(self._dvr_types.index(self.type), self._dvr_types.index(other.type))]
2747
+ if new_type in ('fixed-mod', 'floating-point'):
2748
+ if self.type != other.type:
2749
+ return None # no coercion into fixed-mod or floating-point
2750
+ new_prec = min(self.prec, other.prec)
2751
+ else:
2752
+ new_prec = max(self.prec, other.prec) # since elements track their own precision, we don't want to truncate them
2753
+ extras = self.extras.copy()
2754
+ extras.update(other.extras)
2755
+ extras['type'] = new_type
2756
+ return CompletionFunctor(self.p, new_prec, extras)
2757
+
2758
+ # Completion has a lower rank than FractionField
2759
+ # and is thus applied first. However, fact is that
2760
+ # both commute. This is used in the call method,
2761
+ # since some fraction fields have no completion method
2762
+ # implemented.
2763
+
2764
+ def commutes(self, other):
2765
+ """
2766
+ Completion commutes with fraction fields.
2767
+
2768
+ EXAMPLES::
2769
+
2770
+ sage: F1 = Zp(5).construction()[0] # needs sage.rings.padics
2771
+ sage: F2 = QQ.construction()[0]
2772
+ sage: F1.commutes(F2) # needs sage.rings.padics
2773
+ True
2774
+
2775
+ TESTS:
2776
+
2777
+ The fraction field ``R`` in the example below has no completion
2778
+ method. But completion commutes with the fraction field functor,
2779
+ and so it is tried internally whether applying the construction
2780
+ functors in opposite order works. It does::
2781
+
2782
+ sage: P.<x> = ZZ[]
2783
+ sage: C = P.completion(x).construction()[0]
2784
+ sage: R = FractionField(P)
2785
+ sage: hasattr(R,'completion')
2786
+ False
2787
+ sage: C(R) is Frac(C(P))
2788
+ True
2789
+ sage: F = R.construction()[0]
2790
+ sage: (C*F)(ZZ['x']) is (F*C)(ZZ['x'])
2791
+ True
2792
+
2793
+ The following was fixed in :issue:`15329` (it used to result
2794
+ in an infinite recursion. In :issue:`23218` the construction
2795
+ of `p`-adic fields changed, so there is no longer an
2796
+ Ambiguous base extension error raised)::
2797
+
2798
+ sage: from sage.categories.pushout import pushout
2799
+ sage: pushout(Qp(7), RLF) # needs sage.rings.padics
2800
+ Traceback (most recent call last):
2801
+ ...
2802
+ CoercionException: Don't know how to
2803
+ apply Completion[+Infinity, prec=+Infinity]
2804
+ to 7-adic Ring with capped relative precision 20
2805
+ """
2806
+ return isinstance(other, FractionField)
2807
+
2808
+
2809
+ class QuotientFunctor(ConstructionFunctor):
2810
+ """
2811
+ Construction functor for quotient rings.
2812
+
2813
+ .. NOTE::
2814
+
2815
+ The functor keeps track of variable names. Optionally, it may
2816
+ keep track of additional properties of the quotient, such as
2817
+ its category or its implementation.
2818
+
2819
+ EXAMPLES::
2820
+
2821
+ sage: P.<x,y> = ZZ[]
2822
+ sage: Q = P.quo([x^2 + y^2] * P)
2823
+ sage: F = Q.construction()[0]
2824
+ sage: F(QQ['x','y'])
2825
+ Quotient of Multivariate Polynomial Ring in x, y over Rational Field
2826
+ by the ideal (x^2 + y^2)
2827
+ sage: F(QQ['x','y']) == QQ['x','y'].quo([x^2 + y^2] * QQ['x','y'])
2828
+ True
2829
+ sage: F(QQ['x','y','z'])
2830
+ Traceback (most recent call last):
2831
+ ...
2832
+ CoercionException: Cannot apply this quotient functor to
2833
+ Multivariate Polynomial Ring in x, y, z over Rational Field
2834
+ sage: F(QQ['y','z']) # needs sage.rings.finite_rings
2835
+ Traceback (most recent call last):
2836
+ ...
2837
+ TypeError: Could not find a mapping of the passed element to this ring.
2838
+ """
2839
+ rank = 4.5
2840
+
2841
+ def __init__(self, I, names=None, as_field=False, domain=None,
2842
+ codomain=None, **kwds):
2843
+ """
2844
+ INPUT:
2845
+
2846
+ - ``I`` -- an ideal (the modulus)
2847
+ - ``names`` -- string or list of strings (optional); the names for the
2848
+ quotient ring generators
2849
+ - ``as_field`` -- boolean (default: ``False``); return the quotient
2850
+ ring as field (if available)
2851
+ - ``domain`` -- category (default: ``Rings()``); the domain of
2852
+ this functor
2853
+ - ``codomain`` -- category (default: ``Rings()``); the codomain
2854
+ of this functor
2855
+ - Further named arguments. In particular, an implementation of the
2856
+ quotient can be suggested here. These named arguments are passed to
2857
+ the quotient construction.
2858
+
2859
+ TESTS::
2860
+
2861
+ sage: from sage.categories.pushout import QuotientFunctor
2862
+ sage: P.<t> = ZZ[]
2863
+ sage: F = QuotientFunctor([5 + t^2] * P)
2864
+ sage: F(P) # needs sage.libs.pari
2865
+ Univariate Quotient Polynomial Ring in tbar
2866
+ over Integer Ring with modulus t^2 + 5
2867
+ sage: F(QQ['t']) # needs sage.libs.pari
2868
+ Univariate Quotient Polynomial Ring in tbar
2869
+ over Rational Field with modulus t^2 + 5
2870
+ sage: F = QuotientFunctor([5 + t^2] * P, names='s')
2871
+ sage: F(P) # needs sage.libs.pari
2872
+ Univariate Quotient Polynomial Ring in s
2873
+ over Integer Ring with modulus t^2 + 5
2874
+ sage: F(QQ['t']) # needs sage.libs.pari
2875
+ Univariate Quotient Polynomial Ring in s
2876
+ over Rational Field with modulus t^2 + 5
2877
+ sage: F = QuotientFunctor([5] * ZZ, as_field=True)
2878
+ sage: F(ZZ)
2879
+ Finite Field of size 5
2880
+ sage: F = QuotientFunctor([5] * ZZ)
2881
+ sage: F(ZZ)
2882
+ Ring of integers modulo 5
2883
+ """
2884
+ if domain is None:
2885
+ domain = Rings()
2886
+ if codomain is None:
2887
+ codomain = Rings()
2888
+ Functor.__init__(self, domain, codomain)
2889
+
2890
+ self.I = I
2891
+ if names is None:
2892
+ self.names = None
2893
+ elif isinstance(names, str):
2894
+ self.names = (names,)
2895
+ else:
2896
+ self.names = tuple(names)
2897
+ self.as_field = as_field
2898
+ self.kwds = kwds
2899
+
2900
+ def _apply_functor(self, R):
2901
+ """
2902
+ Apply the functor to an object of ``self``'s domain.
2903
+
2904
+ TESTS::
2905
+
2906
+ sage: P.<x,y> = ZZ[]
2907
+ sage: Q = P.quo([2 + x^2, 3*x + y^2])
2908
+ sage: F = Q.construction()[0]; F
2909
+ QuotientFunctor
2910
+ sage: F(QQ['x','y']) # indirect doctest
2911
+ Quotient of Multivariate Polynomial Ring in x, y over Rational Field
2912
+ by the ideal (x^2 + 2, y^2 + 3*x)
2913
+
2914
+ Note that the ``quo()`` method of a field used to return the
2915
+ integer zero. That strange behaviour was removed in github
2916
+ issue :issue:`9138`. It now returns a trivial quotient ring
2917
+ when applied to a field::
2918
+
2919
+ sage: F = ZZ.quo([5]*ZZ).construction()[0]
2920
+ sage: F(QQ)
2921
+ Ring of integers modulo 1
2922
+ sage: QQ.quo(5)
2923
+ Quotient of Rational Field by the ideal (1)
2924
+ """
2925
+ I = self.I
2926
+ if not I.is_zero():
2927
+ from sage.categories.fields import Fields
2928
+ if R in Fields():
2929
+ from sage.rings.finite_rings.integer_mod_ring import Integers
2930
+ return Integers(1)
2931
+ if I.ring() != R:
2932
+ if I.ring().has_coerce_map_from(R):
2933
+ R = I.ring()
2934
+ else:
2935
+ R = pushout(R, I.ring().base_ring())
2936
+ I = R.ideal([R.one() * t for t in I.gens()], warn=False)
2937
+ try:
2938
+ Q = R.quo(I, names=self.names, **self.kwds)
2939
+ except IndexError: # That may happen!
2940
+ raise CoercionException("Cannot apply this quotient functor to %s" % R)
2941
+ if self.as_field:
2942
+ try:
2943
+ Q = Q.field()
2944
+ except AttributeError:
2945
+ pass
2946
+ return Q
2947
+
2948
+ def __eq__(self, other):
2949
+ """
2950
+ The types, domain, codomain, names and moduli are compared.
2951
+
2952
+ TESTS::
2953
+
2954
+ sage: # needs sage.libs.pari
2955
+ sage: P.<x> = QQ[]
2956
+ sage: F = P.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2957
+ sage: F == loads(dumps(F))
2958
+ True
2959
+ sage: P2.<x,y> = QQ[]
2960
+ sage: F == P2.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2961
+ False
2962
+ sage: P3.<x> = ZZ[]
2963
+ sage: F == P3.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2964
+ True
2965
+ """
2966
+ if not isinstance(other, QuotientFunctor):
2967
+ return False
2968
+ return (type(self) is type(other) and
2969
+ self.domain() == other.domain() and
2970
+ self.codomain() == other.codomain() and
2971
+ self.names == other.names and
2972
+ self.I == other.I)
2973
+
2974
+ def __ne__(self, other):
2975
+ """
2976
+ Check whether ``self`` is not equal to ``other``.
2977
+
2978
+ EXAMPLES::
2979
+
2980
+ sage: # needs sage.libs.pari
2981
+ sage: P.<x> = QQ[]
2982
+ sage: F = P.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2983
+ sage: F != loads(dumps(F))
2984
+ False
2985
+ sage: P2.<x,y> = QQ[]
2986
+ sage: F != P2.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2987
+ True
2988
+ sage: P3.<x> = ZZ[]
2989
+ sage: F != P3.quo([(x^2+1)^2*(x^2-3),(x^2+1)^2*(x^5+3)]).construction()[0]
2990
+ False
2991
+ """
2992
+ return not (self == other)
2993
+
2994
+ __hash__ = ConstructionFunctor.__hash__
2995
+
2996
+ def merge(self, other):
2997
+ """
2998
+ Two quotient functors with coinciding names are merged by taking the gcd
2999
+ of their moduli, the meet of their domains, and the join of their codomains.
3000
+
3001
+ In particular, if one of the functors being merged knows that the quotient
3002
+ is going to be a field, then the merged functor will return fields as
3003
+ well.
3004
+
3005
+ EXAMPLES::
3006
+
3007
+ sage: # needs sage.libs.pari
3008
+ sage: P.<x> = QQ[]
3009
+ sage: Q1 = P.quo([(x^2+1)^2*(x^2-3)])
3010
+ sage: Q2 = P.quo([(x^2+1)^2*(x^5+3)])
3011
+ sage: from sage.categories.pushout import pushout
3012
+ sage: pushout(Q1,Q2) # indirect doctest
3013
+ Univariate Quotient Polynomial Ring in xbar over Rational Field
3014
+ with modulus x^4 + 2*x^2 + 1
3015
+
3016
+ The following was fixed in :issue:`8800`::
3017
+
3018
+ sage: pushout(GF(5), Integers(5)) # needs sage.libs.pari
3019
+ Finite Field of size 5
3020
+ """
3021
+ if type(self) is not type(other):
3022
+ return None
3023
+ if self.names != other.names:
3024
+ return None
3025
+ if self == other:
3026
+ if self.as_field == other.as_field:
3027
+ # The two functors are *really* equal
3028
+ return self
3029
+ # They are equal up to the additional arguments
3030
+ I = self.I
3031
+ domain = self.domain()
3032
+ codomain = self.codomain()
3033
+ else:
3034
+ try:
3035
+ I = self.I + other.I
3036
+ except (TypeError, NotImplementedError):
3037
+ try:
3038
+ I = self.I.gcd(other.I)
3039
+ except (TypeError, NotImplementedError):
3040
+ return None
3041
+ domain = self.domain().meet([self.domain(), other.domain()])
3042
+ codomain = self.codomain().join([self.codomain(), other.codomain()])
3043
+ # Get the optional arguments:
3044
+ as_field = self.as_field or other.as_field
3045
+ kwds = dict(self.kwds)
3046
+ for k, v in other.kwds.items():
3047
+ if k == 'category':
3048
+ if kwds[k] is not None:
3049
+ kwds[k] = v.join([v, kwds[k]])
3050
+ else:
3051
+ kwds[k] = v
3052
+ continue
3053
+ if k in kwds and kwds[k] is not None and v != kwds[k]:
3054
+ # Don't know what default to choose. Hence: No merge!
3055
+ return None
3056
+ kwds[k] = v
3057
+ if I.is_trivial() and not I.is_zero():
3058
+ # quotient by I would result in the trivial ring/group/...
3059
+ # Rather than create the zero ring, we claim they can't be merged
3060
+ # TODO: Perhaps this should be detected at a higher level...
3061
+ raise TypeError("trivial quotient intersection")
3062
+ # GF(p) has a coercion from Integers(p). Hence, merging should
3063
+ # yield a field if either self or other yields a field.
3064
+ return QuotientFunctor(I, names=self.names, as_field=as_field,
3065
+ domain=domain, codomain=codomain, **kwds)
3066
+
3067
+
3068
+ class AlgebraicExtensionFunctor(ConstructionFunctor):
3069
+ """
3070
+ Algebraic extension (univariate polynomial ring modulo principal ideal).
3071
+
3072
+ EXAMPLES::
3073
+
3074
+ sage: x = polygen(QQ, 'x')
3075
+ sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
3076
+ sage: F = K.construction()[0] # needs sage.rings.number_field
3077
+ sage: F(ZZ['t']) # needs sage.rings.number_field
3078
+ Univariate Quotient Polynomial Ring in a
3079
+ over Univariate Polynomial Ring in t over Integer Ring
3080
+ with modulus a^3 + a^2 + 1
3081
+
3082
+ Note that, even if a field is algebraically closed, the algebraic
3083
+ extension will be constructed as the quotient of a univariate
3084
+ polynomial ring::
3085
+
3086
+ sage: F(CC) # needs sage.rings.number_field
3087
+ Univariate Quotient Polynomial Ring in a
3088
+ over Complex Field with 53 bits of precision
3089
+ with modulus a^3 + a^2 + 1.00000000000000
3090
+ sage: F(RR) # needs sage.rings.number_field
3091
+ Univariate Quotient Polynomial Ring in a
3092
+ over Real Field with 53 bits of precision
3093
+ with modulus a^3 + a^2 + 1.00000000000000
3094
+
3095
+ Note that the construction functor of a number field applied to
3096
+ the integers returns an order (not necessarily maximal) of that
3097
+ field, similar to the behaviour of ``ZZ.extension(...)``::
3098
+
3099
+ sage: F(ZZ) # needs sage.rings.number_field
3100
+ Order generated by a in Number Field in a with defining polynomial x^3 + x^2 + 1
3101
+
3102
+ This also holds for non-absolute number fields::
3103
+
3104
+ sage: # needs sage.rings.number_field
3105
+ sage: x = polygen(QQ, 'x')
3106
+ sage: K.<a,b> = NumberField([x^3 + x^2 + 1, x^2 + x + 1])
3107
+ sage: F = K.construction()[0]
3108
+ sage: O = F(ZZ); O
3109
+ Relative Order
3110
+ generated by [(b - 2)*a^2 + (3*b - 1)*a + 3*b + 4, a - b]
3111
+ in Number Field in a with defining polynomial x^3 + x^2 + 1
3112
+ over its base field
3113
+ sage: O.ambient() is K
3114
+ True
3115
+
3116
+ Special cases are made for cyclotomic fields and residue fields::
3117
+
3118
+ sage: # needs sage.rings.number_field
3119
+ sage: C = CyclotomicField(8)
3120
+ sage: F, R = C.construction()
3121
+ sage: F
3122
+ AlgebraicExtensionFunctor
3123
+ sage: R
3124
+ Rational Field
3125
+ sage: F(R)
3126
+ Cyclotomic Field of order 8 and degree 4
3127
+ sage: F(ZZ)
3128
+ Maximal Order generated by zeta8 in Cyclotomic Field of order 8 and degree 4
3129
+
3130
+ ::
3131
+
3132
+ sage: # needs sage.rings.number_field
3133
+ sage: K.<z> = CyclotomicField(7)
3134
+ sage: P = K.factor(17)[0][0]
3135
+ sage: k = K.residue_field(P)
3136
+ sage: F, R = k.construction()
3137
+ sage: F
3138
+ AlgebraicExtensionFunctor
3139
+ sage: R
3140
+ Cyclotomic Field of order 7 and degree 6
3141
+ sage: F(R) is k
3142
+ True
3143
+ sage: F(ZZ)
3144
+ Residue field of Integers modulo 17
3145
+ sage: F(CyclotomicField(49))
3146
+ Residue field in zbar of Fractional ideal (17)
3147
+ """
3148
+ rank = 3
3149
+
3150
+ def __init__(self, polys, names, embeddings=None, structures=None,
3151
+ cyclotomic=None, precs=None, implementations=None,
3152
+ *, residue=None, latex_names=None, **kwds):
3153
+ """
3154
+ INPUT:
3155
+
3156
+ - ``polys`` -- list of polynomials (or of integers, for
3157
+ finite fields and unramified local extensions)
3158
+
3159
+ - ``names`` -- list of strings of the same length as the
3160
+ list ``polys``
3161
+
3162
+ - ``embeddings`` -- (optional) list of approximate complex
3163
+ values, determining an embedding of the generators into the
3164
+ complex field, or ``None`` for each generator whose
3165
+ embedding is not prescribed.
3166
+
3167
+ - ``structures`` -- (optional) list of structural morphisms of
3168
+ number fields; see
3169
+ :class:`~sage.rings.number_field.structure.NumberFieldStructure`.
3170
+
3171
+ - ``cyclotomic`` -- (optional) integer. If it is provided,
3172
+ application of the functor to the rational field yields a
3173
+ cyclotomic field, rather than just a number field.
3174
+
3175
+ - ``precs`` -- (optional) list of integers. If it is provided,
3176
+ it is used to determine the precision of `p`-adic extensions.
3177
+
3178
+ - ``implementations`` -- (optional) list of strings.
3179
+ If it is provided, it is used to determine an implementation in the
3180
+ `p`-adic case.
3181
+
3182
+ - ``residue`` -- (optional) prime ideal of an order in a number
3183
+ field, determining a residue field. If it is provided,
3184
+ application of the functor to a number field yields the
3185
+ residue field with respect to the given prime ideal
3186
+ (coerced into the number field).
3187
+
3188
+ - ``latex_names`` -- (optional) list of strings of the same length
3189
+ as the list ``polys``
3190
+
3191
+ - ``**kwds`` -- further keywords; when the functor is applied
3192
+ to a ring `R`, these are passed to the ``extension()``
3193
+ method of `R`.
3194
+
3195
+ REMARK:
3196
+
3197
+ Currently, an embedding can only be provided for the last
3198
+ generator, and only when the construction functor is applied
3199
+ to the rational field. There will be no error when constructing
3200
+ the functor, but when applying it.
3201
+
3202
+ TESTS::
3203
+
3204
+ sage: from sage.categories.pushout import AlgebraicExtensionFunctor
3205
+ sage: P.<x> = ZZ[]
3206
+ sage: F1 = AlgebraicExtensionFunctor([x^3 - x^2 + 1], ['a'], [None])
3207
+ sage: F2 = AlgebraicExtensionFunctor([x^3 - x^2 + 1], ['a'], [0])
3208
+ sage: F1 == F2
3209
+ False
3210
+ sage: F1(QQ) # needs sage.rings.number_field
3211
+ Number Field in a with defining polynomial x^3 - x^2 + 1
3212
+ sage: F1(QQ).coerce_embedding() # needs sage.rings.number_field
3213
+ sage: phi = F2(QQ).coerce_embedding().__copy__(); phi # needs sage.rings.number_field
3214
+ Generic morphism:
3215
+ From: Number Field in a with defining polynomial x^3 - x^2 + 1
3216
+ with a = -0.7548776662466928?
3217
+ To: Real Lazy Field
3218
+ Defn: a -> -0.7548776662466928?
3219
+ sage: F1(QQ) == F2(QQ) # needs sage.rings.number_field
3220
+ False
3221
+ sage: F1(GF(5)) # needs sage.libs.pari
3222
+ Univariate Quotient Polynomial Ring in a over Finite Field of size 5
3223
+ with modulus a^3 + 4*a^2 + 1
3224
+ sage: F2(GF(5)) # needs sage.libs.pari
3225
+ Traceback (most recent call last):
3226
+ ...
3227
+ NotImplementedError: ring extension with prescribed embedding is not implemented
3228
+
3229
+ When applying a number field constructor to the ring of
3230
+ integers, an order (not necessarily maximal) of that field is
3231
+ returned, similar to the behaviour of ``ZZ.extension``::
3232
+
3233
+ sage: F1(ZZ) # needs sage.rings.number_field
3234
+ Order generated by a in Number Field in a with defining polynomial x^3 - x^2 + 1
3235
+
3236
+ The cyclotomic fields form a special case of number fields
3237
+ with prescribed embeddings::
3238
+
3239
+ sage: # needs sage.rings.number_field
3240
+ sage: C = CyclotomicField(8)
3241
+ sage: F, R = C.construction()
3242
+ sage: F
3243
+ AlgebraicExtensionFunctor
3244
+ sage: R
3245
+ Rational Field
3246
+ sage: F(R)
3247
+ Cyclotomic Field of order 8 and degree 4
3248
+ sage: F(ZZ)
3249
+ Maximal Order generated by zeta8 in Cyclotomic Field of order 8 and degree 4
3250
+
3251
+ The data stored in this construction includes structural
3252
+ morphisms of number fields (see :issue:`20826`)::
3253
+
3254
+ sage: # needs sage.rings.number_field
3255
+ sage: R.<x> = ZZ[]
3256
+ sage: K.<a> = NumberField(x^2 - 3)
3257
+ sage: L0.<b> = K.change_names()
3258
+ sage: L0.structure()
3259
+ (Isomorphism given by variable name change map:
3260
+ From: Number Field in b with defining polynomial x^2 - 3
3261
+ To: Number Field in a with defining polynomial x^2 - 3,
3262
+ Isomorphism given by variable name change map:
3263
+ From: Number Field in a with defining polynomial x^2 - 3
3264
+ To: Number Field in b with defining polynomial x^2 - 3)
3265
+ sage: L1 = (b*x).parent().base_ring()
3266
+ sage: L1 is L0
3267
+ True
3268
+ """
3269
+ Functor.__init__(self, Rings(), Rings())
3270
+ if not (isinstance(polys, (list, tuple)) and isinstance(names, (list, tuple))):
3271
+ raise ValueError("Arguments must be lists or tuples")
3272
+ n = len(polys)
3273
+ if embeddings is None:
3274
+ embeddings = [None] * n
3275
+ if structures is None:
3276
+ structures = [None] * n
3277
+ if precs is None:
3278
+ precs = [None] * n
3279
+ if implementations is None:
3280
+ implementations = [None] * n
3281
+ if latex_names is None:
3282
+ latex_names = [None] * n
3283
+ if not (len(names) == len(embeddings) == len(structures) == len(latex_names) == n):
3284
+ raise ValueError("All arguments must be of the same length")
3285
+ self.polys = list(polys)
3286
+ self.names = list(names)
3287
+ self.embeddings = list(embeddings)
3288
+ self.structures = list(structures)
3289
+ self.cyclotomic = int(cyclotomic) if cyclotomic is not None else None
3290
+ self.precs = list(precs)
3291
+ self.implementations = list(implementations)
3292
+ self.residue = residue
3293
+ # Normalize latex_names: Use None when latex_name does not override the default.
3294
+ latex_names = list(latex_names)
3295
+ for i, name in enumerate(self.names):
3296
+ if latex_names[i] is not None:
3297
+ from sage.misc.latex import latex_variable_name
3298
+ if latex_names[i] == latex_variable_name(name):
3299
+ latex_names[i] = None
3300
+ self.latex_names = latex_names
3301
+ self.kwds = kwds
3302
+
3303
+ def _apply_functor(self, R):
3304
+ """
3305
+ Apply the functor to an object of ``self``'s domain.
3306
+
3307
+ TESTS::
3308
+
3309
+ sage: # needs sage.rings.number_field
3310
+ sage: x = polygen(QQ, 'x')
3311
+ sage: K.<a> = NumberField(x^3 + x^2 + 1)
3312
+ sage: F = K.construction()[0]
3313
+ sage: F(ZZ) # indirect doctest
3314
+ Order generated by a in Number Field in a with defining polynomial x^3 + x^2 + 1
3315
+ sage: F(ZZ['t']) # indirect doctest
3316
+ Univariate Quotient Polynomial Ring in a over
3317
+ Univariate Polynomial Ring in t over Integer Ring with modulus a^3 + a^2 + 1
3318
+ sage: F(RR) # indirect doctest
3319
+ Univariate Quotient Polynomial Ring in a over
3320
+ Real Field with 53 bits of precision with modulus a^3 + a^2 + 1.00000000000000
3321
+
3322
+ Check that :issue:`13538` is fixed::
3323
+
3324
+ sage: # needs sage.rings.padics
3325
+ sage: from sage.categories.pushout import AlgebraicExtensionFunctor
3326
+ sage: K = Qp(3, 3)
3327
+ sage: R.<a> = K[]
3328
+ sage: AEF = AlgebraicExtensionFunctor([a^2 - 3], ['a'], [None])
3329
+ sage: AEF(K)
3330
+ 3-adic Eisenstein Extension Field in a defined by a^2 - 3
3331
+ """
3332
+ from sage.rings.integer_ring import ZZ
3333
+ from sage.rings.rational_field import QQ
3334
+ if self.cyclotomic:
3335
+ from sage.rings.number_field.number_field import CyclotomicField
3336
+ if R == QQ:
3337
+ return CyclotomicField(self.cyclotomic)
3338
+ if R == ZZ:
3339
+ return CyclotomicField(self.cyclotomic).maximal_order()
3340
+ elif self.residue is not None:
3341
+ return R.residue_field(R*self.residue, names=tuple(self.names))
3342
+ if len(self.polys) == 1:
3343
+ return R.extension(self.polys[0], names=self.names[0], embedding=self.embeddings[0],
3344
+ structure=self.structures[0], prec=self.precs[0],
3345
+ implementation=self.implementations[0],
3346
+ latex_names=self.latex_names[0], **self.kwds)
3347
+ return R.extension(self.polys, names=self.names, embedding=self.embeddings,
3348
+ structure=self.structures, prec=self.precs,
3349
+ implementation=self.implementations,
3350
+ latex_names=self.latex_names, **self.kwds)
3351
+
3352
+ def __eq__(self, other):
3353
+ """
3354
+ Check whether ``self`` is equal to ``other``.
3355
+
3356
+ TESTS::
3357
+
3358
+ sage: # needs sage.rings.number_field
3359
+ sage: x = polygen(QQ, 'x')
3360
+ sage: K.<a> = NumberField(x^3 + x^2 + 1)
3361
+ sage: F = K.construction()[0]
3362
+ sage: F == loads(dumps(F))
3363
+ True
3364
+
3365
+ sage: K2.<a> = NumberField(x^3 + x^2 + 1, latex_names='a') # needs sage.rings.number_field
3366
+ sage: F2 = K2.construction()[0] # needs sage.rings.number_field
3367
+ sage: F2 == F # needs sage.rings.number_field
3368
+ True
3369
+
3370
+ sage: K3.<a> = NumberField(x^3 + x^2 + 1, latex_names='alpha') # needs sage.rings.number_field
3371
+ sage: F3 = K3.construction()[0] # needs sage.rings.number_field
3372
+ sage: F3 == F # needs sage.rings.number_field
3373
+ False
3374
+ """
3375
+ if not isinstance(other, AlgebraicExtensionFunctor):
3376
+ return False
3377
+
3378
+ return (self.polys == other.polys and
3379
+ self.embeddings == other.embeddings and
3380
+ self.structures == other.structures and
3381
+ self.precs == other.precs and
3382
+ self.latex_names == other.latex_names)
3383
+
3384
+ def __ne__(self, other):
3385
+ """
3386
+ Check whether ``self`` is not equal to ``other``.
3387
+
3388
+ EXAMPLES::
3389
+
3390
+ sage: x = polygen(QQ, 'x')
3391
+ sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
3392
+ sage: F = K.construction()[0] # needs sage.rings.number_field
3393
+ sage: F != loads(dumps(F)) # needs sage.rings.number_field
3394
+ False
3395
+ """
3396
+ return not (self == other)
3397
+
3398
+ __hash__ = ConstructionFunctor.__hash__
3399
+
3400
+ def merge(self, other):
3401
+ """
3402
+ Merging with another :class:`AlgebraicExtensionFunctor`.
3403
+
3404
+ INPUT:
3405
+
3406
+ - ``other`` -- Construction Functor
3407
+
3408
+ OUTPUT:
3409
+
3410
+ - If ``self==other``, ``self`` is returned.
3411
+ - If ``self`` and ``other`` are simple extensions
3412
+ and both provide an embedding, then it is tested
3413
+ whether one of the number fields provided by
3414
+ the functors coerces into the other; the functor
3415
+ associated with the target of the coercion is
3416
+ returned. Otherwise, the construction functor
3417
+ associated with the pushout of the codomains
3418
+ of the two embeddings is returned, provided that
3419
+ it is a number field.
3420
+ - If these two extensions are defined by Conway polynomials
3421
+ over finite fields, merges them into a single extension of
3422
+ degree the lcm of the two degrees.
3423
+ - Otherwise, ``None`` is returned.
3424
+
3425
+ REMARK:
3426
+
3427
+ Algebraic extension with embeddings currently only
3428
+ works when applied to the rational field. This is
3429
+ why we use the admittedly strange rule above for
3430
+ merging.
3431
+
3432
+ EXAMPLES:
3433
+
3434
+ The following demonstrate coercions for finite fields using Conway or
3435
+ pseudo-Conway polynomials::
3436
+
3437
+ sage: k = GF(3^2, prefix='z'); a = k.gen() # needs sage.rings.finite_rings
3438
+ sage: l = GF(3^3, prefix='z'); b = l.gen() # needs sage.rings.finite_rings
3439
+ sage: a + b # indirect doctest # needs sage.rings.finite_rings
3440
+ z6^5 + 2*z6^4 + 2*z6^3 + z6^2 + 2*z6 + 1
3441
+
3442
+ Note that embeddings are compatible in lattices of such finite fields::
3443
+
3444
+ sage: # needs sage.rings.finite_rings
3445
+ sage: m = GF(3^5, prefix='z'); c = m.gen()
3446
+ sage: (a + b) + c == a + (b + c) # indirect doctest
3447
+ True
3448
+ sage: from sage.categories.pushout import pushout
3449
+ sage: n = pushout(k, l)
3450
+ sage: o = pushout(l, m)
3451
+ sage: q = pushout(n, o)
3452
+ sage: q(o(b)) == q(n(b)) # indirect doctest
3453
+ True
3454
+
3455
+ Coercion is also available for number fields::
3456
+
3457
+ sage: # needs sage.rings.number_field
3458
+ sage: P.<x> = QQ[]
3459
+ sage: L.<b> = NumberField(x^8 - x^4 + 1, embedding=CDF.0)
3460
+ sage: M1.<c1> = NumberField(x^2 + x + 1, embedding=b^4 - 1)
3461
+ sage: M2.<c2> = NumberField(x^2 + 1, embedding=-b^6)
3462
+ sage: M1.coerce_map_from(M2)
3463
+ sage: M2.coerce_map_from(M1)
3464
+ sage: c1 + c2; parent(c1 + c2) #indirect doctest
3465
+ -b^6 + b^4 - 1
3466
+ Number Field in b with defining polynomial x^8 - x^4 + 1
3467
+ with b = -0.2588190451025208? + 0.9659258262890683?*I
3468
+ sage: pushout(M1['x'], M2['x']) # needs sage.rings.finite_rings
3469
+ Univariate Polynomial Ring in x
3470
+ over Number Field in b with defining polynomial x^8 - x^4 + 1
3471
+ with b = -0.2588190451025208? + 0.9659258262890683?*I
3472
+
3473
+ In the previous example, the number field ``L`` becomes the pushout
3474
+ of ``M1`` and ``M2`` since both are provided with an embedding into
3475
+ ``L``, *and* since ``L`` is a number field. If two number fields
3476
+ are embedded into a field that is not a numberfield, no merging
3477
+ occurs::
3478
+
3479
+ sage: # needs sage.rings.complex_double sage.rings.number_field
3480
+ sage: cbrt2 = CDF(2)^(1/3)
3481
+ sage: zeta3 = CDF.zeta(3)
3482
+ sage: K.<a> = NumberField(x^3 - 2, embedding=cbrt2 * zeta3)
3483
+ sage: L.<b> = NumberField(x^6 - 2, embedding=1.1)
3484
+ sage: L.coerce_map_from(K)
3485
+ sage: K.coerce_map_from(L)
3486
+ sage: pushout(K, L) # needs sage.rings.finite_rings
3487
+ Traceback (most recent call last):
3488
+ ...
3489
+ CoercionException: ('Ambiguous Base Extension', Number Field in a with
3490
+ defining polynomial x^3 - 2 with a = -0.6299605249474365? + 1.091123635971722?*I,
3491
+ Number Field in b with defining polynomial x^6 - 2 with b = 1.122462048309373?)
3492
+ """
3493
+ if isinstance(other, AlgebraicClosureFunctor):
3494
+ return other
3495
+ elif not isinstance(other, AlgebraicExtensionFunctor):
3496
+ return None
3497
+ if self == other:
3498
+ return self
3499
+ # This method is supposed to be used in pushout(),
3500
+ # *after* expanding the functors. Hence, we can
3501
+ # assume that both functors have a single variable.
3502
+ # But for being on the safe side...:
3503
+ if not (len(self.names) == 1 == len(other.names)):
3504
+ return None
3505
+ # We don't accept a forgetful coercion, since, together
3506
+ # with bidirectional coercions between two embedded
3507
+ # number fields, it would yield to contradictions in
3508
+ # the coercion system.
3509
+ # if self.polys==other.polys and self.names==other.names:
3510
+ # # We have a forgetful functor:
3511
+ # if self.embeddings==[None]:
3512
+ # return self
3513
+ # if other.embeddings==[None]:
3514
+ # return other
3515
+ # ... or we may use the given embeddings:
3516
+ if self.embeddings != [None] and other.embeddings != [None]:
3517
+ from sage.rings.rational_field import QQ
3518
+ KS = self(QQ)
3519
+ KO = other(QQ)
3520
+ if KS.has_coerce_map_from(KO):
3521
+ return self
3522
+ if KO.has_coerce_map_from(KS):
3523
+ return other
3524
+ # nothing else helps, hence, we move to the pushout of the codomains of the embeddings
3525
+ try:
3526
+ P = pushout(self.embeddings[0].parent(), other.embeddings[0].parent())
3527
+ from sage.rings.number_field.number_field_base import NumberField
3528
+ if isinstance(P, NumberField):
3529
+ return P.construction()[0]
3530
+ except CoercionException:
3531
+ return None
3532
+ # Finite fields and unramified local extensions may use
3533
+ # integers to encode degrees of extensions.
3534
+ from sage.rings.integer import Integer
3535
+ kwds_self = dict(self.kwds.items())
3536
+ if 'impl' in kwds_self:
3537
+ del kwds_self['impl']
3538
+ kwds_other = dict(other.kwds.items())
3539
+ if 'impl' in kwds_other:
3540
+ del kwds_other['impl']
3541
+ if (isinstance(self.polys[0], Integer)
3542
+ and isinstance(other.polys[0], Integer)
3543
+ and self.embeddings == other.embeddings == [None]
3544
+ and self.structures == other.structures == [None]
3545
+ and kwds_self == kwds_other):
3546
+ return AlgebraicExtensionFunctor([self.polys[0].lcm(other.polys[0])], [None], **kwds_self)
3547
+
3548
+ def __mul__(self, other):
3549
+ """
3550
+ Compose construction functors to a composite construction functor, unless one of them is the identity.
3551
+
3552
+ .. NOTE::
3553
+
3554
+ The product is in functorial notation, i.e., when applying the
3555
+ product to an object then the second factor is applied first.
3556
+
3557
+ TESTS::
3558
+
3559
+ sage: # needs sage.rings.number_field
3560
+ sage: P.<x> = QQ[]
3561
+ sage: K.<a> = NumberField(x^3 - 5, embedding=0)
3562
+ sage: L.<b> = K.extension(x^2 + a)
3563
+ sage: F, R = L.construction()
3564
+ sage: prod(F.expand())(R) == L #indirect doctest
3565
+ True
3566
+ """
3567
+ if isinstance(other, IdentityConstructionFunctor):
3568
+ return self
3569
+ if isinstance(other, AlgebraicExtensionFunctor):
3570
+ if set(self.names).intersection(other.names):
3571
+ raise CoercionException("Overlapping names (%s,%s)" % (self.names, other.names))
3572
+ return AlgebraicExtensionFunctor(self.polys + other.polys, self.names + other.names,
3573
+ self.embeddings + other.embeddings,
3574
+ self.structures + other.structures,
3575
+ precs=self.precs + other.precs,
3576
+ implementations=self.implementations + other.implementations,
3577
+ latex_names=self.latex_names + other.latex_names,
3578
+ **self.kwds)
3579
+ elif (isinstance(other, CompositeConstructionFunctor)
3580
+ and isinstance(other.all[-1], AlgebraicExtensionFunctor)):
3581
+ return CompositeConstructionFunctor(other.all[:-1], self * other.all[-1])
3582
+ else:
3583
+ return CompositeConstructionFunctor(other, self)
3584
+
3585
+ def expand(self):
3586
+ """
3587
+ Decompose the functor `F` into sub-functors, whose product returns `F`.
3588
+
3589
+ EXAMPLES::
3590
+
3591
+ sage: # needs sage.rings.number_field
3592
+ sage: P.<x> = QQ[]
3593
+ sage: K.<a> = NumberField(x^3 - 5, embedding=0)
3594
+ sage: L.<b> = K.extension(x^2 + a)
3595
+ sage: F, R = L.construction()
3596
+ sage: prod(F.expand())(R) == L
3597
+ True
3598
+ sage: K = NumberField([x^2 - 2, x^2 - 3],'a')
3599
+ sage: F, R = K.construction()
3600
+ sage: F
3601
+ AlgebraicExtensionFunctor
3602
+ sage: L = F.expand(); L
3603
+ [AlgebraicExtensionFunctor, AlgebraicExtensionFunctor]
3604
+ sage: L[-1](QQ)
3605
+ Number Field in a1 with defining polynomial x^2 - 3
3606
+ """
3607
+ n = len(self.polys)
3608
+ if n == 1:
3609
+ return [self]
3610
+ return [AlgebraicExtensionFunctor([self.polys[i]], [self.names[i]], [self.embeddings[i]],
3611
+ [self.structures[i]], precs=[self.precs[i]],
3612
+ implementations=[self.implementations[i]],
3613
+ latex_names=[self.latex_names[i]], **self.kwds)
3614
+ for i in range(n)]
3615
+
3616
+
3617
+ class AlgebraicClosureFunctor(ConstructionFunctor):
3618
+ """
3619
+ Algebraic Closure.
3620
+
3621
+ EXAMPLES::
3622
+
3623
+ sage: # needs sage.rings.complex_double sage.rings.number_field
3624
+ sage: F = CDF.construction()[0]
3625
+ sage: F(QQ)
3626
+ Algebraic Field
3627
+ sage: F(RR) # needs sage.rings.real_mpfr
3628
+ Complex Field with 53 bits of precision
3629
+ sage: F(F(QQ)) is F(QQ)
3630
+ True
3631
+ """
3632
+ rank = 3
3633
+
3634
+ def __init__(self):
3635
+ """
3636
+ TESTS::
3637
+
3638
+ sage: from sage.categories.pushout import AlgebraicClosureFunctor
3639
+ sage: F = AlgebraicClosureFunctor()
3640
+ sage: F(QQ) # needs sage.rings.number_field
3641
+ Algebraic Field
3642
+ sage: F(RR) # needs sage.rings.real_mpfr
3643
+ Complex Field with 53 bits of precision
3644
+ sage: F == loads(dumps(F))
3645
+ True
3646
+ """
3647
+ Functor.__init__(self, Rings(), Rings())
3648
+
3649
+ def _apply_functor(self, R):
3650
+ """
3651
+ Apply the functor to an object of ``self``'s domain.
3652
+
3653
+ TESTS::
3654
+
3655
+ sage: F = CDF.construction()[0] # needs sage.rings.complex_double
3656
+ sage: F(QQ) # indirect doctest # needs sage.rings.complex_double sage.rings.number_field
3657
+ Algebraic Field
3658
+ """
3659
+ try:
3660
+ c = R.construction()
3661
+ if c is not None and c[0] == self:
3662
+ return R
3663
+ except AttributeError:
3664
+ pass
3665
+ return R.algebraic_closure()
3666
+
3667
+ def merge(self, other):
3668
+ """
3669
+ Mathematically, Algebraic Closure subsumes Algebraic Extension.
3670
+ However, it seems that people do want to work with algebraic
3671
+ extensions of ``RR``. Therefore, we do not merge with algebraic extension.
3672
+
3673
+ TESTS::
3674
+
3675
+ sage: x = polygen(QQ, 'x')
3676
+ sage: K.<a> = NumberField(x^3 + x^2 + 1) # needs sage.rings.number_field
3677
+ sage: CDF.construction()[0].merge(K.construction()[0]) is None # needs sage.rings.number_field
3678
+ True
3679
+ sage: CDF.construction()[0].merge(CDF.construction()[0]) # needs sage.rings.complex_double
3680
+ AlgebraicClosureFunctor
3681
+ """
3682
+ if self == other:
3683
+ return self
3684
+ return None
3685
+ # Mathematically, Algebraic Closure subsumes Algebraic Extension.
3686
+ # However, it seems that people do want to work with
3687
+ # algebraic extensions of RR (namely RR/poly*RR). So, we don't do:
3688
+ # if isinstance(other,AlgebraicExtensionFunctor):
3689
+ # return self
3690
+
3691
+
3692
+ class PermutationGroupFunctor(ConstructionFunctor):
3693
+
3694
+ rank = 10
3695
+
3696
+ def __init__(self, gens, domain):
3697
+ """
3698
+ EXAMPLES::
3699
+
3700
+ sage: from sage.categories.pushout import PermutationGroupFunctor
3701
+ sage: PF = PermutationGroupFunctor([PermutationGroupElement([(1,2)])], # needs sage.groups
3702
+ ....: [1,2]); PF
3703
+ PermutationGroupFunctor[(1,2)]
3704
+ """
3705
+ Functor.__init__(self, Groups(), Groups())
3706
+ self._gens = tuple(gens)
3707
+ self._domain = domain
3708
+
3709
+ def _repr_(self):
3710
+ """
3711
+ EXAMPLES::
3712
+
3713
+ sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
3714
+ sage: PF, P = P1.construction() # needs sage.groups
3715
+ sage: PF # needs sage.groups
3716
+ PermutationGroupFunctor[(1,2)]
3717
+ """
3718
+ return "PermutationGroupFunctor%s" % list(self.gens())
3719
+
3720
+ def __call__(self, R):
3721
+ """
3722
+ EXAMPLES::
3723
+
3724
+ sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
3725
+ sage: PF, P = P1.construction() # needs sage.groups
3726
+ sage: PF(P) # needs sage.groups
3727
+ Permutation Group with generators [(1,2)]
3728
+ """
3729
+ from sage.groups.perm_gps.permgroup import PermutationGroup
3730
+ return PermutationGroup([g for g in (R.gens() + self.gens()) if not g.is_one()],
3731
+ domain=self._domain)
3732
+
3733
+ def gens(self) -> tuple:
3734
+ """
3735
+ EXAMPLES::
3736
+
3737
+ sage: P1 = PermutationGroup([[(1,2)]]) # needs sage.groups
3738
+ sage: PF, P = P1.construction() # needs sage.groups
3739
+ sage: PF.gens() # needs sage.groups
3740
+ ((1,2),)
3741
+ """
3742
+ return self._gens
3743
+
3744
+ def merge(self, other):
3745
+ """
3746
+ Merge ``self`` with another construction functor, or return ``None``.
3747
+
3748
+ EXAMPLES::
3749
+
3750
+ sage: # needs sage.groups
3751
+ sage: P1 = PermutationGroup([[(1,2)]])
3752
+ sage: PF1, P = P1.construction()
3753
+ sage: P2 = PermutationGroup([[(1,3)]])
3754
+ sage: PF2, P = P2.construction()
3755
+ sage: PF1.merge(PF2)
3756
+ PermutationGroupFunctor[(1,2), (1,3)]
3757
+ """
3758
+ if self.__class__ != other.__class__:
3759
+ return None
3760
+ from sage.sets.finite_enumerated_set import FiniteEnumeratedSet
3761
+
3762
+ new_domain = set(self._domain).union(set(other._domain))
3763
+ try:
3764
+ new_domain = FiniteEnumeratedSet(sorted(new_domain))
3765
+ except TypeError:
3766
+ # Sorting the domain will sometimes fail with Python 3.
3767
+ # Fallback (not ideal: find a better solution?)
3768
+ new_domain = FiniteEnumeratedSet(sorted(new_domain, key=str))
3769
+ return PermutationGroupFunctor(self.gens() + other.gens(),
3770
+ new_domain)
3771
+
3772
+
3773
+ class EquivariantSubobjectConstructionFunctor(ConstructionFunctor):
3774
+ r"""
3775
+ Constructor for subobjects invariant or equivariant under given semigroup actions.
3776
+
3777
+ Let `S` be a semigroup that
3778
+ - acts on a parent `X` as `s \cdot x` (``action``, ``side='left'``) or
3779
+ - acts on `X` as `x \cdot s` (``action``, ``side='right'``),
3780
+ and (possibly trivially)
3781
+ - acts on `X` as `s * x` (``other_action``, ``other_side='left'``) or
3782
+ - acts on `X` as `x * s` (``other_action``, ``other_side='right'``).
3783
+
3784
+ The `S`-equivariant subobject is the subobject
3785
+
3786
+ .. MATH::
3787
+
3788
+ X^S := \{x \in X : s \cdot x = s * x,\, \forall s \in S \}
3789
+
3790
+ when ``side = other_side = 'left'`` and mutatis mutandis for the other values
3791
+ of ``side`` and ``other_side``.
3792
+
3793
+ When ``other_action`` is trivial, `X^S` is called the `S`-invariant subobject.
3794
+
3795
+ EXAMPLES:
3796
+
3797
+ Monoterm symmetries of a tensor, here only for matrices: row (index 0),
3798
+ column (index 1); the order of the extra element 2 in a permutation determines
3799
+ whether it is a symmetry or an antisymmetry::
3800
+
3801
+ sage: # needs sage.groups sage.modules
3802
+ sage: GSym01 = PermutationGroup([[(0,1),(2,),(3,)]]); GSym01
3803
+ Permutation Group with generators [(0,1)]
3804
+ sage: GASym01 = PermutationGroup([[(0,1),(2,3)]]); GASym01
3805
+ Permutation Group with generators [(0,1)(2,3)]
3806
+ sage: from sage.categories.action import Action
3807
+ sage: from sage.structure.element import Matrix
3808
+ sage: class TensorIndexAction(Action):
3809
+ ....: def _act_(self, g, x):
3810
+ ....: if isinstance(x, Matrix):
3811
+ ....: if g(0) == 1:
3812
+ ....: if g(2) == 2:
3813
+ ....: return x.transpose()
3814
+ ....: else:
3815
+ ....: return -x.transpose()
3816
+ ....: else:
3817
+ ....: return x
3818
+ ....: raise NotImplementedError
3819
+ sage: M = matrix([[1, 2], [3, 4]]); M
3820
+ [1 2]
3821
+ [3 4]
3822
+ sage: GSym01_action = TensorIndexAction(GSym01, M.parent())
3823
+ sage: GASym01_action = TensorIndexAction(GASym01, M.parent())
3824
+ sage: GSym01_action.act(GSym01.0, M)
3825
+ [1 3]
3826
+ [2 4]
3827
+ sage: GASym01_action.act(GASym01.0, M)
3828
+ [-1 -3]
3829
+ [-2 -4]
3830
+ sage: Sym01 = M.parent().invariant_module(GSym01, action=GSym01_action); Sym01
3831
+ (Permutation Group with generators [(0,1)])-invariant submodule
3832
+ of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
3833
+ sage: list(Sym01.basis())
3834
+ [B[0], B[1], B[2]]
3835
+ sage: list(Sym01.basis().map(Sym01.lift))
3836
+ [
3837
+ [1 0] [0 1] [0 0]
3838
+ [0 0], [1 0], [0 1]
3839
+ ]
3840
+ sage: ASym01 = M.parent().invariant_module(GASym01, action=GASym01_action)
3841
+ sage: ASym01
3842
+ (Permutation Group with generators [(0,1)(2,3)])-invariant submodule
3843
+ of Full MatrixSpace of 2 by 2 dense matrices over Integer Ring
3844
+ sage: list(ASym01.basis())
3845
+ [B[0]]
3846
+ sage: list(ASym01.basis().map(ASym01.lift))
3847
+ [
3848
+ [ 0 1]
3849
+ [-1 0]
3850
+ ]
3851
+ sage: from sage.categories.pushout import pushout
3852
+ sage: pushout(Sym01, QQ)
3853
+ (Permutation Group with generators [(0,1)])-invariant submodule
3854
+ of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
3855
+ """
3856
+ def __init__(self, S, action=operator.mul, side='left',
3857
+ other_action=None, other_side='left'):
3858
+ """
3859
+ EXAMPLES::
3860
+
3861
+ sage: # needs sage.combinat sage.groups sage.modules
3862
+ sage: G = SymmetricGroup(3); G.rename('S3')
3863
+ sage: M = FreeModule(ZZ, [1,2,3], prefix='M'); M.rename('M')
3864
+ sage: action = lambda g, x: M.term(g(x))
3865
+ sage: I = M.invariant_module(G, action_on_basis=action); I
3866
+ (S3)-invariant submodule of M
3867
+ sage: I.construction()
3868
+ (EquivariantSubobjectConstructionFunctor,
3869
+ Representation of S3 indexed by {1, 2, 3} over Integer Ring)
3870
+ """
3871
+ from sage.categories.sets_cat import Sets
3872
+ super().__init__(Sets(), Sets())
3873
+ self.S = S
3874
+ self.action = action
3875
+ self.side = side
3876
+ self.other_action = other_action
3877
+ self.other_side = other_side
3878
+
3879
+ def _apply_functor(self, X):
3880
+ """
3881
+ Apply the functor to an object of ``self``'s domain.
3882
+
3883
+ TESTS::
3884
+
3885
+ sage: from sage.categories.pushout import EquivariantSubobjectConstructionFunctor
3886
+ sage: M2 = MatrixSpace(QQ, 2); M2 # needs sage.modules
3887
+ Full MatrixSpace of 2 by 2 dense matrices over Rational Field
3888
+ sage: F = EquivariantSubobjectConstructionFunctor(M2, # needs sage.modules
3889
+ ....: operator.mul, 'left',
3890
+ ....: operator.mul, 'right'); F
3891
+ EquivariantSubobjectConstructionFunctor
3892
+ sage: F(M2) # needs sage.modules
3893
+ Traceback (most recent call last):
3894
+ ...
3895
+ NotImplementedError: non-trivial other_action=<built-in function mul> is not implemented
3896
+ """
3897
+ other_action = self.other_action
3898
+ if other_action is not None:
3899
+ raise NotImplementedError(f'non-trivial {other_action=} is not implemented')
3900
+ # Currently only implemented for FiniteDimensionalModulesWithBasis
3901
+ return X.invariant_module(self.S, action=self.action, side=self.side)
3902
+
3903
+
3904
+ class BlackBoxConstructionFunctor(ConstructionFunctor):
3905
+ """
3906
+ Construction functor obtained from any callable object.
3907
+
3908
+ EXAMPLES::
3909
+
3910
+ sage: from sage.categories.pushout import BlackBoxConstructionFunctor
3911
+
3912
+ sage: # needs sage.libs.gap
3913
+ sage: from sage.interfaces.gap import gap
3914
+ sage: FG = BlackBoxConstructionFunctor(gap)
3915
+ sage: FG
3916
+ BlackBoxConstructionFunctor
3917
+ sage: FG(ZZ)
3918
+ Integers
3919
+ sage: FG(ZZ).parent()
3920
+ Gap
3921
+ sage: FG == loads(dumps(FG))
3922
+ True
3923
+
3924
+ sage: FS = BlackBoxConstructionFunctor(singular)
3925
+ sage: FS(QQ['t']) # needs sage.libs.singular
3926
+ polynomial ring, over a field, global ordering
3927
+ // coefficients: QQ...
3928
+ // number of vars : 1
3929
+ // block 1 : ordering lp
3930
+ // : names t
3931
+ // block 2 : ordering C
3932
+ sage: FG == FS # needs sage.libs.gap sage.libs.singular
3933
+ False
3934
+ """
3935
+ rank = 100
3936
+
3937
+ def __init__(self, box):
3938
+ """
3939
+ TESTS::
3940
+
3941
+ sage: from sage.categories.pushout import BlackBoxConstructionFunctor
3942
+ sage: FG = BlackBoxConstructionFunctor(gap)
3943
+ sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
3944
+ sage: FM == FG # needs sage.libs.gap sage.symbolic
3945
+ False
3946
+ sage: FM == loads(dumps(FM)) # needs sage.symbolic
3947
+ True
3948
+ """
3949
+ ConstructionFunctor.__init__(self, Objects(), Objects())
3950
+ if not callable(box):
3951
+ raise TypeError("input must be callable")
3952
+ self.box = box
3953
+
3954
+ def _apply_functor(self, R):
3955
+ """
3956
+ Apply the functor to an object of ``self``'s domain.
3957
+
3958
+ TESTS::
3959
+
3960
+ sage: from sage.categories.pushout import BlackBoxConstructionFunctor
3961
+ sage: f = lambda x: x^2
3962
+ sage: F = BlackBoxConstructionFunctor(f)
3963
+ sage: F(ZZ) # indirect doctest # needs sage.modules
3964
+ Ambient free module of rank 2 over the principal ideal domain Integer Ring
3965
+ """
3966
+ return self.box(R)
3967
+
3968
+ def __eq__(self, other):
3969
+ """
3970
+ TESTS::
3971
+
3972
+ sage: from sage.categories.pushout import BlackBoxConstructionFunctor
3973
+ sage: FG = BlackBoxConstructionFunctor(gap)
3974
+ sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
3975
+ sage: FM == FG # indirect doctest # needs sage.libs.gap sage.symbolic
3976
+ False
3977
+ sage: FM == loads(dumps(FM)) # needs sage.symbolic
3978
+ True
3979
+ """
3980
+ if not isinstance(other, BlackBoxConstructionFunctor):
3981
+ return False
3982
+
3983
+ return self.box == other.box
3984
+
3985
+ def __ne__(self, other):
3986
+ """
3987
+ Check whether ``self`` is not equal to ``other``.
3988
+
3989
+ EXAMPLES::
3990
+
3991
+ sage: from sage.categories.pushout import BlackBoxConstructionFunctor
3992
+ sage: FG = BlackBoxConstructionFunctor(gap)
3993
+ sage: FM = BlackBoxConstructionFunctor(maxima) # needs sage.symbolic
3994
+ sage: FM != FG # indirect doctest # needs sage.libs.gap sage.symbolic
3995
+ True
3996
+ sage: FM != loads(dumps(FM)) # needs sage.symbolic
3997
+ False
3998
+ """
3999
+ return not (self == other)
4000
+
4001
+ __hash__ = ConstructionFunctor.__hash__
4002
+
4003
+
4004
+ def pushout(R, S):
4005
+ r"""
4006
+ Given a pair of objects `R` and `S`, try to construct a
4007
+ reasonable object `Y` and return maps such that
4008
+ canonically `R \leftarrow Y \rightarrow S`.
4009
+
4010
+ ALGORITHM:
4011
+
4012
+ This incorporates the idea of functors discussed at Sage Days 4.
4013
+ Every object `R` can be viewed as an initial object and a series
4014
+ of functors (e.g. polynomial, quotient, extension, completion,
4015
+ vector/matrix, etc.). Call the series of increasingly simple
4016
+ objects (with the associated functors) the "tower" of `R`. The
4017
+ construction method is used to create the tower.
4018
+
4019
+ Given two objects `R` and `S`, try to find a common initial object
4020
+ `Z`. If the towers of `R` and `S` meet, let `Z` be their join.
4021
+ Otherwise, see if the top of one coerces naturally into the other.
4022
+
4023
+ Now we have an initial object and two ordered lists of functors to
4024
+ apply. We wish to merge these in an unambiguous order, popping
4025
+ elements off the top of one or the other tower as we apply them to
4026
+ `Z`.
4027
+
4028
+ - If the functors are of distinct types, there is an absolute
4029
+ ordering given by the rank attribute. Use this.
4030
+
4031
+ - Otherwise:
4032
+
4033
+ - If the tops are equal, we (try to) merge them.
4034
+
4035
+ - If exactly one occurs lower in the other tower, we may
4036
+ unambiguously apply the other (hoping for a later merge).
4037
+
4038
+ - If the tops commute, we can apply either first.
4039
+
4040
+ - Otherwise fail due to ambiguity.
4041
+
4042
+ The algorithm assumes by default that when a construction `F` is
4043
+ applied to an object `X`, the object `F(X)` admits a coercion map
4044
+ from `X`. However, the algorithm can also handle the case where
4045
+ `F(X)` has a coercion map *to* `X` instead. In this case, the
4046
+ attribute ``coercion_reversed`` of the class implementing `F`
4047
+ should be set to ``True``.
4048
+
4049
+ EXAMPLES:
4050
+
4051
+ Here our "towers" are `R = Complete_7(Frac(\ZZ))` and `Frac(Poly_x(\ZZ))`,
4052
+ which give us `Frac(Poly_x(Complete_7(Frac(\ZZ))))`::
4053
+
4054
+ sage: from sage.categories.pushout import pushout
4055
+ sage: pushout(Qp(7), Frac(ZZ['x'])) # needs sage.rings.padics
4056
+ Fraction Field of Univariate Polynomial Ring in x
4057
+ over 7-adic Field with capped relative precision 20
4058
+
4059
+ Note we get the same thing with
4060
+ ::
4061
+
4062
+ sage: pushout(Zp(7), Frac(QQ['x'])) # needs sage.rings.padics
4063
+ Fraction Field of Univariate Polynomial Ring in x
4064
+ over 7-adic Field with capped relative precision 20
4065
+ sage: pushout(Zp(7)['x'], Frac(QQ['x'])) # needs sage.rings.padics
4066
+ Fraction Field of Univariate Polynomial Ring in x
4067
+ over 7-adic Field with capped relative precision 20
4068
+
4069
+ Note that polynomial variable ordering must be unambiguously determined.
4070
+ ::
4071
+
4072
+ sage: pushout(ZZ['x,y,z'], QQ['w,z,t'])
4073
+ Traceback (most recent call last):
4074
+ ...
4075
+ CoercionException: ('Ambiguous Base Extension',
4076
+ Multivariate Polynomial Ring in x, y, z over Integer Ring,
4077
+ Multivariate Polynomial Ring in w, z, t over Rational Field)
4078
+ sage: pushout(ZZ['x,y,z'], QQ['w,x,z,t'])
4079
+ Multivariate Polynomial Ring in w, x, y, z, t over Rational Field
4080
+
4081
+ Some other examples::
4082
+
4083
+ sage: pushout(Zp(7)['y'], Frac(QQ['t'])['x,y,z']) # needs sage.rings.padics
4084
+ Multivariate Polynomial Ring in x, y, z
4085
+ over Fraction Field of Univariate Polynomial Ring in t
4086
+ over 7-adic Field with capped relative precision 20
4087
+ sage: pushout(ZZ['x,y,z'], Frac(ZZ['x'])['y'])
4088
+ Multivariate Polynomial Ring in y, z
4089
+ over Fraction Field of Univariate Polynomial Ring in x over Integer Ring
4090
+ sage: pushout(MatrixSpace(RDF, 2, 2), Frac(ZZ['x'])) # needs sage.modules
4091
+ Full MatrixSpace of 2 by 2 dense matrices
4092
+ over Fraction Field of Univariate Polynomial Ring in x over Real Double Field
4093
+ sage: pushout(ZZ, MatrixSpace(ZZ[['x']], 3, 3)) # needs sage.modules
4094
+ Full MatrixSpace of 3 by 3 dense matrices
4095
+ over Power Series Ring in x over Integer Ring
4096
+ sage: pushout(QQ['x,y'], ZZ[['x']])
4097
+ Univariate Polynomial Ring in y
4098
+ over Power Series Ring in x over Rational Field
4099
+ sage: pushout(Frac(ZZ['x']), QQ[['x']])
4100
+ Laurent Series Ring in x over Rational Field
4101
+
4102
+ A construction with ``coercion_reversed=True`` (currently only
4103
+ the :class:`SubspaceFunctor` construction) is only applied if it
4104
+ leads to a valid coercion::
4105
+
4106
+ sage: # needs sage.modules
4107
+ sage: A = ZZ^2
4108
+ sage: V = span([[1, 2]], QQ)
4109
+ sage: P = sage.categories.pushout.pushout(A, V)
4110
+ sage: P
4111
+ Vector space of dimension 2 over Rational Field
4112
+ sage: P.has_coerce_map_from(A)
4113
+ True
4114
+
4115
+ sage: # needs sage.modules
4116
+ sage: V = (QQ^3).span([[1, 2, 3/4]])
4117
+ sage: A = ZZ^3
4118
+ sage: pushout(A, V)
4119
+ Vector space of dimension 3 over Rational Field
4120
+ sage: B = A.span([[0, 0, 2/3]])
4121
+ sage: pushout(B, V)
4122
+ Vector space of degree 3 and dimension 2 over Rational Field
4123
+ User basis matrix:
4124
+ [1 2 0]
4125
+ [0 0 1]
4126
+
4127
+ Some more tests with ``coercion_reversed=True``::
4128
+
4129
+ sage: from sage.categories.pushout import ConstructionFunctor
4130
+ sage: class EvenPolynomialRing(type(QQ['x'])):
4131
+ ....: def __init__(self, base, var):
4132
+ ....: super().__init__(base, var)
4133
+ ....: self.register_embedding(base[var])
4134
+ ....: def __repr__(self):
4135
+ ....: return "Even Power " + super().__repr__()
4136
+ ....: def construction(self):
4137
+ ....: return EvenPolynomialFunctor(), self.base()[self.variable_name()]
4138
+ ....: def _coerce_map_from_(self, R):
4139
+ ....: return self.base().has_coerce_map_from(R)
4140
+ sage: class EvenPolynomialFunctor(ConstructionFunctor):
4141
+ ....: rank = 10
4142
+ ....: coercion_reversed = True
4143
+ ....: def __init__(self):
4144
+ ....: ConstructionFunctor.__init__(self, Rings(), Rings())
4145
+ ....: def _apply_functor(self, R):
4146
+ ....: return EvenPolynomialRing(R.base(), R.variable_name())
4147
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), ZZ)
4148
+ Even Power Univariate Polynomial Ring in x over Rational Field
4149
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), QQ)
4150
+ Even Power Univariate Polynomial Ring in x over Rational Field
4151
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), RR) # needs sage.rings.real_mpfr
4152
+ Even Power Univariate Polynomial Ring in x over Real Field with 53 bits of precision
4153
+
4154
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), ZZ['x'])
4155
+ Univariate Polynomial Ring in x over Rational Field
4156
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), QQ['x'])
4157
+ Univariate Polynomial Ring in x over Rational Field
4158
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), RR['x']) # needs sage.rings.real_mpfr
4159
+ Univariate Polynomial Ring in x over Real Field with 53 bits of precision
4160
+
4161
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), EvenPolynomialRing(QQ, 'x'))
4162
+ Even Power Univariate Polynomial Ring in x over Rational Field
4163
+ sage: pushout(EvenPolynomialRing(QQ, 'x'), EvenPolynomialRing(RR, 'x')) # needs sage.rings.real_mpfr
4164
+ Even Power Univariate Polynomial Ring in x over Real Field with 53 bits of precision
4165
+
4166
+ sage: pushout(EvenPolynomialRing(QQ, 'x')^2, RR^2) # needs sage.modules sage.rings.real_mpfr
4167
+ Ambient free module of rank 2
4168
+ over the principal ideal domain Even Power Univariate Polynomial Ring in x
4169
+ over Real Field with 53 bits of precision
4170
+ sage: pushout(EvenPolynomialRing(QQ, 'x')^2, RR['x']^2) # needs sage.modules sage.rings.real_mpfr
4171
+ Ambient free module of rank 2
4172
+ over the principal ideal domain Univariate Polynomial Ring in x
4173
+ over Real Field with 53 bits of precision
4174
+
4175
+ Some more tests related to univariate/multivariate
4176
+ constructions. We consider a generalization of polynomial rings,
4177
+ where in addition to the coefficient ring `C` we also specify
4178
+ an additive monoid `E` for the exponents of the indeterminate.
4179
+ In particular, the elements of such a parent are given by
4180
+
4181
+ .. MATH::
4182
+
4183
+ \sum_{i=0}^I c_i X^{e_i}
4184
+
4185
+ with `c_i \in C` and `e_i \in E`. We define
4186
+ ::
4187
+
4188
+ sage: class GPolynomialRing(Parent):
4189
+ ....: def __init__(self, coefficients, var, exponents):
4190
+ ....: self.coefficients = coefficients
4191
+ ....: self.var = var
4192
+ ....: self.exponents = exponents
4193
+ ....: super().__init__(category=Rings())
4194
+ ....: def _repr_(self):
4195
+ ....: return 'Generalized Polynomial Ring in %s^(%s) over %s' % (
4196
+ ....: self.var, self.exponents, self.coefficients)
4197
+ ....: def construction(self):
4198
+ ....: return GPolynomialFunctor(self.var, self.exponents), self.coefficients
4199
+ ....: def _coerce_map_from_(self, R):
4200
+ ....: return self.coefficients.has_coerce_map_from(R)
4201
+
4202
+ and
4203
+ ::
4204
+
4205
+ sage: class GPolynomialFunctor(ConstructionFunctor):
4206
+ ....: rank = 10
4207
+ ....: def __init__(self, var, exponents):
4208
+ ....: self.var = var
4209
+ ....: self.exponents = exponents
4210
+ ....: ConstructionFunctor.__init__(self, Rings(), Rings())
4211
+ ....: def _repr_(self):
4212
+ ....: return 'GPoly[%s^(%s)]' % (self.var, self.exponents)
4213
+ ....: def _apply_functor(self, coefficients):
4214
+ ....: return GPolynomialRing(coefficients, self.var, self.exponents)
4215
+ ....: def merge(self, other):
4216
+ ....: if isinstance(other, GPolynomialFunctor) and self.var == other.var:
4217
+ ....: exponents = pushout(self.exponents, other.exponents)
4218
+ ....: return GPolynomialFunctor(self.var, exponents)
4219
+
4220
+ We can construct a parent now in two different ways::
4221
+
4222
+ sage: GPolynomialRing(QQ, 'X', ZZ)
4223
+ Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
4224
+ sage: GP_ZZ = GPolynomialFunctor('X', ZZ); GP_ZZ
4225
+ GPoly[X^(Integer Ring)]
4226
+ sage: GP_ZZ(QQ)
4227
+ Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
4228
+
4229
+ Since the construction
4230
+ ::
4231
+
4232
+ sage: GP_ZZ(QQ).construction()
4233
+ (GPoly[X^(Integer Ring)], Rational Field)
4234
+
4235
+ uses the coefficient ring, we have the usual coercion with respect
4236
+ to this parameter::
4237
+
4238
+ sage: pushout(GP_ZZ(ZZ), GP_ZZ(QQ))
4239
+ Generalized Polynomial Ring in X^(Integer Ring) over Rational Field
4240
+ sage: pushout(GP_ZZ(ZZ['t']), GP_ZZ(QQ))
4241
+ Generalized Polynomial Ring in X^(Integer Ring)
4242
+ over Univariate Polynomial Ring in t over Rational Field
4243
+ sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(ZZ['b,c']))
4244
+ Generalized Polynomial Ring in X^(Integer Ring)
4245
+ over Multivariate Polynomial Ring in a, b, c over Integer Ring
4246
+ sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(QQ['b,c']))
4247
+ Generalized Polynomial Ring in X^(Integer Ring)
4248
+ over Multivariate Polynomial Ring in a, b, c over Rational Field
4249
+ sage: pushout(GP_ZZ(ZZ['a,b']), GP_ZZ(ZZ['c,d']))
4250
+ Traceback (most recent call last):
4251
+ ...
4252
+ CoercionException: ('Ambiguous Base Extension', ...)
4253
+
4254
+ ::
4255
+
4256
+ sage: GP_QQ = GPolynomialFunctor('X', QQ)
4257
+ sage: pushout(GP_ZZ(ZZ), GP_QQ(ZZ))
4258
+ Generalized Polynomial Ring in X^(Rational Field) over Integer Ring
4259
+ sage: pushout(GP_QQ(ZZ), GP_ZZ(ZZ))
4260
+ Generalized Polynomial Ring in X^(Rational Field) over Integer Ring
4261
+
4262
+ ::
4263
+
4264
+ sage: GP_ZZt = GPolynomialFunctor('X', ZZ['t'])
4265
+ sage: pushout(GP_ZZt(ZZ), GP_QQ(ZZ))
4266
+ Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
4267
+ over Rational Field) over Integer Ring
4268
+
4269
+ ::
4270
+
4271
+ sage: pushout(GP_ZZ(ZZ), GP_QQ(QQ))
4272
+ Generalized Polynomial Ring in X^(Rational Field) over Rational Field
4273
+ sage: pushout(GP_ZZ(QQ), GP_QQ(ZZ))
4274
+ Generalized Polynomial Ring in X^(Rational Field) over Rational Field
4275
+ sage: pushout(GP_ZZt(QQ), GP_QQ(ZZ))
4276
+ Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
4277
+ over Rational Field) over Rational Field
4278
+ sage: pushout(GP_ZZt(ZZ), GP_QQ(QQ))
4279
+ Generalized Polynomial Ring in X^(Univariate Polynomial Ring in t
4280
+ over Rational Field) over Rational Field
4281
+ sage: pushout(GP_ZZt(ZZ['a,b']), GP_QQ(ZZ['c,d']))
4282
+ Traceback (most recent call last):
4283
+ ...
4284
+ CoercionException: ('Ambiguous Base Extension', ...)
4285
+ sage: pushout(GP_ZZt(ZZ['a,b']), GP_QQ(ZZ['b,c']))
4286
+ Generalized Polynomial Ring
4287
+ in X^(Univariate Polynomial Ring in t over Rational Field)
4288
+ over Multivariate Polynomial Ring in a, b, c over Integer Ring
4289
+
4290
+ Some tests with Cartesian products::
4291
+
4292
+ sage: from sage.sets.cartesian_product import CartesianProduct
4293
+ sage: A = CartesianProduct((ZZ['x'], QQ['y'], QQ['z']),
4294
+ ....: Sets().CartesianProducts())
4295
+ sage: B = CartesianProduct((ZZ['x'], ZZ['y'], ZZ['t']['z']),
4296
+ ....: Sets().CartesianProducts())
4297
+ sage: A.construction()
4298
+ (The cartesian_product functorial construction,
4299
+ (Univariate Polynomial Ring in x over Integer Ring,
4300
+ Univariate Polynomial Ring in y over Rational Field,
4301
+ Univariate Polynomial Ring in z over Rational Field))
4302
+ sage: pushout(A, B)
4303
+ The Cartesian product of
4304
+ (Univariate Polynomial Ring in x over Integer Ring,
4305
+ Univariate Polynomial Ring in y over Rational Field,
4306
+ Univariate Polynomial Ring in z over
4307
+ Univariate Polynomial Ring in t over Rational Field)
4308
+ sage: pushout(ZZ, cartesian_product([ZZ, QQ]))
4309
+ Traceback (most recent call last):
4310
+ ...
4311
+ CoercionException: 'NoneType' object is not iterable
4312
+
4313
+ ::
4314
+
4315
+ sage: from sage.categories.pushout import PolynomialFunctor
4316
+ sage: from sage.sets.cartesian_product import CartesianProduct
4317
+ sage: class CartesianProductPoly(CartesianProduct):
4318
+ ....: def __init__(self, polynomial_rings):
4319
+ ....: sort = sorted(polynomial_rings,
4320
+ ....: key=lambda P: P.variable_name())
4321
+ ....: super().__init__(sort, Sets().CartesianProducts())
4322
+ ....: def vars(self):
4323
+ ....: return tuple(P.variable_name()
4324
+ ....: for P in self.cartesian_factors())
4325
+ ....: def _pushout_(self, other):
4326
+ ....: if isinstance(other, CartesianProductPoly):
4327
+ ....: s_vars = self.vars()
4328
+ ....: o_vars = other.vars()
4329
+ ....: if s_vars == o_vars:
4330
+ ....: return
4331
+ ....: return pushout(CartesianProductPoly(
4332
+ ....: self.cartesian_factors() +
4333
+ ....: tuple(f for f in other.cartesian_factors()
4334
+ ....: if f.variable_name() not in s_vars)),
4335
+ ....: CartesianProductPoly(
4336
+ ....: other.cartesian_factors() +
4337
+ ....: tuple(f for f in self.cartesian_factors()
4338
+ ....: if f.variable_name() not in o_vars)))
4339
+ ....: C = other.construction()
4340
+ ....: if C is None:
4341
+ ....: return
4342
+ ....: elif isinstance(C[0], PolynomialFunctor):
4343
+ ....: return pushout(self, CartesianProductPoly((other,)))
4344
+
4345
+ ::
4346
+
4347
+ sage: pushout(CartesianProductPoly((ZZ['x'],)),
4348
+ ....: CartesianProductPoly((ZZ['y'],)))
4349
+ The Cartesian product of
4350
+ (Univariate Polynomial Ring in x over Integer Ring,
4351
+ Univariate Polynomial Ring in y over Integer Ring)
4352
+ sage: pushout(CartesianProductPoly((ZZ['x'], ZZ['y'])),
4353
+ ....: CartesianProductPoly((ZZ['x'], ZZ['z'])))
4354
+ The Cartesian product of
4355
+ (Univariate Polynomial Ring in x over Integer Ring,
4356
+ Univariate Polynomial Ring in y over Integer Ring,
4357
+ Univariate Polynomial Ring in z over Integer Ring)
4358
+ sage: pushout(CartesianProductPoly((QQ['a,b']['x'], QQ['y'])), # needs sage.symbolic
4359
+ ....: CartesianProductPoly((ZZ['b,c']['x'], SR['z'])))
4360
+ The Cartesian product of
4361
+ (Univariate Polynomial Ring in x over
4362
+ Multivariate Polynomial Ring in a, b, c over Rational Field,
4363
+ Univariate Polynomial Ring in y over Rational Field,
4364
+ Univariate Polynomial Ring in z over Symbolic Ring)
4365
+
4366
+ ::
4367
+
4368
+ sage: pushout(CartesianProductPoly((ZZ['x'],)), ZZ['y'])
4369
+ The Cartesian product of
4370
+ (Univariate Polynomial Ring in x over Integer Ring,
4371
+ Univariate Polynomial Ring in y over Integer Ring)
4372
+ sage: pushout(QQ['b,c']['y'], CartesianProductPoly((ZZ['a,b']['x'],)))
4373
+ The Cartesian product of
4374
+ (Univariate Polynomial Ring in x over
4375
+ Multivariate Polynomial Ring in a, b over Integer Ring,
4376
+ Univariate Polynomial Ring in y over
4377
+ Multivariate Polynomial Ring in b, c over Rational Field)
4378
+
4379
+ ::
4380
+
4381
+ sage: pushout(CartesianProductPoly((ZZ['x'],)), ZZ)
4382
+ Traceback (most recent call last):
4383
+ ...
4384
+ CoercionException: No common base ("join") found for
4385
+ The cartesian_product functorial construction(...) and None(Integer Ring):
4386
+ (Multivariate) functors are incompatible.
4387
+
4388
+ AUTHORS:
4389
+
4390
+ - Robert Bradshaw
4391
+ - Peter Bruin
4392
+ - Simon King
4393
+ - Daniel Krenn
4394
+ - David Roe
4395
+ """
4396
+ if R is S or R == S:
4397
+ return R
4398
+
4399
+ if hasattr(R, '_pushout_'):
4400
+ P = R._pushout_(S)
4401
+ if P is not None:
4402
+ return P
4403
+
4404
+ if hasattr(S, '_pushout_'):
4405
+ P = S._pushout_(R)
4406
+ if P is not None:
4407
+ return P
4408
+
4409
+ if isinstance(R, type):
4410
+ R = type_to_parent(R)
4411
+
4412
+ if isinstance(S, type):
4413
+ S = type_to_parent(S)
4414
+
4415
+ R_tower = construction_tower(R)
4416
+ S_tower = construction_tower(S)
4417
+ Rs = [c[1] for c in R_tower]
4418
+ Ss = [c[1] for c in S_tower]
4419
+
4420
+ # If there is a multivariate construction functor in the tower, we must chop off the end
4421
+ # because tuples don't have has_coerce_map_from functions and to align with the
4422
+ # modification of Rs and Ss below
4423
+ from sage.structure.parent import Parent
4424
+ if not isinstance(Rs[-1], Parent):
4425
+ Rs = Rs[:-1]
4426
+ if not isinstance(Ss[-1], Parent):
4427
+ Ss = Ss[:-1]
4428
+
4429
+ if R in Ss:
4430
+ if not any(c[0].coercion_reversed for c in S_tower[1:]):
4431
+ return S
4432
+ elif S in Rs:
4433
+ if not any(c[0].coercion_reversed for c in R_tower[1:]):
4434
+ return R
4435
+
4436
+ if Rs[-1] in Ss:
4437
+ Rs, Ss = Ss, Rs
4438
+ R_tower, S_tower = S_tower, R_tower
4439
+
4440
+ # look for join
4441
+ Z = None
4442
+ if Ss[-1] in Rs:
4443
+ if Rs[-1] == Ss[-1]:
4444
+ while Rs and Ss and Rs[-1] == Ss[-1]:
4445
+ Rs.pop()
4446
+ Z = Ss.pop()
4447
+ else:
4448
+ Rs = Rs[:Rs.index(Ss[-1])]
4449
+ Z = Ss.pop()
4450
+
4451
+ # look for topmost coercion
4452
+ elif S.has_coerce_map_from(Rs[-1]):
4453
+ while not Ss[-1].has_coerce_map_from(Rs[-1]):
4454
+ Ss.pop()
4455
+ while Rs and Ss[-1].has_coerce_map_from(Rs[-1]):
4456
+ Rs.pop()
4457
+ Z = Ss.pop()
4458
+
4459
+ elif R.has_coerce_map_from(Ss[-1]):
4460
+ while not Rs[-1].has_coerce_map_from(Ss[-1]):
4461
+ Rs.pop()
4462
+ while Ss and Rs[-1].has_coerce_map_from(Ss[-1]):
4463
+ Ss.pop()
4464
+ Z = Rs.pop()
4465
+
4466
+ if Z is None and R_tower[-1][0] is not None:
4467
+ Z = R_tower[-1][0].common_base(S_tower[-1][0], R_tower[-1][1], S_tower[-1][1])
4468
+ R_tower = expand_tower(R_tower[:len(Rs)])
4469
+ S_tower = expand_tower(S_tower[:len(Ss)])
4470
+ else:
4471
+ # Rc is a list of functors from Z to R and Sc is a list of functors from Z to S
4472
+ R_tower = expand_tower(R_tower[:len(Rs) + 1])
4473
+ S_tower = expand_tower(S_tower[:len(Ss) + 1])
4474
+ Rc = [c[0] for c in R_tower[1:]]
4475
+ Sc = [c[0] for c in S_tower[1:]]
4476
+
4477
+ all = IdentityConstructionFunctor()
4478
+
4479
+ def apply_from(Xc):
4480
+ c = Xc.pop()
4481
+ if c.coercion_reversed:
4482
+ Yc = Sc if Xc is Rc else Rc
4483
+ Y_tower = S_tower if Xc is Rc else R_tower
4484
+ Y_partial = Y_tower[len(Yc)][1]
4485
+ if not (c * all)(Z).has_coerce_map_from(Y_partial):
4486
+ return all
4487
+ return c * all
4488
+
4489
+ try:
4490
+ while Rc or Sc:
4491
+ # if we are out of functors in either tower, there is no ambiguity
4492
+ if not Sc:
4493
+ all = apply_from(Rc)
4494
+ elif not Rc:
4495
+ all = apply_from(Sc)
4496
+ # if one of the functors has lower rank, do it first
4497
+ elif Rc[-1].rank < Sc[-1].rank:
4498
+ all = apply_from(Rc)
4499
+ elif Sc[-1].rank < Rc[-1].rank:
4500
+ all = apply_from(Sc)
4501
+ # the ranks are the same, so things are a bit subtler
4502
+ elif Rc[-1] == Sc[-1]:
4503
+ # If they are indeed the same operation, we only do it once.
4504
+ # The \code{merge} function here takes into account non-mathematical
4505
+ # distinctions (e.g. single vs. multivariate polynomials).
4506
+ cR = Rc.pop()
4507
+ cS = Sc.pop()
4508
+ c = cR.merge(cS) or cS.merge(cR)
4509
+ if c:
4510
+ all = c * all
4511
+ else:
4512
+ raise CoercionException("Incompatible Base Extension %r, %r (on %r, %r)" % (R, S, cR, cS))
4513
+ # Now we look ahead to see if either top functor is
4514
+ # applied later on in the other tower.
4515
+ # If this is the case for exactly one of them, we unambiguously
4516
+ # postpone that operation, but if both then we abort.
4517
+ elif Rc[-1] in Sc:
4518
+ if Sc[-1] in Rc:
4519
+ raise CoercionException("Ambiguous Base Extension", R, S)
4520
+ else:
4521
+ all = apply_from(Sc)
4522
+ elif Sc[-1] in Rc:
4523
+ all = apply_from(Rc)
4524
+ # If, perchance, the two functors commute, then we may do them in any order.
4525
+ elif Rc[-1].commutes(Sc[-1]) or Sc[-1].commutes(Rc[-1]):
4526
+ all = Sc.pop() * Rc.pop() * all
4527
+ else:
4528
+ # try and merge (default merge is failure for unequal functors)
4529
+ cR = Rc.pop()
4530
+ cS = Sc.pop()
4531
+ c = cR.merge(cS) or cS.merge(cR)
4532
+ if c is not None:
4533
+ all = c * all
4534
+ else:
4535
+ # Otherwise, we cannot proceed.
4536
+ raise CoercionException("Ambiguous Base Extension", R, S)
4537
+
4538
+ return all(Z)
4539
+
4540
+ except CoercionException:
4541
+ raise
4542
+ except (TypeError, ValueError, AttributeError, NotImplementedError) as ex:
4543
+ # We do this because we may be trying all kinds of things that don't
4544
+ # make sense, and in this case simply want to return that a pushout
4545
+ # couldn't be found.
4546
+ raise CoercionException(ex)
4547
+
4548
+
4549
+ def pushout_lattice(R, S):
4550
+ r"""
4551
+ Given a pair of objects `R` and `S`, try to construct a
4552
+ reasonable object `Y` and return maps such that
4553
+ canonically `R \leftarrow Y \rightarrow S`.
4554
+
4555
+ ALGORITHM:
4556
+
4557
+ This is based on the model that arose from much discussion at
4558
+ Sage Days 4. Going up the tower of constructions of `R` and `S`
4559
+ (e.g. the reals come from the rationals come from the integers),
4560
+ try to find a common parent, and then try to fill in a lattice
4561
+ with these two towers as sides with the top as the common ancestor
4562
+ and the bottom will be the desired ring.
4563
+
4564
+ See the code for a specific worked-out example.
4565
+
4566
+ EXAMPLES::
4567
+
4568
+ sage: from sage.categories.pushout import pushout_lattice
4569
+ sage: A, B = pushout_lattice(Qp(7), Frac(ZZ['x'])) # needs sage.rings.padics
4570
+ sage: A.codomain() # needs sage.rings.padics
4571
+ Fraction Field of Univariate Polynomial Ring in x
4572
+ over 7-adic Field with capped relative precision 20
4573
+ sage: A.codomain() is B.codomain() # needs sage.rings.padics
4574
+ True
4575
+ sage: A, B = pushout_lattice(ZZ, MatrixSpace(ZZ[['x']], 3, 3)) # needs sage.modules
4576
+ sage: B # needs sage.modules
4577
+ Identity endomorphism of Full MatrixSpace of 3 by 3 dense matrices
4578
+ over Power Series Ring in x over Integer Ring
4579
+
4580
+ AUTHOR:
4581
+
4582
+ - Robert Bradshaw
4583
+ """
4584
+ R_tower = construction_tower(R)
4585
+ S_tower = construction_tower(S)
4586
+ Rs = [c[1] for c in R_tower]
4587
+ Ss = [c[1] for c in S_tower]
4588
+
4589
+ # look for common ancestor
4590
+ start = None
4591
+ for Z in Rs:
4592
+ if Z in Ss:
4593
+ start = Z
4594
+ if start is None:
4595
+ # Should I test for a map between the tops of the towers?
4596
+ # Or, if they're both not ZZ, is it hopeless?
4597
+ return None
4598
+
4599
+ # truncate at common ancestor
4600
+ R_tower = list(reversed(R_tower[:Rs.index(start) + 1]))
4601
+ S_tower = list(reversed(S_tower[:Ss.index(start) + 1]))
4602
+ Rs = [c[1] for c in R_tower] # the list of objects
4603
+ Ss = [c[1] for c in S_tower]
4604
+ Rc = [c[0] for c in R_tower] # the list of functors
4605
+ Sc = [c[0] for c in S_tower]
4606
+
4607
+ # Here we try and construct a 2-dimensional lattice as follows.
4608
+ # Suppose our towers are Z -> Q -> Qp = R and Z -> Z[t] -> Frac(Z[t]) = S
4609
+ lattice = {}
4610
+ # First we fill in the sides
4611
+ #
4612
+ # Z
4613
+ # / \
4614
+ # Q Z[t]
4615
+ # / \
4616
+ # Qp Frac(Z[t])
4617
+ #
4618
+ for i, Rsi in enumerate(Rs):
4619
+ lattice[i, 0] = Rsi
4620
+ for j, Ssj in enumerate(Ss):
4621
+ lattice[0, j] = Ssj
4622
+
4623
+ # Now we attempt to fill in the center, one (diagonal) row at a time,
4624
+ # one commuting square at a time.
4625
+ #
4626
+ # Z
4627
+ # / \
4628
+ # Q Z[t]
4629
+ # / \ / \
4630
+ # Qp Q[t] Frac(Z[t])
4631
+ # \ /
4632
+ # Qp[t]
4633
+ #
4634
+ # There is always exactly one "correct" path/order in which to apply operations
4635
+ # from the top to the bottom. In our example, this is down the far left side.
4636
+ # We keep track of which that is by clearing out Rc and Sc as we go along.
4637
+ #
4638
+ # Note that when applying the functors in the correct order, base extension
4639
+ # is not needed (though it may occur in the resulting morphisms).
4640
+ #
4641
+ for i in range(len(Rc) - 1):
4642
+ for j in range(len(Sc) - 1):
4643
+ try:
4644
+ if lattice[i, j + 1] == lattice[i + 1, j]:
4645
+ # In this case we have R <- S -> R
4646
+ # We don't want to perform the operation twice
4647
+ # and all subsequent squares will come from objects
4648
+ # where the operation was already performed (either
4649
+ # to the left or right)
4650
+ Rc[i] = Sc[j] = None # IdentityConstructionFunctor()
4651
+ lattice[i + 1, j + 1] = lattice[i, j + 1]
4652
+ elif Rc[i] is None and Sc[j] is None:
4653
+ lattice[i + 1, j + 1] = lattice[i, j + 1]
4654
+ elif Rc[i] is None:
4655
+ lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j])
4656
+ elif Sc[j] is None:
4657
+ lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1])
4658
+ # For now, we just look at the rank.
4659
+ # TODO: be more sophisticated and query the functors themselves
4660
+ elif Rc[i].rank < Sc[j].rank:
4661
+ lattice[i + 1, j + 1] = Sc[j](lattice[i + 1, j])
4662
+ Rc[i] = None # force us to use pre-applied Rc[i]
4663
+ else:
4664
+ lattice[i + 1, j + 1] = Rc[i](lattice[i, j + 1])
4665
+ Sc[j] = None # force us to use pre-applied Sc[i]
4666
+ except (AttributeError, NameError):
4667
+ # pp(lattice)
4668
+ for ni in range(100):
4669
+ for nj in range(100):
4670
+ try:
4671
+ R = lattice[ni, nj]
4672
+ print(ni, nj, R)
4673
+ except KeyError:
4674
+ break
4675
+ raise CoercionException("%s does not support %s"
4676
+ % (lattice[ni, nj], 'F'))
4677
+
4678
+ # If we are successful, we should have something that looks like this.
4679
+ #
4680
+ # Z
4681
+ # / \
4682
+ # Q Z[t]
4683
+ # / \ / \
4684
+ # Qp Q[t] Frac(Z[t])
4685
+ # \ / \ /
4686
+ # Qp[t] Frac(Q[t])
4687
+ # \ /
4688
+ # Frac(Qp[t])
4689
+ #
4690
+ R_loc = len(Rs) - 1
4691
+ S_loc = len(Ss) - 1
4692
+
4693
+ # Find the composition coercion morphisms along the bottom left...
4694
+ if S_loc > 0:
4695
+ R_map = lattice[R_loc, 1].coerce_map_from(R)
4696
+ for i in range(1, S_loc):
4697
+ map = lattice[R_loc, i + 1].coerce_map_from(lattice[R_loc, i])
4698
+ # The functor used is implicit here, should it be?
4699
+ R_map = map * R_map
4700
+ else:
4701
+ R_map = R.coerce_map_from(R) # id
4702
+
4703
+ # ... and bottom right
4704
+ if R_loc > 0:
4705
+ S_map = lattice[1, S_loc].coerce_map_from(S)
4706
+ for i in range(1, R_loc):
4707
+ map = lattice[i + 1, S_loc].coerce_map_from(lattice[i, S_loc])
4708
+ S_map = map * S_map
4709
+ else:
4710
+ S_map = S.coerce_map_from(S) # id
4711
+
4712
+ return R_map, S_map
4713
+
4714
+
4715
+ # def pp(lattice):
4716
+ # """
4717
+ # Used in debugging to print the current lattice.
4718
+ # """
4719
+ # for i in range(100):
4720
+ # for j in range(100):
4721
+ # try:
4722
+ # R = lattice[i,j]
4723
+ # print(i, j, R)
4724
+ # except KeyError:
4725
+ # break
4726
+
4727
+
4728
+ def construction_tower(R):
4729
+ """
4730
+ An auxiliary function that is used in :func:`pushout` and :func:`pushout_lattice`.
4731
+
4732
+ INPUT:
4733
+
4734
+ - ``R`` -- an object
4735
+
4736
+ OUTPUT:
4737
+
4738
+ A constructive description of the object from scratch, by a list of pairs
4739
+ of a construction functor and an object to which the construction functor
4740
+ is to be applied. The first pair is formed by ``None`` and the given object.
4741
+
4742
+ EXAMPLES::
4743
+
4744
+ sage: from sage.categories.pushout import construction_tower
4745
+ sage: construction_tower(MatrixSpace(FractionField(QQ['t']), 2)) # needs sage.modules
4746
+ [(None, Full MatrixSpace of 2 by 2 dense matrices over Fraction Field
4747
+ of Univariate Polynomial Ring in t over Rational Field),
4748
+ (MatrixFunctor, Fraction Field
4749
+ of Univariate Polynomial Ring in t over Rational Field),
4750
+ (FractionField, Univariate Polynomial Ring in t over Rational Field),
4751
+ (Poly[t], Rational Field), (FractionField, Integer Ring)]
4752
+ """
4753
+ tower = [(None, R)]
4754
+ c = R.construction()
4755
+ from sage.structure.parent import Parent
4756
+ while c is not None:
4757
+ f, R = c
4758
+ if not isinstance(f, ConstructionFunctor):
4759
+ f = BlackBoxConstructionFunctor(f)
4760
+ tower.append((f, R))
4761
+ if not isinstance(R, Parent):
4762
+ break
4763
+ c = R.construction()
4764
+ return tower
4765
+
4766
+
4767
+ def expand_tower(tower):
4768
+ """
4769
+ An auxiliary function that is used in :func:`pushout`.
4770
+
4771
+ INPUT:
4772
+
4773
+ - ``tower`` -- a construction tower as returned by
4774
+ :func:`construction_tower`
4775
+
4776
+ OUTPUT: a new construction tower with all the construction functors expanded
4777
+
4778
+ EXAMPLES::
4779
+
4780
+ sage: from sage.categories.pushout import construction_tower, expand_tower
4781
+ sage: construction_tower(QQ['x,y,z'])
4782
+ [(None, Multivariate Polynomial Ring in x, y, z over Rational Field),
4783
+ (MPoly[x,y,z], Rational Field),
4784
+ (FractionField, Integer Ring)]
4785
+ sage: expand_tower(construction_tower(QQ['x,y,z']))
4786
+ [(None, Multivariate Polynomial Ring in x, y, z over Rational Field),
4787
+ (MPoly[z], Univariate Polynomial Ring in y
4788
+ over Univariate Polynomial Ring in x over Rational Field),
4789
+ (MPoly[y], Univariate Polynomial Ring in x over Rational Field),
4790
+ (MPoly[x], Rational Field),
4791
+ (FractionField, Integer Ring)]
4792
+ """
4793
+ new_tower = []
4794
+ for f, R in reversed(tower):
4795
+ if f is None:
4796
+ new_tower.append((f, R))
4797
+ else:
4798
+ fs = f.expand()
4799
+ for ff in reversed(fs[1:]):
4800
+ new_tower.append((ff, R))
4801
+ R = ff(R)
4802
+ new_tower.append((fs[0], R))
4803
+ return list(reversed(new_tower))
4804
+
4805
+
4806
+ def type_to_parent(P):
4807
+ """
4808
+ An auxiliary function that is used in :func:`pushout`.
4809
+
4810
+ INPUT:
4811
+
4812
+ - ``P`` -- a type
4813
+
4814
+ OUTPUT: a Sage parent structure corresponding to the given type
4815
+
4816
+ TESTS::
4817
+
4818
+ sage: from sage.categories.pushout import type_to_parent
4819
+ sage: type_to_parent(int)
4820
+ Integer Ring
4821
+ sage: type_to_parent(float)
4822
+ Real Double Field
4823
+ sage: type_to_parent(complex) # needs sage.rings.complex_double
4824
+ Complex Double Field
4825
+ sage: type_to_parent(list)
4826
+ Traceback (most recent call last):
4827
+ ...
4828
+ TypeError: not a scalar type
4829
+ """
4830
+ from sage.structure.coerce import py_scalar_parent
4831
+ parent = py_scalar_parent(P)
4832
+ if parent is None:
4833
+ raise TypeError("not a scalar type")
4834
+ return parent