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,1713 @@
|
|
|
1
|
+
# sage_setup: distribution = sagemath-symbolics
|
|
2
|
+
"""
|
|
3
|
+
Conversion of symbolic expressions to other types
|
|
4
|
+
|
|
5
|
+
This module provides routines for converting new symbolic expressions
|
|
6
|
+
to other types. Primarily, it provides a class :class:`Converter`
|
|
7
|
+
which will walk the expression tree and make calls to methods
|
|
8
|
+
overridden by subclasses.
|
|
9
|
+
"""
|
|
10
|
+
###############################################################################
|
|
11
|
+
# Sage: Open Source Mathematical Software
|
|
12
|
+
# Copyright (C) 2009 Mike Hansen <mhansen@gmail.com>
|
|
13
|
+
#
|
|
14
|
+
# Distributed under the terms of the GNU General Public License (GPL),
|
|
15
|
+
# version 2 or any later version. The full text of the GPL is available at:
|
|
16
|
+
# https://www.gnu.org/licenses/
|
|
17
|
+
###############################################################################
|
|
18
|
+
|
|
19
|
+
from operator import eq, ne, gt, lt, ge, le, mul, pow, neg, add, truediv
|
|
20
|
+
from functools import reduce
|
|
21
|
+
|
|
22
|
+
from sage.misc.lazy_import import lazy_import
|
|
23
|
+
from sage.symbolic.ring import SR
|
|
24
|
+
from sage.structure.element import Expression
|
|
25
|
+
from sage.functions.log import exp
|
|
26
|
+
from sage.symbolic.operators import arithmetic_operators, relation_operators, FDerivativeOperator, add_vararg, mul_vararg
|
|
27
|
+
from sage.rings.number_field.number_field_element_base import NumberFieldElement_base
|
|
28
|
+
|
|
29
|
+
lazy_import('sage.rings.universal_cyclotomic_field', 'UniversalCyclotomicField')
|
|
30
|
+
lazy_import('sage.symbolic.expression_conversion_sympy', ['SympyConverter', 'sympy_converter'])
|
|
31
|
+
lazy_import('sage.symbolic.expression_conversion_algebraic', ['AlgebraicConverter', 'algebraic'])
|
|
32
|
+
lazy_import('sage.symbolic.expression_conversion_fricas', ['FriCASConverter', 'fricas_converter'])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class FakeExpression:
|
|
36
|
+
r"""
|
|
37
|
+
Pynac represents `x/y` as `xy^{-1}`. Often, tree-walkers would prefer
|
|
38
|
+
to see divisions instead of multiplications and negative exponents.
|
|
39
|
+
To allow for this (since Pynac internally doesn't have division at all),
|
|
40
|
+
there is a possibility to pass use_fake_div=True; this will rewrite
|
|
41
|
+
an Expression into a mixture of Expression and FakeExpression nodes,
|
|
42
|
+
where the FakeExpression nodes are used to represent divisions.
|
|
43
|
+
These nodes are intended to act sufficiently like Expression nodes
|
|
44
|
+
that tree-walkers won't care about the difference.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
def __init__(self, operands, operator):
|
|
48
|
+
"""
|
|
49
|
+
EXAMPLES::
|
|
50
|
+
|
|
51
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
52
|
+
sage: import operator; x,y = var('x,y')
|
|
53
|
+
sage: FakeExpression([x, y], operator.truediv)
|
|
54
|
+
FakeExpression([x, y], <built-in function truediv>)
|
|
55
|
+
"""
|
|
56
|
+
self._operands = operands
|
|
57
|
+
self._operator = operator
|
|
58
|
+
|
|
59
|
+
def __repr__(self):
|
|
60
|
+
"""
|
|
61
|
+
EXAMPLES::
|
|
62
|
+
|
|
63
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
64
|
+
sage: import operator; x,y = var('x,y')
|
|
65
|
+
sage: FakeExpression([x, y], operator.truediv)
|
|
66
|
+
FakeExpression([x, y], <built-in function truediv>)
|
|
67
|
+
"""
|
|
68
|
+
return "FakeExpression(%r, %r)" % (self._operands, self._operator)
|
|
69
|
+
|
|
70
|
+
def pyobject(self):
|
|
71
|
+
"""
|
|
72
|
+
EXAMPLES::
|
|
73
|
+
|
|
74
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
75
|
+
sage: import operator; x,y = var('x,y')
|
|
76
|
+
sage: f = FakeExpression([x, y], operator.truediv)
|
|
77
|
+
sage: f.pyobject()
|
|
78
|
+
Traceback (most recent call last):
|
|
79
|
+
...
|
|
80
|
+
TypeError: self must be a numeric expression
|
|
81
|
+
"""
|
|
82
|
+
raise TypeError('self must be a numeric expression')
|
|
83
|
+
|
|
84
|
+
def operands(self):
|
|
85
|
+
"""
|
|
86
|
+
EXAMPLES::
|
|
87
|
+
|
|
88
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
89
|
+
sage: import operator; x,y = var('x,y')
|
|
90
|
+
sage: f = FakeExpression([x, y], operator.truediv)
|
|
91
|
+
sage: f.operands()
|
|
92
|
+
[x, y]
|
|
93
|
+
"""
|
|
94
|
+
return self._operands
|
|
95
|
+
|
|
96
|
+
def __getitem__(self, i):
|
|
97
|
+
"""
|
|
98
|
+
EXAMPLES::
|
|
99
|
+
|
|
100
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
101
|
+
sage: import operator; x,y = var('x,y')
|
|
102
|
+
sage: f = FakeExpression([x, y], operator.truediv)
|
|
103
|
+
sage: f[0]
|
|
104
|
+
x
|
|
105
|
+
"""
|
|
106
|
+
return self._operands[i]
|
|
107
|
+
|
|
108
|
+
def operator(self):
|
|
109
|
+
"""
|
|
110
|
+
EXAMPLES::
|
|
111
|
+
|
|
112
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
113
|
+
sage: import operator; x,y = var('x,y')
|
|
114
|
+
sage: f = FakeExpression([x, y], operator.truediv)
|
|
115
|
+
sage: f.operator()
|
|
116
|
+
<built-in function truediv>
|
|
117
|
+
"""
|
|
118
|
+
return self._operator
|
|
119
|
+
|
|
120
|
+
def _fast_callable_(self, etb):
|
|
121
|
+
"""
|
|
122
|
+
EXAMPLES::
|
|
123
|
+
|
|
124
|
+
sage: from sage.symbolic.expression_conversions import FakeExpression
|
|
125
|
+
sage: import operator; x,y = var('x,y')
|
|
126
|
+
sage: f = FakeExpression([x, y], operator.truediv)
|
|
127
|
+
sage: fast_callable(f, vars=['x','y']).op_list()
|
|
128
|
+
[('load_arg', 0), ('load_arg', 1), 'div', 'return']
|
|
129
|
+
"""
|
|
130
|
+
return fast_callable(self, etb)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class Converter:
|
|
134
|
+
def __init__(self, use_fake_div=False):
|
|
135
|
+
"""
|
|
136
|
+
If use_fake_div is set to True, then the converter will try to
|
|
137
|
+
replace expressions whose operator is operator.mul with the
|
|
138
|
+
corresponding expression whose operator is operator.truediv.
|
|
139
|
+
|
|
140
|
+
EXAMPLES::
|
|
141
|
+
|
|
142
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
143
|
+
sage: c = Converter(use_fake_div=True)
|
|
144
|
+
sage: c.use_fake_div
|
|
145
|
+
True
|
|
146
|
+
"""
|
|
147
|
+
self.use_fake_div = use_fake_div
|
|
148
|
+
|
|
149
|
+
def __call__(self, ex=None):
|
|
150
|
+
"""
|
|
151
|
+
.. NOTE::
|
|
152
|
+
|
|
153
|
+
If this object does not have an attribute ``ex``, then an argument
|
|
154
|
+
must be passed into :meth:`__call__`.
|
|
155
|
+
|
|
156
|
+
EXAMPLES::
|
|
157
|
+
|
|
158
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
159
|
+
sage: c = Converter(use_fake_div=True)
|
|
160
|
+
sage: c(SR(2))
|
|
161
|
+
Traceback (most recent call last):
|
|
162
|
+
...
|
|
163
|
+
NotImplementedError: pyobject
|
|
164
|
+
sage: c(x+2)
|
|
165
|
+
Traceback (most recent call last):
|
|
166
|
+
...
|
|
167
|
+
NotImplementedError: arithmetic
|
|
168
|
+
sage: c(x)
|
|
169
|
+
Traceback (most recent call last):
|
|
170
|
+
...
|
|
171
|
+
NotImplementedError: symbol
|
|
172
|
+
sage: c(x==2)
|
|
173
|
+
Traceback (most recent call last):
|
|
174
|
+
...
|
|
175
|
+
NotImplementedError: relation
|
|
176
|
+
sage: c(sin(x))
|
|
177
|
+
Traceback (most recent call last):
|
|
178
|
+
...
|
|
179
|
+
NotImplementedError: composition
|
|
180
|
+
sage: c(function('f')(x).diff(x))
|
|
181
|
+
Traceback (most recent call last):
|
|
182
|
+
...
|
|
183
|
+
NotImplementedError: derivative
|
|
184
|
+
|
|
185
|
+
We can set a default value for the argument by setting
|
|
186
|
+
the ``ex`` attribute::
|
|
187
|
+
|
|
188
|
+
sage: c.ex = SR(2)
|
|
189
|
+
sage: c()
|
|
190
|
+
Traceback (most recent call last):
|
|
191
|
+
...
|
|
192
|
+
NotImplementedError: pyobject
|
|
193
|
+
"""
|
|
194
|
+
if ex is None:
|
|
195
|
+
ex = self.ex
|
|
196
|
+
|
|
197
|
+
try:
|
|
198
|
+
obj = ex.pyobject()
|
|
199
|
+
return self.pyobject(ex, obj)
|
|
200
|
+
except TypeError as err:
|
|
201
|
+
if 'self must be a numeric expression' not in err.args:
|
|
202
|
+
raise err
|
|
203
|
+
|
|
204
|
+
operator = ex.operator()
|
|
205
|
+
if operator is None:
|
|
206
|
+
return self.symbol(ex)
|
|
207
|
+
|
|
208
|
+
if operator in arithmetic_operators:
|
|
209
|
+
if getattr(self, 'use_fake_div', False) and (operator is mul or operator is mul_vararg):
|
|
210
|
+
div = self.get_fake_div(ex)
|
|
211
|
+
return self.arithmetic(div, div.operator())
|
|
212
|
+
return self.arithmetic(ex, operator)
|
|
213
|
+
elif operator in relation_operators:
|
|
214
|
+
return self.relation(ex, operator)
|
|
215
|
+
elif isinstance(operator, FDerivativeOperator):
|
|
216
|
+
return self.derivative(ex, operator)
|
|
217
|
+
elif operator is tuple:
|
|
218
|
+
return self.tuple(ex)
|
|
219
|
+
else:
|
|
220
|
+
return self.composition(ex, operator)
|
|
221
|
+
|
|
222
|
+
def get_fake_div(self, ex):
|
|
223
|
+
"""
|
|
224
|
+
EXAMPLES::
|
|
225
|
+
|
|
226
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
227
|
+
sage: c = Converter(use_fake_div=True)
|
|
228
|
+
sage: c.get_fake_div(sin(x)/x)
|
|
229
|
+
FakeExpression([sin(x), x], <built-in function truediv>)
|
|
230
|
+
sage: c.get_fake_div(-1*sin(x))
|
|
231
|
+
FakeExpression([sin(x)], <built-in function neg>)
|
|
232
|
+
sage: c.get_fake_div(-x)
|
|
233
|
+
FakeExpression([x], <built-in function neg>)
|
|
234
|
+
sage: c.get_fake_div((2*x^3+2*x-1)/((x-2)*(x+1)))
|
|
235
|
+
FakeExpression([2*x^3 + 2*x - 1, FakeExpression([x + 1, x - 2], <built-in function mul>)], <built-in function truediv>)
|
|
236
|
+
|
|
237
|
+
Check if :issue:`8056` is fixed, i.e., if numerator is 1.::
|
|
238
|
+
|
|
239
|
+
sage: c.get_fake_div(1/pi/x)
|
|
240
|
+
FakeExpression([1, FakeExpression([pi, x], <built-in function mul>)], <built-in function truediv>)
|
|
241
|
+
"""
|
|
242
|
+
d = []
|
|
243
|
+
n = []
|
|
244
|
+
for arg in ex.operands():
|
|
245
|
+
ops = arg.operands()
|
|
246
|
+
try:
|
|
247
|
+
if arg.operator() is pow and repr(ops[1]) == '-1':
|
|
248
|
+
d.append(ops[0])
|
|
249
|
+
else:
|
|
250
|
+
n.append(arg)
|
|
251
|
+
except TypeError:
|
|
252
|
+
n.append(arg)
|
|
253
|
+
|
|
254
|
+
len_d = len(d)
|
|
255
|
+
if len_d == 0:
|
|
256
|
+
repr_n = [repr(_) for _ in n]
|
|
257
|
+
if len(n) == 2 and "-1" in repr_n:
|
|
258
|
+
a = n[0] if repr_n[1] == "-1" else n[1]
|
|
259
|
+
return FakeExpression([a], neg)
|
|
260
|
+
else:
|
|
261
|
+
return ex
|
|
262
|
+
elif len_d == 1:
|
|
263
|
+
d = d[0]
|
|
264
|
+
else:
|
|
265
|
+
d = FakeExpression(d, mul)
|
|
266
|
+
|
|
267
|
+
if len(n) == 0:
|
|
268
|
+
return FakeExpression([SR.one(), d], truediv)
|
|
269
|
+
elif len(n) == 1:
|
|
270
|
+
n = n[0]
|
|
271
|
+
else:
|
|
272
|
+
n = FakeExpression(n, mul)
|
|
273
|
+
|
|
274
|
+
return FakeExpression([n, d], truediv)
|
|
275
|
+
|
|
276
|
+
def pyobject(self, ex, obj):
|
|
277
|
+
"""
|
|
278
|
+
The input to this method is the result of calling
|
|
279
|
+
:meth:`pyobject` on a symbolic expression.
|
|
280
|
+
|
|
281
|
+
.. NOTE::
|
|
282
|
+
|
|
283
|
+
Note that if a constant such as ``pi`` is encountered in
|
|
284
|
+
the expression tree, its corresponding pyobject which is an
|
|
285
|
+
instance of :class:`sage.symbolic.constants.Pi` will be
|
|
286
|
+
passed into this method. One cannot do arithmetic using
|
|
287
|
+
such an object.
|
|
288
|
+
|
|
289
|
+
TESTS::
|
|
290
|
+
|
|
291
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
292
|
+
sage: f = SR(1)
|
|
293
|
+
sage: Converter().pyobject(f, f.pyobject())
|
|
294
|
+
Traceback (most recent call last):
|
|
295
|
+
...
|
|
296
|
+
NotImplementedError: pyobject
|
|
297
|
+
"""
|
|
298
|
+
raise NotImplementedError("pyobject")
|
|
299
|
+
|
|
300
|
+
def symbol(self, ex):
|
|
301
|
+
"""
|
|
302
|
+
The input to this method is a symbolic expression which
|
|
303
|
+
corresponds to a single variable. For example, this method
|
|
304
|
+
could be used to return a generator for a polynomial ring.
|
|
305
|
+
|
|
306
|
+
TESTS::
|
|
307
|
+
|
|
308
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
309
|
+
sage: Converter().symbol(x)
|
|
310
|
+
Traceback (most recent call last):
|
|
311
|
+
...
|
|
312
|
+
NotImplementedError: symbol
|
|
313
|
+
"""
|
|
314
|
+
raise NotImplementedError("symbol")
|
|
315
|
+
|
|
316
|
+
def relation(self, ex, operator):
|
|
317
|
+
"""
|
|
318
|
+
The input to this method is a symbolic expression which
|
|
319
|
+
corresponds to a relation.
|
|
320
|
+
|
|
321
|
+
TESTS::
|
|
322
|
+
|
|
323
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
324
|
+
sage: import operator
|
|
325
|
+
sage: Converter().relation(x==3, operator.eq)
|
|
326
|
+
Traceback (most recent call last):
|
|
327
|
+
...
|
|
328
|
+
NotImplementedError: relation
|
|
329
|
+
sage: Converter().relation(x==3, operator.lt)
|
|
330
|
+
Traceback (most recent call last):
|
|
331
|
+
...
|
|
332
|
+
NotImplementedError: relation
|
|
333
|
+
"""
|
|
334
|
+
raise NotImplementedError("relation")
|
|
335
|
+
|
|
336
|
+
def derivative(self, ex, operator):
|
|
337
|
+
"""
|
|
338
|
+
The input to this method is a symbolic expression which
|
|
339
|
+
corresponds to a relation.
|
|
340
|
+
|
|
341
|
+
TESTS::
|
|
342
|
+
|
|
343
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
344
|
+
sage: a = function('f')(x).diff(x); a
|
|
345
|
+
diff(f(x), x)
|
|
346
|
+
sage: Converter().derivative(a, a.operator())
|
|
347
|
+
Traceback (most recent call last):
|
|
348
|
+
...
|
|
349
|
+
NotImplementedError: derivative
|
|
350
|
+
"""
|
|
351
|
+
raise NotImplementedError("derivative")
|
|
352
|
+
|
|
353
|
+
def arithmetic(self, ex, operator):
|
|
354
|
+
"""
|
|
355
|
+
The input to this method is a symbolic expression and the
|
|
356
|
+
infix operator corresponding to that expression. Typically,
|
|
357
|
+
one will convert all of the arguments and then perform the
|
|
358
|
+
operation afterward.
|
|
359
|
+
|
|
360
|
+
TESTS::
|
|
361
|
+
|
|
362
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
363
|
+
sage: f = x + 2
|
|
364
|
+
sage: Converter().arithmetic(f, f.operator())
|
|
365
|
+
Traceback (most recent call last):
|
|
366
|
+
...
|
|
367
|
+
NotImplementedError: arithmetic
|
|
368
|
+
"""
|
|
369
|
+
raise NotImplementedError("arithmetic")
|
|
370
|
+
|
|
371
|
+
def composition(self, ex, operator):
|
|
372
|
+
"""
|
|
373
|
+
The input to this method is a symbolic expression and its
|
|
374
|
+
operator. This method will get called when you have a symbolic
|
|
375
|
+
function application.
|
|
376
|
+
|
|
377
|
+
TESTS::
|
|
378
|
+
|
|
379
|
+
sage: from sage.symbolic.expression_conversions import Converter
|
|
380
|
+
sage: f = sin(2)
|
|
381
|
+
sage: Converter().composition(f, f.operator())
|
|
382
|
+
Traceback (most recent call last):
|
|
383
|
+
...
|
|
384
|
+
NotImplementedError: composition
|
|
385
|
+
"""
|
|
386
|
+
raise NotImplementedError("composition")
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
class InterfaceInit(Converter):
|
|
390
|
+
def __init__(self, interface):
|
|
391
|
+
"""
|
|
392
|
+
EXAMPLES::
|
|
393
|
+
|
|
394
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
395
|
+
sage: m = InterfaceInit(maxima)
|
|
396
|
+
sage: a = pi + 2
|
|
397
|
+
sage: m(a)
|
|
398
|
+
'(%pi)+(2)'
|
|
399
|
+
sage: m(sin(a))
|
|
400
|
+
'sin((%pi)+(2))'
|
|
401
|
+
sage: m(exp(x^2) + pi + 2)
|
|
402
|
+
'(%pi)+(exp((_SAGE_VAR_x)^(2)))+(2)'
|
|
403
|
+
"""
|
|
404
|
+
self.name_init = "_%s_init_" % interface.name()
|
|
405
|
+
self.interface = interface
|
|
406
|
+
self.relation_symbols = interface._relation_symbols()
|
|
407
|
+
|
|
408
|
+
def symbol(self, ex):
|
|
409
|
+
"""
|
|
410
|
+
EXAMPLES::
|
|
411
|
+
|
|
412
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
413
|
+
sage: m = InterfaceInit(maxima)
|
|
414
|
+
sage: m.symbol(x)
|
|
415
|
+
'_SAGE_VAR_x'
|
|
416
|
+
sage: f(x) = x
|
|
417
|
+
sage: m.symbol(f)
|
|
418
|
+
'_SAGE_VAR_x'
|
|
419
|
+
sage: ii = InterfaceInit(gp) # needs sage.libs.pari
|
|
420
|
+
sage: ii.symbol(x) # needs sage.libs.pari
|
|
421
|
+
'x'
|
|
422
|
+
sage: g = InterfaceInit(giac) # needs sage.libs.giac
|
|
423
|
+
sage: g.symbol(x) # needs sage.libs.giac
|
|
424
|
+
'sageVARx'
|
|
425
|
+
"""
|
|
426
|
+
if self.interface.name() == 'maxima':
|
|
427
|
+
return '_SAGE_VAR_' + repr(SR(ex))
|
|
428
|
+
if self.interface.name() == 'giac':
|
|
429
|
+
return 'sageVAR' + repr(SR(ex))
|
|
430
|
+
return repr(SR(ex))
|
|
431
|
+
|
|
432
|
+
def pyobject(self, ex, obj):
|
|
433
|
+
"""
|
|
434
|
+
EXAMPLES::
|
|
435
|
+
|
|
436
|
+
sage: # needs sage.libs.pari
|
|
437
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
438
|
+
sage: ii = InterfaceInit(gp)
|
|
439
|
+
sage: f = 2+SR(I)
|
|
440
|
+
sage: ii.pyobject(f, f.pyobject())
|
|
441
|
+
'I + 2'
|
|
442
|
+
sage: ii.pyobject(SR(2), 2)
|
|
443
|
+
'2'
|
|
444
|
+
sage: ii.pyobject(pi, pi.pyobject())
|
|
445
|
+
'Pi'
|
|
446
|
+
"""
|
|
447
|
+
if (self.interface.name() in ['pari', 'gp'] and isinstance(obj, NumberFieldElement_base)):
|
|
448
|
+
from sage.rings.number_field.number_field_element_quadratic import NumberFieldElement_gaussian
|
|
449
|
+
if isinstance(obj, NumberFieldElement_gaussian):
|
|
450
|
+
return repr(obj)
|
|
451
|
+
try:
|
|
452
|
+
return getattr(obj, self.name_init)()
|
|
453
|
+
except AttributeError:
|
|
454
|
+
return repr(obj)
|
|
455
|
+
|
|
456
|
+
def relation(self, ex, operator):
|
|
457
|
+
"""
|
|
458
|
+
EXAMPLES::
|
|
459
|
+
|
|
460
|
+
sage: import operator
|
|
461
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
462
|
+
sage: m = InterfaceInit(maxima)
|
|
463
|
+
sage: m.relation(x==3, operator.eq)
|
|
464
|
+
'_SAGE_VAR_x = 3'
|
|
465
|
+
sage: m.relation(x==3, operator.lt)
|
|
466
|
+
'_SAGE_VAR_x < 3'
|
|
467
|
+
"""
|
|
468
|
+
return "%s %s %s" % (self(ex.lhs()), self.relation_symbols[operator],
|
|
469
|
+
self(ex.rhs()))
|
|
470
|
+
|
|
471
|
+
def tuple(self, ex):
|
|
472
|
+
"""
|
|
473
|
+
EXAMPLES::
|
|
474
|
+
|
|
475
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
476
|
+
sage: m = InterfaceInit(maxima)
|
|
477
|
+
sage: t = SR._force_pyobject((3, 4, e^x))
|
|
478
|
+
sage: m.tuple(t)
|
|
479
|
+
'[3,4,exp(_SAGE_VAR_x)]'
|
|
480
|
+
"""
|
|
481
|
+
x = map(self, ex.operands())
|
|
482
|
+
X = ','.join(x)
|
|
483
|
+
return str(self.interface._left_list_delim()) + X + str(self.interface._right_list_delim())
|
|
484
|
+
|
|
485
|
+
def derivative(self, ex, operator):
|
|
486
|
+
"""
|
|
487
|
+
EXAMPLES::
|
|
488
|
+
|
|
489
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
490
|
+
sage: m = InterfaceInit(maxima)
|
|
491
|
+
sage: f = function('f')
|
|
492
|
+
sage: a = f(x).diff(x); a
|
|
493
|
+
diff(f(x), x)
|
|
494
|
+
sage: print(m.derivative(a, a.operator()))
|
|
495
|
+
diff('f(_SAGE_VAR_x), _SAGE_VAR_x, 1)
|
|
496
|
+
sage: b = f(x).diff(x, x)
|
|
497
|
+
sage: print(m.derivative(b, b.operator()))
|
|
498
|
+
diff('f(_SAGE_VAR_x), _SAGE_VAR_x, 2)
|
|
499
|
+
|
|
500
|
+
We can also convert expressions where the argument is not just a
|
|
501
|
+
variable, but the result is an "at" expression using temporary
|
|
502
|
+
variables::
|
|
503
|
+
|
|
504
|
+
sage: y = var('y')
|
|
505
|
+
sage: t = (f(x*y).diff(x))/y
|
|
506
|
+
sage: t
|
|
507
|
+
D[0](f)(x*y)
|
|
508
|
+
sage: m.derivative(t, t.operator())
|
|
509
|
+
"at(diff('f(_SAGE_VAR__symbol0), _SAGE_VAR__symbol0, 1), [_SAGE_VAR__symbol0 = (_SAGE_VAR_x)*(_SAGE_VAR_y)])"
|
|
510
|
+
|
|
511
|
+
TESTS:
|
|
512
|
+
|
|
513
|
+
Most of these confirm that :issue:`7401` was fixed::
|
|
514
|
+
|
|
515
|
+
sage: t = var('t'); f = function('f')(t)
|
|
516
|
+
sage: a = 2^e^t * f.subs(t=e^t) * diff(f, t).subs(t=e^t) + 2*t
|
|
517
|
+
sage: solve(a == 0, diff(f, t).subs(t=e^t))
|
|
518
|
+
[D[0](f)(e^t) == -2^(-e^t + 1)*t/f(e^t)]
|
|
519
|
+
|
|
520
|
+
::
|
|
521
|
+
|
|
522
|
+
sage: f = function('f')(x)
|
|
523
|
+
sage: df = f.diff(x); df
|
|
524
|
+
diff(f(x), x)
|
|
525
|
+
sage: maxima(df)
|
|
526
|
+
'diff('f(_SAGE_VAR_x),_SAGE_VAR_x,1)
|
|
527
|
+
|
|
528
|
+
::
|
|
529
|
+
|
|
530
|
+
sage: a = df.subs(x=exp(x)); a
|
|
531
|
+
D[0](f)(e^x)
|
|
532
|
+
sage: b = maxima(a); b
|
|
533
|
+
%at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = %e^_SAGE_VAR_x)
|
|
534
|
+
sage: bool(b.sage() == a)
|
|
535
|
+
True
|
|
536
|
+
|
|
537
|
+
::
|
|
538
|
+
|
|
539
|
+
sage: a = df.subs(x=4); a
|
|
540
|
+
D[0](f)(4)
|
|
541
|
+
sage: b = maxima(a); b
|
|
542
|
+
%at('diff('f(_SAGE_VAR__symbol0),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4)
|
|
543
|
+
sage: bool(b.sage() == a)
|
|
544
|
+
True
|
|
545
|
+
|
|
546
|
+
It also works with more than one variable. Note the preferred
|
|
547
|
+
syntax ``function('f')(x, y)`` to create a general symbolic
|
|
548
|
+
function of more than one variable::
|
|
549
|
+
|
|
550
|
+
sage: x, y = var('x y')
|
|
551
|
+
sage: f = function('f')(x, y)
|
|
552
|
+
sage: f_x = f.diff(x); f_x
|
|
553
|
+
diff(f(x, y), x)
|
|
554
|
+
sage: maxima(f_x)
|
|
555
|
+
'diff('f(_SAGE_VAR_x,_SAGE_VAR_y),_SAGE_VAR_x,1)
|
|
556
|
+
|
|
557
|
+
::
|
|
558
|
+
|
|
559
|
+
sage: a = f_x.subs(x=4); a
|
|
560
|
+
D[0](f)(4, y)
|
|
561
|
+
sage: b = maxima(a); b
|
|
562
|
+
%at('diff('f(_SAGE_VAR__symbol0,_SAGE_VAR_y),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4)
|
|
563
|
+
sage: bool(b.sage() == a)
|
|
564
|
+
True
|
|
565
|
+
|
|
566
|
+
::
|
|
567
|
+
|
|
568
|
+
sage: a = f_x.subs(x=4).subs(y=8); a
|
|
569
|
+
D[0](f)(4, 8)
|
|
570
|
+
sage: b = maxima(a); b
|
|
571
|
+
%at('diff('f(_SAGE_VAR__symbol0,8),_SAGE_VAR__symbol0,1), _SAGE_VAR__symbol0 = 4)
|
|
572
|
+
sage: bool(b.sage() == a)
|
|
573
|
+
True
|
|
574
|
+
|
|
575
|
+
Test a special case (:issue:`16697`)::
|
|
576
|
+
|
|
577
|
+
sage: x,y = var('x,y')
|
|
578
|
+
sage: (gamma_inc(x,y).diff(x))
|
|
579
|
+
diff(gamma(x, y), x)
|
|
580
|
+
sage: (gamma_inc(x,x+1).diff(x)).simplify()
|
|
581
|
+
-(x + 1)^(x - 1)*e^(-x - 1) + D[0](gamma)(x, x + 1)
|
|
582
|
+
"""
|
|
583
|
+
# This code should probably be moved into the interface
|
|
584
|
+
# object in a nice way.
|
|
585
|
+
if self.name_init != "_maxima_init_":
|
|
586
|
+
raise NotImplementedError
|
|
587
|
+
args = ex.operands()
|
|
588
|
+
if (not all(isinstance(v, Expression) and v.is_symbol() for v in args) or
|
|
589
|
+
len(args) != len(set(args))):
|
|
590
|
+
# An evaluated derivative of the form f'(1) is not a
|
|
591
|
+
# symbolic variable, yet we would like to treat it like
|
|
592
|
+
# one. So, we replace the argument `1` with a temporary
|
|
593
|
+
# variable e.g. `_symbol0` and then evaluate the
|
|
594
|
+
# derivative f'(_symbol0) symbolically at _symbol0=1. See
|
|
595
|
+
# trac #12796. Note that we cannot use SR.temp_var here
|
|
596
|
+
# since two conversions of the same expression have to be
|
|
597
|
+
# equal.
|
|
598
|
+
temp_args = [SR.symbol("_symbol%s" % i) for i in range(len(args))]
|
|
599
|
+
f = operator.function()(*temp_args)
|
|
600
|
+
params = operator.parameter_set()
|
|
601
|
+
params = ["%s, %s" % (temp_args[i]._maxima_init_(), params.count(i)) for i in set(params)]
|
|
602
|
+
subs = ["%s = %s" % (t._maxima_init_(), a._maxima_init_())
|
|
603
|
+
for t, a in zip(temp_args, args)]
|
|
604
|
+
outstr = "at(diff(%s, %s), [%s])" % (f._maxima_init_(),
|
|
605
|
+
", ".join(params),
|
|
606
|
+
", ".join(subs))
|
|
607
|
+
else:
|
|
608
|
+
f = operator.function()(*args)
|
|
609
|
+
params = operator.parameter_set()
|
|
610
|
+
params = ["%s, %s" % (args[i]._maxima_init_(), params.count(i))
|
|
611
|
+
for i in set(params)]
|
|
612
|
+
outstr = "diff(%s, %s)" % (f._maxima_init_(),
|
|
613
|
+
", ".join(params))
|
|
614
|
+
return outstr
|
|
615
|
+
|
|
616
|
+
def arithmetic(self, ex, operator):
|
|
617
|
+
"""
|
|
618
|
+
EXAMPLES::
|
|
619
|
+
|
|
620
|
+
sage: import operator
|
|
621
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
622
|
+
sage: m = InterfaceInit(maxima)
|
|
623
|
+
sage: m.arithmetic(x+2, sage.symbolic.operators.add_vararg)
|
|
624
|
+
'(_SAGE_VAR_x)+(2)'
|
|
625
|
+
"""
|
|
626
|
+
args = ["(%s)" % self(op) for op in ex.operands()]
|
|
627
|
+
return arithmetic_operators[operator].join(args)
|
|
628
|
+
|
|
629
|
+
def composition(self, ex, operator):
|
|
630
|
+
"""
|
|
631
|
+
EXAMPLES::
|
|
632
|
+
|
|
633
|
+
sage: from sage.symbolic.expression_conversions import InterfaceInit
|
|
634
|
+
sage: m = InterfaceInit(maxima)
|
|
635
|
+
sage: m.composition(sin(x), sin)
|
|
636
|
+
'sin(_SAGE_VAR_x)'
|
|
637
|
+
sage: m.composition(ceil(x), ceil)
|
|
638
|
+
'ceiling(_SAGE_VAR_x)'
|
|
639
|
+
|
|
640
|
+
sage: m = InterfaceInit(mathematica)
|
|
641
|
+
sage: m.composition(sin(x), sin)
|
|
642
|
+
'Sin[x]'
|
|
643
|
+
"""
|
|
644
|
+
ops = ex.operands()
|
|
645
|
+
# FIXME: consider stripping pyobjects() in ops
|
|
646
|
+
if hasattr(operator, self.name_init + "evaled_"):
|
|
647
|
+
return getattr(operator, self.name_init + "evaled_")(*ops)
|
|
648
|
+
else:
|
|
649
|
+
ops = [self(_) for _ in ops]
|
|
650
|
+
try:
|
|
651
|
+
op = getattr(operator, self.name_init)()
|
|
652
|
+
except (TypeError, AttributeError):
|
|
653
|
+
op = repr(operator)
|
|
654
|
+
|
|
655
|
+
return self.interface._function_call_string(op, ops, [])
|
|
656
|
+
|
|
657
|
+
|
|
658
|
+
##############
|
|
659
|
+
# Polynomial #
|
|
660
|
+
##############
|
|
661
|
+
class PolynomialConverter(Converter):
|
|
662
|
+
def __init__(self, ex, base_ring=None, ring=None):
|
|
663
|
+
"""
|
|
664
|
+
A converter from symbolic expressions to polynomials.
|
|
665
|
+
|
|
666
|
+
See :func:`polynomial` for details.
|
|
667
|
+
|
|
668
|
+
EXAMPLES::
|
|
669
|
+
|
|
670
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
671
|
+
sage: x, y = var('x,y')
|
|
672
|
+
sage: p = PolynomialConverter(x+y, base_ring=QQ)
|
|
673
|
+
sage: p.base_ring
|
|
674
|
+
Rational Field
|
|
675
|
+
sage: p.ring
|
|
676
|
+
Multivariate Polynomial Ring in x, y over Rational Field
|
|
677
|
+
|
|
678
|
+
sage: p = PolynomialConverter(x, base_ring=QQ)
|
|
679
|
+
sage: p.base_ring
|
|
680
|
+
Rational Field
|
|
681
|
+
sage: p.ring
|
|
682
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
683
|
+
|
|
684
|
+
sage: p = PolynomialConverter(x, ring=QQ['x,y'])
|
|
685
|
+
sage: p.base_ring
|
|
686
|
+
Rational Field
|
|
687
|
+
sage: p.ring
|
|
688
|
+
Multivariate Polynomial Ring in x, y over Rational Field
|
|
689
|
+
|
|
690
|
+
sage: p = PolynomialConverter(x+y, ring=QQ['x'])
|
|
691
|
+
Traceback (most recent call last):
|
|
692
|
+
...
|
|
693
|
+
TypeError: y is not a variable of Univariate Polynomial Ring in x over Rational Field
|
|
694
|
+
|
|
695
|
+
TESTS::
|
|
696
|
+
|
|
697
|
+
sage: t, x, z = SR.var('t,x,z')
|
|
698
|
+
sage: QQ[i]['x,y,z,t'](4*I*t + 2*x -12*z + 2) # needs sage.rings.number_field
|
|
699
|
+
2*x - 12*z + (4*I)*t + 2
|
|
700
|
+
"""
|
|
701
|
+
if not (ring is None or base_ring is None):
|
|
702
|
+
raise TypeError("either base_ring or ring must be specified, but not both")
|
|
703
|
+
self.ex = ex
|
|
704
|
+
|
|
705
|
+
if ring is not None:
|
|
706
|
+
base_ring = ring.base_ring()
|
|
707
|
+
self.varnames = ring.variable_names_recursive()
|
|
708
|
+
for v in ex.variables():
|
|
709
|
+
if repr(v) not in self.varnames and v not in base_ring:
|
|
710
|
+
raise TypeError("%s is not a variable of %s" % (v, ring))
|
|
711
|
+
self.ring = ring
|
|
712
|
+
self.base_ring = base_ring
|
|
713
|
+
elif base_ring is not None:
|
|
714
|
+
self.base_ring = base_ring
|
|
715
|
+
vars = self.ex.variables()
|
|
716
|
+
if len(vars) == 0:
|
|
717
|
+
vars = ['x']
|
|
718
|
+
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
|
|
719
|
+
self.ring = PolynomialRing(self.base_ring, names=vars)
|
|
720
|
+
self.varnames = self.ring.variable_names()
|
|
721
|
+
else:
|
|
722
|
+
raise TypeError("either a ring or base ring must be specified")
|
|
723
|
+
|
|
724
|
+
def symbol(self, ex):
|
|
725
|
+
"""
|
|
726
|
+
Return a variable in the polynomial ring.
|
|
727
|
+
|
|
728
|
+
EXAMPLES::
|
|
729
|
+
|
|
730
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
731
|
+
sage: p = PolynomialConverter(x, base_ring=QQ)
|
|
732
|
+
sage: p.symbol(x)
|
|
733
|
+
x
|
|
734
|
+
sage: _.parent()
|
|
735
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
736
|
+
sage: y = var('y')
|
|
737
|
+
sage: p = PolynomialConverter(x*y, ring=SR['x'])
|
|
738
|
+
sage: p.symbol(y)
|
|
739
|
+
y
|
|
740
|
+
"""
|
|
741
|
+
try:
|
|
742
|
+
# The symbol is one of the polynomial generators
|
|
743
|
+
return self.ring(repr(ex))
|
|
744
|
+
except TypeError:
|
|
745
|
+
# The symbol should go into the base ring
|
|
746
|
+
return self.base_ring(repr(ex))
|
|
747
|
+
|
|
748
|
+
def pyobject(self, ex, obj):
|
|
749
|
+
"""
|
|
750
|
+
EXAMPLES::
|
|
751
|
+
|
|
752
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
753
|
+
sage: p = PolynomialConverter(x, base_ring=QQ)
|
|
754
|
+
sage: f = SR(2)
|
|
755
|
+
sage: p.pyobject(f, f.pyobject())
|
|
756
|
+
2
|
|
757
|
+
sage: _.parent()
|
|
758
|
+
Rational Field
|
|
759
|
+
"""
|
|
760
|
+
return self.base_ring(obj)
|
|
761
|
+
|
|
762
|
+
def composition(self, ex, operator):
|
|
763
|
+
"""
|
|
764
|
+
EXAMPLES::
|
|
765
|
+
|
|
766
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
767
|
+
sage: a = sin(2)
|
|
768
|
+
sage: p = PolynomialConverter(a*x, base_ring=RR)
|
|
769
|
+
sage: p.composition(a, a.operator())
|
|
770
|
+
0.909297426825682
|
|
771
|
+
"""
|
|
772
|
+
return self.base_ring(ex)
|
|
773
|
+
|
|
774
|
+
def relation(self, ex, op):
|
|
775
|
+
"""
|
|
776
|
+
EXAMPLES::
|
|
777
|
+
|
|
778
|
+
sage: import operator
|
|
779
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
780
|
+
|
|
781
|
+
sage: x, y = var('x, y')
|
|
782
|
+
sage: p = PolynomialConverter(x, base_ring=RR)
|
|
783
|
+
|
|
784
|
+
sage: p.relation(x==3, operator.eq)
|
|
785
|
+
x - 3.00000000000000
|
|
786
|
+
sage: p.relation(x==3, operator.lt)
|
|
787
|
+
Traceback (most recent call last):
|
|
788
|
+
...
|
|
789
|
+
ValueError: Unable to represent as a polynomial
|
|
790
|
+
|
|
791
|
+
sage: p = PolynomialConverter(x - y, base_ring=QQ)
|
|
792
|
+
sage: p.relation(x^2 - y^3 + 1 == x^3, operator.eq)
|
|
793
|
+
-x^3 - y^3 + x^2 + 1
|
|
794
|
+
"""
|
|
795
|
+
import operator
|
|
796
|
+
if op == operator.eq:
|
|
797
|
+
return self(ex.lhs()) - self(ex.rhs())
|
|
798
|
+
raise ValueError("Unable to represent as a polynomial")
|
|
799
|
+
|
|
800
|
+
def arithmetic(self, ex, operator):
|
|
801
|
+
"""
|
|
802
|
+
EXAMPLES::
|
|
803
|
+
|
|
804
|
+
sage: import operator
|
|
805
|
+
sage: from sage.symbolic.expression_conversions import PolynomialConverter
|
|
806
|
+
|
|
807
|
+
sage: x, y = var('x, y')
|
|
808
|
+
sage: p = PolynomialConverter(x, base_ring=RR)
|
|
809
|
+
sage: p.arithmetic(pi+e, operator.add)
|
|
810
|
+
5.85987448204884
|
|
811
|
+
sage: p.arithmetic(x^2, operator.pow)
|
|
812
|
+
x^2
|
|
813
|
+
|
|
814
|
+
sage: p = PolynomialConverter(x+y, base_ring=RR)
|
|
815
|
+
sage: p.arithmetic(x*y+y^2, operator.add)
|
|
816
|
+
x*y + y^2
|
|
817
|
+
|
|
818
|
+
sage: p = PolynomialConverter(y^(3/2), ring=SR['x'])
|
|
819
|
+
sage: p.arithmetic(y^(3/2), operator.pow)
|
|
820
|
+
y^(3/2)
|
|
821
|
+
sage: _.parent()
|
|
822
|
+
Symbolic Ring
|
|
823
|
+
"""
|
|
824
|
+
if not any(repr(v) in self.varnames for v in ex.variables()):
|
|
825
|
+
return self.base_ring(ex)
|
|
826
|
+
elif operator == pow:
|
|
827
|
+
from sage.rings.integer import Integer
|
|
828
|
+
base, exp = ex.operands()
|
|
829
|
+
return self(base)**Integer(exp)
|
|
830
|
+
if operator == add_vararg:
|
|
831
|
+
operator = add
|
|
832
|
+
elif operator == mul_vararg:
|
|
833
|
+
operator = mul
|
|
834
|
+
ops = [self(a) for a in ex.operands()]
|
|
835
|
+
return reduce(operator, ops)
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def polynomial(ex, base_ring=None, ring=None):
|
|
839
|
+
"""
|
|
840
|
+
Return a polynomial from the symbolic expression ``ex``.
|
|
841
|
+
|
|
842
|
+
INPUT:
|
|
843
|
+
|
|
844
|
+
- ``ex`` -- a symbolic expression
|
|
845
|
+
|
|
846
|
+
- ``base_ring``, ``ring`` -- either a
|
|
847
|
+
``base_ring`` or a polynomial ``ring`` can be
|
|
848
|
+
specified for the parent of result.
|
|
849
|
+
If just a ``base_ring`` is given, then the variables
|
|
850
|
+
of the ``base_ring`` will be the variables of the expression ``ex``.
|
|
851
|
+
|
|
852
|
+
OUTPUT: a polynomial
|
|
853
|
+
|
|
854
|
+
EXAMPLES::
|
|
855
|
+
|
|
856
|
+
sage: from sage.symbolic.expression_conversions import polynomial
|
|
857
|
+
sage: f = x^2 + 2
|
|
858
|
+
sage: polynomial(f, base_ring=QQ)
|
|
859
|
+
x^2 + 2
|
|
860
|
+
sage: _.parent()
|
|
861
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
862
|
+
|
|
863
|
+
sage: polynomial(f, ring=QQ['x,y'])
|
|
864
|
+
x^2 + 2
|
|
865
|
+
sage: _.parent()
|
|
866
|
+
Multivariate Polynomial Ring in x, y over Rational Field
|
|
867
|
+
|
|
868
|
+
sage: x, y = var('x, y')
|
|
869
|
+
sage: polynomial(x + y^2, ring=QQ['x,y'])
|
|
870
|
+
y^2 + x
|
|
871
|
+
sage: _.parent()
|
|
872
|
+
Multivariate Polynomial Ring in x, y over Rational Field
|
|
873
|
+
|
|
874
|
+
sage: s,t = var('s,t')
|
|
875
|
+
sage: expr = t^2-2*s*t+1
|
|
876
|
+
sage: expr.polynomial(None,ring=SR['t'])
|
|
877
|
+
t^2 - 2*s*t + 1
|
|
878
|
+
sage: _.parent()
|
|
879
|
+
Univariate Polynomial Ring in t over Symbolic Ring
|
|
880
|
+
|
|
881
|
+
sage: polynomial(x*y, ring=SR['x'])
|
|
882
|
+
y*x
|
|
883
|
+
|
|
884
|
+
sage: polynomial(y - sqrt(x), ring=SR['y'])
|
|
885
|
+
y - sqrt(x)
|
|
886
|
+
sage: _.list()
|
|
887
|
+
[-sqrt(x), 1]
|
|
888
|
+
|
|
889
|
+
The polynomials can have arbitrary (constant) coefficients so long as
|
|
890
|
+
they coerce into the base ring::
|
|
891
|
+
|
|
892
|
+
sage: polynomial(2^sin(2)*x^2 + exp(3), base_ring=RR)
|
|
893
|
+
1.87813065119873*x^2 + 20.0855369231877
|
|
894
|
+
"""
|
|
895
|
+
converter = PolynomialConverter(ex, base_ring=base_ring, ring=ring)
|
|
896
|
+
res = converter()
|
|
897
|
+
return converter.ring(res)
|
|
898
|
+
|
|
899
|
+
|
|
900
|
+
class LaurentPolynomialConverter(PolynomialConverter):
|
|
901
|
+
def __init__(self, ex, base_ring=None, ring=None):
|
|
902
|
+
"""
|
|
903
|
+
A converter from symbolic expressions to Laurent polynomials.
|
|
904
|
+
|
|
905
|
+
See :func:`laurent_polynomial` for details.
|
|
906
|
+
|
|
907
|
+
TESTS::
|
|
908
|
+
|
|
909
|
+
sage: from sage.symbolic.expression_conversions import LaurentPolynomialConverter
|
|
910
|
+
sage: x, y = var('x,y')
|
|
911
|
+
sage: p = LaurentPolynomialConverter(x+1/y, base_ring=QQ)
|
|
912
|
+
sage: p.base_ring
|
|
913
|
+
Rational Field
|
|
914
|
+
sage: p.ring
|
|
915
|
+
Multivariate Laurent Polynomial Ring in x, y over Rational Field
|
|
916
|
+
"""
|
|
917
|
+
super().__init__(ex, base_ring, ring)
|
|
918
|
+
|
|
919
|
+
if ring is None and base_ring is not None:
|
|
920
|
+
from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing
|
|
921
|
+
self.ring = LaurentPolynomialRing(self.base_ring,
|
|
922
|
+
names=self.varnames)
|
|
923
|
+
|
|
924
|
+
|
|
925
|
+
def laurent_polynomial(ex, base_ring=None, ring=None):
|
|
926
|
+
"""
|
|
927
|
+
Return a Laurent polynomial from the symbolic expression ``ex``.
|
|
928
|
+
|
|
929
|
+
INPUT:
|
|
930
|
+
|
|
931
|
+
- ``ex`` -- a symbolic expression
|
|
932
|
+
|
|
933
|
+
- ``base_ring``, ``ring`` -- either a
|
|
934
|
+
``base_ring`` or a Laurent polynomial ``ring`` can be
|
|
935
|
+
specified for the parent of result.
|
|
936
|
+
If just a ``base_ring`` is given, then the variables
|
|
937
|
+
of the ``base_ring`` will be the variables of the expression ``ex``.
|
|
938
|
+
|
|
939
|
+
OUTPUT: a Laurent polynomial
|
|
940
|
+
|
|
941
|
+
EXAMPLES::
|
|
942
|
+
|
|
943
|
+
sage: from sage.symbolic.expression_conversions import laurent_polynomial
|
|
944
|
+
sage: f = x^2 + 2/x
|
|
945
|
+
sage: laurent_polynomial(f, base_ring=QQ)
|
|
946
|
+
2*x^-1 + x^2
|
|
947
|
+
sage: _.parent()
|
|
948
|
+
Univariate Laurent Polynomial Ring in x over Rational Field
|
|
949
|
+
|
|
950
|
+
sage: laurent_polynomial(f, ring=LaurentPolynomialRing(QQ, 'x, y'))
|
|
951
|
+
x^2 + 2*x^-1
|
|
952
|
+
sage: _.parent()
|
|
953
|
+
Multivariate Laurent Polynomial Ring in x, y over Rational Field
|
|
954
|
+
|
|
955
|
+
sage: x, y = var('x, y')
|
|
956
|
+
sage: laurent_polynomial(x + 1/y^2, ring=LaurentPolynomialRing(QQ, 'x, y'))
|
|
957
|
+
x + y^-2
|
|
958
|
+
sage: _.parent()
|
|
959
|
+
Multivariate Laurent Polynomial Ring in x, y over Rational Field
|
|
960
|
+
"""
|
|
961
|
+
converter = LaurentPolynomialConverter(ex, base_ring=base_ring, ring=ring)
|
|
962
|
+
res = converter()
|
|
963
|
+
return converter.ring(res)
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
#################
|
|
967
|
+
# Fast Callable #
|
|
968
|
+
#################
|
|
969
|
+
|
|
970
|
+
class FastCallableConverter(Converter):
|
|
971
|
+
def __init__(self, ex, etb):
|
|
972
|
+
"""
|
|
973
|
+
EXAMPLES::
|
|
974
|
+
|
|
975
|
+
sage: from sage.symbolic.expression_conversions import FastCallableConverter
|
|
976
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
977
|
+
sage: etb = ExpressionTreeBuilder(vars=['x'])
|
|
978
|
+
sage: f = FastCallableConverter(x+2, etb)
|
|
979
|
+
sage: f.ex
|
|
980
|
+
x + 2
|
|
981
|
+
sage: f.etb
|
|
982
|
+
<sage.ext.fast_callable.ExpressionTreeBuilder object at 0x...>
|
|
983
|
+
sage: f.use_fake_div
|
|
984
|
+
True
|
|
985
|
+
"""
|
|
986
|
+
self.ex = ex
|
|
987
|
+
self.etb = etb
|
|
988
|
+
Converter.__init__(self, use_fake_div=True)
|
|
989
|
+
|
|
990
|
+
def pyobject(self, ex, obj):
|
|
991
|
+
r"""
|
|
992
|
+
EXAMPLES::
|
|
993
|
+
|
|
994
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
995
|
+
sage: etb = ExpressionTreeBuilder(vars=['x'])
|
|
996
|
+
sage: pi._fast_callable_(etb)
|
|
997
|
+
pi
|
|
998
|
+
sage: etb = ExpressionTreeBuilder(vars=['x'], domain=RDF)
|
|
999
|
+
sage: pi._fast_callable_(etb)
|
|
1000
|
+
3.141592653589793
|
|
1001
|
+
"""
|
|
1002
|
+
from sage.symbolic.constants import Constant
|
|
1003
|
+
if isinstance(obj, Constant):
|
|
1004
|
+
obj = obj.expression()
|
|
1005
|
+
return self.etb.constant(obj)
|
|
1006
|
+
|
|
1007
|
+
def relation(self, ex, operator):
|
|
1008
|
+
"""
|
|
1009
|
+
EXAMPLES::
|
|
1010
|
+
|
|
1011
|
+
sage: ff = fast_callable(x == 2, vars=['x'])
|
|
1012
|
+
sage: ff(2)
|
|
1013
|
+
0
|
|
1014
|
+
sage: ff(4)
|
|
1015
|
+
2
|
|
1016
|
+
sage: ff = fast_callable(x < 2, vars=['x'])
|
|
1017
|
+
Traceback (most recent call last):
|
|
1018
|
+
...
|
|
1019
|
+
NotImplementedError
|
|
1020
|
+
"""
|
|
1021
|
+
if operator is not eq:
|
|
1022
|
+
raise NotImplementedError
|
|
1023
|
+
return self(ex.lhs() - ex.rhs())
|
|
1024
|
+
|
|
1025
|
+
def arithmetic(self, ex, operator):
|
|
1026
|
+
r"""
|
|
1027
|
+
EXAMPLES::
|
|
1028
|
+
|
|
1029
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
1030
|
+
sage: etb = ExpressionTreeBuilder(vars=['x','y'])
|
|
1031
|
+
sage: var('x,y')
|
|
1032
|
+
(x, y)
|
|
1033
|
+
sage: (x+y)._fast_callable_(etb)
|
|
1034
|
+
add(v_0, v_1)
|
|
1035
|
+
sage: (-x)._fast_callable_(etb)
|
|
1036
|
+
neg(v_0)
|
|
1037
|
+
sage: (x+y+x^2)._fast_callable_(etb)
|
|
1038
|
+
add(add(ipow(v_0, 2), v_0), v_1)
|
|
1039
|
+
|
|
1040
|
+
TESTS:
|
|
1041
|
+
|
|
1042
|
+
Check if rational functions with numerator 1 can
|
|
1043
|
+
be converted. (:issue:`8056`)::
|
|
1044
|
+
|
|
1045
|
+
sage: (1/pi/x)._fast_callable_(etb)
|
|
1046
|
+
div(1, mul(pi, v_0))
|
|
1047
|
+
|
|
1048
|
+
sage: etb = ExpressionTreeBuilder(vars=['x'], domain=RDF)
|
|
1049
|
+
sage: (x^7)._fast_callable_(etb)
|
|
1050
|
+
ipow(v_0, 7)
|
|
1051
|
+
sage: f(x) = 1/pi/x; plot(f,2,3) # needs sage.plot
|
|
1052
|
+
Graphics object consisting of 1 graphics primitive
|
|
1053
|
+
"""
|
|
1054
|
+
# This used to convert the operands first. Doing it this way
|
|
1055
|
+
# instead gives a chance to notice powers with an integer
|
|
1056
|
+
# exponent before the exponent gets (potentially) converted
|
|
1057
|
+
# to another type.
|
|
1058
|
+
operands = ex.operands()
|
|
1059
|
+
if operator is pow:
|
|
1060
|
+
exponent = operands[1]
|
|
1061
|
+
if exponent == -1:
|
|
1062
|
+
return self.etb.call(truediv, 1, operands[0])
|
|
1063
|
+
elif exponent == 0.5:
|
|
1064
|
+
from sage.misc.functional import sqrt
|
|
1065
|
+
return self.etb.call(sqrt, operands[0])
|
|
1066
|
+
elif exponent == -0.5:
|
|
1067
|
+
from sage.misc.functional import sqrt
|
|
1068
|
+
return self.etb.call(truediv, 1, self.etb.call(sqrt, operands[0]))
|
|
1069
|
+
elif operator is neg:
|
|
1070
|
+
return self.etb.call(operator, operands[0])
|
|
1071
|
+
if operator == add_vararg:
|
|
1072
|
+
operator = add
|
|
1073
|
+
elif operator == mul_vararg:
|
|
1074
|
+
operator = mul
|
|
1075
|
+
return reduce(lambda x, y: self.etb.call(operator, x, y), operands)
|
|
1076
|
+
|
|
1077
|
+
def symbol(self, ex):
|
|
1078
|
+
r"""
|
|
1079
|
+
Given an ExpressionTreeBuilder, return an Expression representing
|
|
1080
|
+
this value.
|
|
1081
|
+
|
|
1082
|
+
EXAMPLES::
|
|
1083
|
+
|
|
1084
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
1085
|
+
sage: etb = ExpressionTreeBuilder(vars=['x','y'])
|
|
1086
|
+
sage: x, y, z = var('x,y,z')
|
|
1087
|
+
sage: x._fast_callable_(etb)
|
|
1088
|
+
v_0
|
|
1089
|
+
sage: y._fast_callable_(etb)
|
|
1090
|
+
v_1
|
|
1091
|
+
sage: z._fast_callable_(etb)
|
|
1092
|
+
Traceback (most recent call last):
|
|
1093
|
+
...
|
|
1094
|
+
ValueError: Variable 'z' not found...
|
|
1095
|
+
"""
|
|
1096
|
+
return self.etb.var(SR(ex))
|
|
1097
|
+
|
|
1098
|
+
def composition(self, ex, function):
|
|
1099
|
+
r"""
|
|
1100
|
+
Given an ExpressionTreeBuilder, return an Expression representing
|
|
1101
|
+
this value.
|
|
1102
|
+
|
|
1103
|
+
EXAMPLES::
|
|
1104
|
+
|
|
1105
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
1106
|
+
sage: etb = ExpressionTreeBuilder(vars=['x','y'])
|
|
1107
|
+
sage: x,y = var('x,y')
|
|
1108
|
+
sage: sin(sqrt(x+y))._fast_callable_(etb)
|
|
1109
|
+
sin(sqrt(add(v_0, v_1)))
|
|
1110
|
+
sage: arctan2(x,y)._fast_callable_(etb)
|
|
1111
|
+
{arctan2}(v_0, v_1)
|
|
1112
|
+
"""
|
|
1113
|
+
return self.etb.call(function, *ex.operands())
|
|
1114
|
+
|
|
1115
|
+
def tuple(self, ex):
|
|
1116
|
+
r"""
|
|
1117
|
+
Given a symbolic tuple, return its elements as a Python list.
|
|
1118
|
+
|
|
1119
|
+
EXAMPLES::
|
|
1120
|
+
|
|
1121
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
1122
|
+
sage: etb = ExpressionTreeBuilder(vars=['x'])
|
|
1123
|
+
sage: SR._force_pyobject((2, 3, x^2))._fast_callable_(etb)
|
|
1124
|
+
[2, 3, x^2]
|
|
1125
|
+
"""
|
|
1126
|
+
return ex.operands()
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
def fast_callable(ex, etb):
|
|
1130
|
+
"""
|
|
1131
|
+
Given an ExpressionTreeBuilder *etb*, return an Expression representing
|
|
1132
|
+
the symbolic expression *ex*.
|
|
1133
|
+
|
|
1134
|
+
EXAMPLES::
|
|
1135
|
+
|
|
1136
|
+
sage: from sage.ext.fast_callable import ExpressionTreeBuilder
|
|
1137
|
+
sage: etb = ExpressionTreeBuilder(vars=['x','y'])
|
|
1138
|
+
sage: x,y = var('x,y')
|
|
1139
|
+
sage: f = y+2*x^2
|
|
1140
|
+
sage: f._fast_callable_(etb)
|
|
1141
|
+
add(mul(ipow(v_0, 2), 2), v_1)
|
|
1142
|
+
|
|
1143
|
+
sage: f = (2*x^3+2*x-1)/((x-2)*(x+1))
|
|
1144
|
+
sage: f._fast_callable_(etb)
|
|
1145
|
+
div(add(add(mul(ipow(v_0, 3), 2), mul(v_0, 2)), -1), mul(add(v_0, 1), add(v_0, -2)))
|
|
1146
|
+
"""
|
|
1147
|
+
return FastCallableConverter(ex, etb)()
|
|
1148
|
+
|
|
1149
|
+
|
|
1150
|
+
class RingConverter(Converter):
|
|
1151
|
+
def __init__(self, R, subs_dict=None):
|
|
1152
|
+
"""
|
|
1153
|
+
A class to convert expressions to other rings.
|
|
1154
|
+
|
|
1155
|
+
EXAMPLES::
|
|
1156
|
+
|
|
1157
|
+
sage: from sage.symbolic.expression_conversions import RingConverter
|
|
1158
|
+
sage: R = RingConverter(RIF, subs_dict={x:2})
|
|
1159
|
+
sage: R.ring
|
|
1160
|
+
Real Interval Field with 53 bits of precision
|
|
1161
|
+
sage: R.subs_dict
|
|
1162
|
+
{x: 2}
|
|
1163
|
+
sage: R(pi+e)
|
|
1164
|
+
5.85987448204884?
|
|
1165
|
+
sage: loads(dumps(R))
|
|
1166
|
+
<sage.symbolic.expression_conversions.RingConverter object at 0x...>
|
|
1167
|
+
"""
|
|
1168
|
+
self.subs_dict = {} if subs_dict is None else subs_dict
|
|
1169
|
+
self.ring = R
|
|
1170
|
+
|
|
1171
|
+
def symbol(self, ex):
|
|
1172
|
+
"""
|
|
1173
|
+
All symbols appearing in the expression must either appear in
|
|
1174
|
+
*subs_dict* or be convertible by the ring's element
|
|
1175
|
+
constructor in order for the conversion to be successful.
|
|
1176
|
+
|
|
1177
|
+
EXAMPLES::
|
|
1178
|
+
|
|
1179
|
+
sage: from sage.symbolic.expression_conversions import RingConverter
|
|
1180
|
+
sage: R = RingConverter(RIF, subs_dict={x:2})
|
|
1181
|
+
sage: R(x+pi)
|
|
1182
|
+
5.141592653589794?
|
|
1183
|
+
|
|
1184
|
+
sage: R = RingConverter(RIF)
|
|
1185
|
+
sage: R(x+pi)
|
|
1186
|
+
Traceback (most recent call last):
|
|
1187
|
+
...
|
|
1188
|
+
TypeError: unable to simplify to a real interval approximation
|
|
1189
|
+
|
|
1190
|
+
sage: R = RingConverter(QQ['x'])
|
|
1191
|
+
sage: R(x^2+x)
|
|
1192
|
+
x^2 + x
|
|
1193
|
+
sage: R(x^2+x).parent()
|
|
1194
|
+
Univariate Polynomial Ring in x over Rational Field
|
|
1195
|
+
"""
|
|
1196
|
+
try:
|
|
1197
|
+
return self.ring(self.subs_dict[ex])
|
|
1198
|
+
except KeyError:
|
|
1199
|
+
return self.ring(ex)
|
|
1200
|
+
|
|
1201
|
+
def pyobject(self, ex, obj):
|
|
1202
|
+
"""
|
|
1203
|
+
EXAMPLES::
|
|
1204
|
+
|
|
1205
|
+
sage: from sage.symbolic.expression_conversions import RingConverter
|
|
1206
|
+
sage: R = RingConverter(RIF)
|
|
1207
|
+
sage: R(SR(5/2))
|
|
1208
|
+
2.5000000000000000?
|
|
1209
|
+
"""
|
|
1210
|
+
return self.ring(obj)
|
|
1211
|
+
|
|
1212
|
+
def arithmetic(self, ex, operator):
|
|
1213
|
+
"""
|
|
1214
|
+
EXAMPLES::
|
|
1215
|
+
|
|
1216
|
+
sage: from sage.symbolic.expression_conversions import RingConverter
|
|
1217
|
+
sage: P.<z> = ZZ[]
|
|
1218
|
+
sage: R = RingConverter(P, subs_dict={x:z})
|
|
1219
|
+
sage: a = 2*x^2 + x + 3
|
|
1220
|
+
sage: R(a)
|
|
1221
|
+
2*z^2 + z + 3
|
|
1222
|
+
"""
|
|
1223
|
+
if operator not in [pow, add_vararg, mul_vararg]:
|
|
1224
|
+
raise TypeError
|
|
1225
|
+
|
|
1226
|
+
operands = ex.operands()
|
|
1227
|
+
if operator is pow:
|
|
1228
|
+
from sage.rings.integer import Integer
|
|
1229
|
+
from sage.rings.rational import Rational
|
|
1230
|
+
base, expt = operands
|
|
1231
|
+
|
|
1232
|
+
if expt == Rational((1, 2)):
|
|
1233
|
+
from sage.misc.functional import sqrt
|
|
1234
|
+
return sqrt(self(base))
|
|
1235
|
+
try:
|
|
1236
|
+
expt = Integer(expt)
|
|
1237
|
+
except TypeError:
|
|
1238
|
+
pass
|
|
1239
|
+
|
|
1240
|
+
base = self(base)
|
|
1241
|
+
return base ** expt
|
|
1242
|
+
|
|
1243
|
+
if operator == add_vararg:
|
|
1244
|
+
operator = add
|
|
1245
|
+
elif operator == mul_vararg:
|
|
1246
|
+
operator = mul
|
|
1247
|
+
return reduce(operator, map(self, operands))
|
|
1248
|
+
|
|
1249
|
+
def composition(self, ex, operator):
|
|
1250
|
+
"""
|
|
1251
|
+
EXAMPLES::
|
|
1252
|
+
|
|
1253
|
+
sage: from sage.symbolic.expression_conversions import RingConverter
|
|
1254
|
+
sage: R = RingConverter(RIF)
|
|
1255
|
+
sage: R(cos(2))
|
|
1256
|
+
-0.4161468365471424?
|
|
1257
|
+
"""
|
|
1258
|
+
res = operator(*[self(op) for op in ex.operands()])
|
|
1259
|
+
if res.parent() is not self.ring:
|
|
1260
|
+
raise TypeError
|
|
1261
|
+
else:
|
|
1262
|
+
return res
|
|
1263
|
+
|
|
1264
|
+
|
|
1265
|
+
class ExpressionTreeWalker(Converter):
|
|
1266
|
+
def __init__(self, ex):
|
|
1267
|
+
"""
|
|
1268
|
+
A class that walks the tree. Mainly for subclassing.
|
|
1269
|
+
|
|
1270
|
+
EXAMPLES::
|
|
1271
|
+
|
|
1272
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1273
|
+
sage: from sage.symbolic.random_tests import random_expr
|
|
1274
|
+
sage: ex = sin(atan(0,hold=True)+hypergeometric((1,),(1,),x))
|
|
1275
|
+
sage: s = ExpressionTreeWalker(ex)
|
|
1276
|
+
sage: bool(s() == ex)
|
|
1277
|
+
True
|
|
1278
|
+
sage: set_random_seed(0) # random_expr is unstable
|
|
1279
|
+
sage: foo = random_expr(20, nvars=2)
|
|
1280
|
+
sage: s = ExpressionTreeWalker(foo)
|
|
1281
|
+
sage: bool(s() == foo)
|
|
1282
|
+
True
|
|
1283
|
+
"""
|
|
1284
|
+
self.ex = ex
|
|
1285
|
+
|
|
1286
|
+
def symbol(self, ex):
|
|
1287
|
+
"""
|
|
1288
|
+
EXAMPLES::
|
|
1289
|
+
|
|
1290
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1291
|
+
sage: s = ExpressionTreeWalker(x)
|
|
1292
|
+
sage: bool(s.symbol(x) == x)
|
|
1293
|
+
True
|
|
1294
|
+
"""
|
|
1295
|
+
return ex
|
|
1296
|
+
|
|
1297
|
+
def pyobject(self, ex, obj):
|
|
1298
|
+
"""
|
|
1299
|
+
EXAMPLES::
|
|
1300
|
+
|
|
1301
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1302
|
+
sage: f = SR(2)
|
|
1303
|
+
sage: s = ExpressionTreeWalker(f)
|
|
1304
|
+
sage: bool(s.pyobject(f, f.pyobject()) == f.pyobject())
|
|
1305
|
+
True
|
|
1306
|
+
"""
|
|
1307
|
+
return ex
|
|
1308
|
+
|
|
1309
|
+
def relation(self, ex, operator):
|
|
1310
|
+
"""
|
|
1311
|
+
EXAMPLES::
|
|
1312
|
+
|
|
1313
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1314
|
+
sage: foo = function('foo')
|
|
1315
|
+
sage: eq = foo(x) == x
|
|
1316
|
+
sage: s = ExpressionTreeWalker(eq)
|
|
1317
|
+
sage: s.relation(eq, eq.operator()) == eq
|
|
1318
|
+
True
|
|
1319
|
+
"""
|
|
1320
|
+
return operator(self(ex.lhs()), self(ex.rhs()))
|
|
1321
|
+
|
|
1322
|
+
def arithmetic(self, ex, operator):
|
|
1323
|
+
"""
|
|
1324
|
+
EXAMPLES::
|
|
1325
|
+
|
|
1326
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1327
|
+
sage: foo = function('foo')
|
|
1328
|
+
sage: f = x*foo(x) + pi/foo(x)
|
|
1329
|
+
sage: s = ExpressionTreeWalker(f)
|
|
1330
|
+
sage: bool(s.arithmetic(f, f.operator()) == f)
|
|
1331
|
+
True
|
|
1332
|
+
"""
|
|
1333
|
+
return reduce(operator, map(self, ex.operands()))
|
|
1334
|
+
|
|
1335
|
+
def composition(self, ex, operator):
|
|
1336
|
+
"""
|
|
1337
|
+
EXAMPLES::
|
|
1338
|
+
|
|
1339
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1340
|
+
sage: foo = function('foo')
|
|
1341
|
+
sage: f = foo(atan2(0, 0, hold=True))
|
|
1342
|
+
sage: s = ExpressionTreeWalker(f)
|
|
1343
|
+
sage: bool(s.composition(f, f.operator()) == f)
|
|
1344
|
+
True
|
|
1345
|
+
"""
|
|
1346
|
+
from sage.symbolic.function import Function
|
|
1347
|
+
if isinstance(operator, Function):
|
|
1348
|
+
return operator(*map(self, ex.operands()), hold=True)
|
|
1349
|
+
else:
|
|
1350
|
+
return operator(*map(self, ex.operands()))
|
|
1351
|
+
|
|
1352
|
+
def derivative(self, ex, operator):
|
|
1353
|
+
"""
|
|
1354
|
+
EXAMPLES::
|
|
1355
|
+
|
|
1356
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1357
|
+
sage: foo = function('foo')
|
|
1358
|
+
sage: f = foo(x).diff(x)
|
|
1359
|
+
sage: s = ExpressionTreeWalker(f)
|
|
1360
|
+
sage: bool(s.derivative(f, f.operator()) == f)
|
|
1361
|
+
True
|
|
1362
|
+
"""
|
|
1363
|
+
return operator(*map(self, ex.operands()))
|
|
1364
|
+
|
|
1365
|
+
def tuple(self, ex):
|
|
1366
|
+
"""
|
|
1367
|
+
EXAMPLES::
|
|
1368
|
+
|
|
1369
|
+
sage: from sage.symbolic.expression_conversions import ExpressionTreeWalker
|
|
1370
|
+
sage: foo = function('foo')
|
|
1371
|
+
sage: f = hypergeometric((1,2,3,),(x,),x)
|
|
1372
|
+
sage: s = ExpressionTreeWalker(f)
|
|
1373
|
+
sage: bool(s() == f)
|
|
1374
|
+
True
|
|
1375
|
+
"""
|
|
1376
|
+
return ex.operands()
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
class SubstituteFunction(ExpressionTreeWalker):
|
|
1380
|
+
def __init__(self, ex, *args):
|
|
1381
|
+
"""
|
|
1382
|
+
A class that walks the tree and replaces occurrences of a
|
|
1383
|
+
function with another.
|
|
1384
|
+
|
|
1385
|
+
EXAMPLES::
|
|
1386
|
+
|
|
1387
|
+
sage: from sage.symbolic.expression_conversions import SubstituteFunction
|
|
1388
|
+
sage: foo = function('foo'); bar = function('bar')
|
|
1389
|
+
sage: s = SubstituteFunction(foo(x), {foo: bar})
|
|
1390
|
+
sage: s(1/foo(foo(x)) + foo(2))
|
|
1391
|
+
1/bar(bar(x)) + bar(2)
|
|
1392
|
+
|
|
1393
|
+
TESTS:
|
|
1394
|
+
|
|
1395
|
+
Check that the old syntax still works::
|
|
1396
|
+
|
|
1397
|
+
sage: s = SubstituteFunction(foo(x), foo, bar)
|
|
1398
|
+
sage: s(1/foo(foo(x)) + foo(2))
|
|
1399
|
+
1/bar(bar(x)) + bar(2)
|
|
1400
|
+
"""
|
|
1401
|
+
if len(args) == 2:
|
|
1402
|
+
self.substitutions = {args[0]: args[1]}
|
|
1403
|
+
elif len(args) == 1:
|
|
1404
|
+
self.substitutions = args[0]
|
|
1405
|
+
else:
|
|
1406
|
+
raise TypeError('SubstituteFunction takes either one or two arguments.')
|
|
1407
|
+
self.ex = ex
|
|
1408
|
+
|
|
1409
|
+
def composition(self, ex, operator):
|
|
1410
|
+
"""
|
|
1411
|
+
EXAMPLES::
|
|
1412
|
+
|
|
1413
|
+
sage: from sage.symbolic.expression_conversions import SubstituteFunction
|
|
1414
|
+
sage: foo = function('foo'); bar = function('bar')
|
|
1415
|
+
sage: s = SubstituteFunction(foo(x), {foo: bar})
|
|
1416
|
+
sage: f = foo(x)
|
|
1417
|
+
sage: s.composition(f, f.operator())
|
|
1418
|
+
bar(x)
|
|
1419
|
+
sage: f = foo(foo(x))
|
|
1420
|
+
sage: s.composition(f, f.operator())
|
|
1421
|
+
bar(bar(x))
|
|
1422
|
+
sage: f = sin(foo(x))
|
|
1423
|
+
sage: s.composition(f, f.operator())
|
|
1424
|
+
sin(bar(x))
|
|
1425
|
+
sage: f = foo(sin(x))
|
|
1426
|
+
sage: s.composition(f, f.operator())
|
|
1427
|
+
bar(sin(x))
|
|
1428
|
+
"""
|
|
1429
|
+
new = self.substitutions.get(operator)
|
|
1430
|
+
if new is not None:
|
|
1431
|
+
return new(*[self(_) for _ in ex.operands()])
|
|
1432
|
+
else:
|
|
1433
|
+
return super().composition(ex, operator)
|
|
1434
|
+
|
|
1435
|
+
def derivative(self, ex, operator):
|
|
1436
|
+
"""
|
|
1437
|
+
EXAMPLES::
|
|
1438
|
+
|
|
1439
|
+
sage: from sage.symbolic.expression_conversions import SubstituteFunction
|
|
1440
|
+
sage: foo = function('foo'); bar = function('bar')
|
|
1441
|
+
sage: s = SubstituteFunction(foo(x), {foo: bar})
|
|
1442
|
+
sage: f = foo(x).diff(x)
|
|
1443
|
+
sage: s.derivative(f, f.operator())
|
|
1444
|
+
diff(bar(x), x)
|
|
1445
|
+
|
|
1446
|
+
TESTS:
|
|
1447
|
+
|
|
1448
|
+
We can substitute functions under a derivative operator,
|
|
1449
|
+
:issue:`12801`::
|
|
1450
|
+
|
|
1451
|
+
sage: f = function('f')
|
|
1452
|
+
sage: g = function('g')
|
|
1453
|
+
sage: f(g(x)).diff(x).substitute_function({g: sin})
|
|
1454
|
+
cos(x)*D[0](f)(sin(x))
|
|
1455
|
+
"""
|
|
1456
|
+
new = self.substitutions.get(operator.function())
|
|
1457
|
+
if new is not None:
|
|
1458
|
+
return operator.change_function(new)(*[self(_) for _ in ex.operands()])
|
|
1459
|
+
else:
|
|
1460
|
+
return operator(*[self(_) for _ in ex.operands()])
|
|
1461
|
+
|
|
1462
|
+
|
|
1463
|
+
class Exponentialize(ExpressionTreeWalker):
|
|
1464
|
+
# Implementation note: this code is executed once at first
|
|
1465
|
+
# reference in the code using it, therefore avoiding rebuilding
|
|
1466
|
+
# the same canned results dictionary at each call.
|
|
1467
|
+
from sage.calculus.var import function
|
|
1468
|
+
from sage.functions.hyperbolic import sinh, cosh, sech, csch, tanh, coth
|
|
1469
|
+
from sage.functions.trig import sin, cos, sec, csc, tan, cot
|
|
1470
|
+
from sage.rings.integer import Integer
|
|
1471
|
+
from sage.symbolic.constants import e, I
|
|
1472
|
+
from sage.symbolic.ring import SR
|
|
1473
|
+
half = Integer(1) / Integer(2)
|
|
1474
|
+
two = Integer(2)
|
|
1475
|
+
x = SR.var("x")
|
|
1476
|
+
CircDict = {
|
|
1477
|
+
sin: (-half*I*exp(I*x) + half*I*exp(-I*x)).function(x),
|
|
1478
|
+
cos: (half*exp(I*x) + half*exp(-I*x)).function(x),
|
|
1479
|
+
sec: (two/(exp(I*x) + exp(-I*x))).function(x),
|
|
1480
|
+
csc: (two*I/(exp(I*x) - exp(-I*x))).function(x),
|
|
1481
|
+
tan: (-I*(exp(I*x) - exp(-I*x))/(exp(I*x) + exp(-I*x))).function(x),
|
|
1482
|
+
cot: (I*(exp(I*x) + exp(-I*x))/(exp(I*x) - exp(-I*x))).function(x),
|
|
1483
|
+
sinh: (-half*exp(-x) + half*exp(x)).function(x),
|
|
1484
|
+
cosh: (half*exp(-x) + half*exp(x)).function(x),
|
|
1485
|
+
sech: (two/(exp(-x) + exp(x))).function(x),
|
|
1486
|
+
csch: (-two/(exp(-x) - exp(x))).function(x),
|
|
1487
|
+
tanh: (-(exp(-x) - exp(x))/(exp(x) + exp(-x))).function(x),
|
|
1488
|
+
coth: (-(exp(-x) + exp(x))/(exp(-x) - exp(x))).function(x)
|
|
1489
|
+
}
|
|
1490
|
+
Circs = list(CircDict)
|
|
1491
|
+
|
|
1492
|
+
def __init__(self, ex):
|
|
1493
|
+
"""
|
|
1494
|
+
A class that walks a symbolic expression tree and replace circular
|
|
1495
|
+
and hyperbolic functions by their respective exponential
|
|
1496
|
+
expressions.
|
|
1497
|
+
|
|
1498
|
+
EXAMPLES::
|
|
1499
|
+
|
|
1500
|
+
sage: from sage.symbolic.expression_conversions import Exponentialize
|
|
1501
|
+
sage: d = Exponentialize(sin(x))
|
|
1502
|
+
sage: d(sin(x))
|
|
1503
|
+
-1/2*I*e^(I*x) + 1/2*I*e^(-I*x)
|
|
1504
|
+
sage: d(cosh(x))
|
|
1505
|
+
1/2*e^(-x) + 1/2*e^x
|
|
1506
|
+
"""
|
|
1507
|
+
self.ex = ex
|
|
1508
|
+
|
|
1509
|
+
def composition(self, ex, op):
|
|
1510
|
+
r"""
|
|
1511
|
+
Return the composition of ``self`` with ``ex`` by ``op``.
|
|
1512
|
+
|
|
1513
|
+
EXAMPLES::
|
|
1514
|
+
|
|
1515
|
+
sage: x = SR.var("x")
|
|
1516
|
+
sage: from sage.symbolic.expression_conversions import Exponentialize
|
|
1517
|
+
sage: p = x
|
|
1518
|
+
sage: s = Exponentialize(p)
|
|
1519
|
+
sage: q = sin(x)
|
|
1520
|
+
sage: s.composition(q, q.operator())
|
|
1521
|
+
-1/2*I*e^(I*x) + 1/2*I*e^(-I*x)
|
|
1522
|
+
"""
|
|
1523
|
+
if op in self.Circs:
|
|
1524
|
+
return self.CircDict.get(op)(*[self(oper)
|
|
1525
|
+
for oper in ex.operands()])
|
|
1526
|
+
return super().composition(ex, op)
|
|
1527
|
+
|
|
1528
|
+
|
|
1529
|
+
class DeMoivre(ExpressionTreeWalker):
|
|
1530
|
+
def __init__(self, ex, force=False):
|
|
1531
|
+
r"""
|
|
1532
|
+
A class that walks a symbolic expression tree and replaces
|
|
1533
|
+
occurrences of complex exponentials (optionally, all
|
|
1534
|
+
exponentials) by their respective trigonometric expressions.
|
|
1535
|
+
|
|
1536
|
+
INPUT:
|
|
1537
|
+
|
|
1538
|
+
- ``force`` -- boolean (default: ``False``); replace `\exp(x)`
|
|
1539
|
+
with `\cosh(x) + \sinh(x)`
|
|
1540
|
+
|
|
1541
|
+
EXAMPLES::
|
|
1542
|
+
|
|
1543
|
+
sage: a, b = SR.var("a, b")
|
|
1544
|
+
sage: from sage.symbolic.expression_conversions import DeMoivre
|
|
1545
|
+
sage: d = DeMoivre(e^a)
|
|
1546
|
+
sage: d(e^(a+I*b))
|
|
1547
|
+
(cos(b) + I*sin(b))*e^a
|
|
1548
|
+
"""
|
|
1549
|
+
self.ex = ex
|
|
1550
|
+
self.force = force
|
|
1551
|
+
|
|
1552
|
+
def composition(self, ex, op):
|
|
1553
|
+
"""
|
|
1554
|
+
Return the composition of ``self`` with ``ex`` by ``op``.
|
|
1555
|
+
|
|
1556
|
+
EXAMPLES::
|
|
1557
|
+
|
|
1558
|
+
sage: x, a, b = SR.var('x, a, b')
|
|
1559
|
+
sage: from sage.symbolic.expression_conversions import DeMoivre
|
|
1560
|
+
sage: p = exp(x)
|
|
1561
|
+
sage: s = DeMoivre(p)
|
|
1562
|
+
sage: q = exp(a+I*b)
|
|
1563
|
+
sage: s.composition(q, q.operator())
|
|
1564
|
+
(cos(b) + I*sin(b))*e^a
|
|
1565
|
+
"""
|
|
1566
|
+
if op is not exp:
|
|
1567
|
+
# return super().composition(ex, op)
|
|
1568
|
+
return op(*[self(oper) for oper in ex.operands()])
|
|
1569
|
+
|
|
1570
|
+
from sage.rings.imaginary_unit import I
|
|
1571
|
+
from sage.symbolic.ring import SR
|
|
1572
|
+
from sage.functions.hyperbolic import sinh, cosh
|
|
1573
|
+
from sage.functions.trig import sin, cos
|
|
1574
|
+
arg = self(ex.operands()[0])()
|
|
1575
|
+
w0, w1 = (SR.wild(u) for u in range(2))
|
|
1576
|
+
D = arg.match(w0 + I*w1)
|
|
1577
|
+
if D is not None:
|
|
1578
|
+
A = D.get(w1)
|
|
1579
|
+
return exp(D.get(w0))*(cos(A) + I*sin(A))
|
|
1580
|
+
D = arg.match(I*w0)
|
|
1581
|
+
if D is not None:
|
|
1582
|
+
A = D.get(w0)
|
|
1583
|
+
return cos(A) + I*sin(A)
|
|
1584
|
+
if self.force:
|
|
1585
|
+
return cosh(arg) + sinh(arg)
|
|
1586
|
+
return exp(arg)
|
|
1587
|
+
|
|
1588
|
+
|
|
1589
|
+
# Half_angle transformation. Sometimes useful in integration
|
|
1590
|
+
|
|
1591
|
+
class HalfAngle(ExpressionTreeWalker):
|
|
1592
|
+
"""
|
|
1593
|
+
A class that walks a symbolic expression tree, replacing each
|
|
1594
|
+
occurrence of a trigonometric or hyperbolic function by its
|
|
1595
|
+
expression as a rational fraction in the (hyperbolic) tangent
|
|
1596
|
+
of half the original argument.
|
|
1597
|
+
"""
|
|
1598
|
+
# Code executed once at first class reference: create canned formulae.
|
|
1599
|
+
from sage.calculus.var import function
|
|
1600
|
+
from sage.functions.hyperbolic import sinh, cosh, sech, csch, tanh, coth
|
|
1601
|
+
from sage.functions.trig import sin, cos, sec, csc, tan, cot
|
|
1602
|
+
from sage.rings.integer import Integer
|
|
1603
|
+
from sage.symbolic.ring import SR
|
|
1604
|
+
x = SR.var("x")
|
|
1605
|
+
one = Integer(1)
|
|
1606
|
+
two = Integer(2)
|
|
1607
|
+
half = one / two
|
|
1608
|
+
halfx = half * x
|
|
1609
|
+
HalvesDict = {
|
|
1610
|
+
sin: two * tan(halfx) / (tan(halfx)**2 + one).function(x),
|
|
1611
|
+
cos: -(tan(halfx)**2 - one) / (tan(halfx)**2 + one).function(x),
|
|
1612
|
+
tan: -two * tan(halfx) / (tan(halfx)**2 - one).function(x),
|
|
1613
|
+
csc: half * (tan(halfx)**2 + one) / tan(halfx).function(x),
|
|
1614
|
+
sec: -(tan(halfx)**2 + one) / (tan(halfx)**2 - one).function(x),
|
|
1615
|
+
cot: -half * (tan(halfx)**2 - one) / tan(halfx).function(x),
|
|
1616
|
+
sinh: -two * tanh(halfx) / (tanh(halfx)**2 - one).function(x),
|
|
1617
|
+
cosh: -(tanh(halfx)**2 + one) / (tanh(halfx)**2 - one).function(x),
|
|
1618
|
+
tanh: two * tanh(halfx) / (tanh(halfx)**2 + one).function(x),
|
|
1619
|
+
csch: -half * (tanh(halfx)**2 - one) / tanh(halfx).function(x),
|
|
1620
|
+
sech: -(tanh(halfx)**2 - one) / (tanh(halfx)**2 + one).function(x),
|
|
1621
|
+
coth: half * (tanh(halfx)**2 + one) / tanh(halfx).function(x)
|
|
1622
|
+
}
|
|
1623
|
+
Halves = list(HalvesDict)
|
|
1624
|
+
|
|
1625
|
+
def __init__(self, ex):
|
|
1626
|
+
"""
|
|
1627
|
+
A class that walks a symbolic expression tree, replacing each
|
|
1628
|
+
occurrence of a trigonometric or hyperbolic function by its
|
|
1629
|
+
expression as a rational fraction in the (hyperbolic) tangent
|
|
1630
|
+
of half the original argument.
|
|
1631
|
+
|
|
1632
|
+
EXAMPLES::
|
|
1633
|
+
|
|
1634
|
+
sage: a, b = SR.var("a, b")
|
|
1635
|
+
sage: from sage.symbolic.expression_conversions import HalfAngle
|
|
1636
|
+
sage: HalfAngle(tan(a))(tan(a)+4)
|
|
1637
|
+
-2*tan(1/2*a)/(tan(1/2*a)^2 - 1) + 4
|
|
1638
|
+
"""
|
|
1639
|
+
self.ex = ex
|
|
1640
|
+
|
|
1641
|
+
def composition(self, ex, op):
|
|
1642
|
+
"""
|
|
1643
|
+
Compose.
|
|
1644
|
+
|
|
1645
|
+
EXAMPLES::
|
|
1646
|
+
|
|
1647
|
+
sage: from sage.symbolic.expression_conversions import HalfAngle
|
|
1648
|
+
sage: x, t = SR.var("x, t")
|
|
1649
|
+
sage: a = HalfAngle(cos(3*x)/(4-cos(x)).trig_expand())()
|
|
1650
|
+
sage: a.subs(tan(x/2) == t).simplify_full()
|
|
1651
|
+
(2*(t^2 + 1)*cos(3/2*x)^2 - t^2 - 1)/(5*t^2 + 3)
|
|
1652
|
+
"""
|
|
1653
|
+
if op in self.Halves:
|
|
1654
|
+
return self.HalvesDict.get(op)(*[self(x) for x in ex.operands()])
|
|
1655
|
+
return super().composition(ex, op)
|
|
1656
|
+
|
|
1657
|
+
|
|
1658
|
+
class HoldRemover(ExpressionTreeWalker):
|
|
1659
|
+
def __init__(self, ex, exclude=None):
|
|
1660
|
+
"""
|
|
1661
|
+
A class that walks the tree and evaluates every operator
|
|
1662
|
+
that is not in a given list of exceptions.
|
|
1663
|
+
|
|
1664
|
+
EXAMPLES::
|
|
1665
|
+
|
|
1666
|
+
sage: from sage.symbolic.expression_conversions import HoldRemover
|
|
1667
|
+
sage: ex = sin(pi*cos(0, hold=True), hold=True); ex
|
|
1668
|
+
sin(pi*cos(0))
|
|
1669
|
+
sage: h = HoldRemover(ex)
|
|
1670
|
+
sage: h()
|
|
1671
|
+
0
|
|
1672
|
+
sage: h = HoldRemover(ex, [sin])
|
|
1673
|
+
sage: h()
|
|
1674
|
+
sin(pi)
|
|
1675
|
+
sage: h = HoldRemover(ex, [cos])
|
|
1676
|
+
sage: h()
|
|
1677
|
+
sin(pi*cos(0))
|
|
1678
|
+
sage: ex = atan2(0, 0, hold=True) + hypergeometric([1,2], [3,4], 0, hold=True)
|
|
1679
|
+
sage: h = HoldRemover(ex, [atan2])
|
|
1680
|
+
sage: h()
|
|
1681
|
+
arctan2(0, 0) + 1
|
|
1682
|
+
sage: h = HoldRemover(ex, [hypergeometric])
|
|
1683
|
+
sage: h()
|
|
1684
|
+
NaN + hypergeometric((1, 2), (3, 4), 0)
|
|
1685
|
+
"""
|
|
1686
|
+
self.ex = ex
|
|
1687
|
+
if exclude is None:
|
|
1688
|
+
exclude = []
|
|
1689
|
+
self._exclude = exclude
|
|
1690
|
+
|
|
1691
|
+
def composition(self, ex, operator):
|
|
1692
|
+
"""
|
|
1693
|
+
EXAMPLES::
|
|
1694
|
+
|
|
1695
|
+
sage: from sage.symbolic.expression_conversions import HoldRemover
|
|
1696
|
+
sage: ex = sin(pi*cos(0, hold=True), hold=True); ex
|
|
1697
|
+
sin(pi*cos(0))
|
|
1698
|
+
sage: h = HoldRemover(ex)
|
|
1699
|
+
sage: h()
|
|
1700
|
+
0
|
|
1701
|
+
"""
|
|
1702
|
+
from sage.calculus.calculus import symbolic_sum, symbolic_product
|
|
1703
|
+
from sage.functions.other import Function_sum, Function_prod
|
|
1704
|
+
if not operator:
|
|
1705
|
+
return self
|
|
1706
|
+
if isinstance(operator, Function_sum):
|
|
1707
|
+
return symbolic_sum(*map(self, ex.operands()))
|
|
1708
|
+
if isinstance(operator, Function_prod):
|
|
1709
|
+
return symbolic_product(*map(self, ex.operands()))
|
|
1710
|
+
if operator in self._exclude:
|
|
1711
|
+
return operator(*map(self, ex.operands()), hold=True)
|
|
1712
|
+
else:
|
|
1713
|
+
return operator(*map(self, ex.operands()))
|