passagemath-objects 10.8.1a3__cp314-cp314-win_amd64.whl

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