passagemath-symbolics 10.6.43__cp314-cp314t-musllinux_1_2_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


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

Files changed (171) hide show
  1. passagemath_symbolics/__init__.py +3 -0
  2. passagemath_symbolics-10.6.43.dist-info/METADATA +187 -0
  3. passagemath_symbolics-10.6.43.dist-info/RECORD +171 -0
  4. passagemath_symbolics-10.6.43.dist-info/WHEEL +5 -0
  5. passagemath_symbolics-10.6.43.dist-info/top_level.txt +3 -0
  6. sage/all__sagemath_symbolics.py +17 -0
  7. sage/calculus/all.py +14 -0
  8. sage/calculus/calculus.py +2826 -0
  9. sage/calculus/desolvers.py +1866 -0
  10. sage/calculus/predefined.py +51 -0
  11. sage/calculus/tests.py +225 -0
  12. sage/calculus/var.cpython-314t-x86_64-linux-musl.so +0 -0
  13. sage/calculus/var.pyx +401 -0
  14. sage/dynamics/all__sagemath_symbolics.py +6 -0
  15. sage/dynamics/complex_dynamics/all.py +5 -0
  16. sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
  17. sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-x86_64-linux-musl.so +0 -0
  18. sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
  19. sage/ext/all__sagemath_symbolics.py +1 -0
  20. sage/ext_data/kenzo/CP2.txt +45 -0
  21. sage/ext_data/kenzo/CP3.txt +349 -0
  22. sage/ext_data/kenzo/CP4.txt +4774 -0
  23. sage/ext_data/kenzo/README.txt +49 -0
  24. sage/ext_data/kenzo/S4.txt +20 -0
  25. sage/ext_data/magma/latex/latex.m +1021 -0
  26. sage/ext_data/magma/latex/latex.spec +1 -0
  27. sage/ext_data/magma/sage/basic.m +356 -0
  28. sage/ext_data/magma/sage/sage.spec +1 -0
  29. sage/ext_data/magma/spec +9 -0
  30. sage/geometry/all__sagemath_symbolics.py +8 -0
  31. sage/geometry/hyperbolic_space/all.py +5 -0
  32. sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
  33. sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
  34. sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
  35. sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
  36. sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
  37. sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
  38. sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
  39. sage/geometry/riemannian_manifolds/all.py +7 -0
  40. sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
  41. sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
  42. sage/interfaces/all__sagemath_symbolics.py +1 -0
  43. sage/interfaces/magma.py +3017 -0
  44. sage/interfaces/magma_free.py +92 -0
  45. sage/interfaces/maple.py +1397 -0
  46. sage/interfaces/mathematica.py +1345 -0
  47. sage/interfaces/mathics.py +1312 -0
  48. sage/interfaces/sympy.py +1398 -0
  49. sage/interfaces/sympy_wrapper.py +197 -0
  50. sage/interfaces/tides.py +938 -0
  51. sage/libs/all__sagemath_symbolics.py +6 -0
  52. sage/manifolds/all.py +7 -0
  53. sage/manifolds/calculus_method.py +555 -0
  54. sage/manifolds/catalog.py +437 -0
  55. sage/manifolds/chart.py +4019 -0
  56. sage/manifolds/chart_func.py +3419 -0
  57. sage/manifolds/continuous_map.py +2183 -0
  58. sage/manifolds/continuous_map_image.py +155 -0
  59. sage/manifolds/differentiable/affine_connection.py +2475 -0
  60. sage/manifolds/differentiable/all.py +1 -0
  61. sage/manifolds/differentiable/automorphismfield.py +1383 -0
  62. sage/manifolds/differentiable/automorphismfield_group.py +604 -0
  63. sage/manifolds/differentiable/bundle_connection.py +1445 -0
  64. sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
  65. sage/manifolds/differentiable/chart.py +1241 -0
  66. sage/manifolds/differentiable/curve.py +1028 -0
  67. sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
  68. sage/manifolds/differentiable/degenerate.py +559 -0
  69. sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
  70. sage/manifolds/differentiable/diff_form.py +1658 -0
  71. sage/manifolds/differentiable/diff_form_module.py +1062 -0
  72. sage/manifolds/differentiable/diff_map.py +1315 -0
  73. sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
  74. sage/manifolds/differentiable/examples/all.py +1 -0
  75. sage/manifolds/differentiable/examples/euclidean.py +2517 -0
  76. sage/manifolds/differentiable/examples/real_line.py +897 -0
  77. sage/manifolds/differentiable/examples/sphere.py +1186 -0
  78. sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
  79. sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
  80. sage/manifolds/differentiable/integrated_curve.py +4035 -0
  81. sage/manifolds/differentiable/levi_civita_connection.py +841 -0
  82. sage/manifolds/differentiable/manifold.py +4254 -0
  83. sage/manifolds/differentiable/manifold_homset.py +1826 -0
  84. sage/manifolds/differentiable/metric.py +3032 -0
  85. sage/manifolds/differentiable/mixed_form.py +1507 -0
  86. sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
  87. sage/manifolds/differentiable/multivector_module.py +800 -0
  88. sage/manifolds/differentiable/multivectorfield.py +1520 -0
  89. sage/manifolds/differentiable/poisson_tensor.py +268 -0
  90. sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
  91. sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
  92. sage/manifolds/differentiable/scalarfield.py +1343 -0
  93. sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
  94. sage/manifolds/differentiable/symplectic_form.py +910 -0
  95. sage/manifolds/differentiable/symplectic_form_test.py +220 -0
  96. sage/manifolds/differentiable/tangent_space.py +412 -0
  97. sage/manifolds/differentiable/tangent_vector.py +616 -0
  98. sage/manifolds/differentiable/tensorfield.py +4665 -0
  99. sage/manifolds/differentiable/tensorfield_module.py +963 -0
  100. sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
  101. sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
  102. sage/manifolds/differentiable/vector_bundle.py +1728 -0
  103. sage/manifolds/differentiable/vectorfield.py +1717 -0
  104. sage/manifolds/differentiable/vectorfield_module.py +2445 -0
  105. sage/manifolds/differentiable/vectorframe.py +1832 -0
  106. sage/manifolds/family.py +270 -0
  107. sage/manifolds/local_frame.py +1490 -0
  108. sage/manifolds/manifold.py +3090 -0
  109. sage/manifolds/manifold_homset.py +452 -0
  110. sage/manifolds/operators.py +359 -0
  111. sage/manifolds/point.py +994 -0
  112. sage/manifolds/scalarfield.py +3718 -0
  113. sage/manifolds/scalarfield_algebra.py +629 -0
  114. sage/manifolds/section.py +3111 -0
  115. sage/manifolds/section_module.py +831 -0
  116. sage/manifolds/structure.py +229 -0
  117. sage/manifolds/subset.py +2764 -0
  118. sage/manifolds/subsets/all.py +1 -0
  119. sage/manifolds/subsets/closure.py +131 -0
  120. sage/manifolds/subsets/pullback.py +885 -0
  121. sage/manifolds/topological_submanifold.py +891 -0
  122. sage/manifolds/trivialization.py +733 -0
  123. sage/manifolds/utilities.py +1348 -0
  124. sage/manifolds/vector_bundle.py +1342 -0
  125. sage/manifolds/vector_bundle_fiber.py +332 -0
  126. sage/manifolds/vector_bundle_fiber_element.py +111 -0
  127. sage/matrix/all__sagemath_symbolics.py +1 -0
  128. sage/matrix/matrix_symbolic_dense.cpython-314t-x86_64-linux-musl.so +0 -0
  129. sage/matrix/matrix_symbolic_dense.pxd +6 -0
  130. sage/matrix/matrix_symbolic_dense.pyx +1022 -0
  131. sage/matrix/matrix_symbolic_sparse.cpython-314t-x86_64-linux-musl.so +0 -0
  132. sage/matrix/matrix_symbolic_sparse.pxd +6 -0
  133. sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
  134. sage/modules/all__sagemath_symbolics.py +1 -0
  135. sage/modules/vector_callable_symbolic_dense.py +105 -0
  136. sage/modules/vector_symbolic_dense.py +116 -0
  137. sage/modules/vector_symbolic_sparse.py +118 -0
  138. sage/rings/all__sagemath_symbolics.py +4 -0
  139. sage/rings/asymptotic/all.py +6 -0
  140. sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
  141. sage/rings/asymptotic/asymptotic_ring.py +4858 -0
  142. sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
  143. sage/rings/asymptotic/growth_group.py +5373 -0
  144. sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
  145. sage/rings/asymptotic/term_monoid.py +5237 -0
  146. sage/rings/function_field/all__sagemath_symbolics.py +2 -0
  147. sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
  148. sage/symbolic/all.py +15 -0
  149. sage/symbolic/assumptions.py +985 -0
  150. sage/symbolic/benchmark.py +93 -0
  151. sage/symbolic/callable.py +459 -0
  152. sage/symbolic/complexity_measures.py +35 -0
  153. sage/symbolic/constants.py +1287 -0
  154. sage/symbolic/expression_conversion_algebraic.py +310 -0
  155. sage/symbolic/expression_conversion_sympy.py +317 -0
  156. sage/symbolic/expression_conversions.py +1713 -0
  157. sage/symbolic/function_factory.py +355 -0
  158. sage/symbolic/integration/all.py +1 -0
  159. sage/symbolic/integration/external.py +270 -0
  160. sage/symbolic/integration/integral.py +1115 -0
  161. sage/symbolic/maxima_wrapper.py +162 -0
  162. sage/symbolic/operators.py +267 -0
  163. sage/symbolic/random_tests.py +462 -0
  164. sage/symbolic/relation.py +1907 -0
  165. sage/symbolic/ring.cpython-314t-x86_64-linux-musl.so +0 -0
  166. sage/symbolic/ring.pxd +5 -0
  167. sage/symbolic/ring.pyx +1396 -0
  168. sage/symbolic/subring.py +1025 -0
  169. sage/symbolic/symengine.py +19 -0
  170. sage/symbolic/tests.py +40 -0
  171. sage/symbolic/units.py +1470 -0
@@ -0,0 +1,4153 @@
1
+ # sage_setup: distribution = sagemath-symbolics
2
+ # sage.doctest: needs sage.graphs sage.libs.pari sage.libs.singular
3
+ r"""
4
+ Asymptotics of Multivariate Generating Series
5
+
6
+ Let `F(x) = \sum_{\nu \in \NN^d} F_{\nu} x^\nu` be a multivariate power series
7
+ with complex coefficients that converges in a neighborhood of the origin.
8
+ Assume that `F = G/H` for some functions `G` and `H` holomorphic in a
9
+ neighborhood of the origin. Assume also that `H` is a polynomial.
10
+
11
+ This computes asymptotics for the coefficients `F_{r \alpha}` as `r \to \infty`
12
+ with `r \alpha \in \NN^d` for `\alpha` in a permissible subset of `d`-tuples of
13
+ positive reals. More specifically, it computes arbitrary terms of the
14
+ asymptotic expansion for `F_{r \alpha}` when the asymptotics are controlled by
15
+ a strictly minimal multiple point of the algebraic variety `H = 0`.
16
+
17
+ The algorithms and formulas implemented here come from [RW2008]_
18
+ and [RW2012]_. For a general reference take a look in the book [PW2013]_.
19
+
20
+
21
+ Introductory Examples
22
+ =====================
23
+
24
+ ::
25
+
26
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
27
+
28
+ A univariate smooth point example::
29
+
30
+ sage: R.<x> = PolynomialRing(QQ)
31
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
32
+ sage: H = (x - 1/2)^3
33
+ sage: Hfac = H.factor()
34
+ sage: G = -1/(x + 3)/Hfac.unit()
35
+ sage: F = FFPD(G, Hfac)
36
+ sage: F
37
+ (-1/(x + 3), [(x - 1/2, 3)])
38
+ sage: alpha = [1]
39
+ sage: decomp = F.asymptotic_decomposition(alpha)
40
+ sage: decomp
41
+ (0, []) +
42
+ (-1/2*r^2*(x^2/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
43
+ + 6*x/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
44
+ + 9/(x^5 + 9*x^4 + 27*x^3 + 27*x^2))
45
+ - 1/2*r*(5*x^2/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
46
+ + 24*x/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
47
+ + 27/(x^5 + 9*x^4 + 27*x^3 + 27*x^2))
48
+ - 3*x^2/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
49
+ - 9*x/(x^5 + 9*x^4 + 27*x^3 + 27*x^2)
50
+ - 9/(x^5 + 9*x^4 + 27*x^3 + 27*x^2),
51
+ [(x - 1/2, 1)])
52
+ sage: F1 = decomp[1]
53
+ sage: p = {x: 1/2}
54
+ sage: asy = F1.asymptotics(p, alpha, 3)
55
+ sage: asy
56
+ (8/343*(49*r^2 + 161*r + 114)*2^r, 2, 8/7*r^2 + 184/49*r + 912/343)
57
+ sage: F.relative_error(asy[0], alpha, [1, 2, 4, 8, 16], asy[1])
58
+ [((1,), 7.555555556, [7.556851312], [-0.0001714971672]),
59
+ ((2,), 14.74074074, [14.74052478], [0.00001465051901]),
60
+ ((4,), 35.96502058, [35.96501458], [1.667911934e-7]),
61
+ ((8,), 105.8425656, [105.8425656], [4.399565380e-11]),
62
+ ((16,), 355.3119534, [355.3119534], [0.0000000000])]
63
+
64
+ Another smooth point example (Example 5.4 of [RW2008]_)::
65
+
66
+ sage: R.<x,y> = PolynomialRing(QQ)
67
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
68
+ sage: q = 1/2
69
+ sage: qq = q.denominator()
70
+ sage: H = 1 - q*x + q*x*y - x^2*y
71
+ sage: Hfac = H.factor()
72
+ sage: G = (1 - q*x)/Hfac.unit()
73
+ sage: F = FFPD(G, Hfac)
74
+ sage: alpha = list(qq*vector([2, 1 - q]))
75
+ sage: alpha
76
+ [4, 1]
77
+ sage: I = F.smooth_critical_ideal(alpha)
78
+ sage: I
79
+ Ideal (y^2 - 2*y + 1, x + 1/4*y - 5/4) of
80
+ Multivariate Polynomial Ring in x, y over Rational Field
81
+ sage: s = solve([SR(z) for z in I.gens()],
82
+ ....: [SR(z) for z in R.gens()], solution_dict=true)
83
+ sage: s == [{SR(x): 1, SR(y): 1}]
84
+ True
85
+ sage: p = s[0]
86
+ sage: asy = F.asymptotics(p, alpha, 1, verbose=True)
87
+ Creating auxiliary functions...
88
+ Computing derivatives of auxiliary functions...
89
+ Computing derivatives of more auxiliary functions...
90
+ Computing second order differential operator actions...
91
+ sage: asy
92
+ (1/24*2^(2/3)*(sqrt(3) + 4/(sqrt(3) + I) + I)*gamma(1/3)/(pi*r^(1/3)),
93
+ 1,
94
+ 1/24*2^(2/3)*(sqrt(3) + 4/(sqrt(3) + I) + I)*gamma(1/3)/(pi*r^(1/3)))
95
+ sage: r = SR('r')
96
+ sage: tuple((a*r^(1/3)).full_simplify() / r^(1/3) for a in asy) # make nicer coefficients
97
+ (1/12*sqrt(3)*2^(2/3)*gamma(1/3)/(pi*r^(1/3)),
98
+ 1,
99
+ 1/12*sqrt(3)*2^(2/3)*gamma(1/3)/(pi*r^(1/3)))
100
+ sage: F.relative_error(asy[0], alpha, [1, 2, 4, 8, 16], asy[1])
101
+ [((4, 1), 0.1875000000, [0.1953794675...], [-0.042023826...]),
102
+ ((8, 2), 0.1523437500, [0.1550727862...], [-0.017913673...]),
103
+ ((16, 4), 0.1221771240, [0.1230813519...], [-0.0074009592...]),
104
+ ((32, 8), 0.09739671811, [0.09768973377...], [-0.0030084757...]),
105
+ ((64, 16), 0.07744253816, [0.07753639308...], [-0.0012119297...])]
106
+
107
+ A multiple point example (Example 6.5 of [RW2012]_)::
108
+
109
+ sage: R.<x,y> = PolynomialRing(QQ)
110
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
111
+ sage: H = (1 - 2*x - y)**2 * (1 - x - 2*y)**2
112
+ sage: Hfac = H.factor()
113
+ sage: G = 1/Hfac.unit()
114
+ sage: F = FFPD(G, Hfac)
115
+ sage: F
116
+ (1, [(x + 2*y - 1, 2), (2*x + y - 1, 2)])
117
+ sage: I = F.singular_ideal()
118
+ sage: I
119
+ Ideal (x - 1/3, y - 1/3) of
120
+ Multivariate Polynomial Ring in x, y over Rational Field
121
+ sage: p = {x: 1/3, y: 1/3}
122
+ sage: F.is_convenient_multiple_point(p)
123
+ (True, 'convenient in variables [x, y]')
124
+ sage: alpha = (var('a'), var('b'))
125
+ sage: decomp = F.asymptotic_decomposition(alpha); decomp
126
+ (0, []) +
127
+ (-1/9*r^2*(2*a^2/x^2 + 2*b^2/y^2 - 5*a*b/(x*y))
128
+ - 1/9*r*(6*a/x^2 + 6*b/y^2 - 5*a/(x*y) - 5*b/(x*y))
129
+ - 4/9/x^2 - 4/9/y^2 + 5/9/(x*y),
130
+ [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
131
+ sage: F1 = decomp[1]
132
+ sage: F1.asymptotics(p, alpha, 2)
133
+ (-3*((2*a^2 - 5*a*b + 2*b^2)*r^2 + (a + b)*r + 3)*(1/((1/3)^a*(1/3)^b))^r,
134
+ 1/((1/3)^a*(1/3)^b), -3*(2*a^2 - 5*a*b + 2*b^2)*r^2 - 3*(a + b)*r - 9)
135
+ sage: alpha = [4, 3]
136
+ sage: decomp = F.asymptotic_decomposition(alpha)
137
+ sage: F1 = decomp[1]
138
+ sage: asy = F1.asymptotics(p, alpha, 2)
139
+ sage: asy
140
+ (3*(10*r^2 - 7*r - 3)*2187^r, 2187, 30*r^2 - 21*r - 9)
141
+ sage: F.relative_error(asy[0], alpha, [1, 2, 4, 8], asy[1])
142
+ [((4, 3), 30.72702332, [0.0000000000], [1.000000000]),
143
+ ((8, 6), 111.9315678, [69.00000000], [0.3835519207]),
144
+ ((16, 12), 442.7813138, [387.0000000], [0.1259793763]),
145
+ ((32, 24), 1799.879232, [1743.000000], [0.03160169385])]
146
+
147
+ TESTS::
148
+
149
+ sage: R.<x,y> = PolynomialRing(QQ)
150
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
151
+ sage: H = (1 - 2*x - y) * (1 - x - 2*y)
152
+ sage: G = 1
153
+ sage: Hfac = H.factor()
154
+ sage: G = G / Hfac.unit()
155
+ sage: F = FFPD(G, Hfac); F
156
+ (1, [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
157
+ sage: p = {x: 1, y: 1}
158
+ sage: alpha = [1, 1]
159
+ sage: F.asymptotics(p, alpha, 1)
160
+ (1/3, 1, 1/3)
161
+
162
+ ::
163
+
164
+ sage: R.<x,y,t> = PolynomialRing(QQ)
165
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
166
+ sage: H = (1 - y) * (1 + x^2) * (1 - t*(1 + x^2 + x*y^2))
167
+ sage: G = (1 + x) * (1 + x^2 - x*y^2)
168
+ sage: Hfac = H.factor()
169
+ sage: G = G / Hfac.unit()
170
+ sage: F = FFPD(G, Hfac); F
171
+ (-x^2*y^2 + x^3 - x*y^2 + x^2 + x + 1,
172
+ [(y - 1, 1), (x^2 + 1, 1), (x*y^2*t + x^2*t + t - 1, 1)])
173
+ sage: p = {x: 1, y: 1, t: 1/3}
174
+ sage: alpha = [1, 1, 1]
175
+ sage: F.asymptotics_multiple(p, alpha, 1, var('r')) # not tested - see #19989
176
+
177
+
178
+ Various
179
+ =======
180
+
181
+ AUTHORS:
182
+
183
+ - Alexander Raichev (2008)
184
+ - Daniel Krenn (2014, 2016)
185
+
186
+
187
+ Classes and Methods
188
+ ===================
189
+ """
190
+ # ****************************************************************************
191
+ # Copyright (C) 2008 Alexander Raichev <tortoise.said@gmail.com>
192
+ # Copyright (C) 2014, 2016 Daniel Krenn <dev@danielkrenn.at>
193
+ #
194
+ # This program is free software: you can redistribute it and/or modify
195
+ # it under the terms of the GNU General Public License as published by
196
+ # the Free Software Foundation, either version 2 of the License, or
197
+ # (at your option) any later version.
198
+ # https://www.gnu.org/licenses/
199
+ # ****************************************************************************
200
+
201
+ from functools import total_ordering
202
+ from itertools import combinations_with_replacement
203
+
204
+ from sage.categories.rings import Rings
205
+ from sage.misc.lazy_import import lazy_import
206
+ from sage.misc.misc_c import prod
207
+ from sage.rings.integer import Integer
208
+ from sage.rings.integer_ring import ZZ
209
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
210
+ from sage.structure.element import RingElement
211
+ from sage.structure.parent import Parent
212
+ from sage.structure.richcmp import richcmp_by_eq_and_lt
213
+ from sage.structure.unique_representation import UniqueRepresentation
214
+
215
+ lazy_import("sage.calculus.var", "var")
216
+ lazy_import("sage.calculus.functional", "diff")
217
+ lazy_import("sage.symbolic.ring", "SR")
218
+
219
+
220
+ @total_ordering
221
+ class FractionWithFactoredDenominator(RingElement):
222
+ r"""
223
+ This element represents a fraction with a factored polynomial
224
+ denominator. See also its parent
225
+ :class:`FractionWithFactoredDenominatorRing` for details.
226
+
227
+ Represents a fraction with factored polynomial denominator (FFPD)
228
+ `p/(q_1^{e_1} \cdots q_n^{e_n})` by storing the parts `p` and
229
+ `[(q_1, e_1), \ldots, (q_n, e_n)]`.
230
+ Here `q_1, \ldots, q_n` are elements of a 0- or multi-variate factorial
231
+ polynomial ring `R` , `q_1, \ldots, q_n` are distinct irreducible elements
232
+ of `R` , `e_1, \ldots, e_n` are positive integers, and `p` is a function
233
+ of the indeterminates of `R` (e.g., a Sage symbolic expression). An
234
+ element `r` with no polynomial denominator is represented as ``(r, [])``.
235
+
236
+ INPUT:
237
+
238
+ - ``numerator`` -- an element `p`; this can be of any ring from which
239
+ parent's base has coercion in
240
+ - ``denominator_factored`` -- list of the form
241
+ `[(q_1, e_1), \ldots, (q_n, e_n)]`, where the `q_1, \ldots, q_n` are
242
+ distinct irreducible elements of `R` and the `e_i` are positive
243
+ integers
244
+ - ``reduce`` -- (optional) if ``True``, then represent
245
+ `p/(q_1^{e_1} \cdots q_n^{e_n})` in lowest terms, otherwise
246
+ this won't attempt to divide `p` by any of the `q_i`
247
+
248
+ OUTPUT:
249
+
250
+ An element representing the rational expression
251
+ `p/(q_1^{e_1} \cdots q_n^{e_n})`.
252
+
253
+ EXAMPLES::
254
+
255
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
256
+ sage: R.<x,y> = PolynomialRing(QQ)
257
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
258
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
259
+ sage: f = FFPD(x, df)
260
+ sage: f
261
+ (1, [(y, 1), (x*y + 1, 1)])
262
+ sage: ff = FFPD(x, df, reduce=False)
263
+ sage: ff
264
+ (x, [(y, 1), (x, 1), (x*y + 1, 1)])
265
+
266
+ sage: f = FFPD(x + y, [(x + y, 1)])
267
+ sage: f
268
+ (1, [])
269
+
270
+ ::
271
+
272
+ sage: R.<x> = PolynomialRing(QQ)
273
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
274
+ sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1)
275
+ sage: FFPD(f)
276
+ (5*x^7 - 5*x^6 + 5/3*x^5 - 5/3*x^4 + 2*x^3 - 2/3*x^2 + 1/3*x - 1/3,
277
+ [(x - 1, 1), (x, 1), (x^2 + 1/3, 1)])
278
+
279
+ ::
280
+
281
+ sage: R.<x,y> = PolynomialRing(QQ)
282
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
283
+ sage: f = 2*y/(5*(x^3 - 1)*(y + 1))
284
+ sage: FFPD(f)
285
+ (2/5*y, [(y + 1, 1), (x - 1, 1), (x^2 + x + 1, 1)])
286
+
287
+ sage: p = 1/x^2
288
+ sage: q = 3*x**2*y
289
+ sage: qs = q.factor()
290
+ sage: f = FFPD(p/qs.unit(), qs)
291
+ sage: f
292
+ (1/3/x^2, [(y, 1), (x, 2)])
293
+
294
+ sage: f = FFPD(cos(x)*x*y^2, [(x, 2), (y, 1)])
295
+ sage: f
296
+ (x*y^2*cos(x), [(y, 1), (x, 2)])
297
+
298
+ sage: G = exp(x + y)
299
+ sage: H = (1 - 2*x - y) * (1 - x - 2*y)
300
+ sage: a = FFPD(G/H)
301
+ sage: a
302
+ (e^(x + y), [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
303
+ sage: a.denominator_ring
304
+ Multivariate Polynomial Ring in x, y over Rational Field
305
+ sage: b = FFPD(G, H.factor())
306
+ sage: b
307
+ (e^(x + y), [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
308
+ sage: b.denominator_ring
309
+ Multivariate Polynomial Ring in x, y over Rational Field
310
+
311
+ Singular throws a 'not implemented' error when trying to factor in
312
+ a multivariate polynomial ring over an inexact field::
313
+
314
+ sage: R.<x,y> = PolynomialRing(CC)
315
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
316
+ sage: f = (x + 1)/(x*y*(x*y + 1)^2)
317
+ sage: FFPD(f)
318
+ Traceback (most recent call last):
319
+ ...
320
+ TypeError: Singular error:
321
+ ? not implemented
322
+ ? error occurred in or before STDIN line ...:
323
+ `def sage...=factorize(sage...);`
324
+
325
+ AUTHORS:
326
+
327
+ - Alexander Raichev (2012-07-26)
328
+ - Daniel Krenn (2014-12-01)
329
+ """
330
+
331
+ def __init__(self, parent, numerator, denominator_factored, reduce=True):
332
+ r"""
333
+ Initialize ``self``.
334
+
335
+ EXAMPLES::
336
+
337
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
338
+ sage: R.<x,y> = PolynomialRing(QQ)
339
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
340
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
341
+ sage: f = FFPD(x, df)
342
+ sage: TestSuite(f).run()
343
+ """
344
+ super().__init__(parent)
345
+
346
+ from sage.rings.semirings.non_negative_integer_semiring import NN
347
+ self._numerator = parent._numerator_ring(numerator)
348
+ self._denominator_factored = [(parent._denominator_ring(d), NN(n))
349
+ for d, n in denominator_factored]
350
+
351
+ R = self.denominator_ring
352
+ if numerator in R and reduce:
353
+ # Reduce fraction if possible.
354
+ numer = R(self._numerator)
355
+ df = self._denominator_factored
356
+ new_df = []
357
+ for q, e in df:
358
+ ee = e
359
+ quo, rem = numer.quo_rem(q)
360
+ while rem == 0 and ee > 0:
361
+ ee -= 1
362
+ numer = quo
363
+ quo, rem = numer.quo_rem(q)
364
+ if ee > 0:
365
+ new_df.append((q, ee))
366
+ self._numerator = numer
367
+ self._denominator_factored = new_df
368
+
369
+ def numerator(self):
370
+ r"""
371
+ Return the numerator of ``self``.
372
+
373
+ OUTPUT: the numerator
374
+
375
+ EXAMPLES::
376
+
377
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
378
+ sage: R.<x,y> = PolynomialRing(QQ)
379
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
380
+ sage: H = (1 - x - y - x*y)**2*(1-x)
381
+ sage: Hfac = H.factor()
382
+ sage: G = exp(y)/Hfac.unit()
383
+ sage: F = FFPD(G, Hfac)
384
+ sage: F.numerator()
385
+ -e^y
386
+ """
387
+ return self._numerator
388
+
389
+ def denominator(self):
390
+ r"""
391
+ Return the denominator of ``self``.
392
+
393
+ OUTPUT:
394
+
395
+ The denominator (i.e., the product of the factored denominator).
396
+
397
+ EXAMPLES::
398
+
399
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
400
+ sage: R.<x,y> = PolynomialRing(QQ)
401
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
402
+ sage: H = (1 - x - y - x*y)**2*(1-x)
403
+ sage: Hfac = H.factor()
404
+ sage: G = exp(y)/Hfac.unit()
405
+ sage: F = FFPD(G, Hfac)
406
+ sage: F.denominator()
407
+ x^3*y^2 + 2*x^3*y + x^2*y^2 + x^3 - 2*x^2*y - x*y^2 - 3*x^2 - 2*x*y
408
+ - y^2 + 3*x + 2*y - 1
409
+ """
410
+ return prod(q ** e for q, e in self.denominator_factored())
411
+
412
+ def denominator_factored(self):
413
+ r"""
414
+ Return the factorization in ``self.denominator_ring`` of the denominator of
415
+ ``self`` but without the unit part.
416
+
417
+ OUTPUT:
418
+
419
+ The factored denominator as a list of tuple ``(f, m)``, where `f` is
420
+ a factor and `m` its multiplicity.
421
+
422
+ EXAMPLES::
423
+
424
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
425
+ sage: R.<x,y> = PolynomialRing(QQ)
426
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
427
+ sage: H = (1 - x - y - x*y)**2*(1-x)
428
+ sage: Hfac = H.factor()
429
+ sage: G = exp(y)/Hfac.unit()
430
+ sage: F = FFPD(G, Hfac)
431
+ sage: F.denominator_factored()
432
+ [(x - 1, 1), (x*y + x + y - 1, 2)]
433
+ """
434
+ return self._denominator_factored
435
+
436
+ @property
437
+ def denominator_ring(self):
438
+ r"""
439
+ Return the ring of the denominator.
440
+
441
+ OUTPUT: a ring
442
+
443
+ EXAMPLES::
444
+
445
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
446
+ sage: R.<x,y> = PolynomialRing(QQ)
447
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
448
+ sage: H = (1 - x - y - x*y)**2*(1-x)
449
+ sage: Hfac = H.factor()
450
+ sage: G = exp(y)/Hfac.unit()
451
+ sage: F = FFPD(G, Hfac)
452
+ sage: F.denominator_ring
453
+ Multivariate Polynomial Ring in x, y over Rational Field
454
+ sage: F = FFPD(G/H)
455
+ sage: F
456
+ (e^y, [(x - 1, 1), (x*y + x + y - 1, 2)])
457
+ sage: F.denominator_ring
458
+ Multivariate Polynomial Ring in x, y over Rational Field
459
+ """
460
+ return self.parent()._denominator_ring
461
+
462
+ @property
463
+ def numerator_ring(self):
464
+ r"""
465
+ Return the ring of the numerator.
466
+
467
+ OUTPUT: a ring
468
+
469
+ EXAMPLES::
470
+
471
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
472
+ sage: R.<x,y> = PolynomialRing(QQ)
473
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
474
+ sage: H = (1 - x - y - x*y)**2*(1-x)
475
+ sage: Hfac = H.factor()
476
+ sage: G = exp(y)/Hfac.unit()
477
+ sage: F = FFPD(G, Hfac)
478
+ sage: F.numerator_ring
479
+ Symbolic Ring
480
+ sage: F = FFPD(G/H)
481
+ sage: F
482
+ (e^y, [(x - 1, 1), (x*y + x + y - 1, 2)])
483
+ sage: F.numerator_ring
484
+ Symbolic Ring
485
+ """
486
+ return self.parent()._numerator_ring
487
+
488
+ def dimension(self):
489
+ r"""
490
+ Return the number of indeterminates of ``self.denominator_ring``.
491
+
492
+ OUTPUT: integer
493
+
494
+ EXAMPLES::
495
+
496
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
497
+ sage: R.<x,y> = PolynomialRing(QQ)
498
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
499
+ sage: H = (1 - x - y - x*y)**2*(1-x)
500
+ sage: Hfac = H.factor()
501
+ sage: G = exp(y)/Hfac.unit()
502
+ sage: F = FFPD(G, Hfac)
503
+ sage: F.dimension()
504
+ 2
505
+ """
506
+ from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic
507
+ from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base
508
+ R = self.denominator_ring
509
+ if isinstance(R, (PolynomialRing_generic, MPolynomialRing_base)):
510
+ return R.ngens()
511
+ raise NotImplementedError('only polynomial rings are supported as base')
512
+
513
+ def quotient(self):
514
+ r"""
515
+ Convert ``self`` into a quotient.
516
+
517
+ OUTPUT: an element
518
+
519
+ EXAMPLES::
520
+
521
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
522
+ sage: R.<x,y> = PolynomialRing(QQ)
523
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
524
+ sage: H = (1 - x - y - x*y)**2*(1-x)
525
+ sage: Hfac = H.factor()
526
+ sage: G = exp(y)/Hfac.unit()
527
+ sage: F = FFPD(G, Hfac)
528
+ sage: F
529
+ (-e^y, [(x - 1, 1), (x*y + x + y - 1, 2)])
530
+ sage: F.quotient()
531
+ -e^y/(x^3*y^2 + 2*x^3*y + x^2*y^2 + x^3 - 2*x^2*y - x*y^2 - 3*x^2 -
532
+ 2*x*y - y^2 + 3*x + 2*y - 1)
533
+ """
534
+ return self.numerator() / self.denominator()
535
+
536
+ def _repr_(self) -> str:
537
+ r"""
538
+ Return a string representation of ``self``.
539
+
540
+ OUTPUT: string
541
+
542
+ EXAMPLES::
543
+
544
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
545
+ sage: R.<x,y> = PolynomialRing(QQ)
546
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
547
+ sage: f = FFPD(x + y, [(y, 1), (x, 1)])
548
+ sage: f
549
+ (x + y, [(y, 1), (x, 1)])
550
+ """
551
+ return repr((self.numerator(), self.denominator_factored()))
552
+
553
+ _richcmp_ = richcmp_by_eq_and_lt("_eq_", "_lt_")
554
+
555
+ def _eq_(self, other):
556
+ r"""
557
+ Return whether the FFPD instance ``other`` is equal to
558
+ this FFPD instance.
559
+
560
+ Two FFPD instances are equal iff they represent the same
561
+ fraction.
562
+
563
+ INPUT:
564
+
565
+ - ``other`` -- an instance of :class:`FractionWithFactoredDenominator`
566
+
567
+ OUTPUT: boolean
568
+
569
+ It can be assumed that ``self`` and ``other`` have the same parent.
570
+
571
+ EXAMPLES::
572
+
573
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
574
+ sage: R.<x,y> = PolynomialRing(QQ)
575
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
576
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
577
+ sage: f = FFPD(x, df)
578
+ sage: ff = FFPD(x, df, reduce=False)
579
+ sage: f == ff
580
+ True
581
+ sage: g = FFPD(y, df)
582
+ sage: g == f
583
+ False
584
+
585
+ ::
586
+
587
+ sage: R.<x,y> = PolynomialRing(QQ)
588
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
589
+ sage: G = exp(x + y)
590
+ sage: H = (1 - 2*x - y) * (1 - x - 2*y)
591
+ sage: a = FFPD(G/H)
592
+ sage: b = FFPD(G, H.factor())
593
+ sage: bool(a == b)
594
+ True
595
+
596
+ ::
597
+
598
+ sage: R.<x,y> = PolynomialRing(QQ)
599
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
600
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
601
+ sage: f = FFPD(x, df)
602
+ sage: ff = FFPD(x, df, reduce=False)
603
+ sage: f != ff
604
+ False
605
+ sage: g = FFPD(y, df)
606
+ sage: g != f
607
+ True
608
+
609
+ TESTS::
610
+
611
+ sage: R.<x,y> = PolynomialRing(QQ)
612
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
613
+ sage: f = FFPD(x, [])
614
+ sage: f == x
615
+ True
616
+
617
+ ::
618
+
619
+ sage: R.<x,y> = PolynomialRing(QQ)
620
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
621
+ sage: f = FFPD(x*y, [(x-1, 1), (y-2, 2)])
622
+ sage: g = FFPD(x, [(x-1, 1), (y-2, 2)])
623
+ sage: f == f
624
+ True
625
+ sage: f == g
626
+ False
627
+
628
+ sage: f < g
629
+ Traceback (most recent call last):
630
+ ...
631
+ AttributeError:
632
+ 'FractionWithFactoredDenominatorRing_with_category.element_class'
633
+ object has no attribute '_lt_'
634
+ """
635
+ return (self.numerator() * other.denominator() ==
636
+ other.numerator() * self.denominator())
637
+
638
+ def _total_order_key_(self):
639
+ r"""
640
+ Return a key that can be used for sorting.
641
+
642
+ FFPD ``A`` is less than FFPD ``B`` iff
643
+ (the denominator factorization of ``A`` is shorter than that of ``B``)
644
+ of (the denominator factorization lengths are equal and
645
+ the denominator of ``A`` is less than that of ``B`` in their ring) or
646
+ (the denominator factorization lengths are equal and the
647
+ denominators are equal and the numerator of ``A`` is less than that
648
+ of ``B`` in their ring).
649
+
650
+ OUTPUT: a tuple
651
+
652
+ EXAMPLES::
653
+
654
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
655
+ sage: R.<x,y> = PolynomialRing(QQ)
656
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
657
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
658
+ sage: f = FFPD(x, df); f
659
+ (1, [(y, 1), (x*y + 1, 1)])
660
+ sage: ff = FFPD(x, df, reduce=False); ff
661
+ (x, [(y, 1), (x, 1), (x*y + 1, 1)])
662
+ sage: g = FFPD(y, df)
663
+ sage: h = FFPD(exp(x), df)
664
+ sage: i = FFPD(sin(x + 2), df)
665
+ sage: f._total_order_key_() < ff._total_order_key_()
666
+ True
667
+ sage: f._total_order_key_() < g._total_order_key_()
668
+ True
669
+ sage: g._total_order_key_() < h._total_order_key_()
670
+ True
671
+ sage: bool(h._total_order_key_() < i._total_order_key_())
672
+ False
673
+ """
674
+ return (len(self.denominator_factored()),
675
+ self.denominator(),
676
+ self.numerator())
677
+
678
+ def univariate_decomposition(self):
679
+ r"""
680
+ Return the usual univariate partial fraction decomposition
681
+ of ``self``.
682
+
683
+ Assume that the numerator of ``self`` lies in the same univariate
684
+ factorial polynomial ring as the factors of the denominator.
685
+
686
+ Let `f = p/q` be a rational expression where `p` and `q` lie in a
687
+ univariate factorial polynomial ring `R`.
688
+ Let `q_1^{e_1} \cdots q_n^{e_n}` be the
689
+ unique factorization of `q` in `R` into irreducible factors.
690
+ Then `f` can be written uniquely as:
691
+
692
+ .. MATH::
693
+
694
+ (*) \quad p_0 + \sum_{i=1}^{m} \frac{p_i}{q_i^{e_i}},
695
+
696
+ for some `p_j \in R`.
697
+ We call `(*)` the *usual partial fraction decomposition* of `f`.
698
+
699
+ .. NOTE::
700
+
701
+ This partial fraction decomposition can be computed using
702
+ :meth:`~sage.symbolic.expression.Expression.partial_fraction` or
703
+ :meth:`~sage.categories.quotient_fields.QuotientFields.ElementMethods.partial_fraction_decomposition`
704
+ as well. However, here we use the already obtained/cached
705
+ factorization of the denominator. This gives a speed up for
706
+ non-small instances.
707
+
708
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
709
+
710
+ EXAMPLES::
711
+
712
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
713
+
714
+ One variable::
715
+
716
+ sage: R.<x> = PolynomialRing(QQ)
717
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
718
+ sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1)
719
+ sage: f
720
+ (5*x^7 - 5*x^6 + 5/3*x^5 - 5/3*x^4 + 2*x^3 - 2/3*x^2 + 1/3*x - 1/3)/(x^4 - x^3 + 1/3*x^2 - 1/3*x)
721
+ sage: decomp = FFPD(f).univariate_decomposition()
722
+ sage: decomp
723
+ (5*x^3, []) +
724
+ (1, [(x - 1, 1)]) +
725
+ (1, [(x, 1)]) +
726
+ (1/3, [(x^2 + 1/3, 1)])
727
+ sage: decomp.sum().quotient() == f
728
+ True
729
+
730
+ One variable with numerator in symbolic ring::
731
+
732
+ sage: R.<x> = PolynomialRing(QQ)
733
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
734
+ sage: f = 5*x^3 + 1/x + 1/(x-1) + exp(x)/(3*x^2 + 1)
735
+ sage: f
736
+ (5*x^5 - 5*x^4 + 2*x - 1)/(x^2 - x) + e^x/(3*x^2 + 1)
737
+ sage: decomp = FFPD(f).univariate_decomposition()
738
+ sage: decomp
739
+ (0, []) +
740
+ (15/4*x^7 - 15/4*x^6 + 5/4*x^5 - 5/4*x^4 + 3/2*x^3 + 1/4*x^2*e^x -
741
+ 3/4*x^2 - 1/4*x*e^x + 1/2*x - 1/4, [(x - 1, 1)]) +
742
+ (-15*x^7 + 15*x^6 - 5*x^5 + 5*x^4 - 6*x^3 -
743
+ x^2*e^x + 3*x^2 + x*e^x - 2*x + 1, [(x, 1)]) +
744
+ (1/4*(15*x^7 - 15*x^6 + 5*x^5 - 5*x^4 + 6*x^3 + x^2*e^x -
745
+ 3*x^2 - x*e^x + 2*x - 1)*(3*x - 1), [(x^2 + 1/3, 1)])
746
+
747
+ One variable over a finite field::
748
+
749
+ sage: R.<x> = PolynomialRing(GF(2))
750
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
751
+ sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1)
752
+ sage: f
753
+ (x^6 + x^4 + 1)/(x^3 + x)
754
+ sage: decomp = FFPD(f).univariate_decomposition()
755
+ sage: decomp
756
+ (x^3, []) + (1, [(x, 1)]) + (x, [(x + 1, 2)])
757
+ sage: decomp.sum().quotient() == f
758
+ True
759
+
760
+ One variable over an inexact field::
761
+
762
+ sage: R.<x> = PolynomialRing(CC)
763
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
764
+ sage: f = 5*x^3 + 1/x + 1/(x-1) + 1/(3*x^2 + 1)
765
+ sage: f
766
+ (5.00000000000000*x^7 - 5.00000000000000*x^6 + 1.66666666666667*x^5 - 1.66666666666667*x^4 + 2.00000000000000*x^3 - 0.666666666666667*x^2 + 0.333333333333333*x - 0.333333333333333)/(x^4 - x^3 + 0.333333333333333*x^2 - 0.333333333333333*x)
767
+ sage: decomp = FFPD(f).univariate_decomposition()
768
+ sage: decomp
769
+ (5.00000000000000*x^3, []) +
770
+ (1.00000000000000, [(x - 1.00000000000000, 1)]) +
771
+ (-0.288675134594813*I, [(x - 0.577350269189626*I, 1)]) +
772
+ (1.00000000000000, [(x, 1)]) +
773
+ (0.288675134594813*I, [(x + 0.577350269189626*I, 1)])
774
+ sage: decomp.sum().quotient() == f # Rounding error coming
775
+ False
776
+
777
+ TESTS::
778
+
779
+ sage: R.<x> = PolynomialRing(QQ)
780
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
781
+ sage: f = exp(x) / (x^2-x)
782
+ sage: f
783
+ e^x/(x^2 - x)
784
+ sage: FFPD(f).univariate_decomposition()
785
+ (0, []) + (e^x, [(x - 1, 1)]) + (-e^x, [(x, 1)])
786
+
787
+ AUTHORS:
788
+
789
+ - Robert Bradshaw (2007-05-31)
790
+ - Alexander Raichev (2012-06-25)
791
+ - Daniel Krenn (2014-12-01)
792
+ """
793
+ if self.dimension() > 1:
794
+ return FractionWithFactoredDenominatorSum([self])
795
+
796
+ R = self.denominator_ring
797
+ p = self.numerator()
798
+ q = self.denominator()
799
+ try:
800
+ whole, p = R(p).quo_rem(q)
801
+ mn = R.one()
802
+ except (TypeError, ValueError):
803
+ whole = R(0)
804
+ mn = p
805
+ p = R.one()
806
+ df = self.denominator_factored()
807
+ decomp = [self.parent()(whole, [])]
808
+ denominator = prod(b**n for b, n in df)
809
+ for a, m in df:
810
+ am = a**m
811
+ q, r = denominator.quo_rem(am)
812
+ assert r == 0
813
+ numer = p * q.inverse_mod(am) % am
814
+ # The inverse exists because the product and a**m
815
+ # are relatively prime.
816
+ decomp.append(self.parent()(mn * numer, [(a, m)]))
817
+ return FractionWithFactoredDenominatorSum(decomp)
818
+
819
+ def nullstellensatz_certificate(self):
820
+ r"""
821
+ Return a Nullstellensatz certificate of ``self`` if it exists.
822
+
823
+ Let `[(q_1, e_1), \ldots, (q_n, e_n)]` be the denominator
824
+ factorization of ``self``. The Nullstellensatz certificate is
825
+ a list of polynomials `h_1, \ldots, h_m` in ``self.denominator_ring``
826
+ that satisfies `h_1 q_1 + \cdots + h_m q_n = 1` if it exists.
827
+
828
+ .. NOTE::
829
+
830
+ Only works for multivariate base rings.
831
+
832
+ OUTPUT:
833
+
834
+ A list of polynomials or ``None`` if no Nullstellensatz
835
+ certificate exists.
836
+
837
+ EXAMPLES::
838
+
839
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
840
+ sage: R.<x,y> = PolynomialRing(QQ)
841
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
842
+ sage: G = sin(x)
843
+ sage: H = x^2 * (x*y + 1)
844
+ sage: f = FFPD(G, H.factor())
845
+ sage: L = f.nullstellensatz_certificate()
846
+ sage: L
847
+ [y^2, -x*y + 1]
848
+ sage: df = f.denominator_factored()
849
+ sage: sum(L[i]*df[i][0]**df[i][1] for i in range(len(df))) == 1
850
+ True
851
+
852
+ ::
853
+
854
+ sage: f = 1/(x*y)
855
+ sage: L = FFPD(f).nullstellensatz_certificate()
856
+ sage: L is None
857
+ True
858
+ """
859
+ R = self.denominator_ring
860
+ df = self.denominator_factored()
861
+ J = R.ideal([q ** e for q, e in df])
862
+ if R.one() in J:
863
+ return R.one().lift(J)
864
+ return None
865
+
866
+ def nullstellensatz_decomposition(self):
867
+ r"""
868
+ Return a Nullstellensatz decomposition of ``self``.
869
+
870
+ Let `f = p/q` where `q` lies in a `d` -variate polynomial ring
871
+ `K[X]` for some field `K` and `d \geq 1`.
872
+ Let `q_1^{e_1} \cdots q_n^{e_n}` be the
873
+ unique factorization of `q` in `K[X]` into irreducible factors and
874
+ let `V_i` be the algebraic variety `\{x \in L^d \mid q_i(x) = 0\}`
875
+ of `q_i` over the algebraic closure `L` of `K`.
876
+ By [Rai2012]_, `f` can be written as
877
+
878
+ .. MATH::
879
+
880
+ (*) \quad \sum_A \frac{p_A}{\prod_{i \in A} q_i^{e_i}},
881
+
882
+ where the `p_A` are products of `p` and elements in `K[X]` and
883
+ the sum is taken over all subsets
884
+ `A \subseteq \{1, \ldots, m\}` such that
885
+ `\bigcap_{i\in A} T_i \neq \emptyset`.
886
+
887
+ We call `(*)` a *Nullstellensatz decomposition* of `f`.
888
+ Nullstellensatz decompositions are not unique.
889
+
890
+ The algorithm used comes from [Rai2012]_.
891
+
892
+ .. NOTE::
893
+
894
+ Recursive. Only works for multivariate ``self``.
895
+
896
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
897
+
898
+ EXAMPLES::
899
+
900
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import *
901
+ sage: R.<x,y> = PolynomialRing(QQ)
902
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
903
+ sage: f = 1/(x*(x*y + 1))
904
+ sage: decomp = FFPD(f).nullstellensatz_decomposition()
905
+ sage: decomp
906
+ (0, []) + (1, [(x, 1)]) + (-y, [(x*y + 1, 1)])
907
+ sage: decomp.sum().quotient() == f
908
+ True
909
+ sage: [r.nullstellensatz_certificate() is None for r in decomp]
910
+ [True, True, True]
911
+
912
+ ::
913
+
914
+ sage: R.<x,y> = PolynomialRing(QQ)
915
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
916
+ sage: G = sin(y)
917
+ sage: H = x*(x*y + 1)
918
+ sage: f = FFPD(G, H.factor())
919
+ sage: decomp = f.nullstellensatz_decomposition()
920
+ sage: decomp
921
+ (0, []) + (sin(y), [(x, 1)]) + (-y*sin(y), [(x*y + 1, 1)])
922
+ sage: bool(decomp.sum().quotient() == G/H)
923
+ True
924
+ sage: [r.nullstellensatz_certificate() is None for r in decomp]
925
+ [True, True, True]
926
+ """
927
+ L = self.nullstellensatz_certificate()
928
+ if L is None:
929
+ # No decomposing possible.
930
+ return FractionWithFactoredDenominatorSum([self])
931
+
932
+ # Otherwise decompose recursively.
933
+ decomp = FractionWithFactoredDenominatorSum()
934
+ p = self.numerator()
935
+ df = self.denominator_factored()
936
+ m = len(df)
937
+ iteration1 = FractionWithFactoredDenominatorSum(
938
+ [self.parent()(p * L[i], [df[j] for j in range(m) if j != i])
939
+ for i in range(m) if L[i] != 0])
940
+
941
+ # Now decompose each FFPD of iteration1.
942
+ for r in iteration1:
943
+ decomp.extend(r.nullstellensatz_decomposition())
944
+
945
+ # Simplify and return result.
946
+ return decomp._combine_like_terms_().whole_and_parts()
947
+
948
+ def algebraic_dependence_certificate(self):
949
+ r"""
950
+ Return the algebraic dependence certificate of ``self``.
951
+
952
+ The algebraic dependence certificate is the ideal `J` of
953
+ annihilating polynomials for the set of polynomials
954
+ ``[q^e for (q, e) in self.denominator_factored()]``,
955
+ which could be the zero ideal.
956
+ The ideal `J` lies in a polynomial ring over the field
957
+ ``self.denominator_ring.base_ring()`` that has
958
+ ``m = len(self.denominator_factored())`` indeterminates.
959
+
960
+ OUTPUT: an ideal
961
+
962
+ EXAMPLES::
963
+
964
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
965
+ sage: R.<x,y> = PolynomialRing(QQ)
966
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
967
+ sage: f = 1/(x^2 * (x*y + 1) * y^3)
968
+ sage: ff = FFPD(f)
969
+ sage: J = ff.algebraic_dependence_certificate(); J
970
+ Ideal (1 - 6*T2 + 15*T2^2 - 20*T2^3 + 15*T2^4 - T0^2*T1^3 -
971
+ 6*T2^5 + T2^6) of Multivariate Polynomial Ring in
972
+ T0, T1, T2 over Rational Field
973
+ sage: g = J.gens()[0]
974
+ sage: df = ff.denominator_factored()
975
+ sage: g(*(q**e for q, e in df)) == 0
976
+ True
977
+
978
+ ::
979
+
980
+ sage: R.<x,y> = PolynomialRing(QQ)
981
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
982
+ sage: G = exp(x + y)
983
+ sage: H = x^2 * (x*y + 1) * y^3
984
+ sage: ff = FFPD(G, H.factor())
985
+ sage: J = ff.algebraic_dependence_certificate(); J
986
+ Ideal (1 - 6*T2 + 15*T2^2 - 20*T2^3 + 15*T2^4 - T0^2*T1^3 -
987
+ 6*T2^5 + T2^6) of Multivariate Polynomial Ring in
988
+ T0, T1, T2 over Rational Field
989
+ sage: g = J.gens()[0]
990
+ sage: df = ff.denominator_factored()
991
+ sage: g(*(q**e for q, e in df)) == 0
992
+ True
993
+
994
+ ::
995
+
996
+ sage: f = 1/(x^3 * y^2)
997
+ sage: J = FFPD(f).algebraic_dependence_certificate()
998
+ sage: J
999
+ Ideal (0) of Multivariate Polynomial Ring in T0, T1 over Rational Field
1000
+
1001
+ ::
1002
+
1003
+ sage: f = sin(1)/(x^3 * y^2)
1004
+ sage: J = FFPD(f).algebraic_dependence_certificate()
1005
+ sage: J
1006
+ Ideal (0) of Multivariate Polynomial Ring in T0, T1 over Rational Field
1007
+ """
1008
+ R = self.denominator_ring
1009
+ df = self.denominator_factored()
1010
+ if not df:
1011
+ return R.ideal() # The zero ideal.
1012
+ m = len(df)
1013
+ F = R.base_ring()
1014
+ Xs = list(R.gens())
1015
+ d = len(Xs)
1016
+
1017
+ # Expand R by 2 * m new variables.
1018
+ S = 'S'
1019
+ while S in [str(x) for x in Xs]:
1020
+ S = S + 'S'
1021
+ Ss = [S + str(i) for i in range(m)]
1022
+ T = 'T'
1023
+ while T in [str(x) for x in Xs]:
1024
+ T = T + 'T'
1025
+ Ts = [T + str(i) for i in range(m)]
1026
+
1027
+ Vs = [str(x) for x in Xs] + Ss + Ts
1028
+ RR = PolynomialRing(F, Vs)
1029
+ Xs = RR.gens()[:d]
1030
+ Ss = RR.gens()[d: d + m]
1031
+ Ts = RR.gens()[d + m: d + 2 * m]
1032
+
1033
+ # Compute the appropriate elimination ideal.
1034
+ J = RR.ideal([Ss[j] - RR(df[j][0]) for j in range(m)] +
1035
+ [Ss[j] ** df[j][1] - Ts[j] for j in range(m)])
1036
+ J = J.elimination_ideal(Xs + Ss)
1037
+
1038
+ # Coerce J into the polynomial ring in the indeterminates Ts[m:].
1039
+ # I choose the negdeglex order because i find it useful in my work.
1040
+ RRR = PolynomialRing(F, [str(t) for t in Ts], order='negdeglex')
1041
+ return RRR.ideal(J)
1042
+
1043
+ def algebraic_dependence_decomposition(self, whole_and_parts=True):
1044
+ r"""
1045
+ Return an algebraic dependence decomposition of ``self``.
1046
+
1047
+ Let `f = p/q` where `q` lies in a `d`-variate polynomial ring
1048
+ `K[X]` for some field `K`.
1049
+ Let `q_1^{e_1} \cdots q_n^{e_n}` be the
1050
+ unique factorization of `q` in `K[X]` into irreducible factors and
1051
+ let `V_i` be the algebraic variety `\{x \in L^d \mid q_i(x) = 0\}`
1052
+ of `q_i` over the algebraic closure `L` of `K`.
1053
+ By [Rai2012]_, `f` can be written as
1054
+
1055
+ .. MATH::
1056
+
1057
+ (*) \quad \sum_A \frac{p_A}{\prod_{i \in A} q_i^{b_i}},
1058
+
1059
+ where the `b_i` are positive integers, each `p_A` is a products
1060
+ of `p` and an element in `K[X]`,
1061
+ and the sum is taken over all subsets
1062
+ `A \subseteq \{1, \ldots, m\}` such that `|A| \leq d` and
1063
+ `\{q_i \mid i \in A\}` is algebraically independent.
1064
+
1065
+ We call `(*)` an *algebraic dependence decomposition* of `f`.
1066
+ Algebraic dependence decompositions are not unique.
1067
+
1068
+ The algorithm used comes from [Rai2012]_.
1069
+
1070
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
1071
+
1072
+ EXAMPLES::
1073
+
1074
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1075
+ sage: R.<x,y> = PolynomialRing(QQ)
1076
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1077
+ sage: f = 1/(x^2 * (x*y + 1) * y^3)
1078
+ sage: ff = FFPD(f)
1079
+ sage: decomp = ff.algebraic_dependence_decomposition()
1080
+ sage: decomp
1081
+ (0, []) + (-x, [(x*y + 1, 1)]) +
1082
+ (x^2*y^2 - x*y + 1, [(y, 3), (x, 2)])
1083
+ sage: decomp.sum().quotient() == f
1084
+ True
1085
+ sage: for r in decomp:
1086
+ ....: J = r.algebraic_dependence_certificate()
1087
+ ....: J is None or J == J.ring().ideal() # The zero ideal
1088
+ True
1089
+ True
1090
+ True
1091
+
1092
+ ::
1093
+
1094
+ sage: R.<x,y> = PolynomialRing(QQ)
1095
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1096
+ sage: G = sin(x)
1097
+ sage: H = x^2 * (x*y + 1) * y^3
1098
+ sage: f = FFPD(G, H.factor())
1099
+ sage: decomp = f.algebraic_dependence_decomposition()
1100
+ sage: decomp
1101
+ (0, []) + (x^4*y^3*sin(x), [(x*y + 1, 1)]) +
1102
+ (-(x^5*y^5 - x^4*y^4 + x^3*y^3 - x^2*y^2 + x*y - 1)*sin(x),
1103
+ [(y, 3), (x, 2)])
1104
+ sage: bool(decomp.sum().quotient() == G/H)
1105
+ True
1106
+ sage: for r in decomp:
1107
+ ....: J = r.algebraic_dependence_certificate()
1108
+ ....: J is None or J == J.ring().ideal()
1109
+ True
1110
+ True
1111
+ True
1112
+ """
1113
+ J = self.algebraic_dependence_certificate()
1114
+ if not J:
1115
+ # No decomposing possible.
1116
+ return FractionWithFactoredDenominatorSum([self])
1117
+
1118
+ # Otherwise decompose recursively.
1119
+ decomp = FractionWithFactoredDenominatorSum()
1120
+ p = self.numerator()
1121
+ df = self.denominator_factored()
1122
+ m = len(df)
1123
+ g = J.gens()[0] # An annihilating polynomial for df.
1124
+ new_vars = J.ring().gens()
1125
+ # Note that each new_vars[j] corresponds to df[j] such that
1126
+ # g([q**e for q, e in df]) = 0.
1127
+ # Assuming here that g.parent() has negdeglex term order
1128
+ # so that g.lt() is indeed the monomial we want below.
1129
+ # Use g to rewrite r into a sum of FFPDs,
1130
+ # each with < m distinct denominator factors.
1131
+ gg = (g.lt() - g) / (g.lc())
1132
+ numers = map(prod, zip(gg.coefficients(), gg.monomials()))
1133
+ e = list(g.lt().exponents())[0: m]
1134
+ denoms = [(new_vars[j], e[0][j] + 1) for j in range(m)]
1135
+ # Write r in terms of new_vars,
1136
+ # cancel factors in the denominator, and combine like terms.
1137
+ FFPD = FractionWithFactoredDenominatorRing(J.ring())
1138
+ iteration1_temp = FractionWithFactoredDenominatorSum(
1139
+ [FFPD(a, denoms) for a in numers])._combine_like_terms_()
1140
+ # Substitute in df.
1141
+ qpowsub = {new_vars[j]: df[j][0] ** df[j][1] for j in range(m)}
1142
+ iteration1 = FractionWithFactoredDenominatorSum()
1143
+ for r in iteration1_temp:
1144
+ num1 = p * J.ring()(r.numerator()).subs(qpowsub)
1145
+ denoms1 = []
1146
+ for q, e in r.denominator_factored():
1147
+ j = new_vars.index(q)
1148
+ denoms1.append((df[j][0], df[j][1] * e))
1149
+ iteration1.append(self.parent()(num1, denoms1))
1150
+ # Now decompose each FFPD of iteration1.
1151
+ for r in iteration1:
1152
+ decomp.extend(r.algebraic_dependence_decomposition())
1153
+
1154
+ # Simplify and return result.
1155
+ return decomp._combine_like_terms_().whole_and_parts()
1156
+
1157
+ def leinartas_decomposition(self):
1158
+ r"""
1159
+ Return a Leinartas decomposition of ``self``.
1160
+
1161
+ Let `f = p/q` where `q` lies in a `d` -variate polynomial
1162
+ ring `K[X]` for some field `K`.
1163
+ Let `q_1^{e_1} \cdots q_n^{e_n}` be the
1164
+ unique factorization of `q` in `K[X]` into irreducible factors and
1165
+ let `V_i` be the algebraic variety
1166
+ `\{x\in L^d \mid q_i(x) = 0\}` of `q_i` over the algebraic closure
1167
+ `L` of `K`. By [Rai2012]_, `f` can be written as
1168
+
1169
+ .. MATH::
1170
+
1171
+ (*) \quad \sum_A \frac{p_A}{\prod_{i \in A} q_i^{b_i}},
1172
+
1173
+ where the `b_i` are positive integers, each `p_A` is a product of
1174
+ `p` and an element of `K[X]`, and the sum is taken over all
1175
+ subsets `A \subseteq \{1, \ldots, m\}` such that
1176
+
1177
+ 1. `|A| \le d`,
1178
+ 2. `\bigcap_{i\in A} T_i \neq \emptyset`, and
1179
+ 3. `\{q_i \mid i\in A\}` is algebraically independent.
1180
+
1181
+ In particular, any rational expression in `d` variables
1182
+ can be represented as a sum of rational expressions
1183
+ whose denominators each contain at most `d` distinct irreducible
1184
+ factors.
1185
+
1186
+ We call `(*)` a *Leinartas decomposition* of `f`.
1187
+ Leinartas decompositions are not unique.
1188
+
1189
+ The algorithm used comes from [Rai2012]_.
1190
+
1191
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
1192
+
1193
+ EXAMPLES::
1194
+
1195
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1196
+ sage: R.<x> = PolynomialRing(QQ)
1197
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1198
+ sage: f = (x^2 + 1)/((x + 2)*(x - 1)*(x^2 + x + 1))
1199
+ sage: decomp = FFPD(f).leinartas_decomposition()
1200
+ sage: decomp
1201
+ (0, []) + (2/9, [(x - 1, 1)]) +
1202
+ (-5/9, [(x + 2, 1)]) + (1/3*x, [(x^2 + x + 1, 1)])
1203
+ sage: decomp.sum().quotient() == f
1204
+ True
1205
+
1206
+ ::
1207
+
1208
+ sage: R.<x,y> = PolynomialRing(QQ)
1209
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1210
+ sage: f = 1/x + 1/y + 1/(x*y + 1)
1211
+ sage: decomp = FFPD(f).leinartas_decomposition()
1212
+ sage: decomp
1213
+ (0, []) + (1, [(x*y + 1, 1)]) + (x + y, [(y, 1), (x, 1)])
1214
+ sage: decomp.sum().quotient() == f
1215
+ True
1216
+ sage: def check_decomp(r):
1217
+ ....: L = r.nullstellensatz_certificate()
1218
+ ....: J = r.algebraic_dependence_certificate()
1219
+ ....: return L is None and (J is None or J == J.ring().ideal())
1220
+ sage: all(check_decomp(r) for r in decomp)
1221
+ True
1222
+
1223
+ ::
1224
+
1225
+ sage: R.<x,y> = PolynomialRing(QQ)
1226
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1227
+ sage: f = sin(x)/x + 1/y + 1/(x*y + 1)
1228
+ sage: G = f.numerator()
1229
+ sage: H = R(f.denominator())
1230
+ sage: ff = FFPD(G, H.factor())
1231
+ sage: decomp = ff.leinartas_decomposition()
1232
+ sage: decomp # random - non canonical depends on singular version
1233
+ (0, []) +
1234
+ (-(x*y^2*sin(x) + x^2*y + x*y + y*sin(x) + x)*y, [(y, 1)]) +
1235
+ ((x*y^2*sin(x) + x^2*y + x*y + y*sin(x) + x)*x*y, [(x*y + 1, 1)]) +
1236
+ (x*y^2*sin(x) + x^2*y + x*y + y*sin(x) + x, [(y, 1), (x, 1)])
1237
+ sage: bool(decomp.sum().quotient() == f)
1238
+ True
1239
+ sage: all(check_decomp(r) for r in decomp)
1240
+ True
1241
+
1242
+ ::
1243
+
1244
+ sage: R.<x,y,z>= PolynomialRing(GF(2, 'a'))
1245
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1246
+ sage: f = 1/(x * y * z * (x*y + z))
1247
+ sage: decomp = FFPD(f).leinartas_decomposition()
1248
+ sage: decomp
1249
+ (0, []) + (1, [(z, 2), (x*y + z, 1)]) +
1250
+ (1, [(z, 2), (y, 1), (x, 1)])
1251
+ sage: decomp.sum().quotient() == f
1252
+ True
1253
+ """
1254
+ if self.dimension() == 1:
1255
+ # Sage's lift() function doesn't work in
1256
+ # univariate polynomial rings.
1257
+ # So nullstellensatz_decomposition() won't work.
1258
+ # Can use algebraic_dependence_decomposition(),
1259
+ # which is sufficient.
1260
+ # temp = FractionWithFactoredDenominatorSum([self])
1261
+ # Alternatively can use univariate_decomposition(),
1262
+ # which is more efficient.
1263
+ return self.univariate_decomposition()
1264
+ temp = self.nullstellensatz_decomposition()
1265
+ decomp = FractionWithFactoredDenominatorSum()
1266
+ for r in temp:
1267
+ decomp.extend(r.algebraic_dependence_decomposition())
1268
+
1269
+ # Simplify and return result.
1270
+ return decomp._combine_like_terms_().whole_and_parts()
1271
+
1272
+ def cohomology_decomposition(self):
1273
+ r"""
1274
+ Return the cohomology decomposition of ``self``.
1275
+
1276
+ Let `p / (q_1^{e_1} \cdots q_n^{e_n})` be the fraction represented
1277
+ by ``self`` and let `K[x_1, \ldots, x_d]` be the polynomial ring
1278
+ in which the `q_i` lie.
1279
+ Assume that `n \leq d` and that the gradients of the `q_i` are linearly
1280
+ independent at all points in the intersection
1281
+ `V_1 \cap \ldots \cap V_n` of the algebraic varieties
1282
+ `V_i = \{x \in L^d \mid q_i(x) = 0 \}`, where `L` is the algebraic
1283
+ closure of the field `K`.
1284
+ Return a :class:`FractionWithFactoredDenominatorSum`
1285
+ `f` such that the differential form
1286
+ `f dx_1 \wedge \cdots \wedge dx_d` is de Rham cohomologous to the
1287
+ differential form
1288
+ `p / (q_1^{e_1} \cdots q_n^{e_n}) dx_1 \wedge \cdots \wedge dx_d`
1289
+ and such that the denominator of each summand of `f` contains
1290
+ no repeated irreducible factors.
1291
+
1292
+ The algorithm used here comes from the proof of Theorem 17.4 of
1293
+ [AY1983]_.
1294
+
1295
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
1296
+
1297
+ EXAMPLES::
1298
+
1299
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1300
+ sage: R.<x> = PolynomialRing(QQ)
1301
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1302
+ sage: f = 1/(x^2 + x + 1)^3
1303
+ sage: decomp = FFPD(f).cohomology_decomposition()
1304
+ sage: decomp
1305
+ (0, []) + (2/3, [(x^2 + x + 1, 1)])
1306
+
1307
+ ::
1308
+
1309
+ sage: R.<x,y> = PolynomialRing(QQ)
1310
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1311
+ sage: FFPD(1, [(x, 1), (y, 2)]).cohomology_decomposition()
1312
+ (0, [])
1313
+
1314
+ The following example was fixed in :issue:`29465`::
1315
+
1316
+ sage: p = 1
1317
+ sage: qs = [(x*y - 1, 1), (x**2 + y**2 - 1, 2)]
1318
+ sage: f = FFPD(p, qs)
1319
+ sage: f.cohomology_decomposition()
1320
+ (0, []) + (-4/3*x*y, [(x^2 + y^2 - 1, 1)]) +
1321
+ (1/3, [(x*y - 1, 1), (x^2 + y^2 - 1, 1)])
1322
+ """
1323
+ from sage.calculus.functions import jacobian
1324
+ from sage.arith.misc import XGCD as xgcd
1325
+ from sage.sets.set import Set
1326
+
1327
+ R = self.denominator_ring
1328
+ df = self.denominator_factored()
1329
+ n = len(df)
1330
+ if sum(e for (q, e) in df) <= n:
1331
+ # No decomposing possible.
1332
+ return FractionWithFactoredDenominatorSum([self])
1333
+
1334
+ # Otherwise decompose recursively.
1335
+ decomp = FractionWithFactoredDenominatorSum()
1336
+ p = self.numerator()
1337
+ qs = [q for (q, e) in df]
1338
+ # sort according to the term order of R
1339
+ X = sorted(R.gens())
1340
+ var_sets_n = sorted(sorted(s) for s in Set(X).subsets(n))
1341
+ Par = self.parent()
1342
+
1343
+ # Compute Jacobian determinants for qs.
1344
+ dets = [R(jacobian(qs, x).determinant())
1345
+ for x in var_sets_n]
1346
+
1347
+ # Get a Nullstellensatz certificate for qs and dets.
1348
+ if self.dimension() == 1:
1349
+ # Sage's lift() function doesn't work in
1350
+ # univariate polynomial rings.
1351
+ # So use xgcd(), which does the same thing in this case.
1352
+ # Note that by assumption qs and dets have length 1.
1353
+ L = xgcd(qs[0], dets[0])[1:]
1354
+ else:
1355
+ L = R.one().lift(R.ideal(qs + dets))
1356
+
1357
+ # Do first iteration of decomposition.
1358
+ iteration1 = FractionWithFactoredDenominatorSum()
1359
+ # Contributions from qs.
1360
+ for i in range(n):
1361
+ if L[i] == 0:
1362
+ continue
1363
+ # Cancel one df[i] from denominator.
1364
+ new_df = [list(t) for t in df]
1365
+ if new_df[i][1] > 1:
1366
+ new_df[i][1] -= 1
1367
+ else:
1368
+ del new_df[i]
1369
+ iteration1.append(Par(p * L[i], new_df))
1370
+
1371
+ # Contributions from dets.
1372
+ # Compute each contribution's cohomologous form using
1373
+ # the least index j such that new_df[j][1] > 1.
1374
+ # Know such an index exists by first 'if' statement at
1375
+ # the top.
1376
+ for j in range(n):
1377
+ if df[j][1] > 1:
1378
+ J = j
1379
+ break
1380
+ new_df = [list(t) for t in df]
1381
+ new_df[J][1] -= 1
1382
+ for k, x in enumerate(var_sets_n):
1383
+ if L[n + k] == 0:
1384
+ continue
1385
+ # Compute Jacobian in the Symbolic Ring.
1386
+ jac = jacobian([SR(p * L[n + k])] +
1387
+ [SR(qs[j]) for j in range(n) if j != J],
1388
+ [SR(xx) for xx in x])
1389
+ det = jac.determinant()
1390
+ # The parity epsilon from [AY1983, eq. (17.11)] does not
1391
+ # enter this computation, since we do not order the
1392
+ # coordinates x to the front of X in the representation of
1393
+ # this differential form (:issue:`29465`).
1394
+ iteration1.append(Par((-1) ** J * det / new_df[J][1], new_df))
1395
+
1396
+ # Now decompose each FFPD of iteration1.
1397
+ for r in iteration1:
1398
+ decomp.extend(r.cohomology_decomposition())
1399
+
1400
+ # Simplify and return result.
1401
+ return decomp._combine_like_terms_().whole_and_parts()
1402
+
1403
+ def asymptotic_decomposition(self, alpha, asy_var=None):
1404
+ r"""
1405
+ Return the asymptotic decomposition of ``self``.
1406
+
1407
+ The asymptotic decomposition of `F` is a sum that has the
1408
+ same asymptotic expansion as `f` in the direction ``alpha``
1409
+ but each summand has a denominator factorization of the form
1410
+ `[(q_1, 1), \ldots, (q_n, 1)]`, where `n` is at most the
1411
+ :meth:`dimension` of `F`.
1412
+
1413
+ INPUT:
1414
+
1415
+ - ``alpha`` -- a `d`-tuple of positive integers or symbolic variables
1416
+ - ``asy_var`` -- (default: ``None``) a symbolic variable with
1417
+ respect to which to compute asymptotics;
1418
+ if ``None`` is given, we set ``asy_var = var('r')``
1419
+
1420
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
1421
+
1422
+ The output results from a Leinartas decomposition followed by a
1423
+ cohomology decomposition.
1424
+
1425
+ EXAMPLES::
1426
+
1427
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1428
+ sage: R.<x> = PolynomialRing(QQ)
1429
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1430
+ sage: f = (x^2 + 1)/((x - 1)^3*(x + 2))
1431
+ sage: F = FFPD(f)
1432
+ sage: alpha = [var('a')]
1433
+ sage: F.asymptotic_decomposition(alpha)
1434
+ (0, []) +
1435
+ (1/54*(5*a^2 + 2*a^2/x + 11*a^2/x^2)*r^2
1436
+ - 1/54*(5*a - 2*a/x - 33*a/x^2)*r + 11/27/x^2,
1437
+ [(x - 1, 1)]) + (-5/27, [(x + 2, 1)])
1438
+
1439
+ ::
1440
+
1441
+ sage: R.<x,y> = PolynomialRing(QQ)
1442
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1443
+ sage: H = (1 - 2*x -y)*(1 - x -2*y)**2
1444
+ sage: Hfac = H.factor()
1445
+ sage: G = 1/Hfac.unit()
1446
+ sage: F = FFPD(G, Hfac)
1447
+ sage: alpha = var('a, b')
1448
+ sage: F.asymptotic_decomposition(alpha)
1449
+ (0, []) +
1450
+ (-1/3*r*(a/x - 2*b/y) - 1/3/x + 2/3/y,
1451
+ [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
1452
+ """
1453
+ R = self.denominator_ring
1454
+ d = self.dimension()
1455
+ n = len(self.denominator_factored())
1456
+ X = [SR(x) for x in R.gens()]
1457
+
1458
+ # Reduce number of distinct factors in denominator of self
1459
+ # down to at most d.
1460
+ decomp1 = FractionWithFactoredDenominatorSum([self])
1461
+ if n > d:
1462
+ decomp1 = decomp1[0].leinartas_decomposition()
1463
+
1464
+ # Reduce to no repeated factors in denominator of each element
1465
+ # of decomp1.
1466
+ # Compute the cohomology decomposition for each
1467
+ # Cauchy differential form generated by each element of decomp.
1468
+ if asy_var is None:
1469
+ asy_var = var('r')
1470
+ cauchy_stuff = prod([X[j] ** (-alpha[j] * asy_var - 1)
1471
+ for j in range(d)])
1472
+ decomp2 = FractionWithFactoredDenominatorSum()
1473
+ for f in decomp1:
1474
+ ff = self.parent()(f.numerator() * cauchy_stuff,
1475
+ f.denominator_factored())
1476
+ decomp2.extend(ff.cohomology_decomposition())
1477
+ decomp2 = decomp2._combine_like_terms_()
1478
+
1479
+ # Divide out cauchy_stuff from integrands.
1480
+ decomp3 = FractionWithFactoredDenominatorSum()
1481
+ for f in decomp2:
1482
+ ff = self.parent()((f.numerator() /
1483
+ cauchy_stuff).simplify_full().collect(asy_var),
1484
+ f.denominator_factored())
1485
+ decomp3.append(ff)
1486
+
1487
+ return decomp3
1488
+
1489
+ def asymptotics(self, p, alpha, N, asy_var=None, numerical=0,
1490
+ verbose=False):
1491
+ r"""
1492
+ Return the asymptotics in the given direction.
1493
+
1494
+ This function returns the first `N` terms (some of which could be
1495
+ zero) of the asymptotic expansion of the Maclaurin ray coefficients
1496
+ `F_{r \alpha}` of the function `F` represented by ``self`` as
1497
+ `r \to \infty`, where `r` is ``asy_var`` and ``alpha`` is a tuple
1498
+ of positive integers of length `d` which is ``self.dimension()``.
1499
+ Assume that
1500
+
1501
+ - `F` is holomorphic in a neighborhood of the origin;
1502
+ - the unique factorization of the denominator `H` of `F` in the local
1503
+ algebraic ring at `p` equals its unique factorization in the local
1504
+ analytic ring at `p`;
1505
+ - the unique factorization of `H` in the local algebraic ring at `p`
1506
+ has at most ``d`` irreducible factors, none of which are repeated
1507
+ (one can reduce to this case via :meth:`asymptotic_decomposition()`);
1508
+ - `p` is a convenient strictly minimal smooth or multiple point
1509
+ with all nonzero coordinates that is critical and nondegenerate
1510
+ for ``alpha``.
1511
+
1512
+ The algorithms used here come from [RW2008]_ and [RW2012]_.
1513
+
1514
+ INPUT:
1515
+
1516
+ - ``p`` -- dictionary with keys that can be coerced to equal
1517
+ ``self.denominator_ring.gens()``
1518
+ - ``alpha`` -- tuple of length ``self.dimension()`` of positive
1519
+ integers or, if `p` is a smooth point, possibly of symbolic variables
1520
+ - ``N`` -- positive integer
1521
+ - ``asy_var`` -- (default: ``None``) a symbolic variable for the
1522
+ asymptotic expansion; if ``none`` is given, then ``var('r')`` will be
1523
+ assigned
1524
+ - ``numerical`` -- (default: 0) a natural number; if ``numerical`` is
1525
+ greater than 0, then return a numerical approximation of
1526
+ `F_{r \alpha}` with ``numerical`` digits of precision; otherwise
1527
+ return exact values
1528
+ - ``verbose`` -- boolean (default: ``False``); print the current state of
1529
+ the algorithm
1530
+
1531
+ OUTPUT:
1532
+
1533
+ The tuple ``(asy, exp_scale, subexp_part)``.
1534
+ Here ``asy`` is the sum of the first `N` terms (some of which might
1535
+ be 0) of the asymptotic expansion of `F_{r\alpha}` as `r \to \infty`;
1536
+ ``exp_scale**r`` is the exponential factor of ``asy``;
1537
+ ``subexp_part`` is the subexponential factor of ``asy``.
1538
+
1539
+ EXAMPLES::
1540
+
1541
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1542
+
1543
+ A smooth point example::
1544
+
1545
+ sage: R.<x,y> = PolynomialRing(QQ)
1546
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1547
+ sage: H = (1 - x - y - x*y)**2
1548
+ sage: Hfac = H.factor()
1549
+ sage: G = 1/Hfac.unit()
1550
+ sage: F = FFPD(G, Hfac); print(F)
1551
+ (1, [(x*y + x + y - 1, 2)])
1552
+ sage: alpha = [4, 3]
1553
+ sage: decomp = F.asymptotic_decomposition(alpha); decomp
1554
+ (0, []) + (... - 1/2, [(x*y + x + y - 1, 1)])
1555
+ sage: F1 = decomp[1]
1556
+ sage: p = {y: 1/3, x: 1/2}
1557
+ sage: asy = F1.asymptotics(p, alpha, 2, verbose=True)
1558
+ Creating auxiliary functions...
1559
+ Computing derivatives of auxiliary functions...
1560
+ Computing derivatives of more auxiliary functions...
1561
+ Computing second order differential operator actions...
1562
+ sage: asy
1563
+ (1/6000*(3600*sqrt(5)*sqrt(3)*sqrt(2)*sqrt(r)/sqrt(pi)
1564
+ + 463*sqrt(5)*sqrt(3)*sqrt(2)/(sqrt(pi)*sqrt(r)))*432^r,
1565
+ 432,
1566
+ 3/5*sqrt(5)*sqrt(3)*sqrt(2)*sqrt(r)/sqrt(pi)
1567
+ + 463/6000*sqrt(5)*sqrt(3)*sqrt(2)/(sqrt(pi)*sqrt(r)))
1568
+ sage: F.relative_error(asy[0], alpha, [1, 2, 4, 8, 16], asy[1]) # abs tol 1e-10 # long time
1569
+ [((4, 3), 2.083333333, [2.092576110], [-0.004436533009]),
1570
+ ((8, 6), 2.787374614, [2.790732875], [-0.001204811281]),
1571
+ ((16, 12), 3.826259447, [3.827462310], [-0.0003143703383]),
1572
+ ((32, 24), 5.328112821, [5.328540787], [-0.00008032230388]),
1573
+ ((64, 48), 7.475927885, [7.476079664], [-0.00002030232879])]
1574
+
1575
+ A multiple point example::
1576
+
1577
+ sage: R.<x,y,z>= PolynomialRing(QQ)
1578
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
1579
+ sage: H = (4 - 2*x - y - z)**2*(4 - x - 2*y - z)
1580
+ sage: Hfac = H.factor()
1581
+ sage: G = 16/Hfac.unit()
1582
+ sage: F = FFPD(G, Hfac)
1583
+ sage: F
1584
+ (-16, [(x + 2*y + z - 4, 1), (2*x + y + z - 4, 2)])
1585
+ sage: alpha = [3, 3, 2]
1586
+ sage: decomp = F.asymptotic_decomposition(alpha); decomp
1587
+ (0, []) + (..., [(x + 2*y + z - 4, 1), (2*x + y + z - 4, 1)])
1588
+ sage: F1 = decomp[1]
1589
+ sage: p = {x: 1, y: 1, z: 1}
1590
+ sage: asy = F1.asymptotics(p, alpha, 2, verbose=True) # long time
1591
+ Creating auxiliary functions...
1592
+ Computing derivatives of auxiliary functions...
1593
+ Computing derivatives of more auxiliary functions...
1594
+ Computing second-order differential operator actions...
1595
+ sage: asy # long time
1596
+ (4/3*sqrt(3)*sqrt(r)/sqrt(pi) + 47/216*sqrt(3)/(sqrt(pi)*sqrt(r)),
1597
+ 1, 4/3*sqrt(3)*sqrt(r)/sqrt(pi) + 47/216*sqrt(3)/(sqrt(pi)*sqrt(r)))
1598
+ sage: F.relative_error(asy[0], alpha, [1, 2, 4, 8], asy[1]) # long time
1599
+ [((3, 3, 2), 0.9812164307, [1.515572606], [-0.54458543...]),
1600
+ ((6, 6, 4), 1.576181132, [1.992989399], [-0.26444185...]),
1601
+ ((12, 12, 8), 2.485286378, [2.712196351], [-0.091301338...]),
1602
+ ((24, 24, 16), 3.700576827, [3.760447895], [-0.016178847...])]
1603
+ """
1604
+ R = self.denominator_ring
1605
+
1606
+ # Coerce keys of p into R.
1607
+ p = coerce_point(R, p)
1608
+
1609
+ if asy_var is None:
1610
+ asy_var = var('r')
1611
+ d = self.dimension()
1612
+ X = list(R.gens())
1613
+ alpha = list(alpha)
1614
+ df = self.denominator_factored()
1615
+ n = len(df) # Number of smooth factors
1616
+
1617
+ # Find greatest i such that X[i] is a convenient coordinate,
1618
+ # that is, such that for all (h, e) in df, we have
1619
+ # (X[i]*diff(h, X[i])).subs(p) != 0.
1620
+ # Assuming such an i exists.
1621
+ i = d - 1
1622
+ while 0 in [(X[i] * diff(h, X[i])).subs(p) for (h, e) in df]:
1623
+ i -= 1
1624
+ coordinate = i
1625
+
1626
+ if n == 1:
1627
+ # Smooth point.
1628
+ return self.asymptotics_smooth(p, alpha, N, asy_var, coordinate,
1629
+ numerical, verbose=verbose)
1630
+
1631
+ # Multiple point.
1632
+ return self.asymptotics_multiple(p, alpha, N, asy_var, coordinate,
1633
+ numerical, verbose=verbose)
1634
+
1635
+ def asymptotics_smooth(self, p, alpha, N, asy_var, coordinate=None,
1636
+ numerical=0, verbose=False):
1637
+ r"""
1638
+ Return the asymptotics in the given direction of a smooth point.
1639
+
1640
+ This is the same as :meth:`asymptotics()`, but only in the
1641
+ case of a convenient smooth point.
1642
+
1643
+ The formulas used for computing the asymptotic expansions are
1644
+ Theorems 3.2 and 3.3 [RW2008]_ with the exponent of `H`
1645
+ equal to 1. Theorem 3.2 is a specialization of Theorem 3.4
1646
+ of [RW2012]_ with `n = 1`.
1647
+
1648
+ INPUT:
1649
+
1650
+ - ``p`` -- dictionary with keys that can be coerced to equal
1651
+ ``self.denominator_ring.gens()``
1652
+ - ``alpha`` -- tuple of length ``d = self.dimension()`` of positive
1653
+ integers or, if `p` is a smooth point, possibly of symbolic variables
1654
+ - ``N`` -- positive integer
1655
+ - ``asy_var`` -- (default: ``None``) a symbolic variable; the variable
1656
+ of the asymptotic expansion, if none is given, ``var('r')`` will be
1657
+ assigned
1658
+ - ``coordinate`` -- (default: ``None``) an integer in
1659
+ `\{0, \ldots, d-1\}` indicating a convenient coordinate to base the
1660
+ asymptotic calculations on; if ``None`` is assigned, then choose
1661
+ ``coordinate=d-1``
1662
+ - ``numerical`` -- (default: 0) a natural number; if numerical is
1663
+ greater than 0, then return a numerical approximation of the
1664
+ Maclaurin ray coefficients of ``self`` with ``numerical`` digits of
1665
+ precision; otherwise return exact values
1666
+ - ``verbose`` -- boolean (default: ``False``); print the current state
1667
+ of the algorithm
1668
+
1669
+ OUTPUT: the asymptotic expansion
1670
+
1671
+ EXAMPLES::
1672
+
1673
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
1674
+ sage: R.<x> = PolynomialRing(QQ)
1675
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1676
+ sage: H = 2 - 3*x
1677
+ sage: Hfac = H.factor()
1678
+ sage: G = 1/Hfac.unit()
1679
+ sage: F = FFPD(G, Hfac)
1680
+ sage: F
1681
+ (-1/3, [(x - 2/3, 1)])
1682
+ sage: alpha = [2]
1683
+ sage: p = {x: 2/3}
1684
+ sage: asy = F.asymptotics_smooth(p, alpha, 3, asy_var=var('r'))
1685
+ sage: asy
1686
+ (1/2*(9/4)^r, 9/4, 1/2)
1687
+
1688
+ ::
1689
+
1690
+ sage: R.<x,y> = PolynomialRing(QQ)
1691
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
1692
+ sage: H = 1-x-y-x*y
1693
+ sage: Hfac = H.factor()
1694
+ sage: G = 1/Hfac.unit()
1695
+ sage: F = FFPD(G, Hfac)
1696
+ sage: alpha = [3, 2]
1697
+ sage: p = {y: 1/2*sqrt(13) - 3/2, x: 1/3*sqrt(13) - 2/3}
1698
+ sage: F.asymptotics_smooth(p, alpha, 2, var('r'), numerical=3, verbose=True)
1699
+ Creating auxiliary functions...
1700
+ Computing derivatives of auxiliary functions...
1701
+ Computing derivatives of more auxiliary functions...
1702
+ Computing second order differential operator actions...
1703
+ (71.2^r*(0.369/sqrt(r) - 0.018.../r^(3/2)), 71.2, 0.369/sqrt(r) - 0.018.../r^(3/2))
1704
+
1705
+ sage: q = 1/2
1706
+ sage: qq = q.denominator()
1707
+ sage: H = 1 - q*x + q*x*y - x^2*y
1708
+ sage: Hfac = H.factor()
1709
+ sage: G = (1 - q*x)/Hfac.unit()
1710
+ sage: F = FFPD(G, Hfac)
1711
+ sage: alpha = list(qq*vector([2, 1 - q]))
1712
+ sage: alpha
1713
+ [4, 1]
1714
+ sage: p = {x: 1, y: 1}
1715
+ sage: F.asymptotics_smooth(p, alpha, 5, var('r'), verbose=True) # not tested (140 seconds)
1716
+ Creating auxiliary functions...
1717
+ Computing derivatives of auxiliary functions...
1718
+ Computing derivatives of more auxiliary functions...
1719
+ Computing second order differential operator actions...
1720
+ (1/12*sqrt(3)*2^(2/3)*gamma(1/3)/(pi*r^(1/3))
1721
+ - 1/96*sqrt(3)*2^(1/3)*gamma(2/3)/(pi*r^(5/3)),
1722
+ 1,
1723
+ 1/12*sqrt(3)*2^(2/3)*gamma(1/3)/(pi*r^(1/3))
1724
+ - 1/96*sqrt(3)*2^(1/3)*gamma(2/3)/(pi*r^(5/3)))
1725
+ """
1726
+ from sage.calculus.functions import jacobian
1727
+ from sage.calculus.var import function
1728
+ from sage.functions.other import factorial
1729
+ from sage.misc.functional import sqrt
1730
+ from sage.functions.gamma import gamma
1731
+ from sage.functions.log import exp, log
1732
+ from sage.matrix.constructor import matrix
1733
+ from sage.modules.free_module_element import vector
1734
+ from sage.symbolic.constants import pi
1735
+ from sage.symbolic.relation import solve
1736
+ from sage.rings.cc import CC
1737
+ from sage.rings.rational_field import QQ
1738
+
1739
+ R = self.denominator_ring
1740
+ d = self.dimension()
1741
+ I = sqrt(-ZZ.one())
1742
+ # Coerce everything into the Symbolic Ring.
1743
+ X = [SR(x) for x in R.gens()]
1744
+ G = SR(self.numerator())
1745
+ H = SR(self.denominator())
1746
+ p = {SR(x): p[x] for x in R.gens()}
1747
+ alpha = [SR(a) for a in alpha]
1748
+
1749
+ # Put given convenient coordinate at end of variable list.
1750
+ if coordinate is not None:
1751
+ x = X.pop(coordinate)
1752
+ X.append(x)
1753
+ a = alpha.pop(coordinate)
1754
+ alpha.append(a)
1755
+
1756
+ # Deal with the simple univariate case first.
1757
+ # Same as the multiple point case with n == d.
1758
+ # but with a negative sign.
1759
+ # I'll just past the code from the multiple point case.
1760
+ if d == 1:
1761
+ det = jacobian(H, X).subs(p).determinant().abs()
1762
+ exp_scale = prod([(p[X[i]] ** (-alpha[i])).subs(p)
1763
+ for i in range(d)])
1764
+ subexp_part = -G.subs(p) / (det * prod(p.values()))
1765
+ if numerical:
1766
+ exp_scale = exp_scale.n(digits=numerical)
1767
+ subexp_part = subexp_part.n(digits=numerical)
1768
+ return (exp_scale ** asy_var * subexp_part, exp_scale, subexp_part)
1769
+
1770
+ # If p is a tuple of rationals, then compute with it directly.
1771
+ # Otherwise, compute symbolically and plug in p at the end.
1772
+ if vector(p.values()) in QQ ** d:
1773
+ P = p
1774
+ else:
1775
+ sP = [var('p' + str(j)) for j in range(d)]
1776
+ P = {X[j]: sP[j] for j in range(d)}
1777
+ p = {sP[j]: p[X[j]] for j in range(d)}
1778
+
1779
+ # Setup.
1780
+ if verbose:
1781
+ print("Creating auxiliary functions...")
1782
+ # Implicit functions.
1783
+ h = function('h')(*tuple(X[:d - 1]))
1784
+ U = function('U')(*tuple(X))
1785
+ # All other functions are defined in terms of h, U, and
1786
+ # explicit functions.
1787
+ Gcheck = -G / U * (h / X[d - 1])
1788
+ A = Gcheck.subs({X[d - 1]: ZZ.one() / h}) / h
1789
+ t = 't'
1790
+ L = [str(elt) for elt in X]
1791
+ while t in L:
1792
+ t = t + 't'
1793
+ T = [var(t + str(i)) for i in range(d - 1)]
1794
+ e = {X[i]: P[X[i]] * exp(I * T[i]) for i in range(d - 1)}
1795
+ ht = h.subs(e)
1796
+ At = A.subs(e)
1797
+ Phit = (-log(P[X[d - 1]] * ht) +
1798
+ I * sum([alpha[i] / alpha[d - 1] * T[i]
1799
+ for i in range(d - 1)]))
1800
+ Tstar = {t: ZZ.zero() for t in T}
1801
+ # Store h and U and all their derivatives evaluated at P.
1802
+ atP = P.copy()
1803
+ atP.update({h.subs(P): ZZ.one() / P[X[d - 1]]})
1804
+
1805
+ # Compute the derivatives of h up to order 2 * N, evaluate at P,
1806
+ # and store in atP.
1807
+ # Keep a copy of unevaluated h derivatives for use in the case
1808
+ # d = 2 and v > 2 below.
1809
+ hderivs1 = {} # First derivatives of h.
1810
+ for i in range(d - 1):
1811
+ s = solve(diff(H.subs({X[d - 1]: ZZ.one() / h}), X[i]),
1812
+ diff(h, X[i]))[0].rhs().simplify()
1813
+ hderivs1.update({diff(h, X[i]): s})
1814
+ atP.update({diff(h, X[i]).subs(P): s.subs(P).subs(atP)})
1815
+ hderivs = diff_all(h, X[0: d - 1], 2 * N, sub=hderivs1, rekey=h)
1816
+ for k in hderivs:
1817
+ atP.update({k.subs(P): hderivs[k].subs(atP)})
1818
+
1819
+ # Compute the derivatives of U up to order 2 * N and evaluate at P.
1820
+ # To do this, differentiate H = U*Hcheck over and over, evaluate at P,
1821
+ # and solve for the derivatives of U at P.
1822
+ # Need the derivatives of H with short keys to pass on
1823
+ # to diff_prod later.
1824
+ Hderivs = diff_all(H, X, 2 * N, ending=[X[d - 1]], sub_final=P)
1825
+ if verbose:
1826
+ print("Computing derivatives of auxiliary functions...")
1827
+ # For convenience in checking if all the nontrivial derivatives of U
1828
+ # at p are zero a few line below, store the value of U(p) in atP
1829
+ # instead of in Uderivs.
1830
+ Uderivs = {}
1831
+ atP.update({U.subs(P): diff(H, X[d - 1]).subs(P)})
1832
+ end = [X[d - 1]]
1833
+ Hcheck = X[d - 1] - ZZ.one() / h
1834
+ k = H.polynomial(CC).degree() - 1
1835
+ if k == 0:
1836
+ # Then we can conclude that all higher derivatives of U are zero.
1837
+ for l in range(1, 2 * N + 1):
1838
+ for s in combinations_with_replacement(X, l):
1839
+ Uderivs[diff(U, list(s)).subs(P)] = ZZ.zero()
1840
+ elif k > 0 and k < 2 * N:
1841
+ all_zero = True
1842
+ Uderivs = diff_prod(Hderivs, U, Hcheck, X,
1843
+ range(1, k + 1), end, Uderivs, atP)
1844
+ # Check for a nonzero U derivative.
1845
+ if any(Uderivs.values()):
1846
+ all_zero = False
1847
+ if all_zero:
1848
+ # Then, using a proposition at the end of [RW2012], we can
1849
+ # conclude that all higher derivatives of U are zero.
1850
+ for l in range(k + 1, 2 * N + 1):
1851
+ for s in combinations_with_replacement(X, l):
1852
+ Uderivs.update({diff(U, list(s)).subs(P): ZZ.zero()})
1853
+ else:
1854
+ # Have to compute the rest of the derivatives.
1855
+ Uderivs = diff_prod(Hderivs, U, Hcheck, X,
1856
+ range(k + 1, 2 * N + 1), end, Uderivs, atP)
1857
+ else:
1858
+ Uderivs = diff_prod(Hderivs, U, Hcheck, X,
1859
+ range(1, 2 * N + 1), end, Uderivs, atP)
1860
+ atP.update(Uderivs)
1861
+
1862
+ # In general, this algorithm is not designed to handle the case of a
1863
+ # singular Phit''(Tstar).
1864
+ # However, when d = 2 the algorithm can cope.
1865
+ if d == 2:
1866
+ # Compute v, the order of vanishing at Tstar of Phit.
1867
+ # It is at least 2.
1868
+ v = Integer(2)
1869
+ Phitderiv = diff(Phit, T[0], 2)
1870
+ splat = Phitderiv.subs(Tstar).subs(atP).subs(p).simplify()
1871
+ while splat == 0:
1872
+ v += 1
1873
+ if v > 2 * N:
1874
+ # Then need to compute more derivatives of h for atP.
1875
+ hderivs.update({diff(h, X[0], v):
1876
+ diff(hderivs[diff(h, X[0], v - 1)],
1877
+ X[0]).subs(hderivs1)})
1878
+ atP.update({diff(h, X[0], v).subs(P):
1879
+ hderivs[diff(h, X[0], v)].subs(atP)})
1880
+ Phitderiv = diff(Phitderiv, T[0])
1881
+ splat = Phitderiv.subs(Tstar).subs(atP).subs(p).simplify()
1882
+
1883
+ if d == 2 and v > 2:
1884
+ t = T[0] # Simplify variable names.
1885
+ a = splat / factorial(v)
1886
+ Phitu = Phit - a * t ** v
1887
+
1888
+ # Compute all partial derivatives of At and Phitu
1889
+ # up to orders 2*(N - 1) and 2*(N - 1) + v, respectively,
1890
+ # in case v is even.
1891
+ # Otherwise, compute up to orders N - 1 and N - 1 + v,
1892
+ # respectively.
1893
+ # To speed up later computations,
1894
+ # create symbolic functions AA and BB
1895
+ # to stand in for the expressions At and Phitu, respectively.
1896
+ if verbose:
1897
+ print("Computing derivatives of more auxiliary functions...")
1898
+ AA = function('AA')(t)
1899
+ BB = function('BB')(t)
1900
+ if v.mod(2) == 0:
1901
+ At_derivs = diff_all(At, T, 2 * N - 2, sub=hderivs1,
1902
+ sub_final=[Tstar, atP], rekey=AA)
1903
+ Phitu_derivs = diff_all(Phitu, T, 2 * N - 2 + v,
1904
+ sub=hderivs1, sub_final=[Tstar, atP],
1905
+ zero_order=v + 1, rekey=BB)
1906
+ else:
1907
+ At_derivs = diff_all(At, T, N - 1, sub=hderivs1,
1908
+ sub_final=[Tstar, atP], rekey=AA)
1909
+ Phitu_derivs = diff_all(Phitu, T, N - 1 + v,
1910
+ sub=hderivs1, sub_final=[Tstar, atP],
1911
+ zero_order=v + 1, rekey=BB)
1912
+ AABB_derivs = At_derivs
1913
+ AABB_derivs.update(Phitu_derivs)
1914
+ AABB_derivs[AA] = At.subs(Tstar).subs(atP)
1915
+ AABB_derivs[BB] = Phitu.subs(Tstar).subs(atP)
1916
+ if verbose:
1917
+ print("Computing second order differential operator actions...")
1918
+ DD = diff_op_simple(AA, BB, AABB_derivs, t, v, a, N)
1919
+
1920
+ # Plug above into asymptotic formula.
1921
+ L = []
1922
+ if v.mod(2) == 0:
1923
+ for k in range(N):
1924
+ L.append(sum([(-1) ** l * gamma((2 * k + v * l + 1) / v) /
1925
+ (factorial(l) * factorial(2 * k + v * l)) *
1926
+ DD[(k, l)] for l in range(0, 2 * k + 1)]))
1927
+ chunk = (a ** (-1 / v) / (pi * v) *
1928
+ sum([alpha[d - 1] ** (-(2 * k + 1) / v) *
1929
+ L[k] * asy_var ** (-(2 * k + 1) / v)
1930
+ for k in range(N)]))
1931
+ else:
1932
+ zeta = exp(I * pi / (2 * v))
1933
+ for k in range(N):
1934
+ L.append(sum([(-1) ** l * gamma((k + v * l + 1) / v) /
1935
+ (factorial(l) * factorial(k + v * l)) *
1936
+ (zeta ** (k + v * l + 1) +
1937
+ (-1) ** (k + v * l) *
1938
+ zeta ** (-(k + v * l + 1))) *
1939
+ DD[(k, l)] for l in range(0, k + 1)]))
1940
+ chunk = (abs(a) ** (-1 / v) / (2 * pi * v) *
1941
+ sum([alpha[d - 1] ** (-(k + 1) / v) *
1942
+ L[k] * asy_var ** (-(k + 1) / v)
1943
+ for k in range(N)]))
1944
+
1945
+ # Asymptotics for d >= 2 case.
1946
+ # A singular Phit''(Tstar) will cause a crash in this case.
1947
+ else:
1948
+ Phit1 = jacobian(Phit, T).subs(hderivs1)
1949
+ a = jacobian(Phit1, T).subs(hderivs1).subs(Tstar).subs(atP)
1950
+ a_inv = a.inverse()
1951
+ Phitu = (Phit - (1 / QQ(2)) * matrix([T]) *
1952
+ a * matrix([T]).transpose())
1953
+ Phitu = Phitu[0][0]
1954
+ # Compute all partial derivatives of At and Phitu up to
1955
+ # orders 2 * N-2 and 2 * N, respectively.
1956
+ # Take advantage of the fact that At and Phitu
1957
+ # are sufficiently differentiable functions so that mixed partials
1958
+ # are equal. Thus only need to compute representative partials.
1959
+ # Choose nondecreasing sequences as representative differentiation-
1960
+ # order sequences.
1961
+ # To speed up later computations,
1962
+ # create symbolic functions AA and BB
1963
+ # to stand in for the expressions At and Phitu, respectively.
1964
+ if verbose:
1965
+ print("Computing derivatives of more auxiliary functions...")
1966
+ AA = function('AA')(*tuple(T))
1967
+ At_derivs = diff_all(At, T, 2 * N - 2, sub=hderivs1,
1968
+ sub_final=[Tstar, atP], rekey=AA)
1969
+ BB = function('BB')(*tuple(T))
1970
+ Phitu_derivs = diff_all(Phitu, T, 2 * N, sub=hderivs1,
1971
+ sub_final=[Tstar, atP], rekey=BB, zero_order=3)
1972
+ AABB_derivs = At_derivs
1973
+ AABB_derivs.update(Phitu_derivs)
1974
+ AABB_derivs[AA] = At.subs(Tstar).subs(atP)
1975
+ AABB_derivs[BB] = Phitu.subs(Tstar).subs(atP)
1976
+ if verbose:
1977
+ print("Computing second order differential operator actions...")
1978
+ DD = diff_op(AA, BB, AABB_derivs, T, a_inv, 1, N)
1979
+
1980
+ # Plug above into asymptotic formula.
1981
+ L = []
1982
+ for k in range(N):
1983
+ L.append(sum([DD[(0, k, l)] / ((-1) ** k * 2 ** (l + k) *
1984
+ factorial(l) * factorial(l + k))
1985
+ for l in range(0, 2 * k + 1)]))
1986
+ chunk = sum([(2 * pi) ** ((1 - d) / Integer(2)) *
1987
+ a.determinant() ** (-ZZ.one() / Integer(2)) *
1988
+ alpha[d - 1] ** ((ZZ.one() - d) / Integer(2) - k) *
1989
+ L[k] *
1990
+ asy_var ** ((ZZ.one() - d) / Integer(2) - k)
1991
+ for k in range(N)])
1992
+
1993
+ chunk = chunk.subs(p).simplify()
1994
+ coeffs = chunk.coefficients(asy_var)
1995
+ coeffs.reverse()
1996
+ coeffs = coeffs[:N]
1997
+ if numerical:
1998
+ subexp_part = sum([co[0].subs(p).n(digits=numerical) *
1999
+ asy_var ** co[1] for co in coeffs])
2000
+ exp_scale = prod([(P[X[i]] ** (-alpha[i])).subs(p)
2001
+ for i in range(d)]).n(digits=numerical)
2002
+ else:
2003
+ subexp_part = sum([co[0].subs(p) * asy_var ** co[1]
2004
+ for co in coeffs])
2005
+ exp_scale = prod([(P[X[i]] ** (-alpha[i])).subs(p)
2006
+ for i in range(d)])
2007
+ return (exp_scale ** asy_var * subexp_part, exp_scale, subexp_part)
2008
+
2009
+ def asymptotics_multiple(self, p, alpha, N, asy_var, coordinate=None,
2010
+ numerical=0, verbose=False):
2011
+ r"""
2012
+ Return the asymptotics in the given direction of a multiple
2013
+ point nondegenerate for ``alpha``.
2014
+
2015
+ This is the same as :meth:`asymptotics`, but only in the case
2016
+ of a convenient multiple point nondegenerate for ``alpha``.
2017
+ Assume also that ``self.dimension >= 2`` and that the
2018
+ ``p.values()`` are not symbolic variables.
2019
+
2020
+ The formulas used for computing the asymptotic expansion are
2021
+ Theorem 3.4 and Theorem 3.7 of [RW2012]_.
2022
+
2023
+ INPUT:
2024
+
2025
+ - ``p`` -- dictionary with keys that can be coerced to equal
2026
+ ``self.denominator_ring.gens()``
2027
+ - ``alpha`` -- tuple of length ``d = self.dimension()`` of positive
2028
+ integers or, if `p` is a smooth point, possibly of symbolic variables
2029
+ - ``N`` -- positive integer
2030
+ - ``asy_var`` -- (default: ``None``) a symbolic variable; the variable
2031
+ of the asymptotic expansion, if none is given, ``var('r')`` will be
2032
+ assigned
2033
+ - ``coordinate`` -- (default: ``None``) an integer in
2034
+ `\{0, \ldots, d-1\}` indicating a convenient coordinate to base the
2035
+ asymptotic calculations on; if ``None`` is assigned, then choose
2036
+ ``coordinate=d-1``
2037
+ - ``numerical`` -- (default: 0) a natural number; if numerical is
2038
+ greater than 0, then return a numerical approximation of the
2039
+ Maclaurin ray coefficients of ``self`` with ``numerical`` digits of
2040
+ precision. Otherwise return exact values.
2041
+ - ``verbose`` -- boolean (default: ``False``); print the current state of
2042
+ the algorithm
2043
+
2044
+ OUTPUT: the asymptotic expansion
2045
+
2046
+ EXAMPLES::
2047
+
2048
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2049
+ sage: R.<x,y,z>= PolynomialRing(QQ)
2050
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2051
+ sage: H = (4 - 2*x - y - z)*(4 - x -2*y - z)
2052
+ sage: Hfac = H.factor()
2053
+ sage: G = 16/Hfac.unit()
2054
+ sage: F = FFPD(G, Hfac)
2055
+ sage: F
2056
+ (16, [(x + 2*y + z - 4, 1), (2*x + y + z - 4, 1)])
2057
+ sage: p = {x: 1, y: 1, z: 1}
2058
+ sage: alpha = [3, 3, 2]
2059
+ sage: F.asymptotics_multiple(p, alpha, 2, var('r'), verbose=True) # long time
2060
+ Creating auxiliary functions...
2061
+ Computing derivatives of auxiliary functions...
2062
+ Computing derivatives of more auxiliary functions...
2063
+ Computing second-order differential operator actions...
2064
+ (4/3*sqrt(3)/(sqrt(pi)*sqrt(r)) - 25/216*sqrt(3)/(sqrt(pi)*r^(3/2)),
2065
+ 1,
2066
+ 4/3*sqrt(3)/(sqrt(pi)*sqrt(r)) - 25/216*sqrt(3)/(sqrt(pi)*r^(3/2)))
2067
+
2068
+ sage: H = (1 - x*(1 + y))*(1 - z*x**2*(1 + 2*y))
2069
+ sage: Hfac = H.factor()
2070
+ sage: G = 1/Hfac.unit()
2071
+ sage: F = FFPD(G, Hfac)
2072
+ sage: F
2073
+ (1, [(x*y + x - 1, 1), (2*x^2*y*z + x^2*z - 1, 1)])
2074
+ sage: p = {x: 1/2, z: 4/3, y: 1}
2075
+ sage: alpha = [8, 3, 3]
2076
+ sage: F.asymptotics_multiple(p, alpha, 2, var('r'), coordinate=1, verbose=True) # long time
2077
+ Creating auxiliary functions...
2078
+ Computing derivatives of auxiliary functions...
2079
+ Computing derivatives of more auxiliary functions...
2080
+ Computing second-order differential operator actions...
2081
+ (1/172872*108^r*(24696*sqrt(7)*sqrt(3)/(sqrt(pi)*sqrt(r))
2082
+ - 1231*sqrt(7)*sqrt(3)/(sqrt(pi)*r^(3/2))),
2083
+ 108,
2084
+ 1/7*sqrt(7)*sqrt(3)/(sqrt(pi)*sqrt(r))
2085
+ - 1231/172872*sqrt(7)*sqrt(3)/(sqrt(pi)*r^(3/2)))
2086
+
2087
+ ::
2088
+
2089
+ sage: R.<x,y> = PolynomialRing(QQ)
2090
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
2091
+ sage: H = (1 - 2*x - y) * (1 - x - 2*y)
2092
+ sage: Hfac = H.factor()
2093
+ sage: G = exp(x + y)/Hfac.unit()
2094
+ sage: F = FFPD(G, Hfac)
2095
+ sage: F
2096
+ (e^(x + y), [(x + 2*y - 1, 1), (2*x + y - 1, 1)])
2097
+ sage: p = {x: 1/3, y: 1/3}
2098
+ sage: alpha = (var('a'), var('b'))
2099
+ sage: F.asymptotics_multiple(p, alpha, 2, var('r')) # long time
2100
+ (3*(1/((1/3)^a*(1/3)^b))^r*e^(2/3), 1/((1/3)^a*(1/3)^b), 3*e^(2/3))
2101
+ """
2102
+ from itertools import product
2103
+ from sage.calculus.functions import jacobian
2104
+ from sage.calculus.var import function
2105
+ from sage.combinat.combinat import stirling_number1
2106
+ from sage.functions.log import exp, log
2107
+ from sage.functions.other import factorial
2108
+ from sage.misc.functional import sqrt
2109
+ from sage.matrix.constructor import matrix
2110
+ from sage.misc.mrange import xmrange
2111
+ from sage.modules.free_module_element import vector
2112
+ from sage.rings.cc import CC
2113
+ from sage.arith.misc import binomial
2114
+ from sage.rings.rational_field import QQ
2115
+ from sage.symbolic.constants import pi
2116
+ from sage.symbolic.relation import solve
2117
+
2118
+ R = self.denominator_ring
2119
+
2120
+ # Coerce keys of p into R.
2121
+ p = coerce_point(R, p)
2122
+
2123
+ d = self.dimension()
2124
+ I = sqrt(-ZZ.one())
2125
+ # Coerce everything into the Symbolic Ring.
2126
+ X = [SR(x) for x in R.gens()]
2127
+ G = SR(self.numerator())
2128
+ H = [SR(h) for (h, e) in self.denominator_factored()]
2129
+ Hprod = prod(H)
2130
+ n = len(H)
2131
+ P = {SR(x): p[x] for x in R.gens()}
2132
+ Sstar = self._crit_cone_combo(p, alpha, coordinate)
2133
+
2134
+ # Put the given convenient variable at end of variable list.
2135
+ if coordinate is not None:
2136
+ x = X.pop(coordinate)
2137
+ X.append(x)
2138
+ a = alpha.pop(coordinate)
2139
+ alpha.append(a)
2140
+
2141
+ # Case n = d.
2142
+ if n == d:
2143
+ det = jacobian(H, X).subs(P).determinant().abs()
2144
+ exp_scale = prod([(P[X[i]] ** (-alpha[i])).subs(P)
2145
+ for i in range(d)])
2146
+ subexp_part = G.subs(P) / (det * prod(P.values()))
2147
+ if numerical:
2148
+ exp_scale = exp_scale.n(digits=numerical)
2149
+ subexp_part = subexp_part.n(digits=numerical)
2150
+ return (exp_scale ** asy_var * subexp_part, exp_scale, subexp_part)
2151
+
2152
+ # Case n < d.
2153
+ # If P is a tuple of rationals, then compute with it directly.
2154
+ # Otherwise, compute symbolically and plug in P at the end.
2155
+ if vector(P.values()) not in QQ ** d:
2156
+ sP = [var('p' + str(j)) for j in range(d)]
2157
+ P = {X[j]: sP[j] for j in range(d)}
2158
+ p = {sP[j]: p[X[j]] for j in range(d)}
2159
+
2160
+ # Setup.
2161
+ if verbose:
2162
+ print("Creating auxiliary functions...")
2163
+ # Create T and S variables.
2164
+ t = 't'
2165
+ L = [str(elt) for elt in X]
2166
+ while t in L:
2167
+ t = t + 't'
2168
+ T = [var(t + str(i)) for i in range(d - 1)]
2169
+ s = 's'
2170
+ while s in L:
2171
+ s = s + 't'
2172
+ S = [var(s + str(i)) for i in range(n - 1)]
2173
+ Sstar = {S[j]: Sstar[j] for j in range(n - 1)}
2174
+ thetastar = {t: ZZ.zero() for t in T}
2175
+ thetastar.update(Sstar)
2176
+ # Create implicit functions.
2177
+ h = [function('h' + str(j))(*tuple(X[:d - 1])) for j in range(n)]
2178
+ U = function('U')(*tuple(X))
2179
+ # All other functions are defined in terms of h, U, and
2180
+ # explicit functions.
2181
+ Hcheck = prod([X[d - 1] - ZZ.one() / h[j] for j in range(n)])
2182
+ Gcheck = -G / U * prod([-h[j] / X[d - 1] for j in range(n)])
2183
+ A = [(-1) ** (n - 1) * X[d - 1] ** (-n + j) *
2184
+ diff(Gcheck.subs({X[d - 1]: ZZ.one() / X[d - 1]}), X[d - 1], j)
2185
+ for j in range(n)]
2186
+ e = {X[i]: P[X[i]] * exp(I * T[i]) for i in range(d - 1)}
2187
+ ht = [hh.subs(e) for hh in h]
2188
+ hsumt = (sum([S[j] * ht[j] for j in range(n - 1)]) +
2189
+ (ZZ.one() - sum(S)) * ht[n - 1])
2190
+ At = [AA.subs(e).subs({X[d - 1]: hsumt}) for AA in A]
2191
+ Phit = (-log(P[X[d - 1]] * hsumt) +
2192
+ I * sum([alpha[i] / alpha[d - 1] * T[i]
2193
+ for i in range(d - 1)]))
2194
+ # atP Stores h and U and all their derivatives evaluated at C.
2195
+ atP = P.copy()
2196
+ atP.update({hh.subs(P): ZZ.one() / P[X[d - 1]] for hh in h})
2197
+
2198
+ # Compute the derivatives of h up to order 2 * N and evaluate at P.
2199
+ hderivs1 = {} # First derivatives of h.
2200
+ for (i, j) in xmrange([d - 1, n], tuple):
2201
+ s = solve(diff(H[j].subs({X[d - 1]: ZZ.one() / h[j]}), X[i]),
2202
+ diff(h[j], X[i]))[0].rhs().simplify()
2203
+ hderivs1.update({diff(h[j], X[i]): s})
2204
+ atP.update({diff(h[j], X[i]).subs(P): s.subs(P).subs(atP)})
2205
+ hderivs = diff_all(h, X[0:d - 1], 2 * N, sub=hderivs1, rekey=h)
2206
+ for k in hderivs:
2207
+ atP.update({k.subs(P): hderivs[k].subs(atP)})
2208
+
2209
+ # Compute the derivatives of U up to order 2 * N - 2 + min{n, N} - 1 and
2210
+ # evaluate at P.
2211
+ # To do this, differentiate H = U*Hcheck over and over, evaluate at P,
2212
+ # and solve for the derivatives of U at P.
2213
+ # Need the derivatives of H with short keys to pass on to
2214
+ # diff_prod later.
2215
+ if verbose:
2216
+ print("Computing derivatives of auxiliary functions...")
2217
+ m = min(n, N)
2218
+ end = [X[d - 1] for j in range(n)]
2219
+ Hprodderivs = diff_all(Hprod, X, 2 * N - 2 + n, ending=end, sub_final=P)
2220
+ atP.update({U.subs(P): diff(Hprod, X[d - 1], n).subs(P) / factorial(n)})
2221
+ Uderivs = {}
2222
+ k = Hprod.polynomial(CC).degree() - n
2223
+ if k == 0:
2224
+ # Then we can conclude that all higher derivatives of U are zero.
2225
+ for l in range(1, 2 * N - 2 + m):
2226
+ for s in combinations_with_replacement(X, l):
2227
+ Uderivs[diff(U, list(s)).subs(P)] = ZZ.zero()
2228
+ elif k > 0 and k < 2 * N - 2 + m - 1:
2229
+ all_zero = True
2230
+ Uderivs = diff_prod(Hprodderivs, U, Hcheck, X,
2231
+ range(1, k + 1), end, Uderivs, atP)
2232
+ # Check for a nonzero U derivative.
2233
+ if any(Uderivs.values()):
2234
+ all_zero = False
2235
+ if all_zero:
2236
+ # Then all higher derivatives of U are zero.
2237
+ for l in range(k + 1, 2 * N - 2 + m):
2238
+ for s in combinations_with_replacement(X, l):
2239
+ Uderivs.update({diff(U, list(s)).subs(P): ZZ.zero()})
2240
+ else:
2241
+ # Have to compute the rest of the derivatives.
2242
+ Uderivs = diff_prod(Hprodderivs, U, Hcheck, X,
2243
+ range(k + 1, 2 * N - 2 + m), end,
2244
+ Uderivs, atP)
2245
+ else:
2246
+ Uderivs = diff_prod(Hprodderivs, U, Hcheck, X,
2247
+ range(1, 2 * N - 2 + m), end, Uderivs, atP)
2248
+ atP.update(Uderivs)
2249
+ Phit1 = jacobian(Phit, T + S).subs(hderivs1)
2250
+ a = jacobian(Phit1, T + S).subs(hderivs1).subs(thetastar).subs(atP)
2251
+ a_inv = a.inverse()
2252
+ Phitu = (Phit - (1 / Integer(2)) * matrix([T + S]) * a *
2253
+ matrix([T + S]).transpose())
2254
+ Phitu = Phitu[0][0]
2255
+
2256
+ # Compute all partial derivatives of At and Phitu up to orders 2 * N - 2
2257
+ # and 2 * N, respectively. Take advantage of the fact that At and Phitu
2258
+ # are sufficiently differentiable functions so that mixed partials
2259
+ # are equal. Thus only need to compute representative partials.
2260
+ # Choose nondecreasing sequences as representative differentiation-
2261
+ # order sequences.
2262
+ # To speed up later computations, create symbolic functions AA and BB
2263
+ # to stand in for the expressions At and Phitu respectively.
2264
+ if verbose:
2265
+ print("Computing derivatives of more auxiliary functions...")
2266
+ AA = [function('A' + str(j))(*tuple(T + S)) for j in range(n)]
2267
+ At_derivs = diff_all(At, T + S, 2 * N - 2, sub=hderivs1,
2268
+ sub_final=[thetastar, atP], rekey=AA)
2269
+ BB = function('BB')(*tuple(T + S))
2270
+ Phitu_derivs = diff_all(Phitu, T + S, 2 * N, sub=hderivs1,
2271
+ sub_final=[thetastar, atP], rekey=BB, zero_order=3)
2272
+ AABB_derivs = At_derivs
2273
+ AABB_derivs.update(Phitu_derivs)
2274
+ for j in range(n):
2275
+ AABB_derivs[AA[j]] = At[j].subs(thetastar).subs(atP)
2276
+ AABB_derivs[BB] = Phitu.subs(thetastar).subs(atP)
2277
+
2278
+ if verbose:
2279
+ print("Computing second-order differential operator actions...")
2280
+ DD = diff_op(AA, BB, AABB_derivs, T + S, a_inv, n, N)
2281
+ L = {}
2282
+ for (j, k) in product(range(min(n, N)), range(max(0, N - 1 - n), N)):
2283
+ if j + k <= N - 1:
2284
+ L[(j, k)] = sum([DD[(j, k, l)] / ((-1) ** k * 2 ** (k + l) *
2285
+ factorial(l) *
2286
+ factorial(k + l))
2287
+ for l in range(2 * k + 1)])
2288
+ det = (a.determinant() ** (-1 / Integer(2)) *
2289
+ (2 * pi) ** ((n - d) / Integer(2)))
2290
+ chunk = det * sum([(alpha[d - 1] * asy_var) ** ((n - d) /
2291
+ Integer(2) - q) *
2292
+ sum([L[(j, k)] * binomial(n - 1, j) *
2293
+ stirling_number1(n - j, n + k - q) *
2294
+ (-1) ** (q - j - k)
2295
+ for (j, k) in product(range(min(n - 1, q) + 1),
2296
+ range(max(0, q - n),
2297
+ q + 1))
2298
+ if j + k <= q])
2299
+ for q in range(N)])
2300
+ chunk = chunk.subs(P).simplify()
2301
+ coeffs = chunk.coefficients(asy_var)
2302
+ coeffs.reverse()
2303
+ coeffs = coeffs[:N]
2304
+ if numerical:
2305
+ subexp_part = sum([co[0].subs(p).n(digits=numerical) *
2306
+ asy_var ** co[1]
2307
+ for co in coeffs])
2308
+ exp_scale = prod([(P[X[i]] ** (-alpha[i])).subs(p)
2309
+ for i in range(d)]).n(digits=numerical)
2310
+ else:
2311
+ subexp_part = sum([co[0].subs(p) * asy_var ** co[1]
2312
+ for co in coeffs])
2313
+ exp_scale = prod([(P[X[i]] ** (-alpha[i])).subs(p)
2314
+ for i in range(d)])
2315
+ return (exp_scale ** asy_var * subexp_part, exp_scale, subexp_part)
2316
+
2317
+ def _crit_cone_combo(self, p, alpha, coordinate=None):
2318
+ r"""
2319
+ Return an auxiliary point associated to the multiple
2320
+ point ``p`` of the factors ``self``.
2321
+
2322
+ INPUT:
2323
+
2324
+ - ``p`` -- dictionary with keys that can be coerced to equal
2325
+ ``self.denominator_ring.gens()``
2326
+ - ``alpha`` -- list of rationals
2327
+
2328
+ OUTPUT:
2329
+
2330
+ A solution of the matrix equation `y \Gamma = \alpha^{\prime}` for `y`,
2331
+ where `\Gamma` is the matrix given by
2332
+ ``[direction(v) for v in self.log_grads(p)]`` and
2333
+ `\alpha^{\prime}` is ``direction(alpha)``.
2334
+
2335
+ .. SEEALSO::
2336
+
2337
+ :func:`direction`
2338
+
2339
+ .. NOTE::
2340
+
2341
+ For internal use by
2342
+ :meth:`FractionWithFactoredDenominator.asymptotics_multiple()`.
2343
+
2344
+ .. NOTE::
2345
+
2346
+ Use this function only when `\Gamma` is well-defined and
2347
+ there is a unique solution to the matrix equation
2348
+ `y \Gamma = \alpha'`. Fails otherwise.
2349
+
2350
+ EXAMPLES::
2351
+
2352
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2353
+ sage: R.<x,y> = PolynomialRing(QQ)
2354
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
2355
+ sage: p = exp(x)
2356
+ sage: df = [(1 - 2*x - y, 1), (1 - x - 2*y, 1)]
2357
+ sage: f = FFPD(p, df)
2358
+ sage: p = {x: 1/3, y: 1/3}
2359
+ sage: alpha = (var('a'), var('b'))
2360
+ sage: f._crit_cone_combo(p, alpha)
2361
+ [1/3*(2*a - b)/b, -2/3*(a - 2*b)/b]
2362
+ """
2363
+ from sage.matrix.constructor import matrix
2364
+ from sage.symbolic.relation import solve
2365
+
2366
+ # Assuming here that each log_grads(f) has nonzero final component.
2367
+ # Then 'direction' will not throw a division by zero error.
2368
+ R = self.denominator_ring
2369
+
2370
+ # Coerce keys of p into R.
2371
+ p = coerce_point(R, p)
2372
+
2373
+ d = self.dimension()
2374
+ n = len(self.denominator_factored())
2375
+ Gamma = matrix([direction(v, coordinate) for v in self.log_grads(p)])
2376
+ beta = direction(alpha, coordinate)
2377
+ # solve_left() fails when working in SR :-(.
2378
+ # So use solve() instead.
2379
+ # Gamma.solve_left(vector(beta))
2380
+ V = [var('sss' + str(i)) for i in range(n)]
2381
+ M = matrix(V) * Gamma
2382
+ eqns = [M[0][j] == beta[j] for j in range(d)]
2383
+ s = solve(eqns, V, solution_dict=True)[0] # Assume a unique solution.
2384
+ return [s[v] for v in V]
2385
+
2386
+ def grads(self, p):
2387
+ r"""
2388
+ Return a list of the gradients of the polynomials
2389
+ ``[q for (q, e) in self.denominator_factored()]`` evaluated at ``p``.
2390
+
2391
+ INPUT:
2392
+
2393
+ - ``p`` -- (default: ``None``) a dictionary whose keys are
2394
+ the generators of ``self.denominator_ring``
2395
+
2396
+ OUTPUT: list
2397
+
2398
+ EXAMPLES::
2399
+
2400
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2401
+ sage: R.<x,y> = PolynomialRing(QQ)
2402
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
2403
+ sage: p = exp(x)
2404
+ sage: df = [(x^3 + 3*y^2, 5), (x*y, 2), (y, 1)]
2405
+ sage: f = FFPD(p, df)
2406
+ sage: f
2407
+ (e^x, [(y, 1), (x*y, 2), (x^3 + 3*y^2, 5)])
2408
+ sage: R.gens()
2409
+ (x, y)
2410
+ sage: p = None
2411
+ sage: f.grads(p)
2412
+ [(0, 1), (y, x), (3*x^2, 6*y)]
2413
+
2414
+ sage: p = {x: sqrt(2), y: var('a')}
2415
+ sage: f.grads(p)
2416
+ [(0, 1), (a, sqrt(2)), (6, 6*a)]
2417
+ """
2418
+ R = self.denominator_ring
2419
+
2420
+ # Coerce keys of p into R.
2421
+ p = coerce_point(R, p)
2422
+
2423
+ X = R.gens()
2424
+ d = self.dimension()
2425
+ H = [h for (h, e) in self.denominator_factored()]
2426
+ n = len(H)
2427
+ return [tuple([diff(H[i], X[j]).subs(p) for j in range(d)])
2428
+ for i in range(n)]
2429
+
2430
+ def log_grads(self, p):
2431
+ r"""
2432
+ Return a list of the logarithmic gradients of the polynomials
2433
+ ``[q for (q, e) in self.denominator_factored()]`` evaluated at ``p``.
2434
+
2435
+ The logarithmic gradient of a function `f` at point `p` is the
2436
+ vector `(x_1 \partial_1 f(x), \ldots, x_d \partial_d f(x) )`
2437
+ evaluated at `p`.
2438
+
2439
+ INPUT:
2440
+
2441
+ - ``p`` -- (default: ``None``) a dictionary whose keys
2442
+ are the generators of ``self.denominator_ring``
2443
+
2444
+ OUTPUT: list
2445
+
2446
+ EXAMPLES::
2447
+
2448
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2449
+ sage: R.<x,y> = PolynomialRing(QQ)
2450
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
2451
+ sage: p = exp(x)
2452
+ sage: df = [(x^3 + 3*y^2, 5), (x*y, 2), (y, 1)]
2453
+ sage: f = FFPD(p, df)
2454
+ sage: f
2455
+ (e^x, [(y, 1), (x*y, 2), (x^3 + 3*y^2, 5)])
2456
+ sage: R.gens()
2457
+ (x, y)
2458
+ sage: p = None
2459
+ sage: f.log_grads(p)
2460
+ [(0, y), (x*y, x*y), (3*x^3, 6*y^2)]
2461
+
2462
+ sage: p = {x: sqrt(2), y: var('a')}
2463
+ sage: f.log_grads(p)
2464
+ [(0, a), (sqrt(2)*a, sqrt(2)*a), (6*sqrt(2), 6*a^2)]
2465
+ """
2466
+ R = self.denominator_ring
2467
+
2468
+ # Coerce keys of p into R.
2469
+ p = coerce_point(R, p)
2470
+
2471
+ X = R.gens()
2472
+ d = self.dimension()
2473
+ H = [h for (h, e) in self.denominator_factored()]
2474
+ n = len(H)
2475
+ return [tuple([(X[j] * diff(H[i], X[j])).subs(p) for j in range(d)])
2476
+ for i in range(n)]
2477
+
2478
+ def critical_cone(self, p, coordinate=None):
2479
+ r"""
2480
+ Return the critical cone of the convenient multiple point ``p``.
2481
+
2482
+ INPUT:
2483
+
2484
+ - ``p`` -- dictionary with keys that can be coerced to equal
2485
+ ``self.denominator_ring.gens()`` and values in a field
2486
+ - ``coordinate`` -- (default: ``None``) a natural number
2487
+
2488
+ OUTPUT: list of vectors
2489
+
2490
+ This list of vectors generate the critical cone of ``p`` and
2491
+ the cone itself, which is ``None`` if the values of ``p`` don't lie in
2492
+ `\QQ`. Divide logarithmic gradients by their component ``coordinate``
2493
+ entries. If ``coordinate = None``, then search from `d-1` down to 0
2494
+ for the first index ``j`` such that for all ``i`` we have
2495
+ ``self.log_grads()[i][j] != 0`` and set ``coordinate = j``.
2496
+
2497
+ EXAMPLES::
2498
+
2499
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2500
+ sage: R.<x,y,z> = PolynomialRing(QQ)
2501
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2502
+ sage: G = 1
2503
+ sage: H = (1 - x*(1 + y)) * (1 - z*x**2*(1 + 2*y))
2504
+ sage: Hfac = H.factor()
2505
+ sage: G = 1/Hfac.unit()
2506
+ sage: F = FFPD(G, Hfac)
2507
+ sage: p = {x: 1/2, y: 1, z: 4/3}
2508
+ sage: F.critical_cone(p)
2509
+ ([(2, 1, 0), (3, 1, 3/2)], 2-d cone in 3-d lattice N)
2510
+ """
2511
+ from sage.geometry.cone import Cone
2512
+
2513
+ R = self.denominator_ring
2514
+
2515
+ # Coerce keys of p into R.
2516
+ p = coerce_point(R, p)
2517
+
2518
+ d = self.dimension()
2519
+ lg = self.log_grads(p)
2520
+ n = len(lg)
2521
+ if coordinate not in range(d):
2522
+ # Search from d-1 down to 0 for a coordinate j such that
2523
+ # for all i we have lg[i][j] != 0.
2524
+ # One is guaranteed to exist in the case of a convenient multiple
2525
+ # point.
2526
+ for j in reversed(range(d)):
2527
+ if 0 not in [lg[i][j] for i in range(n)]:
2528
+ coordinate = j
2529
+ break
2530
+ Gamma = [direction(v, coordinate) for v in lg]
2531
+ try:
2532
+ cone = Cone(Gamma)
2533
+ except TypeError:
2534
+ cone = None
2535
+ return (Gamma, cone)
2536
+
2537
+ def is_convenient_multiple_point(self, p):
2538
+ r"""
2539
+ Test if ``p`` is a convenient multiple point of ``self``.
2540
+
2541
+ In case ``p`` is a convenient multiple point, ``verdict = True`` and
2542
+ ``comment`` is a string stating which variables it's convenient to use.
2543
+ In case ``p`` is not, ``verdict = False`` and ``comment`` is a string
2544
+ explaining why ``p`` fails to be a convenient multiple point.
2545
+
2546
+ See [RW2012]_ for more details.
2547
+
2548
+ INPUT:
2549
+
2550
+ - ``p`` -- dictionary with keys that can be coerced to equal
2551
+ ``self.denominator_ring.gens()``
2552
+
2553
+ OUTPUT:
2554
+
2555
+ A pair ``(verdict, comment)``.
2556
+
2557
+ EXAMPLES::
2558
+
2559
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2560
+ sage: R.<x,y,z> = PolynomialRing(QQ)
2561
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2562
+ sage: H = (1 - x*(1 + y)) * (1 - z*x**2*(1 + 2*y))
2563
+ sage: df = H.factor()
2564
+ sage: G = 1 / df.unit()
2565
+ sage: F = FFPD(G, df)
2566
+ sage: p1 = {x: 1/2, y: 1, z: 4/3}
2567
+ sage: p2 = {x: 1, y: 2, z: 1/2}
2568
+ sage: F.is_convenient_multiple_point(p1)
2569
+ (True, 'convenient in variables [x, y]')
2570
+ sage: F.is_convenient_multiple_point(p2)
2571
+ (False, 'not a singular point')
2572
+ """
2573
+ from sage.combinat.subset import Subsets
2574
+ from sage.matrix.constructor import matrix
2575
+
2576
+ R = self.denominator_ring
2577
+
2578
+ # Coerce keys of p into R.
2579
+ p = coerce_point(R, p)
2580
+
2581
+ H = [h for (h, e) in self.denominator_factored()]
2582
+ n = len(H)
2583
+ d = self.dimension()
2584
+
2585
+ # Test 1: Are the factors in H zero at p?
2586
+ if [h.subs(p) for h in H] != [0 for h in H]:
2587
+ # Failed test 1. Move on to next point.
2588
+ return (False, 'not a singular point')
2589
+
2590
+ # Test 2: Are the factors in H smooth at p?
2591
+ grads = self.grads(p)
2592
+ for v in grads:
2593
+ if v == [0 for i in range(d)]:
2594
+ return (False, 'not smooth point of factors')
2595
+
2596
+ # Test 3: Do the factors in H intersect transversely at p?
2597
+ if n <= d:
2598
+ M = matrix(grads)
2599
+ if M.rank() != n:
2600
+ return (False, 'not a transverse intersection')
2601
+ else:
2602
+ # Check all sub-multisets of grads of size d.
2603
+ for S in Subsets(grads, d, submultiset=True):
2604
+ M = matrix(S)
2605
+ if M.rank() != d:
2606
+ return (False, 'not a transverse intersection')
2607
+
2608
+ # Test 4: Is p convenient?
2609
+ M = matrix(self.log_grads(p))
2610
+ cols = M.columns()
2611
+ convenient_coordinates = [j for j, c in enumerate(cols) if 0 not in c]
2612
+ if not convenient_coordinates:
2613
+ return (False, 'multiple point but not convenient')
2614
+
2615
+ # Tests all passed
2616
+ X = R.gens()
2617
+ convenientX = [X[i] for i in convenient_coordinates]
2618
+ return (True, 'convenient in variables {}'.format(convenientX))
2619
+
2620
+ def singular_ideal(self):
2621
+ r"""
2622
+ Return the singular ideal of ``self``.
2623
+
2624
+ Let `R` be the ring of ``self`` and `H` its denominator.
2625
+ Let `H_{red}` be the reduction (square-free part) of `H`.
2626
+ Return the ideal in `R` generated by `H_{red}` and
2627
+ its partial derivatives.
2628
+ If the coefficient field of `R` is algebraically closed,
2629
+ then the output is the ideal of the singular locus (which
2630
+ is a variety) of the variety of `H`.
2631
+
2632
+ OUTPUT: an ideal
2633
+
2634
+ EXAMPLES::
2635
+
2636
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2637
+ sage: R.<x,y,z> = PolynomialRing(QQ)
2638
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2639
+ sage: H = (1 - x*(1 + y))^3 * (1 - z*x**2*(1 + 2*y))
2640
+ sage: df = H.factor()
2641
+ sage: G = 1 / df.unit()
2642
+ sage: F = FFPD(G, df)
2643
+ sage: F.singular_ideal()
2644
+ Ideal (x*y + x - 1, y^2 - 2*y*z + 2*y - z + 1, x*z + y - 2*z + 1) of
2645
+ Multivariate Polynomial Ring in x, y, z over Rational Field
2646
+ """
2647
+ R = self.denominator_ring
2648
+
2649
+ Hred = prod([h for (h, e) in self.denominator_factored()])
2650
+ J = R.ideal([Hred] + Hred.gradient())
2651
+ return R.ideal(J.groebner_basis())
2652
+
2653
+ def smooth_critical_ideal(self, alpha):
2654
+ r"""
2655
+ Return the smooth critical ideal of ``self``.
2656
+
2657
+ Let `R` be the ring of ``self`` and `H` its denominator.
2658
+ Return the ideal in `R` of smooth critical points of the variety
2659
+ of `H` for the direction ``alpha``.
2660
+ If the variety `V` of `H` has no smooth points, then return the ideal
2661
+ in `R` of `V`.
2662
+
2663
+ See [RW2012]_ for more details.
2664
+
2665
+ INPUT:
2666
+
2667
+ - ``alpha`` -- tuple of positive integers and/or symbolic entries
2668
+ of length ``self.denominator_ring.ngens()``
2669
+
2670
+ OUTPUT: an ideal
2671
+
2672
+ EXAMPLES::
2673
+
2674
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2675
+ sage: R.<x,y> = PolynomialRing(QQ)
2676
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2677
+ sage: H = (1 - x - y - x*y)^2
2678
+ sage: Hfac = H.factor()
2679
+ sage: G = 1/Hfac.unit()
2680
+ sage: F = FFPD(G, Hfac)
2681
+ sage: alpha = var('a1, a2')
2682
+ sage: F.smooth_critical_ideal(alpha)
2683
+ Ideal (y^2 + (2*a1)/a2*y - 1, x + (-a2)/a1*y + (-a1 + a2)/a1) of
2684
+ Multivariate Polynomial Ring in x, y over Fraction Field of
2685
+ Multivariate Polynomial Ring in a1, a2 over Rational Field
2686
+
2687
+ sage: H = (1-x-y-x*y)^2
2688
+ sage: Hfac = H.factor()
2689
+ sage: G = 1/Hfac.unit()
2690
+ sage: F = FFPD(G, Hfac)
2691
+ sage: alpha = [7/3, var('a')]
2692
+ sage: F.smooth_critical_ideal(alpha)
2693
+ Ideal (y^2 + 14/(3*a)*y - 1, x + (-3*a)/7*y + (3*a - 7)/7) of Multivariate Polynomial Ring in x, y over Fraction Field of Univariate Polynomial Ring in a over Rational Field
2694
+ """
2695
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
2696
+
2697
+ R = self.denominator_ring
2698
+ Hred = prod([h for (h, e) in self.denominator_factored()])
2699
+ K = R.base_ring()
2700
+ d = self.dimension()
2701
+
2702
+ # Expand K by the variables of alpha if there are any.
2703
+ indets = [a for a in alpha if a not in K and a in SR]
2704
+
2705
+ indets = sorted(set(indets), key=str) # Delete duplicates in indets.
2706
+ if indets:
2707
+ L = PolynomialRing(K, indets).fraction_field()
2708
+ S = R.change_ring(L)
2709
+ # Coerce alpha into L.
2710
+ alpha = [L(a) for a in alpha]
2711
+ else:
2712
+ S = R
2713
+
2714
+ # Find smooth, critical points for alpha.
2715
+ X = S.gens()
2716
+ Hred = S(Hred)
2717
+ J = S.ideal([Hred] +
2718
+ [alpha[d - 1] * X[i] * diff(Hred, X[i]) -
2719
+ alpha[i] * X[d - 1] * diff(Hred, X[d - 1])
2720
+ for i in range(d - 1)])
2721
+ return S.ideal(J.groebner_basis())
2722
+
2723
+ def maclaurin_coefficients(self, multi_indices, numerical=0):
2724
+ r"""
2725
+ Return the Maclaurin coefficients of ``self`` with given
2726
+ ``multi_indices``.
2727
+
2728
+ INPUT:
2729
+
2730
+ - ``multi_indices`` -- list of tuples of positive integers, where
2731
+ each tuple has length ``self.dimension()``
2732
+ - ``numerical`` -- (default: 0) a natural number; if positive, return
2733
+ numerical approximations of coefficients with ``numerical`` digits of
2734
+ accuracy
2735
+
2736
+ OUTPUT:
2737
+
2738
+ A dictionary whose value of the key ``nu`` are the Maclaurin
2739
+ coefficient of index ``nu`` of ``self``.
2740
+
2741
+ .. NOTE::
2742
+
2743
+ Uses iterated univariate Maclaurin expansions. Slow.
2744
+
2745
+ EXAMPLES::
2746
+
2747
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2748
+ sage: R.<x> = PolynomialRing(QQ)
2749
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2750
+ sage: H = 2 - 3*x
2751
+ sage: Hfac = H.factor()
2752
+ sage: G = 1 / Hfac.unit()
2753
+ sage: F = FFPD(G, Hfac)
2754
+ sage: F
2755
+ (-1/3, [(x - 2/3, 1)])
2756
+ sage: F.maclaurin_coefficients([(2*k,) for k in range(6)])
2757
+ {(0,): 1/2,
2758
+ (2,): 9/8,
2759
+ (4,): 81/32,
2760
+ (6,): 729/128,
2761
+ (8,): 6561/512,
2762
+ (10,): 59049/2048}
2763
+
2764
+ ::
2765
+
2766
+ sage: R.<x,y,z> = PolynomialRing(QQ)
2767
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2768
+ sage: H = (4 - 2*x - y - z) * (4 - x - 2*y - z)
2769
+ sage: Hfac = H.factor()
2770
+ sage: G = 16 / Hfac.unit()
2771
+ sage: F = FFPD(G, Hfac)
2772
+ sage: alpha = vector([3, 3, 2])
2773
+ sage: interval = [1, 2, 4]
2774
+ sage: S = [r*alpha for r in interval]
2775
+ sage: F.maclaurin_coefficients(S, numerical=10) # long time
2776
+ {(3, 3, 2): 0.7849731445,
2777
+ (6, 6, 4): 0.7005249476,
2778
+ (12, 12, 8): 0.5847732654}
2779
+ """
2780
+ R = self.denominator_ring
2781
+ d = self.dimension()
2782
+ coeffs = {}
2783
+
2784
+ # Deal with the simple univariate case first.
2785
+ if d == 1:
2786
+ f = SR(self.quotient())
2787
+ x = SR(R.gens()[0])
2788
+ m = max(multi_indices)[0]
2789
+ f = f.taylor(x, 0, m)
2790
+ F = R(f)
2791
+ tmp = F.coefficients()
2792
+ for nu in multi_indices:
2793
+ val = tmp[nu[0]]
2794
+ if numerical:
2795
+ val = val.n(digits=numerical)
2796
+ coeffs[tuple(nu)] = val
2797
+ return coeffs
2798
+
2799
+ # Create biggest multi-index needed.
2800
+ alpha = [max(nu[i] for nu in multi_indices) for i in range(d)]
2801
+
2802
+ # Compute Maclaurin expansion of self up to index alpha.
2803
+ # Use iterated univariate expansions.
2804
+ # Slow!
2805
+ f = SR(self.quotient())
2806
+ X = [SR(g) for g in R.gens()]
2807
+ for i in range(d):
2808
+ f = f.taylor(X[i], 0, alpha[i])
2809
+ F = R(f)
2810
+
2811
+ # Collect coefficients.
2812
+ X = R.gens()
2813
+ for nu in multi_indices:
2814
+ monomial = prod(X[i] ** nu[i] for i in range(d))
2815
+ val = F.monomial_coefficient(monomial)
2816
+ if numerical:
2817
+ val = val.n(digits=numerical)
2818
+ coeffs[tuple(nu)] = val
2819
+ return coeffs
2820
+
2821
+ def relative_error(self, approx, alpha, interval, exp_scale=Integer(1),
2822
+ digits=10):
2823
+ r"""
2824
+ Return the relative error between the values of the Maclaurin
2825
+ coefficients of ``self`` with multi-indices ``r alpha`` for ``r`` in
2826
+ ``interval`` and the values of the functions (of the variable ``r``)
2827
+ in ``approx``.
2828
+
2829
+ INPUT:
2830
+
2831
+ - ``approx`` -- an individual or list of symbolic expressions in
2832
+ one variable
2833
+ - ``alpha`` -- list of positive integers of length
2834
+ ``self.denominator_ring.ngens()``
2835
+ - ``interval`` -- list of positive integers
2836
+ - ``exp_scale`` -- (default: 1) a number
2837
+
2838
+ OUTPUT: list of tuples with properties described below
2839
+
2840
+ This outputs a list whose entries are a tuple
2841
+ ``(r*alpha, a_r, b_r, err_r)`` for ``r`` in ``interval``.
2842
+ Here ``r*alpha`` is a tuple; ``a_r`` is the ``r*alpha`` (multi-index)
2843
+ coefficient of the Maclaurin series for ``self`` divided by
2844
+ ``exp_scale**r``;
2845
+ ``b_r`` is a list of the values of the functions in ``approx``
2846
+ evaluated at ``r`` and divided by ``exp_scale**m``;
2847
+ ``err_r`` is the list of relative errors
2848
+ ``(a_r - f)/a_r`` for ``f`` in ``b_r``.
2849
+ All outputs are decimal approximations.
2850
+
2851
+ EXAMPLES::
2852
+
2853
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2854
+ sage: R.<x,y> = PolynomialRing(QQ)
2855
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2856
+ sage: H = 1 - x - y - x*y
2857
+ sage: Hfac = H.factor()
2858
+ sage: G = 1 / Hfac.unit()
2859
+ sage: F = FFPD(G, Hfac)
2860
+ sage: alpha = [1, 1]
2861
+ sage: r = var('r')
2862
+ sage: a1 = (0.573/sqrt(r))*5.83^r
2863
+ sage: a2 = (0.573/sqrt(r) - 0.0674/r^(3/2))*5.83^r
2864
+ sage: es = 5.83
2865
+ sage: F.relative_error([a1, a2], alpha, [1, 2, 4, 8], es) # long time
2866
+ [((1, 1), 0.5145797599,
2867
+ [0.5730000000, 0.5056000000], [-0.1135300000, 0.01745066667]),
2868
+ ((2, 2), 0.3824778089,
2869
+ [0.4051721856, 0.3813426871], [-0.05933514614, 0.002967810973]),
2870
+ ((4, 4), 0.2778630595,
2871
+ [0.2865000000, 0.2780750000], [-0.03108344267, -0.0007627515584]),
2872
+ ((8, 8), 0.1991088276,
2873
+ [0.2025860928, 0.1996074055], [-0.01746414394, -0.002504047242])]
2874
+ """
2875
+ from sage.modules.free_module_element import vector
2876
+
2877
+ if not isinstance(approx, (list, tuple)):
2878
+ approx = [approx]
2879
+ if approx[0].variables():
2880
+ av = approx[0].variables()[0]
2881
+ else:
2882
+ av = ZZ.one()
2883
+
2884
+ # Get Maclaurin coefficients of self.
2885
+ alpha = vector(alpha)
2886
+ multi_indices = [r * alpha for r in interval]
2887
+ mac = self.maclaurin_coefficients(multi_indices, numerical=digits)
2888
+ # mac = self.old_maclaurin_coefficients(alpha, max(interval))
2889
+ mac_approx = {}
2890
+ stats = []
2891
+ for r in interval:
2892
+ exp_s_r = exp_scale ** r
2893
+ beta = tuple(r * alpha)
2894
+ mac[beta] = (mac[beta] / exp_s_r).n(digits=digits)
2895
+ mac_approx[beta] = [(f.subs({av: r}) / exp_s_r).n(digits=digits)
2896
+ for f in approx]
2897
+ stats_row = [beta, mac[beta], mac_approx[beta]]
2898
+ if mac[beta] == 0:
2899
+ stats_row.extend([None for a in mac_approx[beta]])
2900
+ else:
2901
+ stats_row.append([(mac[beta] - a) / mac[beta]
2902
+ for a in mac_approx[beta]])
2903
+ stats.append(tuple(stats_row))
2904
+ return stats
2905
+
2906
+ def _add_(left, right):
2907
+ r"""
2908
+ Return the sum of ``left`` with ``right``.
2909
+
2910
+ INPUT:
2911
+
2912
+ - ``left`` -- the left summand (i.e. ``self``)
2913
+
2914
+ - ``right`` -- the right summand
2915
+
2916
+ OUTPUT: the sum as a new element
2917
+
2918
+ EXAMPLES::
2919
+
2920
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2921
+ sage: R.<x,y> = PolynomialRing(QQ)
2922
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2923
+ sage: df = (x, 1), (y, 1), (x*y + 1, 1)
2924
+ sage: f = FFPD(2, df)
2925
+ sage: g = FFPD(2*x*y, df)
2926
+ sage: f + g
2927
+ (2, [(y, 1), (x, 1)])
2928
+ """
2929
+ return FractionWithFactoredDenominatorSum([left, right]).sum()
2930
+
2931
+ def _mul_(left, right):
2932
+ r"""
2933
+ Return the product of ``left`` with ``right``.
2934
+
2935
+ INPUT:
2936
+
2937
+ - ``left`` -- the left factor (i.e. ``self``)
2938
+
2939
+ - ``right`` -- the right factor
2940
+
2941
+ OUTPUT: the product as a new element
2942
+
2943
+ EXAMPLES::
2944
+
2945
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2946
+ sage: R.<x,y> = PolynomialRing(QQ)
2947
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2948
+ sage: f = FFPD(2, [(x, 1), (x*y + 1, 1), (x*y^2 + 1, 1)])
2949
+ sage: g = FFPD(2*x*y, [(y, 1), (x*y + 1, 1), (x^2*y + 1, 1)])
2950
+ sage: f * g
2951
+ (4, [(x*y + 1, 1), (x*y + 1, 1), (x*y^2 + 1, 1), (x^2*y + 1, 1)])
2952
+ """
2953
+ numer = left.numerator() * right.numerator()
2954
+ df = left.denominator_factored() + right.denominator_factored()
2955
+ return left.parent()(numer, df)
2956
+
2957
+
2958
+ class FractionWithFactoredDenominatorRing(UniqueRepresentation, Parent):
2959
+ r"""
2960
+ This is the ring of fractions with factored denominator.
2961
+
2962
+ INPUT:
2963
+
2964
+ - ``denominator_ring`` -- the base ring (a polynomial ring)
2965
+
2966
+ - ``numerator_ring`` -- (optional) the numerator ring; the default is
2967
+ the ``denominator_ring``
2968
+
2969
+ - ``category`` -- (default: :class:`Rings`) the category
2970
+
2971
+ .. SEEALSO::
2972
+
2973
+ :class:`FractionWithFactoredDenominator`,
2974
+ :mod:`~sage.rings.asymptotic.asymptotics_multivariate_generating_functions`
2975
+
2976
+ EXAMPLES::
2977
+
2978
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2979
+ sage: R.<x,y> = PolynomialRing(QQ)
2980
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
2981
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
2982
+ sage: f = FFPD(x, df) # indirect doctest
2983
+ sage: f
2984
+ (1, [(y, 1), (x*y + 1, 1)])
2985
+
2986
+ AUTHORS:
2987
+
2988
+ - Daniel Krenn (2014-12-01)
2989
+ """
2990
+
2991
+ @staticmethod
2992
+ def __classcall_private__(cls, denominator_ring, numerator_ring=None, category=None):
2993
+ """
2994
+ Normalize input to ensure a unique representation.
2995
+
2996
+ EXAMPLES::
2997
+
2998
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
2999
+ sage: R.<x,y> = PolynomialRing(QQ)
3000
+ sage: FFPD1 = FractionWithFactoredDenominatorRing(R)
3001
+ sage: cat = Rings().Commutative()
3002
+ sage: FFPD2 = FractionWithFactoredDenominatorRing(R, R, cat)
3003
+ sage: FFPD1 is FFPD2
3004
+ True
3005
+ """
3006
+ if numerator_ring is None:
3007
+ numerator_ring = denominator_ring
3008
+ if not numerator_ring.has_coerce_map_from(denominator_ring):
3009
+ raise ValueError('numerator ring {} has no coercion map from the '
3010
+ 'denominator ring {}'.format(
3011
+ numerator_ring, denominator_ring))
3012
+ category = Rings().Commutative().or_subcategory(category)
3013
+ return super().__classcall__(cls, denominator_ring,
3014
+ numerator_ring, category)
3015
+
3016
+ def __init__(self, denominator_ring, numerator_ring=None, category=None):
3017
+ r"""
3018
+ Initialize ``self``.
3019
+
3020
+ TESTS::
3021
+
3022
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3023
+ sage: P.<X, Y> = ZZ[]
3024
+ sage: FractionWithFactoredDenominatorRing(P)
3025
+ Ring of fractions with factored denominator
3026
+ over Multivariate Polynomial Ring in X, Y over Integer Ring
3027
+ """
3028
+ self._numerator_ring = numerator_ring
3029
+ self._denominator_ring = denominator_ring
3030
+ Parent.__init__(self, denominator_ring, category=category)
3031
+
3032
+ def _repr_(self) -> str:
3033
+ r"""
3034
+ Return a representation.
3035
+
3036
+ OUTPUT: string
3037
+
3038
+ TESTS::
3039
+
3040
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3041
+ sage: P.<X, Y> = ZZ[]
3042
+ sage: FractionWithFactoredDenominatorRing(P) # indirect doctest
3043
+ Ring of fractions with factored denominator
3044
+ over Multivariate Polynomial Ring in X, Y over Integer Ring
3045
+ """
3046
+ return ("Ring of fractions with factored denominator "
3047
+ "over {!r}".format(self.base()))
3048
+
3049
+ def base_ring(self):
3050
+ r"""
3051
+ Return the base ring.
3052
+
3053
+ OUTPUT: a ring
3054
+
3055
+ EXAMPLES::
3056
+
3057
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3058
+ sage: P.<X, Y> = ZZ[]
3059
+ sage: F = FractionWithFactoredDenominatorRing(P); F
3060
+ Ring of fractions with factored denominator
3061
+ over Multivariate Polynomial Ring in X, Y over Integer Ring
3062
+ sage: F.base_ring()
3063
+ Integer Ring
3064
+ sage: F.base()
3065
+ Multivariate Polynomial Ring in X, Y over Integer Ring
3066
+ """
3067
+ return self.base().base_ring()
3068
+
3069
+ def _element_constructor_(self, *args, **kwargs):
3070
+ r"""
3071
+ Return an element of this ring.
3072
+
3073
+ See :class:`FractionWithFactoredDenominator` for details.
3074
+
3075
+ TESTS::
3076
+
3077
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3078
+ sage: R.<x,y> = PolynomialRing(QQ)
3079
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3080
+ sage: df = [x, 1], [y, 1], [x*y+1, 1]
3081
+ sage: f = FFPD(x, df) # indirect doctest
3082
+ sage: f
3083
+ (1, [(y, 1), (x*y + 1, 1)])
3084
+ """
3085
+ R = self.base()
3086
+ Q = R.fraction_field()
3087
+
3088
+ # process keyword arguments
3089
+ reduce = kwargs.pop('reduce', None)
3090
+
3091
+ if kwargs:
3092
+ raise ValueError('Unknown keyword arguments '
3093
+ '%s given' % (kwargs,))
3094
+
3095
+ # process arguments
3096
+ if len(args) > 2:
3097
+ raise ValueError('too many arguments given')
3098
+
3099
+ elif not args:
3100
+ raise ValueError('No argument given. '
3101
+ 'We are in serious troubles...')
3102
+
3103
+ # At this point we have one or two input arguments.
3104
+
3105
+ x = args[0]
3106
+ try:
3107
+ P = x.parent()
3108
+ except AttributeError:
3109
+ P = None
3110
+
3111
+ denominator_factored = None # init
3112
+ reduce_default = True
3113
+
3114
+ if len(args) == 2:
3115
+ numerator, denominator_factored = args
3116
+ if numerator is None:
3117
+ numerator = R(0)
3118
+ if denominator_factored is None:
3119
+ denominator_factored = []
3120
+
3121
+ from sage.rings.semirings.non_negative_integer_semiring import NN
3122
+ try:
3123
+ denominator_factored = sorted(
3124
+ (R(d[0]), NN(d[1])) for d in denominator_factored)
3125
+ except TypeError:
3126
+ raise TypeError('factored denominator is not well-formed '
3127
+ 'or of wrong type')
3128
+
3129
+ # From now on we only have one input argument;
3130
+ # it's called x and has parent P.
3131
+
3132
+ elif isinstance(P, FractionWithFactoredDenominatorRing):
3133
+ numerator = x._numerator
3134
+ denominator_factored = self._denominator_factored
3135
+ reduce_default = False
3136
+
3137
+ elif P == SR:
3138
+ numerator = x.numerator()
3139
+ denominator = x.denominator()
3140
+ reduce_default = False
3141
+
3142
+ elif x in R:
3143
+ numerator = R(x)
3144
+ denominator_factored = []
3145
+
3146
+ elif x in Q:
3147
+ quotient = Q(x)
3148
+ numerator = quotient.numerator()
3149
+ denominator = quotient.denominator()
3150
+ reduce_default = False
3151
+
3152
+ elif hasattr(x, 'numerator') and hasattr(x, 'denominator'):
3153
+ numerator = x.numerator()
3154
+ denominator = x.denominator()
3155
+ reduce_default = False
3156
+
3157
+ else:
3158
+ raise TypeError('element {} is not contained in {}'.format(x, self))
3159
+
3160
+ if reduce is None:
3161
+ reduce = reduce_default
3162
+
3163
+ if denominator_factored is None:
3164
+ if denominator not in R:
3165
+ raise TypeError('extracted denominator {} is not in {}'.format(denominator, self))
3166
+ p = numerator
3167
+ q = R(denominator)
3168
+
3169
+ from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic
3170
+ from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base
3171
+ if isinstance(R, (PolynomialRing_generic, MPolynomialRing_base)):
3172
+ if not R(q).is_unit():
3173
+ # Factor denominator
3174
+ try:
3175
+ df = q.factor()
3176
+ except NotImplementedError:
3177
+ # Singular's factor() needs 'proof=False'.
3178
+ df = q.factor(proof=False)
3179
+ numerator = p / df.unit()
3180
+ df = sorted(tuple(t) for t in df) # sort for consistency
3181
+ denominator_factored = df
3182
+ else:
3183
+ # At this point, denominator could not be factored.
3184
+ numerator = p / q
3185
+ denominator_factored = []
3186
+
3187
+ return self.element_class(self,
3188
+ numerator=numerator,
3189
+ denominator_factored=denominator_factored,
3190
+ reduce=reduce)
3191
+
3192
+ def _coerce_map_from_(self, P):
3193
+ r"""
3194
+ Check if there is a coercion from the given parent.
3195
+
3196
+ INPUT:
3197
+
3198
+ - ``P`` -- a parent
3199
+
3200
+ OUTPUT: ``True`` if there is a coercion, otherwise ``False`` or ``None``
3201
+
3202
+ TESTS::
3203
+
3204
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3205
+ sage: Q = QQ['x,y']
3206
+ sage: FFPD_QQ = FractionWithFactoredDenominatorRing(Q)
3207
+ sage: FFPD_QQ.has_coerce_map_from(Q)
3208
+ True
3209
+ sage: FFPD_QQ.has_coerce_map_from(QQ)
3210
+ True
3211
+ sage: FFPD_QQ.has_coerce_map_from(ZZ)
3212
+ True
3213
+ sage: FFPD_QQ.has_coerce_map_from(Q.fraction_field())
3214
+ True
3215
+ sage: Z = ZZ['x,y']
3216
+ sage: FFPD_ZZ = FractionWithFactoredDenominatorRing(Z)
3217
+ sage: FFPD_ZZ.has_coerce_map_from(FFPD_QQ)
3218
+ False
3219
+ sage: FFPD_QQ.has_coerce_map_from(FFPD_ZZ)
3220
+ True
3221
+ sage: FFPD_ZZ.has_coerce_map_from(QQ)
3222
+ False
3223
+ sage: FFPD_ZZ.has_coerce_map_from(Z.fraction_field())
3224
+ True
3225
+ sage: FFPD_ZZ.has_coerce_map_from(Q.fraction_field())
3226
+ False
3227
+ sage: FFPD_QQ.has_coerce_map_from(Z.fraction_field())
3228
+ True
3229
+ """
3230
+ if isinstance(P, FractionWithFactoredDenominatorRing):
3231
+ if self.base().has_coerce_map_from(P.base()):
3232
+ return True
3233
+
3234
+ from sage.rings.fraction_field import FractionField_generic
3235
+ if isinstance(P, FractionField_generic):
3236
+ B = P.base()
3237
+ from sage.rings.polynomial.polynomial_ring import PolynomialRing_generic
3238
+ from sage.rings.polynomial.multi_polynomial_ring_base import MPolynomialRing_base
3239
+ if isinstance(B, (PolynomialRing_generic, MPolynomialRing_base)) and self.base().has_coerce_map_from(B):
3240
+ return True
3241
+
3242
+ if self.base().has_coerce_map_from(P):
3243
+ return True
3244
+
3245
+ def _an_element_(self):
3246
+ r"""
3247
+ Return an element.
3248
+
3249
+ TESTS::
3250
+
3251
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing
3252
+ sage: R.<x,y> = PolynomialRing(QQ)
3253
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3254
+ sage: FFPD.an_element() # indirect doctest
3255
+ (42, [(x, 3)])
3256
+ """
3257
+ from sage.rings.semirings.non_negative_integer_semiring import NN
3258
+ return self(NN.an_element(), [(self.base().an_element(), NN(3))])
3259
+
3260
+ Element = FractionWithFactoredDenominator
3261
+
3262
+
3263
+ class FractionWithFactoredDenominatorSum(list):
3264
+ r"""
3265
+ A list representing the sum of :class:`FractionWithFactoredDenominator`
3266
+ objects with distinct denominator factorizations.
3267
+
3268
+ AUTHORS:
3269
+
3270
+ - Alexander Raichev (2012-06-25)
3271
+
3272
+ - Daniel Krenn (2014-12-01)
3273
+ """
3274
+
3275
+ def __repr__(self) -> str:
3276
+ r"""
3277
+ Return a string representation of ``self``.
3278
+
3279
+ OUTPUT: string
3280
+
3281
+ EXAMPLES::
3282
+
3283
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3284
+ sage: R.<x,y> = PolynomialRing(QQ)
3285
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3286
+ sage: f = FFPD(x + y, [(y, 1), (x, 1)])
3287
+ sage: g = FFPD(x**2 + y, [(y, 1), (x, 2)])
3288
+ sage: FractionWithFactoredDenominatorSum([f, g])
3289
+ (x + y, [(y, 1), (x, 1)]) + (x^2 + y, [(y, 1), (x, 2)])
3290
+ """
3291
+ return ' + '.join(repr(r) for r in self)
3292
+
3293
+ def __eq__(self, other) -> bool:
3294
+ r"""
3295
+ Return ``True`` if ``self`` is equal to ``other``.
3296
+
3297
+ OUTPUT: boolean
3298
+
3299
+ EXAMPLES::
3300
+
3301
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3302
+ sage: R.<x,y> = PolynomialRing(QQ)
3303
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3304
+ sage: f = FFPD(x + y, [(y, 1), (x, 1)])
3305
+ sage: g = FFPD(x*(x + y), [(y, 1), (x, 2)])
3306
+ sage: s = FractionWithFactoredDenominatorSum([f]); s
3307
+ (x + y, [(y, 1), (x, 1)])
3308
+ sage: t = FractionWithFactoredDenominatorSum([g]); t
3309
+ (x + y, [(y, 1), (x, 1)])
3310
+ sage: s == t
3311
+ True
3312
+ """
3313
+ from operator import methodcaller
3314
+ return (sorted(self, key=methodcaller('_total_order_key_')) ==
3315
+ sorted(other, key=methodcaller('_total_order_key_')))
3316
+
3317
+ def __ne__(self, other) -> bool:
3318
+ r"""
3319
+ Return ``True`` if ``self`` is not equal to ``other``.
3320
+
3321
+ OUTPUT: boolean
3322
+
3323
+ EXAMPLES::
3324
+
3325
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3326
+ sage: R.<x,y> = PolynomialRing(QQ)
3327
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3328
+ sage: f = FFPD(x + y, [(y, 1), (x, 1)])
3329
+ sage: g = FFPD(x + y, [(y, 1), (x, 2)])
3330
+ sage: s = FractionWithFactoredDenominatorSum([f]); s
3331
+ (x + y, [(y, 1), (x, 1)])
3332
+ sage: t = FractionWithFactoredDenominatorSum([g]); t
3333
+ (x + y, [(y, 1), (x, 2)])
3334
+ sage: s != t
3335
+ True
3336
+ """
3337
+ return not self.__eq__(other)
3338
+
3339
+ @property
3340
+ def denominator_ring(self):
3341
+ r"""
3342
+ Return the polynomial ring of the denominators of ``self``.
3343
+
3344
+ OUTPUT: a ring or ``None`` if the list is empty
3345
+
3346
+ EXAMPLES::
3347
+
3348
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3349
+ sage: R.<x,y> = PolynomialRing(QQ)
3350
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
3351
+ sage: f = FFPD(x + y, [(y, 1), (x, 1)])
3352
+ sage: s = FractionWithFactoredDenominatorSum([f])
3353
+ sage: s.denominator_ring
3354
+ Multivariate Polynomial Ring in x, y over Rational Field
3355
+ sage: g = FFPD(x + y, [])
3356
+ sage: t = FractionWithFactoredDenominatorSum([g])
3357
+ sage: t.denominator_ring
3358
+ Multivariate Polynomial Ring in x, y over Rational Field
3359
+ """
3360
+ for r in self:
3361
+ return r.denominator_ring
3362
+ return None
3363
+
3364
+ def whole_and_parts(self):
3365
+ r"""
3366
+ Rewrite ``self`` as a sum of a (possibly zero) polynomial
3367
+ followed by reduced rational expressions.
3368
+
3369
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
3370
+
3371
+ Only useful for multivariate decompositions.
3372
+
3373
+ EXAMPLES::
3374
+
3375
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3376
+ sage: R.<x,y> = PolynomialRing(QQ)
3377
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
3378
+ sage: f = x**2 + 3*y + 1/x + 1/y
3379
+ sage: f = FFPD(f); f
3380
+ (x^3*y + 3*x*y^2 + x + y, [(y, 1), (x, 1)])
3381
+ sage: FractionWithFactoredDenominatorSum([f]).whole_and_parts()
3382
+ (x^2 + 3*y, []) + (x + y, [(y, 1), (x, 1)])
3383
+
3384
+ sage: f = cos(x)**2 + 3*y + 1/x + 1/y; f
3385
+ cos(x)^2 + 3*y + 1/x + 1/y
3386
+ sage: G = f.numerator()
3387
+ sage: H = R(f.denominator())
3388
+ sage: f = FFPD(G, H.factor()); f
3389
+ (x*y*cos(x)^2 + 3*x*y^2 + x + y, [(y, 1), (x, 1)])
3390
+ sage: FractionWithFactoredDenominatorSum([f]).whole_and_parts()
3391
+ (0, []) + (x*y*cos(x)^2 + 3*x*y^2 + x + y, [(y, 1), (x, 1)])
3392
+ """
3393
+ whole = 0
3394
+ parts = []
3395
+ R = self.denominator_ring
3396
+ for r in self:
3397
+ # Since r has already passed through FFPD.__init__()'s reducing
3398
+ # procedure, r is already in lowest terms.
3399
+ # Check if can write r as a mixed fraction: whole + fraction.
3400
+ p = r.numerator()
3401
+ q = r.denominator()
3402
+ if q == 1:
3403
+ # r is already whole
3404
+ whole += p
3405
+ else:
3406
+ try:
3407
+ # Coerce p into R and divide p by q
3408
+ p = R(p)
3409
+ a, b = p.quo_rem(q)
3410
+ except TypeError:
3411
+ # p is not in R and so can't divide p by q
3412
+ a = 0
3413
+ b = p
3414
+ whole += a
3415
+ parts.append(r.parent()(b, r.denominator_factored(), reduce=False))
3416
+ return FractionWithFactoredDenominatorSum(
3417
+ [r.parent()(whole, ())] + parts) # r.parent() is not the nicest here
3418
+
3419
+ def _combine_like_terms_(self):
3420
+ r"""
3421
+ Combine terms in ``self`` with the same denominator.
3422
+ Only useful for multivariate decompositions.
3423
+
3424
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominatorSum`
3425
+
3426
+ EXAMPLES::
3427
+
3428
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3429
+ sage: R.<x,y> = PolynomialRing(QQ)
3430
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
3431
+ sage: f = FFPD(1/(x * y * (x*y + 1)))
3432
+ sage: g = FFPD(x/(x * y * (x*y + 1)))
3433
+ sage: s = FractionWithFactoredDenominatorSum([f, g, f])
3434
+ sage: t = s._combine_like_terms_()
3435
+ sage: s
3436
+ (1, [(y, 1), (x, 1), (x*y + 1, 1)]) +
3437
+ (1, [(y, 1), (x*y + 1, 1)]) +
3438
+ (1, [(y, 1), (x, 1), (x*y + 1, 1)])
3439
+ sage: t
3440
+ (1, [(y, 1), (x*y + 1, 1)]) + (2, [(y, 1), (x, 1), (x*y + 1, 1)])
3441
+
3442
+ sage: H = x * y * (x*y + 1)
3443
+ sage: f = FFPD(1, H.factor())
3444
+ sage: g = FFPD(exp(x + y), H.factor())
3445
+ sage: s = FractionWithFactoredDenominatorSum([f, g])
3446
+ sage: s
3447
+ (1, [(y, 1), (x, 1), (x*y + 1, 1)]) +
3448
+ (e^(x + y), [(y, 1), (x, 1), (x*y + 1, 1)])
3449
+ sage: t = s._combine_like_terms_()
3450
+ sage: t
3451
+ (e^(x + y) + 1, [(y, 1), (x, 1), (x*y + 1, 1)])
3452
+ """
3453
+ if not self:
3454
+ return self
3455
+
3456
+ from operator import methodcaller
3457
+ # Combine like terms.
3458
+ FFPDs = sorted(self, key=methodcaller('_total_order_key_'))
3459
+ new_FFPDs = []
3460
+ temp = FFPDs[0]
3461
+ for f in FFPDs[1:]:
3462
+ if temp.denominator_factored() == f.denominator_factored():
3463
+ # Add f to temp.
3464
+ num = temp.numerator() + f.numerator()
3465
+ temp = f.parent()(num, temp.denominator_factored())
3466
+ else:
3467
+ # Append temp to new_FFPDs and update temp.
3468
+ new_FFPDs.append(temp)
3469
+ temp = f
3470
+ new_FFPDs.append(temp)
3471
+ return FractionWithFactoredDenominatorSum(new_FFPDs)
3472
+
3473
+ def sum(self):
3474
+ r"""
3475
+ Return the sum of the elements in ``self``.
3476
+
3477
+ OUTPUT: an instance of :class:`FractionWithFactoredDenominator`
3478
+
3479
+ EXAMPLES::
3480
+
3481
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, FractionWithFactoredDenominatorSum
3482
+ sage: R.<x,y> = PolynomialRing(QQ)
3483
+ sage: FFPD = FractionWithFactoredDenominatorRing(R, SR)
3484
+ sage: df = (x, 1), (y, 1), (x*y + 1, 1)
3485
+ sage: f = FFPD(2, df)
3486
+ sage: g = FFPD(2*x*y, df)
3487
+ sage: FractionWithFactoredDenominatorSum([f, g])
3488
+ (2, [(y, 1), (x, 1), (x*y + 1, 1)]) + (2, [(x*y + 1, 1)])
3489
+ sage: FractionWithFactoredDenominatorSum([f, g]).sum()
3490
+ (2, [(y, 1), (x, 1)])
3491
+
3492
+ sage: f = FFPD(cos(x), [(x, 2)])
3493
+ sage: g = FFPD(cos(y), [(x, 1), (y, 2)])
3494
+ sage: FractionWithFactoredDenominatorSum([f, g])
3495
+ (cos(x), [(x, 2)]) + (cos(y), [(y, 2), (x, 1)])
3496
+ sage: FractionWithFactoredDenominatorSum([f, g]).sum()
3497
+ (y^2*cos(x) + x*cos(y), [(y, 2), (x, 2)])
3498
+ """
3499
+ if not self:
3500
+ return self
3501
+
3502
+ # Compute the sum's numerator and denominator.
3503
+ R = self.denominator_ring
3504
+ summy = sum(f.quotient() for f in self)
3505
+ numer = summy.numerator()
3506
+ denom = R(summy.denominator())
3507
+
3508
+ # Compute the sum's denominator factorization.
3509
+ # Could use the factor() command, but it's probably faster to use
3510
+ # the irreducible factors of the denominators of self.
3511
+ df = [] # The denominator factorization for the sum.
3512
+ if denom == 1:
3513
+ # Done
3514
+ return FractionWithFactoredDenominatorRing(numer.parent())(numer, df, reduce=False)
3515
+
3516
+ factors = []
3517
+ for f in self:
3518
+ factors.extend([q for (q, e) in f.denominator_factored()])
3519
+
3520
+ # Eliminate repeats from factors and sort.
3521
+ factors = sorted(set(factors))
3522
+
3523
+ # The irreducible factors of denom lie in factors.
3524
+ # Use this fact to build df.
3525
+ for q in factors:
3526
+ e = 0
3527
+ quo, rem = denom.quo_rem(q)
3528
+ while rem == 0:
3529
+ e += 1
3530
+ denom = quo
3531
+ quo, rem = denom.quo_rem(q)
3532
+ if e > 0:
3533
+ df.append((q, e))
3534
+ return FractionWithFactoredDenominatorRing(numer.parent())(numer, df, reduce=False)
3535
+
3536
+
3537
+ #####################################################################
3538
+ # Helper functions
3539
+
3540
+
3541
+ def diff_prod(f_derivs, u, g, X, interval, end, uderivs, atc):
3542
+ r"""
3543
+ Take various derivatives of the equation `f = ug`,
3544
+ evaluate them at a point `c`, and solve for the derivatives of `u`.
3545
+
3546
+ INPUT:
3547
+
3548
+ - ``f_derivs`` -- dictionary whose keys are all tuples of the form
3549
+ ``s + end``, where ``s`` is a sequence of variables from ``X`` whose
3550
+ length lies in ``interval``, and whose values are the derivatives
3551
+ of a function `f` evaluated at `c`
3552
+ - ``u`` -- a callable symbolic function
3553
+ - ``g`` -- an expression or callable symbolic function
3554
+ - ``X`` -- list of symbolic variables
3555
+ - ``interval`` -- list of positive integers
3556
+ Call the first and last values `n` and `nn`, respectively
3557
+ - ``end`` -- a possibly empty list of repetitions of the
3558
+ variable ``z``, where ``z`` is the last element of ``X``
3559
+ - ``uderivs`` -- dictionary whose keys are the symbolic
3560
+ derivatives of order 0 to order `n-1` of ``u`` evaluated at `c`
3561
+ and whose values are the corresponding derivatives evaluated at `c`
3562
+ - ``atc`` -- dictionary whose keys are the keys of `c` and all
3563
+ the symbolic derivatives of order 0 to order `nn` of ``g``
3564
+ evaluated `c` and whose values are the corresponding
3565
+ derivatives evaluated at `c`
3566
+
3567
+ OUTPUT:
3568
+
3569
+ A dictionary whose keys are the derivatives of ``u`` up to order
3570
+ `nn` and whose values are those derivatives evaluated at `c`.
3571
+
3572
+ This function works by differentiating the equation `f = ug`
3573
+ with respect to the variable sequence ``s + end``,
3574
+ for all tuples ``s`` of ``X`` of lengths in ``interval``,
3575
+ evaluating at the point `c` ,
3576
+ and solving for the remaining derivatives of ``u``.
3577
+ This function assumes that ``u`` never appears in the
3578
+ differentiations of `f = ug` after evaluating at `c`.
3579
+
3580
+ .. NOTE::
3581
+
3582
+ For internal use by
3583
+ :meth:`FractionWithFactoredDenominator.asymptotics_multiple()`.
3584
+
3585
+ EXAMPLES::
3586
+
3587
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import diff_prod
3588
+ sage: u = function('u')(x)
3589
+ sage: g = function('g')(x)
3590
+ sage: fd = {(x,):1,(x, x):1}
3591
+ sage: ud = {u(x=2): 1}
3592
+ sage: atc = {x: 2, g(x=2): 3, diff(g, x)(x=2): 5}
3593
+ sage: atc[diff(g, x, x)(x=2)] = 7
3594
+ sage: dd = diff_prod(fd, u, g, [x], [1, 2], [], ud, atc)
3595
+ sage: dd[diff(u, x, 2)(x=2)]
3596
+ 22/9
3597
+ """
3598
+ from sage.symbolic.relation import solve
3599
+
3600
+ for l in interval:
3601
+ D = {}
3602
+ rhs = []
3603
+ lhs = []
3604
+ new_vars = []
3605
+ for t in combinations_with_replacement(X, l):
3606
+ t = list(t)
3607
+ s = t + end
3608
+ lhs.append(f_derivs[tuple(s)])
3609
+ rhs.append(diff(u * g, s).subs(atc).subs(uderivs))
3610
+ # Since Sage's solve command can't take derivatives as variable
3611
+ # names, make new variables based on t to stand in for
3612
+ # diff(u, t) and store them in D.
3613
+ new_var = SR.temp_var()
3614
+ new_vars.append(new_var)
3615
+ D[diff(u, t).subs(atc)] = new_var
3616
+ eqns = [lhs[i] == rhs[i].subs(uderivs).subs(D)
3617
+ for i in range(len(lhs))]
3618
+ variables = D.values()
3619
+ sol = solve(eqns, *variables, solution_dict=True)
3620
+ uderivs.update(subs_all(D, sol[ZZ.zero()]))
3621
+ SR.cleanup_var(new_vars)
3622
+ return uderivs
3623
+
3624
+
3625
+ def permutation_sign(s, u):
3626
+ r"""
3627
+ This function returns the sign of the permutation on
3628
+ ``1, ..., len(u)`` that is induced by the sublist ``s`` of ``u``.
3629
+
3630
+ .. NOTE::
3631
+
3632
+ This function was intended for internal use and is deprecated now
3633
+ (:issue:`29465`).
3634
+
3635
+ INPUT:
3636
+
3637
+ - ``s`` -- a sublist of ``u``
3638
+ - ``u`` -- list
3639
+
3640
+ OUTPUT:
3641
+
3642
+ The sign of the permutation obtained by taking indices
3643
+ within ``u`` of the list ``s + sc``, where ``sc`` is ``u``
3644
+ with the elements of ``s`` removed.
3645
+
3646
+ EXAMPLES::
3647
+
3648
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import permutation_sign
3649
+ sage: u = ['a', 'b', 'c', 'd', 'e']
3650
+ sage: s = ['b', 'd']
3651
+ sage: permutation_sign(s, u)
3652
+ doctest:...: DeprecationWarning: the function permutation_sign is deprecated
3653
+ See https://github.com/sagemath/sage/issues/29465 for details.
3654
+ -1
3655
+ sage: s = ['d', 'b']
3656
+ sage: permutation_sign(s, u)
3657
+ 1
3658
+ """
3659
+ from sage.misc.superseded import deprecation
3660
+ deprecation(29465, 'the function permutation_sign is deprecated')
3661
+ from sage.combinat.permutation import Permutation
3662
+
3663
+ # Convert lists to lists of numbers in {1,..., len(u)}
3664
+ A = [i + 1 for i in range(len(u))]
3665
+ B = [u.index(x) + 1 for x in s]
3666
+
3667
+ C = sorted(set(A).difference(set(B)))
3668
+ P = Permutation(B + C)
3669
+ return P.signature()
3670
+
3671
+
3672
+ def subs_all(f, sub, simplify=False):
3673
+ r"""
3674
+ Return the items of `f` substituted by the dictionaries
3675
+ of ``sub`` in order of their appearance in ``sub``.
3676
+
3677
+ INPUT:
3678
+
3679
+ - ``f`` -- an individual or list of symbolic expressions
3680
+ or dictionaries
3681
+ - ``sub`` -- an individual or list of dictionaries
3682
+ - ``simplify`` -- boolean (default: ``False``); set to ``True`` to
3683
+ simplify the result
3684
+
3685
+ OUTPUT:
3686
+
3687
+ The items of ``f`` substituted by the dictionaries of ``sub`` in order
3688
+ of their appearance in ``sub``. The ``subs()`` command is used. If
3689
+ simplify is ``True``, then ``simplify()`` is used after substitution.
3690
+
3691
+ EXAMPLES::
3692
+
3693
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import subs_all
3694
+ sage: var('x, y, z')
3695
+ (x, y, z)
3696
+ sage: a = {x:1}
3697
+ sage: b = {y:2}
3698
+ sage: c = {z:3}
3699
+ sage: subs_all(x + y + z, a)
3700
+ y + z + 1
3701
+ sage: subs_all(x + y + z, [c, a])
3702
+ y + 4
3703
+ sage: subs_all([x + y + z, y^2], b)
3704
+ [x + z + 2, 4]
3705
+ sage: subs_all([x + y + z, y^2], [b, c])
3706
+ [x + 5, 4]
3707
+
3708
+ ::
3709
+
3710
+ sage: var('x, y')
3711
+ (x, y)
3712
+ sage: a = {'foo': x**2 + y**2, 'bar': x - y}
3713
+ sage: b = {x: 1, y: 2}
3714
+ sage: subs_all(a, b)
3715
+ {'bar': -1, 'foo': 5}
3716
+ """
3717
+ singleton = False
3718
+ if not isinstance(f, (list, tuple)):
3719
+ f = [f]
3720
+ singleton = True
3721
+ if not isinstance(sub, (list, tuple)):
3722
+ sub = [sub]
3723
+ g = []
3724
+ for ff in f:
3725
+ for D in sub:
3726
+ if isinstance(ff, dict):
3727
+ ff = {k: ff[k].subs(D) for k in ff}
3728
+ else:
3729
+ ff = ff.subs(D)
3730
+ g.append(ff)
3731
+
3732
+ if singleton and simplify:
3733
+ if isinstance(g[0], dict):
3734
+ return g[0]
3735
+ return g[0].simplify()
3736
+
3737
+ if singleton and not simplify:
3738
+ return g[0]
3739
+
3740
+ if not singleton and simplify:
3741
+ G = []
3742
+ for gg in g:
3743
+ if isinstance(gg, dict):
3744
+ G.append(gg)
3745
+ else:
3746
+ G.append(gg.simplify())
3747
+ return G
3748
+
3749
+ return g
3750
+
3751
+
3752
+ def diff_all(f, V, n, ending=[], sub=None, sub_final=None,
3753
+ zero_order=0, rekey=None):
3754
+ r"""
3755
+ Return a dictionary of representative mixed partial
3756
+ derivatives of `f` from order 1 up to order `n` with respect to the
3757
+ variables in `V`.
3758
+
3759
+ The default is to key the dictionary by all nondecreasing sequences
3760
+ in `V` of length 1 up to length `n`.
3761
+
3762
+ INPUT:
3763
+
3764
+ - ``f`` -- an individual or list of `\mathcal{C}^{n+1}` functions
3765
+ - ``V`` -- list of variables occurring in `f`
3766
+ - ``n`` -- a natural number
3767
+ - ``ending`` -- list of variables in `V`
3768
+ - ``sub`` -- an individual or list of dictionaries
3769
+ - ``sub_final`` -- an individual or list of dictionaries
3770
+ - ``rekey`` -- a callable symbolic function in `V` or list thereof
3771
+ - ``zero_order`` -- a natural number
3772
+
3773
+ OUTPUT: the dictionary ``{s_1:deriv_1, ..., sr:deriv_r}``
3774
+
3775
+ Here ``s_1, ..., s_r`` is a listing of
3776
+ all nondecreasing sequences of length 1 up to length `n` over the
3777
+ alphabet `V`, where `w > v` in `X` if and only if ``str(w) > str(v)``,
3778
+ and ``deriv_j`` is the derivative of `f` with respect to the derivative
3779
+ sequence ``s_j`` and simplified with respect to the substitutions in
3780
+ ``sub`` and evaluated at ``sub_final``.
3781
+ Moreover, all derivatives with respect to sequences of length less than
3782
+ ``zero_order`` (derivatives of order less than ``zero_order`` )
3783
+ will be made zero.
3784
+
3785
+ If ``rekey`` is nonempty, then ``s_1, ..., s_r`` will be replaced
3786
+ by the symbolic derivatives of the functions in ``rekey``.
3787
+
3788
+ If ``ending`` is nonempty, then every derivative sequence ``s_j``
3789
+ will be suffixed by ``ending``.
3790
+
3791
+ EXAMPLES::
3792
+
3793
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import diff_all
3794
+ sage: f = function('f')(x)
3795
+ sage: dd = diff_all(f, [x], 3)
3796
+ sage: dd[(x, x, x)]
3797
+ diff(f(x), x, x, x)
3798
+
3799
+ sage: d1 = {diff(f, x): 4*x^3}
3800
+ sage: dd = diff_all(f, [x], 3, sub=d1)
3801
+ sage: dd[(x, x, x)]
3802
+ 24*x
3803
+
3804
+ sage: dd = diff_all(f, [x], 3, sub=d1, rekey=f)
3805
+ sage: dd[diff(f, x, 3)]
3806
+ 24*x
3807
+
3808
+ sage: a = {x:1}
3809
+ sage: dd = diff_all(f, [x], 3, sub=d1, rekey=f, sub_final=a)
3810
+ sage: dd[diff(f, x, 3)]
3811
+ 24
3812
+
3813
+ ::
3814
+
3815
+ sage: X = var('x, y, z')
3816
+ sage: f = function('f')(*X)
3817
+ sage: dd = diff_all(f, X, 2, ending=[y, y, y])
3818
+ sage: dd[(z, y, y, y)]
3819
+ diff(f(x, y, z), y, y, y, z)
3820
+
3821
+ ::
3822
+
3823
+ sage: g = function('g')(*X)
3824
+ sage: dd = diff_all([f, g], X, 2)
3825
+ sage: dd[(0, y, z)]
3826
+ diff(f(x, y, z), y, z)
3827
+
3828
+ sage: dd[(1, z, z)]
3829
+ diff(g(x, y, z), z, z)
3830
+
3831
+ sage: f = exp(x*y*z)
3832
+ sage: ff = function('ff')(*X)
3833
+ sage: dd = diff_all(f, X, 2, rekey=ff)
3834
+ sage: dd[diff(ff, x, z)]
3835
+ x*y^2*z*e^(x*y*z) + y*e^(x*y*z)
3836
+ """
3837
+ singleton = False
3838
+ if not isinstance(f, list):
3839
+ f = [f]
3840
+ singleton = True
3841
+
3842
+ # Build the dictionary of derivatives iteratively from a list
3843
+ # of nondecreasing derivative-order sequences.
3844
+ derivs = {}
3845
+ r = len(f)
3846
+ if ending:
3847
+ seeds = [ending]
3848
+ start = ZZ.one()
3849
+ else:
3850
+ seeds = [[v] for v in V]
3851
+ start = Integer(2)
3852
+ if singleton:
3853
+ for s in seeds:
3854
+ derivs[tuple(s)] = subs_all(diff(f[0], s), sub)
3855
+ for l in range(start, n + 1):
3856
+ for t in combinations_with_replacement(V, l):
3857
+ s = t + tuple(ending)
3858
+ derivs[s] = subs_all(diff(derivs[s[1:]], s[0]), sub)
3859
+ else:
3860
+ # Make the dictionary keys of the form (j, sequence of variables),
3861
+ # where j in range(r).
3862
+ for s in seeds:
3863
+ value = subs_all([diff(f[j], s) for j in range(r)], sub)
3864
+ derivs.update({tuple([j] + s): value[j] for j in range(r)})
3865
+ for l in range(start, n + 1):
3866
+ for t in combinations_with_replacement(V, l):
3867
+ s = t + tuple(ending)
3868
+ value = subs_all([diff(derivs[(j,) + s[1:]], s[0]) for j in range(r)], sub)
3869
+ derivs.update({(j,) + s: value[j] for j in range(r)})
3870
+ if zero_order:
3871
+ # Zero out all the derivatives of order < zero_order
3872
+ if singleton:
3873
+ for k in derivs:
3874
+ if len(k) < zero_order:
3875
+ derivs[k] = ZZ.zero()
3876
+ else:
3877
+ # Ignore the first of element of k, which is an index.
3878
+ for k in derivs:
3879
+ if len(k) - 1 < zero_order:
3880
+ derivs[k] = ZZ.zero()
3881
+ if sub_final:
3882
+ # Substitute sub_final into the values of derivs.
3883
+ for k in derivs:
3884
+ derivs[k] = subs_all(derivs[k], sub_final)
3885
+ if rekey:
3886
+ # Rekey the derivs dictionary by the value of rekey.
3887
+ F = rekey
3888
+ if singleton:
3889
+ # F must be a singleton.
3890
+ derivs = {diff(F, list(k)): derivs[k] for k in derivs}
3891
+ else:
3892
+ # F must be a list.
3893
+ derivs = {diff(F[k[0]], list(k)[1:]): derivs[k] for k in derivs}
3894
+ return derivs
3895
+
3896
+
3897
+ def diff_op(A, B, AB_derivs, V, M, r, N):
3898
+ r"""
3899
+ Return the derivatives `DD^{(l+k)}(A[j] B^l)` evaluated at a point
3900
+ `p` for various natural numbers `j, k, l` which depend on `r` and `N`.
3901
+
3902
+ Here `DD` is a specific second-order linear differential operator
3903
+ that depends on `M` , `A` is a list of symbolic functions,
3904
+ `B` is symbolic function, and ``AB_derivs`` contains all the derivatives
3905
+ of `A` and `B` evaluated at `p` that are necessary for the computation.
3906
+
3907
+ INPUT:
3908
+
3909
+ - ``A`` -- a single or length ``r`` list of symbolic functions in the
3910
+ variables ``V``
3911
+ - ``B`` -- a symbolic function in the variables ``V``
3912
+ - ``AB_derivs`` -- dictionary whose keys are the (symbolic)
3913
+ derivatives of ``A[0], ..., A[r-1]`` up to order ``2 * N-2`` and
3914
+ the (symbolic) derivatives of ``B`` up to order ``2 * N``;
3915
+ the values of the dictionary are complex numbers that are
3916
+ the keys evaluated at a common point `p`
3917
+ - ``V`` -- the variables of the ``A[j]`` and ``B``
3918
+ - ``M`` -- a symmetric `l \times l` matrix, where `l` is the
3919
+ length of ``V``
3920
+ - ``r``, ``N`` -- natural numbers
3921
+
3922
+ OUTPUT: a dictionary
3923
+
3924
+ The output is
3925
+ a dictionary whose keys are natural number tuples of the form
3926
+ `(j, k, l)`, where `l \leq 2k`, `j \leq r-1`, and `j+k \leq N-1`,
3927
+ and whose values are `DD^(l+k)(A[j] B^l)` evaluated at a point
3928
+ `p`, where `DD` is the linear second-order differential operator
3929
+ `-\sum_{i=0}^{l-1} \sum_{j=0}^{l-1} M[i][j]
3930
+ \partial^2 /(\partial V[j] \partial V[i])`.
3931
+
3932
+ .. NOTE::
3933
+
3934
+ For internal use by
3935
+ :meth:`FractionWithFactoredDenominator.asymptotics_smooth()` and
3936
+ :meth:`FractionWithFactoredDenominator.asymptotics_multiple()`.
3937
+
3938
+ EXAMPLES::
3939
+
3940
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import diff_op
3941
+ sage: T = var('x, y')
3942
+ sage: A = function('A')(*tuple(T))
3943
+ sage: B = function('B')(*tuple(T))
3944
+ sage: AB_derivs = {}
3945
+ sage: M = matrix([[1, 2],[2, 1]])
3946
+ sage: DD = diff_op(A, B, AB_derivs, T, M, 1, 2) # long time (see :issue:`35207`)
3947
+ sage: sorted(DD) # long time
3948
+ [(0, 0, 0), (0, 1, 0), (0, 1, 1), (0, 1, 2)]
3949
+ sage: DD[(0, 1, 2)].number_of_operands() # long time
3950
+ 246
3951
+ """
3952
+ from itertools import product
3953
+ from sage.misc.mrange import xmrange
3954
+
3955
+ if not isinstance(A, list):
3956
+ A = [A]
3957
+
3958
+ # First, compute the necessary product derivatives of A and B.
3959
+ product_derivs = {}
3960
+ for j, k in xmrange([r, N], tuple):
3961
+ if j + k < N:
3962
+ for l in range(2 * k + 1):
3963
+ for s in combinations_with_replacement(V, 2 * (k + l)):
3964
+ DF = diff(A[j] * B ** l, list(s)).subs(AB_derivs)
3965
+ product_derivs[(j, k, l) + s] = DF
3966
+
3967
+ # Second, compute DD^(k+l)(A[j]*B^l)(p) and store values in dictionary.
3968
+ DD = {}
3969
+ rows = M.nrows()
3970
+ for j, k in xmrange([r, N], tuple):
3971
+ if j + k < N:
3972
+ for l in range(2 * k + 1):
3973
+ # Take advantage of the symmetry of M by ignoring
3974
+ # the upper-diagonal entries of M and multiplying by
3975
+ # appropriate powers of 2.
3976
+ if k + l == 0:
3977
+ DD[(j, k, l)] = product_derivs[(j, k, l)]
3978
+ continue
3979
+ S = [(a, b) for a, b in xmrange([rows, rows], tuple) if b <= a]
3980
+ P = product(S, repeat=k + l)
3981
+ diffo = ZZ.zero()
3982
+ for t in P:
3983
+ idx = (j, k, l) + diff_seq(V, t)
3984
+ if product_derivs[idx] != ZZ.zero():
3985
+ MM = ZZ.one()
3986
+ for (a, b) in t:
3987
+ MM *= M[a][b]
3988
+ if a != b:
3989
+ MM *= Integer(2)
3990
+ diffo += MM * product_derivs[idx]
3991
+ DD[(j, k, l)] = (-ZZ.one()) ** (k + l) * diffo
3992
+ return DD
3993
+
3994
+
3995
+ def diff_seq(V, s):
3996
+ r"""
3997
+ Given a list ``s`` of tuples of natural numbers, return the
3998
+ list of elements of ``V`` with indices the elements of the elements
3999
+ of ``s``.
4000
+
4001
+ INPUT:
4002
+
4003
+ - ``V`` -- list
4004
+ - ``s`` -- list of tuples of natural numbers in the interval
4005
+ ``range(len(V))``
4006
+
4007
+ OUTPUT:
4008
+
4009
+ The tuple ``tuple([V[tt] for tt in sorted(t)])``, where ``t`` is the
4010
+ list of elements of the elements of ``s``.
4011
+
4012
+ EXAMPLES::
4013
+
4014
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import diff_seq
4015
+ sage: V = list(var('x, t, z'))
4016
+ sage: diff_seq(V,([0, 1],[0, 2, 1],[0, 0]))
4017
+ (x, x, x, x, t, t, z)
4018
+
4019
+ .. NOTE::
4020
+
4021
+ This function is for internal use by :func:`diff_op()`.
4022
+ """
4023
+ t = []
4024
+ for ss in s:
4025
+ t.extend(ss)
4026
+ return tuple([V[tt] for tt in sorted(t)])
4027
+
4028
+
4029
+ def diff_op_simple(A, B, AB_derivs, x, v, a, N):
4030
+ r"""
4031
+ Return `DD^(e k + v l)(A B^l)` evaluated at a point `p` for
4032
+ various natural numbers `e, k, l` that depend on `v` and `N`.
4033
+
4034
+ Here `DD` is a specific linear differential operator that depends
4035
+ on `a` and `v` , `A` and `B` are symbolic functions, and ``AB_derivs``
4036
+ contains all the derivatives of `A` and `B` evaluated at `p` that are
4037
+ necessary for the computation.
4038
+
4039
+ .. NOTE::
4040
+
4041
+ For internal use by the function
4042
+ :meth:`FractionWithFactoredDenominator.asymptotics_smooth()`.
4043
+
4044
+ INPUT:
4045
+
4046
+ - ``A``, ``B`` -- symbolic functions in the variable ``x``
4047
+ - ``AB_derivs`` -- dictionary whose keys are the (symbolic)
4048
+ derivatives of ``A`` up to order ``2 * N`` if ``v`` is even or
4049
+ ``N`` if ``v`` is odd and the (symbolic) derivatives of ``B``
4050
+ up to order ``2 * N + v`` if ``v`` is even or ``N + v``
4051
+ if ``v`` is odd; the values of the dictionary are complex numbers
4052
+ that are the keys evaluated at a common point `p`
4053
+ - ``x`` -- a symbolic variable
4054
+ - ``a`` -- a complex number
4055
+ - ``v``, ``N`` -- natural numbers
4056
+
4057
+ OUTPUT: a dictionary
4058
+
4059
+ The output is
4060
+ a dictionary whose keys are natural number pairs of the form `(k, l)`,
4061
+ where `k < N` and `l \leq 2k` and whose values are
4062
+ `DD^(e k + v l)(A B^l)` evaluated at a point `p`.
4063
+ Here `e=2` if `v` is even, `e=1` if `v` is odd, and `DD` is the
4064
+ linear differential operator
4065
+ `(a^{-1/v} d/dt)` if `v` is even and
4066
+ `(|a|^{-1/v} i \text{sgn}(a) d/dt)` if `v` is odd.
4067
+
4068
+ EXAMPLES::
4069
+
4070
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import diff_op_simple
4071
+ sage: A = function('A')(x)
4072
+ sage: B = function('B')(x)
4073
+ sage: AB_derivs = {}
4074
+ sage: sorted(diff_op_simple(A, B, AB_derivs, x, 3, 2, 2).items())
4075
+ [((0, 0), A(x)),
4076
+ ((1, 0), 1/2*I*2^(2/3)*diff(A(x), x)),
4077
+ ((1, 1),
4078
+ 1/4*2^(2/3)*(B(x)*diff(A(x), x, x, x, x) + 4*diff(A(x), x, x, x)*diff(B(x), x) + 6*diff(A(x), x, x)*diff(B(x), x, x) + 4*diff(A(x), x)*diff(B(x), x, x, x) + A(x)*diff(B(x), x, x, x, x)))]
4079
+ """
4080
+ from sage.misc.functional import sqrt
4081
+
4082
+ I = sqrt(-ZZ.one())
4083
+ DD = {}
4084
+ if v.mod(Integer(2)) == ZZ.zero():
4085
+ for k in range(N):
4086
+ for l in range(2 * k + 1):
4087
+ DD[(k, l)] = ((a ** (-ZZ.one() / v)) ** (2 * k + v * l) *
4088
+ diff(A * B ** l, x,
4089
+ 2 * k + v * l).subs(AB_derivs))
4090
+ else:
4091
+ for k in range(N):
4092
+ for l in range(k + 1):
4093
+ DD[(k, l)] = ((abs(a) ** (-ZZ.one() / v) * I *
4094
+ a / abs(a)) ** (k + v * l) *
4095
+ diff(A * B ** l, x,
4096
+ k + v * l).subs(AB_derivs))
4097
+ return DD
4098
+
4099
+
4100
+ def direction(v, coordinate=None):
4101
+ r"""
4102
+ Return ``[vv/v[coordinate] for vv in v]`` where
4103
+ ``coordinate`` is the last index of ``v`` if not specified otherwise.
4104
+
4105
+ INPUT:
4106
+
4107
+ - ``v`` -- a vector
4108
+ - ``coordinate`` -- (default: ``None``) an index for ``v``
4109
+
4110
+ EXAMPLES::
4111
+
4112
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import direction
4113
+ sage: direction([2, 3, 5])
4114
+ (2/5, 3/5, 1)
4115
+ sage: direction([2, 3, 5], 0)
4116
+ (1, 3/2, 5/2)
4117
+ """
4118
+ if coordinate is None:
4119
+ coordinate = len(v) - 1
4120
+ return tuple([vv / v[coordinate] for vv in v])
4121
+
4122
+
4123
+ def coerce_point(R, p):
4124
+ r"""
4125
+ Coerce the keys of the dictionary ``p`` into the ring ``R``.
4126
+
4127
+ .. WARNING::
4128
+
4129
+ This method assumes that it is possible.
4130
+
4131
+ EXAMPLES::
4132
+
4133
+ sage: from sage.rings.asymptotic.asymptotics_multivariate_generating_functions import FractionWithFactoredDenominatorRing, coerce_point
4134
+ sage: R.<x,y> = PolynomialRing(QQ)
4135
+ sage: FFPD = FractionWithFactoredDenominatorRing(R)
4136
+ sage: f = FFPD()
4137
+ sage: p = {SR(x): 1, SR(y): 7/8}
4138
+ sage: for k in sorted(p, key=str):
4139
+ ....: print("{} {} {}".format(k, k.parent(), p[k]))
4140
+ x Symbolic Ring 1
4141
+ y Symbolic Ring 7/8
4142
+ sage: q = coerce_point(R, p)
4143
+ sage: for k in sorted(q, key=str):
4144
+ ....: print("{} {} {}".format(k, k.parent(), q[k]))
4145
+ x Multivariate Polynomial Ring in x, y over Rational Field 1
4146
+ y Multivariate Polynomial Ring in x, y over Rational Field 7/8
4147
+ """
4148
+ if p and list(p)[0].parent() != R:
4149
+ try:
4150
+ return {x: p[SR(x)] for x in R.gens()}
4151
+ except TypeError:
4152
+ pass
4153
+ return p