passagemath-symbolics 10.6.37__cp314-cp314t-macosx_13_0_arm64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- passagemath_symbolics/.dylibs/libgmp.10.dylib +0 -0
- passagemath_symbolics/__init__.py +3 -0
- passagemath_symbolics-10.6.37.dist-info/METADATA +187 -0
- passagemath_symbolics-10.6.37.dist-info/RECORD +172 -0
- passagemath_symbolics-10.6.37.dist-info/WHEEL +6 -0
- passagemath_symbolics-10.6.37.dist-info/top_level.txt +3 -0
- sage/all__sagemath_symbolics.py +17 -0
- sage/calculus/all.py +14 -0
- sage/calculus/calculus.py +2826 -0
- sage/calculus/desolvers.py +1866 -0
- sage/calculus/predefined.py +51 -0
- sage/calculus/tests.py +225 -0
- sage/calculus/var.cpython-314t-darwin.so +0 -0
- sage/calculus/var.pyx +401 -0
- sage/dynamics/all__sagemath_symbolics.py +6 -0
- sage/dynamics/complex_dynamics/all.py +5 -0
- sage/dynamics/complex_dynamics/mandel_julia.py +765 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.cpython-314t-darwin.so +0 -0
- sage/dynamics/complex_dynamics/mandel_julia_helper.pyx +1035 -0
- sage/ext/all__sagemath_symbolics.py +1 -0
- sage/ext_data/kenzo/CP2.txt +45 -0
- sage/ext_data/kenzo/CP3.txt +349 -0
- sage/ext_data/kenzo/CP4.txt +4774 -0
- sage/ext_data/kenzo/README.txt +49 -0
- sage/ext_data/kenzo/S4.txt +20 -0
- sage/ext_data/magma/latex/latex.m +1021 -0
- sage/ext_data/magma/latex/latex.spec +1 -0
- sage/ext_data/magma/sage/basic.m +356 -0
- sage/ext_data/magma/sage/sage.spec +1 -0
- sage/ext_data/magma/spec +9 -0
- sage/geometry/all__sagemath_symbolics.py +8 -0
- sage/geometry/hyperbolic_space/all.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_coercion.py +743 -0
- sage/geometry/hyperbolic_space/hyperbolic_constants.py +5 -0
- sage/geometry/hyperbolic_space/hyperbolic_geodesic.py +2409 -0
- sage/geometry/hyperbolic_space/hyperbolic_interface.py +206 -0
- sage/geometry/hyperbolic_space/hyperbolic_isometry.py +1082 -0
- sage/geometry/hyperbolic_space/hyperbolic_model.py +1502 -0
- sage/geometry/hyperbolic_space/hyperbolic_point.py +621 -0
- sage/geometry/riemannian_manifolds/all.py +7 -0
- sage/geometry/riemannian_manifolds/parametrized_surface3d.py +1632 -0
- sage/geometry/riemannian_manifolds/surface3d_generators.py +461 -0
- sage/interfaces/all__sagemath_symbolics.py +1 -0
- sage/interfaces/magma.py +3017 -0
- sage/interfaces/magma_free.py +92 -0
- sage/interfaces/maple.py +1397 -0
- sage/interfaces/mathematica.py +1345 -0
- sage/interfaces/mathics.py +1312 -0
- sage/interfaces/sympy.py +1398 -0
- sage/interfaces/sympy_wrapper.py +197 -0
- sage/interfaces/tides.py +938 -0
- sage/libs/all__sagemath_symbolics.py +6 -0
- sage/manifolds/all.py +7 -0
- sage/manifolds/calculus_method.py +555 -0
- sage/manifolds/catalog.py +437 -0
- sage/manifolds/chart.py +4019 -0
- sage/manifolds/chart_func.py +3419 -0
- sage/manifolds/continuous_map.py +2183 -0
- sage/manifolds/continuous_map_image.py +155 -0
- sage/manifolds/differentiable/affine_connection.py +2475 -0
- sage/manifolds/differentiable/all.py +1 -0
- sage/manifolds/differentiable/automorphismfield.py +1383 -0
- sage/manifolds/differentiable/automorphismfield_group.py +604 -0
- sage/manifolds/differentiable/bundle_connection.py +1445 -0
- sage/manifolds/differentiable/characteristic_cohomology_class.py +1840 -0
- sage/manifolds/differentiable/chart.py +1241 -0
- sage/manifolds/differentiable/curve.py +1028 -0
- sage/manifolds/differentiable/de_rham_cohomology.py +541 -0
- sage/manifolds/differentiable/degenerate.py +559 -0
- sage/manifolds/differentiable/degenerate_submanifold.py +1671 -0
- sage/manifolds/differentiable/diff_form.py +1658 -0
- sage/manifolds/differentiable/diff_form_module.py +1062 -0
- sage/manifolds/differentiable/diff_map.py +1315 -0
- sage/manifolds/differentiable/differentiable_submanifold.py +291 -0
- sage/manifolds/differentiable/examples/all.py +1 -0
- sage/manifolds/differentiable/examples/euclidean.py +2517 -0
- sage/manifolds/differentiable/examples/real_line.py +897 -0
- sage/manifolds/differentiable/examples/sphere.py +1186 -0
- sage/manifolds/differentiable/examples/symplectic_space.py +187 -0
- sage/manifolds/differentiable/examples/symplectic_space_test.py +40 -0
- sage/manifolds/differentiable/integrated_curve.py +4035 -0
- sage/manifolds/differentiable/levi_civita_connection.py +841 -0
- sage/manifolds/differentiable/manifold.py +4254 -0
- sage/manifolds/differentiable/manifold_homset.py +1826 -0
- sage/manifolds/differentiable/metric.py +3032 -0
- sage/manifolds/differentiable/mixed_form.py +1507 -0
- sage/manifolds/differentiable/mixed_form_algebra.py +559 -0
- sage/manifolds/differentiable/multivector_module.py +800 -0
- sage/manifolds/differentiable/multivectorfield.py +1520 -0
- sage/manifolds/differentiable/poisson_tensor.py +268 -0
- sage/manifolds/differentiable/pseudo_riemannian.py +755 -0
- sage/manifolds/differentiable/pseudo_riemannian_submanifold.py +1839 -0
- sage/manifolds/differentiable/scalarfield.py +1343 -0
- sage/manifolds/differentiable/scalarfield_algebra.py +472 -0
- sage/manifolds/differentiable/symplectic_form.py +910 -0
- sage/manifolds/differentiable/symplectic_form_test.py +220 -0
- sage/manifolds/differentiable/tangent_space.py +412 -0
- sage/manifolds/differentiable/tangent_vector.py +616 -0
- sage/manifolds/differentiable/tensorfield.py +4665 -0
- sage/manifolds/differentiable/tensorfield_module.py +963 -0
- sage/manifolds/differentiable/tensorfield_paral.py +2450 -0
- sage/manifolds/differentiable/tensorfield_paral_test.py +16 -0
- sage/manifolds/differentiable/vector_bundle.py +1728 -0
- sage/manifolds/differentiable/vectorfield.py +1717 -0
- sage/manifolds/differentiable/vectorfield_module.py +2445 -0
- sage/manifolds/differentiable/vectorframe.py +1832 -0
- sage/manifolds/family.py +270 -0
- sage/manifolds/local_frame.py +1490 -0
- sage/manifolds/manifold.py +3090 -0
- sage/manifolds/manifold_homset.py +452 -0
- sage/manifolds/operators.py +359 -0
- sage/manifolds/point.py +994 -0
- sage/manifolds/scalarfield.py +3718 -0
- sage/manifolds/scalarfield_algebra.py +629 -0
- sage/manifolds/section.py +3111 -0
- sage/manifolds/section_module.py +831 -0
- sage/manifolds/structure.py +229 -0
- sage/manifolds/subset.py +2764 -0
- sage/manifolds/subsets/all.py +1 -0
- sage/manifolds/subsets/closure.py +131 -0
- sage/manifolds/subsets/pullback.py +885 -0
- sage/manifolds/topological_submanifold.py +891 -0
- sage/manifolds/trivialization.py +733 -0
- sage/manifolds/utilities.py +1348 -0
- sage/manifolds/vector_bundle.py +1342 -0
- sage/manifolds/vector_bundle_fiber.py +332 -0
- sage/manifolds/vector_bundle_fiber_element.py +111 -0
- sage/matrix/all__sagemath_symbolics.py +1 -0
- sage/matrix/matrix_symbolic_dense.cpython-314t-darwin.so +0 -0
- sage/matrix/matrix_symbolic_dense.pxd +6 -0
- sage/matrix/matrix_symbolic_dense.pyx +1022 -0
- sage/matrix/matrix_symbolic_sparse.cpython-314t-darwin.so +0 -0
- sage/matrix/matrix_symbolic_sparse.pxd +6 -0
- sage/matrix/matrix_symbolic_sparse.pyx +1029 -0
- sage/modules/all__sagemath_symbolics.py +1 -0
- sage/modules/vector_callable_symbolic_dense.py +105 -0
- sage/modules/vector_symbolic_dense.py +116 -0
- sage/modules/vector_symbolic_sparse.py +118 -0
- sage/rings/all__sagemath_symbolics.py +4 -0
- sage/rings/asymptotic/all.py +6 -0
- sage/rings/asymptotic/asymptotic_expansion_generators.py +1485 -0
- sage/rings/asymptotic/asymptotic_ring.py +4858 -0
- sage/rings/asymptotic/asymptotics_multivariate_generating_functions.py +4153 -0
- sage/rings/asymptotic/growth_group.py +5373 -0
- sage/rings/asymptotic/growth_group_cartesian.py +1400 -0
- sage/rings/asymptotic/term_monoid.py +5237 -0
- sage/rings/function_field/all__sagemath_symbolics.py +2 -0
- sage/rings/polynomial/all__sagemath_symbolics.py +1 -0
- sage/symbolic/all.py +15 -0
- sage/symbolic/assumptions.py +985 -0
- sage/symbolic/benchmark.py +93 -0
- sage/symbolic/callable.py +459 -0
- sage/symbolic/complexity_measures.py +35 -0
- sage/symbolic/constants.py +1287 -0
- sage/symbolic/expression_conversion_algebraic.py +310 -0
- sage/symbolic/expression_conversion_sympy.py +317 -0
- sage/symbolic/expression_conversions.py +1713 -0
- sage/symbolic/function_factory.py +355 -0
- sage/symbolic/integration/all.py +1 -0
- sage/symbolic/integration/external.py +270 -0
- sage/symbolic/integration/integral.py +1115 -0
- sage/symbolic/maxima_wrapper.py +162 -0
- sage/symbolic/operators.py +267 -0
- sage/symbolic/random_tests.py +462 -0
- sage/symbolic/relation.py +1907 -0
- sage/symbolic/ring.cpython-314t-darwin.so +0 -0
- sage/symbolic/ring.pxd +5 -0
- sage/symbolic/ring.pyx +1396 -0
- sage/symbolic/subring.py +1025 -0
- sage/symbolic/symengine.py +19 -0
- sage/symbolic/tests.py +40 -0
- 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
|