ZhengyangZhang commited on
Commit
ccca77b
·
verified ·
1 Parent(s): 44831b5

Add files using upload-large-folder tool

Browse files
Files changed (50) hide show
  1. lib/python3.12/site-packages/sympy/simplify/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/sympy/simplify/__pycache__/combsimp.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/sympy/simplify/__pycache__/cse_opts.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/sympy/simplify/__pycache__/gammasimp.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/sympy/simplify/__pycache__/hyperexpand_doc.cpython-312.pyc +0 -0
  6. lib/python3.12/site-packages/sympy/simplify/__pycache__/powsimp.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/sympy/simplify/__pycache__/radsimp.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/sympy/simplify/__pycache__/ratsimp.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/sympy/simplify/__pycache__/simplify.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/sympy/simplify/__pycache__/sqrtdenest.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/sympy/simplify/__pycache__/traversaltools.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/sympy/simplify/__pycache__/trigsimp.cpython-312.pyc +0 -0
  13. lib/python3.12/site-packages/sympy/simplify/cse_main.py +946 -0
  14. lib/python3.12/site-packages/sympy/simplify/epathtools.py +356 -0
  15. lib/python3.12/site-packages/sympy/simplify/fu.py +2111 -0
  16. lib/python3.12/site-packages/sympy/simplify/gammasimp.py +497 -0
  17. lib/python3.12/site-packages/sympy/simplify/ratsimp.py +222 -0
  18. lib/python3.12/site-packages/sympy/simplify/simplify.py +2150 -0
  19. lib/python3.12/site-packages/sympy/simplify/sqrtdenest.py +678 -0
  20. lib/python3.12/site-packages/sympy/simplify/tests/__init__.py +0 -0
  21. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_combsimp.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_cse.cpython-312.pyc +0 -0
  24. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_epathtools.cpython-312.pyc +0 -0
  25. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_fu.cpython-312.pyc +0 -0
  26. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_function.cpython-312.pyc +0 -0
  27. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_gammasimp.cpython-312.pyc +0 -0
  28. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_hyperexpand.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_powsimp.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_radsimp.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_ratsimp.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_rewrite.cpython-312.pyc +0 -0
  33. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_simplify.cpython-312.pyc +0 -0
  34. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_sqrtdenest.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_trigsimp.cpython-312.pyc +0 -0
  36. lib/python3.12/site-packages/sympy/simplify/tests/test_combsimp.py +75 -0
  37. lib/python3.12/site-packages/sympy/simplify/tests/test_cse.py +758 -0
  38. lib/python3.12/site-packages/sympy/simplify/tests/test_epathtools.py +90 -0
  39. lib/python3.12/site-packages/sympy/simplify/tests/test_fu.py +492 -0
  40. lib/python3.12/site-packages/sympy/simplify/tests/test_function.py +54 -0
  41. lib/python3.12/site-packages/sympy/simplify/tests/test_gammasimp.py +127 -0
  42. lib/python3.12/site-packages/sympy/simplify/tests/test_hyperexpand.py +1063 -0
  43. lib/python3.12/site-packages/sympy/simplify/tests/test_powsimp.py +366 -0
  44. lib/python3.12/site-packages/sympy/simplify/tests/test_radsimp.py +490 -0
  45. lib/python3.12/site-packages/sympy/simplify/tests/test_ratsimp.py +78 -0
  46. lib/python3.12/site-packages/sympy/simplify/tests/test_rewrite.py +31 -0
  47. lib/python3.12/site-packages/sympy/simplify/tests/test_simplify.py +1087 -0
  48. lib/python3.12/site-packages/sympy/simplify/tests/test_sqrtdenest.py +204 -0
  49. lib/python3.12/site-packages/sympy/simplify/tests/test_trigsimp.py +520 -0
  50. lib/python3.12/site-packages/sympy/simplify/trigsimp.py +1253 -0
lib/python3.12/site-packages/sympy/simplify/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (1.41 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/combsimp.cpython-312.pyc ADDED
Binary file (4.91 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/cse_opts.cpython-312.pyc ADDED
Binary file (2.6 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/gammasimp.cpython-312.pyc ADDED
Binary file (19.3 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/hyperexpand_doc.cpython-312.pyc ADDED
Binary file (1.05 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/powsimp.cpython-312.pyc ADDED
Binary file (28.5 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/radsimp.cpython-312.pyc ADDED
Binary file (49.4 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/ratsimp.cpython-312.pyc ADDED
Binary file (11.4 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/simplify.cpython-312.pyc ADDED
Binary file (85.7 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/sqrtdenest.cpython-312.pyc ADDED
Binary file (28.6 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/traversaltools.cpython-312.pyc ADDED
Binary file (659 Bytes). View file
 
lib/python3.12/site-packages/sympy/simplify/__pycache__/trigsimp.cpython-312.pyc ADDED
Binary file (56.3 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/cse_main.py ADDED
@@ -0,0 +1,946 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ Tools for doing common subexpression elimination.
2
+ """
3
+ from collections import defaultdict
4
+
5
+ from sympy.core import Basic, Mul, Add, Pow, sympify
6
+ from sympy.core.containers import Tuple, OrderedSet
7
+ from sympy.core.exprtools import factor_terms
8
+ from sympy.core.singleton import S
9
+ from sympy.core.sorting import ordered
10
+ from sympy.core.symbol import symbols, Symbol
11
+ from sympy.matrices import (MatrixBase, Matrix, ImmutableMatrix,
12
+ SparseMatrix, ImmutableSparseMatrix)
13
+ from sympy.matrices.expressions import (MatrixExpr, MatrixSymbol, MatMul,
14
+ MatAdd, MatPow, Inverse)
15
+ from sympy.matrices.expressions.matexpr import MatrixElement
16
+ from sympy.polys.rootoftools import RootOf
17
+ from sympy.utilities.iterables import numbered_symbols, sift, \
18
+ topological_sort, iterable
19
+
20
+ from . import cse_opts
21
+
22
+ # (preprocessor, postprocessor) pairs which are commonly useful. They should
23
+ # each take a SymPy expression and return a possibly transformed expression.
24
+ # When used in the function ``cse()``, the target expressions will be transformed
25
+ # by each of the preprocessor functions in order. After the common
26
+ # subexpressions are eliminated, each resulting expression will have the
27
+ # postprocessor functions transform them in *reverse* order in order to undo the
28
+ # transformation if necessary. This allows the algorithm to operate on
29
+ # a representation of the expressions that allows for more optimization
30
+ # opportunities.
31
+ # ``None`` can be used to specify no transformation for either the preprocessor or
32
+ # postprocessor.
33
+
34
+
35
+ basic_optimizations = [(cse_opts.sub_pre, cse_opts.sub_post),
36
+ (factor_terms, None)]
37
+
38
+ # sometimes we want the output in a different format; non-trivial
39
+ # transformations can be put here for users
40
+ # ===============================================================
41
+
42
+
43
+ def reps_toposort(r):
44
+ """Sort replacements ``r`` so (k1, v1) appears before (k2, v2)
45
+ if k2 is in v1's free symbols. This orders items in the
46
+ way that cse returns its results (hence, in order to use the
47
+ replacements in a substitution option it would make sense
48
+ to reverse the order).
49
+
50
+ Examples
51
+ ========
52
+
53
+ >>> from sympy.simplify.cse_main import reps_toposort
54
+ >>> from sympy.abc import x, y
55
+ >>> from sympy import Eq
56
+ >>> for l, r in reps_toposort([(x, y + 1), (y, 2)]):
57
+ ... print(Eq(l, r))
58
+ ...
59
+ Eq(y, 2)
60
+ Eq(x, y + 1)
61
+
62
+ """
63
+ r = sympify(r)
64
+ E = []
65
+ for c1, (k1, v1) in enumerate(r):
66
+ for c2, (k2, v2) in enumerate(r):
67
+ if k1 in v2.free_symbols:
68
+ E.append((c1, c2))
69
+ return [r[i] for i in topological_sort((range(len(r)), E))]
70
+
71
+
72
+ def cse_separate(r, e):
73
+ """Move expressions that are in the form (symbol, expr) out of the
74
+ expressions and sort them into the replacements using the reps_toposort.
75
+
76
+ Examples
77
+ ========
78
+
79
+ >>> from sympy.simplify.cse_main import cse_separate
80
+ >>> from sympy.abc import x, y, z
81
+ >>> from sympy import cos, exp, cse, Eq, symbols
82
+ >>> x0, x1 = symbols('x:2')
83
+ >>> eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
84
+ >>> cse([eq, Eq(x, z + 1), z - 2], postprocess=cse_separate) in [
85
+ ... [[(x0, y + 1), (x, z + 1), (x1, x + 1)],
86
+ ... [x1 + exp(x1/x0) + cos(x0), z - 2]],
87
+ ... [[(x1, y + 1), (x, z + 1), (x0, x + 1)],
88
+ ... [x0 + exp(x0/x1) + cos(x1), z - 2]]]
89
+ ...
90
+ True
91
+ """
92
+ d = sift(e, lambda w: w.is_Equality and w.lhs.is_Symbol)
93
+ r = r + [w.args for w in d[True]]
94
+ e = d[False]
95
+ return [reps_toposort(r), e]
96
+
97
+
98
+ def cse_release_variables(r, e):
99
+ """
100
+ Return tuples giving ``(a, b)`` where ``a`` is a symbol and ``b`` is
101
+ either an expression or None. The value of None is used when a
102
+ symbol is no longer needed for subsequent expressions.
103
+
104
+ Use of such output can reduce the memory footprint of lambdified
105
+ expressions that contain large, repeated subexpressions.
106
+
107
+ Examples
108
+ ========
109
+
110
+ >>> from sympy import cse
111
+ >>> from sympy.simplify.cse_main import cse_release_variables
112
+ >>> from sympy.abc import x, y
113
+ >>> eqs = [(x + y - 1)**2, x, x + y, (x + y)/(2*x + 1) + (x + y - 1)**2, (2*x + 1)**(x + y)]
114
+ >>> defs, rvs = cse_release_variables(*cse(eqs))
115
+ >>> for i in defs:
116
+ ... print(i)
117
+ ...
118
+ (x0, x + y)
119
+ (x1, (x0 - 1)**2)
120
+ (x2, 2*x + 1)
121
+ (_3, x0/x2 + x1)
122
+ (_4, x2**x0)
123
+ (x2, None)
124
+ (_0, x1)
125
+ (x1, None)
126
+ (_2, x0)
127
+ (x0, None)
128
+ (_1, x)
129
+ >>> print(rvs)
130
+ (_0, _1, _2, _3, _4)
131
+ """
132
+ if not r:
133
+ return r, e
134
+
135
+ s, p = zip(*r)
136
+ esyms = symbols('_:%d' % len(e))
137
+ syms = list(esyms)
138
+ s = list(s)
139
+ in_use = set(s)
140
+ p = list(p)
141
+ # sort e so those with most sub-expressions appear first
142
+ e = [(e[i], syms[i]) for i in range(len(e))]
143
+ e, syms = zip(*sorted(e,
144
+ key=lambda x: -sum(p[s.index(i)].count_ops()
145
+ for i in x[0].free_symbols & in_use)))
146
+ syms = list(syms)
147
+ p += e
148
+ rv = []
149
+ i = len(p) - 1
150
+ while i >= 0:
151
+ _p = p.pop()
152
+ c = in_use & _p.free_symbols
153
+ if c: # sorting for canonical results
154
+ rv.extend([(s, None) for s in sorted(c, key=str)])
155
+ if i >= len(r):
156
+ rv.append((syms.pop(), _p))
157
+ else:
158
+ rv.append((s[i], _p))
159
+ in_use -= c
160
+ i -= 1
161
+ rv.reverse()
162
+ return rv, esyms
163
+
164
+
165
+ # ====end of cse postprocess idioms===========================
166
+
167
+
168
+ def preprocess_for_cse(expr, optimizations):
169
+ """ Preprocess an expression to optimize for common subexpression
170
+ elimination.
171
+
172
+ Parameters
173
+ ==========
174
+
175
+ expr : SymPy expression
176
+ The target expression to optimize.
177
+ optimizations : list of (callable, callable) pairs
178
+ The (preprocessor, postprocessor) pairs.
179
+
180
+ Returns
181
+ =======
182
+
183
+ expr : SymPy expression
184
+ The transformed expression.
185
+ """
186
+ for pre, post in optimizations:
187
+ if pre is not None:
188
+ expr = pre(expr)
189
+ return expr
190
+
191
+
192
+ def postprocess_for_cse(expr, optimizations):
193
+ """Postprocess an expression after common subexpression elimination to
194
+ return the expression to canonical SymPy form.
195
+
196
+ Parameters
197
+ ==========
198
+
199
+ expr : SymPy expression
200
+ The target expression to transform.
201
+ optimizations : list of (callable, callable) pairs, optional
202
+ The (preprocessor, postprocessor) pairs. The postprocessors will be
203
+ applied in reversed order to undo the effects of the preprocessors
204
+ correctly.
205
+
206
+ Returns
207
+ =======
208
+
209
+ expr : SymPy expression
210
+ The transformed expression.
211
+ """
212
+ for pre, post in reversed(optimizations):
213
+ if post is not None:
214
+ expr = post(expr)
215
+ return expr
216
+
217
+
218
+ class FuncArgTracker:
219
+ """
220
+ A class which manages a mapping from functions to arguments and an inverse
221
+ mapping from arguments to functions.
222
+ """
223
+
224
+ def __init__(self, funcs):
225
+ # To minimize the number of symbolic comparisons, all function arguments
226
+ # get assigned a value number.
227
+ self.value_numbers = {}
228
+ self.value_number_to_value = []
229
+
230
+ # Both of these maps use integer indices for arguments / functions.
231
+ self.arg_to_funcset = []
232
+ self.func_to_argset = []
233
+
234
+ for func_i, func in enumerate(funcs):
235
+ func_argset = OrderedSet()
236
+
237
+ for func_arg in func.args:
238
+ arg_number = self.get_or_add_value_number(func_arg)
239
+ func_argset.add(arg_number)
240
+ self.arg_to_funcset[arg_number].add(func_i)
241
+
242
+ self.func_to_argset.append(func_argset)
243
+
244
+ def get_args_in_value_order(self, argset):
245
+ """
246
+ Return the list of arguments in sorted order according to their value
247
+ numbers.
248
+ """
249
+ return [self.value_number_to_value[argn] for argn in sorted(argset)]
250
+
251
+ def get_or_add_value_number(self, value):
252
+ """
253
+ Return the value number for the given argument.
254
+ """
255
+ nvalues = len(self.value_numbers)
256
+ value_number = self.value_numbers.setdefault(value, nvalues)
257
+ if value_number == nvalues:
258
+ self.value_number_to_value.append(value)
259
+ self.arg_to_funcset.append(OrderedSet())
260
+ return value_number
261
+
262
+ def stop_arg_tracking(self, func_i):
263
+ """
264
+ Remove the function func_i from the argument to function mapping.
265
+ """
266
+ for arg in self.func_to_argset[func_i]:
267
+ self.arg_to_funcset[arg].remove(func_i)
268
+
269
+
270
+ def get_common_arg_candidates(self, argset, min_func_i=0):
271
+ """Return a dict whose keys are function numbers. The entries of the dict are
272
+ the number of arguments said function has in common with
273
+ ``argset``. Entries have at least 2 items in common. All keys have
274
+ value at least ``min_func_i``.
275
+ """
276
+ count_map = defaultdict(lambda: 0)
277
+ if not argset:
278
+ return count_map
279
+
280
+ funcsets = [self.arg_to_funcset[arg] for arg in argset]
281
+ # As an optimization below, we handle the largest funcset separately from
282
+ # the others.
283
+ largest_funcset = max(funcsets, key=len)
284
+
285
+ for funcset in funcsets:
286
+ if largest_funcset is funcset:
287
+ continue
288
+ for func_i in funcset:
289
+ if func_i >= min_func_i:
290
+ count_map[func_i] += 1
291
+
292
+ # We pick the smaller of the two containers (count_map, largest_funcset)
293
+ # to iterate over to reduce the number of iterations needed.
294
+ (smaller_funcs_container,
295
+ larger_funcs_container) = sorted(
296
+ [largest_funcset, count_map],
297
+ key=len)
298
+
299
+ for func_i in smaller_funcs_container:
300
+ # Not already in count_map? It can't possibly be in the output, so
301
+ # skip it.
302
+ if count_map[func_i] < 1:
303
+ continue
304
+
305
+ if func_i in larger_funcs_container:
306
+ count_map[func_i] += 1
307
+
308
+ return {k: v for k, v in count_map.items() if v >= 2}
309
+
310
+ def get_subset_candidates(self, argset, restrict_to_funcset=None):
311
+ """
312
+ Return a set of functions each of which whose argument list contains
313
+ ``argset``, optionally filtered only to contain functions in
314
+ ``restrict_to_funcset``.
315
+ """
316
+ iarg = iter(argset)
317
+
318
+ indices = OrderedSet(
319
+ fi for fi in self.arg_to_funcset[next(iarg)])
320
+
321
+ if restrict_to_funcset is not None:
322
+ indices &= restrict_to_funcset
323
+
324
+ for arg in iarg:
325
+ indices &= self.arg_to_funcset[arg]
326
+
327
+ return indices
328
+
329
+ def update_func_argset(self, func_i, new_argset):
330
+ """
331
+ Update a function with a new set of arguments.
332
+ """
333
+ new_args = OrderedSet(new_argset)
334
+ old_args = self.func_to_argset[func_i]
335
+
336
+ for deleted_arg in old_args - new_args:
337
+ self.arg_to_funcset[deleted_arg].remove(func_i)
338
+ for added_arg in new_args - old_args:
339
+ self.arg_to_funcset[added_arg].add(func_i)
340
+
341
+ self.func_to_argset[func_i].clear()
342
+ self.func_to_argset[func_i].update(new_args)
343
+
344
+
345
+ class Unevaluated:
346
+
347
+ def __init__(self, func, args):
348
+ self.func = func
349
+ self.args = args
350
+
351
+ def __str__(self):
352
+ return "Uneval<{}>({})".format(
353
+ self.func, ", ".join(str(a) for a in self.args))
354
+
355
+ def as_unevaluated_basic(self):
356
+ return self.func(*self.args, evaluate=False)
357
+
358
+ @property
359
+ def free_symbols(self):
360
+ return set().union(*[a.free_symbols for a in self.args])
361
+
362
+ __repr__ = __str__
363
+
364
+
365
+ def match_common_args(func_class, funcs, opt_subs):
366
+ """
367
+ Recognize and extract common subexpressions of function arguments within a
368
+ set of function calls. For instance, for the following function calls::
369
+
370
+ x + z + y
371
+ sin(x + y)
372
+
373
+ this will extract a common subexpression of `x + y`::
374
+
375
+ w = x + y
376
+ w + z
377
+ sin(w)
378
+
379
+ The function we work with is assumed to be associative and commutative.
380
+
381
+ Parameters
382
+ ==========
383
+
384
+ func_class: class
385
+ The function class (e.g. Add, Mul)
386
+ funcs: list of functions
387
+ A list of function calls.
388
+ opt_subs: dict
389
+ A dictionary of substitutions which this function may update.
390
+ """
391
+
392
+ # Sort to ensure that whole-function subexpressions come before the items
393
+ # that use them.
394
+ funcs = sorted(funcs, key=lambda f: len(f.args))
395
+ arg_tracker = FuncArgTracker(funcs)
396
+
397
+ changed = OrderedSet()
398
+
399
+ for i in range(len(funcs)):
400
+ common_arg_candidates_counts = arg_tracker.get_common_arg_candidates(
401
+ arg_tracker.func_to_argset[i], min_func_i=i + 1)
402
+
403
+ # Sort the candidates in order of match size.
404
+ # This makes us try combining smaller matches first.
405
+ common_arg_candidates = OrderedSet(sorted(
406
+ common_arg_candidates_counts.keys(),
407
+ key=lambda k: (common_arg_candidates_counts[k], k)))
408
+
409
+ while common_arg_candidates:
410
+ j = common_arg_candidates.pop(last=False)
411
+
412
+ com_args = arg_tracker.func_to_argset[i].intersection(
413
+ arg_tracker.func_to_argset[j])
414
+
415
+ if len(com_args) <= 1:
416
+ # This may happen if a set of common arguments was already
417
+ # combined in a previous iteration.
418
+ continue
419
+
420
+ # For all sets, replace the common symbols by the function
421
+ # over them, to allow recursive matches.
422
+
423
+ diff_i = arg_tracker.func_to_argset[i].difference(com_args)
424
+ if diff_i:
425
+ # com_func needs to be unevaluated to allow for recursive matches.
426
+ com_func = Unevaluated(
427
+ func_class, arg_tracker.get_args_in_value_order(com_args))
428
+ com_func_number = arg_tracker.get_or_add_value_number(com_func)
429
+ arg_tracker.update_func_argset(i, diff_i | OrderedSet([com_func_number]))
430
+ changed.add(i)
431
+ else:
432
+ # Treat the whole expression as a CSE.
433
+ #
434
+ # The reason this needs to be done is somewhat subtle. Within
435
+ # tree_cse(), to_eliminate only contains expressions that are
436
+ # seen more than once. The problem is unevaluated expressions
437
+ # do not compare equal to the evaluated equivalent. So
438
+ # tree_cse() won't mark funcs[i] as a CSE if we use an
439
+ # unevaluated version.
440
+ com_func_number = arg_tracker.get_or_add_value_number(funcs[i])
441
+
442
+ diff_j = arg_tracker.func_to_argset[j].difference(com_args)
443
+ arg_tracker.update_func_argset(j, diff_j | OrderedSet([com_func_number]))
444
+ changed.add(j)
445
+
446
+ for k in arg_tracker.get_subset_candidates(
447
+ com_args, common_arg_candidates):
448
+ diff_k = arg_tracker.func_to_argset[k].difference(com_args)
449
+ arg_tracker.update_func_argset(k, diff_k | OrderedSet([com_func_number]))
450
+ changed.add(k)
451
+
452
+ if i in changed:
453
+ opt_subs[funcs[i]] = Unevaluated(func_class,
454
+ arg_tracker.get_args_in_value_order(arg_tracker.func_to_argset[i]))
455
+
456
+ arg_tracker.stop_arg_tracking(i)
457
+
458
+
459
+ def opt_cse(exprs, order='canonical'):
460
+ """Find optimization opportunities in Adds, Muls, Pows and negative
461
+ coefficient Muls.
462
+
463
+ Parameters
464
+ ==========
465
+
466
+ exprs : list of SymPy expressions
467
+ The expressions to optimize.
468
+ order : string, 'none' or 'canonical'
469
+ The order by which Mul and Add arguments are processed. For large
470
+ expressions where speed is a concern, use the setting order='none'.
471
+
472
+ Returns
473
+ =======
474
+
475
+ opt_subs : dictionary of expression substitutions
476
+ The expression substitutions which can be useful to optimize CSE.
477
+
478
+ Examples
479
+ ========
480
+
481
+ >>> from sympy.simplify.cse_main import opt_cse
482
+ >>> from sympy.abc import x
483
+ >>> opt_subs = opt_cse([x**-2])
484
+ >>> k, v = list(opt_subs.keys())[0], list(opt_subs.values())[0]
485
+ >>> print((k, v.as_unevaluated_basic()))
486
+ (x**(-2), 1/(x**2))
487
+ """
488
+ opt_subs = {}
489
+
490
+ adds = OrderedSet()
491
+ muls = OrderedSet()
492
+
493
+ seen_subexp = set()
494
+ collapsible_subexp = set()
495
+
496
+ def _find_opts(expr):
497
+
498
+ if not isinstance(expr, (Basic, Unevaluated)):
499
+ return
500
+
501
+ if expr.is_Atom or expr.is_Order:
502
+ return
503
+
504
+ if iterable(expr):
505
+ list(map(_find_opts, expr))
506
+ return
507
+
508
+ if expr in seen_subexp:
509
+ return expr
510
+ seen_subexp.add(expr)
511
+
512
+ list(map(_find_opts, expr.args))
513
+
514
+ if not isinstance(expr, MatrixExpr) and expr.could_extract_minus_sign():
515
+ # XXX -expr does not always work rigorously for some expressions
516
+ # containing UnevaluatedExpr.
517
+ # https://github.com/sympy/sympy/issues/24818
518
+ if isinstance(expr, Add):
519
+ neg_expr = Add(*(-i for i in expr.args))
520
+ else:
521
+ neg_expr = -expr
522
+
523
+ if not neg_expr.is_Atom:
524
+ opt_subs[expr] = Unevaluated(Mul, (S.NegativeOne, neg_expr))
525
+ seen_subexp.add(neg_expr)
526
+ expr = neg_expr
527
+
528
+ if isinstance(expr, (Mul, MatMul)):
529
+ if len(expr.args) == 1:
530
+ collapsible_subexp.add(expr)
531
+ else:
532
+ muls.add(expr)
533
+
534
+ elif isinstance(expr, (Add, MatAdd)):
535
+ if len(expr.args) == 1:
536
+ collapsible_subexp.add(expr)
537
+ else:
538
+ adds.add(expr)
539
+
540
+ elif isinstance(expr, Inverse):
541
+ # Do not want to treat `Inverse` as a `MatPow`
542
+ pass
543
+
544
+ elif isinstance(expr, (Pow, MatPow)):
545
+ base, exp = expr.base, expr.exp
546
+ if exp.could_extract_minus_sign():
547
+ opt_subs[expr] = Unevaluated(Pow, (Pow(base, -exp), -1))
548
+
549
+ for e in exprs:
550
+ if isinstance(e, (Basic, Unevaluated)):
551
+ _find_opts(e)
552
+
553
+ # Handle collapsing of multinary operations with single arguments
554
+ edges = [(s, s.args[0]) for s in collapsible_subexp
555
+ if s.args[0] in collapsible_subexp]
556
+ for e in reversed(topological_sort((collapsible_subexp, edges))):
557
+ opt_subs[e] = opt_subs.get(e.args[0], e.args[0])
558
+
559
+ # split muls into commutative
560
+ commutative_muls = OrderedSet()
561
+ for m in muls:
562
+ c, nc = m.args_cnc(cset=False)
563
+ if c:
564
+ c_mul = m.func(*c)
565
+ if nc:
566
+ if c_mul == 1:
567
+ new_obj = m.func(*nc)
568
+ else:
569
+ if isinstance(m, MatMul):
570
+ new_obj = m.func(c_mul, *nc, evaluate=False)
571
+ else:
572
+ new_obj = m.func(c_mul, m.func(*nc), evaluate=False)
573
+ opt_subs[m] = new_obj
574
+ if len(c) > 1:
575
+ commutative_muls.add(c_mul)
576
+
577
+ match_common_args(Add, adds, opt_subs)
578
+ match_common_args(Mul, commutative_muls, opt_subs)
579
+
580
+ return opt_subs
581
+
582
+
583
+ def tree_cse(exprs, symbols, opt_subs=None, order='canonical', ignore=()):
584
+ """Perform raw CSE on expression tree, taking opt_subs into account.
585
+
586
+ Parameters
587
+ ==========
588
+
589
+ exprs : list of SymPy expressions
590
+ The expressions to reduce.
591
+ symbols : infinite iterator yielding unique Symbols
592
+ The symbols used to label the common subexpressions which are pulled
593
+ out.
594
+ opt_subs : dictionary of expression substitutions
595
+ The expressions to be substituted before any CSE action is performed.
596
+ order : string, 'none' or 'canonical'
597
+ The order by which Mul and Add arguments are processed. For large
598
+ expressions where speed is a concern, use the setting order='none'.
599
+ ignore : iterable of Symbols
600
+ Substitutions containing any Symbol from ``ignore`` will be ignored.
601
+ """
602
+ if opt_subs is None:
603
+ opt_subs = {}
604
+
605
+ ## Find repeated sub-expressions
606
+
607
+ to_eliminate = set()
608
+
609
+ seen_subexp = set()
610
+ excluded_symbols = set()
611
+
612
+ def _find_repeated(expr):
613
+ if not isinstance(expr, (Basic, Unevaluated)):
614
+ return
615
+
616
+ if isinstance(expr, RootOf):
617
+ return
618
+
619
+ if isinstance(expr, Basic) and (
620
+ expr.is_Atom or
621
+ expr.is_Order or
622
+ isinstance(expr, (MatrixSymbol, MatrixElement))):
623
+ if expr.is_Symbol:
624
+ excluded_symbols.add(expr.name)
625
+ return
626
+
627
+ if iterable(expr):
628
+ args = expr
629
+
630
+ else:
631
+ if expr in seen_subexp:
632
+ for ign in ignore:
633
+ if ign in expr.free_symbols:
634
+ break
635
+ else:
636
+ to_eliminate.add(expr)
637
+ return
638
+
639
+ seen_subexp.add(expr)
640
+
641
+ if expr in opt_subs:
642
+ expr = opt_subs[expr]
643
+
644
+ args = expr.args
645
+
646
+ list(map(_find_repeated, args))
647
+
648
+ for e in exprs:
649
+ if isinstance(e, Basic):
650
+ _find_repeated(e)
651
+
652
+ ## Rebuild tree
653
+
654
+ # Remove symbols from the generator that conflict with names in the expressions.
655
+ symbols = (_ for _ in symbols if _.name not in excluded_symbols)
656
+
657
+ replacements = []
658
+
659
+ subs = {}
660
+
661
+ def _rebuild(expr):
662
+ if not isinstance(expr, (Basic, Unevaluated)):
663
+ return expr
664
+
665
+ if not expr.args:
666
+ return expr
667
+
668
+ if iterable(expr):
669
+ new_args = [_rebuild(arg) for arg in expr.args]
670
+ return expr.func(*new_args)
671
+
672
+ if expr in subs:
673
+ return subs[expr]
674
+
675
+ orig_expr = expr
676
+ if expr in opt_subs:
677
+ expr = opt_subs[expr]
678
+
679
+ # If enabled, parse Muls and Adds arguments by order to ensure
680
+ # replacement order independent from hashes
681
+ if order != 'none':
682
+ if isinstance(expr, (Mul, MatMul)):
683
+ c, nc = expr.args_cnc()
684
+ if c == [1]:
685
+ args = nc
686
+ else:
687
+ args = list(ordered(c)) + nc
688
+ elif isinstance(expr, (Add, MatAdd)):
689
+ args = list(ordered(expr.args))
690
+ else:
691
+ args = expr.args
692
+ else:
693
+ args = expr.args
694
+
695
+ new_args = list(map(_rebuild, args))
696
+ if isinstance(expr, Unevaluated) or new_args != args:
697
+ new_expr = expr.func(*new_args)
698
+ else:
699
+ new_expr = expr
700
+
701
+ if orig_expr in to_eliminate:
702
+ try:
703
+ sym = next(symbols)
704
+ except StopIteration:
705
+ raise ValueError("Symbols iterator ran out of symbols.")
706
+
707
+ if isinstance(orig_expr, MatrixExpr):
708
+ sym = MatrixSymbol(sym.name, orig_expr.rows,
709
+ orig_expr.cols)
710
+
711
+ subs[orig_expr] = sym
712
+ replacements.append((sym, new_expr))
713
+ return sym
714
+
715
+ else:
716
+ return new_expr
717
+
718
+ reduced_exprs = []
719
+ for e in exprs:
720
+ if isinstance(e, Basic):
721
+ reduced_e = _rebuild(e)
722
+ else:
723
+ reduced_e = e
724
+ reduced_exprs.append(reduced_e)
725
+ return replacements, reduced_exprs
726
+
727
+
728
+ def cse(exprs, symbols=None, optimizations=None, postprocess=None,
729
+ order='canonical', ignore=(), list=True):
730
+ """ Perform common subexpression elimination on an expression.
731
+
732
+ Parameters
733
+ ==========
734
+
735
+ exprs : list of SymPy expressions, or a single SymPy expression
736
+ The expressions to reduce.
737
+ symbols : infinite iterator yielding unique Symbols
738
+ The symbols used to label the common subexpressions which are pulled
739
+ out. The ``numbered_symbols`` generator is useful. The default is a
740
+ stream of symbols of the form "x0", "x1", etc. This must be an
741
+ infinite iterator.
742
+ optimizations : list of (callable, callable) pairs
743
+ The (preprocessor, postprocessor) pairs of external optimization
744
+ functions. Optionally 'basic' can be passed for a set of predefined
745
+ basic optimizations. Such 'basic' optimizations were used by default
746
+ in old implementation, however they can be really slow on larger
747
+ expressions. Now, no pre or post optimizations are made by default.
748
+ postprocess : a function which accepts the two return values of cse and
749
+ returns the desired form of output from cse, e.g. if you want the
750
+ replacements reversed the function might be the following lambda:
751
+ lambda r, e: return reversed(r), e
752
+ order : string, 'none' or 'canonical'
753
+ The order by which Mul and Add arguments are processed. If set to
754
+ 'canonical', arguments will be canonically ordered. If set to 'none',
755
+ ordering will be faster but dependent on expressions hashes, thus
756
+ machine dependent and variable. For large expressions where speed is a
757
+ concern, use the setting order='none'.
758
+ ignore : iterable of Symbols
759
+ Substitutions containing any Symbol from ``ignore`` will be ignored.
760
+ list : bool, (default True)
761
+ Returns expression in list or else with same type as input (when False).
762
+
763
+ Returns
764
+ =======
765
+
766
+ replacements : list of (Symbol, expression) pairs
767
+ All of the common subexpressions that were replaced. Subexpressions
768
+ earlier in this list might show up in subexpressions later in this
769
+ list.
770
+ reduced_exprs : list of SymPy expressions
771
+ The reduced expressions with all of the replacements above.
772
+
773
+ Examples
774
+ ========
775
+
776
+ >>> from sympy import cse, SparseMatrix
777
+ >>> from sympy.abc import x, y, z, w
778
+ >>> cse(((w + x + y + z)*(w + y + z))/(w + x)**3)
779
+ ([(x0, y + z), (x1, w + x)], [(w + x0)*(x0 + x1)/x1**3])
780
+
781
+
782
+ List of expressions with recursive substitutions:
783
+
784
+ >>> m = SparseMatrix([x + y, x + y + z])
785
+ >>> cse([(x+y)**2, x + y + z, y + z, x + z + y, m])
786
+ ([(x0, x + y), (x1, x0 + z)], [x0**2, x1, y + z, x1, Matrix([
787
+ [x0],
788
+ [x1]])])
789
+
790
+ Note: the type and mutability of input matrices is retained.
791
+
792
+ >>> isinstance(_[1][-1], SparseMatrix)
793
+ True
794
+
795
+ The user may disallow substitutions containing certain symbols:
796
+
797
+ >>> cse([y**2*(x + 1), 3*y**2*(x + 1)], ignore=(y,))
798
+ ([(x0, x + 1)], [x0*y**2, 3*x0*y**2])
799
+
800
+ The default return value for the reduced expression(s) is a list, even if there is only
801
+ one expression. The `list` flag preserves the type of the input in the output:
802
+
803
+ >>> cse(x)
804
+ ([], [x])
805
+ >>> cse(x, list=False)
806
+ ([], x)
807
+ """
808
+ if not list:
809
+ return _cse_homogeneous(exprs,
810
+ symbols=symbols, optimizations=optimizations,
811
+ postprocess=postprocess, order=order, ignore=ignore)
812
+
813
+ if isinstance(exprs, (int, float)):
814
+ exprs = sympify(exprs)
815
+
816
+ # Handle the case if just one expression was passed.
817
+ if isinstance(exprs, (Basic, MatrixBase)):
818
+ exprs = [exprs]
819
+
820
+ copy = exprs
821
+ temp = []
822
+ for e in exprs:
823
+ if isinstance(e, (Matrix, ImmutableMatrix)):
824
+ temp.append(Tuple(*e.flat()))
825
+ elif isinstance(e, (SparseMatrix, ImmutableSparseMatrix)):
826
+ temp.append(Tuple(*e.todok().items()))
827
+ else:
828
+ temp.append(e)
829
+ exprs = temp
830
+ del temp
831
+
832
+ if optimizations is None:
833
+ optimizations = []
834
+ elif optimizations == 'basic':
835
+ optimizations = basic_optimizations
836
+
837
+ # Preprocess the expressions to give us better optimization opportunities.
838
+ reduced_exprs = [preprocess_for_cse(e, optimizations) for e in exprs]
839
+
840
+ if symbols is None:
841
+ symbols = numbered_symbols(cls=Symbol)
842
+ else:
843
+ # In case we get passed an iterable with an __iter__ method instead of
844
+ # an actual iterator.
845
+ symbols = iter(symbols)
846
+
847
+ # Find other optimization opportunities.
848
+ opt_subs = opt_cse(reduced_exprs, order)
849
+
850
+ # Main CSE algorithm.
851
+ replacements, reduced_exprs = tree_cse(reduced_exprs, symbols, opt_subs,
852
+ order, ignore)
853
+
854
+ # Postprocess the expressions to return the expressions to canonical form.
855
+ exprs = copy
856
+ for i, (sym, subtree) in enumerate(replacements):
857
+ subtree = postprocess_for_cse(subtree, optimizations)
858
+ replacements[i] = (sym, subtree)
859
+ reduced_exprs = [postprocess_for_cse(e, optimizations)
860
+ for e in reduced_exprs]
861
+
862
+ # Get the matrices back
863
+ for i, e in enumerate(exprs):
864
+ if isinstance(e, (Matrix, ImmutableMatrix)):
865
+ reduced_exprs[i] = Matrix(e.rows, e.cols, reduced_exprs[i])
866
+ if isinstance(e, ImmutableMatrix):
867
+ reduced_exprs[i] = reduced_exprs[i].as_immutable()
868
+ elif isinstance(e, (SparseMatrix, ImmutableSparseMatrix)):
869
+ m = SparseMatrix(e.rows, e.cols, {})
870
+ for k, v in reduced_exprs[i]:
871
+ m[k] = v
872
+ if isinstance(e, ImmutableSparseMatrix):
873
+ m = m.as_immutable()
874
+ reduced_exprs[i] = m
875
+
876
+ if postprocess is None:
877
+ return replacements, reduced_exprs
878
+
879
+ return postprocess(replacements, reduced_exprs)
880
+
881
+
882
+ def _cse_homogeneous(exprs, **kwargs):
883
+ """
884
+ Same as ``cse`` but the ``reduced_exprs`` are returned
885
+ with the same type as ``exprs`` or a sympified version of the same.
886
+
887
+ Parameters
888
+ ==========
889
+
890
+ exprs : an Expr, iterable of Expr or dictionary with Expr values
891
+ the expressions in which repeated subexpressions will be identified
892
+ kwargs : additional arguments for the ``cse`` function
893
+
894
+ Returns
895
+ =======
896
+
897
+ replacements : list of (Symbol, expression) pairs
898
+ All of the common subexpressions that were replaced. Subexpressions
899
+ earlier in this list might show up in subexpressions later in this
900
+ list.
901
+ reduced_exprs : list of SymPy expressions
902
+ The reduced expressions with all of the replacements above.
903
+
904
+ Examples
905
+ ========
906
+
907
+ >>> from sympy.simplify.cse_main import cse
908
+ >>> from sympy import cos, Tuple, Matrix
909
+ >>> from sympy.abc import x
910
+ >>> output = lambda x: type(cse(x, list=False)[1])
911
+ >>> output(1)
912
+ <class 'sympy.core.numbers.One'>
913
+ >>> output('cos(x)')
914
+ <class 'str'>
915
+ >>> output(cos(x))
916
+ cos
917
+ >>> output(Tuple(1, x))
918
+ <class 'sympy.core.containers.Tuple'>
919
+ >>> output(Matrix([[1,0], [0,1]]))
920
+ <class 'sympy.matrices.dense.MutableDenseMatrix'>
921
+ >>> output([1, x])
922
+ <class 'list'>
923
+ >>> output((1, x))
924
+ <class 'tuple'>
925
+ >>> output({1, x})
926
+ <class 'set'>
927
+ """
928
+ if isinstance(exprs, str):
929
+ replacements, reduced_exprs = _cse_homogeneous(
930
+ sympify(exprs), **kwargs)
931
+ return replacements, repr(reduced_exprs)
932
+ if isinstance(exprs, (list, tuple, set)):
933
+ replacements, reduced_exprs = cse(exprs, **kwargs)
934
+ return replacements, type(exprs)(reduced_exprs)
935
+ if isinstance(exprs, dict):
936
+ keys = list(exprs.keys()) # In order to guarantee the order of the elements.
937
+ replacements, values = cse([exprs[k] for k in keys], **kwargs)
938
+ reduced_exprs = dict(zip(keys, values))
939
+ return replacements, reduced_exprs
940
+
941
+ try:
942
+ replacements, (reduced_exprs,) = cse(exprs, **kwargs)
943
+ except TypeError: # For example 'mpf' objects
944
+ return [], exprs
945
+ else:
946
+ return replacements, reduced_exprs
lib/python3.12/site-packages/sympy/simplify/epathtools.py ADDED
@@ -0,0 +1,356 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tools for manipulation of expressions using paths. """
2
+
3
+ from sympy.core import Basic
4
+
5
+
6
+ class EPath:
7
+ r"""
8
+ Manipulate expressions using paths.
9
+
10
+ EPath grammar in EBNF notation::
11
+
12
+ literal ::= /[A-Za-z_][A-Za-z_0-9]*/
13
+ number ::= /-?\d+/
14
+ type ::= literal
15
+ attribute ::= literal "?"
16
+ all ::= "*"
17
+ slice ::= "[" number? (":" number? (":" number?)?)? "]"
18
+ range ::= all | slice
19
+ query ::= (type | attribute) ("|" (type | attribute))*
20
+ selector ::= range | query range?
21
+ path ::= "/" selector ("/" selector)*
22
+
23
+ See the docstring of the epath() function.
24
+
25
+ """
26
+
27
+ __slots__ = ("_path", "_epath")
28
+
29
+ def __new__(cls, path):
30
+ """Construct new EPath. """
31
+ if isinstance(path, EPath):
32
+ return path
33
+
34
+ if not path:
35
+ raise ValueError("empty EPath")
36
+
37
+ _path = path
38
+
39
+ if path[0] == '/':
40
+ path = path[1:]
41
+ else:
42
+ raise NotImplementedError("non-root EPath")
43
+
44
+ epath = []
45
+
46
+ for selector in path.split('/'):
47
+ selector = selector.strip()
48
+
49
+ if not selector:
50
+ raise ValueError("empty selector")
51
+
52
+ index = 0
53
+
54
+ for c in selector:
55
+ if c.isalnum() or c in ('_', '|', '?'):
56
+ index += 1
57
+ else:
58
+ break
59
+
60
+ attrs = []
61
+ types = []
62
+
63
+ if index:
64
+ elements = selector[:index]
65
+ selector = selector[index:]
66
+
67
+ for element in elements.split('|'):
68
+ element = element.strip()
69
+
70
+ if not element:
71
+ raise ValueError("empty element")
72
+
73
+ if element.endswith('?'):
74
+ attrs.append(element[:-1])
75
+ else:
76
+ types.append(element)
77
+
78
+ span = None
79
+
80
+ if selector == '*':
81
+ pass
82
+ else:
83
+ if selector.startswith('['):
84
+ try:
85
+ i = selector.index(']')
86
+ except ValueError:
87
+ raise ValueError("expected ']', got EOL")
88
+
89
+ _span, span = selector[1:i], []
90
+
91
+ if ':' not in _span:
92
+ span = int(_span)
93
+ else:
94
+ for elt in _span.split(':', 3):
95
+ if not elt:
96
+ span.append(None)
97
+ else:
98
+ span.append(int(elt))
99
+
100
+ span = slice(*span)
101
+
102
+ selector = selector[i + 1:]
103
+
104
+ if selector:
105
+ raise ValueError("trailing characters in selector")
106
+
107
+ epath.append((attrs, types, span))
108
+
109
+ obj = object.__new__(cls)
110
+
111
+ obj._path = _path
112
+ obj._epath = epath
113
+
114
+ return obj
115
+
116
+ def __repr__(self):
117
+ return "%s(%r)" % (self.__class__.__name__, self._path)
118
+
119
+ def _get_ordered_args(self, expr):
120
+ """Sort ``expr.args`` using printing order. """
121
+ if expr.is_Add:
122
+ return expr.as_ordered_terms()
123
+ elif expr.is_Mul:
124
+ return expr.as_ordered_factors()
125
+ else:
126
+ return expr.args
127
+
128
+ def _hasattrs(self, expr, attrs):
129
+ """Check if ``expr`` has any of ``attrs``. """
130
+ for attr in attrs:
131
+ if not hasattr(expr, attr):
132
+ return False
133
+
134
+ return True
135
+
136
+ def _hastypes(self, expr, types):
137
+ """Check if ``expr`` is any of ``types``. """
138
+ _types = [ cls.__name__ for cls in expr.__class__.mro() ]
139
+ return bool(set(_types).intersection(types))
140
+
141
+ def _has(self, expr, attrs, types):
142
+ """Apply ``_hasattrs`` and ``_hastypes`` to ``expr``. """
143
+ if not (attrs or types):
144
+ return True
145
+
146
+ if attrs and self._hasattrs(expr, attrs):
147
+ return True
148
+
149
+ if types and self._hastypes(expr, types):
150
+ return True
151
+
152
+ return False
153
+
154
+ def apply(self, expr, func, args=None, kwargs=None):
155
+ """
156
+ Modify parts of an expression selected by a path.
157
+
158
+ Examples
159
+ ========
160
+
161
+ >>> from sympy.simplify.epathtools import EPath
162
+ >>> from sympy import sin, cos, E
163
+ >>> from sympy.abc import x, y, z, t
164
+
165
+ >>> path = EPath("/*/[0]/Symbol")
166
+ >>> expr = [((x, 1), 2), ((3, y), z)]
167
+
168
+ >>> path.apply(expr, lambda expr: expr**2)
169
+ [((x**2, 1), 2), ((3, y**2), z)]
170
+
171
+ >>> path = EPath("/*/*/Symbol")
172
+ >>> expr = t + sin(x + 1) + cos(x + y + E)
173
+
174
+ >>> path.apply(expr, lambda expr: 2*expr)
175
+ t + sin(2*x + 1) + cos(2*x + 2*y + E)
176
+
177
+ """
178
+ def _apply(path, expr, func):
179
+ if not path:
180
+ return func(expr)
181
+ else:
182
+ selector, path = path[0], path[1:]
183
+ attrs, types, span = selector
184
+
185
+ if isinstance(expr, Basic):
186
+ if not expr.is_Atom:
187
+ args, basic = self._get_ordered_args(expr), True
188
+ else:
189
+ return expr
190
+ elif hasattr(expr, '__iter__'):
191
+ args, basic = expr, False
192
+ else:
193
+ return expr
194
+
195
+ args = list(args)
196
+
197
+ if span is not None:
198
+ if isinstance(span, slice):
199
+ indices = range(*span.indices(len(args)))
200
+ else:
201
+ indices = [span]
202
+ else:
203
+ indices = range(len(args))
204
+
205
+ for i in indices:
206
+ try:
207
+ arg = args[i]
208
+ except IndexError:
209
+ continue
210
+
211
+ if self._has(arg, attrs, types):
212
+ args[i] = _apply(path, arg, func)
213
+
214
+ if basic:
215
+ return expr.func(*args)
216
+ else:
217
+ return expr.__class__(args)
218
+
219
+ _args, _kwargs = args or (), kwargs or {}
220
+ _func = lambda expr: func(expr, *_args, **_kwargs)
221
+
222
+ return _apply(self._epath, expr, _func)
223
+
224
+ def select(self, expr):
225
+ """
226
+ Retrieve parts of an expression selected by a path.
227
+
228
+ Examples
229
+ ========
230
+
231
+ >>> from sympy.simplify.epathtools import EPath
232
+ >>> from sympy import sin, cos, E
233
+ >>> from sympy.abc import x, y, z, t
234
+
235
+ >>> path = EPath("/*/[0]/Symbol")
236
+ >>> expr = [((x, 1), 2), ((3, y), z)]
237
+
238
+ >>> path.select(expr)
239
+ [x, y]
240
+
241
+ >>> path = EPath("/*/*/Symbol")
242
+ >>> expr = t + sin(x + 1) + cos(x + y + E)
243
+
244
+ >>> path.select(expr)
245
+ [x, x, y]
246
+
247
+ """
248
+ result = []
249
+
250
+ def _select(path, expr):
251
+ if not path:
252
+ result.append(expr)
253
+ else:
254
+ selector, path = path[0], path[1:]
255
+ attrs, types, span = selector
256
+
257
+ if isinstance(expr, Basic):
258
+ args = self._get_ordered_args(expr)
259
+ elif hasattr(expr, '__iter__'):
260
+ args = expr
261
+ else:
262
+ return
263
+
264
+ if span is not None:
265
+ if isinstance(span, slice):
266
+ args = args[span]
267
+ else:
268
+ try:
269
+ args = [args[span]]
270
+ except IndexError:
271
+ return
272
+
273
+ for arg in args:
274
+ if self._has(arg, attrs, types):
275
+ _select(path, arg)
276
+
277
+ _select(self._epath, expr)
278
+ return result
279
+
280
+
281
+ def epath(path, expr=None, func=None, args=None, kwargs=None):
282
+ r"""
283
+ Manipulate parts of an expression selected by a path.
284
+
285
+ Explanation
286
+ ===========
287
+
288
+ This function allows to manipulate large nested expressions in single
289
+ line of code, utilizing techniques to those applied in XML processing
290
+ standards (e.g. XPath).
291
+
292
+ If ``func`` is ``None``, :func:`epath` retrieves elements selected by
293
+ the ``path``. Otherwise it applies ``func`` to each matching element.
294
+
295
+ Note that it is more efficient to create an EPath object and use the select
296
+ and apply methods of that object, since this will compile the path string
297
+ only once. This function should only be used as a convenient shortcut for
298
+ interactive use.
299
+
300
+ This is the supported syntax:
301
+
302
+ * select all: ``/*``
303
+ Equivalent of ``for arg in args:``.
304
+ * select slice: ``/[0]`` or ``/[1:5]`` or ``/[1:5:2]``
305
+ Supports standard Python's slice syntax.
306
+ * select by type: ``/list`` or ``/list|tuple``
307
+ Emulates ``isinstance()``.
308
+ * select by attribute: ``/__iter__?``
309
+ Emulates ``hasattr()``.
310
+
311
+ Parameters
312
+ ==========
313
+
314
+ path : str | EPath
315
+ A path as a string or a compiled EPath.
316
+ expr : Basic | iterable
317
+ An expression or a container of expressions.
318
+ func : callable (optional)
319
+ A callable that will be applied to matching parts.
320
+ args : tuple (optional)
321
+ Additional positional arguments to ``func``.
322
+ kwargs : dict (optional)
323
+ Additional keyword arguments to ``func``.
324
+
325
+ Examples
326
+ ========
327
+
328
+ >>> from sympy.simplify.epathtools import epath
329
+ >>> from sympy import sin, cos, E
330
+ >>> from sympy.abc import x, y, z, t
331
+
332
+ >>> path = "/*/[0]/Symbol"
333
+ >>> expr = [((x, 1), 2), ((3, y), z)]
334
+
335
+ >>> epath(path, expr)
336
+ [x, y]
337
+ >>> epath(path, expr, lambda expr: expr**2)
338
+ [((x**2, 1), 2), ((3, y**2), z)]
339
+
340
+ >>> path = "/*/*/Symbol"
341
+ >>> expr = t + sin(x + 1) + cos(x + y + E)
342
+
343
+ >>> epath(path, expr)
344
+ [x, x, y]
345
+ >>> epath(path, expr, lambda expr: 2*expr)
346
+ t + sin(2*x + 1) + cos(2*x + 2*y + E)
347
+
348
+ """
349
+ _epath = EPath(path)
350
+
351
+ if expr is None:
352
+ return _epath
353
+ if func is None:
354
+ return _epath.select(expr)
355
+ else:
356
+ return _epath.apply(expr, func, args, kwargs)
lib/python3.12/site-packages/sympy/simplify/fu.py ADDED
@@ -0,0 +1,2111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict
2
+
3
+ from sympy.core.add import Add
4
+ from sympy.core.expr import Expr
5
+ from sympy.core.exprtools import Factors, gcd_terms, factor_terms
6
+ from sympy.core.function import expand_mul
7
+ from sympy.core.mul import Mul
8
+ from sympy.core.numbers import pi, I
9
+ from sympy.core.power import Pow
10
+ from sympy.core.singleton import S
11
+ from sympy.core.sorting import ordered
12
+ from sympy.core.symbol import Dummy
13
+ from sympy.core.sympify import sympify
14
+ from sympy.core.traversal import bottom_up
15
+ from sympy.functions.combinatorial.factorials import binomial
16
+ from sympy.functions.elementary.hyperbolic import (
17
+ cosh, sinh, tanh, coth, sech, csch, HyperbolicFunction)
18
+ from sympy.functions.elementary.trigonometric import (
19
+ cos, sin, tan, cot, sec, csc, sqrt, TrigonometricFunction)
20
+ from sympy.ntheory.factor_ import perfect_power
21
+ from sympy.polys.polytools import factor
22
+ from sympy.strategies.tree import greedy
23
+ from sympy.strategies.core import identity, debug
24
+
25
+ from sympy import SYMPY_DEBUG
26
+
27
+
28
+ # ================== Fu-like tools ===========================
29
+
30
+
31
+ def TR0(rv):
32
+ """Simplification of rational polynomials, trying to simplify
33
+ the expression, e.g. combine things like 3*x + 2*x, etc....
34
+ """
35
+ # although it would be nice to use cancel, it doesn't work
36
+ # with noncommutatives
37
+ return rv.normal().factor().expand()
38
+
39
+
40
+ def TR1(rv):
41
+ """Replace sec, csc with 1/cos, 1/sin
42
+
43
+ Examples
44
+ ========
45
+
46
+ >>> from sympy.simplify.fu import TR1, sec, csc
47
+ >>> from sympy.abc import x
48
+ >>> TR1(2*csc(x) + sec(x))
49
+ 1/cos(x) + 2/sin(x)
50
+ """
51
+
52
+ def f(rv):
53
+ if isinstance(rv, sec):
54
+ a = rv.args[0]
55
+ return S.One/cos(a)
56
+ elif isinstance(rv, csc):
57
+ a = rv.args[0]
58
+ return S.One/sin(a)
59
+ return rv
60
+
61
+ return bottom_up(rv, f)
62
+
63
+
64
+ def TR2(rv):
65
+ """Replace tan and cot with sin/cos and cos/sin
66
+
67
+ Examples
68
+ ========
69
+
70
+ >>> from sympy.simplify.fu import TR2
71
+ >>> from sympy.abc import x
72
+ >>> from sympy import tan, cot, sin, cos
73
+ >>> TR2(tan(x))
74
+ sin(x)/cos(x)
75
+ >>> TR2(cot(x))
76
+ cos(x)/sin(x)
77
+ >>> TR2(tan(tan(x) - sin(x)/cos(x)))
78
+ 0
79
+
80
+ """
81
+
82
+ def f(rv):
83
+ if isinstance(rv, tan):
84
+ a = rv.args[0]
85
+ return sin(a)/cos(a)
86
+ elif isinstance(rv, cot):
87
+ a = rv.args[0]
88
+ return cos(a)/sin(a)
89
+ return rv
90
+
91
+ return bottom_up(rv, f)
92
+
93
+
94
+ def TR2i(rv, half=False):
95
+ """Converts ratios involving sin and cos as follows::
96
+ sin(x)/cos(x) -> tan(x)
97
+ sin(x)/(cos(x) + 1) -> tan(x/2) if half=True
98
+
99
+ Examples
100
+ ========
101
+
102
+ >>> from sympy.simplify.fu import TR2i
103
+ >>> from sympy.abc import x, a
104
+ >>> from sympy import sin, cos
105
+ >>> TR2i(sin(x)/cos(x))
106
+ tan(x)
107
+
108
+ Powers of the numerator and denominator are also recognized
109
+
110
+ >>> TR2i(sin(x)**2/(cos(x) + 1)**2, half=True)
111
+ tan(x/2)**2
112
+
113
+ The transformation does not take place unless assumptions allow
114
+ (i.e. the base must be positive or the exponent must be an integer
115
+ for both numerator and denominator)
116
+
117
+ >>> TR2i(sin(x)**a/(cos(x) + 1)**a)
118
+ sin(x)**a/(cos(x) + 1)**a
119
+
120
+ """
121
+
122
+ def f(rv):
123
+ if not rv.is_Mul:
124
+ return rv
125
+
126
+ n, d = rv.as_numer_denom()
127
+ if n.is_Atom or d.is_Atom:
128
+ return rv
129
+
130
+ def ok(k, e):
131
+ # initial filtering of factors
132
+ return (
133
+ (e.is_integer or k.is_positive) and (
134
+ k.func in (sin, cos) or (half and
135
+ k.is_Add and
136
+ len(k.args) >= 2 and
137
+ any(any(isinstance(ai, cos) or ai.is_Pow and ai.base is cos
138
+ for ai in Mul.make_args(a)) for a in k.args))))
139
+
140
+ n = n.as_powers_dict()
141
+ ndone = [(k, n.pop(k)) for k in list(n.keys()) if not ok(k, n[k])]
142
+ if not n:
143
+ return rv
144
+
145
+ d = d.as_powers_dict()
146
+ ddone = [(k, d.pop(k)) for k in list(d.keys()) if not ok(k, d[k])]
147
+ if not d:
148
+ return rv
149
+
150
+ # factoring if necessary
151
+
152
+ def factorize(d, ddone):
153
+ newk = []
154
+ for k in d:
155
+ if k.is_Add and len(k.args) > 1:
156
+ knew = factor(k) if half else factor_terms(k)
157
+ if knew != k:
158
+ newk.append((k, knew))
159
+ if newk:
160
+ for i, (k, knew) in enumerate(newk):
161
+ del d[k]
162
+ newk[i] = knew
163
+ newk = Mul(*newk).as_powers_dict()
164
+ for k in newk:
165
+ v = d[k] + newk[k]
166
+ if ok(k, v):
167
+ d[k] = v
168
+ else:
169
+ ddone.append((k, v))
170
+ del newk
171
+ factorize(n, ndone)
172
+ factorize(d, ddone)
173
+
174
+ # joining
175
+ t = []
176
+ for k in n:
177
+ if isinstance(k, sin):
178
+ a = cos(k.args[0], evaluate=False)
179
+ if a in d and d[a] == n[k]:
180
+ t.append(tan(k.args[0])**n[k])
181
+ n[k] = d[a] = None
182
+ elif half:
183
+ a1 = 1 + a
184
+ if a1 in d and d[a1] == n[k]:
185
+ t.append((tan(k.args[0]/2))**n[k])
186
+ n[k] = d[a1] = None
187
+ elif isinstance(k, cos):
188
+ a = sin(k.args[0], evaluate=False)
189
+ if a in d and d[a] == n[k]:
190
+ t.append(tan(k.args[0])**-n[k])
191
+ n[k] = d[a] = None
192
+ elif half and k.is_Add and k.args[0] is S.One and \
193
+ isinstance(k.args[1], cos):
194
+ a = sin(k.args[1].args[0], evaluate=False)
195
+ if a in d and d[a] == n[k] and (d[a].is_integer or \
196
+ a.is_positive):
197
+ t.append(tan(a.args[0]/2)**-n[k])
198
+ n[k] = d[a] = None
199
+
200
+ if t:
201
+ rv = Mul(*(t + [b**e for b, e in n.items() if e]))/\
202
+ Mul(*[b**e for b, e in d.items() if e])
203
+ rv *= Mul(*[b**e for b, e in ndone])/Mul(*[b**e for b, e in ddone])
204
+
205
+ return rv
206
+
207
+ return bottom_up(rv, f)
208
+
209
+
210
+ def TR3(rv):
211
+ """Induced formula: example sin(-a) = -sin(a)
212
+
213
+ Examples
214
+ ========
215
+
216
+ >>> from sympy.simplify.fu import TR3
217
+ >>> from sympy.abc import x, y
218
+ >>> from sympy import pi
219
+ >>> from sympy import cos
220
+ >>> TR3(cos(y - x*(y - x)))
221
+ cos(x*(x - y) + y)
222
+ >>> cos(pi/2 + x)
223
+ -sin(x)
224
+ >>> cos(30*pi/2 + x)
225
+ -cos(x)
226
+
227
+ """
228
+ from sympy.simplify.simplify import signsimp
229
+
230
+ # Negative argument (already automatic for funcs like sin(-x) -> -sin(x)
231
+ # but more complicated expressions can use it, too). Also, trig angles
232
+ # between pi/4 and pi/2 are not reduced to an angle between 0 and pi/4.
233
+ # The following are automatically handled:
234
+ # Argument of type: pi/2 +/- angle
235
+ # Argument of type: pi +/- angle
236
+ # Argument of type : 2k*pi +/- angle
237
+
238
+ def f(rv):
239
+ if not isinstance(rv, TrigonometricFunction):
240
+ return rv
241
+ rv = rv.func(signsimp(rv.args[0]))
242
+ if not isinstance(rv, TrigonometricFunction):
243
+ return rv
244
+ if (rv.args[0] - S.Pi/4).is_positive is (S.Pi/2 - rv.args[0]).is_positive is True:
245
+ fmap = {cos: sin, sin: cos, tan: cot, cot: tan, sec: csc, csc: sec}
246
+ rv = fmap[type(rv)](S.Pi/2 - rv.args[0])
247
+ return rv
248
+
249
+ # touch numbers iside of trig functions to let them automatically update
250
+ rv = rv.replace(
251
+ lambda x: isinstance(x, TrigonometricFunction),
252
+ lambda x: x.replace(
253
+ lambda n: n.is_number and n.is_Mul,
254
+ lambda n: n.func(*n.args)))
255
+
256
+ return bottom_up(rv, f)
257
+
258
+
259
+ def TR4(rv):
260
+ """Identify values of special angles.
261
+
262
+ a= 0 pi/6 pi/4 pi/3 pi/2
263
+ ----------------------------------------------------
264
+ sin(a) 0 1/2 sqrt(2)/2 sqrt(3)/2 1
265
+ cos(a) 1 sqrt(3)/2 sqrt(2)/2 1/2 0
266
+ tan(a) 0 sqt(3)/3 1 sqrt(3) --
267
+
268
+ Examples
269
+ ========
270
+
271
+ >>> from sympy import pi
272
+ >>> from sympy import cos, sin, tan, cot
273
+ >>> for s in (0, pi/6, pi/4, pi/3, pi/2):
274
+ ... print('%s %s %s %s' % (cos(s), sin(s), tan(s), cot(s)))
275
+ ...
276
+ 1 0 0 zoo
277
+ sqrt(3)/2 1/2 sqrt(3)/3 sqrt(3)
278
+ sqrt(2)/2 sqrt(2)/2 1 1
279
+ 1/2 sqrt(3)/2 sqrt(3) sqrt(3)/3
280
+ 0 1 zoo 0
281
+ """
282
+ # special values at 0, pi/6, pi/4, pi/3, pi/2 already handled
283
+ return rv.replace(
284
+ lambda x:
285
+ isinstance(x, TrigonometricFunction) and
286
+ (r:=x.args[0]/pi).is_Rational and r.q in (1, 2, 3, 4, 6),
287
+ lambda x:
288
+ x.func(x.args[0].func(*x.args[0].args)))
289
+
290
+
291
+ def _TR56(rv, f, g, h, max, pow):
292
+ """Helper for TR5 and TR6 to replace f**2 with h(g**2)
293
+
294
+ Options
295
+ =======
296
+
297
+ max : controls size of exponent that can appear on f
298
+ e.g. if max=4 then f**4 will be changed to h(g**2)**2.
299
+ pow : controls whether the exponent must be a perfect power of 2
300
+ e.g. if pow=True (and max >= 6) then f**6 will not be changed
301
+ but f**8 will be changed to h(g**2)**4
302
+
303
+ >>> from sympy.simplify.fu import _TR56 as T
304
+ >>> from sympy.abc import x
305
+ >>> from sympy import sin, cos
306
+ >>> h = lambda x: 1 - x
307
+ >>> T(sin(x)**3, sin, cos, h, 4, False)
308
+ (1 - cos(x)**2)*sin(x)
309
+ >>> T(sin(x)**6, sin, cos, h, 6, False)
310
+ (1 - cos(x)**2)**3
311
+ >>> T(sin(x)**6, sin, cos, h, 6, True)
312
+ sin(x)**6
313
+ >>> T(sin(x)**8, sin, cos, h, 10, True)
314
+ (1 - cos(x)**2)**4
315
+ """
316
+
317
+ def _f(rv):
318
+ # I'm not sure if this transformation should target all even powers
319
+ # or only those expressible as powers of 2. Also, should it only
320
+ # make the changes in powers that appear in sums -- making an isolated
321
+ # change is not going to allow a simplification as far as I can tell.
322
+ if not (rv.is_Pow and rv.base.func == f):
323
+ return rv
324
+ if not rv.exp.is_real:
325
+ return rv
326
+
327
+ if (rv.exp < 0) == True:
328
+ return rv
329
+ if (rv.exp > max) == True:
330
+ return rv
331
+ if rv.exp == 1:
332
+ return rv
333
+ if rv.exp == 2:
334
+ return h(g(rv.base.args[0])**2)
335
+ else:
336
+ if rv.exp % 2 == 1:
337
+ e = rv.exp//2
338
+ return f(rv.base.args[0])*h(g(rv.base.args[0])**2)**e
339
+ elif rv.exp == 4:
340
+ e = 2
341
+ elif not pow:
342
+ if rv.exp % 2:
343
+ return rv
344
+ e = rv.exp//2
345
+ else:
346
+ p = perfect_power(rv.exp)
347
+ if not p:
348
+ return rv
349
+ e = rv.exp//2
350
+ return h(g(rv.base.args[0])**2)**e
351
+
352
+ return bottom_up(rv, _f)
353
+
354
+
355
+ def TR5(rv, max=4, pow=False):
356
+ """Replacement of sin**2 with 1 - cos(x)**2.
357
+
358
+ See _TR56 docstring for advanced use of ``max`` and ``pow``.
359
+
360
+ Examples
361
+ ========
362
+
363
+ >>> from sympy.simplify.fu import TR5
364
+ >>> from sympy.abc import x
365
+ >>> from sympy import sin
366
+ >>> TR5(sin(x)**2)
367
+ 1 - cos(x)**2
368
+ >>> TR5(sin(x)**-2) # unchanged
369
+ sin(x)**(-2)
370
+ >>> TR5(sin(x)**4)
371
+ (1 - cos(x)**2)**2
372
+ """
373
+ return _TR56(rv, sin, cos, lambda x: 1 - x, max=max, pow=pow)
374
+
375
+
376
+ def TR6(rv, max=4, pow=False):
377
+ """Replacement of cos**2 with 1 - sin(x)**2.
378
+
379
+ See _TR56 docstring for advanced use of ``max`` and ``pow``.
380
+
381
+ Examples
382
+ ========
383
+
384
+ >>> from sympy.simplify.fu import TR6
385
+ >>> from sympy.abc import x
386
+ >>> from sympy import cos
387
+ >>> TR6(cos(x)**2)
388
+ 1 - sin(x)**2
389
+ >>> TR6(cos(x)**-2) #unchanged
390
+ cos(x)**(-2)
391
+ >>> TR6(cos(x)**4)
392
+ (1 - sin(x)**2)**2
393
+ """
394
+ return _TR56(rv, cos, sin, lambda x: 1 - x, max=max, pow=pow)
395
+
396
+
397
+ def TR7(rv):
398
+ """Lowering the degree of cos(x)**2.
399
+
400
+ Examples
401
+ ========
402
+
403
+ >>> from sympy.simplify.fu import TR7
404
+ >>> from sympy.abc import x
405
+ >>> from sympy import cos
406
+ >>> TR7(cos(x)**2)
407
+ cos(2*x)/2 + 1/2
408
+ >>> TR7(cos(x)**2 + 1)
409
+ cos(2*x)/2 + 3/2
410
+
411
+ """
412
+
413
+ def f(rv):
414
+ if not (rv.is_Pow and rv.base.func == cos and rv.exp == 2):
415
+ return rv
416
+ return (1 + cos(2*rv.base.args[0]))/2
417
+
418
+ return bottom_up(rv, f)
419
+
420
+
421
+ def TR8(rv, first=True):
422
+ """Converting products of ``cos`` and/or ``sin`` to a sum or
423
+ difference of ``cos`` and or ``sin`` terms.
424
+
425
+ Examples
426
+ ========
427
+
428
+ >>> from sympy.simplify.fu import TR8
429
+ >>> from sympy import cos, sin
430
+ >>> TR8(cos(2)*cos(3))
431
+ cos(5)/2 + cos(1)/2
432
+ >>> TR8(cos(2)*sin(3))
433
+ sin(5)/2 + sin(1)/2
434
+ >>> TR8(sin(2)*sin(3))
435
+ -cos(5)/2 + cos(1)/2
436
+ """
437
+
438
+ def f(rv):
439
+ if not (
440
+ rv.is_Mul or
441
+ rv.is_Pow and
442
+ rv.base.func in (cos, sin) and
443
+ (rv.exp.is_integer or rv.base.is_positive)):
444
+ return rv
445
+
446
+ if first:
447
+ n, d = [expand_mul(i) for i in rv.as_numer_denom()]
448
+ newn = TR8(n, first=False)
449
+ newd = TR8(d, first=False)
450
+ if newn != n or newd != d:
451
+ rv = gcd_terms(newn/newd)
452
+ if rv.is_Mul and rv.args[0].is_Rational and \
453
+ len(rv.args) == 2 and rv.args[1].is_Add:
454
+ rv = Mul(*rv.as_coeff_Mul())
455
+ return rv
456
+
457
+ args = {cos: [], sin: [], None: []}
458
+ for a in Mul.make_args(rv):
459
+ if a.func in (cos, sin):
460
+ args[type(a)].append(a.args[0])
461
+ elif (a.is_Pow and a.exp.is_Integer and a.exp > 0 and \
462
+ a.base.func in (cos, sin)):
463
+ # XXX this is ok but pathological expression could be handled
464
+ # more efficiently as in TRmorrie
465
+ args[type(a.base)].extend([a.base.args[0]]*a.exp)
466
+ else:
467
+ args[None].append(a)
468
+ c = args[cos]
469
+ s = args[sin]
470
+ if not (c and s or len(c) > 1 or len(s) > 1):
471
+ return rv
472
+
473
+ args = args[None]
474
+ n = min(len(c), len(s))
475
+ for i in range(n):
476
+ a1 = s.pop()
477
+ a2 = c.pop()
478
+ args.append((sin(a1 + a2) + sin(a1 - a2))/2)
479
+ while len(c) > 1:
480
+ a1 = c.pop()
481
+ a2 = c.pop()
482
+ args.append((cos(a1 + a2) + cos(a1 - a2))/2)
483
+ if c:
484
+ args.append(cos(c.pop()))
485
+ while len(s) > 1:
486
+ a1 = s.pop()
487
+ a2 = s.pop()
488
+ args.append((-cos(a1 + a2) + cos(a1 - a2))/2)
489
+ if s:
490
+ args.append(sin(s.pop()))
491
+ return TR8(expand_mul(Mul(*args)))
492
+
493
+ return bottom_up(rv, f)
494
+
495
+
496
+ def TR9(rv):
497
+ """Sum of ``cos`` or ``sin`` terms as a product of ``cos`` or ``sin``.
498
+
499
+ Examples
500
+ ========
501
+
502
+ >>> from sympy.simplify.fu import TR9
503
+ >>> from sympy import cos, sin
504
+ >>> TR9(cos(1) + cos(2))
505
+ 2*cos(1/2)*cos(3/2)
506
+ >>> TR9(cos(1) + 2*sin(1) + 2*sin(2))
507
+ cos(1) + 4*sin(3/2)*cos(1/2)
508
+
509
+ If no change is made by TR9, no re-arrangement of the
510
+ expression will be made. For example, though factoring
511
+ of common term is attempted, if the factored expression
512
+ was not changed, the original expression will be returned:
513
+
514
+ >>> TR9(cos(3) + cos(3)*cos(2))
515
+ cos(3) + cos(2)*cos(3)
516
+
517
+ """
518
+
519
+ def f(rv):
520
+ if not rv.is_Add:
521
+ return rv
522
+
523
+ def do(rv, first=True):
524
+ # cos(a)+/-cos(b) can be combined into a product of cosines and
525
+ # sin(a)+/-sin(b) can be combined into a product of cosine and
526
+ # sine.
527
+ #
528
+ # If there are more than two args, the pairs which "work" will
529
+ # have a gcd extractable and the remaining two terms will have
530
+ # the above structure -- all pairs must be checked to find the
531
+ # ones that work. args that don't have a common set of symbols
532
+ # are skipped since this doesn't lead to a simpler formula and
533
+ # also has the arbitrariness of combining, for example, the x
534
+ # and y term instead of the y and z term in something like
535
+ # cos(x) + cos(y) + cos(z).
536
+
537
+ if not rv.is_Add:
538
+ return rv
539
+
540
+ args = list(ordered(rv.args))
541
+ if len(args) != 2:
542
+ hit = False
543
+ for i in range(len(args)):
544
+ ai = args[i]
545
+ if ai is None:
546
+ continue
547
+ for j in range(i + 1, len(args)):
548
+ aj = args[j]
549
+ if aj is None:
550
+ continue
551
+ was = ai + aj
552
+ new = do(was)
553
+ if new != was:
554
+ args[i] = new # update in place
555
+ args[j] = None
556
+ hit = True
557
+ break # go to next i
558
+ if hit:
559
+ rv = Add(*[_f for _f in args if _f])
560
+ if rv.is_Add:
561
+ rv = do(rv)
562
+
563
+ return rv
564
+
565
+ # two-arg Add
566
+ split = trig_split(*args)
567
+ if not split:
568
+ return rv
569
+ gcd, n1, n2, a, b, iscos = split
570
+
571
+ # application of rule if possible
572
+ if iscos:
573
+ if n1 == n2:
574
+ return gcd*n1*2*cos((a + b)/2)*cos((a - b)/2)
575
+ if n1 < 0:
576
+ a, b = b, a
577
+ return -2*gcd*sin((a + b)/2)*sin((a - b)/2)
578
+ else:
579
+ if n1 == n2:
580
+ return gcd*n1*2*sin((a + b)/2)*cos((a - b)/2)
581
+ if n1 < 0:
582
+ a, b = b, a
583
+ return 2*gcd*cos((a + b)/2)*sin((a - b)/2)
584
+
585
+ return process_common_addends(rv, do) # DON'T sift by free symbols
586
+
587
+ return bottom_up(rv, f)
588
+
589
+
590
+ def TR10(rv, first=True):
591
+ """Separate sums in ``cos`` and ``sin``.
592
+
593
+ Examples
594
+ ========
595
+
596
+ >>> from sympy.simplify.fu import TR10
597
+ >>> from sympy.abc import a, b, c
598
+ >>> from sympy import cos, sin
599
+ >>> TR10(cos(a + b))
600
+ -sin(a)*sin(b) + cos(a)*cos(b)
601
+ >>> TR10(sin(a + b))
602
+ sin(a)*cos(b) + sin(b)*cos(a)
603
+ >>> TR10(sin(a + b + c))
604
+ (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + \
605
+ (sin(a)*cos(b) + sin(b)*cos(a))*cos(c)
606
+ """
607
+
608
+ def f(rv):
609
+ if rv.func not in (cos, sin):
610
+ return rv
611
+
612
+ f = rv.func
613
+ arg = rv.args[0]
614
+ if arg.is_Add:
615
+ if first:
616
+ args = list(ordered(arg.args))
617
+ else:
618
+ args = list(arg.args)
619
+ a = args.pop()
620
+ b = Add._from_args(args)
621
+ if b.is_Add:
622
+ if f == sin:
623
+ return sin(a)*TR10(cos(b), first=False) + \
624
+ cos(a)*TR10(sin(b), first=False)
625
+ else:
626
+ return cos(a)*TR10(cos(b), first=False) - \
627
+ sin(a)*TR10(sin(b), first=False)
628
+ else:
629
+ if f == sin:
630
+ return sin(a)*cos(b) + cos(a)*sin(b)
631
+ else:
632
+ return cos(a)*cos(b) - sin(a)*sin(b)
633
+ return rv
634
+
635
+ return bottom_up(rv, f)
636
+
637
+
638
+ def TR10i(rv):
639
+ """Sum of products to function of sum.
640
+
641
+ Examples
642
+ ========
643
+
644
+ >>> from sympy.simplify.fu import TR10i
645
+ >>> from sympy import cos, sin, sqrt
646
+ >>> from sympy.abc import x
647
+
648
+ >>> TR10i(cos(1)*cos(3) + sin(1)*sin(3))
649
+ cos(2)
650
+ >>> TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3))
651
+ cos(3) + sin(4)
652
+ >>> TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x)
653
+ 2*sqrt(2)*x*sin(x + pi/6)
654
+
655
+ """
656
+ global _ROOT2, _ROOT3, _invROOT3
657
+ if _ROOT2 is None:
658
+ _roots()
659
+
660
+ def f(rv):
661
+ if not rv.is_Add:
662
+ return rv
663
+
664
+ def do(rv, first=True):
665
+ # args which can be expressed as A*(cos(a)*cos(b)+/-sin(a)*sin(b))
666
+ # or B*(cos(a)*sin(b)+/-cos(b)*sin(a)) can be combined into
667
+ # A*f(a+/-b) where f is either sin or cos.
668
+ #
669
+ # If there are more than two args, the pairs which "work" will have
670
+ # a gcd extractable and the remaining two terms will have the above
671
+ # structure -- all pairs must be checked to find the ones that
672
+ # work.
673
+
674
+ if not rv.is_Add:
675
+ return rv
676
+
677
+ args = list(ordered(rv.args))
678
+ if len(args) != 2:
679
+ hit = False
680
+ for i in range(len(args)):
681
+ ai = args[i]
682
+ if ai is None:
683
+ continue
684
+ for j in range(i + 1, len(args)):
685
+ aj = args[j]
686
+ if aj is None:
687
+ continue
688
+ was = ai + aj
689
+ new = do(was)
690
+ if new != was:
691
+ args[i] = new # update in place
692
+ args[j] = None
693
+ hit = True
694
+ break # go to next i
695
+ if hit:
696
+ rv = Add(*[_f for _f in args if _f])
697
+ if rv.is_Add:
698
+ rv = do(rv)
699
+
700
+ return rv
701
+
702
+ # two-arg Add
703
+ split = trig_split(*args, two=True)
704
+ if not split:
705
+ return rv
706
+ gcd, n1, n2, a, b, same = split
707
+
708
+ # identify and get c1 to be cos then apply rule if possible
709
+ if same: # coscos, sinsin
710
+ gcd = n1*gcd
711
+ if n1 == n2:
712
+ return gcd*cos(a - b)
713
+ return gcd*cos(a + b)
714
+ else: #cossin, cossin
715
+ gcd = n1*gcd
716
+ if n1 == n2:
717
+ return gcd*sin(a + b)
718
+ return gcd*sin(b - a)
719
+
720
+ rv = process_common_addends(
721
+ rv, do, lambda x: tuple(ordered(x.free_symbols)))
722
+
723
+ # need to check for inducible pairs in ratio of sqrt(3):1 that
724
+ # appeared in different lists when sorting by coefficient
725
+ while rv.is_Add:
726
+ byrad = defaultdict(list)
727
+ for a in rv.args:
728
+ hit = 0
729
+ if a.is_Mul:
730
+ for ai in a.args:
731
+ if ai.is_Pow and ai.exp is S.Half and \
732
+ ai.base.is_Integer:
733
+ byrad[ai].append(a)
734
+ hit = 1
735
+ break
736
+ if not hit:
737
+ byrad[S.One].append(a)
738
+
739
+ # no need to check all pairs -- just check for the onees
740
+ # that have the right ratio
741
+ args = []
742
+ for a in byrad:
743
+ for b in [_ROOT3*a, _invROOT3]:
744
+ if b in byrad:
745
+ for i in range(len(byrad[a])):
746
+ if byrad[a][i] is None:
747
+ continue
748
+ for j in range(len(byrad[b])):
749
+ if byrad[b][j] is None:
750
+ continue
751
+ was = Add(byrad[a][i] + byrad[b][j])
752
+ new = do(was)
753
+ if new != was:
754
+ args.append(new)
755
+ byrad[a][i] = None
756
+ byrad[b][j] = None
757
+ break
758
+ if args:
759
+ rv = Add(*(args + [Add(*[_f for _f in v if _f])
760
+ for v in byrad.values()]))
761
+ else:
762
+ rv = do(rv) # final pass to resolve any new inducible pairs
763
+ break
764
+
765
+ return rv
766
+
767
+ return bottom_up(rv, f)
768
+
769
+
770
+ def TR11(rv, base=None):
771
+ """Function of double angle to product. The ``base`` argument can be used
772
+ to indicate what is the un-doubled argument, e.g. if 3*pi/7 is the base
773
+ then cosine and sine functions with argument 6*pi/7 will be replaced.
774
+
775
+ Examples
776
+ ========
777
+
778
+ >>> from sympy.simplify.fu import TR11
779
+ >>> from sympy import cos, sin, pi
780
+ >>> from sympy.abc import x
781
+ >>> TR11(sin(2*x))
782
+ 2*sin(x)*cos(x)
783
+ >>> TR11(cos(2*x))
784
+ -sin(x)**2 + cos(x)**2
785
+ >>> TR11(sin(4*x))
786
+ 4*(-sin(x)**2 + cos(x)**2)*sin(x)*cos(x)
787
+ >>> TR11(sin(4*x/3))
788
+ 4*(-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3)
789
+
790
+ If the arguments are simply integers, no change is made
791
+ unless a base is provided:
792
+
793
+ >>> TR11(cos(2))
794
+ cos(2)
795
+ >>> TR11(cos(4), 2)
796
+ -sin(2)**2 + cos(2)**2
797
+
798
+ There is a subtle issue here in that autosimplification will convert
799
+ some higher angles to lower angles
800
+
801
+ >>> cos(6*pi/7) + cos(3*pi/7)
802
+ -cos(pi/7) + cos(3*pi/7)
803
+
804
+ The 6*pi/7 angle is now pi/7 but can be targeted with TR11 by supplying
805
+ the 3*pi/7 base:
806
+
807
+ >>> TR11(_, 3*pi/7)
808
+ -sin(3*pi/7)**2 + cos(3*pi/7)**2 + cos(3*pi/7)
809
+
810
+ """
811
+
812
+ def f(rv):
813
+ if rv.func not in (cos, sin):
814
+ return rv
815
+
816
+ if base:
817
+ f = rv.func
818
+ t = f(base*2)
819
+ co = S.One
820
+ if t.is_Mul:
821
+ co, t = t.as_coeff_Mul()
822
+ if t.func not in (cos, sin):
823
+ return rv
824
+ if rv.args[0] == t.args[0]:
825
+ c = cos(base)
826
+ s = sin(base)
827
+ if f is cos:
828
+ return (c**2 - s**2)/co
829
+ else:
830
+ return 2*c*s/co
831
+ return rv
832
+
833
+ elif not rv.args[0].is_Number:
834
+ # make a change if the leading coefficient's numerator is
835
+ # divisible by 2
836
+ c, m = rv.args[0].as_coeff_Mul(rational=True)
837
+ if c.p % 2 == 0:
838
+ arg = c.p//2*m/c.q
839
+ c = TR11(cos(arg))
840
+ s = TR11(sin(arg))
841
+ if rv.func == sin:
842
+ rv = 2*s*c
843
+ else:
844
+ rv = c**2 - s**2
845
+ return rv
846
+
847
+ return bottom_up(rv, f)
848
+
849
+
850
+ def _TR11(rv):
851
+ """
852
+ Helper for TR11 to find half-arguments for sin in factors of
853
+ num/den that appear in cos or sin factors in the den/num.
854
+
855
+ Examples
856
+ ========
857
+
858
+ >>> from sympy.simplify.fu import TR11, _TR11
859
+ >>> from sympy import cos, sin
860
+ >>> from sympy.abc import x
861
+ >>> TR11(sin(x/3)/(cos(x/6)))
862
+ sin(x/3)/cos(x/6)
863
+ >>> _TR11(sin(x/3)/(cos(x/6)))
864
+ 2*sin(x/6)
865
+ >>> TR11(sin(x/6)/(sin(x/3)))
866
+ sin(x/6)/sin(x/3)
867
+ >>> _TR11(sin(x/6)/(sin(x/3)))
868
+ 1/(2*cos(x/6))
869
+
870
+ """
871
+ def f(rv):
872
+ if not isinstance(rv, Expr):
873
+ return rv
874
+
875
+ def sincos_args(flat):
876
+ # find arguments of sin and cos that
877
+ # appears as bases in args of flat
878
+ # and have Integer exponents
879
+ args = defaultdict(set)
880
+ for fi in Mul.make_args(flat):
881
+ b, e = fi.as_base_exp()
882
+ if e.is_Integer and e > 0:
883
+ if b.func in (cos, sin):
884
+ args[type(b)].add(b.args[0])
885
+ return args
886
+ num_args, den_args = map(sincos_args, rv.as_numer_denom())
887
+ def handle_match(rv, num_args, den_args):
888
+ # for arg in sin args of num_args, look for arg/2
889
+ # in den_args and pass this half-angle to TR11
890
+ # for handling in rv
891
+ for narg in num_args[sin]:
892
+ half = narg/2
893
+ if half in den_args[cos]:
894
+ func = cos
895
+ elif half in den_args[sin]:
896
+ func = sin
897
+ else:
898
+ continue
899
+ rv = TR11(rv, half)
900
+ den_args[func].remove(half)
901
+ return rv
902
+ # sin in num, sin or cos in den
903
+ rv = handle_match(rv, num_args, den_args)
904
+ # sin in den, sin or cos in num
905
+ rv = handle_match(rv, den_args, num_args)
906
+ return rv
907
+
908
+ return bottom_up(rv, f)
909
+
910
+
911
+ def TR12(rv, first=True):
912
+ """Separate sums in ``tan``.
913
+
914
+ Examples
915
+ ========
916
+
917
+ >>> from sympy.abc import x, y
918
+ >>> from sympy import tan
919
+ >>> from sympy.simplify.fu import TR12
920
+ >>> TR12(tan(x + y))
921
+ (tan(x) + tan(y))/(-tan(x)*tan(y) + 1)
922
+ """
923
+
924
+ def f(rv):
925
+ if not rv.func == tan:
926
+ return rv
927
+
928
+ arg = rv.args[0]
929
+ if arg.is_Add:
930
+ if first:
931
+ args = list(ordered(arg.args))
932
+ else:
933
+ args = list(arg.args)
934
+ a = args.pop()
935
+ b = Add._from_args(args)
936
+ if b.is_Add:
937
+ tb = TR12(tan(b), first=False)
938
+ else:
939
+ tb = tan(b)
940
+ return (tan(a) + tb)/(1 - tan(a)*tb)
941
+ return rv
942
+
943
+ return bottom_up(rv, f)
944
+
945
+
946
+ def TR12i(rv):
947
+ """Combine tan arguments as
948
+ (tan(y) + tan(x))/(tan(x)*tan(y) - 1) -> -tan(x + y).
949
+
950
+ Examples
951
+ ========
952
+
953
+ >>> from sympy.simplify.fu import TR12i
954
+ >>> from sympy import tan
955
+ >>> from sympy.abc import a, b, c
956
+ >>> ta, tb, tc = [tan(i) for i in (a, b, c)]
957
+ >>> TR12i((ta + tb)/(-ta*tb + 1))
958
+ tan(a + b)
959
+ >>> TR12i((ta + tb)/(ta*tb - 1))
960
+ -tan(a + b)
961
+ >>> TR12i((-ta - tb)/(ta*tb - 1))
962
+ tan(a + b)
963
+ >>> eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1))
964
+ >>> TR12i(eq.expand())
965
+ -3*tan(a + b)*tan(a + c)/(2*(tan(a) + tan(b) - 1))
966
+ """
967
+ def f(rv):
968
+ if not (rv.is_Add or rv.is_Mul or rv.is_Pow):
969
+ return rv
970
+
971
+ n, d = rv.as_numer_denom()
972
+ if not d.args or not n.args:
973
+ return rv
974
+
975
+ dok = {}
976
+
977
+ def ok(di):
978
+ m = as_f_sign_1(di)
979
+ if m:
980
+ g, f, s = m
981
+ if s is S.NegativeOne and f.is_Mul and len(f.args) == 2 and \
982
+ all(isinstance(fi, tan) for fi in f.args):
983
+ return g, f
984
+
985
+ d_args = list(Mul.make_args(d))
986
+ for i, di in enumerate(d_args):
987
+ m = ok(di)
988
+ if m:
989
+ g, t = m
990
+ s = Add(*[_.args[0] for _ in t.args])
991
+ dok[s] = S.One
992
+ d_args[i] = g
993
+ continue
994
+ if di.is_Add:
995
+ di = factor(di)
996
+ if di.is_Mul:
997
+ d_args.extend(di.args)
998
+ d_args[i] = S.One
999
+ elif di.is_Pow and (di.exp.is_integer or di.base.is_positive):
1000
+ m = ok(di.base)
1001
+ if m:
1002
+ g, t = m
1003
+ s = Add(*[_.args[0] for _ in t.args])
1004
+ dok[s] = di.exp
1005
+ d_args[i] = g**di.exp
1006
+ else:
1007
+ di = factor(di)
1008
+ if di.is_Mul:
1009
+ d_args.extend(di.args)
1010
+ d_args[i] = S.One
1011
+ if not dok:
1012
+ return rv
1013
+
1014
+ def ok(ni):
1015
+ if ni.is_Add and len(ni.args) == 2:
1016
+ a, b = ni.args
1017
+ if isinstance(a, tan) and isinstance(b, tan):
1018
+ return a, b
1019
+ n_args = list(Mul.make_args(factor_terms(n)))
1020
+ hit = False
1021
+ for i, ni in enumerate(n_args):
1022
+ m = ok(ni)
1023
+ if not m:
1024
+ m = ok(-ni)
1025
+ if m:
1026
+ n_args[i] = S.NegativeOne
1027
+ else:
1028
+ if ni.is_Add:
1029
+ ni = factor(ni)
1030
+ if ni.is_Mul:
1031
+ n_args.extend(ni.args)
1032
+ n_args[i] = S.One
1033
+ continue
1034
+ elif ni.is_Pow and (
1035
+ ni.exp.is_integer or ni.base.is_positive):
1036
+ m = ok(ni.base)
1037
+ if m:
1038
+ n_args[i] = S.One
1039
+ else:
1040
+ ni = factor(ni)
1041
+ if ni.is_Mul:
1042
+ n_args.extend(ni.args)
1043
+ n_args[i] = S.One
1044
+ continue
1045
+ else:
1046
+ continue
1047
+ else:
1048
+ n_args[i] = S.One
1049
+ hit = True
1050
+ s = Add(*[_.args[0] for _ in m])
1051
+ ed = dok[s]
1052
+ newed = ed.extract_additively(S.One)
1053
+ if newed is not None:
1054
+ if newed:
1055
+ dok[s] = newed
1056
+ else:
1057
+ dok.pop(s)
1058
+ n_args[i] *= -tan(s)
1059
+
1060
+ if hit:
1061
+ rv = Mul(*n_args)/Mul(*d_args)/Mul(*[(Add(*[
1062
+ tan(a) for a in i.args]) - 1)**e for i, e in dok.items()])
1063
+
1064
+ return rv
1065
+
1066
+ return bottom_up(rv, f)
1067
+
1068
+
1069
+ def TR13(rv):
1070
+ """Change products of ``tan`` or ``cot``.
1071
+
1072
+ Examples
1073
+ ========
1074
+
1075
+ >>> from sympy.simplify.fu import TR13
1076
+ >>> from sympy import tan, cot
1077
+ >>> TR13(tan(3)*tan(2))
1078
+ -tan(2)/tan(5) - tan(3)/tan(5) + 1
1079
+ >>> TR13(cot(3)*cot(2))
1080
+ cot(2)*cot(5) + 1 + cot(3)*cot(5)
1081
+ """
1082
+
1083
+ def f(rv):
1084
+ if not rv.is_Mul:
1085
+ return rv
1086
+
1087
+ # XXX handle products of powers? or let power-reducing handle it?
1088
+ args = {tan: [], cot: [], None: []}
1089
+ for a in Mul.make_args(rv):
1090
+ if a.func in (tan, cot):
1091
+ args[type(a)].append(a.args[0])
1092
+ else:
1093
+ args[None].append(a)
1094
+ t = args[tan]
1095
+ c = args[cot]
1096
+ if len(t) < 2 and len(c) < 2:
1097
+ return rv
1098
+ args = args[None]
1099
+ while len(t) > 1:
1100
+ t1 = t.pop()
1101
+ t2 = t.pop()
1102
+ args.append(1 - (tan(t1)/tan(t1 + t2) + tan(t2)/tan(t1 + t2)))
1103
+ if t:
1104
+ args.append(tan(t.pop()))
1105
+ while len(c) > 1:
1106
+ t1 = c.pop()
1107
+ t2 = c.pop()
1108
+ args.append(1 + cot(t1)*cot(t1 + t2) + cot(t2)*cot(t1 + t2))
1109
+ if c:
1110
+ args.append(cot(c.pop()))
1111
+ return Mul(*args)
1112
+
1113
+ return bottom_up(rv, f)
1114
+
1115
+
1116
+ def TRmorrie(rv):
1117
+ """Returns cos(x)*cos(2*x)*...*cos(2**(k-1)*x) -> sin(2**k*x)/(2**k*sin(x))
1118
+
1119
+ Examples
1120
+ ========
1121
+
1122
+ >>> from sympy.simplify.fu import TRmorrie, TR8, TR3
1123
+ >>> from sympy.abc import x
1124
+ >>> from sympy import Mul, cos, pi
1125
+ >>> TRmorrie(cos(x)*cos(2*x))
1126
+ sin(4*x)/(4*sin(x))
1127
+ >>> TRmorrie(7*Mul(*[cos(x) for x in range(10)]))
1128
+ 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3))
1129
+
1130
+ Sometimes autosimplification will cause a power to be
1131
+ not recognized. e.g. in the following, cos(4*pi/7) automatically
1132
+ simplifies to -cos(3*pi/7) so only 2 of the 3 terms are
1133
+ recognized:
1134
+
1135
+ >>> TRmorrie(cos(pi/7)*cos(2*pi/7)*cos(4*pi/7))
1136
+ -sin(3*pi/7)*cos(3*pi/7)/(4*sin(pi/7))
1137
+
1138
+ A touch by TR8 resolves the expression to a Rational
1139
+
1140
+ >>> TR8(_)
1141
+ -1/8
1142
+
1143
+ In this case, if eq is unsimplified, the answer is obtained
1144
+ directly:
1145
+
1146
+ >>> eq = cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9)
1147
+ >>> TRmorrie(eq)
1148
+ 1/16
1149
+
1150
+ But if angles are made canonical with TR3 then the answer
1151
+ is not simplified without further work:
1152
+
1153
+ >>> TR3(eq)
1154
+ sin(pi/18)*cos(pi/9)*cos(2*pi/9)/2
1155
+ >>> TRmorrie(_)
1156
+ sin(pi/18)*sin(4*pi/9)/(8*sin(pi/9))
1157
+ >>> TR8(_)
1158
+ cos(7*pi/18)/(16*sin(pi/9))
1159
+ >>> TR3(_)
1160
+ 1/16
1161
+
1162
+ The original expression would have resolve to 1/16 directly with TR8,
1163
+ however:
1164
+
1165
+ >>> TR8(eq)
1166
+ 1/16
1167
+
1168
+ References
1169
+ ==========
1170
+
1171
+ .. [1] https://en.wikipedia.org/wiki/Morrie%27s_law
1172
+
1173
+ """
1174
+
1175
+ def f(rv, first=True):
1176
+ if not rv.is_Mul:
1177
+ return rv
1178
+ if first:
1179
+ n, d = rv.as_numer_denom()
1180
+ return f(n, 0)/f(d, 0)
1181
+
1182
+ args = defaultdict(list)
1183
+ coss = {}
1184
+ other = []
1185
+ for c in rv.args:
1186
+ b, e = c.as_base_exp()
1187
+ if e.is_Integer and isinstance(b, cos):
1188
+ co, a = b.args[0].as_coeff_Mul()
1189
+ args[a].append(co)
1190
+ coss[b] = e
1191
+ else:
1192
+ other.append(c)
1193
+
1194
+ new = []
1195
+ for a in args:
1196
+ c = args[a]
1197
+ c.sort()
1198
+ while c:
1199
+ k = 0
1200
+ cc = ci = c[0]
1201
+ while cc in c:
1202
+ k += 1
1203
+ cc *= 2
1204
+ if k > 1:
1205
+ newarg = sin(2**k*ci*a)/2**k/sin(ci*a)
1206
+ # see how many times this can be taken
1207
+ take = None
1208
+ ccs = []
1209
+ for i in range(k):
1210
+ cc /= 2
1211
+ key = cos(a*cc, evaluate=False)
1212
+ ccs.append(cc)
1213
+ take = min(coss[key], take or coss[key])
1214
+ # update exponent counts
1215
+ for i in range(k):
1216
+ cc = ccs.pop()
1217
+ key = cos(a*cc, evaluate=False)
1218
+ coss[key] -= take
1219
+ if not coss[key]:
1220
+ c.remove(cc)
1221
+ new.append(newarg**take)
1222
+ else:
1223
+ b = cos(c.pop(0)*a)
1224
+ other.append(b**coss[b])
1225
+
1226
+ if new:
1227
+ rv = Mul(*(new + other + [
1228
+ cos(k*a, evaluate=False) for a in args for k in args[a]]))
1229
+
1230
+ return rv
1231
+
1232
+ return bottom_up(rv, f)
1233
+
1234
+
1235
+ def TR14(rv, first=True):
1236
+ """Convert factored powers of sin and cos identities into simpler
1237
+ expressions.
1238
+
1239
+ Examples
1240
+ ========
1241
+
1242
+ >>> from sympy.simplify.fu import TR14
1243
+ >>> from sympy.abc import x, y
1244
+ >>> from sympy import cos, sin
1245
+ >>> TR14((cos(x) - 1)*(cos(x) + 1))
1246
+ -sin(x)**2
1247
+ >>> TR14((sin(x) - 1)*(sin(x) + 1))
1248
+ -cos(x)**2
1249
+ >>> p1 = (cos(x) + 1)*(cos(x) - 1)
1250
+ >>> p2 = (cos(y) - 1)*2*(cos(y) + 1)
1251
+ >>> p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1))
1252
+ >>> TR14(p1*p2*p3*(x - 1))
1253
+ -18*(x - 1)*sin(x)**2*sin(y)**4
1254
+
1255
+ """
1256
+
1257
+ def f(rv):
1258
+ if not rv.is_Mul:
1259
+ return rv
1260
+
1261
+ if first:
1262
+ # sort them by location in numerator and denominator
1263
+ # so the code below can just deal with positive exponents
1264
+ n, d = rv.as_numer_denom()
1265
+ if d is not S.One:
1266
+ newn = TR14(n, first=False)
1267
+ newd = TR14(d, first=False)
1268
+ if newn != n or newd != d:
1269
+ rv = newn/newd
1270
+ return rv
1271
+
1272
+ other = []
1273
+ process = []
1274
+ for a in rv.args:
1275
+ if a.is_Pow:
1276
+ b, e = a.as_base_exp()
1277
+ if not (e.is_integer or b.is_positive):
1278
+ other.append(a)
1279
+ continue
1280
+ a = b
1281
+ else:
1282
+ e = S.One
1283
+ m = as_f_sign_1(a)
1284
+ if not m or m[1].func not in (cos, sin):
1285
+ if e is S.One:
1286
+ other.append(a)
1287
+ else:
1288
+ other.append(a**e)
1289
+ continue
1290
+ g, f, si = m
1291
+ process.append((g, e.is_Number, e, f, si, a))
1292
+
1293
+ # sort them to get like terms next to each other
1294
+ process = list(ordered(process))
1295
+
1296
+ # keep track of whether there was any change
1297
+ nother = len(other)
1298
+
1299
+ # access keys
1300
+ keys = (g, t, e, f, si, a) = list(range(6))
1301
+
1302
+ while process:
1303
+ A = process.pop(0)
1304
+ if process:
1305
+ B = process[0]
1306
+
1307
+ if A[e].is_Number and B[e].is_Number:
1308
+ # both exponents are numbers
1309
+ if A[f] == B[f]:
1310
+ if A[si] != B[si]:
1311
+ B = process.pop(0)
1312
+ take = min(A[e], B[e])
1313
+
1314
+ # reinsert any remainder
1315
+ # the B will likely sort after A so check it first
1316
+ if B[e] != take:
1317
+ rem = [B[i] for i in keys]
1318
+ rem[e] -= take
1319
+ process.insert(0, rem)
1320
+ elif A[e] != take:
1321
+ rem = [A[i] for i in keys]
1322
+ rem[e] -= take
1323
+ process.insert(0, rem)
1324
+
1325
+ if isinstance(A[f], cos):
1326
+ t = sin
1327
+ else:
1328
+ t = cos
1329
+ other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take)
1330
+ continue
1331
+
1332
+ elif A[e] == B[e]:
1333
+ # both exponents are equal symbols
1334
+ if A[f] == B[f]:
1335
+ if A[si] != B[si]:
1336
+ B = process.pop(0)
1337
+ take = A[e]
1338
+ if isinstance(A[f], cos):
1339
+ t = sin
1340
+ else:
1341
+ t = cos
1342
+ other.append((-A[g]*B[g]*t(A[f].args[0])**2)**take)
1343
+ continue
1344
+
1345
+ # either we are done or neither condition above applied
1346
+ other.append(A[a]**A[e])
1347
+
1348
+ if len(other) != nother:
1349
+ rv = Mul(*other)
1350
+
1351
+ return rv
1352
+
1353
+ return bottom_up(rv, f)
1354
+
1355
+
1356
+ def TR15(rv, max=4, pow=False):
1357
+ """Convert sin(x)**-2 to 1 + cot(x)**2.
1358
+
1359
+ See _TR56 docstring for advanced use of ``max`` and ``pow``.
1360
+
1361
+ Examples
1362
+ ========
1363
+
1364
+ >>> from sympy.simplify.fu import TR15
1365
+ >>> from sympy.abc import x
1366
+ >>> from sympy import sin
1367
+ >>> TR15(1 - 1/sin(x)**2)
1368
+ -cot(x)**2
1369
+
1370
+ """
1371
+
1372
+ def f(rv):
1373
+ if not (isinstance(rv, Pow) and isinstance(rv.base, sin)):
1374
+ return rv
1375
+
1376
+ e = rv.exp
1377
+ if e % 2 == 1:
1378
+ return TR15(rv.base**(e + 1))/rv.base
1379
+
1380
+ ia = 1/rv
1381
+ a = _TR56(ia, sin, cot, lambda x: 1 + x, max=max, pow=pow)
1382
+ if a != ia:
1383
+ rv = a
1384
+ return rv
1385
+
1386
+ return bottom_up(rv, f)
1387
+
1388
+
1389
+ def TR16(rv, max=4, pow=False):
1390
+ """Convert cos(x)**-2 to 1 + tan(x)**2.
1391
+
1392
+ See _TR56 docstring for advanced use of ``max`` and ``pow``.
1393
+
1394
+ Examples
1395
+ ========
1396
+
1397
+ >>> from sympy.simplify.fu import TR16
1398
+ >>> from sympy.abc import x
1399
+ >>> from sympy import cos
1400
+ >>> TR16(1 - 1/cos(x)**2)
1401
+ -tan(x)**2
1402
+
1403
+ """
1404
+
1405
+ def f(rv):
1406
+ if not (isinstance(rv, Pow) and isinstance(rv.base, cos)):
1407
+ return rv
1408
+
1409
+ e = rv.exp
1410
+ if e % 2 == 1:
1411
+ return TR15(rv.base**(e + 1))/rv.base
1412
+
1413
+ ia = 1/rv
1414
+ a = _TR56(ia, cos, tan, lambda x: 1 + x, max=max, pow=pow)
1415
+ if a != ia:
1416
+ rv = a
1417
+ return rv
1418
+
1419
+ return bottom_up(rv, f)
1420
+
1421
+
1422
+ def TR111(rv):
1423
+ """Convert f(x)**-i to g(x)**i where either ``i`` is an integer
1424
+ or the base is positive and f, g are: tan, cot; sin, csc; or cos, sec.
1425
+
1426
+ Examples
1427
+ ========
1428
+
1429
+ >>> from sympy.simplify.fu import TR111
1430
+ >>> from sympy.abc import x
1431
+ >>> from sympy import tan
1432
+ >>> TR111(1 - 1/tan(x)**2)
1433
+ 1 - cot(x)**2
1434
+
1435
+ """
1436
+
1437
+ def f(rv):
1438
+ if not (
1439
+ isinstance(rv, Pow) and
1440
+ (rv.base.is_positive or rv.exp.is_integer and rv.exp.is_negative)):
1441
+ return rv
1442
+
1443
+ if isinstance(rv.base, tan):
1444
+ return cot(rv.base.args[0])**-rv.exp
1445
+ elif isinstance(rv.base, sin):
1446
+ return csc(rv.base.args[0])**-rv.exp
1447
+ elif isinstance(rv.base, cos):
1448
+ return sec(rv.base.args[0])**-rv.exp
1449
+ return rv
1450
+
1451
+ return bottom_up(rv, f)
1452
+
1453
+
1454
+ def TR22(rv, max=4, pow=False):
1455
+ """Convert tan(x)**2 to sec(x)**2 - 1 and cot(x)**2 to csc(x)**2 - 1.
1456
+
1457
+ See _TR56 docstring for advanced use of ``max`` and ``pow``.
1458
+
1459
+ Examples
1460
+ ========
1461
+
1462
+ >>> from sympy.simplify.fu import TR22
1463
+ >>> from sympy.abc import x
1464
+ >>> from sympy import tan, cot
1465
+ >>> TR22(1 + tan(x)**2)
1466
+ sec(x)**2
1467
+ >>> TR22(1 + cot(x)**2)
1468
+ csc(x)**2
1469
+
1470
+ """
1471
+
1472
+ def f(rv):
1473
+ if not (isinstance(rv, Pow) and rv.base.func in (cot, tan)):
1474
+ return rv
1475
+
1476
+ rv = _TR56(rv, tan, sec, lambda x: x - 1, max=max, pow=pow)
1477
+ rv = _TR56(rv, cot, csc, lambda x: x - 1, max=max, pow=pow)
1478
+ return rv
1479
+
1480
+ return bottom_up(rv, f)
1481
+
1482
+
1483
+ def TRpower(rv):
1484
+ """Convert sin(x)**n and cos(x)**n with positive n to sums.
1485
+
1486
+ Examples
1487
+ ========
1488
+
1489
+ >>> from sympy.simplify.fu import TRpower
1490
+ >>> from sympy.abc import x
1491
+ >>> from sympy import cos, sin
1492
+ >>> TRpower(sin(x)**6)
1493
+ -15*cos(2*x)/32 + 3*cos(4*x)/16 - cos(6*x)/32 + 5/16
1494
+ >>> TRpower(sin(x)**3*cos(2*x)**4)
1495
+ (3*sin(x)/4 - sin(3*x)/4)*(cos(4*x)/2 + cos(8*x)/8 + 3/8)
1496
+
1497
+ References
1498
+ ==========
1499
+
1500
+ .. [1] https://en.wikipedia.org/wiki/List_of_trigonometric_identities#Power-reduction_formulae
1501
+
1502
+ """
1503
+
1504
+ def f(rv):
1505
+ if not (isinstance(rv, Pow) and isinstance(rv.base, (sin, cos))):
1506
+ return rv
1507
+ b, n = rv.as_base_exp()
1508
+ x = b.args[0]
1509
+ if n.is_Integer and n.is_positive:
1510
+ if n.is_odd and isinstance(b, cos):
1511
+ rv = 2**(1-n)*Add(*[binomial(n, k)*cos((n - 2*k)*x)
1512
+ for k in range((n + 1)/2)])
1513
+ elif n.is_odd and isinstance(b, sin):
1514
+ rv = 2**(1-n)*S.NegativeOne**((n-1)/2)*Add(*[binomial(n, k)*
1515
+ S.NegativeOne**k*sin((n - 2*k)*x) for k in range((n + 1)/2)])
1516
+ elif n.is_even and isinstance(b, cos):
1517
+ rv = 2**(1-n)*Add(*[binomial(n, k)*cos((n - 2*k)*x)
1518
+ for k in range(n/2)])
1519
+ elif n.is_even and isinstance(b, sin):
1520
+ rv = 2**(1-n)*S.NegativeOne**(n/2)*Add(*[binomial(n, k)*
1521
+ S.NegativeOne**k*cos((n - 2*k)*x) for k in range(n/2)])
1522
+ if n.is_even:
1523
+ rv += 2**(-n)*binomial(n, n/2)
1524
+ return rv
1525
+
1526
+ return bottom_up(rv, f)
1527
+
1528
+
1529
+ def L(rv):
1530
+ """Return count of trigonometric functions in expression.
1531
+
1532
+ Examples
1533
+ ========
1534
+
1535
+ >>> from sympy.simplify.fu import L
1536
+ >>> from sympy.abc import x
1537
+ >>> from sympy import cos, sin
1538
+ >>> L(cos(x)+sin(x))
1539
+ 2
1540
+ """
1541
+ return S(rv.count(TrigonometricFunction))
1542
+
1543
+
1544
+ # ============== end of basic Fu-like tools =====================
1545
+
1546
+ if SYMPY_DEBUG:
1547
+ (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13,
1548
+ TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22
1549
+ )= list(map(debug,
1550
+ (TR0, TR1, TR2, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TR10, TR11, TR12, TR13,
1551
+ TR2i, TRmorrie, TR14, TR15, TR16, TR12i, TR111, TR22)))
1552
+
1553
+
1554
+ # tuples are chains -- (f, g) -> lambda x: g(f(x))
1555
+ # lists are choices -- [f, g] -> lambda x: min(f(x), g(x), key=objective)
1556
+
1557
+ CTR1 = [(TR5, TR0), (TR6, TR0), identity]
1558
+
1559
+ CTR2 = (TR11, [(TR5, TR0), (TR6, TR0), TR0])
1560
+
1561
+ CTR3 = [(TRmorrie, TR8, TR0), (TRmorrie, TR8, TR10i, TR0), identity]
1562
+
1563
+ CTR4 = [(TR4, TR10i), identity]
1564
+
1565
+ RL1 = (TR4, TR3, TR4, TR12, TR4, TR13, TR4, TR0)
1566
+
1567
+
1568
+ # XXX it's a little unclear how this one is to be implemented
1569
+ # see Fu paper of reference, page 7. What is the Union symbol referring to?
1570
+ # The diagram shows all these as one chain of transformations, but the
1571
+ # text refers to them being applied independently. Also, a break
1572
+ # if L starts to increase has not been implemented.
1573
+ RL2 = [
1574
+ (TR4, TR3, TR10, TR4, TR3, TR11),
1575
+ (TR5, TR7, TR11, TR4),
1576
+ (CTR3, CTR1, TR9, CTR2, TR4, TR9, TR9, CTR4),
1577
+ identity,
1578
+ ]
1579
+
1580
+
1581
+ def fu(rv, measure=lambda x: (L(x), x.count_ops())):
1582
+ """Attempt to simplify expression by using transformation rules given
1583
+ in the algorithm by Fu et al.
1584
+
1585
+ :func:`fu` will try to minimize the objective function ``measure``.
1586
+ By default this first minimizes the number of trig terms and then minimizes
1587
+ the number of total operations.
1588
+
1589
+ Examples
1590
+ ========
1591
+
1592
+ >>> from sympy.simplify.fu import fu
1593
+ >>> from sympy import cos, sin, tan, pi, S, sqrt
1594
+ >>> from sympy.abc import x, y, a, b
1595
+
1596
+ >>> fu(sin(50)**2 + cos(50)**2 + sin(pi/6))
1597
+ 3/2
1598
+ >>> fu(sqrt(6)*cos(x) + sqrt(2)*sin(x))
1599
+ 2*sqrt(2)*sin(x + pi/3)
1600
+
1601
+ CTR1 example
1602
+
1603
+ >>> eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2
1604
+ >>> fu(eq)
1605
+ cos(x)**4 - 2*cos(y)**2 + 2
1606
+
1607
+ CTR2 example
1608
+
1609
+ >>> fu(S.Half - cos(2*x)/2)
1610
+ sin(x)**2
1611
+
1612
+ CTR3 example
1613
+
1614
+ >>> fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b)))
1615
+ sqrt(2)*sin(a + b + pi/4)
1616
+
1617
+ CTR4 example
1618
+
1619
+ >>> fu(sqrt(3)*cos(x)/2 + sin(x)/2)
1620
+ sin(x + pi/3)
1621
+
1622
+ Example 1
1623
+
1624
+ >>> fu(1-sin(2*x)**2/4-sin(y)**2-cos(x)**4)
1625
+ -cos(x)**2 + cos(y)**2
1626
+
1627
+ Example 2
1628
+
1629
+ >>> fu(cos(4*pi/9))
1630
+ sin(pi/18)
1631
+ >>> fu(cos(pi/9)*cos(2*pi/9)*cos(3*pi/9)*cos(4*pi/9))
1632
+ 1/16
1633
+
1634
+ Example 3
1635
+
1636
+ >>> fu(tan(7*pi/18)+tan(5*pi/18)-sqrt(3)*tan(5*pi/18)*tan(7*pi/18))
1637
+ -sqrt(3)
1638
+
1639
+ Objective function example
1640
+
1641
+ >>> fu(sin(x)/cos(x)) # default objective function
1642
+ tan(x)
1643
+ >>> fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) # maximize op count
1644
+ sin(x)/cos(x)
1645
+
1646
+ References
1647
+ ==========
1648
+
1649
+ .. [1] https://www.sciencedirect.com/science/article/pii/S0895717706001609
1650
+ """
1651
+ fRL1 = greedy(RL1, measure)
1652
+ fRL2 = greedy(RL2, measure)
1653
+
1654
+ was = rv
1655
+ rv = sympify(rv)
1656
+ if not isinstance(rv, Expr):
1657
+ return rv.func(*[fu(a, measure=measure) for a in rv.args])
1658
+ rv = TR1(rv)
1659
+ if rv.has(tan, cot):
1660
+ rv1 = fRL1(rv)
1661
+ if (measure(rv1) < measure(rv)):
1662
+ rv = rv1
1663
+ if rv.has(tan, cot):
1664
+ rv = TR2(rv)
1665
+ if rv.has(sin, cos):
1666
+ rv1 = fRL2(rv)
1667
+ rv2 = TR8(TRmorrie(rv1))
1668
+ rv = min([was, rv, rv1, rv2], key=measure)
1669
+ return min(TR2i(rv), rv, key=measure)
1670
+
1671
+
1672
+ def process_common_addends(rv, do, key2=None, key1=True):
1673
+ """Apply ``do`` to addends of ``rv`` that (if ``key1=True``) share at least
1674
+ a common absolute value of their coefficient and the value of ``key2`` when
1675
+ applied to the argument. If ``key1`` is False ``key2`` must be supplied and
1676
+ will be the only key applied.
1677
+ """
1678
+
1679
+ # collect by absolute value of coefficient and key2
1680
+ absc = defaultdict(list)
1681
+ if key1:
1682
+ for a in rv.args:
1683
+ c, a = a.as_coeff_Mul()
1684
+ if c < 0:
1685
+ c = -c
1686
+ a = -a # put the sign on `a`
1687
+ absc[(c, key2(a) if key2 else 1)].append(a)
1688
+ elif key2:
1689
+ for a in rv.args:
1690
+ absc[(S.One, key2(a))].append(a)
1691
+ else:
1692
+ raise ValueError('must have at least one key')
1693
+
1694
+ args = []
1695
+ hit = False
1696
+ for k in absc:
1697
+ v = absc[k]
1698
+ c, _ = k
1699
+ if len(v) > 1:
1700
+ e = Add(*v, evaluate=False)
1701
+ new = do(e)
1702
+ if new != e:
1703
+ e = new
1704
+ hit = True
1705
+ args.append(c*e)
1706
+ else:
1707
+ args.append(c*v[0])
1708
+ if hit:
1709
+ rv = Add(*args)
1710
+
1711
+ return rv
1712
+
1713
+
1714
+ fufuncs = '''
1715
+ TR0 TR1 TR2 TR3 TR4 TR5 TR6 TR7 TR8 TR9 TR10 TR10i TR11
1716
+ TR12 TR13 L TR2i TRmorrie TR12i
1717
+ TR14 TR15 TR16 TR111 TR22'''.split()
1718
+ FU = dict(list(zip(fufuncs, list(map(locals().get, fufuncs)))))
1719
+
1720
+
1721
+ def _roots():
1722
+ global _ROOT2, _ROOT3, _invROOT3
1723
+ _ROOT2, _ROOT3 = sqrt(2), sqrt(3)
1724
+ _invROOT3 = 1/_ROOT3
1725
+ _ROOT2 = None
1726
+
1727
+
1728
+ def trig_split(a, b, two=False):
1729
+ """Return the gcd, s1, s2, a1, a2, bool where
1730
+
1731
+ If two is False (default) then::
1732
+ a + b = gcd*(s1*f(a1) + s2*f(a2)) where f = cos if bool else sin
1733
+ else:
1734
+ if bool, a + b was +/- cos(a1)*cos(a2) +/- sin(a1)*sin(a2) and equals
1735
+ n1*gcd*cos(a - b) if n1 == n2 else
1736
+ n1*gcd*cos(a + b)
1737
+ else a + b was +/- cos(a1)*sin(a2) +/- sin(a1)*cos(a2) and equals
1738
+ n1*gcd*sin(a + b) if n1 = n2 else
1739
+ n1*gcd*sin(b - a)
1740
+
1741
+ Examples
1742
+ ========
1743
+
1744
+ >>> from sympy.simplify.fu import trig_split
1745
+ >>> from sympy.abc import x, y, z
1746
+ >>> from sympy import cos, sin, sqrt
1747
+
1748
+ >>> trig_split(cos(x), cos(y))
1749
+ (1, 1, 1, x, y, True)
1750
+ >>> trig_split(2*cos(x), -2*cos(y))
1751
+ (2, 1, -1, x, y, True)
1752
+ >>> trig_split(cos(x)*sin(y), cos(y)*sin(y))
1753
+ (sin(y), 1, 1, x, y, True)
1754
+
1755
+ >>> trig_split(cos(x), -sqrt(3)*sin(x), two=True)
1756
+ (2, 1, -1, x, pi/6, False)
1757
+ >>> trig_split(cos(x), sin(x), two=True)
1758
+ (sqrt(2), 1, 1, x, pi/4, False)
1759
+ >>> trig_split(cos(x), -sin(x), two=True)
1760
+ (sqrt(2), 1, -1, x, pi/4, False)
1761
+ >>> trig_split(sqrt(2)*cos(x), -sqrt(6)*sin(x), two=True)
1762
+ (2*sqrt(2), 1, -1, x, pi/6, False)
1763
+ >>> trig_split(-sqrt(6)*cos(x), -sqrt(2)*sin(x), two=True)
1764
+ (-2*sqrt(2), 1, 1, x, pi/3, False)
1765
+ >>> trig_split(cos(x)/sqrt(6), sin(x)/sqrt(2), two=True)
1766
+ (sqrt(6)/3, 1, 1, x, pi/6, False)
1767
+ >>> trig_split(-sqrt(6)*cos(x)*sin(y), -sqrt(2)*sin(x)*sin(y), two=True)
1768
+ (-2*sqrt(2)*sin(y), 1, 1, x, pi/3, False)
1769
+
1770
+ >>> trig_split(cos(x), sin(x))
1771
+ >>> trig_split(cos(x), sin(z))
1772
+ >>> trig_split(2*cos(x), -sin(x))
1773
+ >>> trig_split(cos(x), -sqrt(3)*sin(x))
1774
+ >>> trig_split(cos(x)*cos(y), sin(x)*sin(z))
1775
+ >>> trig_split(cos(x)*cos(y), sin(x)*sin(y))
1776
+ >>> trig_split(-sqrt(6)*cos(x), sqrt(2)*sin(x)*sin(y), two=True)
1777
+ """
1778
+ global _ROOT2, _ROOT3, _invROOT3
1779
+ if _ROOT2 is None:
1780
+ _roots()
1781
+
1782
+ a, b = [Factors(i) for i in (a, b)]
1783
+ ua, ub = a.normal(b)
1784
+ gcd = a.gcd(b).as_expr()
1785
+ n1 = n2 = 1
1786
+ if S.NegativeOne in ua.factors:
1787
+ ua = ua.quo(S.NegativeOne)
1788
+ n1 = -n1
1789
+ elif S.NegativeOne in ub.factors:
1790
+ ub = ub.quo(S.NegativeOne)
1791
+ n2 = -n2
1792
+ a, b = [i.as_expr() for i in (ua, ub)]
1793
+
1794
+ def pow_cos_sin(a, two):
1795
+ """Return ``a`` as a tuple (r, c, s) such that
1796
+ ``a = (r or 1)*(c or 1)*(s or 1)``.
1797
+
1798
+ Three arguments are returned (radical, c-factor, s-factor) as
1799
+ long as the conditions set by ``two`` are met; otherwise None is
1800
+ returned. If ``two`` is True there will be one or two non-None
1801
+ values in the tuple: c and s or c and r or s and r or s or c with c
1802
+ being a cosine function (if possible) else a sine, and s being a sine
1803
+ function (if possible) else oosine. If ``two`` is False then there
1804
+ will only be a c or s term in the tuple.
1805
+
1806
+ ``two`` also require that either two cos and/or sin be present (with
1807
+ the condition that if the functions are the same the arguments are
1808
+ different or vice versa) or that a single cosine or a single sine
1809
+ be present with an optional radical.
1810
+
1811
+ If the above conditions dictated by ``two`` are not met then None
1812
+ is returned.
1813
+ """
1814
+ c = s = None
1815
+ co = S.One
1816
+ if a.is_Mul:
1817
+ co, a = a.as_coeff_Mul()
1818
+ if len(a.args) > 2 or not two:
1819
+ return None
1820
+ if a.is_Mul:
1821
+ args = list(a.args)
1822
+ else:
1823
+ args = [a]
1824
+ a = args.pop(0)
1825
+ if isinstance(a, cos):
1826
+ c = a
1827
+ elif isinstance(a, sin):
1828
+ s = a
1829
+ elif a.is_Pow and a.exp is S.Half: # autoeval doesn't allow -1/2
1830
+ co *= a
1831
+ else:
1832
+ return None
1833
+ if args:
1834
+ b = args[0]
1835
+ if isinstance(b, cos):
1836
+ if c:
1837
+ s = b
1838
+ else:
1839
+ c = b
1840
+ elif isinstance(b, sin):
1841
+ if s:
1842
+ c = b
1843
+ else:
1844
+ s = b
1845
+ elif b.is_Pow and b.exp is S.Half:
1846
+ co *= b
1847
+ else:
1848
+ return None
1849
+ return co if co is not S.One else None, c, s
1850
+ elif isinstance(a, cos):
1851
+ c = a
1852
+ elif isinstance(a, sin):
1853
+ s = a
1854
+ if c is None and s is None:
1855
+ return
1856
+ co = co if co is not S.One else None
1857
+ return co, c, s
1858
+
1859
+ # get the parts
1860
+ m = pow_cos_sin(a, two)
1861
+ if m is None:
1862
+ return
1863
+ coa, ca, sa = m
1864
+ m = pow_cos_sin(b, two)
1865
+ if m is None:
1866
+ return
1867
+ cob, cb, sb = m
1868
+
1869
+ # check them
1870
+ if (not ca) and cb or ca and isinstance(ca, sin):
1871
+ coa, ca, sa, cob, cb, sb = cob, cb, sb, coa, ca, sa
1872
+ n1, n2 = n2, n1
1873
+ if not two: # need cos(x) and cos(y) or sin(x) and sin(y)
1874
+ c = ca or sa
1875
+ s = cb or sb
1876
+ if not isinstance(c, s.func):
1877
+ return None
1878
+ return gcd, n1, n2, c.args[0], s.args[0], isinstance(c, cos)
1879
+ else:
1880
+ if not coa and not cob:
1881
+ if (ca and cb and sa and sb):
1882
+ if isinstance(ca, sa.func) is not isinstance(cb, sb.func):
1883
+ return
1884
+ args = {j.args for j in (ca, sa)}
1885
+ if not all(i.args in args for i in (cb, sb)):
1886
+ return
1887
+ return gcd, n1, n2, ca.args[0], sa.args[0], isinstance(ca, sa.func)
1888
+ if ca and sa or cb and sb or \
1889
+ two and (ca is None and sa is None or cb is None and sb is None):
1890
+ return
1891
+ c = ca or sa
1892
+ s = cb or sb
1893
+ if c.args != s.args:
1894
+ return
1895
+ if not coa:
1896
+ coa = S.One
1897
+ if not cob:
1898
+ cob = S.One
1899
+ if coa is cob:
1900
+ gcd *= _ROOT2
1901
+ return gcd, n1, n2, c.args[0], pi/4, False
1902
+ elif coa/cob == _ROOT3:
1903
+ gcd *= 2*cob
1904
+ return gcd, n1, n2, c.args[0], pi/3, False
1905
+ elif coa/cob == _invROOT3:
1906
+ gcd *= 2*coa
1907
+ return gcd, n1, n2, c.args[0], pi/6, False
1908
+
1909
+
1910
+ def as_f_sign_1(e):
1911
+ """If ``e`` is a sum that can be written as ``g*(a + s)`` where
1912
+ ``s`` is ``+/-1``, return ``g``, ``a``, and ``s`` where ``a`` does
1913
+ not have a leading negative coefficient.
1914
+
1915
+ Examples
1916
+ ========
1917
+
1918
+ >>> from sympy.simplify.fu import as_f_sign_1
1919
+ >>> from sympy.abc import x
1920
+ >>> as_f_sign_1(x + 1)
1921
+ (1, x, 1)
1922
+ >>> as_f_sign_1(x - 1)
1923
+ (1, x, -1)
1924
+ >>> as_f_sign_1(-x + 1)
1925
+ (-1, x, -1)
1926
+ >>> as_f_sign_1(-x - 1)
1927
+ (-1, x, 1)
1928
+ >>> as_f_sign_1(2*x + 2)
1929
+ (2, x, 1)
1930
+ """
1931
+ if not e.is_Add or len(e.args) != 2:
1932
+ return
1933
+ # exact match
1934
+ a, b = e.args
1935
+ if a in (S.NegativeOne, S.One):
1936
+ g = S.One
1937
+ if b.is_Mul and b.args[0].is_Number and b.args[0] < 0:
1938
+ a, b = -a, -b
1939
+ g = -g
1940
+ return g, b, a
1941
+ # gcd match
1942
+ a, b = [Factors(i) for i in e.args]
1943
+ ua, ub = a.normal(b)
1944
+ gcd = a.gcd(b).as_expr()
1945
+ if S.NegativeOne in ua.factors:
1946
+ ua = ua.quo(S.NegativeOne)
1947
+ n1 = -1
1948
+ n2 = 1
1949
+ elif S.NegativeOne in ub.factors:
1950
+ ub = ub.quo(S.NegativeOne)
1951
+ n1 = 1
1952
+ n2 = -1
1953
+ else:
1954
+ n1 = n2 = 1
1955
+ a, b = [i.as_expr() for i in (ua, ub)]
1956
+ if a is S.One:
1957
+ a, b = b, a
1958
+ n1, n2 = n2, n1
1959
+ if n1 == -1:
1960
+ gcd = -gcd
1961
+ n2 = -n2
1962
+
1963
+ if b is S.One:
1964
+ return gcd, a, n2
1965
+
1966
+
1967
+ def _osborne(e, d):
1968
+ """Replace all hyperbolic functions with trig functions using
1969
+ the Osborne rule.
1970
+
1971
+ Notes
1972
+ =====
1973
+
1974
+ ``d`` is a dummy variable to prevent automatic evaluation
1975
+ of trigonometric/hyperbolic functions.
1976
+
1977
+
1978
+ References
1979
+ ==========
1980
+
1981
+ .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
1982
+ """
1983
+
1984
+ def f(rv):
1985
+ if not isinstance(rv, HyperbolicFunction):
1986
+ return rv
1987
+ a = rv.args[0]
1988
+ a = a*d if not a.is_Add else Add._from_args([i*d for i in a.args])
1989
+ if isinstance(rv, sinh):
1990
+ return I*sin(a)
1991
+ elif isinstance(rv, cosh):
1992
+ return cos(a)
1993
+ elif isinstance(rv, tanh):
1994
+ return I*tan(a)
1995
+ elif isinstance(rv, coth):
1996
+ return cot(a)/I
1997
+ elif isinstance(rv, sech):
1998
+ return sec(a)
1999
+ elif isinstance(rv, csch):
2000
+ return csc(a)/I
2001
+ else:
2002
+ raise NotImplementedError('unhandled %s' % rv.func)
2003
+
2004
+ return bottom_up(e, f)
2005
+
2006
+
2007
+ def _osbornei(e, d):
2008
+ """Replace all trig functions with hyperbolic functions using
2009
+ the Osborne rule.
2010
+
2011
+ Notes
2012
+ =====
2013
+
2014
+ ``d`` is a dummy variable to prevent automatic evaluation
2015
+ of trigonometric/hyperbolic functions.
2016
+
2017
+ References
2018
+ ==========
2019
+
2020
+ .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
2021
+ """
2022
+
2023
+ def f(rv):
2024
+ if not isinstance(rv, TrigonometricFunction):
2025
+ return rv
2026
+ const, x = rv.args[0].as_independent(d, as_Add=True)
2027
+ a = x.xreplace({d: S.One}) + const*I
2028
+ if isinstance(rv, sin):
2029
+ return sinh(a)/I
2030
+ elif isinstance(rv, cos):
2031
+ return cosh(a)
2032
+ elif isinstance(rv, tan):
2033
+ return tanh(a)/I
2034
+ elif isinstance(rv, cot):
2035
+ return coth(a)*I
2036
+ elif isinstance(rv, sec):
2037
+ return sech(a)
2038
+ elif isinstance(rv, csc):
2039
+ return csch(a)*I
2040
+ else:
2041
+ raise NotImplementedError('unhandled %s' % rv.func)
2042
+
2043
+ return bottom_up(e, f)
2044
+
2045
+
2046
+ def hyper_as_trig(rv):
2047
+ """Return an expression containing hyperbolic functions in terms
2048
+ of trigonometric functions. Any trigonometric functions initially
2049
+ present are replaced with Dummy symbols and the function to undo
2050
+ the masking and the conversion back to hyperbolics is also returned. It
2051
+ should always be true that::
2052
+
2053
+ t, f = hyper_as_trig(expr)
2054
+ expr == f(t)
2055
+
2056
+ Examples
2057
+ ========
2058
+
2059
+ >>> from sympy.simplify.fu import hyper_as_trig, fu
2060
+ >>> from sympy.abc import x
2061
+ >>> from sympy import cosh, sinh
2062
+ >>> eq = sinh(x)**2 + cosh(x)**2
2063
+ >>> t, f = hyper_as_trig(eq)
2064
+ >>> f(fu(t))
2065
+ cosh(2*x)
2066
+
2067
+ References
2068
+ ==========
2069
+
2070
+ .. [1] https://en.wikipedia.org/wiki/Hyperbolic_function
2071
+ """
2072
+ from sympy.simplify.simplify import signsimp
2073
+ from sympy.simplify.radsimp import collect
2074
+
2075
+ # mask off trig functions
2076
+ trigs = rv.atoms(TrigonometricFunction)
2077
+ reps = [(t, Dummy()) for t in trigs]
2078
+ masked = rv.xreplace(dict(reps))
2079
+
2080
+ # get inversion substitutions in place
2081
+ reps = [(v, k) for k, v in reps]
2082
+
2083
+ d = Dummy()
2084
+
2085
+ return _osborne(masked, d), lambda x: collect(signsimp(
2086
+ _osbornei(x, d).xreplace(dict(reps))), S.ImaginaryUnit)
2087
+
2088
+
2089
+ def sincos_to_sum(expr):
2090
+ """Convert products and powers of sin and cos to sums.
2091
+
2092
+ Explanation
2093
+ ===========
2094
+
2095
+ Applied power reduction TRpower first, then expands products, and
2096
+ converts products to sums with TR8.
2097
+
2098
+ Examples
2099
+ ========
2100
+
2101
+ >>> from sympy.simplify.fu import sincos_to_sum
2102
+ >>> from sympy.abc import x
2103
+ >>> from sympy import cos, sin
2104
+ >>> sincos_to_sum(16*sin(x)**3*cos(2*x)**2)
2105
+ 7*sin(x) - 5*sin(3*x) + 3*sin(5*x) - sin(7*x)
2106
+ """
2107
+
2108
+ if not expr.has(cos, sin):
2109
+ return expr
2110
+ else:
2111
+ return TR8(expand_mul(TRpower(expr)))
lib/python3.12/site-packages/sympy/simplify/gammasimp.py ADDED
@@ -0,0 +1,497 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Function, S, Mul, Pow, Add
2
+ from sympy.core.sorting import ordered, default_sort_key
3
+ from sympy.core.function import expand_func
4
+ from sympy.core.symbol import Dummy
5
+ from sympy.functions import gamma, sqrt, sin
6
+ from sympy.polys import factor, cancel
7
+ from sympy.utilities.iterables import sift, uniq
8
+
9
+
10
+ def gammasimp(expr):
11
+ r"""
12
+ Simplify expressions with gamma functions.
13
+
14
+ Explanation
15
+ ===========
16
+
17
+ This function takes as input an expression containing gamma
18
+ functions or functions that can be rewritten in terms of gamma
19
+ functions and tries to minimize the number of those functions and
20
+ reduce the size of their arguments.
21
+
22
+ The algorithm works by rewriting all gamma functions as expressions
23
+ involving rising factorials (Pochhammer symbols) and applies
24
+ recurrence relations and other transformations applicable to rising
25
+ factorials, to reduce their arguments, possibly letting the resulting
26
+ rising factorial to cancel. Rising factorials with the second argument
27
+ being an integer are expanded into polynomial forms and finally all
28
+ other rising factorial are rewritten in terms of gamma functions.
29
+
30
+ Then the following two steps are performed.
31
+
32
+ 1. Reduce the number of gammas by applying the reflection theorem
33
+ gamma(x)*gamma(1-x) == pi/sin(pi*x).
34
+ 2. Reduce the number of gammas by applying the multiplication theorem
35
+ gamma(x)*gamma(x+1/n)*...*gamma(x+(n-1)/n) == C*gamma(n*x).
36
+
37
+ It then reduces the number of prefactors by absorbing them into gammas
38
+ where possible and expands gammas with rational argument.
39
+
40
+ All transformation rules can be found (or were derived from) here:
41
+
42
+ .. [1] https://functions.wolfram.com/GammaBetaErf/Pochhammer/17/01/02/
43
+ .. [2] https://functions.wolfram.com/GammaBetaErf/Pochhammer/27/01/0005/
44
+
45
+ Examples
46
+ ========
47
+
48
+ >>> from sympy.simplify import gammasimp
49
+ >>> from sympy import gamma, Symbol
50
+ >>> from sympy.abc import x
51
+ >>> n = Symbol('n', integer = True)
52
+
53
+ >>> gammasimp(gamma(x)/gamma(x - 3))
54
+ (x - 3)*(x - 2)*(x - 1)
55
+ >>> gammasimp(gamma(n + 3))
56
+ gamma(n + 3)
57
+
58
+ """
59
+
60
+ expr = expr.rewrite(gamma)
61
+
62
+ # compute_ST will be looking for Functions and we don't want
63
+ # it looking for non-gamma functions: issue 22606
64
+ # so we mask free, non-gamma functions
65
+ f = expr.atoms(Function)
66
+ # take out gammas
67
+ gammas = {i for i in f if isinstance(i, gamma)}
68
+ if not gammas:
69
+ return expr # avoid side effects like factoring
70
+ f -= gammas
71
+ # keep only those without bound symbols
72
+ f = f & expr.as_dummy().atoms(Function)
73
+ if f:
74
+ dum, fun, simp = zip(*[
75
+ (Dummy(), fi, fi.func(*[
76
+ _gammasimp(a, as_comb=False) for a in fi.args]))
77
+ for fi in ordered(f)])
78
+ d = expr.xreplace(dict(zip(fun, dum)))
79
+ return _gammasimp(d, as_comb=False).xreplace(dict(zip(dum, simp)))
80
+
81
+ return _gammasimp(expr, as_comb=False)
82
+
83
+
84
+ def _gammasimp(expr, as_comb):
85
+ """
86
+ Helper function for gammasimp and combsimp.
87
+
88
+ Explanation
89
+ ===========
90
+
91
+ Simplifies expressions written in terms of gamma function. If
92
+ as_comb is True, it tries to preserve integer arguments. See
93
+ docstring of gammasimp for more information. This was part of
94
+ combsimp() in combsimp.py.
95
+ """
96
+ expr = expr.replace(gamma,
97
+ lambda n: _rf(1, (n - 1).expand()))
98
+
99
+ if as_comb:
100
+ expr = expr.replace(_rf,
101
+ lambda a, b: gamma(b + 1))
102
+ else:
103
+ expr = expr.replace(_rf,
104
+ lambda a, b: gamma(a + b)/gamma(a))
105
+
106
+ def rule_gamma(expr, level=0):
107
+ """ Simplify products of gamma functions further. """
108
+
109
+ if expr.is_Atom:
110
+ return expr
111
+
112
+ def gamma_rat(x):
113
+ # helper to simplify ratios of gammas
114
+ was = x.count(gamma)
115
+ xx = x.replace(gamma, lambda n: _rf(1, (n - 1).expand()
116
+ ).replace(_rf, lambda a, b: gamma(a + b)/gamma(a)))
117
+ if xx.count(gamma) < was:
118
+ x = xx
119
+ return x
120
+
121
+ def gamma_factor(x):
122
+ # return True if there is a gamma factor in shallow args
123
+ if isinstance(x, gamma):
124
+ return True
125
+ if x.is_Add or x.is_Mul:
126
+ return any(gamma_factor(xi) for xi in x.args)
127
+ if x.is_Pow and (x.exp.is_integer or x.base.is_positive):
128
+ return gamma_factor(x.base)
129
+ return False
130
+
131
+ # recursion step
132
+ if level == 0:
133
+ expr = expr.func(*[rule_gamma(x, level + 1) for x in expr.args])
134
+ level += 1
135
+
136
+ if not expr.is_Mul:
137
+ return expr
138
+
139
+ # non-commutative step
140
+ if level == 1:
141
+ args, nc = expr.args_cnc()
142
+ if not args:
143
+ return expr
144
+ if nc:
145
+ return rule_gamma(Mul._from_args(args), level + 1)*Mul._from_args(nc)
146
+ level += 1
147
+
148
+ # pure gamma handling, not factor absorption
149
+ if level == 2:
150
+ T, F = sift(expr.args, gamma_factor, binary=True)
151
+ gamma_ind = Mul(*F)
152
+ d = Mul(*T)
153
+
154
+ nd, dd = d.as_numer_denom()
155
+ for ipass in range(2):
156
+ args = list(ordered(Mul.make_args(nd)))
157
+ for i, ni in enumerate(args):
158
+ if ni.is_Add:
159
+ ni, dd = Add(*[
160
+ rule_gamma(gamma_rat(a/dd), level + 1) for a in ni.args]
161
+ ).as_numer_denom()
162
+ args[i] = ni
163
+ if not dd.has(gamma):
164
+ break
165
+ nd = Mul(*args)
166
+ if ipass == 0 and not gamma_factor(nd):
167
+ break
168
+ nd, dd = dd, nd # now process in reversed order
169
+ expr = gamma_ind*nd/dd
170
+ if not (expr.is_Mul and (gamma_factor(dd) or gamma_factor(nd))):
171
+ return expr
172
+ level += 1
173
+
174
+ # iteration until constant
175
+ if level == 3:
176
+ while True:
177
+ was = expr
178
+ expr = rule_gamma(expr, 4)
179
+ if expr == was:
180
+ return expr
181
+
182
+ numer_gammas = []
183
+ denom_gammas = []
184
+ numer_others = []
185
+ denom_others = []
186
+ def explicate(p):
187
+ if p is S.One:
188
+ return None, []
189
+ b, e = p.as_base_exp()
190
+ if e.is_Integer:
191
+ if isinstance(b, gamma):
192
+ return True, [b.args[0]]*e
193
+ else:
194
+ return False, [b]*e
195
+ else:
196
+ return False, [p]
197
+
198
+ newargs = list(ordered(expr.args))
199
+ while newargs:
200
+ n, d = newargs.pop().as_numer_denom()
201
+ isg, l = explicate(n)
202
+ if isg:
203
+ numer_gammas.extend(l)
204
+ elif isg is False:
205
+ numer_others.extend(l)
206
+ isg, l = explicate(d)
207
+ if isg:
208
+ denom_gammas.extend(l)
209
+ elif isg is False:
210
+ denom_others.extend(l)
211
+
212
+ # =========== level 2 work: pure gamma manipulation =========
213
+
214
+ if not as_comb:
215
+ # Try to reduce the number of gamma factors by applying the
216
+ # reflection formula gamma(x)*gamma(1-x) = pi/sin(pi*x)
217
+ for gammas, numer, denom in [(
218
+ numer_gammas, numer_others, denom_others),
219
+ (denom_gammas, denom_others, numer_others)]:
220
+ new = []
221
+ while gammas:
222
+ g1 = gammas.pop()
223
+ if g1.is_integer:
224
+ new.append(g1)
225
+ continue
226
+ for i, g2 in enumerate(gammas):
227
+ n = g1 + g2 - 1
228
+ if not n.is_Integer:
229
+ continue
230
+ numer.append(S.Pi)
231
+ denom.append(sin(S.Pi*g1))
232
+ gammas.pop(i)
233
+ if n > 0:
234
+ for k in range(n):
235
+ numer.append(1 - g1 + k)
236
+ elif n < 0:
237
+ for k in range(-n):
238
+ denom.append(-g1 - k)
239
+ break
240
+ else:
241
+ new.append(g1)
242
+ # /!\ updating IN PLACE
243
+ gammas[:] = new
244
+
245
+ # Try to reduce the number of gammas by using the duplication
246
+ # theorem to cancel an upper and lower: gamma(2*s)/gamma(s) =
247
+ # 2**(2*s + 1)/(4*sqrt(pi))*gamma(s + 1/2). Although this could
248
+ # be done with higher argument ratios like gamma(3*x)/gamma(x),
249
+ # this would not reduce the number of gammas as in this case.
250
+ for ng, dg, no, do in [(numer_gammas, denom_gammas, numer_others,
251
+ denom_others),
252
+ (denom_gammas, numer_gammas, denom_others,
253
+ numer_others)]:
254
+
255
+ while True:
256
+ for x in ng:
257
+ for y in dg:
258
+ n = x - 2*y
259
+ if n.is_Integer:
260
+ break
261
+ else:
262
+ continue
263
+ break
264
+ else:
265
+ break
266
+ ng.remove(x)
267
+ dg.remove(y)
268
+ if n > 0:
269
+ for k in range(n):
270
+ no.append(2*y + k)
271
+ elif n < 0:
272
+ for k in range(-n):
273
+ do.append(2*y - 1 - k)
274
+ ng.append(y + S.Half)
275
+ no.append(2**(2*y - 1))
276
+ do.append(sqrt(S.Pi))
277
+
278
+ # Try to reduce the number of gamma factors by applying the
279
+ # multiplication theorem (used when n gammas with args differing
280
+ # by 1/n mod 1 are encountered).
281
+ #
282
+ # run of 2 with args differing by 1/2
283
+ #
284
+ # >>> gammasimp(gamma(x)*gamma(x+S.Half))
285
+ # 2*sqrt(2)*2**(-2*x - 1/2)*sqrt(pi)*gamma(2*x)
286
+ #
287
+ # run of 3 args differing by 1/3 (mod 1)
288
+ #
289
+ # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(2)/3))
290
+ # 6*3**(-3*x - 1/2)*pi*gamma(3*x)
291
+ # >>> gammasimp(gamma(x)*gamma(x+S(1)/3)*gamma(x+S(5)/3))
292
+ # 2*3**(-3*x - 1/2)*pi*(3*x + 2)*gamma(3*x)
293
+ #
294
+ def _run(coeffs):
295
+ # find runs in coeffs such that the difference in terms (mod 1)
296
+ # of t1, t2, ..., tn is 1/n
297
+ u = list(uniq(coeffs))
298
+ for i in range(len(u)):
299
+ dj = ([((u[j] - u[i]) % 1, j) for j in range(i + 1, len(u))])
300
+ for one, j in dj:
301
+ if one.p == 1 and one.q != 1:
302
+ n = one.q
303
+ got = [i]
304
+ get = list(range(1, n))
305
+ for d, j in dj:
306
+ m = n*d
307
+ if m.is_Integer and m in get:
308
+ get.remove(m)
309
+ got.append(j)
310
+ if not get:
311
+ break
312
+ else:
313
+ continue
314
+ for i, j in enumerate(got):
315
+ c = u[j]
316
+ coeffs.remove(c)
317
+ got[i] = c
318
+ return one.q, got[0], got[1:]
319
+
320
+ def _mult_thm(gammas, numer, denom):
321
+ # pull off and analyze the leading coefficient from each gamma arg
322
+ # looking for runs in those Rationals
323
+
324
+ # expr -> coeff + resid -> rats[resid] = coeff
325
+ rats = {}
326
+ for g in gammas:
327
+ c, resid = g.as_coeff_Add()
328
+ rats.setdefault(resid, []).append(c)
329
+
330
+ # look for runs in Rationals for each resid
331
+ keys = sorted(rats, key=default_sort_key)
332
+ for resid in keys:
333
+ coeffs = sorted(rats[resid])
334
+ new = []
335
+ while True:
336
+ run = _run(coeffs)
337
+ if run is None:
338
+ break
339
+
340
+ # process the sequence that was found:
341
+ # 1) convert all the gamma functions to have the right
342
+ # argument (could be off by an integer)
343
+ # 2) append the factors corresponding to the theorem
344
+ # 3) append the new gamma function
345
+
346
+ n, ui, other = run
347
+
348
+ # (1)
349
+ for u in other:
350
+ con = resid + u - 1
351
+ for k in range(int(u - ui)):
352
+ numer.append(con - k)
353
+
354
+ con = n*(resid + ui) # for (2) and (3)
355
+
356
+ # (2)
357
+ numer.append((2*S.Pi)**(S(n - 1)/2)*
358
+ n**(S.Half - con))
359
+ # (3)
360
+ new.append(con)
361
+
362
+ # restore resid to coeffs
363
+ rats[resid] = [resid + c for c in coeffs] + new
364
+
365
+ # rebuild the gamma arguments
366
+ g = []
367
+ for resid in keys:
368
+ g += rats[resid]
369
+ # /!\ updating IN PLACE
370
+ gammas[:] = g
371
+
372
+ for l, numer, denom in [(numer_gammas, numer_others, denom_others),
373
+ (denom_gammas, denom_others, numer_others)]:
374
+ _mult_thm(l, numer, denom)
375
+
376
+ # =========== level >= 2 work: factor absorption =========
377
+
378
+ if level >= 2:
379
+ # Try to absorb factors into the gammas: x*gamma(x) -> gamma(x + 1)
380
+ # and gamma(x)/(x - 1) -> gamma(x - 1)
381
+ # This code (in particular repeated calls to find_fuzzy) can be very
382
+ # slow.
383
+ def find_fuzzy(l, x):
384
+ if not l:
385
+ return
386
+ S1, T1 = compute_ST(x)
387
+ for y in l:
388
+ S2, T2 = inv[y]
389
+ if T1 != T2 or (not S1.intersection(S2) and
390
+ (S1 != set() or S2 != set())):
391
+ continue
392
+ # XXX we want some simplification (e.g. cancel or
393
+ # simplify) but no matter what it's slow.
394
+ a = len(cancel(x/y).free_symbols)
395
+ b = len(x.free_symbols)
396
+ c = len(y.free_symbols)
397
+ # TODO is there a better heuristic?
398
+ if a == 0 and (b > 0 or c > 0):
399
+ return y
400
+
401
+ # We thus try to avoid expensive calls by building the following
402
+ # "invariants": For every factor or gamma function argument
403
+ # - the set of free symbols S
404
+ # - the set of functional components T
405
+ # We will only try to absorb if T1==T2 and (S1 intersect S2 != emptyset
406
+ # or S1 == S2 == emptyset)
407
+ inv = {}
408
+
409
+ def compute_ST(expr):
410
+ if expr in inv:
411
+ return inv[expr]
412
+ return (expr.free_symbols, expr.atoms(Function).union(
413
+ {e.exp for e in expr.atoms(Pow)}))
414
+
415
+ def update_ST(expr):
416
+ inv[expr] = compute_ST(expr)
417
+ for expr in numer_gammas + denom_gammas + numer_others + denom_others:
418
+ update_ST(expr)
419
+
420
+ for gammas, numer, denom in [(
421
+ numer_gammas, numer_others, denom_others),
422
+ (denom_gammas, denom_others, numer_others)]:
423
+ new = []
424
+ while gammas:
425
+ g = gammas.pop()
426
+ cont = True
427
+ while cont:
428
+ cont = False
429
+ y = find_fuzzy(numer, g)
430
+ if y is not None:
431
+ numer.remove(y)
432
+ if y != g:
433
+ numer.append(y/g)
434
+ update_ST(y/g)
435
+ g += 1
436
+ cont = True
437
+ y = find_fuzzy(denom, g - 1)
438
+ if y is not None:
439
+ denom.remove(y)
440
+ if y != g - 1:
441
+ numer.append((g - 1)/y)
442
+ update_ST((g - 1)/y)
443
+ g -= 1
444
+ cont = True
445
+ new.append(g)
446
+ # /!\ updating IN PLACE
447
+ gammas[:] = new
448
+
449
+ # =========== rebuild expr ==================================
450
+
451
+ return Mul(*[gamma(g) for g in numer_gammas]) \
452
+ / Mul(*[gamma(g) for g in denom_gammas]) \
453
+ * Mul(*numer_others) / Mul(*denom_others)
454
+
455
+ was = factor(expr)
456
+ # (for some reason we cannot use Basic.replace in this case)
457
+ expr = rule_gamma(was)
458
+ if expr != was:
459
+ expr = factor(expr)
460
+
461
+ expr = expr.replace(gamma,
462
+ lambda n: expand_func(gamma(n)) if n.is_Rational else gamma(n))
463
+
464
+ return expr
465
+
466
+
467
+ class _rf(Function):
468
+ @classmethod
469
+ def eval(cls, a, b):
470
+ if b.is_Integer:
471
+ if not b:
472
+ return S.One
473
+
474
+ n = int(b)
475
+
476
+ if n > 0:
477
+ return Mul(*[a + i for i in range(n)])
478
+ elif n < 0:
479
+ return 1/Mul(*[a - i for i in range(1, -n + 1)])
480
+ else:
481
+ if b.is_Add:
482
+ c, _b = b.as_coeff_Add()
483
+
484
+ if c.is_Integer:
485
+ if c > 0:
486
+ return _rf(a, _b)*_rf(a + _b, c)
487
+ elif c < 0:
488
+ return _rf(a, _b)/_rf(a + _b + c, -c)
489
+
490
+ if a.is_Add:
491
+ c, _a = a.as_coeff_Add()
492
+
493
+ if c.is_Integer:
494
+ if c > 0:
495
+ return _rf(_a, b)*_rf(_a + b, c)/_rf(_a, c)
496
+ elif c < 0:
497
+ return _rf(_a, b)*_rf(_a + c, -c)/_rf(_a + b + c, -c)
lib/python3.12/site-packages/sympy/simplify/ratsimp.py ADDED
@@ -0,0 +1,222 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import combinations_with_replacement
2
+ from sympy.core import symbols, Add, Dummy
3
+ from sympy.core.numbers import Rational
4
+ from sympy.polys import cancel, ComputationFailed, parallel_poly_from_expr, reduced, Poly
5
+ from sympy.polys.monomials import Monomial, monomial_div
6
+ from sympy.polys.polyerrors import DomainError, PolificationFailed
7
+ from sympy.utilities.misc import debug, debugf
8
+
9
+ def ratsimp(expr):
10
+ """
11
+ Put an expression over a common denominator, cancel and reduce.
12
+
13
+ Examples
14
+ ========
15
+
16
+ >>> from sympy import ratsimp
17
+ >>> from sympy.abc import x, y
18
+ >>> ratsimp(1/x + 1/y)
19
+ (x + y)/(x*y)
20
+ """
21
+
22
+ f, g = cancel(expr).as_numer_denom()
23
+ try:
24
+ Q, r = reduced(f, [g], field=True, expand=False)
25
+ except ComputationFailed:
26
+ return f/g
27
+
28
+ return Add(*Q) + cancel(r/g)
29
+
30
+
31
+ def ratsimpmodprime(expr, G, *gens, quick=True, polynomial=False, **args):
32
+ """
33
+ Simplifies a rational expression ``expr`` modulo the prime ideal
34
+ generated by ``G``. ``G`` should be a Groebner basis of the
35
+ ideal.
36
+
37
+ Examples
38
+ ========
39
+
40
+ >>> from sympy.simplify.ratsimp import ratsimpmodprime
41
+ >>> from sympy.abc import x, y
42
+ >>> eq = (x + y**5 + y)/(x - y)
43
+ >>> ratsimpmodprime(eq, [x*y**5 - x - y], x, y, order='lex')
44
+ (-x**2 - x*y - x - y)/(-x**2 + x*y)
45
+
46
+ If ``polynomial`` is ``False``, the algorithm computes a rational
47
+ simplification which minimizes the sum of the total degrees of
48
+ the numerator and the denominator.
49
+
50
+ If ``polynomial`` is ``True``, this function just brings numerator and
51
+ denominator into a canonical form. This is much faster, but has
52
+ potentially worse results.
53
+
54
+ References
55
+ ==========
56
+
57
+ .. [1] M. Monagan, R. Pearce, Rational Simplification Modulo a Polynomial
58
+ Ideal, https://dl.acm.org/doi/pdf/10.1145/1145768.1145809
59
+ (specifically, the second algorithm)
60
+ """
61
+ from sympy.solvers.solvers import solve
62
+
63
+ debug('ratsimpmodprime', expr)
64
+
65
+ # usual preparation of polynomials:
66
+
67
+ num, denom = cancel(expr).as_numer_denom()
68
+
69
+ try:
70
+ polys, opt = parallel_poly_from_expr([num, denom] + G, *gens, **args)
71
+ except PolificationFailed:
72
+ return expr
73
+
74
+ domain = opt.domain
75
+
76
+ if domain.has_assoc_Field:
77
+ opt.domain = domain.get_field()
78
+ else:
79
+ raise DomainError(
80
+ "Cannot compute rational simplification over %s" % domain)
81
+
82
+ # compute only once
83
+ leading_monomials = [g.LM(opt.order) for g in polys[2:]]
84
+ tested = set()
85
+
86
+ def staircase(n):
87
+ """
88
+ Compute all monomials with degree less than ``n`` that are
89
+ not divisible by any element of ``leading_monomials``.
90
+ """
91
+ if n == 0:
92
+ return [1]
93
+ S = []
94
+ for mi in combinations_with_replacement(range(len(opt.gens)), n):
95
+ m = [0]*len(opt.gens)
96
+ for i in mi:
97
+ m[i] += 1
98
+ if all(monomial_div(m, lmg) is None for lmg in
99
+ leading_monomials):
100
+ S.append(m)
101
+
102
+ return [Monomial(s).as_expr(*opt.gens) for s in S] + staircase(n - 1)
103
+
104
+ def _ratsimpmodprime(a, b, allsol, N=0, D=0):
105
+ r"""
106
+ Computes a rational simplification of ``a/b`` which minimizes
107
+ the sum of the total degrees of the numerator and the denominator.
108
+
109
+ Explanation
110
+ ===========
111
+
112
+ The algorithm proceeds by looking at ``a * d - b * c`` modulo
113
+ the ideal generated by ``G`` for some ``c`` and ``d`` with degree
114
+ less than ``a`` and ``b`` respectively.
115
+ The coefficients of ``c`` and ``d`` are indeterminates and thus
116
+ the coefficients of the normalform of ``a * d - b * c`` are
117
+ linear polynomials in these indeterminates.
118
+ If these linear polynomials, considered as system of
119
+ equations, have a nontrivial solution, then `\frac{a}{b}
120
+ \equiv \frac{c}{d}` modulo the ideal generated by ``G``. So,
121
+ by construction, the degree of ``c`` and ``d`` is less than
122
+ the degree of ``a`` and ``b``, so a simpler representation
123
+ has been found.
124
+ After a simpler representation has been found, the algorithm
125
+ tries to reduce the degree of the numerator and denominator
126
+ and returns the result afterwards.
127
+
128
+ As an extension, if quick=False, we look at all possible degrees such
129
+ that the total degree is less than *or equal to* the best current
130
+ solution. We retain a list of all solutions of minimal degree, and try
131
+ to find the best one at the end.
132
+ """
133
+ c, d = a, b
134
+ steps = 0
135
+
136
+ maxdeg = a.total_degree() + b.total_degree()
137
+ if quick:
138
+ bound = maxdeg - 1
139
+ else:
140
+ bound = maxdeg
141
+ while N + D <= bound:
142
+ if (N, D) in tested:
143
+ break
144
+ tested.add((N, D))
145
+
146
+ M1 = staircase(N)
147
+ M2 = staircase(D)
148
+ debugf('%s / %s: %s, %s', (N, D, M1, M2))
149
+
150
+ Cs = symbols("c:%d" % len(M1), cls=Dummy)
151
+ Ds = symbols("d:%d" % len(M2), cls=Dummy)
152
+ ng = Cs + Ds
153
+
154
+ c_hat = Poly(
155
+ sum(Cs[i] * M1[i] for i in range(len(M1))), opt.gens + ng)
156
+ d_hat = Poly(
157
+ sum(Ds[i] * M2[i] for i in range(len(M2))), opt.gens + ng)
158
+
159
+ r = reduced(a * d_hat - b * c_hat, G, opt.gens + ng,
160
+ order=opt.order, polys=True)[1]
161
+
162
+ S = Poly(r, gens=opt.gens).coeffs()
163
+ sol = solve(S, Cs + Ds, particular=True, quick=True)
164
+
165
+ if sol and not all(s == 0 for s in sol.values()):
166
+ c = c_hat.subs(sol)
167
+ d = d_hat.subs(sol)
168
+
169
+ # The "free" variables occurring before as parameters
170
+ # might still be in the substituted c, d, so set them
171
+ # to the value chosen before:
172
+ c = c.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds))))))
173
+ d = d.subs(dict(list(zip(Cs + Ds, [1] * (len(Cs) + len(Ds))))))
174
+
175
+ c = Poly(c, opt.gens)
176
+ d = Poly(d, opt.gens)
177
+ if d == 0:
178
+ raise ValueError('Ideal not prime?')
179
+
180
+ allsol.append((c_hat, d_hat, S, Cs + Ds))
181
+ if N + D != maxdeg:
182
+ allsol = [allsol[-1]]
183
+
184
+ break
185
+
186
+ steps += 1
187
+ N += 1
188
+ D += 1
189
+
190
+ if steps > 0:
191
+ c, d, allsol = _ratsimpmodprime(c, d, allsol, N, D - steps)
192
+ c, d, allsol = _ratsimpmodprime(c, d, allsol, N - steps, D)
193
+
194
+ return c, d, allsol
195
+
196
+ # preprocessing. this improves performance a bit when deg(num)
197
+ # and deg(denom) are large:
198
+ num = reduced(num, G, opt.gens, order=opt.order)[1]
199
+ denom = reduced(denom, G, opt.gens, order=opt.order)[1]
200
+
201
+ if polynomial:
202
+ return (num/denom).cancel()
203
+
204
+ c, d, allsol = _ratsimpmodprime(
205
+ Poly(num, opt.gens, domain=opt.domain), Poly(denom, opt.gens, domain=opt.domain), [])
206
+ if not quick and allsol:
207
+ debugf('Looking for best minimal solution. Got: %s', len(allsol))
208
+ newsol = []
209
+ for c_hat, d_hat, S, ng in allsol:
210
+ sol = solve(S, ng, particular=True, quick=False)
211
+ # all values of sol should be numbers; if not, solve is broken
212
+ newsol.append((c_hat.subs(sol), d_hat.subs(sol)))
213
+ c, d = min(newsol, key=lambda x: len(x[0].terms()) + len(x[1].terms()))
214
+
215
+ if not domain.is_Field:
216
+ cn, c = c.clear_denoms(convert=True)
217
+ dn, d = d.clear_denoms(convert=True)
218
+ r = Rational(cn, dn)
219
+ else:
220
+ r = Rational(1)
221
+
222
+ return (c*r.q)/(d*r.p)
lib/python3.12/site-packages/sympy/simplify/simplify.py ADDED
@@ -0,0 +1,2150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict
2
+
3
+ from sympy.concrete.products import Product
4
+ from sympy.concrete.summations import Sum
5
+ from sympy.core import (Basic, S, Add, Mul, Pow, Symbol, sympify,
6
+ expand_func, Function, Dummy, Expr, factor_terms,
7
+ expand_power_exp, Eq)
8
+ from sympy.core.exprtools import factor_nc
9
+ from sympy.core.parameters import global_parameters
10
+ from sympy.core.function import (expand_log, count_ops, _mexpand,
11
+ nfloat, expand_mul, expand)
12
+ from sympy.core.numbers import Float, I, pi, Rational, equal_valued
13
+ from sympy.core.relational import Relational
14
+ from sympy.core.rules import Transform
15
+ from sympy.core.sorting import ordered
16
+ from sympy.core.sympify import _sympify
17
+ from sympy.core.traversal import bottom_up as _bottom_up, walk as _walk
18
+ from sympy.functions import gamma, exp, sqrt, log, exp_polar, re
19
+ from sympy.functions.combinatorial.factorials import CombinatorialFunction
20
+ from sympy.functions.elementary.complexes import unpolarify, Abs, sign
21
+ from sympy.functions.elementary.exponential import ExpBase
22
+ from sympy.functions.elementary.hyperbolic import HyperbolicFunction
23
+ from sympy.functions.elementary.integers import ceiling
24
+ from sympy.functions.elementary.piecewise import (Piecewise, piecewise_fold,
25
+ piecewise_simplify)
26
+ from sympy.functions.elementary.trigonometric import TrigonometricFunction
27
+ from sympy.functions.special.bessel import (BesselBase, besselj, besseli,
28
+ besselk, bessely, jn)
29
+ from sympy.functions.special.tensor_functions import KroneckerDelta
30
+ from sympy.integrals.integrals import Integral
31
+ from sympy.matrices.expressions import (MatrixExpr, MatAdd, MatMul,
32
+ MatPow, MatrixSymbol)
33
+ from sympy.polys import together, cancel, factor
34
+ from sympy.polys.numberfields.minpoly import _is_sum_surds, _minimal_polynomial_sq
35
+ from sympy.simplify.combsimp import combsimp
36
+ from sympy.simplify.cse_opts import sub_pre, sub_post
37
+ from sympy.simplify.hyperexpand import hyperexpand
38
+ from sympy.simplify.powsimp import powsimp
39
+ from sympy.simplify.radsimp import radsimp, fraction, collect_abs
40
+ from sympy.simplify.sqrtdenest import sqrtdenest
41
+ from sympy.simplify.trigsimp import trigsimp, exptrigsimp
42
+ from sympy.utilities.decorator import deprecated
43
+ from sympy.utilities.iterables import has_variety, sift, subsets, iterable
44
+ from sympy.utilities.misc import as_int
45
+
46
+ import mpmath
47
+
48
+
49
+ def separatevars(expr, symbols=[], dict=False, force=False):
50
+ """
51
+ Separates variables in an expression, if possible. By
52
+ default, it separates with respect to all symbols in an
53
+ expression and collects constant coefficients that are
54
+ independent of symbols.
55
+
56
+ Explanation
57
+ ===========
58
+
59
+ If ``dict=True`` then the separated terms will be returned
60
+ in a dictionary keyed to their corresponding symbols.
61
+ By default, all symbols in the expression will appear as
62
+ keys; if symbols are provided, then all those symbols will
63
+ be used as keys, and any terms in the expression containing
64
+ other symbols or non-symbols will be returned keyed to the
65
+ string 'coeff'. (Passing None for symbols will return the
66
+ expression in a dictionary keyed to 'coeff'.)
67
+
68
+ If ``force=True``, then bases of powers will be separated regardless
69
+ of assumptions on the symbols involved.
70
+
71
+ Notes
72
+ =====
73
+
74
+ The order of the factors is determined by Mul, so that the
75
+ separated expressions may not necessarily be grouped together.
76
+
77
+ Although factoring is necessary to separate variables in some
78
+ expressions, it is not necessary in all cases, so one should not
79
+ count on the returned factors being factored.
80
+
81
+ Examples
82
+ ========
83
+
84
+ >>> from sympy.abc import x, y, z, alpha
85
+ >>> from sympy import separatevars, sin
86
+ >>> separatevars((x*y)**y)
87
+ (x*y)**y
88
+ >>> separatevars((x*y)**y, force=True)
89
+ x**y*y**y
90
+
91
+ >>> e = 2*x**2*z*sin(y)+2*z*x**2
92
+ >>> separatevars(e)
93
+ 2*x**2*z*(sin(y) + 1)
94
+ >>> separatevars(e, symbols=(x, y), dict=True)
95
+ {'coeff': 2*z, x: x**2, y: sin(y) + 1}
96
+ >>> separatevars(e, [x, y, alpha], dict=True)
97
+ {'coeff': 2*z, alpha: 1, x: x**2, y: sin(y) + 1}
98
+
99
+ If the expression is not really separable, or is only partially
100
+ separable, separatevars will do the best it can to separate it
101
+ by using factoring.
102
+
103
+ >>> separatevars(x + x*y - 3*x**2)
104
+ -x*(3*x - y - 1)
105
+
106
+ If the expression is not separable then expr is returned unchanged
107
+ or (if dict=True) then None is returned.
108
+
109
+ >>> eq = 2*x + y*sin(x)
110
+ >>> separatevars(eq) == eq
111
+ True
112
+ >>> separatevars(2*x + y*sin(x), symbols=(x, y), dict=True) is None
113
+ True
114
+
115
+ """
116
+ expr = sympify(expr)
117
+ if dict:
118
+ return _separatevars_dict(_separatevars(expr, force), symbols)
119
+ else:
120
+ return _separatevars(expr, force)
121
+
122
+
123
+ def _separatevars(expr, force):
124
+ if isinstance(expr, Abs):
125
+ arg = expr.args[0]
126
+ if arg.is_Mul and not arg.is_number:
127
+ s = separatevars(arg, dict=True, force=force)
128
+ if s is not None:
129
+ return Mul(*map(expr.func, s.values()))
130
+ else:
131
+ return expr
132
+
133
+ if len(expr.free_symbols) < 2:
134
+ return expr
135
+
136
+ # don't destroy a Mul since much of the work may already be done
137
+ if expr.is_Mul:
138
+ args = list(expr.args)
139
+ changed = False
140
+ for i, a in enumerate(args):
141
+ args[i] = separatevars(a, force)
142
+ changed = changed or args[i] != a
143
+ if changed:
144
+ expr = expr.func(*args)
145
+ return expr
146
+
147
+ # get a Pow ready for expansion
148
+ if expr.is_Pow and expr.base != S.Exp1:
149
+ expr = Pow(separatevars(expr.base, force=force), expr.exp)
150
+
151
+ # First try other expansion methods
152
+ expr = expr.expand(mul=False, multinomial=False, force=force)
153
+
154
+ _expr, reps = posify(expr) if force else (expr, {})
155
+ expr = factor(_expr).subs(reps)
156
+
157
+ if not expr.is_Add:
158
+ return expr
159
+
160
+ # Find any common coefficients to pull out
161
+ args = list(expr.args)
162
+ commonc = args[0].args_cnc(cset=True, warn=False)[0]
163
+ for i in args[1:]:
164
+ commonc &= i.args_cnc(cset=True, warn=False)[0]
165
+ commonc = Mul(*commonc)
166
+ commonc = commonc.as_coeff_Mul()[1] # ignore constants
167
+ commonc_set = commonc.args_cnc(cset=True, warn=False)[0]
168
+
169
+ # remove them
170
+ for i, a in enumerate(args):
171
+ c, nc = a.args_cnc(cset=True, warn=False)
172
+ c = c - commonc_set
173
+ args[i] = Mul(*c)*Mul(*nc)
174
+ nonsepar = Add(*args)
175
+
176
+ if len(nonsepar.free_symbols) > 1:
177
+ _expr = nonsepar
178
+ _expr, reps = posify(_expr) if force else (_expr, {})
179
+ _expr = (factor(_expr)).subs(reps)
180
+
181
+ if not _expr.is_Add:
182
+ nonsepar = _expr
183
+
184
+ return commonc*nonsepar
185
+
186
+
187
+ def _separatevars_dict(expr, symbols):
188
+ if symbols:
189
+ if not all(t.is_Atom for t in symbols):
190
+ raise ValueError("symbols must be Atoms.")
191
+ symbols = list(symbols)
192
+ elif symbols is None:
193
+ return {'coeff': expr}
194
+ else:
195
+ symbols = list(expr.free_symbols)
196
+ if not symbols:
197
+ return None
198
+
199
+ ret = {i: [] for i in symbols + ['coeff']}
200
+
201
+ for i in Mul.make_args(expr):
202
+ expsym = i.free_symbols
203
+ intersection = set(symbols).intersection(expsym)
204
+ if len(intersection) > 1:
205
+ return None
206
+ if len(intersection) == 0:
207
+ # There are no symbols, so it is part of the coefficient
208
+ ret['coeff'].append(i)
209
+ else:
210
+ ret[intersection.pop()].append(i)
211
+
212
+ # rebuild
213
+ for k, v in ret.items():
214
+ ret[k] = Mul(*v)
215
+
216
+ return ret
217
+
218
+
219
+ def posify(eq):
220
+ """Return ``eq`` (with generic symbols made positive) and a
221
+ dictionary containing the mapping between the old and new
222
+ symbols.
223
+
224
+ Explanation
225
+ ===========
226
+
227
+ Any symbol that has positive=None will be replaced with a positive dummy
228
+ symbol having the same name. This replacement will allow more symbolic
229
+ processing of expressions, especially those involving powers and
230
+ logarithms.
231
+
232
+ A dictionary that can be sent to subs to restore ``eq`` to its original
233
+ symbols is also returned.
234
+
235
+ >>> from sympy import posify, Symbol, log, solve
236
+ >>> from sympy.abc import x
237
+ >>> posify(x + Symbol('p', positive=True) + Symbol('n', negative=True))
238
+ (_x + n + p, {_x: x})
239
+
240
+ >>> eq = 1/x
241
+ >>> log(eq).expand()
242
+ log(1/x)
243
+ >>> log(posify(eq)[0]).expand()
244
+ -log(_x)
245
+ >>> p, rep = posify(eq)
246
+ >>> log(p).expand().subs(rep)
247
+ -log(x)
248
+
249
+ It is possible to apply the same transformations to an iterable
250
+ of expressions:
251
+
252
+ >>> eq = x**2 - 4
253
+ >>> solve(eq, x)
254
+ [-2, 2]
255
+ >>> eq_x, reps = posify([eq, x]); eq_x
256
+ [_x**2 - 4, _x]
257
+ >>> solve(*eq_x)
258
+ [2]
259
+ """
260
+ eq = sympify(eq)
261
+ if iterable(eq):
262
+ f = type(eq)
263
+ eq = list(eq)
264
+ syms = set()
265
+ for e in eq:
266
+ syms = syms.union(e.atoms(Symbol))
267
+ reps = {}
268
+ for s in syms:
269
+ reps.update({v: k for k, v in posify(s)[1].items()})
270
+ for i, e in enumerate(eq):
271
+ eq[i] = e.subs(reps)
272
+ return f(eq), {r: s for s, r in reps.items()}
273
+
274
+ reps = {s: Dummy(s.name, positive=True, **s.assumptions0)
275
+ for s in eq.free_symbols if s.is_positive is None}
276
+ eq = eq.subs(reps)
277
+ return eq, {r: s for s, r in reps.items()}
278
+
279
+
280
+ def hypersimp(f, k):
281
+ """Given combinatorial term f(k) simplify its consecutive term ratio
282
+ i.e. f(k+1)/f(k). The input term can be composed of functions and
283
+ integer sequences which have equivalent representation in terms
284
+ of gamma special function.
285
+
286
+ Explanation
287
+ ===========
288
+
289
+ The algorithm performs three basic steps:
290
+
291
+ 1. Rewrite all functions in terms of gamma, if possible.
292
+
293
+ 2. Rewrite all occurrences of gamma in terms of products
294
+ of gamma and rising factorial with integer, absolute
295
+ constant exponent.
296
+
297
+ 3. Perform simplification of nested fractions, powers
298
+ and if the resulting expression is a quotient of
299
+ polynomials, reduce their total degree.
300
+
301
+ If f(k) is hypergeometric then as result we arrive with a
302
+ quotient of polynomials of minimal degree. Otherwise None
303
+ is returned.
304
+
305
+ For more information on the implemented algorithm refer to:
306
+
307
+ 1. W. Koepf, Algorithms for m-fold Hypergeometric Summation,
308
+ Journal of Symbolic Computation (1995) 20, 399-417
309
+ """
310
+ f = sympify(f)
311
+
312
+ g = f.subs(k, k + 1) / f
313
+
314
+ g = g.rewrite(gamma)
315
+ if g.has(Piecewise):
316
+ g = piecewise_fold(g)
317
+ g = g.args[-1][0]
318
+ g = expand_func(g)
319
+ g = powsimp(g, deep=True, combine='exp')
320
+
321
+ if g.is_rational_function(k):
322
+ return simplify(g, ratio=S.Infinity)
323
+ else:
324
+ return None
325
+
326
+
327
+ def hypersimilar(f, g, k):
328
+ """
329
+ Returns True if ``f`` and ``g`` are hyper-similar.
330
+
331
+ Explanation
332
+ ===========
333
+
334
+ Similarity in hypergeometric sense means that a quotient of
335
+ f(k) and g(k) is a rational function in ``k``. This procedure
336
+ is useful in solving recurrence relations.
337
+
338
+ For more information see hypersimp().
339
+
340
+ """
341
+ f, g = list(map(sympify, (f, g)))
342
+
343
+ h = (f/g).rewrite(gamma)
344
+ h = h.expand(func=True, basic=False)
345
+
346
+ return h.is_rational_function(k)
347
+
348
+
349
+ def signsimp(expr, evaluate=None):
350
+ """Make all Add sub-expressions canonical wrt sign.
351
+
352
+ Explanation
353
+ ===========
354
+
355
+ If an Add subexpression, ``a``, can have a sign extracted,
356
+ as determined by could_extract_minus_sign, it is replaced
357
+ with Mul(-1, a, evaluate=False). This allows signs to be
358
+ extracted from powers and products.
359
+
360
+ Examples
361
+ ========
362
+
363
+ >>> from sympy import signsimp, exp, symbols
364
+ >>> from sympy.abc import x, y
365
+ >>> i = symbols('i', odd=True)
366
+ >>> n = -1 + 1/x
367
+ >>> n/x/(-n)**2 - 1/n/x
368
+ (-1 + 1/x)/(x*(1 - 1/x)**2) - 1/(x*(-1 + 1/x))
369
+ >>> signsimp(_)
370
+ 0
371
+ >>> x*n + x*-n
372
+ x*(-1 + 1/x) + x*(1 - 1/x)
373
+ >>> signsimp(_)
374
+ 0
375
+
376
+ Since powers automatically handle leading signs
377
+
378
+ >>> (-2)**i
379
+ -2**i
380
+
381
+ signsimp can be used to put the base of a power with an integer
382
+ exponent into canonical form:
383
+
384
+ >>> n**i
385
+ (-1 + 1/x)**i
386
+
387
+ By default, signsimp does not leave behind any hollow simplification:
388
+ if making an Add canonical wrt sign didn't change the expression, the
389
+ original Add is restored. If this is not desired then the keyword
390
+ ``evaluate`` can be set to False:
391
+
392
+ >>> e = exp(y - x)
393
+ >>> signsimp(e) == e
394
+ True
395
+ >>> signsimp(e, evaluate=False)
396
+ exp(-(x - y))
397
+
398
+ """
399
+ if evaluate is None:
400
+ evaluate = global_parameters.evaluate
401
+ expr = sympify(expr)
402
+ if not isinstance(expr, (Expr, Relational)) or expr.is_Atom:
403
+ return expr
404
+ # get rid of an pre-existing unevaluation regarding sign
405
+ e = expr.replace(lambda x: x.is_Mul and -(-x) != x, lambda x: -(-x))
406
+ e = sub_post(sub_pre(e))
407
+ if not isinstance(e, (Expr, Relational)) or e.is_Atom:
408
+ return e
409
+ if e.is_Add:
410
+ rv = e.func(*[signsimp(a) for a in e.args])
411
+ if not evaluate and isinstance(rv, Add
412
+ ) and rv.could_extract_minus_sign():
413
+ return Mul(S.NegativeOne, -rv, evaluate=False)
414
+ return rv
415
+ if evaluate:
416
+ e = e.replace(lambda x: x.is_Mul and -(-x) != x, lambda x: -(-x))
417
+ return e
418
+
419
+
420
+ def simplify(expr, ratio=1.7, measure=count_ops, rational=False, inverse=False, doit=True, **kwargs):
421
+ """Simplifies the given expression.
422
+
423
+ Explanation
424
+ ===========
425
+
426
+ Simplification is not a well defined term and the exact strategies
427
+ this function tries can change in the future versions of SymPy. If
428
+ your algorithm relies on "simplification" (whatever it is), try to
429
+ determine what you need exactly - is it powsimp()?, radsimp()?,
430
+ together()?, logcombine()?, or something else? And use this particular
431
+ function directly, because those are well defined and thus your algorithm
432
+ will be robust.
433
+
434
+ Nonetheless, especially for interactive use, or when you do not know
435
+ anything about the structure of the expression, simplify() tries to apply
436
+ intelligent heuristics to make the input expression "simpler". For
437
+ example:
438
+
439
+ >>> from sympy import simplify, cos, sin
440
+ >>> from sympy.abc import x, y
441
+ >>> a = (x + x**2)/(x*sin(y)**2 + x*cos(y)**2)
442
+ >>> a
443
+ (x**2 + x)/(x*sin(y)**2 + x*cos(y)**2)
444
+ >>> simplify(a)
445
+ x + 1
446
+
447
+ Note that we could have obtained the same result by using specific
448
+ simplification functions:
449
+
450
+ >>> from sympy import trigsimp, cancel
451
+ >>> trigsimp(a)
452
+ (x**2 + x)/x
453
+ >>> cancel(_)
454
+ x + 1
455
+
456
+ In some cases, applying :func:`simplify` may actually result in some more
457
+ complicated expression. The default ``ratio=1.7`` prevents more extreme
458
+ cases: if (result length)/(input length) > ratio, then input is returned
459
+ unmodified. The ``measure`` parameter lets you specify the function used
460
+ to determine how complex an expression is. The function should take a
461
+ single argument as an expression and return a number such that if
462
+ expression ``a`` is more complex than expression ``b``, then
463
+ ``measure(a) > measure(b)``. The default measure function is
464
+ :func:`~.count_ops`, which returns the total number of operations in the
465
+ expression.
466
+
467
+ For example, if ``ratio=1``, ``simplify`` output cannot be longer
468
+ than input.
469
+
470
+ ::
471
+
472
+ >>> from sympy import sqrt, simplify, count_ops, oo
473
+ >>> root = 1/(sqrt(2)+3)
474
+
475
+ Since ``simplify(root)`` would result in a slightly longer expression,
476
+ root is returned unchanged instead::
477
+
478
+ >>> simplify(root, ratio=1) == root
479
+ True
480
+
481
+ If ``ratio=oo``, simplify will be applied anyway::
482
+
483
+ >>> count_ops(simplify(root, ratio=oo)) > count_ops(root)
484
+ True
485
+
486
+ Note that the shortest expression is not necessary the simplest, so
487
+ setting ``ratio`` to 1 may not be a good idea.
488
+ Heuristically, the default value ``ratio=1.7`` seems like a reasonable
489
+ choice.
490
+
491
+ You can easily define your own measure function based on what you feel
492
+ should represent the "size" or "complexity" of the input expression. Note
493
+ that some choices, such as ``lambda expr: len(str(expr))`` may appear to be
494
+ good metrics, but have other problems (in this case, the measure function
495
+ may slow down simplify too much for very large expressions). If you do not
496
+ know what a good metric would be, the default, ``count_ops``, is a good
497
+ one.
498
+
499
+ For example:
500
+
501
+ >>> from sympy import symbols, log
502
+ >>> a, b = symbols('a b', positive=True)
503
+ >>> g = log(a) + log(b) + log(a)*log(1/b)
504
+ >>> h = simplify(g)
505
+ >>> h
506
+ log(a*b**(1 - log(a)))
507
+ >>> count_ops(g)
508
+ 8
509
+ >>> count_ops(h)
510
+ 5
511
+
512
+ So you can see that ``h`` is simpler than ``g`` using the count_ops metric.
513
+ However, we may not like how ``simplify`` (in this case, using
514
+ ``logcombine``) has created the ``b**(log(1/a) + 1)`` term. A simple way
515
+ to reduce this would be to give more weight to powers as operations in
516
+ ``count_ops``. We can do this by using the ``visual=True`` option:
517
+
518
+ >>> print(count_ops(g, visual=True))
519
+ 2*ADD + DIV + 4*LOG + MUL
520
+ >>> print(count_ops(h, visual=True))
521
+ 2*LOG + MUL + POW + SUB
522
+
523
+ >>> from sympy import Symbol, S
524
+ >>> def my_measure(expr):
525
+ ... POW = Symbol('POW')
526
+ ... # Discourage powers by giving POW a weight of 10
527
+ ... count = count_ops(expr, visual=True).subs(POW, 10)
528
+ ... # Every other operation gets a weight of 1 (the default)
529
+ ... count = count.replace(Symbol, type(S.One))
530
+ ... return count
531
+ >>> my_measure(g)
532
+ 8
533
+ >>> my_measure(h)
534
+ 14
535
+ >>> 15./8 > 1.7 # 1.7 is the default ratio
536
+ True
537
+ >>> simplify(g, measure=my_measure)
538
+ -log(a)*log(b) + log(a) + log(b)
539
+
540
+ Note that because ``simplify()`` internally tries many different
541
+ simplification strategies and then compares them using the measure
542
+ function, we get a completely different result that is still different
543
+ from the input expression by doing this.
544
+
545
+ If ``rational=True``, Floats will be recast as Rationals before simplification.
546
+ If ``rational=None``, Floats will be recast as Rationals but the result will
547
+ be recast as Floats. If rational=False(default) then nothing will be done
548
+ to the Floats.
549
+
550
+ If ``inverse=True``, it will be assumed that a composition of inverse
551
+ functions, such as sin and asin, can be cancelled in any order.
552
+ For example, ``asin(sin(x))`` will yield ``x`` without checking whether
553
+ x belongs to the set where this relation is true. The default is
554
+ False.
555
+
556
+ Note that ``simplify()`` automatically calls ``doit()`` on the final
557
+ expression. You can avoid this behavior by passing ``doit=False`` as
558
+ an argument.
559
+
560
+ Also, it should be noted that simplifying a boolean expression is not
561
+ well defined. If the expression prefers automatic evaluation (such as
562
+ :obj:`~.Eq()` or :obj:`~.Or()`), simplification will return ``True`` or
563
+ ``False`` if truth value can be determined. If the expression is not
564
+ evaluated by default (such as :obj:`~.Predicate()`), simplification will
565
+ not reduce it and you should use :func:`~.refine()` or :func:`~.ask()`
566
+ function. This inconsistency will be resolved in future version.
567
+
568
+ See Also
569
+ ========
570
+
571
+ sympy.assumptions.refine.refine : Simplification using assumptions.
572
+ sympy.assumptions.ask.ask : Query for boolean expressions using assumptions.
573
+ """
574
+
575
+ def shorter(*choices):
576
+ """
577
+ Return the choice that has the fewest ops. In case of a tie,
578
+ the expression listed first is selected.
579
+ """
580
+ if not has_variety(choices):
581
+ return choices[0]
582
+ return min(choices, key=measure)
583
+
584
+ def done(e):
585
+ rv = e.doit() if doit else e
586
+ return shorter(rv, collect_abs(rv))
587
+
588
+ expr = sympify(expr, rational=rational)
589
+ kwargs = {
590
+ "ratio": kwargs.get('ratio', ratio),
591
+ "measure": kwargs.get('measure', measure),
592
+ "rational": kwargs.get('rational', rational),
593
+ "inverse": kwargs.get('inverse', inverse),
594
+ "doit": kwargs.get('doit', doit)}
595
+ # no routine for Expr needs to check for is_zero
596
+ if isinstance(expr, Expr) and expr.is_zero:
597
+ return S.Zero if not expr.is_Number else expr
598
+
599
+ _eval_simplify = getattr(expr, '_eval_simplify', None)
600
+ if _eval_simplify is not None:
601
+ return _eval_simplify(**kwargs)
602
+
603
+ original_expr = expr = collect_abs(signsimp(expr))
604
+
605
+ if not isinstance(expr, Basic) or not expr.args: # XXX: temporary hack
606
+ return expr
607
+
608
+ if inverse and expr.has(Function):
609
+ expr = inversecombine(expr)
610
+ if not expr.args: # simplified to atomic
611
+ return expr
612
+
613
+ # do deep simplification
614
+ handled = Add, Mul, Pow, ExpBase
615
+ expr = expr.replace(
616
+ # here, checking for x.args is not enough because Basic has
617
+ # args but Basic does not always play well with replace, e.g.
618
+ # when simultaneous is True found expressions will be masked
619
+ # off with a Dummy but not all Basic objects in an expression
620
+ # can be replaced with a Dummy
621
+ lambda x: isinstance(x, Expr) and x.args and not isinstance(
622
+ x, handled),
623
+ lambda x: x.func(*[simplify(i, **kwargs) for i in x.args]),
624
+ simultaneous=False)
625
+ if not isinstance(expr, handled):
626
+ return done(expr)
627
+
628
+ if not expr.is_commutative:
629
+ expr = nc_simplify(expr)
630
+
631
+ # TODO: Apply different strategies, considering expression pattern:
632
+ # is it a purely rational function? Is there any trigonometric function?...
633
+ # See also https://github.com/sympy/sympy/pull/185.
634
+
635
+
636
+ # rationalize Floats
637
+ floats = False
638
+ if rational is not False and expr.has(Float):
639
+ floats = True
640
+ expr = nsimplify(expr, rational=True)
641
+
642
+ expr = _bottom_up(expr, lambda w: getattr(w, 'normal', lambda: w)())
643
+ expr = Mul(*powsimp(expr).as_content_primitive())
644
+ _e = cancel(expr)
645
+ expr1 = shorter(_e, _mexpand(_e).cancel()) # issue 6829
646
+ expr2 = shorter(together(expr, deep=True), together(expr1, deep=True))
647
+
648
+ if ratio is S.Infinity:
649
+ expr = expr2
650
+ else:
651
+ expr = shorter(expr2, expr1, expr)
652
+ if not isinstance(expr, Basic): # XXX: temporary hack
653
+ return expr
654
+
655
+ expr = factor_terms(expr, sign=False)
656
+
657
+ # must come before `Piecewise` since this introduces more `Piecewise` terms
658
+ if expr.has(sign):
659
+ expr = expr.rewrite(Abs)
660
+
661
+ # Deal with Piecewise separately to avoid recursive growth of expressions
662
+ if expr.has(Piecewise):
663
+ # Fold into a single Piecewise
664
+ expr = piecewise_fold(expr)
665
+ # Apply doit, if doit=True
666
+ expr = done(expr)
667
+ # Still a Piecewise?
668
+ if expr.has(Piecewise):
669
+ # Fold into a single Piecewise, in case doit lead to some
670
+ # expressions being Piecewise
671
+ expr = piecewise_fold(expr)
672
+ # kroneckersimp also affects Piecewise
673
+ if expr.has(KroneckerDelta):
674
+ expr = kroneckersimp(expr)
675
+ # Still a Piecewise?
676
+ if expr.has(Piecewise):
677
+ # Do not apply doit on the segments as it has already
678
+ # been done above, but simplify
679
+ expr = piecewise_simplify(expr, deep=True, doit=False)
680
+ # Still a Piecewise?
681
+ if expr.has(Piecewise):
682
+ # Try factor common terms
683
+ expr = shorter(expr, factor_terms(expr))
684
+ # As all expressions have been simplified above with the
685
+ # complete simplify, nothing more needs to be done here
686
+ return expr
687
+
688
+ # hyperexpand automatically only works on hypergeometric terms
689
+ # Do this after the Piecewise part to avoid recursive expansion
690
+ expr = hyperexpand(expr)
691
+
692
+ if expr.has(KroneckerDelta):
693
+ expr = kroneckersimp(expr)
694
+
695
+ if expr.has(BesselBase):
696
+ expr = besselsimp(expr)
697
+
698
+ if expr.has(TrigonometricFunction, HyperbolicFunction):
699
+ expr = trigsimp(expr, deep=True)
700
+
701
+ if expr.has(log):
702
+ expr = shorter(expand_log(expr, deep=True), logcombine(expr))
703
+
704
+ if expr.has(CombinatorialFunction, gamma):
705
+ # expression with gamma functions or non-integer arguments is
706
+ # automatically passed to gammasimp
707
+ expr = combsimp(expr)
708
+
709
+ if expr.has(Sum):
710
+ expr = sum_simplify(expr, **kwargs)
711
+
712
+ if expr.has(Integral):
713
+ expr = expr.xreplace({
714
+ i: factor_terms(i) for i in expr.atoms(Integral)})
715
+
716
+ if expr.has(Product):
717
+ expr = product_simplify(expr, **kwargs)
718
+
719
+ from sympy.physics.units import Quantity
720
+
721
+ if expr.has(Quantity):
722
+ from sympy.physics.units.util import quantity_simplify
723
+ expr = quantity_simplify(expr)
724
+
725
+ short = shorter(powsimp(expr, combine='exp', deep=True), powsimp(expr), expr)
726
+ short = shorter(short, cancel(short))
727
+ short = shorter(short, factor_terms(short), expand_power_exp(expand_mul(short)))
728
+ if short.has(TrigonometricFunction, HyperbolicFunction, ExpBase, exp):
729
+ short = exptrigsimp(short)
730
+
731
+ # get rid of hollow 2-arg Mul factorization
732
+ hollow_mul = Transform(
733
+ lambda x: Mul(*x.args),
734
+ lambda x:
735
+ x.is_Mul and
736
+ len(x.args) == 2 and
737
+ x.args[0].is_Number and
738
+ x.args[1].is_Add and
739
+ x.is_commutative)
740
+ expr = short.xreplace(hollow_mul)
741
+
742
+ numer, denom = expr.as_numer_denom()
743
+ if denom.is_Add:
744
+ n, d = fraction(radsimp(1/denom, symbolic=False, max_terms=1))
745
+ if n is not S.One:
746
+ expr = (numer*n).expand()/d
747
+
748
+ if expr.could_extract_minus_sign():
749
+ n, d = fraction(expr)
750
+ if d != 0:
751
+ expr = signsimp(-n/(-d))
752
+
753
+ if measure(expr) > ratio*measure(original_expr):
754
+ expr = original_expr
755
+
756
+ # restore floats
757
+ if floats and rational is None:
758
+ expr = nfloat(expr, exponent=False)
759
+
760
+ return done(expr)
761
+
762
+
763
+ def sum_simplify(s, **kwargs):
764
+ """Main function for Sum simplification"""
765
+ if not isinstance(s, Add):
766
+ s = s.xreplace({a: sum_simplify(a, **kwargs)
767
+ for a in s.atoms(Add) if a.has(Sum)})
768
+ s = expand(s)
769
+ if not isinstance(s, Add):
770
+ return s
771
+
772
+ terms = s.args
773
+ s_t = [] # Sum Terms
774
+ o_t = [] # Other Terms
775
+
776
+ for term in terms:
777
+ sum_terms, other = sift(Mul.make_args(term),
778
+ lambda i: isinstance(i, Sum), binary=True)
779
+ if not sum_terms:
780
+ o_t.append(term)
781
+ continue
782
+ other = [Mul(*other)]
783
+ s_t.append(Mul(*(other + [s._eval_simplify(**kwargs) for s in sum_terms])))
784
+
785
+ result = Add(sum_combine(s_t), *o_t)
786
+
787
+ return result
788
+
789
+
790
+ def sum_combine(s_t):
791
+ """Helper function for Sum simplification
792
+
793
+ Attempts to simplify a list of sums, by combining limits / sum function's
794
+ returns the simplified sum
795
+ """
796
+ used = [False] * len(s_t)
797
+
798
+ for method in range(2):
799
+ for i, s_term1 in enumerate(s_t):
800
+ if not used[i]:
801
+ for j, s_term2 in enumerate(s_t):
802
+ if not used[j] and i != j:
803
+ temp = sum_add(s_term1, s_term2, method)
804
+ if isinstance(temp, (Sum, Mul)):
805
+ s_t[i] = temp
806
+ s_term1 = s_t[i]
807
+ used[j] = True
808
+
809
+ result = S.Zero
810
+ for i, s_term in enumerate(s_t):
811
+ if not used[i]:
812
+ result = Add(result, s_term)
813
+
814
+ return result
815
+
816
+
817
+ def factor_sum(self, limits=None, radical=False, clear=False, fraction=False, sign=True):
818
+ """Return Sum with constant factors extracted.
819
+
820
+ If ``limits`` is specified then ``self`` is the summand; the other
821
+ keywords are passed to ``factor_terms``.
822
+
823
+ Examples
824
+ ========
825
+
826
+ >>> from sympy import Sum
827
+ >>> from sympy.abc import x, y
828
+ >>> from sympy.simplify.simplify import factor_sum
829
+ >>> s = Sum(x*y, (x, 1, 3))
830
+ >>> factor_sum(s)
831
+ y*Sum(x, (x, 1, 3))
832
+ >>> factor_sum(s.function, s.limits)
833
+ y*Sum(x, (x, 1, 3))
834
+ """
835
+ # XXX deprecate in favor of direct call to factor_terms
836
+ kwargs = {"radical": radical, "clear": clear,
837
+ "fraction": fraction, "sign": sign}
838
+ expr = Sum(self, *limits) if limits else self
839
+ return factor_terms(expr, **kwargs)
840
+
841
+
842
+ def sum_add(self, other, method=0):
843
+ """Helper function for Sum simplification"""
844
+ #we know this is something in terms of a constant * a sum
845
+ #so we temporarily put the constants inside for simplification
846
+ #then simplify the result
847
+ def __refactor(val):
848
+ args = Mul.make_args(val)
849
+ sumv = next(x for x in args if isinstance(x, Sum))
850
+ constant = Mul(*[x for x in args if x != sumv])
851
+ return Sum(constant * sumv.function, *sumv.limits)
852
+
853
+ if isinstance(self, Mul):
854
+ rself = __refactor(self)
855
+ else:
856
+ rself = self
857
+
858
+ if isinstance(other, Mul):
859
+ rother = __refactor(other)
860
+ else:
861
+ rother = other
862
+
863
+ if type(rself) is type(rother):
864
+ if method == 0:
865
+ if rself.limits == rother.limits:
866
+ return factor_sum(Sum(rself.function + rother.function, *rself.limits))
867
+ elif method == 1:
868
+ if simplify(rself.function - rother.function) == 0:
869
+ if len(rself.limits) == len(rother.limits) == 1:
870
+ i = rself.limits[0][0]
871
+ x1 = rself.limits[0][1]
872
+ y1 = rself.limits[0][2]
873
+ j = rother.limits[0][0]
874
+ x2 = rother.limits[0][1]
875
+ y2 = rother.limits[0][2]
876
+
877
+ if i == j:
878
+ if x2 == y1 + 1:
879
+ return factor_sum(Sum(rself.function, (i, x1, y2)))
880
+ elif x1 == y2 + 1:
881
+ return factor_sum(Sum(rself.function, (i, x2, y1)))
882
+
883
+ return Add(self, other)
884
+
885
+
886
+ def product_simplify(s, **kwargs):
887
+ """Main function for Product simplification"""
888
+ terms = Mul.make_args(s)
889
+ p_t = [] # Product Terms
890
+ o_t = [] # Other Terms
891
+
892
+ deep = kwargs.get('deep', True)
893
+ for term in terms:
894
+ if isinstance(term, Product):
895
+ if deep:
896
+ p_t.append(Product(term.function.simplify(**kwargs),
897
+ *term.limits))
898
+ else:
899
+ p_t.append(term)
900
+ else:
901
+ o_t.append(term)
902
+
903
+ used = [False] * len(p_t)
904
+
905
+ for method in range(2):
906
+ for i, p_term1 in enumerate(p_t):
907
+ if not used[i]:
908
+ for j, p_term2 in enumerate(p_t):
909
+ if not used[j] and i != j:
910
+ tmp_prod = product_mul(p_term1, p_term2, method)
911
+ if isinstance(tmp_prod, Product):
912
+ p_t[i] = tmp_prod
913
+ used[j] = True
914
+
915
+ result = Mul(*o_t)
916
+
917
+ for i, p_term in enumerate(p_t):
918
+ if not used[i]:
919
+ result = Mul(result, p_term)
920
+
921
+ return result
922
+
923
+
924
+ def product_mul(self, other, method=0):
925
+ """Helper function for Product simplification"""
926
+ if type(self) is type(other):
927
+ if method == 0:
928
+ if self.limits == other.limits:
929
+ return Product(self.function * other.function, *self.limits)
930
+ elif method == 1:
931
+ if simplify(self.function - other.function) == 0:
932
+ if len(self.limits) == len(other.limits) == 1:
933
+ i = self.limits[0][0]
934
+ x1 = self.limits[0][1]
935
+ y1 = self.limits[0][2]
936
+ j = other.limits[0][0]
937
+ x2 = other.limits[0][1]
938
+ y2 = other.limits[0][2]
939
+
940
+ if i == j:
941
+ if x2 == y1 + 1:
942
+ return Product(self.function, (i, x1, y2))
943
+ elif x1 == y2 + 1:
944
+ return Product(self.function, (i, x2, y1))
945
+
946
+ return Mul(self, other)
947
+
948
+
949
+ def _nthroot_solve(p, n, prec):
950
+ """
951
+ helper function for ``nthroot``
952
+ It denests ``p**Rational(1, n)`` using its minimal polynomial
953
+ """
954
+ from sympy.solvers import solve
955
+ while n % 2 == 0:
956
+ p = sqrtdenest(sqrt(p))
957
+ n = n // 2
958
+ if n == 1:
959
+ return p
960
+ pn = p**Rational(1, n)
961
+ x = Symbol('x')
962
+ f = _minimal_polynomial_sq(p, n, x)
963
+ if f is None:
964
+ return None
965
+ sols = solve(f, x)
966
+ for sol in sols:
967
+ if abs(sol - pn).n() < 1./10**prec:
968
+ sol = sqrtdenest(sol)
969
+ if _mexpand(sol**n) == p:
970
+ return sol
971
+
972
+
973
+ def logcombine(expr, force=False):
974
+ """
975
+ Takes logarithms and combines them using the following rules:
976
+
977
+ - log(x) + log(y) == log(x*y) if both are positive
978
+ - a*log(x) == log(x**a) if x is positive and a is real
979
+
980
+ If ``force`` is ``True`` then the assumptions above will be assumed to hold if
981
+ there is no assumption already in place on a quantity. For example, if
982
+ ``a`` is imaginary or the argument negative, force will not perform a
983
+ combination but if ``a`` is a symbol with no assumptions the change will
984
+ take place.
985
+
986
+ Examples
987
+ ========
988
+
989
+ >>> from sympy import Symbol, symbols, log, logcombine, I
990
+ >>> from sympy.abc import a, x, y, z
991
+ >>> logcombine(a*log(x) + log(y) - log(z))
992
+ a*log(x) + log(y) - log(z)
993
+ >>> logcombine(a*log(x) + log(y) - log(z), force=True)
994
+ log(x**a*y/z)
995
+ >>> x,y,z = symbols('x,y,z', positive=True)
996
+ >>> a = Symbol('a', real=True)
997
+ >>> logcombine(a*log(x) + log(y) - log(z))
998
+ log(x**a*y/z)
999
+
1000
+ The transformation is limited to factors and/or terms that
1001
+ contain logs, so the result depends on the initial state of
1002
+ expansion:
1003
+
1004
+ >>> eq = (2 + 3*I)*log(x)
1005
+ >>> logcombine(eq, force=True) == eq
1006
+ True
1007
+ >>> logcombine(eq.expand(), force=True)
1008
+ log(x**2) + I*log(x**3)
1009
+
1010
+ See Also
1011
+ ========
1012
+
1013
+ posify: replace all symbols with symbols having positive assumptions
1014
+ sympy.core.function.expand_log: expand the logarithms of products
1015
+ and powers; the opposite of logcombine
1016
+
1017
+ """
1018
+
1019
+ def f(rv):
1020
+ if not (rv.is_Add or rv.is_Mul):
1021
+ return rv
1022
+
1023
+ def gooda(a):
1024
+ # bool to tell whether the leading ``a`` in ``a*log(x)``
1025
+ # could appear as log(x**a)
1026
+ return (a is not S.NegativeOne and # -1 *could* go, but we disallow
1027
+ (a.is_extended_real or force and a.is_extended_real is not False))
1028
+
1029
+ def goodlog(l):
1030
+ # bool to tell whether log ``l``'s argument can combine with others
1031
+ a = l.args[0]
1032
+ return a.is_positive or force and a.is_nonpositive is not False
1033
+
1034
+ other = []
1035
+ logs = []
1036
+ log1 = defaultdict(list)
1037
+ for a in Add.make_args(rv):
1038
+ if isinstance(a, log) and goodlog(a):
1039
+ log1[()].append(([], a))
1040
+ elif not a.is_Mul:
1041
+ other.append(a)
1042
+ else:
1043
+ ot = []
1044
+ co = []
1045
+ lo = []
1046
+ for ai in a.args:
1047
+ if ai.is_Rational and ai < 0:
1048
+ ot.append(S.NegativeOne)
1049
+ co.append(-ai)
1050
+ elif isinstance(ai, log) and goodlog(ai):
1051
+ lo.append(ai)
1052
+ elif gooda(ai):
1053
+ co.append(ai)
1054
+ else:
1055
+ ot.append(ai)
1056
+ if len(lo) > 1:
1057
+ logs.append((ot, co, lo))
1058
+ elif lo:
1059
+ log1[tuple(ot)].append((co, lo[0]))
1060
+ else:
1061
+ other.append(a)
1062
+
1063
+ # if there is only one log in other, put it with the
1064
+ # good logs
1065
+ if len(other) == 1 and isinstance(other[0], log):
1066
+ log1[()].append(([], other.pop()))
1067
+ # if there is only one log at each coefficient and none have
1068
+ # an exponent to place inside the log then there is nothing to do
1069
+ if not logs and all(len(log1[k]) == 1 and log1[k][0] == [] for k in log1):
1070
+ return rv
1071
+
1072
+ # collapse multi-logs as far as possible in a canonical way
1073
+ # TODO: see if x*log(a)+x*log(a)*log(b) -> x*log(a)*(1+log(b))?
1074
+ # -- in this case, it's unambiguous, but if it were were a log(c) in
1075
+ # each term then it's arbitrary whether they are grouped by log(a) or
1076
+ # by log(c). So for now, just leave this alone; it's probably better to
1077
+ # let the user decide
1078
+ for o, e, l in logs:
1079
+ l = list(ordered(l))
1080
+ e = log(l.pop(0).args[0]**Mul(*e))
1081
+ while l:
1082
+ li = l.pop(0)
1083
+ e = log(li.args[0]**e)
1084
+ c, l = Mul(*o), e
1085
+ if isinstance(l, log): # it should be, but check to be sure
1086
+ log1[(c,)].append(([], l))
1087
+ else:
1088
+ other.append(c*l)
1089
+
1090
+ # logs that have the same coefficient can multiply
1091
+ for k in list(log1.keys()):
1092
+ log1[Mul(*k)] = log(logcombine(Mul(*[
1093
+ l.args[0]**Mul(*c) for c, l in log1.pop(k)]),
1094
+ force=force), evaluate=False)
1095
+
1096
+ # logs that have oppositely signed coefficients can divide
1097
+ for k in ordered(list(log1.keys())):
1098
+ if k not in log1: # already popped as -k
1099
+ continue
1100
+ if -k in log1:
1101
+ # figure out which has the minus sign; the one with
1102
+ # more op counts should be the one
1103
+ num, den = k, -k
1104
+ if num.count_ops() > den.count_ops():
1105
+ num, den = den, num
1106
+ other.append(
1107
+ num*log(log1.pop(num).args[0]/log1.pop(den).args[0],
1108
+ evaluate=False))
1109
+ else:
1110
+ other.append(k*log1.pop(k))
1111
+
1112
+ return Add(*other)
1113
+
1114
+ return _bottom_up(expr, f)
1115
+
1116
+
1117
+ def inversecombine(expr):
1118
+ """Simplify the composition of a function and its inverse.
1119
+
1120
+ Explanation
1121
+ ===========
1122
+
1123
+ No attention is paid to whether the inverse is a left inverse or a
1124
+ right inverse; thus, the result will in general not be equivalent
1125
+ to the original expression.
1126
+
1127
+ Examples
1128
+ ========
1129
+
1130
+ >>> from sympy.simplify.simplify import inversecombine
1131
+ >>> from sympy import asin, sin, log, exp
1132
+ >>> from sympy.abc import x
1133
+ >>> inversecombine(asin(sin(x)))
1134
+ x
1135
+ >>> inversecombine(2*log(exp(3*x)))
1136
+ 6*x
1137
+ """
1138
+
1139
+ def f(rv):
1140
+ if isinstance(rv, log):
1141
+ if isinstance(rv.args[0], exp) or (rv.args[0].is_Pow and rv.args[0].base == S.Exp1):
1142
+ rv = rv.args[0].exp
1143
+ elif rv.is_Function and hasattr(rv, "inverse"):
1144
+ if (len(rv.args) == 1 and len(rv.args[0].args) == 1 and
1145
+ isinstance(rv.args[0], rv.inverse(argindex=1))):
1146
+ rv = rv.args[0].args[0]
1147
+ if rv.is_Pow and rv.base == S.Exp1:
1148
+ if isinstance(rv.exp, log):
1149
+ rv = rv.exp.args[0]
1150
+ return rv
1151
+
1152
+ return _bottom_up(expr, f)
1153
+
1154
+
1155
+ def kroneckersimp(expr):
1156
+ """
1157
+ Simplify expressions with KroneckerDelta.
1158
+
1159
+ The only simplification currently attempted is to identify multiplicative cancellation:
1160
+
1161
+ Examples
1162
+ ========
1163
+
1164
+ >>> from sympy import KroneckerDelta, kroneckersimp
1165
+ >>> from sympy.abc import i
1166
+ >>> kroneckersimp(1 + KroneckerDelta(0, i) * KroneckerDelta(1, i))
1167
+ 1
1168
+ """
1169
+ def args_cancel(args1, args2):
1170
+ for i1 in range(2):
1171
+ for i2 in range(2):
1172
+ a1 = args1[i1]
1173
+ a2 = args2[i2]
1174
+ a3 = args1[(i1 + 1) % 2]
1175
+ a4 = args2[(i2 + 1) % 2]
1176
+ if Eq(a1, a2) is S.true and Eq(a3, a4) is S.false:
1177
+ return True
1178
+ return False
1179
+
1180
+ def cancel_kronecker_mul(m):
1181
+ args = m.args
1182
+ deltas = [a for a in args if isinstance(a, KroneckerDelta)]
1183
+ for delta1, delta2 in subsets(deltas, 2):
1184
+ args1 = delta1.args
1185
+ args2 = delta2.args
1186
+ if args_cancel(args1, args2):
1187
+ return S.Zero * m # In case of oo etc
1188
+ return m
1189
+
1190
+ if not expr.has(KroneckerDelta):
1191
+ return expr
1192
+
1193
+ if expr.has(Piecewise):
1194
+ expr = expr.rewrite(KroneckerDelta)
1195
+
1196
+ newexpr = expr
1197
+ expr = None
1198
+
1199
+ while newexpr != expr:
1200
+ expr = newexpr
1201
+ newexpr = expr.replace(lambda e: isinstance(e, Mul), cancel_kronecker_mul)
1202
+
1203
+ return expr
1204
+
1205
+
1206
+ def besselsimp(expr):
1207
+ """
1208
+ Simplify bessel-type functions.
1209
+
1210
+ Explanation
1211
+ ===========
1212
+
1213
+ This routine tries to simplify bessel-type functions. Currently it only
1214
+ works on the Bessel J and I functions, however. It works by looking at all
1215
+ such functions in turn, and eliminating factors of "I" and "-1" (actually
1216
+ their polar equivalents) in front of the argument. Then, functions of
1217
+ half-integer order are rewritten using trigonometric functions and
1218
+ functions of integer order (> 1) are rewritten using functions
1219
+ of low order. Finally, if the expression was changed, compute
1220
+ factorization of the result with factor().
1221
+
1222
+ >>> from sympy import besselj, besseli, besselsimp, polar_lift, I, S
1223
+ >>> from sympy.abc import z, nu
1224
+ >>> besselsimp(besselj(nu, z*polar_lift(-1)))
1225
+ exp(I*pi*nu)*besselj(nu, z)
1226
+ >>> besselsimp(besseli(nu, z*polar_lift(-I)))
1227
+ exp(-I*pi*nu/2)*besselj(nu, z)
1228
+ >>> besselsimp(besseli(S(-1)/2, z))
1229
+ sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z))
1230
+ >>> besselsimp(z*besseli(0, z) + z*(besseli(2, z))/2 + besseli(1, z))
1231
+ 3*z*besseli(0, z)/2
1232
+ """
1233
+ # TODO
1234
+ # - better algorithm?
1235
+ # - simplify (cos(pi*b)*besselj(b,z) - besselj(-b,z))/sin(pi*b) ...
1236
+ # - use contiguity relations?
1237
+
1238
+ def replacer(fro, to, factors):
1239
+ factors = set(factors)
1240
+
1241
+ def repl(nu, z):
1242
+ if factors.intersection(Mul.make_args(z)):
1243
+ return to(nu, z)
1244
+ return fro(nu, z)
1245
+ return repl
1246
+
1247
+ def torewrite(fro, to):
1248
+ def tofunc(nu, z):
1249
+ return fro(nu, z).rewrite(to)
1250
+ return tofunc
1251
+
1252
+ def tominus(fro):
1253
+ def tofunc(nu, z):
1254
+ return exp(I*pi*nu)*fro(nu, exp_polar(-I*pi)*z)
1255
+ return tofunc
1256
+
1257
+ orig_expr = expr
1258
+
1259
+ ifactors = [I, exp_polar(I*pi/2), exp_polar(-I*pi/2)]
1260
+ expr = expr.replace(
1261
+ besselj, replacer(besselj,
1262
+ torewrite(besselj, besseli), ifactors))
1263
+ expr = expr.replace(
1264
+ besseli, replacer(besseli,
1265
+ torewrite(besseli, besselj), ifactors))
1266
+
1267
+ minusfactors = [-1, exp_polar(I*pi)]
1268
+ expr = expr.replace(
1269
+ besselj, replacer(besselj, tominus(besselj), minusfactors))
1270
+ expr = expr.replace(
1271
+ besseli, replacer(besseli, tominus(besseli), minusfactors))
1272
+
1273
+ z0 = Dummy('z')
1274
+
1275
+ def expander(fro):
1276
+ def repl(nu, z):
1277
+ if (nu % 1) == S.Half:
1278
+ return simplify(trigsimp(unpolarify(
1279
+ fro(nu, z0).rewrite(besselj).rewrite(jn).expand(
1280
+ func=True)).subs(z0, z)))
1281
+ elif nu.is_Integer and nu > 1:
1282
+ return fro(nu, z).expand(func=True)
1283
+ return fro(nu, z)
1284
+ return repl
1285
+
1286
+ expr = expr.replace(besselj, expander(besselj))
1287
+ expr = expr.replace(bessely, expander(bessely))
1288
+ expr = expr.replace(besseli, expander(besseli))
1289
+ expr = expr.replace(besselk, expander(besselk))
1290
+
1291
+ def _bessel_simp_recursion(expr):
1292
+
1293
+ def _use_recursion(bessel, expr):
1294
+ while True:
1295
+ bessels = expr.find(lambda x: isinstance(x, bessel))
1296
+ try:
1297
+ for ba in sorted(bessels, key=lambda x: re(x.args[0])):
1298
+ a, x = ba.args
1299
+ bap1 = bessel(a+1, x)
1300
+ bap2 = bessel(a+2, x)
1301
+ if expr.has(bap1) and expr.has(bap2):
1302
+ expr = expr.subs(ba, 2*(a+1)/x*bap1 - bap2)
1303
+ break
1304
+ else:
1305
+ return expr
1306
+ except (ValueError, TypeError):
1307
+ return expr
1308
+ if expr.has(besselj):
1309
+ expr = _use_recursion(besselj, expr)
1310
+ if expr.has(bessely):
1311
+ expr = _use_recursion(bessely, expr)
1312
+ return expr
1313
+
1314
+ expr = _bessel_simp_recursion(expr)
1315
+ if expr != orig_expr:
1316
+ expr = expr.factor()
1317
+
1318
+ return expr
1319
+
1320
+
1321
+ def nthroot(expr, n, max_len=4, prec=15):
1322
+ """
1323
+ Compute a real nth-root of a sum of surds.
1324
+
1325
+ Parameters
1326
+ ==========
1327
+
1328
+ expr : sum of surds
1329
+ n : integer
1330
+ max_len : maximum number of surds passed as constants to ``nsimplify``
1331
+
1332
+ Algorithm
1333
+ =========
1334
+
1335
+ First ``nsimplify`` is used to get a candidate root; if it is not a
1336
+ root the minimal polynomial is computed; the answer is one of its
1337
+ roots.
1338
+
1339
+ Examples
1340
+ ========
1341
+
1342
+ >>> from sympy.simplify.simplify import nthroot
1343
+ >>> from sympy import sqrt
1344
+ >>> nthroot(90 + 34*sqrt(7), 3)
1345
+ sqrt(7) + 3
1346
+
1347
+ """
1348
+ expr = sympify(expr)
1349
+ n = sympify(n)
1350
+ p = expr**Rational(1, n)
1351
+ if not n.is_integer:
1352
+ return p
1353
+ if not _is_sum_surds(expr):
1354
+ return p
1355
+ surds = []
1356
+ coeff_muls = [x.as_coeff_Mul() for x in expr.args]
1357
+ for x, y in coeff_muls:
1358
+ if not x.is_rational:
1359
+ return p
1360
+ if y is S.One:
1361
+ continue
1362
+ if not (y.is_Pow and y.exp == S.Half and y.base.is_integer):
1363
+ return p
1364
+ surds.append(y)
1365
+ surds.sort()
1366
+ surds = surds[:max_len]
1367
+ if expr < 0 and n % 2 == 1:
1368
+ p = (-expr)**Rational(1, n)
1369
+ a = nsimplify(p, constants=surds)
1370
+ res = a if _mexpand(a**n) == _mexpand(-expr) else p
1371
+ return -res
1372
+ a = nsimplify(p, constants=surds)
1373
+ if _mexpand(a) is not _mexpand(p) and _mexpand(a**n) == _mexpand(expr):
1374
+ return _mexpand(a)
1375
+ expr = _nthroot_solve(expr, n, prec)
1376
+ if expr is None:
1377
+ return p
1378
+ return expr
1379
+
1380
+
1381
+ def nsimplify(expr, constants=(), tolerance=None, full=False, rational=None,
1382
+ rational_conversion='base10'):
1383
+ """
1384
+ Find a simple representation for a number or, if there are free symbols or
1385
+ if ``rational=True``, then replace Floats with their Rational equivalents. If
1386
+ no change is made and rational is not False then Floats will at least be
1387
+ converted to Rationals.
1388
+
1389
+ Explanation
1390
+ ===========
1391
+
1392
+ For numerical expressions, a simple formula that numerically matches the
1393
+ given numerical expression is sought (and the input should be possible
1394
+ to evalf to a precision of at least 30 digits).
1395
+
1396
+ Optionally, a list of (rationally independent) constants to
1397
+ include in the formula may be given.
1398
+
1399
+ A lower tolerance may be set to find less exact matches. If no tolerance
1400
+ is given then the least precise value will set the tolerance (e.g. Floats
1401
+ default to 15 digits of precision, so would be tolerance=10**-15).
1402
+
1403
+ With ``full=True``, a more extensive search is performed
1404
+ (this is useful to find simpler numbers when the tolerance
1405
+ is set low).
1406
+
1407
+ When converting to rational, if rational_conversion='base10' (the default), then
1408
+ convert floats to rationals using their base-10 (string) representation.
1409
+ When rational_conversion='exact' it uses the exact, base-2 representation.
1410
+
1411
+ Examples
1412
+ ========
1413
+
1414
+ >>> from sympy import nsimplify, sqrt, GoldenRatio, exp, I, pi
1415
+ >>> nsimplify(4/(1+sqrt(5)), [GoldenRatio])
1416
+ -2 + 2*GoldenRatio
1417
+ >>> nsimplify((1/(exp(3*pi*I/5)+1)))
1418
+ 1/2 - I*sqrt(sqrt(5)/10 + 1/4)
1419
+ >>> nsimplify(I**I, [pi])
1420
+ exp(-pi/2)
1421
+ >>> nsimplify(pi, tolerance=0.01)
1422
+ 22/7
1423
+
1424
+ >>> nsimplify(0.333333333333333, rational=True, rational_conversion='exact')
1425
+ 6004799503160655/18014398509481984
1426
+ >>> nsimplify(0.333333333333333, rational=True)
1427
+ 1/3
1428
+
1429
+ See Also
1430
+ ========
1431
+
1432
+ sympy.core.function.nfloat
1433
+
1434
+ """
1435
+ try:
1436
+ return sympify(as_int(expr))
1437
+ except (TypeError, ValueError):
1438
+ pass
1439
+ expr = sympify(expr).xreplace({
1440
+ Float('inf'): S.Infinity,
1441
+ Float('-inf'): S.NegativeInfinity,
1442
+ })
1443
+ if expr is S.Infinity or expr is S.NegativeInfinity:
1444
+ return expr
1445
+ if rational or expr.free_symbols:
1446
+ return _real_to_rational(expr, tolerance, rational_conversion)
1447
+
1448
+ # SymPy's default tolerance for Rationals is 15; other numbers may have
1449
+ # lower tolerances set, so use them to pick the largest tolerance if None
1450
+ # was given
1451
+ if tolerance is None:
1452
+ tolerance = 10**-min([15] +
1453
+ [mpmath.libmp.libmpf.prec_to_dps(n._prec)
1454
+ for n in expr.atoms(Float)])
1455
+ # XXX should prec be set independent of tolerance or should it be computed
1456
+ # from tolerance?
1457
+ prec = 30
1458
+ bprec = int(prec*3.33)
1459
+
1460
+ constants_dict = {}
1461
+ for constant in constants:
1462
+ constant = sympify(constant)
1463
+ v = constant.evalf(prec)
1464
+ if not v.is_Float:
1465
+ raise ValueError("constants must be real-valued")
1466
+ constants_dict[str(constant)] = v._to_mpmath(bprec)
1467
+
1468
+ exprval = expr.evalf(prec, chop=True)
1469
+ re, im = exprval.as_real_imag()
1470
+
1471
+ # safety check to make sure that this evaluated to a number
1472
+ if not (re.is_Number and im.is_Number):
1473
+ return expr
1474
+
1475
+ def nsimplify_real(x):
1476
+ orig = mpmath.mp.dps
1477
+ xv = x._to_mpmath(bprec)
1478
+ try:
1479
+ # We'll be happy with low precision if a simple fraction
1480
+ if not (tolerance or full):
1481
+ mpmath.mp.dps = 15
1482
+ rat = mpmath.pslq([xv, 1])
1483
+ if rat is not None:
1484
+ return Rational(-int(rat[1]), int(rat[0]))
1485
+ mpmath.mp.dps = prec
1486
+ newexpr = mpmath.identify(xv, constants=constants_dict,
1487
+ tol=tolerance, full=full)
1488
+ if not newexpr:
1489
+ raise ValueError
1490
+ if full:
1491
+ newexpr = newexpr[0]
1492
+ expr = sympify(newexpr)
1493
+ if x and not expr: # don't let x become 0
1494
+ raise ValueError
1495
+ if expr.is_finite is False and xv not in [mpmath.inf, mpmath.ninf]:
1496
+ raise ValueError
1497
+ return expr
1498
+ finally:
1499
+ # even though there are returns above, this is executed
1500
+ # before leaving
1501
+ mpmath.mp.dps = orig
1502
+ try:
1503
+ if re:
1504
+ re = nsimplify_real(re)
1505
+ if im:
1506
+ im = nsimplify_real(im)
1507
+ except ValueError:
1508
+ if rational is None:
1509
+ return _real_to_rational(expr, rational_conversion=rational_conversion)
1510
+ return expr
1511
+
1512
+ rv = re + im*S.ImaginaryUnit
1513
+ # if there was a change or rational is explicitly not wanted
1514
+ # return the value, else return the Rational representation
1515
+ if rv != expr or rational is False:
1516
+ return rv
1517
+ return _real_to_rational(expr, rational_conversion=rational_conversion)
1518
+
1519
+
1520
+ def _real_to_rational(expr, tolerance=None, rational_conversion='base10'):
1521
+ """
1522
+ Replace all reals in expr with rationals.
1523
+
1524
+ Examples
1525
+ ========
1526
+
1527
+ >>> from sympy.simplify.simplify import _real_to_rational
1528
+ >>> from sympy.abc import x
1529
+
1530
+ >>> _real_to_rational(.76 + .1*x**.5)
1531
+ sqrt(x)/10 + 19/25
1532
+
1533
+ If rational_conversion='base10', this uses the base-10 string. If
1534
+ rational_conversion='exact', the exact, base-2 representation is used.
1535
+
1536
+ >>> _real_to_rational(0.333333333333333, rational_conversion='exact')
1537
+ 6004799503160655/18014398509481984
1538
+ >>> _real_to_rational(0.333333333333333)
1539
+ 1/3
1540
+
1541
+ """
1542
+ expr = _sympify(expr)
1543
+ inf = Float('inf')
1544
+ p = expr
1545
+ reps = {}
1546
+ reduce_num = None
1547
+ if tolerance is not None and tolerance < 1:
1548
+ reduce_num = ceiling(1/tolerance)
1549
+ for fl in p.atoms(Float):
1550
+ key = fl
1551
+ if reduce_num is not None:
1552
+ r = Rational(fl).limit_denominator(reduce_num)
1553
+ elif (tolerance is not None and tolerance >= 1 and
1554
+ fl.is_Integer is False):
1555
+ r = Rational(tolerance*round(fl/tolerance)
1556
+ ).limit_denominator(int(tolerance))
1557
+ else:
1558
+ if rational_conversion == 'exact':
1559
+ r = Rational(fl)
1560
+ reps[key] = r
1561
+ continue
1562
+ elif rational_conversion != 'base10':
1563
+ raise ValueError("rational_conversion must be 'base10' or 'exact'")
1564
+
1565
+ r = nsimplify(fl, rational=False)
1566
+ # e.g. log(3).n() -> log(3) instead of a Rational
1567
+ if fl and not r:
1568
+ r = Rational(fl)
1569
+ elif not r.is_Rational:
1570
+ if fl in (inf, -inf):
1571
+ r = S.ComplexInfinity
1572
+ elif fl < 0:
1573
+ fl = -fl
1574
+ d = Pow(10, int(mpmath.log(fl)/mpmath.log(10)))
1575
+ r = -Rational(str(fl/d))*d
1576
+ elif fl > 0:
1577
+ d = Pow(10, int(mpmath.log(fl)/mpmath.log(10)))
1578
+ r = Rational(str(fl/d))*d
1579
+ else:
1580
+ r = S.Zero
1581
+ reps[key] = r
1582
+ return p.subs(reps, simultaneous=True)
1583
+
1584
+
1585
+ def clear_coefficients(expr, rhs=S.Zero):
1586
+ """Return `p, r` where `p` is the expression obtained when Rational
1587
+ additive and multiplicative coefficients of `expr` have been stripped
1588
+ away in a naive fashion (i.e. without simplification). The operations
1589
+ needed to remove the coefficients will be applied to `rhs` and returned
1590
+ as `r`.
1591
+
1592
+ Examples
1593
+ ========
1594
+
1595
+ >>> from sympy.simplify.simplify import clear_coefficients
1596
+ >>> from sympy.abc import x, y
1597
+ >>> from sympy import Dummy
1598
+ >>> expr = 4*y*(6*x + 3)
1599
+ >>> clear_coefficients(expr - 2)
1600
+ (y*(2*x + 1), 1/6)
1601
+
1602
+ When solving 2 or more expressions like `expr = a`,
1603
+ `expr = b`, etc..., it is advantageous to provide a Dummy symbol
1604
+ for `rhs` and simply replace it with `a`, `b`, etc... in `r`.
1605
+
1606
+ >>> rhs = Dummy('rhs')
1607
+ >>> clear_coefficients(expr, rhs)
1608
+ (y*(2*x + 1), _rhs/12)
1609
+ >>> _[1].subs(rhs, 2)
1610
+ 1/6
1611
+ """
1612
+ was = None
1613
+ free = expr.free_symbols
1614
+ if expr.is_Rational:
1615
+ return (S.Zero, rhs - expr)
1616
+ while expr and was != expr:
1617
+ was = expr
1618
+ m, expr = (
1619
+ expr.as_content_primitive()
1620
+ if free else
1621
+ factor_terms(expr).as_coeff_Mul(rational=True))
1622
+ rhs /= m
1623
+ c, expr = expr.as_coeff_Add(rational=True)
1624
+ rhs -= c
1625
+ expr = signsimp(expr, evaluate = False)
1626
+ if expr.could_extract_minus_sign():
1627
+ expr = -expr
1628
+ rhs = -rhs
1629
+ return expr, rhs
1630
+
1631
+ def nc_simplify(expr, deep=True):
1632
+ '''
1633
+ Simplify a non-commutative expression composed of multiplication
1634
+ and raising to a power by grouping repeated subterms into one power.
1635
+ Priority is given to simplifications that give the fewest number
1636
+ of arguments in the end (for example, in a*b*a*b*c*a*b*c simplifying
1637
+ to (a*b)**2*c*a*b*c gives 5 arguments while a*b*(a*b*c)**2 has 3).
1638
+ If ``expr`` is a sum of such terms, the sum of the simplified terms
1639
+ is returned.
1640
+
1641
+ Keyword argument ``deep`` controls whether or not subexpressions
1642
+ nested deeper inside the main expression are simplified. See examples
1643
+ below. Setting `deep` to `False` can save time on nested expressions
1644
+ that do not need simplifying on all levels.
1645
+
1646
+ Examples
1647
+ ========
1648
+
1649
+ >>> from sympy import symbols
1650
+ >>> from sympy.simplify.simplify import nc_simplify
1651
+ >>> a, b, c = symbols("a b c", commutative=False)
1652
+ >>> nc_simplify(a*b*a*b*c*a*b*c)
1653
+ a*b*(a*b*c)**2
1654
+ >>> expr = a**2*b*a**4*b*a**4
1655
+ >>> nc_simplify(expr)
1656
+ a**2*(b*a**4)**2
1657
+ >>> nc_simplify(a*b*a*b*c**2*(a*b)**2*c**2)
1658
+ ((a*b)**2*c**2)**2
1659
+ >>> nc_simplify(a*b*a*b + 2*a*c*a**2*c*a**2*c*a)
1660
+ (a*b)**2 + 2*(a*c*a)**3
1661
+ >>> nc_simplify(b**-1*a**-1*(a*b)**2)
1662
+ a*b
1663
+ >>> nc_simplify(a**-1*b**-1*c*a)
1664
+ (b*a)**(-1)*c*a
1665
+ >>> expr = (a*b*a*b)**2*a*c*a*c
1666
+ >>> nc_simplify(expr)
1667
+ (a*b)**4*(a*c)**2
1668
+ >>> nc_simplify(expr, deep=False)
1669
+ (a*b*a*b)**2*(a*c)**2
1670
+
1671
+ '''
1672
+ if isinstance(expr, MatrixExpr):
1673
+ expr = expr.doit(inv_expand=False)
1674
+ _Add, _Mul, _Pow, _Symbol = MatAdd, MatMul, MatPow, MatrixSymbol
1675
+ else:
1676
+ _Add, _Mul, _Pow, _Symbol = Add, Mul, Pow, Symbol
1677
+
1678
+ # =========== Auxiliary functions ========================
1679
+ def _overlaps(args):
1680
+ # Calculate a list of lists m such that m[i][j] contains the lengths
1681
+ # of all possible overlaps between args[:i+1] and args[i+1+j:].
1682
+ # An overlap is a suffix of the prefix that matches a prefix
1683
+ # of the suffix.
1684
+ # For example, let expr=c*a*b*a*b*a*b*a*b. Then m[3][0] contains
1685
+ # the lengths of overlaps of c*a*b*a*b with a*b*a*b. The overlaps
1686
+ # are a*b*a*b, a*b and the empty word so that m[3][0]=[4,2,0].
1687
+ # All overlaps rather than only the longest one are recorded
1688
+ # because this information helps calculate other overlap lengths.
1689
+ m = [[([1, 0] if a == args[0] else [0]) for a in args[1:]]]
1690
+ for i in range(1, len(args)):
1691
+ overlaps = []
1692
+ j = 0
1693
+ for j in range(len(args) - i - 1):
1694
+ overlap = []
1695
+ for v in m[i-1][j+1]:
1696
+ if j + i + 1 + v < len(args) and args[i] == args[j+i+1+v]:
1697
+ overlap.append(v + 1)
1698
+ overlap += [0]
1699
+ overlaps.append(overlap)
1700
+ m.append(overlaps)
1701
+ return m
1702
+
1703
+ def _reduce_inverses(_args):
1704
+ # replace consecutive negative powers by an inverse
1705
+ # of a product of positive powers, e.g. a**-1*b**-1*c
1706
+ # will simplify to (a*b)**-1*c;
1707
+ # return that new args list and the number of negative
1708
+ # powers in it (inv_tot)
1709
+ inv_tot = 0 # total number of inverses
1710
+ inverses = []
1711
+ args = []
1712
+ for arg in _args:
1713
+ if isinstance(arg, _Pow) and arg.args[1].is_extended_negative:
1714
+ inverses = [arg**-1] + inverses
1715
+ inv_tot += 1
1716
+ else:
1717
+ if len(inverses) == 1:
1718
+ args.append(inverses[0]**-1)
1719
+ elif len(inverses) > 1:
1720
+ args.append(_Pow(_Mul(*inverses), -1))
1721
+ inv_tot -= len(inverses) - 1
1722
+ inverses = []
1723
+ args.append(arg)
1724
+ if inverses:
1725
+ args.append(_Pow(_Mul(*inverses), -1))
1726
+ inv_tot -= len(inverses) - 1
1727
+ return inv_tot, tuple(args)
1728
+
1729
+ def get_score(s):
1730
+ # compute the number of arguments of s
1731
+ # (including in nested expressions) overall
1732
+ # but ignore exponents
1733
+ if isinstance(s, _Pow):
1734
+ return get_score(s.args[0])
1735
+ elif isinstance(s, (_Add, _Mul)):
1736
+ return sum(get_score(a) for a in s.args)
1737
+ return 1
1738
+
1739
+ def compare(s, alt_s):
1740
+ # compare two possible simplifications and return a
1741
+ # "better" one
1742
+ if s != alt_s and get_score(alt_s) < get_score(s):
1743
+ return alt_s
1744
+ return s
1745
+ # ========================================================
1746
+
1747
+ if not isinstance(expr, (_Add, _Mul, _Pow)) or expr.is_commutative:
1748
+ return expr
1749
+ args = expr.args[:]
1750
+ if isinstance(expr, _Pow):
1751
+ if deep:
1752
+ return _Pow(nc_simplify(args[0]), args[1]).doit()
1753
+ else:
1754
+ return expr
1755
+ elif isinstance(expr, _Add):
1756
+ return _Add(*[nc_simplify(a, deep=deep) for a in args]).doit()
1757
+ else:
1758
+ # get the non-commutative part
1759
+ c_args, args = expr.args_cnc()
1760
+ com_coeff = Mul(*c_args)
1761
+ if not equal_valued(com_coeff, 1):
1762
+ return com_coeff*nc_simplify(expr/com_coeff, deep=deep)
1763
+
1764
+ inv_tot, args = _reduce_inverses(args)
1765
+ # if most arguments are negative, work with the inverse
1766
+ # of the expression, e.g. a**-1*b*a**-1*c**-1 will become
1767
+ # (c*a*b**-1*a)**-1 at the end so can work with c*a*b**-1*a
1768
+ invert = False
1769
+ if inv_tot > len(args)/2:
1770
+ invert = True
1771
+ args = [a**-1 for a in args[::-1]]
1772
+
1773
+ if deep:
1774
+ args = tuple(nc_simplify(a) for a in args)
1775
+
1776
+ m = _overlaps(args)
1777
+
1778
+ # simps will be {subterm: end} where `end` is the ending
1779
+ # index of a sequence of repetitions of subterm;
1780
+ # this is for not wasting time with subterms that are part
1781
+ # of longer, already considered sequences
1782
+ simps = {}
1783
+
1784
+ post = 1
1785
+ pre = 1
1786
+
1787
+ # the simplification coefficient is the number of
1788
+ # arguments by which contracting a given sequence
1789
+ # would reduce the word; e.g. in a*b*a*b*c*a*b*c,
1790
+ # contracting a*b*a*b to (a*b)**2 removes 3 arguments
1791
+ # while a*b*c*a*b*c to (a*b*c)**2 removes 6. It's
1792
+ # better to contract the latter so simplification
1793
+ # with a maximum simplification coefficient will be chosen
1794
+ max_simp_coeff = 0
1795
+ simp = None # information about future simplification
1796
+
1797
+ for i in range(1, len(args)):
1798
+ simp_coeff = 0
1799
+ l = 0 # length of a subterm
1800
+ p = 0 # the power of a subterm
1801
+ if i < len(args) - 1:
1802
+ rep = m[i][0]
1803
+ start = i # starting index of the repeated sequence
1804
+ end = i+1 # ending index of the repeated sequence
1805
+ if i == len(args)-1 or rep == [0]:
1806
+ # no subterm is repeated at this stage, at least as
1807
+ # far as the arguments are concerned - there may be
1808
+ # a repetition if powers are taken into account
1809
+ if (isinstance(args[i], _Pow) and
1810
+ not isinstance(args[i].args[0], _Symbol)):
1811
+ subterm = args[i].args[0].args
1812
+ l = len(subterm)
1813
+ if args[i-l:i] == subterm:
1814
+ # e.g. a*b in a*b*(a*b)**2 is not repeated
1815
+ # in args (= [a, b, (a*b)**2]) but it
1816
+ # can be matched here
1817
+ p += 1
1818
+ start -= l
1819
+ if args[i+1:i+1+l] == subterm:
1820
+ # e.g. a*b in (a*b)**2*a*b
1821
+ p += 1
1822
+ end += l
1823
+ if p:
1824
+ p += args[i].args[1]
1825
+ else:
1826
+ continue
1827
+ else:
1828
+ l = rep[0] # length of the longest repeated subterm at this point
1829
+ start -= l - 1
1830
+ subterm = args[start:end]
1831
+ p = 2
1832
+ end += l
1833
+
1834
+ if subterm in simps and simps[subterm] >= start:
1835
+ # the subterm is part of a sequence that
1836
+ # has already been considered
1837
+ continue
1838
+
1839
+ # count how many times it's repeated
1840
+ while end < len(args):
1841
+ if l in m[end-1][0]:
1842
+ p += 1
1843
+ end += l
1844
+ elif isinstance(args[end], _Pow) and args[end].args[0].args == subterm:
1845
+ # for cases like a*b*a*b*(a*b)**2*a*b
1846
+ p += args[end].args[1]
1847
+ end += 1
1848
+ else:
1849
+ break
1850
+
1851
+ # see if another match can be made, e.g.
1852
+ # for b*a**2 in b*a**2*b*a**3 or a*b in
1853
+ # a**2*b*a*b
1854
+
1855
+ pre_exp = 0
1856
+ pre_arg = 1
1857
+ if start - l >= 0 and args[start-l+1:start] == subterm[1:]:
1858
+ if isinstance(subterm[0], _Pow):
1859
+ pre_arg = subterm[0].args[0]
1860
+ exp = subterm[0].args[1]
1861
+ else:
1862
+ pre_arg = subterm[0]
1863
+ exp = 1
1864
+ if isinstance(args[start-l], _Pow) and args[start-l].args[0] == pre_arg:
1865
+ pre_exp = args[start-l].args[1] - exp
1866
+ start -= l
1867
+ p += 1
1868
+ elif args[start-l] == pre_arg:
1869
+ pre_exp = 1 - exp
1870
+ start -= l
1871
+ p += 1
1872
+
1873
+ post_exp = 0
1874
+ post_arg = 1
1875
+ if end + l - 1 < len(args) and args[end:end+l-1] == subterm[:-1]:
1876
+ if isinstance(subterm[-1], _Pow):
1877
+ post_arg = subterm[-1].args[0]
1878
+ exp = subterm[-1].args[1]
1879
+ else:
1880
+ post_arg = subterm[-1]
1881
+ exp = 1
1882
+ if isinstance(args[end+l-1], _Pow) and args[end+l-1].args[0] == post_arg:
1883
+ post_exp = args[end+l-1].args[1] - exp
1884
+ end += l
1885
+ p += 1
1886
+ elif args[end+l-1] == post_arg:
1887
+ post_exp = 1 - exp
1888
+ end += l
1889
+ p += 1
1890
+
1891
+ # Consider a*b*a**2*b*a**2*b*a:
1892
+ # b*a**2 is explicitly repeated, but note
1893
+ # that in this case a*b*a is also repeated
1894
+ # so there are two possible simplifications:
1895
+ # a*(b*a**2)**3*a**-1 or (a*b*a)**3
1896
+ # The latter is obviously simpler.
1897
+ # But in a*b*a**2*b**2*a**2 the simplifications are
1898
+ # a*(b*a**2)**2 and (a*b*a)**3*a in which case
1899
+ # it's better to stick with the shorter subterm
1900
+ if post_exp and exp % 2 == 0 and start > 0:
1901
+ exp = exp/2
1902
+ _pre_exp = 1
1903
+ _post_exp = 1
1904
+ if isinstance(args[start-1], _Pow) and args[start-1].args[0] == post_arg:
1905
+ _post_exp = post_exp + exp
1906
+ _pre_exp = args[start-1].args[1] - exp
1907
+ elif args[start-1] == post_arg:
1908
+ _post_exp = post_exp + exp
1909
+ _pre_exp = 1 - exp
1910
+ if _pre_exp == 0 or _post_exp == 0:
1911
+ if not pre_exp:
1912
+ start -= 1
1913
+ post_exp = _post_exp
1914
+ pre_exp = _pre_exp
1915
+ pre_arg = post_arg
1916
+ subterm = (post_arg**exp,) + subterm[:-1] + (post_arg**exp,)
1917
+
1918
+ simp_coeff += end-start
1919
+
1920
+ if post_exp:
1921
+ simp_coeff -= 1
1922
+ if pre_exp:
1923
+ simp_coeff -= 1
1924
+
1925
+ simps[subterm] = end
1926
+
1927
+ if simp_coeff > max_simp_coeff:
1928
+ max_simp_coeff = simp_coeff
1929
+ simp = (start, _Mul(*subterm), p, end, l)
1930
+ pre = pre_arg**pre_exp
1931
+ post = post_arg**post_exp
1932
+
1933
+ if simp:
1934
+ subterm = _Pow(nc_simplify(simp[1], deep=deep), simp[2])
1935
+ pre = nc_simplify(_Mul(*args[:simp[0]])*pre, deep=deep)
1936
+ post = post*nc_simplify(_Mul(*args[simp[3]:]), deep=deep)
1937
+ simp = pre*subterm*post
1938
+ if pre != 1 or post != 1:
1939
+ # new simplifications may be possible but no need
1940
+ # to recurse over arguments
1941
+ simp = nc_simplify(simp, deep=False)
1942
+ else:
1943
+ simp = _Mul(*args)
1944
+
1945
+ if invert:
1946
+ simp = _Pow(simp, -1)
1947
+
1948
+ # see if factor_nc(expr) is simplified better
1949
+ if not isinstance(expr, MatrixExpr):
1950
+ f_expr = factor_nc(expr)
1951
+ if f_expr != expr:
1952
+ alt_simp = nc_simplify(f_expr, deep=deep)
1953
+ simp = compare(simp, alt_simp)
1954
+ else:
1955
+ simp = simp.doit(inv_expand=False)
1956
+ return simp
1957
+
1958
+
1959
+ def dotprodsimp(expr, withsimp=False):
1960
+ """Simplification for a sum of products targeted at the kind of blowup that
1961
+ occurs during summation of products. Intended to reduce expression blowup
1962
+ during matrix multiplication or other similar operations. Only works with
1963
+ algebraic expressions and does not recurse into non.
1964
+
1965
+ Parameters
1966
+ ==========
1967
+
1968
+ withsimp : bool, optional
1969
+ Specifies whether a flag should be returned along with the expression
1970
+ to indicate roughly whether simplification was successful. It is used
1971
+ in ``MatrixArithmetic._eval_pow_by_recursion`` to avoid attempting to
1972
+ simplify an expression repetitively which does not simplify.
1973
+ """
1974
+
1975
+ def count_ops_alg(expr):
1976
+ """Optimized count algebraic operations with no recursion into
1977
+ non-algebraic args that ``core.function.count_ops`` does. Also returns
1978
+ whether rational functions may be present according to negative
1979
+ exponents of powers or non-number fractions.
1980
+
1981
+ Returns
1982
+ =======
1983
+
1984
+ ops, ratfunc : int, bool
1985
+ ``ops`` is the number of algebraic operations starting at the top
1986
+ level expression (not recursing into non-alg children). ``ratfunc``
1987
+ specifies whether the expression MAY contain rational functions
1988
+ which ``cancel`` MIGHT optimize.
1989
+ """
1990
+
1991
+ ops = 0
1992
+ args = [expr]
1993
+ ratfunc = False
1994
+
1995
+ while args:
1996
+ a = args.pop()
1997
+
1998
+ if not isinstance(a, Basic):
1999
+ continue
2000
+
2001
+ if a.is_Rational:
2002
+ if a is not S.One: # -1/3 = NEG + DIV
2003
+ ops += bool (a.p < 0) + bool (a.q != 1)
2004
+
2005
+ elif a.is_Mul:
2006
+ if a.could_extract_minus_sign():
2007
+ ops += 1
2008
+ if a.args[0] is S.NegativeOne:
2009
+ a = a.as_two_terms()[1]
2010
+ else:
2011
+ a = -a
2012
+
2013
+ n, d = fraction(a)
2014
+
2015
+ if n.is_Integer:
2016
+ ops += 1 + bool (n < 0)
2017
+ args.append(d) # won't be -Mul but could be Add
2018
+
2019
+ elif d is not S.One:
2020
+ if not d.is_Integer:
2021
+ args.append(d)
2022
+ ratfunc=True
2023
+
2024
+ ops += 1
2025
+ args.append(n) # could be -Mul
2026
+
2027
+ else:
2028
+ ops += len(a.args) - 1
2029
+ args.extend(a.args)
2030
+
2031
+ elif a.is_Add:
2032
+ laargs = len(a.args)
2033
+ negs = 0
2034
+
2035
+ for ai in a.args:
2036
+ if ai.could_extract_minus_sign():
2037
+ negs += 1
2038
+ ai = -ai
2039
+ args.append(ai)
2040
+
2041
+ ops += laargs - (negs != laargs) # -x - y = NEG + SUB
2042
+
2043
+ elif a.is_Pow:
2044
+ ops += 1
2045
+ args.append(a.base)
2046
+
2047
+ if not ratfunc:
2048
+ ratfunc = a.exp.is_negative is not False
2049
+
2050
+ return ops, ratfunc
2051
+
2052
+ def nonalg_subs_dummies(expr, dummies):
2053
+ """Substitute dummy variables for non-algebraic expressions to avoid
2054
+ evaluation of non-algebraic terms that ``polys.polytools.cancel`` does.
2055
+ """
2056
+
2057
+ if not expr.args:
2058
+ return expr
2059
+
2060
+ if expr.is_Add or expr.is_Mul or expr.is_Pow:
2061
+ args = None
2062
+
2063
+ for i, a in enumerate(expr.args):
2064
+ c = nonalg_subs_dummies(a, dummies)
2065
+
2066
+ if c is a:
2067
+ continue
2068
+
2069
+ if args is None:
2070
+ args = list(expr.args)
2071
+
2072
+ args[i] = c
2073
+
2074
+ if args is None:
2075
+ return expr
2076
+
2077
+ return expr.func(*args)
2078
+
2079
+ return dummies.setdefault(expr, Dummy())
2080
+
2081
+ simplified = False # doesn't really mean simplified, rather "can simplify again"
2082
+
2083
+ if isinstance(expr, Basic) and (expr.is_Add or expr.is_Mul or expr.is_Pow):
2084
+ expr2 = expr.expand(deep=True, modulus=None, power_base=False,
2085
+ power_exp=False, mul=True, log=False, multinomial=True, basic=False)
2086
+
2087
+ if expr2 != expr:
2088
+ expr = expr2
2089
+ simplified = True
2090
+
2091
+ exprops, ratfunc = count_ops_alg(expr)
2092
+
2093
+ if exprops >= 6: # empirically tested cutoff for expensive simplification
2094
+ if ratfunc:
2095
+ dummies = {}
2096
+ expr2 = nonalg_subs_dummies(expr, dummies)
2097
+
2098
+ if expr2 is expr or count_ops_alg(expr2)[0] >= 6: # check again after substitution
2099
+ expr3 = cancel(expr2)
2100
+
2101
+ if expr3 != expr2:
2102
+ expr = expr3.subs([(d, e) for e, d in dummies.items()])
2103
+ simplified = True
2104
+
2105
+ # very special case: x/(x-1) - 1/(x-1) -> 1
2106
+ elif (exprops == 5 and expr.is_Add and expr.args [0].is_Mul and
2107
+ expr.args [1].is_Mul and expr.args [0].args [-1].is_Pow and
2108
+ expr.args [1].args [-1].is_Pow and
2109
+ expr.args [0].args [-1].exp is S.NegativeOne and
2110
+ expr.args [1].args [-1].exp is S.NegativeOne):
2111
+
2112
+ expr2 = together (expr)
2113
+ expr2ops = count_ops_alg(expr2)[0]
2114
+
2115
+ if expr2ops < exprops:
2116
+ expr = expr2
2117
+ simplified = True
2118
+
2119
+ else:
2120
+ simplified = True
2121
+
2122
+ return (expr, simplified) if withsimp else expr
2123
+
2124
+
2125
+ bottom_up = deprecated(
2126
+ """
2127
+ Using bottom_up from the sympy.simplify.simplify submodule is
2128
+ deprecated.
2129
+
2130
+ Instead, use bottom_up from the top-level sympy namespace, like
2131
+
2132
+ sympy.bottom_up
2133
+ """,
2134
+ deprecated_since_version="1.10",
2135
+ active_deprecations_target="deprecated-traversal-functions-moved",
2136
+ )(_bottom_up)
2137
+
2138
+
2139
+ # XXX: This function really should either be private API or exported in the
2140
+ # top-level sympy/__init__.py
2141
+ walk = deprecated(
2142
+ """
2143
+ Using walk from the sympy.simplify.simplify submodule is
2144
+ deprecated.
2145
+
2146
+ Instead, use walk from sympy.core.traversal.walk
2147
+ """,
2148
+ deprecated_since_version="1.10",
2149
+ active_deprecations_target="deprecated-traversal-functions-moved",
2150
+ )(_walk)
lib/python3.12/site-packages/sympy/simplify/sqrtdenest.py ADDED
@@ -0,0 +1,678 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import Add, Expr, Mul, S, sympify
2
+ from sympy.core.function import _mexpand, count_ops, expand_mul
3
+ from sympy.core.sorting import default_sort_key
4
+ from sympy.core.symbol import Dummy
5
+ from sympy.functions import root, sign, sqrt
6
+ from sympy.polys import Poly, PolynomialError
7
+
8
+
9
+ def is_sqrt(expr):
10
+ """Return True if expr is a sqrt, otherwise False."""
11
+
12
+ return expr.is_Pow and expr.exp.is_Rational and abs(expr.exp) is S.Half
13
+
14
+
15
+ def sqrt_depth(p) -> int:
16
+ """Return the maximum depth of any square root argument of p.
17
+
18
+ >>> from sympy.functions.elementary.miscellaneous import sqrt
19
+ >>> from sympy.simplify.sqrtdenest import sqrt_depth
20
+
21
+ Neither of these square roots contains any other square roots
22
+ so the depth is 1:
23
+
24
+ >>> sqrt_depth(1 + sqrt(2)*(1 + sqrt(3)))
25
+ 1
26
+
27
+ The sqrt(3) is contained within a square root so the depth is
28
+ 2:
29
+
30
+ >>> sqrt_depth(1 + sqrt(2)*sqrt(1 + sqrt(3)))
31
+ 2
32
+ """
33
+ if p is S.ImaginaryUnit:
34
+ return 1
35
+ if p.is_Atom:
36
+ return 0
37
+ if p.is_Add or p.is_Mul:
38
+ return max(sqrt_depth(x) for x in p.args)
39
+ if is_sqrt(p):
40
+ return sqrt_depth(p.base) + 1
41
+ return 0
42
+
43
+
44
+ def is_algebraic(p):
45
+ """Return True if p is comprised of only Rationals or square roots
46
+ of Rationals and algebraic operations.
47
+
48
+ Examples
49
+ ========
50
+
51
+ >>> from sympy.functions.elementary.miscellaneous import sqrt
52
+ >>> from sympy.simplify.sqrtdenest import is_algebraic
53
+ >>> from sympy import cos
54
+ >>> is_algebraic(sqrt(2)*(3/(sqrt(7) + sqrt(5)*sqrt(2))))
55
+ True
56
+ >>> is_algebraic(sqrt(2)*(3/(sqrt(7) + sqrt(5)*cos(2))))
57
+ False
58
+ """
59
+
60
+ if p.is_Rational:
61
+ return True
62
+ elif p.is_Atom:
63
+ return False
64
+ elif is_sqrt(p) or p.is_Pow and p.exp.is_Integer:
65
+ return is_algebraic(p.base)
66
+ elif p.is_Add or p.is_Mul:
67
+ return all(is_algebraic(x) for x in p.args)
68
+ else:
69
+ return False
70
+
71
+
72
+ def _subsets(n):
73
+ """
74
+ Returns all possible subsets of the set (0, 1, ..., n-1) except the
75
+ empty set, listed in reversed lexicographical order according to binary
76
+ representation, so that the case of the fourth root is treated last.
77
+
78
+ Examples
79
+ ========
80
+
81
+ >>> from sympy.simplify.sqrtdenest import _subsets
82
+ >>> _subsets(2)
83
+ [[1, 0], [0, 1], [1, 1]]
84
+
85
+ """
86
+ if n == 1:
87
+ a = [[1]]
88
+ elif n == 2:
89
+ a = [[1, 0], [0, 1], [1, 1]]
90
+ elif n == 3:
91
+ a = [[1, 0, 0], [0, 1, 0], [1, 1, 0],
92
+ [0, 0, 1], [1, 0, 1], [0, 1, 1], [1, 1, 1]]
93
+ else:
94
+ b = _subsets(n - 1)
95
+ a0 = [x + [0] for x in b]
96
+ a1 = [x + [1] for x in b]
97
+ a = a0 + [[0]*(n - 1) + [1]] + a1
98
+ return a
99
+
100
+
101
+ def sqrtdenest(expr, max_iter=3):
102
+ """Denests sqrts in an expression that contain other square roots
103
+ if possible, otherwise returns the expr unchanged. This is based on the
104
+ algorithms of [1].
105
+
106
+ Examples
107
+ ========
108
+
109
+ >>> from sympy.simplify.sqrtdenest import sqrtdenest
110
+ >>> from sympy import sqrt
111
+ >>> sqrtdenest(sqrt(5 + 2 * sqrt(6)))
112
+ sqrt(2) + sqrt(3)
113
+
114
+ See Also
115
+ ========
116
+
117
+ sympy.solvers.solvers.unrad
118
+
119
+ References
120
+ ==========
121
+
122
+ .. [1] https://web.archive.org/web/20210806201615/https://researcher.watson.ibm.com/researcher/files/us-fagin/symb85.pdf
123
+
124
+ .. [2] D. J. Jeffrey and A. D. Rich, 'Symplifying Square Roots of Square Roots
125
+ by Denesting' (available at https://www.cybertester.com/data/denest.pdf)
126
+
127
+ """
128
+ expr = expand_mul(expr)
129
+ for i in range(max_iter):
130
+ z = _sqrtdenest0(expr)
131
+ if expr == z:
132
+ return expr
133
+ expr = z
134
+ return expr
135
+
136
+
137
+ def _sqrt_match(p):
138
+ """Return [a, b, r] for p.match(a + b*sqrt(r)) where, in addition to
139
+ matching, sqrt(r) also has then maximal sqrt_depth among addends of p.
140
+
141
+ Examples
142
+ ========
143
+
144
+ >>> from sympy.functions.elementary.miscellaneous import sqrt
145
+ >>> from sympy.simplify.sqrtdenest import _sqrt_match
146
+ >>> _sqrt_match(1 + sqrt(2) + sqrt(2)*sqrt(3) + 2*sqrt(1+sqrt(5)))
147
+ [1 + sqrt(2) + sqrt(6), 2, 1 + sqrt(5)]
148
+ """
149
+ from sympy.simplify.radsimp import split_surds
150
+
151
+ p = _mexpand(p)
152
+ if p.is_Number:
153
+ res = (p, S.Zero, S.Zero)
154
+ elif p.is_Add:
155
+ pargs = sorted(p.args, key=default_sort_key)
156
+ sqargs = [x**2 for x in pargs]
157
+ if all(sq.is_Rational and sq.is_positive for sq in sqargs):
158
+ r, b, a = split_surds(p)
159
+ res = a, b, r
160
+ return list(res)
161
+ # to make the process canonical, the argument is included in the tuple
162
+ # so when the max is selected, it will be the largest arg having a
163
+ # given depth
164
+ v = [(sqrt_depth(x), x, i) for i, x in enumerate(pargs)]
165
+ nmax = max(v, key=default_sort_key)
166
+ if nmax[0] == 0:
167
+ res = []
168
+ else:
169
+ # select r
170
+ depth, _, i = nmax
171
+ r = pargs.pop(i)
172
+ v.pop(i)
173
+ b = S.One
174
+ if r.is_Mul:
175
+ bv = []
176
+ rv = []
177
+ for x in r.args:
178
+ if sqrt_depth(x) < depth:
179
+ bv.append(x)
180
+ else:
181
+ rv.append(x)
182
+ b = Mul._from_args(bv)
183
+ r = Mul._from_args(rv)
184
+ # collect terms comtaining r
185
+ a1 = []
186
+ b1 = [b]
187
+ for x in v:
188
+ if x[0] < depth:
189
+ a1.append(x[1])
190
+ else:
191
+ x1 = x[1]
192
+ if x1 == r:
193
+ b1.append(1)
194
+ else:
195
+ if x1.is_Mul:
196
+ x1args = list(x1.args)
197
+ if r in x1args:
198
+ x1args.remove(r)
199
+ b1.append(Mul(*x1args))
200
+ else:
201
+ a1.append(x[1])
202
+ else:
203
+ a1.append(x[1])
204
+ a = Add(*a1)
205
+ b = Add(*b1)
206
+ res = (a, b, r**2)
207
+ else:
208
+ b, r = p.as_coeff_Mul()
209
+ if is_sqrt(r):
210
+ res = (S.Zero, b, r**2)
211
+ else:
212
+ res = []
213
+ return list(res)
214
+
215
+
216
+ class SqrtdenestStopIteration(StopIteration):
217
+ pass
218
+
219
+
220
+ def _sqrtdenest0(expr):
221
+ """Returns expr after denesting its arguments."""
222
+
223
+ if is_sqrt(expr):
224
+ n, d = expr.as_numer_denom()
225
+ if d is S.One: # n is a square root
226
+ if n.base.is_Add:
227
+ args = sorted(n.base.args, key=default_sort_key)
228
+ if len(args) > 2 and all((x**2).is_Integer for x in args):
229
+ try:
230
+ return _sqrtdenest_rec(n)
231
+ except SqrtdenestStopIteration:
232
+ pass
233
+ expr = sqrt(_mexpand(Add(*[_sqrtdenest0(x) for x in args])))
234
+ return _sqrtdenest1(expr)
235
+ else:
236
+ n, d = [_sqrtdenest0(i) for i in (n, d)]
237
+ return n/d
238
+
239
+ if isinstance(expr, Add):
240
+ cs = []
241
+ args = []
242
+ for arg in expr.args:
243
+ c, a = arg.as_coeff_Mul()
244
+ cs.append(c)
245
+ args.append(a)
246
+
247
+ if all(c.is_Rational for c in cs) and all(is_sqrt(arg) for arg in args):
248
+ return _sqrt_ratcomb(cs, args)
249
+
250
+ if isinstance(expr, Expr):
251
+ args = expr.args
252
+ if args:
253
+ return expr.func(*[_sqrtdenest0(a) for a in args])
254
+ return expr
255
+
256
+
257
+ def _sqrtdenest_rec(expr):
258
+ """Helper that denests the square root of three or more surds.
259
+
260
+ Explanation
261
+ ===========
262
+
263
+ It returns the denested expression; if it cannot be denested it
264
+ throws SqrtdenestStopIteration
265
+
266
+ Algorithm: expr.base is in the extension Q_m = Q(sqrt(r_1),..,sqrt(r_k));
267
+ split expr.base = a + b*sqrt(r_k), where `a` and `b` are on
268
+ Q_(m-1) = Q(sqrt(r_1),..,sqrt(r_(k-1))); then a**2 - b**2*r_k is
269
+ on Q_(m-1); denest sqrt(a**2 - b**2*r_k) and so on.
270
+ See [1], section 6.
271
+
272
+ Examples
273
+ ========
274
+
275
+ >>> from sympy import sqrt
276
+ >>> from sympy.simplify.sqrtdenest import _sqrtdenest_rec
277
+ >>> _sqrtdenest_rec(sqrt(-72*sqrt(2) + 158*sqrt(5) + 498))
278
+ -sqrt(10) + sqrt(2) + 9 + 9*sqrt(5)
279
+ >>> w=-6*sqrt(55)-6*sqrt(35)-2*sqrt(22)-2*sqrt(14)+2*sqrt(77)+6*sqrt(10)+65
280
+ >>> _sqrtdenest_rec(sqrt(w))
281
+ -sqrt(11) - sqrt(7) + sqrt(2) + 3*sqrt(5)
282
+ """
283
+ from sympy.simplify.radsimp import radsimp, rad_rationalize, split_surds
284
+ if not expr.is_Pow:
285
+ return sqrtdenest(expr)
286
+ if expr.base < 0:
287
+ return sqrt(-1)*_sqrtdenest_rec(sqrt(-expr.base))
288
+ g, a, b = split_surds(expr.base)
289
+ a = a*sqrt(g)
290
+ if a < b:
291
+ a, b = b, a
292
+ c2 = _mexpand(a**2 - b**2)
293
+ if len(c2.args) > 2:
294
+ g, a1, b1 = split_surds(c2)
295
+ a1 = a1*sqrt(g)
296
+ if a1 < b1:
297
+ a1, b1 = b1, a1
298
+ c2_1 = _mexpand(a1**2 - b1**2)
299
+ c_1 = _sqrtdenest_rec(sqrt(c2_1))
300
+ d_1 = _sqrtdenest_rec(sqrt(a1 + c_1))
301
+ num, den = rad_rationalize(b1, d_1)
302
+ c = _mexpand(d_1/sqrt(2) + num/(den*sqrt(2)))
303
+ else:
304
+ c = _sqrtdenest1(sqrt(c2))
305
+
306
+ if sqrt_depth(c) > 1:
307
+ raise SqrtdenestStopIteration
308
+ ac = a + c
309
+ if len(ac.args) >= len(expr.args):
310
+ if count_ops(ac) >= count_ops(expr.base):
311
+ raise SqrtdenestStopIteration
312
+ d = sqrtdenest(sqrt(ac))
313
+ if sqrt_depth(d) > 1:
314
+ raise SqrtdenestStopIteration
315
+ num, den = rad_rationalize(b, d)
316
+ r = d/sqrt(2) + num/(den*sqrt(2))
317
+ r = radsimp(r)
318
+ return _mexpand(r)
319
+
320
+
321
+ def _sqrtdenest1(expr, denester=True):
322
+ """Return denested expr after denesting with simpler methods or, that
323
+ failing, using the denester."""
324
+
325
+ from sympy.simplify.simplify import radsimp
326
+
327
+ if not is_sqrt(expr):
328
+ return expr
329
+
330
+ a = expr.base
331
+ if a.is_Atom:
332
+ return expr
333
+ val = _sqrt_match(a)
334
+ if not val:
335
+ return expr
336
+
337
+ a, b, r = val
338
+ # try a quick numeric denesting
339
+ d2 = _mexpand(a**2 - b**2*r)
340
+ if d2.is_Rational:
341
+ if d2.is_positive:
342
+ z = _sqrt_numeric_denest(a, b, r, d2)
343
+ if z is not None:
344
+ return z
345
+ else:
346
+ # fourth root case
347
+ # sqrtdenest(sqrt(3 + 2*sqrt(3))) =
348
+ # sqrt(2)*3**(1/4)/2 + sqrt(2)*3**(3/4)/2
349
+ dr2 = _mexpand(-d2*r)
350
+ dr = sqrt(dr2)
351
+ if dr.is_Rational:
352
+ z = _sqrt_numeric_denest(_mexpand(b*r), a, r, dr2)
353
+ if z is not None:
354
+ return z/root(r, 4)
355
+
356
+ else:
357
+ z = _sqrt_symbolic_denest(a, b, r)
358
+ if z is not None:
359
+ return z
360
+
361
+ if not denester or not is_algebraic(expr):
362
+ return expr
363
+
364
+ res = sqrt_biquadratic_denest(expr, a, b, r, d2)
365
+ if res:
366
+ return res
367
+
368
+ # now call to the denester
369
+ av0 = [a, b, r, d2]
370
+ z = _denester([radsimp(expr**2)], av0, 0, sqrt_depth(expr))[0]
371
+ if av0[1] is None:
372
+ return expr
373
+ if z is not None:
374
+ if sqrt_depth(z) == sqrt_depth(expr) and count_ops(z) > count_ops(expr):
375
+ return expr
376
+ return z
377
+ return expr
378
+
379
+
380
+ def _sqrt_symbolic_denest(a, b, r):
381
+ """Given an expression, sqrt(a + b*sqrt(b)), return the denested
382
+ expression or None.
383
+
384
+ Explanation
385
+ ===========
386
+
387
+ If r = ra + rb*sqrt(rr), try replacing sqrt(rr) in ``a`` with
388
+ (y**2 - ra)/rb, and if the result is a quadratic, ca*y**2 + cb*y + cc, and
389
+ (cb + b)**2 - 4*ca*cc is 0, then sqrt(a + b*sqrt(r)) can be rewritten as
390
+ sqrt(ca*(sqrt(r) + (cb + b)/(2*ca))**2).
391
+
392
+ Examples
393
+ ========
394
+
395
+ >>> from sympy.simplify.sqrtdenest import _sqrt_symbolic_denest, sqrtdenest
396
+ >>> from sympy import sqrt, Symbol
397
+ >>> from sympy.abc import x
398
+
399
+ >>> a, b, r = 16 - 2*sqrt(29), 2, -10*sqrt(29) + 55
400
+ >>> _sqrt_symbolic_denest(a, b, r)
401
+ sqrt(11 - 2*sqrt(29)) + sqrt(5)
402
+
403
+ If the expression is numeric, it will be simplified:
404
+
405
+ >>> w = sqrt(sqrt(sqrt(3) + 1) + 1) + 1 + sqrt(2)
406
+ >>> sqrtdenest(sqrt((w**2).expand()))
407
+ 1 + sqrt(2) + sqrt(1 + sqrt(1 + sqrt(3)))
408
+
409
+ Otherwise, it will only be simplified if assumptions allow:
410
+
411
+ >>> w = w.subs(sqrt(3), sqrt(x + 3))
412
+ >>> sqrtdenest(sqrt((w**2).expand()))
413
+ sqrt((sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2))**2)
414
+
415
+ Notice that the argument of the sqrt is a square. If x is made positive
416
+ then the sqrt of the square is resolved:
417
+
418
+ >>> _.subs(x, Symbol('x', positive=True))
419
+ sqrt(sqrt(sqrt(x + 3) + 1) + 1) + 1 + sqrt(2)
420
+ """
421
+
422
+ a, b, r = map(sympify, (a, b, r))
423
+ rval = _sqrt_match(r)
424
+ if not rval:
425
+ return None
426
+ ra, rb, rr = rval
427
+ if rb:
428
+ y = Dummy('y', positive=True)
429
+ try:
430
+ newa = Poly(a.subs(sqrt(rr), (y**2 - ra)/rb), y)
431
+ except PolynomialError:
432
+ return None
433
+ if newa.degree() == 2:
434
+ ca, cb, cc = newa.all_coeffs()
435
+ cb += b
436
+ if _mexpand(cb**2 - 4*ca*cc).equals(0):
437
+ z = sqrt(ca*(sqrt(r) + cb/(2*ca))**2)
438
+ if z.is_number:
439
+ z = _mexpand(Mul._from_args(z.as_content_primitive()))
440
+ return z
441
+
442
+
443
+ def _sqrt_numeric_denest(a, b, r, d2):
444
+ r"""Helper that denest
445
+ $\sqrt{a + b \sqrt{r}}, d^2 = a^2 - b^2 r > 0$
446
+
447
+ If it cannot be denested, it returns ``None``.
448
+ """
449
+ d = sqrt(d2)
450
+ s = a + d
451
+ # sqrt_depth(res) <= sqrt_depth(s) + 1
452
+ # sqrt_depth(expr) = sqrt_depth(r) + 2
453
+ # there is denesting if sqrt_depth(s) + 1 < sqrt_depth(r) + 2
454
+ # if s**2 is Number there is a fourth root
455
+ if sqrt_depth(s) < sqrt_depth(r) + 1 or (s**2).is_Rational:
456
+ s1, s2 = sign(s), sign(b)
457
+ if s1 == s2 == -1:
458
+ s1 = s2 = 1
459
+ res = (s1 * sqrt(a + d) + s2 * sqrt(a - d)) * sqrt(2) / 2
460
+ return res.expand()
461
+
462
+
463
+ def sqrt_biquadratic_denest(expr, a, b, r, d2):
464
+ """denest expr = sqrt(a + b*sqrt(r))
465
+ where a, b, r are linear combinations of square roots of
466
+ positive rationals on the rationals (SQRR) and r > 0, b != 0,
467
+ d2 = a**2 - b**2*r > 0
468
+
469
+ If it cannot denest it returns None.
470
+
471
+ Explanation
472
+ ===========
473
+
474
+ Search for a solution A of type SQRR of the biquadratic equation
475
+ 4*A**4 - 4*a*A**2 + b**2*r = 0 (1)
476
+ sqd = sqrt(a**2 - b**2*r)
477
+ Choosing the sqrt to be positive, the possible solutions are
478
+ A = sqrt(a/2 +/- sqd/2)
479
+ Since a, b, r are SQRR, then a**2 - b**2*r is a SQRR,
480
+ so if sqd can be denested, it is done by
481
+ _sqrtdenest_rec, and the result is a SQRR.
482
+ Similarly for A.
483
+ Examples of solutions (in both cases a and sqd are positive):
484
+
485
+ Example of expr with solution sqrt(a/2 + sqd/2) but not
486
+ solution sqrt(a/2 - sqd/2):
487
+ expr = sqrt(-sqrt(15) - sqrt(2)*sqrt(-sqrt(5) + 5) - sqrt(3) + 8)
488
+ a = -sqrt(15) - sqrt(3) + 8; sqd = -2*sqrt(5) - 2 + 4*sqrt(3)
489
+
490
+ Example of expr with solution sqrt(a/2 - sqd/2) but not
491
+ solution sqrt(a/2 + sqd/2):
492
+ w = 2 + r2 + r3 + (1 + r3)*sqrt(2 + r2 + 5*r3)
493
+ expr = sqrt((w**2).expand())
494
+ a = 4*sqrt(6) + 8*sqrt(2) + 47 + 28*sqrt(3)
495
+ sqd = 29 + 20*sqrt(3)
496
+
497
+ Define B = b/2*A; eq.(1) implies a = A**2 + B**2*r; then
498
+ expr**2 = a + b*sqrt(r) = (A + B*sqrt(r))**2
499
+
500
+ Examples
501
+ ========
502
+
503
+ >>> from sympy import sqrt
504
+ >>> from sympy.simplify.sqrtdenest import _sqrt_match, sqrt_biquadratic_denest
505
+ >>> z = sqrt((2*sqrt(2) + 4)*sqrt(2 + sqrt(2)) + 5*sqrt(2) + 8)
506
+ >>> a, b, r = _sqrt_match(z**2)
507
+ >>> d2 = a**2 - b**2*r
508
+ >>> sqrt_biquadratic_denest(z, a, b, r, d2)
509
+ sqrt(2) + sqrt(sqrt(2) + 2) + 2
510
+ """
511
+ from sympy.simplify.radsimp import radsimp, rad_rationalize
512
+ if r <= 0 or d2 < 0 or not b or sqrt_depth(expr.base) < 2:
513
+ return None
514
+ for x in (a, b, r):
515
+ for y in x.args:
516
+ y2 = y**2
517
+ if not y2.is_Integer or not y2.is_positive:
518
+ return None
519
+ sqd = _mexpand(sqrtdenest(sqrt(radsimp(d2))))
520
+ if sqrt_depth(sqd) > 1:
521
+ return None
522
+ x1, x2 = [a/2 + sqd/2, a/2 - sqd/2]
523
+ # look for a solution A with depth 1
524
+ for x in (x1, x2):
525
+ A = sqrtdenest(sqrt(x))
526
+ if sqrt_depth(A) > 1:
527
+ continue
528
+ Bn, Bd = rad_rationalize(b, _mexpand(2*A))
529
+ B = Bn/Bd
530
+ z = A + B*sqrt(r)
531
+ if z < 0:
532
+ z = -z
533
+ return _mexpand(z)
534
+ return None
535
+
536
+
537
+ def _denester(nested, av0, h, max_depth_level):
538
+ """Denests a list of expressions that contain nested square roots.
539
+
540
+ Explanation
541
+ ===========
542
+
543
+ Algorithm based on <http://www.almaden.ibm.com/cs/people/fagin/symb85.pdf>.
544
+
545
+ It is assumed that all of the elements of 'nested' share the same
546
+ bottom-level radicand. (This is stated in the paper, on page 177, in
547
+ the paragraph immediately preceding the algorithm.)
548
+
549
+ When evaluating all of the arguments in parallel, the bottom-level
550
+ radicand only needs to be denested once. This means that calling
551
+ _denester with x arguments results in a recursive invocation with x+1
552
+ arguments; hence _denester has polynomial complexity.
553
+
554
+ However, if the arguments were evaluated separately, each call would
555
+ result in two recursive invocations, and the algorithm would have
556
+ exponential complexity.
557
+
558
+ This is discussed in the paper in the middle paragraph of page 179.
559
+ """
560
+ from sympy.simplify.simplify import radsimp
561
+ if h > max_depth_level:
562
+ return None, None
563
+ if av0[1] is None:
564
+ return None, None
565
+ if (av0[0] is None and
566
+ all(n.is_Number for n in nested)): # no arguments are nested
567
+ for f in _subsets(len(nested)): # test subset 'f' of nested
568
+ p = _mexpand(Mul(*[nested[i] for i in range(len(f)) if f[i]]))
569
+ if f.count(1) > 1 and f[-1]:
570
+ p = -p
571
+ sqp = sqrt(p)
572
+ if sqp.is_Rational:
573
+ return sqp, f # got a perfect square so return its square root.
574
+ # Otherwise, return the radicand from the previous invocation.
575
+ return sqrt(nested[-1]), [0]*len(nested)
576
+ else:
577
+ R = None
578
+ if av0[0] is not None:
579
+ values = [av0[:2]]
580
+ R = av0[2]
581
+ nested2 = [av0[3], R]
582
+ av0[0] = None
583
+ else:
584
+ values = list(filter(None, [_sqrt_match(expr) for expr in nested]))
585
+ for v in values:
586
+ if v[2]: # Since if b=0, r is not defined
587
+ if R is not None:
588
+ if R != v[2]:
589
+ av0[1] = None
590
+ return None, None
591
+ else:
592
+ R = v[2]
593
+ if R is None:
594
+ # return the radicand from the previous invocation
595
+ return sqrt(nested[-1]), [0]*len(nested)
596
+ nested2 = [_mexpand(v[0]**2) -
597
+ _mexpand(R*v[1]**2) for v in values] + [R]
598
+ d, f = _denester(nested2, av0, h + 1, max_depth_level)
599
+ if not f:
600
+ return None, None
601
+ if not any(f[i] for i in range(len(nested))):
602
+ v = values[-1]
603
+ return sqrt(v[0] + _mexpand(v[1]*d)), f
604
+ else:
605
+ p = Mul(*[nested[i] for i in range(len(nested)) if f[i]])
606
+ v = _sqrt_match(p)
607
+ if 1 in f and f.index(1) < len(nested) - 1 and f[len(nested) - 1]:
608
+ v[0] = -v[0]
609
+ v[1] = -v[1]
610
+ if not f[len(nested)]: # Solution denests with square roots
611
+ vad = _mexpand(v[0] + d)
612
+ if vad <= 0:
613
+ # return the radicand from the previous invocation.
614
+ return sqrt(nested[-1]), [0]*len(nested)
615
+ if not(sqrt_depth(vad) <= sqrt_depth(R) + 1 or
616
+ (vad**2).is_Number):
617
+ av0[1] = None
618
+ return None, None
619
+
620
+ sqvad = _sqrtdenest1(sqrt(vad), denester=False)
621
+ if not (sqrt_depth(sqvad) <= sqrt_depth(R) + 1):
622
+ av0[1] = None
623
+ return None, None
624
+ sqvad1 = radsimp(1/sqvad)
625
+ res = _mexpand(sqvad/sqrt(2) + (v[1]*sqrt(R)*sqvad1/sqrt(2)))
626
+ return res, f
627
+
628
+ # sign(v[1])*sqrt(_mexpand(v[1]**2*R*vad1/2))), f
629
+ else: # Solution requires a fourth root
630
+ s2 = _mexpand(v[1]*R) + d
631
+ if s2 <= 0:
632
+ return sqrt(nested[-1]), [0]*len(nested)
633
+ FR, s = root(_mexpand(R), 4), sqrt(s2)
634
+ return _mexpand(s/(sqrt(2)*FR) + v[0]*FR/(sqrt(2)*s)), f
635
+
636
+
637
+ def _sqrt_ratcomb(cs, args):
638
+ """Denest rational combinations of radicals.
639
+
640
+ Based on section 5 of [1].
641
+
642
+ Examples
643
+ ========
644
+
645
+ >>> from sympy import sqrt
646
+ >>> from sympy.simplify.sqrtdenest import sqrtdenest
647
+ >>> z = sqrt(1+sqrt(3)) + sqrt(3+3*sqrt(3)) - sqrt(10+6*sqrt(3))
648
+ >>> sqrtdenest(z)
649
+ 0
650
+ """
651
+ from sympy.simplify.radsimp import radsimp
652
+
653
+ # check if there exists a pair of sqrt that can be denested
654
+ def find(a):
655
+ n = len(a)
656
+ for i in range(n - 1):
657
+ for j in range(i + 1, n):
658
+ s1 = a[i].base
659
+ s2 = a[j].base
660
+ p = _mexpand(s1 * s2)
661
+ s = sqrtdenest(sqrt(p))
662
+ if s != sqrt(p):
663
+ return s, i, j
664
+
665
+ indices = find(args)
666
+ if indices is None:
667
+ return Add(*[c * arg for c, arg in zip(cs, args)])
668
+
669
+ s, i1, i2 = indices
670
+
671
+ c2 = cs.pop(i2)
672
+ args.pop(i2)
673
+ a1 = args[i1]
674
+
675
+ # replace a2 by s/a1
676
+ cs[i1] += radsimp(c2 * s / a1.base)
677
+
678
+ return _sqrt_ratcomb(cs, args)
lib/python3.12/site-packages/sympy/simplify/tests/__init__.py ADDED
File without changes
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (199 Bytes). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_combsimp.cpython-312.pyc ADDED
Binary file (6.19 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_cse.cpython-312.pyc ADDED
Binary file (55.9 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_epathtools.cpython-312.pyc ADDED
Binary file (8.05 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_fu.cpython-312.pyc ADDED
Binary file (58.5 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_function.cpython-312.pyc ADDED
Binary file (4.73 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_gammasimp.cpython-312.pyc ADDED
Binary file (13.9 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_hyperexpand.cpython-312.pyc ADDED
Binary file (84.5 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_powsimp.cpython-312.pyc ADDED
Binary file (33.2 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_radsimp.cpython-312.pyc ADDED
Binary file (47.6 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_ratsimp.cpython-312.pyc ADDED
Binary file (6.2 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_rewrite.cpython-312.pyc ADDED
Binary file (3.29 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_simplify.cpython-312.pyc ADDED
Binary file (98.2 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_sqrtdenest.cpython-312.pyc ADDED
Binary file (19.6 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/__pycache__/test_trigsimp.cpython-312.pyc ADDED
Binary file (51.4 kB). View file
 
lib/python3.12/site-packages/sympy/simplify/tests/test_combsimp.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.numbers import Rational
2
+ from sympy.core.symbol import symbols
3
+ from sympy.functions.combinatorial.factorials import (FallingFactorial, RisingFactorial, binomial, factorial)
4
+ from sympy.functions.special.gamma_functions import gamma
5
+ from sympy.simplify.combsimp import combsimp
6
+ from sympy.abc import x
7
+
8
+
9
+ def test_combsimp():
10
+ k, m, n = symbols('k m n', integer = True)
11
+
12
+ assert combsimp(factorial(n)) == factorial(n)
13
+ assert combsimp(binomial(n, k)) == binomial(n, k)
14
+
15
+ assert combsimp(factorial(n)/factorial(n - 3)) == n*(-1 + n)*(-2 + n)
16
+ assert combsimp(binomial(n + 1, k + 1)/binomial(n, k)) == (1 + n)/(1 + k)
17
+
18
+ assert combsimp(binomial(3*n + 4, n + 1)/binomial(3*n + 1, n)) == \
19
+ Rational(3, 2)*((3*n + 2)*(3*n + 4)/((n + 1)*(2*n + 3)))
20
+
21
+ assert combsimp(factorial(n)**2/factorial(n - 3)) == \
22
+ factorial(n)*n*(-1 + n)*(-2 + n)
23
+ assert combsimp(factorial(n)*binomial(n + 1, k + 1)/binomial(n, k)) == \
24
+ factorial(n + 1)/(1 + k)
25
+
26
+ assert combsimp(gamma(n + 3)) == factorial(n + 2)
27
+
28
+ assert combsimp(factorial(x)) == gamma(x + 1)
29
+
30
+ # issue 9699
31
+ assert combsimp((n + 1)*factorial(n)) == factorial(n + 1)
32
+ assert combsimp(factorial(n)/n) == factorial(n-1)
33
+
34
+ # issue 6658
35
+ assert combsimp(binomial(n, n - k)) == binomial(n, k)
36
+
37
+ # issue 6341, 7135
38
+ assert combsimp(factorial(n)/(factorial(k)*factorial(n - k))) == \
39
+ binomial(n, k)
40
+ assert combsimp(factorial(k)*factorial(n - k)/factorial(n)) == \
41
+ 1/binomial(n, k)
42
+ assert combsimp(factorial(2*n)/factorial(n)**2) == binomial(2*n, n)
43
+ assert combsimp(factorial(2*n)*factorial(k)*factorial(n - k)/
44
+ factorial(n)**3) == binomial(2*n, n)/binomial(n, k)
45
+
46
+ assert combsimp(factorial(n*(1 + n) - n**2 - n)) == 1
47
+
48
+ assert combsimp(6*FallingFactorial(-4, n)/factorial(n)) == \
49
+ (-1)**n*(n + 1)*(n + 2)*(n + 3)
50
+ assert combsimp(6*FallingFactorial(-4, n - 1)/factorial(n - 1)) == \
51
+ (-1)**(n - 1)*n*(n + 1)*(n + 2)
52
+ assert combsimp(6*FallingFactorial(-4, n - 3)/factorial(n - 3)) == \
53
+ (-1)**(n - 3)*n*(n - 1)*(n - 2)
54
+ assert combsimp(6*FallingFactorial(-4, -n - 1)/factorial(-n - 1)) == \
55
+ -(-1)**(-n - 1)*n*(n - 1)*(n - 2)
56
+
57
+ assert combsimp(6*RisingFactorial(4, n)/factorial(n)) == \
58
+ (n + 1)*(n + 2)*(n + 3)
59
+ assert combsimp(6*RisingFactorial(4, n - 1)/factorial(n - 1)) == \
60
+ n*(n + 1)*(n + 2)
61
+ assert combsimp(6*RisingFactorial(4, n - 3)/factorial(n - 3)) == \
62
+ n*(n - 1)*(n - 2)
63
+ assert combsimp(6*RisingFactorial(4, -n - 1)/factorial(-n - 1)) == \
64
+ -n*(n - 1)*(n - 2)
65
+
66
+
67
+ def test_issue_6878():
68
+ n = symbols('n', integer=True)
69
+ assert combsimp(RisingFactorial(-10, n)) == 3628800*(-1)**n/factorial(10 - n)
70
+
71
+
72
+ def test_issue_14528():
73
+ p = symbols("p", integer=True, positive=True)
74
+ assert combsimp(binomial(1,p)) == 1/(factorial(p)*factorial(1-p))
75
+ assert combsimp(factorial(2-p)) == factorial(2-p)
lib/python3.12/site-packages/sympy/simplify/tests/test_cse.py ADDED
@@ -0,0 +1,758 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from functools import reduce
2
+ import itertools
3
+ from operator import add
4
+
5
+ from sympy.codegen.matrix_nodes import MatrixSolve
6
+ from sympy.core.add import Add
7
+ from sympy.core.containers import Tuple
8
+ from sympy.core.expr import UnevaluatedExpr
9
+ from sympy.core.function import Function
10
+ from sympy.core.mul import Mul
11
+ from sympy.core.power import Pow
12
+ from sympy.core.relational import Eq
13
+ from sympy.core.singleton import S
14
+ from sympy.core.symbol import (Symbol, symbols)
15
+ from sympy.core.sympify import sympify
16
+ from sympy.functions.elementary.exponential import exp
17
+ from sympy.functions.elementary.miscellaneous import sqrt
18
+ from sympy.functions.elementary.piecewise import Piecewise
19
+ from sympy.functions.elementary.trigonometric import (cos, sin)
20
+ from sympy.matrices.dense import Matrix
21
+ from sympy.matrices.expressions import Inverse, MatAdd, MatMul, Transpose
22
+ from sympy.polys.rootoftools import CRootOf
23
+ from sympy.series.order import O
24
+ from sympy.simplify.cse_main import cse
25
+ from sympy.simplify.simplify import signsimp
26
+ from sympy.tensor.indexed import (Idx, IndexedBase)
27
+
28
+ from sympy.core.function import count_ops
29
+ from sympy.simplify.cse_opts import sub_pre, sub_post
30
+ from sympy.functions.special.hyper import meijerg
31
+ from sympy.simplify import cse_main, cse_opts
32
+ from sympy.utilities.iterables import subsets
33
+ from sympy.testing.pytest import XFAIL, raises
34
+ from sympy.matrices import (MutableDenseMatrix, MutableSparseMatrix,
35
+ ImmutableDenseMatrix, ImmutableSparseMatrix)
36
+ from sympy.matrices.expressions import MatrixSymbol
37
+
38
+
39
+ w, x, y, z = symbols('w,x,y,z')
40
+ x0, x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12 = symbols('x:13')
41
+
42
+
43
+ def test_numbered_symbols():
44
+ ns = cse_main.numbered_symbols(prefix='y')
45
+ assert list(itertools.islice(
46
+ ns, 0, 10)) == [Symbol('y%s' % i) for i in range(0, 10)]
47
+ ns = cse_main.numbered_symbols(prefix='y')
48
+ assert list(itertools.islice(
49
+ ns, 10, 20)) == [Symbol('y%s' % i) for i in range(10, 20)]
50
+ ns = cse_main.numbered_symbols()
51
+ assert list(itertools.islice(
52
+ ns, 0, 10)) == [Symbol('x%s' % i) for i in range(0, 10)]
53
+
54
+ # Dummy "optimization" functions for testing.
55
+
56
+
57
+ def opt1(expr):
58
+ return expr + y
59
+
60
+
61
+ def opt2(expr):
62
+ return expr*z
63
+
64
+
65
+ def test_preprocess_for_cse():
66
+ assert cse_main.preprocess_for_cse(x, [(opt1, None)]) == x + y
67
+ assert cse_main.preprocess_for_cse(x, [(None, opt1)]) == x
68
+ assert cse_main.preprocess_for_cse(x, [(None, None)]) == x
69
+ assert cse_main.preprocess_for_cse(x, [(opt1, opt2)]) == x + y
70
+ assert cse_main.preprocess_for_cse(
71
+ x, [(opt1, None), (opt2, None)]) == (x + y)*z
72
+
73
+
74
+ def test_postprocess_for_cse():
75
+ assert cse_main.postprocess_for_cse(x, [(opt1, None)]) == x
76
+ assert cse_main.postprocess_for_cse(x, [(None, opt1)]) == x + y
77
+ assert cse_main.postprocess_for_cse(x, [(None, None)]) == x
78
+ assert cse_main.postprocess_for_cse(x, [(opt1, opt2)]) == x*z
79
+ # Note the reverse order of application.
80
+ assert cse_main.postprocess_for_cse(
81
+ x, [(None, opt1), (None, opt2)]) == x*z + y
82
+
83
+
84
+ def test_cse_single():
85
+ # Simple substitution.
86
+ e = Add(Pow(x + y, 2), sqrt(x + y))
87
+ substs, reduced = cse([e])
88
+ assert substs == [(x0, x + y)]
89
+ assert reduced == [sqrt(x0) + x0**2]
90
+
91
+ subst42, (red42,) = cse([42]) # issue_15082
92
+ assert len(subst42) == 0 and red42 == 42
93
+ subst_half, (red_half,) = cse([0.5])
94
+ assert len(subst_half) == 0 and red_half == 0.5
95
+
96
+
97
+ def test_cse_single2():
98
+ # Simple substitution, test for being able to pass the expression directly
99
+ e = Add(Pow(x + y, 2), sqrt(x + y))
100
+ substs, reduced = cse(e)
101
+ assert substs == [(x0, x + y)]
102
+ assert reduced == [sqrt(x0) + x0**2]
103
+ substs, reduced = cse(Matrix([[1]]))
104
+ assert isinstance(reduced[0], Matrix)
105
+
106
+ subst42, (red42,) = cse(42) # issue 15082
107
+ assert len(subst42) == 0 and red42 == 42
108
+ subst_half, (red_half,) = cse(0.5) # issue 15082
109
+ assert len(subst_half) == 0 and red_half == 0.5
110
+
111
+
112
+ def test_cse_not_possible():
113
+ # No substitution possible.
114
+ e = Add(x, y)
115
+ substs, reduced = cse([e])
116
+ assert substs == []
117
+ assert reduced == [x + y]
118
+ # issue 6329
119
+ eq = (meijerg((1, 2), (y, 4), (5,), [], x) +
120
+ meijerg((1, 3), (y, 4), (5,), [], x))
121
+ assert cse(eq) == ([], [eq])
122
+
123
+
124
+ def test_nested_substitution():
125
+ # Substitution within a substitution.
126
+ e = Add(Pow(w*x + y, 2), sqrt(w*x + y))
127
+ substs, reduced = cse([e])
128
+ assert substs == [(x0, w*x + y)]
129
+ assert reduced == [sqrt(x0) + x0**2]
130
+
131
+
132
+ def test_subtraction_opt():
133
+ # Make sure subtraction is optimized.
134
+ e = (x - y)*(z - y) + exp((x - y)*(z - y))
135
+ substs, reduced = cse(
136
+ [e], optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)])
137
+ assert substs == [(x0, (x - y)*(y - z))]
138
+ assert reduced == [-x0 + exp(-x0)]
139
+ e = -(x - y)*(z - y) + exp(-(x - y)*(z - y))
140
+ substs, reduced = cse(
141
+ [e], optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)])
142
+ assert substs == [(x0, (x - y)*(y - z))]
143
+ assert reduced == [x0 + exp(x0)]
144
+ # issue 4077
145
+ n = -1 + 1/x
146
+ e = n/x/(-n)**2 - 1/n/x
147
+ assert cse(e, optimizations=[(cse_opts.sub_pre, cse_opts.sub_post)]) == \
148
+ ([], [0])
149
+ assert cse(((w + x + y + z)*(w - y - z))/(w + x)**3) == \
150
+ ([(x0, w + x), (x1, y + z)], [(w - x1)*(x0 + x1)/x0**3])
151
+
152
+
153
+ def test_multiple_expressions():
154
+ e1 = (x + y)*z
155
+ e2 = (x + y)*w
156
+ substs, reduced = cse([e1, e2])
157
+ assert substs == [(x0, x + y)]
158
+ assert reduced == [x0*z, x0*w]
159
+ l = [w*x*y + z, w*y]
160
+ substs, reduced = cse(l)
161
+ rsubsts, _ = cse(reversed(l))
162
+ assert substs == rsubsts
163
+ assert reduced == [z + x*x0, x0]
164
+ l = [w*x*y, w*x*y + z, w*y]
165
+ substs, reduced = cse(l)
166
+ rsubsts, _ = cse(reversed(l))
167
+ assert substs == rsubsts
168
+ assert reduced == [x1, x1 + z, x0]
169
+ l = [(x - z)*(y - z), x - z, y - z]
170
+ substs, reduced = cse(l)
171
+ rsubsts, _ = cse(reversed(l))
172
+ assert substs == [(x0, -z), (x1, x + x0), (x2, x0 + y)]
173
+ assert rsubsts == [(x0, -z), (x1, x0 + y), (x2, x + x0)]
174
+ assert reduced == [x1*x2, x1, x2]
175
+ l = [w*y + w + x + y + z, w*x*y]
176
+ assert cse(l) == ([(x0, w*y)], [w + x + x0 + y + z, x*x0])
177
+ assert cse([x + y, x + y + z]) == ([(x0, x + y)], [x0, z + x0])
178
+ assert cse([x + y, x + z]) == ([], [x + y, x + z])
179
+ assert cse([x*y, z + x*y, x*y*z + 3]) == \
180
+ ([(x0, x*y)], [x0, z + x0, 3 + x0*z])
181
+
182
+
183
+ @XFAIL # CSE of non-commutative Mul terms is disabled
184
+ def test_non_commutative_cse():
185
+ A, B, C = symbols('A B C', commutative=False)
186
+ l = [A*B*C, A*C]
187
+ assert cse(l) == ([], l)
188
+ l = [A*B*C, A*B]
189
+ assert cse(l) == ([(x0, A*B)], [x0*C, x0])
190
+
191
+
192
+ # Test if CSE of non-commutative Mul terms is disabled
193
+ def test_bypass_non_commutatives():
194
+ A, B, C = symbols('A B C', commutative=False)
195
+ l = [A*B*C, A*C]
196
+ assert cse(l) == ([], l)
197
+ l = [A*B*C, A*B]
198
+ assert cse(l) == ([], l)
199
+ l = [B*C, A*B*C]
200
+ assert cse(l) == ([], l)
201
+
202
+
203
+ @XFAIL # CSE fails when replacing non-commutative sub-expressions
204
+ def test_non_commutative_order():
205
+ A, B, C = symbols('A B C', commutative=False)
206
+ x0 = symbols('x0', commutative=False)
207
+ l = [B+C, A*(B+C)]
208
+ assert cse(l) == ([(x0, B+C)], [x0, A*x0])
209
+
210
+
211
+ @XFAIL # Worked in gh-11232, but was reverted due to performance considerations
212
+ def test_issue_10228():
213
+ assert cse([x*y**2 + x*y]) == ([(x0, x*y)], [x0*y + x0])
214
+ assert cse([x + y, 2*x + y]) == ([(x0, x + y)], [x0, x + x0])
215
+ assert cse((w + 2*x + y + z, w + x + 1)) == (
216
+ [(x0, w + x)], [x0 + x + y + z, x0 + 1])
217
+ assert cse(((w + x + y + z)*(w - x))/(w + x)) == (
218
+ [(x0, w + x)], [(x0 + y + z)*(w - x)/x0])
219
+ a, b, c, d, f, g, j, m = symbols('a, b, c, d, f, g, j, m')
220
+ exprs = (d*g**2*j*m, 4*a*f*g*m, a*b*c*f**2)
221
+ assert cse(exprs) == (
222
+ [(x0, g*m), (x1, a*f)], [d*g*j*x0, 4*x0*x1, b*c*f*x1]
223
+ )
224
+
225
+ @XFAIL
226
+ def test_powers():
227
+ assert cse(x*y**2 + x*y) == ([(x0, x*y)], [x0*y + x0])
228
+
229
+
230
+ def test_issue_4498():
231
+ assert cse(w/(x - y) + z/(y - x), optimizations='basic') == \
232
+ ([], [(w - z)/(x - y)])
233
+
234
+
235
+ def test_issue_4020():
236
+ assert cse(x**5 + x**4 + x**3 + x**2, optimizations='basic') \
237
+ == ([(x0, x**2)], [x0*(x**3 + x + x0 + 1)])
238
+
239
+
240
+ def test_issue_4203():
241
+ assert cse(sin(x**x)/x**x) == ([(x0, x**x)], [sin(x0)/x0])
242
+
243
+
244
+ def test_issue_6263():
245
+ e = Eq(x*(-x + 1) + x*(x - 1), 0)
246
+ assert cse(e, optimizations='basic') == ([], [True])
247
+
248
+
249
+ def test_issue_25043():
250
+ c = symbols("c")
251
+ x = symbols("x0", real=True)
252
+ cse_expr = cse(c*x**2 + c*(x**4 - x**2))[-1][-1]
253
+ free = cse_expr.free_symbols
254
+ assert len(free) == len({i.name for i in free})
255
+
256
+
257
+ def test_dont_cse_tuples():
258
+ from sympy.core.function import Subs
259
+ f = Function("f")
260
+ g = Function("g")
261
+
262
+ name_val, (expr,) = cse(
263
+ Subs(f(x, y), (x, y), (0, 1))
264
+ + Subs(g(x, y), (x, y), (0, 1)))
265
+
266
+ assert name_val == []
267
+ assert expr == (Subs(f(x, y), (x, y), (0, 1))
268
+ + Subs(g(x, y), (x, y), (0, 1)))
269
+
270
+ name_val, (expr,) = cse(
271
+ Subs(f(x, y), (x, y), (0, x + y))
272
+ + Subs(g(x, y), (x, y), (0, x + y)))
273
+
274
+ assert name_val == [(x0, x + y)]
275
+ assert expr == Subs(f(x, y), (x, y), (0, x0)) + \
276
+ Subs(g(x, y), (x, y), (0, x0))
277
+
278
+
279
+ def test_pow_invpow():
280
+ assert cse(1/x**2 + x**2) == \
281
+ ([(x0, x**2)], [x0 + 1/x0])
282
+ assert cse(x**2 + (1 + 1/x**2)/x**2) == \
283
+ ([(x0, x**2), (x1, 1/x0)], [x0 + x1*(x1 + 1)])
284
+ assert cse(1/x**2 + (1 + 1/x**2)*x**2) == \
285
+ ([(x0, x**2), (x1, 1/x0)], [x0*(x1 + 1) + x1])
286
+ assert cse(cos(1/x**2) + sin(1/x**2)) == \
287
+ ([(x0, x**(-2))], [sin(x0) + cos(x0)])
288
+ assert cse(cos(x**2) + sin(x**2)) == \
289
+ ([(x0, x**2)], [sin(x0) + cos(x0)])
290
+ assert cse(y/(2 + x**2) + z/x**2/y) == \
291
+ ([(x0, x**2)], [y/(x0 + 2) + z/(x0*y)])
292
+ assert cse(exp(x**2) + x**2*cos(1/x**2)) == \
293
+ ([(x0, x**2)], [x0*cos(1/x0) + exp(x0)])
294
+ assert cse((1 + 1/x**2)/x**2) == \
295
+ ([(x0, x**(-2))], [x0*(x0 + 1)])
296
+ assert cse(x**(2*y) + x**(-2*y)) == \
297
+ ([(x0, x**(2*y))], [x0 + 1/x0])
298
+
299
+
300
+ def test_postprocess():
301
+ eq = (x + 1 + exp((x + 1)/(y + 1)) + cos(y + 1))
302
+ assert cse([eq, Eq(x, z + 1), z - 2, (z + 1)*(x + 1)],
303
+ postprocess=cse_main.cse_separate) == \
304
+ [[(x0, y + 1), (x2, z + 1), (x, x2), (x1, x + 1)],
305
+ [x1 + exp(x1/x0) + cos(x0), z - 2, x1*x2]]
306
+
307
+
308
+ def test_issue_4499():
309
+ # previously, this gave 16 constants
310
+ from sympy.abc import a, b
311
+ B = Function('B')
312
+ G = Function('G')
313
+ t = Tuple(*
314
+ (a, a + S.Half, 2*a, b, 2*a - b + 1, (sqrt(z)/2)**(-2*a + 1)*B(2*a -
315
+ b, sqrt(z))*B(b - 1, sqrt(z))*G(b)*G(2*a - b + 1),
316
+ sqrt(z)*(sqrt(z)/2)**(-2*a + 1)*B(b, sqrt(z))*B(2*a - b,
317
+ sqrt(z))*G(b)*G(2*a - b + 1), sqrt(z)*(sqrt(z)/2)**(-2*a + 1)*B(b - 1,
318
+ sqrt(z))*B(2*a - b + 1, sqrt(z))*G(b)*G(2*a - b + 1),
319
+ (sqrt(z)/2)**(-2*a + 1)*B(b, sqrt(z))*B(2*a - b + 1,
320
+ sqrt(z))*G(b)*G(2*a - b + 1), 1, 0, S.Half, z/2, -b + 1, -2*a + b,
321
+ -2*a))
322
+ c = cse(t)
323
+ ans = (
324
+ [(x0, 2*a), (x1, -b + x0), (x2, x1 + 1), (x3, b - 1), (x4, sqrt(z)),
325
+ (x5, B(x3, x4)), (x6, (x4/2)**(1 - x0)*G(b)*G(x2)), (x7, x6*B(x1, x4)),
326
+ (x8, B(b, x4)), (x9, x6*B(x2, x4))],
327
+ [(a, a + S.Half, x0, b, x2, x5*x7, x4*x7*x8, x4*x5*x9, x8*x9,
328
+ 1, 0, S.Half, z/2, -x3, -x1, -x0)])
329
+ assert ans == c
330
+
331
+
332
+ def test_issue_6169():
333
+ r = CRootOf(x**6 - 4*x**5 - 2, 1)
334
+ assert cse(r) == ([], [r])
335
+ # and a check that the right thing is done with the new
336
+ # mechanism
337
+ assert sub_post(sub_pre((-x - y)*z - x - y)) == -z*(x + y) - x - y
338
+
339
+
340
+ def test_cse_Indexed():
341
+ len_y = 5
342
+ y = IndexedBase('y', shape=(len_y,))
343
+ x = IndexedBase('x', shape=(len_y,))
344
+ i = Idx('i', len_y-1)
345
+
346
+ expr1 = (y[i+1]-y[i])/(x[i+1]-x[i])
347
+ expr2 = 1/(x[i+1]-x[i])
348
+ replacements, reduced_exprs = cse([expr1, expr2])
349
+ assert len(replacements) > 0
350
+
351
+
352
+ def test_cse_MatrixSymbol():
353
+ # MatrixSymbols have non-Basic args, so make sure that works
354
+ A = MatrixSymbol("A", 3, 3)
355
+ assert cse(A) == ([], [A])
356
+
357
+ n = symbols('n', integer=True)
358
+ B = MatrixSymbol("B", n, n)
359
+ assert cse(B) == ([], [B])
360
+
361
+ assert cse(A[0] * A[0]) == ([], [A[0]*A[0]])
362
+
363
+ assert cse(A[0,0]*A[0,1] + A[0,0]*A[0,1]*A[0,2]) == ([(x0, A[0, 0]*A[0, 1])], [x0*A[0, 2] + x0])
364
+
365
+ def test_cse_MatrixExpr():
366
+ A = MatrixSymbol('A', 3, 3)
367
+ y = MatrixSymbol('y', 3, 1)
368
+
369
+ expr1 = (A.T*A).I * A * y
370
+ expr2 = (A.T*A) * A * y
371
+ replacements, reduced_exprs = cse([expr1, expr2])
372
+ assert len(replacements) > 0
373
+
374
+ replacements, reduced_exprs = cse([expr1 + expr2, expr1])
375
+ assert replacements
376
+
377
+ replacements, reduced_exprs = cse([A**2, A + A**2])
378
+ assert replacements
379
+
380
+
381
+ def test_Piecewise():
382
+ f = Piecewise((-z + x*y, Eq(y, 0)), (-z - x*y, True))
383
+ ans = cse(f)
384
+ actual_ans = ([(x0, x*y)],
385
+ [Piecewise((x0 - z, Eq(y, 0)), (-z - x0, True))])
386
+ assert ans == actual_ans
387
+
388
+
389
+ def test_ignore_order_terms():
390
+ eq = exp(x).series(x,0,3) + sin(y+x**3) - 1
391
+ assert cse(eq) == ([], [sin(x**3 + y) + x + x**2/2 + O(x**3)])
392
+
393
+
394
+ def test_name_conflict():
395
+ z1 = x0 + y
396
+ z2 = x2 + x3
397
+ l = [cos(z1) + z1, cos(z2) + z2, x0 + x2]
398
+ substs, reduced = cse(l)
399
+ assert [e.subs(reversed(substs)) for e in reduced] == l
400
+
401
+
402
+ def test_name_conflict_cust_symbols():
403
+ z1 = x0 + y
404
+ z2 = x2 + x3
405
+ l = [cos(z1) + z1, cos(z2) + z2, x0 + x2]
406
+ substs, reduced = cse(l, symbols("x:10"))
407
+ assert [e.subs(reversed(substs)) for e in reduced] == l
408
+
409
+
410
+ def test_symbols_exhausted_error():
411
+ l = cos(x+y)+x+y+cos(w+y)+sin(w+y)
412
+ sym = [x, y, z]
413
+ with raises(ValueError):
414
+ cse(l, symbols=sym)
415
+
416
+
417
+ def test_issue_7840():
418
+ # daveknippers' example
419
+ C393 = sympify( \
420
+ 'Piecewise((C391 - 1.65, C390 < 0.5), (Piecewise((C391 - 1.65, \
421
+ C391 > 2.35), (C392, True)), True))'
422
+ )
423
+ C391 = sympify( \
424
+ 'Piecewise((2.05*C390**(-1.03), C390 < 0.5), (2.5*C390**(-0.625), True))'
425
+ )
426
+ C393 = C393.subs('C391',C391)
427
+ # simple substitution
428
+ sub = {}
429
+ sub['C390'] = 0.703451854
430
+ sub['C392'] = 1.01417794
431
+ ss_answer = C393.subs(sub)
432
+ # cse
433
+ substitutions,new_eqn = cse(C393)
434
+ for pair in substitutions:
435
+ sub[pair[0].name] = pair[1].subs(sub)
436
+ cse_answer = new_eqn[0].subs(sub)
437
+ # both methods should be the same
438
+ assert ss_answer == cse_answer
439
+
440
+ # GitRay's example
441
+ expr = sympify(
442
+ "Piecewise((Symbol('ON'), Equality(Symbol('mode'), Symbol('ON'))), \
443
+ (Piecewise((Piecewise((Symbol('OFF'), StrictLessThan(Symbol('x'), \
444
+ Symbol('threshold'))), (Symbol('ON'), true)), Equality(Symbol('mode'), \
445
+ Symbol('AUTO'))), (Symbol('OFF'), true)), true))"
446
+ )
447
+ substitutions, new_eqn = cse(expr)
448
+ # this Piecewise should be exactly the same
449
+ assert new_eqn[0] == expr
450
+ # there should not be any replacements
451
+ assert len(substitutions) < 1
452
+
453
+
454
+ def test_issue_8891():
455
+ for cls in (MutableDenseMatrix, MutableSparseMatrix,
456
+ ImmutableDenseMatrix, ImmutableSparseMatrix):
457
+ m = cls(2, 2, [x + y, 0, 0, 0])
458
+ res = cse([x + y, m])
459
+ ans = ([(x0, x + y)], [x0, cls([[x0, 0], [0, 0]])])
460
+ assert res == ans
461
+ assert isinstance(res[1][-1], cls)
462
+
463
+
464
+ def test_issue_11230():
465
+ # a specific test that always failed
466
+ a, b, f, k, l, i = symbols('a b f k l i')
467
+ p = [a*b*f*k*l, a*i*k**2*l, f*i*k**2*l]
468
+ R, C = cse(p)
469
+ assert not any(i.is_Mul for a in C for i in a.args)
470
+
471
+ # random tests for the issue
472
+ from sympy.core.random import choice
473
+ from sympy.core.function import expand_mul
474
+ s = symbols('a:m')
475
+ # 35 Mul tests, none of which should ever fail
476
+ ex = [Mul(*[choice(s) for i in range(5)]) for i in range(7)]
477
+ for p in subsets(ex, 3):
478
+ p = list(p)
479
+ R, C = cse(p)
480
+ assert not any(i.is_Mul for a in C for i in a.args)
481
+ for ri in reversed(R):
482
+ for i in range(len(C)):
483
+ C[i] = C[i].subs(*ri)
484
+ assert p == C
485
+ # 35 Add tests, none of which should ever fail
486
+ ex = [Add(*[choice(s[:7]) for i in range(5)]) for i in range(7)]
487
+ for p in subsets(ex, 3):
488
+ p = list(p)
489
+ R, C = cse(p)
490
+ assert not any(i.is_Add for a in C for i in a.args)
491
+ for ri in reversed(R):
492
+ for i in range(len(C)):
493
+ C[i] = C[i].subs(*ri)
494
+ # use expand_mul to handle cases like this:
495
+ # p = [a + 2*b + 2*e, 2*b + c + 2*e, b + 2*c + 2*g]
496
+ # x0 = 2*(b + e) is identified giving a rebuilt p that
497
+ # is now `[a + 2*(b + e), c + 2*(b + e), b + 2*c + 2*g]`
498
+ assert p == [expand_mul(i) for i in C]
499
+
500
+
501
+ @XFAIL
502
+ def test_issue_11577():
503
+ def check(eq):
504
+ r, c = cse(eq)
505
+ assert eq.count_ops() >= \
506
+ len(r) + sum(i[1].count_ops() for i in r) + \
507
+ count_ops(c)
508
+
509
+ eq = x**5*y**2 + x**5*y + x**5
510
+ assert cse(eq) == (
511
+ [(x0, x**4), (x1, x*y)], [x**5 + x0*x1*y + x0*x1])
512
+ # ([(x0, x**5*y)], [x0*y + x0 + x**5]) or
513
+ # ([(x0, x**5)], [x0*y**2 + x0*y + x0])
514
+ check(eq)
515
+
516
+ eq = x**2/(y + 1)**2 + x/(y + 1)
517
+ assert cse(eq) == (
518
+ [(x0, y + 1)], [x**2/x0**2 + x/x0])
519
+ # ([(x0, x/(y + 1))], [x0**2 + x0])
520
+ check(eq)
521
+
522
+
523
+ def test_hollow_rejection():
524
+ eq = [x + 3, x + 4]
525
+ assert cse(eq) == ([], eq)
526
+
527
+
528
+ def test_cse_ignore():
529
+ exprs = [exp(y)*(3*y + 3*sqrt(x+1)), exp(y)*(5*y + 5*sqrt(x+1))]
530
+ subst1, red1 = cse(exprs)
531
+ assert any(y in sub.free_symbols for _, sub in subst1), "cse failed to identify any term with y"
532
+
533
+ subst2, red2 = cse(exprs, ignore=(y,)) # y is not allowed in substitutions
534
+ assert not any(y in sub.free_symbols for _, sub in subst2), "Sub-expressions containing y must be ignored"
535
+ assert any(sub - sqrt(x + 1) == 0 for _, sub in subst2), "cse failed to identify sqrt(x + 1) as sub-expression"
536
+
537
+ def test_cse_ignore_issue_15002():
538
+ l = [
539
+ w*exp(x)*exp(-z),
540
+ exp(y)*exp(x)*exp(-z)
541
+ ]
542
+ substs, reduced = cse(l, ignore=(x,))
543
+ rl = [e.subs(reversed(substs)) for e in reduced]
544
+ assert rl == l
545
+
546
+
547
+ def test_cse_unevaluated():
548
+ xp1 = UnevaluatedExpr(x + 1)
549
+ # This used to cause RecursionError
550
+ [(x0, ue)], [red] = cse([(-1 - xp1) / (1 - xp1)])
551
+ if ue == xp1:
552
+ assert red == (-1 - x0) / (1 - x0)
553
+ elif ue == -xp1:
554
+ assert red == (-1 + x0) / (1 + x0)
555
+ else:
556
+ msg = f'Expected common subexpression {xp1} or {-xp1}, instead got {ue}'
557
+ assert False, msg
558
+
559
+
560
+ def test_cse__performance():
561
+ nexprs, nterms = 3, 20
562
+ x = symbols('x:%d' % nterms)
563
+ exprs = [
564
+ reduce(add, [x[j]*(-1)**(i+j) for j in range(nterms)])
565
+ for i in range(nexprs)
566
+ ]
567
+ assert (exprs[0] + exprs[1]).simplify() == 0
568
+ subst, red = cse(exprs)
569
+ assert len(subst) > 0, "exprs[0] == -exprs[2], i.e. a CSE"
570
+ for i, e in enumerate(red):
571
+ assert (e.subs(reversed(subst)) - exprs[i]).simplify() == 0
572
+
573
+
574
+ def test_issue_12070():
575
+ exprs = [x + y, 2 + x + y, x + y + z, 3 + x + y + z]
576
+ subst, red = cse(exprs)
577
+ assert 6 >= (len(subst) + sum(v.count_ops() for k, v in subst) +
578
+ count_ops(red))
579
+
580
+
581
+ def test_issue_13000():
582
+ eq = x/(-4*x**2 + y**2)
583
+ cse_eq = cse(eq)[1][0]
584
+ assert cse_eq == eq
585
+
586
+
587
+ def test_issue_18203():
588
+ eq = CRootOf(x**5 + 11*x - 2, 0) + CRootOf(x**5 + 11*x - 2, 1)
589
+ assert cse(eq) == ([], [eq])
590
+
591
+
592
+ def test_unevaluated_mul():
593
+ eq = Mul(x + y, x + y, evaluate=False)
594
+ assert cse(eq) == ([(x0, x + y)], [x0**2])
595
+
596
+ def test_cse_release_variables():
597
+ from sympy.simplify.cse_main import cse_release_variables
598
+ _0, _1, _2, _3, _4 = symbols('_:5')
599
+ eqs = [(x + y - 1)**2, x,
600
+ x + y, (x + y)/(2*x + 1) + (x + y - 1)**2,
601
+ (2*x + 1)**(x + y)]
602
+ r, e = cse(eqs, postprocess=cse_release_variables)
603
+ # this can change in keeping with the intention of the function
604
+ assert r, e == ([
605
+ (x0, x + y), (x1, (x0 - 1)**2), (x2, 2*x + 1),
606
+ (_3, x0/x2 + x1), (_4, x2**x0), (x2, None), (_0, x1),
607
+ (x1, None), (_2, x0), (x0, None), (_1, x)], (_0, _1, _2, _3, _4))
608
+ r.reverse()
609
+ r = [(s, v) for s, v in r if v is not None]
610
+ assert eqs == [i.subs(r) for i in e]
611
+
612
+ def test_cse_list():
613
+ _cse = lambda x: cse(x, list=False)
614
+ assert _cse(x) == ([], x)
615
+ assert _cse('x') == ([], 'x')
616
+ it = [x]
617
+ for c in (list, tuple, set):
618
+ assert _cse(c(it)) == ([], c(it))
619
+ #Tuple works different from tuple:
620
+ assert _cse(Tuple(*it)) == ([], Tuple(*it))
621
+ d = {x: 1}
622
+ assert _cse(d) == ([], d)
623
+
624
+ def test_issue_18991():
625
+ A = MatrixSymbol('A', 2, 2)
626
+ assert signsimp(-A * A - A) == -A * A - A
627
+
628
+
629
+ def test_unevaluated_Mul():
630
+ m = [Mul(1, 2, evaluate=False)]
631
+ assert cse(m) == ([], m)
632
+
633
+
634
+ def test_cse_matrix_expression_inverse():
635
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
636
+ x = Inverse(A)
637
+ cse_expr = cse(x)
638
+ assert cse_expr == ([], [Inverse(A)])
639
+
640
+
641
+ def test_cse_matrix_expression_matmul_inverse():
642
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
643
+ b = ImmutableDenseMatrix(symbols('b:2'))
644
+ x = MatMul(Inverse(A), b)
645
+ cse_expr = cse(x)
646
+ assert cse_expr == ([], [x])
647
+
648
+
649
+ def test_cse_matrix_negate_matrix():
650
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
651
+ x = MatMul(S.NegativeOne, A)
652
+ cse_expr = cse(x)
653
+ assert cse_expr == ([], [x])
654
+
655
+
656
+ def test_cse_matrix_negate_matmul_not_extracted():
657
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
658
+ B = ImmutableDenseMatrix(symbols('B:4')).reshape(2, 2)
659
+ x = MatMul(S.NegativeOne, A, B)
660
+ cse_expr = cse(x)
661
+ assert cse_expr == ([], [x])
662
+
663
+
664
+ @XFAIL # No simplification rule for nested associative operations
665
+ def test_cse_matrix_nested_matmul_collapsed():
666
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
667
+ B = ImmutableDenseMatrix(symbols('B:4')).reshape(2, 2)
668
+ x = MatMul(S.NegativeOne, MatMul(A, B))
669
+ cse_expr = cse(x)
670
+ assert cse_expr == ([], [MatMul(S.NegativeOne, A, B)])
671
+
672
+
673
+ def test_cse_matrix_optimize_out_single_argument_mul():
674
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
675
+ x = MatMul(MatMul(MatMul(A)))
676
+ cse_expr = cse(x)
677
+ assert cse_expr == ([], [A])
678
+
679
+
680
+ @XFAIL # Multiple simplification passed not supported in CSE
681
+ def test_cse_matrix_optimize_out_single_argument_mul_combined():
682
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
683
+ x = MatAdd(MatMul(MatMul(MatMul(A))), MatMul(MatMul(A)), MatMul(A), A)
684
+ cse_expr = cse(x)
685
+ assert cse_expr == ([], [MatMul(4, A)])
686
+
687
+
688
+ def test_cse_matrix_optimize_out_single_argument_add():
689
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
690
+ x = MatAdd(MatAdd(MatAdd(MatAdd(A))))
691
+ cse_expr = cse(x)
692
+ assert cse_expr == ([], [A])
693
+
694
+
695
+ @XFAIL # Multiple simplification passed not supported in CSE
696
+ def test_cse_matrix_optimize_out_single_argument_add_combined():
697
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
698
+ x = MatMul(MatAdd(MatAdd(MatAdd(A))), MatAdd(MatAdd(A)), MatAdd(A), A)
699
+ cse_expr = cse(x)
700
+ assert cse_expr == ([], [MatMul(4, A)])
701
+
702
+
703
+ def test_cse_matrix_expression_matrix_solve():
704
+ A = ImmutableDenseMatrix(symbols('A:4')).reshape(2, 2)
705
+ b = ImmutableDenseMatrix(symbols('b:2'))
706
+ x = MatrixSolve(A, b)
707
+ cse_expr = cse(x)
708
+ assert cse_expr == ([], [x])
709
+
710
+
711
+ def test_cse_matrix_matrix_expression():
712
+ X = ImmutableDenseMatrix(symbols('X:4')).reshape(2, 2)
713
+ y = ImmutableDenseMatrix(symbols('y:2'))
714
+ b = MatMul(Inverse(MatMul(Transpose(X), X)), Transpose(X), y)
715
+ cse_expr = cse(b)
716
+ x0 = MatrixSymbol('x0', 2, 2)
717
+ reduced_expr_expected = MatMul(Inverse(MatMul(x0, X)), x0, y)
718
+ assert cse_expr == ([(x0, Transpose(X))], [reduced_expr_expected])
719
+
720
+
721
+ def test_cse_matrix_kalman_filter():
722
+ """Kalman Filter example from Matthew Rocklin's SciPy 2013 talk.
723
+
724
+ Talk titled: "Matrix Expressions and BLAS/LAPACK; SciPy 2013 Presentation"
725
+
726
+ Video: https://pyvideo.org/scipy-2013/matrix-expressions-and-blaslapack-scipy-2013-pr.html
727
+
728
+ Notes
729
+ =====
730
+
731
+ Equations are:
732
+
733
+ new_mu = mu + Sigma*H.T * (R + H*Sigma*H.T).I * (H*mu - data)
734
+ = MatAdd(mu, MatMul(Sigma, Transpose(H), Inverse(MatAdd(R, MatMul(H, Sigma, Transpose(H)))), MatAdd(MatMul(H, mu), MatMul(S.NegativeOne, data))))
735
+ new_Sigma = Sigma - Sigma*H.T * (R + H*Sigma*H.T).I * H * Sigma
736
+ = MatAdd(Sigma, MatMul(S.NegativeOne, Sigma, Transpose(H)), Inverse(MatAdd(R, MatMul(H*Sigma*Transpose(H)))), H, Sigma))
737
+
738
+ """
739
+ N = 2
740
+ mu = ImmutableDenseMatrix(symbols(f'mu:{N}'))
741
+ Sigma = ImmutableDenseMatrix(symbols(f'Sigma:{N * N}')).reshape(N, N)
742
+ H = ImmutableDenseMatrix(symbols(f'H:{N * N}')).reshape(N, N)
743
+ R = ImmutableDenseMatrix(symbols(f'R:{N * N}')).reshape(N, N)
744
+ data = ImmutableDenseMatrix(symbols(f'data:{N}'))
745
+ new_mu = MatAdd(mu, MatMul(Sigma, Transpose(H), Inverse(MatAdd(R, MatMul(H, Sigma, Transpose(H)))), MatAdd(MatMul(H, mu), MatMul(S.NegativeOne, data))))
746
+ new_Sigma = MatAdd(Sigma, MatMul(S.NegativeOne, Sigma, Transpose(H), Inverse(MatAdd(R, MatMul(H, Sigma, Transpose(H)))), H, Sigma))
747
+ cse_expr = cse([new_mu, new_Sigma])
748
+ x0 = MatrixSymbol('x0', N, N)
749
+ x1 = MatrixSymbol('x1', N, N)
750
+ replacements_expected = [
751
+ (x0, Transpose(H)),
752
+ (x1, Inverse(MatAdd(R, MatMul(H, Sigma, x0)))),
753
+ ]
754
+ reduced_exprs_expected = [
755
+ MatAdd(mu, MatMul(Sigma, x0, x1, MatAdd(MatMul(H, mu), MatMul(S.NegativeOne, data)))),
756
+ MatAdd(Sigma, MatMul(S.NegativeOne, Sigma, x0, x1, H, Sigma)),
757
+ ]
758
+ assert cse_expr == (replacements_expected, reduced_exprs_expected)
lib/python3.12/site-packages/sympy/simplify/tests/test_epathtools.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Tests for tools for manipulation of expressions using paths. """
2
+
3
+ from sympy.simplify.epathtools import epath, EPath
4
+ from sympy.testing.pytest import raises
5
+
6
+ from sympy.core.numbers import E
7
+ from sympy.functions.elementary.trigonometric import (cos, sin)
8
+ from sympy.abc import x, y, z, t
9
+
10
+
11
+ def test_epath_select():
12
+ expr = [((x, 1, t), 2), ((3, y, 4), z)]
13
+
14
+ assert epath("/*", expr) == [((x, 1, t), 2), ((3, y, 4), z)]
15
+ assert epath("/*/*", expr) == [(x, 1, t), 2, (3, y, 4), z]
16
+ assert epath("/*/*/*", expr) == [x, 1, t, 3, y, 4]
17
+ assert epath("/*/*/*/*", expr) == []
18
+
19
+ assert epath("/[:]", expr) == [((x, 1, t), 2), ((3, y, 4), z)]
20
+ assert epath("/[:]/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z]
21
+ assert epath("/[:]/[:]/[:]", expr) == [x, 1, t, 3, y, 4]
22
+ assert epath("/[:]/[:]/[:]/[:]", expr) == []
23
+
24
+ assert epath("/*/[:]", expr) == [(x, 1, t), 2, (3, y, 4), z]
25
+
26
+ assert epath("/*/[0]", expr) == [(x, 1, t), (3, y, 4)]
27
+ assert epath("/*/[1]", expr) == [2, z]
28
+ assert epath("/*/[2]", expr) == []
29
+
30
+ assert epath("/*/int", expr) == [2]
31
+ assert epath("/*/Symbol", expr) == [z]
32
+ assert epath("/*/tuple", expr) == [(x, 1, t), (3, y, 4)]
33
+ assert epath("/*/__iter__?", expr) == [(x, 1, t), (3, y, 4)]
34
+
35
+ assert epath("/*/int|tuple", expr) == [(x, 1, t), 2, (3, y, 4)]
36
+ assert epath("/*/Symbol|tuple", expr) == [(x, 1, t), (3, y, 4), z]
37
+ assert epath("/*/int|Symbol|tuple", expr) == [(x, 1, t), 2, (3, y, 4), z]
38
+
39
+ assert epath("/*/int|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4)]
40
+ assert epath("/*/Symbol|__iter__?", expr) == [(x, 1, t), (3, y, 4), z]
41
+ assert epath(
42
+ "/*/int|Symbol|__iter__?", expr) == [(x, 1, t), 2, (3, y, 4), z]
43
+
44
+ assert epath("/*/[0]/int", expr) == [1, 3, 4]
45
+ assert epath("/*/[0]/Symbol", expr) == [x, t, y]
46
+
47
+ assert epath("/*/[0]/int[1:]", expr) == [1, 4]
48
+ assert epath("/*/[0]/Symbol[1:]", expr) == [t, y]
49
+
50
+ assert epath("/Symbol", x + y + z + 1) == [x, y, z]
51
+ assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E)) == [x, x, y]
52
+
53
+
54
+ def test_epath_apply():
55
+ expr = [((x, 1, t), 2), ((3, y, 4), z)]
56
+ func = lambda expr: expr**2
57
+
58
+ assert epath("/*", expr, list) == [[(x, 1, t), 2], [(3, y, 4), z]]
59
+
60
+ assert epath("/*/[0]", expr, list) == [([x, 1, t], 2), ([3, y, 4], z)]
61
+ assert epath("/*/[1]", expr, func) == [((x, 1, t), 4), ((3, y, 4), z**2)]
62
+ assert epath("/*/[2]", expr, list) == expr
63
+
64
+ assert epath("/*/[0]/int", expr, func) == [((x, 1, t), 2), ((9, y, 16), z)]
65
+ assert epath("/*/[0]/Symbol", expr, func) == [((x**2, 1, t**2), 2),
66
+ ((3, y**2, 4), z)]
67
+ assert epath(
68
+ "/*/[0]/int[1:]", expr, func) == [((x, 1, t), 2), ((3, y, 16), z)]
69
+ assert epath("/*/[0]/Symbol[1:]", expr, func) == [((x, 1, t**2),
70
+ 2), ((3, y**2, 4), z)]
71
+
72
+ assert epath("/Symbol", x + y + z + 1, func) == x**2 + y**2 + z**2 + 1
73
+ assert epath("/*/*/Symbol", t + sin(x + 1) + cos(x + y + E), func) == \
74
+ t + sin(x**2 + 1) + cos(x**2 + y**2 + E)
75
+
76
+
77
+ def test_EPath():
78
+ assert EPath("/*/[0]")._path == "/*/[0]"
79
+ assert EPath(EPath("/*/[0]"))._path == "/*/[0]"
80
+ assert isinstance(epath("/*/[0]"), EPath) is True
81
+
82
+ assert repr(EPath("/*/[0]")) == "EPath('/*/[0]')"
83
+
84
+ raises(ValueError, lambda: EPath(""))
85
+ raises(ValueError, lambda: EPath("/"))
86
+ raises(ValueError, lambda: EPath("/|x"))
87
+ raises(ValueError, lambda: EPath("/["))
88
+ raises(ValueError, lambda: EPath("/[0]%"))
89
+
90
+ raises(NotImplementedError, lambda: EPath("Symbol"))
lib/python3.12/site-packages/sympy/simplify/tests/test_fu.py ADDED
@@ -0,0 +1,492 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.add import Add
2
+ from sympy.core.mul import Mul
3
+ from sympy.core.numbers import (I, Rational, pi)
4
+ from sympy.core.parameters import evaluate
5
+ from sympy.core.singleton import S
6
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
7
+ from sympy.functions.elementary.hyperbolic import (cosh, coth, csch, sech, sinh, tanh)
8
+ from sympy.functions.elementary.miscellaneous import (root, sqrt)
9
+ from sympy.functions.elementary.trigonometric import (cos, cot, csc, sec, sin, tan)
10
+ from sympy.simplify.powsimp import powsimp
11
+ from sympy.simplify.fu import (
12
+ L, TR1, TR10, TR10i, TR11, _TR11, TR12, TR12i, TR13, TR14, TR15, TR16,
13
+ TR111, TR2, TR2i, TR3, TR4, TR5, TR6, TR7, TR8, TR9, TRmorrie, _TR56 as T,
14
+ TRpower, hyper_as_trig, fu, process_common_addends, trig_split,
15
+ as_f_sign_1)
16
+ from sympy.core.random import verify_numerically
17
+ from sympy.abc import a, b, c, x, y, z
18
+
19
+
20
+ def test_TR1():
21
+ assert TR1(2*csc(x) + sec(x)) == 1/cos(x) + 2/sin(x)
22
+
23
+
24
+ def test_TR2():
25
+ assert TR2(tan(x)) == sin(x)/cos(x)
26
+ assert TR2(cot(x)) == cos(x)/sin(x)
27
+ assert TR2(tan(tan(x) - sin(x)/cos(x))) == 0
28
+
29
+
30
+ def test_TR2i():
31
+ # just a reminder that ratios of powers only simplify if both
32
+ # numerator and denominator satisfy the condition that each
33
+ # has a positive base or an integer exponent; e.g. the following,
34
+ # at y=-1, x=1/2 gives sqrt(2)*I != -sqrt(2)*I
35
+ assert powsimp(2**x/y**x) != (2/y)**x
36
+
37
+ assert TR2i(sin(x)/cos(x)) == tan(x)
38
+ assert TR2i(sin(x)*sin(y)/cos(x)) == tan(x)*sin(y)
39
+ assert TR2i(1/(sin(x)/cos(x))) == 1/tan(x)
40
+ assert TR2i(1/(sin(x)*sin(y)/cos(x))) == 1/tan(x)/sin(y)
41
+ assert TR2i(sin(x)/2/(cos(x) + 1)) == sin(x)/(cos(x) + 1)/2
42
+
43
+ assert TR2i(sin(x)/2/(cos(x) + 1), half=True) == tan(x/2)/2
44
+ assert TR2i(sin(1)/(cos(1) + 1), half=True) == tan(S.Half)
45
+ assert TR2i(sin(2)/(cos(2) + 1), half=True) == tan(1)
46
+ assert TR2i(sin(4)/(cos(4) + 1), half=True) == tan(2)
47
+ assert TR2i(sin(5)/(cos(5) + 1), half=True) == tan(5*S.Half)
48
+ assert TR2i((cos(1) + 1)/sin(1), half=True) == 1/tan(S.Half)
49
+ assert TR2i((cos(2) + 1)/sin(2), half=True) == 1/tan(1)
50
+ assert TR2i((cos(4) + 1)/sin(4), half=True) == 1/tan(2)
51
+ assert TR2i((cos(5) + 1)/sin(5), half=True) == 1/tan(5*S.Half)
52
+ assert TR2i((cos(1) + 1)**(-a)*sin(1)**a, half=True) == tan(S.Half)**a
53
+ assert TR2i((cos(2) + 1)**(-a)*sin(2)**a, half=True) == tan(1)**a
54
+ assert TR2i((cos(4) + 1)**(-a)*sin(4)**a, half=True) == (cos(4) + 1)**(-a)*sin(4)**a
55
+ assert TR2i((cos(5) + 1)**(-a)*sin(5)**a, half=True) == (cos(5) + 1)**(-a)*sin(5)**a
56
+ assert TR2i((cos(1) + 1)**a*sin(1)**(-a), half=True) == tan(S.Half)**(-a)
57
+ assert TR2i((cos(2) + 1)**a*sin(2)**(-a), half=True) == tan(1)**(-a)
58
+ assert TR2i((cos(4) + 1)**a*sin(4)**(-a), half=True) == (cos(4) + 1)**a*sin(4)**(-a)
59
+ assert TR2i((cos(5) + 1)**a*sin(5)**(-a), half=True) == (cos(5) + 1)**a*sin(5)**(-a)
60
+
61
+ i = symbols('i', integer=True)
62
+ assert TR2i(((cos(5) + 1)**i*sin(5)**(-i)), half=True) == tan(5*S.Half)**(-i)
63
+ assert TR2i(1/((cos(5) + 1)**i*sin(5)**(-i)), half=True) == tan(5*S.Half)**i
64
+
65
+
66
+ def test_TR3():
67
+ assert TR3(cos(y - x*(y - x))) == cos(x*(x - y) + y)
68
+ assert cos(pi/2 + x) == -sin(x)
69
+ assert cos(30*pi/2 + x) == -cos(x)
70
+
71
+ for f in (cos, sin, tan, cot, csc, sec):
72
+ i = f(pi*Rational(3, 7))
73
+ j = TR3(i)
74
+ assert verify_numerically(i, j) and i.func != j.func
75
+
76
+ with evaluate(False):
77
+ eq = cos(9*pi/22)
78
+ assert eq.has(9*pi) and TR3(eq) == sin(pi/11)
79
+
80
+
81
+ def test_TR4():
82
+ for i in [0, pi/6, pi/4, pi/3, pi/2]:
83
+ with evaluate(False):
84
+ eq = cos(i)
85
+ assert isinstance(eq, cos) and TR4(eq) == cos(i)
86
+
87
+
88
+ def test__TR56():
89
+ h = lambda x: 1 - x
90
+ assert T(sin(x)**3, sin, cos, h, 4, False) == sin(x)*(-cos(x)**2 + 1)
91
+ assert T(sin(x)**10, sin, cos, h, 4, False) == sin(x)**10
92
+ assert T(sin(x)**6, sin, cos, h, 6, False) == (-cos(x)**2 + 1)**3
93
+ assert T(sin(x)**6, sin, cos, h, 6, True) == sin(x)**6
94
+ assert T(sin(x)**8, sin, cos, h, 10, True) == (-cos(x)**2 + 1)**4
95
+
96
+ # issue 17137
97
+ assert T(sin(x)**I, sin, cos, h, 4, True) == sin(x)**I
98
+ assert T(sin(x)**(2*I + 1), sin, cos, h, 4, True) == sin(x)**(2*I + 1)
99
+
100
+
101
+ def test_TR5():
102
+ assert TR5(sin(x)**2) == -cos(x)**2 + 1
103
+ assert TR5(sin(x)**-2) == sin(x)**(-2)
104
+ assert TR5(sin(x)**4) == (-cos(x)**2 + 1)**2
105
+
106
+
107
+ def test_TR6():
108
+ assert TR6(cos(x)**2) == -sin(x)**2 + 1
109
+ assert TR6(cos(x)**-2) == cos(x)**(-2)
110
+ assert TR6(cos(x)**4) == (-sin(x)**2 + 1)**2
111
+
112
+
113
+ def test_TR7():
114
+ assert TR7(cos(x)**2) == cos(2*x)/2 + S.Half
115
+ assert TR7(cos(x)**2 + 1) == cos(2*x)/2 + Rational(3, 2)
116
+
117
+
118
+ def test_TR8():
119
+ assert TR8(cos(2)*cos(3)) == cos(5)/2 + cos(1)/2
120
+ assert TR8(cos(2)*sin(3)) == sin(5)/2 + sin(1)/2
121
+ assert TR8(sin(2)*sin(3)) == -cos(5)/2 + cos(1)/2
122
+ assert TR8(sin(1)*sin(2)*sin(3)) == sin(4)/4 - sin(6)/4 + sin(2)/4
123
+ assert TR8(cos(2)*cos(3)*cos(4)*cos(5)) == \
124
+ cos(4)/4 + cos(10)/8 + cos(2)/8 + cos(8)/8 + cos(14)/8 + \
125
+ cos(6)/8 + Rational(1, 8)
126
+ assert TR8(cos(2)*cos(3)*cos(4)*cos(5)*cos(6)) == \
127
+ cos(10)/8 + cos(4)/8 + 3*cos(2)/16 + cos(16)/16 + cos(8)/8 + \
128
+ cos(14)/16 + cos(20)/16 + cos(12)/16 + Rational(1, 16) + cos(6)/8
129
+ assert TR8(sin(pi*Rational(3, 7))**2*cos(pi*Rational(3, 7))**2/(16*sin(pi/7)**2)) == Rational(1, 64)
130
+
131
+ def test_TR9():
132
+ a = S.Half
133
+ b = 3*a
134
+ assert TR9(a) == a
135
+ assert TR9(cos(1) + cos(2)) == 2*cos(a)*cos(b)
136
+ assert TR9(cos(1) - cos(2)) == 2*sin(a)*sin(b)
137
+ assert TR9(sin(1) - sin(2)) == -2*sin(a)*cos(b)
138
+ assert TR9(sin(1) + sin(2)) == 2*sin(b)*cos(a)
139
+ assert TR9(cos(1) + 2*sin(1) + 2*sin(2)) == cos(1) + 4*sin(b)*cos(a)
140
+ assert TR9(cos(4) + cos(2) + 2*cos(1)*cos(3)) == 4*cos(1)*cos(3)
141
+ assert TR9((cos(4) + cos(2))/cos(3)/2 + cos(3)) == 2*cos(1)*cos(2)
142
+ assert TR9(cos(3) + cos(4) + cos(5) + cos(6)) == \
143
+ 4*cos(S.Half)*cos(1)*cos(Rational(9, 2))
144
+ assert TR9(cos(3) + cos(3)*cos(2)) == cos(3) + cos(2)*cos(3)
145
+ assert TR9(-cos(y) + cos(x*y)) == -2*sin(x*y/2 - y/2)*sin(x*y/2 + y/2)
146
+ assert TR9(-sin(y) + sin(x*y)) == 2*sin(x*y/2 - y/2)*cos(x*y/2 + y/2)
147
+ c = cos(x)
148
+ s = sin(x)
149
+ for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)):
150
+ for a in ((c, s), (s, c), (cos(x), cos(x*y)), (sin(x), sin(x*y))):
151
+ args = zip(si, a)
152
+ ex = Add(*[Mul(*ai) for ai in args])
153
+ t = TR9(ex)
154
+ assert not (a[0].func == a[1].func and (
155
+ not verify_numerically(ex, t.expand(trig=True)) or t.is_Add)
156
+ or a[1].func != a[0].func and ex != t)
157
+
158
+
159
+ def test_TR10():
160
+ assert TR10(cos(a + b)) == -sin(a)*sin(b) + cos(a)*cos(b)
161
+ assert TR10(sin(a + b)) == sin(a)*cos(b) + sin(b)*cos(a)
162
+ assert TR10(sin(a + b + c)) == \
163
+ (-sin(a)*sin(b) + cos(a)*cos(b))*sin(c) + \
164
+ (sin(a)*cos(b) + sin(b)*cos(a))*cos(c)
165
+ assert TR10(cos(a + b + c)) == \
166
+ (-sin(a)*sin(b) + cos(a)*cos(b))*cos(c) - \
167
+ (sin(a)*cos(b) + sin(b)*cos(a))*sin(c)
168
+
169
+
170
+ def test_TR10i():
171
+ assert TR10i(cos(1)*cos(3) + sin(1)*sin(3)) == cos(2)
172
+ assert TR10i(cos(1)*cos(3) - sin(1)*sin(3)) == cos(4)
173
+ assert TR10i(cos(1)*sin(3) - sin(1)*cos(3)) == sin(2)
174
+ assert TR10i(cos(1)*sin(3) + sin(1)*cos(3)) == sin(4)
175
+ assert TR10i(cos(1)*sin(3) + sin(1)*cos(3) + 7) == sin(4) + 7
176
+ assert TR10i(cos(1)*sin(3) + sin(1)*cos(3) + cos(3)) == cos(3) + sin(4)
177
+ assert TR10i(2*cos(1)*sin(3) + 2*sin(1)*cos(3) + cos(3)) == \
178
+ 2*sin(4) + cos(3)
179
+ assert TR10i(cos(2)*cos(3) + sin(2)*(cos(1)*sin(2) + cos(2)*sin(1))) == \
180
+ cos(1)
181
+ eq = (cos(2)*cos(3) + sin(2)*(
182
+ cos(1)*sin(2) + cos(2)*sin(1)))*cos(5) + sin(1)*sin(5)
183
+ assert TR10i(eq) == TR10i(eq.expand()) == cos(4)
184
+ assert TR10i(sqrt(2)*cos(x)*x + sqrt(6)*sin(x)*x) == \
185
+ 2*sqrt(2)*x*sin(x + pi/6)
186
+ assert TR10i(cos(x)/sqrt(6) + sin(x)/sqrt(2) +
187
+ cos(x)/sqrt(6)/3 + sin(x)/sqrt(2)/3) == 4*sqrt(6)*sin(x + pi/6)/9
188
+ assert TR10i(cos(x)/sqrt(6) + sin(x)/sqrt(2) +
189
+ cos(y)/sqrt(6)/3 + sin(y)/sqrt(2)/3) == \
190
+ sqrt(6)*sin(x + pi/6)/3 + sqrt(6)*sin(y + pi/6)/9
191
+ assert TR10i(cos(x) + sqrt(3)*sin(x) + 2*sqrt(3)*cos(x + pi/6)) == 4*cos(x)
192
+ assert TR10i(cos(x) + sqrt(3)*sin(x) +
193
+ 2*sqrt(3)*cos(x + pi/6) + 4*sin(x)) == 4*sqrt(2)*sin(x + pi/4)
194
+ assert TR10i(cos(2)*sin(3) + sin(2)*cos(4)) == \
195
+ sin(2)*cos(4) + sin(3)*cos(2)
196
+
197
+ A = Symbol('A', commutative=False)
198
+ assert TR10i(sqrt(2)*cos(x)*A + sqrt(6)*sin(x)*A) == \
199
+ 2*sqrt(2)*sin(x + pi/6)*A
200
+
201
+
202
+ c = cos(x)
203
+ s = sin(x)
204
+ h = sin(y)
205
+ r = cos(y)
206
+ for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)):
207
+ for argsi in ((c*r, s*h), (c*h, s*r)): # explicit 2-args
208
+ args = zip(si, argsi)
209
+ ex = Add(*[Mul(*ai) for ai in args])
210
+ t = TR10i(ex)
211
+ assert not (ex - t.expand(trig=True) or t.is_Add)
212
+
213
+ c = cos(x)
214
+ s = sin(x)
215
+ h = sin(pi/6)
216
+ r = cos(pi/6)
217
+ for si in ((1, 1), (1, -1), (-1, 1), (-1, -1)):
218
+ for argsi in ((c*r, s*h), (c*h, s*r)): # induced
219
+ args = zip(si, argsi)
220
+ ex = Add(*[Mul(*ai) for ai in args])
221
+ t = TR10i(ex)
222
+ assert not (ex - t.expand(trig=True) or t.is_Add)
223
+
224
+
225
+ def test_TR11():
226
+
227
+ assert TR11(sin(2*x)) == 2*sin(x)*cos(x)
228
+ assert TR11(sin(4*x)) == 4*((-sin(x)**2 + cos(x)**2)*sin(x)*cos(x))
229
+ assert TR11(sin(x*Rational(4, 3))) == \
230
+ 4*((-sin(x/3)**2 + cos(x/3)**2)*sin(x/3)*cos(x/3))
231
+
232
+ assert TR11(cos(2*x)) == -sin(x)**2 + cos(x)**2
233
+ assert TR11(cos(4*x)) == \
234
+ (-sin(x)**2 + cos(x)**2)**2 - 4*sin(x)**2*cos(x)**2
235
+
236
+ assert TR11(cos(2)) == cos(2)
237
+
238
+ assert TR11(cos(pi*Rational(3, 7)), pi*Rational(2, 7)) == -cos(pi*Rational(2, 7))**2 + sin(pi*Rational(2, 7))**2
239
+ assert TR11(cos(4), 2) == -sin(2)**2 + cos(2)**2
240
+ assert TR11(cos(6), 2) == cos(6)
241
+ assert TR11(sin(x)/cos(x/2), x/2) == 2*sin(x/2)
242
+
243
+ def test__TR11():
244
+
245
+ assert _TR11(sin(x/3)*sin(2*x)*sin(x/4)/(cos(x/6)*cos(x/8))) == \
246
+ 4*sin(x/8)*sin(x/6)*sin(2*x),_TR11(sin(x/3)*sin(2*x)*sin(x/4)/(cos(x/6)*cos(x/8)))
247
+ assert _TR11(sin(x/3)/cos(x/6)) == 2*sin(x/6)
248
+
249
+ assert _TR11(cos(x/6)/sin(x/3)) == 1/(2*sin(x/6))
250
+ assert _TR11(sin(2*x)*cos(x/8)/sin(x/4)) == sin(2*x)/(2*sin(x/8)), _TR11(sin(2*x)*cos(x/8)/sin(x/4))
251
+ assert _TR11(sin(x)/sin(x/2)) == 2*cos(x/2)
252
+
253
+
254
+ def test_TR12():
255
+ assert TR12(tan(x + y)) == (tan(x) + tan(y))/(-tan(x)*tan(y) + 1)
256
+ assert TR12(tan(x + y + z)) ==\
257
+ (tan(z) + (tan(x) + tan(y))/(-tan(x)*tan(y) + 1))/(
258
+ 1 - (tan(x) + tan(y))*tan(z)/(-tan(x)*tan(y) + 1))
259
+ assert TR12(tan(x*y)) == tan(x*y)
260
+
261
+
262
+ def test_TR13():
263
+ assert TR13(tan(3)*tan(2)) == -tan(2)/tan(5) - tan(3)/tan(5) + 1
264
+ assert TR13(cot(3)*cot(2)) == 1 + cot(3)*cot(5) + cot(2)*cot(5)
265
+ assert TR13(tan(1)*tan(2)*tan(3)) == \
266
+ (-tan(2)/tan(5) - tan(3)/tan(5) + 1)*tan(1)
267
+ assert TR13(tan(1)*tan(2)*cot(3)) == \
268
+ (-tan(2)/tan(3) + 1 - tan(1)/tan(3))*cot(3)
269
+
270
+
271
+ def test_L():
272
+ assert L(cos(x) + sin(x)) == 2
273
+
274
+
275
+ def test_fu():
276
+
277
+ assert fu(sin(50)**2 + cos(50)**2 + sin(pi/6)) == Rational(3, 2)
278
+ assert fu(sqrt(6)*cos(x) + sqrt(2)*sin(x)) == 2*sqrt(2)*sin(x + pi/3)
279
+
280
+
281
+ eq = sin(x)**4 - cos(y)**2 + sin(y)**2 + 2*cos(x)**2
282
+ assert fu(eq) == cos(x)**4 - 2*cos(y)**2 + 2
283
+
284
+ assert fu(S.Half - cos(2*x)/2) == sin(x)**2
285
+
286
+ assert fu(sin(a)*(cos(b) - sin(b)) + cos(a)*(sin(b) + cos(b))) == \
287
+ sqrt(2)*sin(a + b + pi/4)
288
+
289
+ assert fu(sqrt(3)*cos(x)/2 + sin(x)/2) == sin(x + pi/3)
290
+
291
+ assert fu(1 - sin(2*x)**2/4 - sin(y)**2 - cos(x)**4) == \
292
+ -cos(x)**2 + cos(y)**2
293
+
294
+ assert fu(cos(pi*Rational(4, 9))) == sin(pi/18)
295
+ assert fu(cos(pi/9)*cos(pi*Rational(2, 9))*cos(pi*Rational(3, 9))*cos(pi*Rational(4, 9))) == Rational(1, 16)
296
+
297
+ assert fu(
298
+ tan(pi*Rational(7, 18)) + tan(pi*Rational(5, 18)) - sqrt(3)*tan(pi*Rational(5, 18))*tan(pi*Rational(7, 18))) == \
299
+ -sqrt(3)
300
+
301
+ assert fu(tan(1)*tan(2)) == tan(1)*tan(2)
302
+
303
+ expr = Mul(*[cos(2**i) for i in range(10)])
304
+ assert fu(expr) == sin(1024)/(1024*sin(1))
305
+
306
+ # issue #18059:
307
+ assert fu(cos(x) + sqrt(sin(x)**2)) == cos(x) + sqrt(sin(x)**2)
308
+
309
+ assert fu((-14*sin(x)**3 + 35*sin(x) + 6*sqrt(3)*cos(x)**3 + 9*sqrt(3)*cos(x))/((cos(2*x) + 4))) == \
310
+ 7*sin(x) + 3*sqrt(3)*cos(x)
311
+
312
+
313
+ def test_objective():
314
+ assert fu(sin(x)/cos(x), measure=lambda x: x.count_ops()) == \
315
+ tan(x)
316
+ assert fu(sin(x)/cos(x), measure=lambda x: -x.count_ops()) == \
317
+ sin(x)/cos(x)
318
+
319
+
320
+ def test_process_common_addends():
321
+ # this tests that the args are not evaluated as they are given to do
322
+ # and that key2 works when key1 is False
323
+ do = lambda x: Add(*[i**(i%2) for i in x.args])
324
+ assert process_common_addends(Add(*[1, 2, 3, 4], evaluate=False), do,
325
+ key2=lambda x: x%2, key1=False) == 1**1 + 3**1 + 2**0 + 4**0
326
+
327
+
328
+ def test_trig_split():
329
+ assert trig_split(cos(x), cos(y)) == (1, 1, 1, x, y, True)
330
+ assert trig_split(2*cos(x), -2*cos(y)) == (2, 1, -1, x, y, True)
331
+ assert trig_split(cos(x)*sin(y), cos(y)*sin(y)) == \
332
+ (sin(y), 1, 1, x, y, True)
333
+
334
+ assert trig_split(cos(x), -sqrt(3)*sin(x), two=True) == \
335
+ (2, 1, -1, x, pi/6, False)
336
+ assert trig_split(cos(x), sin(x), two=True) == \
337
+ (sqrt(2), 1, 1, x, pi/4, False)
338
+ assert trig_split(cos(x), -sin(x), two=True) == \
339
+ (sqrt(2), 1, -1, x, pi/4, False)
340
+ assert trig_split(sqrt(2)*cos(x), -sqrt(6)*sin(x), two=True) == \
341
+ (2*sqrt(2), 1, -1, x, pi/6, False)
342
+ assert trig_split(-sqrt(6)*cos(x), -sqrt(2)*sin(x), two=True) == \
343
+ (-2*sqrt(2), 1, 1, x, pi/3, False)
344
+ assert trig_split(cos(x)/sqrt(6), sin(x)/sqrt(2), two=True) == \
345
+ (sqrt(6)/3, 1, 1, x, pi/6, False)
346
+ assert trig_split(-sqrt(6)*cos(x)*sin(y),
347
+ -sqrt(2)*sin(x)*sin(y), two=True) == \
348
+ (-2*sqrt(2)*sin(y), 1, 1, x, pi/3, False)
349
+
350
+ assert trig_split(cos(x), sin(x)) is None
351
+ assert trig_split(cos(x), sin(z)) is None
352
+ assert trig_split(2*cos(x), -sin(x)) is None
353
+ assert trig_split(cos(x), -sqrt(3)*sin(x)) is None
354
+ assert trig_split(cos(x)*cos(y), sin(x)*sin(z)) is None
355
+ assert trig_split(cos(x)*cos(y), sin(x)*sin(y)) is None
356
+ assert trig_split(-sqrt(6)*cos(x), sqrt(2)*sin(x)*sin(y), two=True) is \
357
+ None
358
+
359
+ assert trig_split(sqrt(3)*sqrt(x), cos(3), two=True) is None
360
+ assert trig_split(sqrt(3)*root(x, 3), sin(3)*cos(2), two=True) is None
361
+ assert trig_split(cos(5)*cos(6), cos(7)*sin(5), two=True) is None
362
+
363
+
364
+ def test_TRmorrie():
365
+ assert TRmorrie(7*Mul(*[cos(i) for i in range(10)])) == \
366
+ 7*sin(12)*sin(16)*cos(5)*cos(7)*cos(9)/(64*sin(1)*sin(3))
367
+ assert TRmorrie(x) == x
368
+ assert TRmorrie(2*x) == 2*x
369
+ e = cos(pi/7)*cos(pi*Rational(2, 7))*cos(pi*Rational(4, 7))
370
+ assert TR8(TRmorrie(e)) == Rational(-1, 8)
371
+ e = Mul(*[cos(2**i*pi/17) for i in range(1, 17)])
372
+ assert TR8(TR3(TRmorrie(e))) == Rational(1, 65536)
373
+ # issue 17063
374
+ eq = cos(x)/cos(x/2)
375
+ assert TRmorrie(eq) == eq
376
+ # issue #20430
377
+ eq = cos(x/2)*sin(x/2)*cos(x)**3
378
+ assert TRmorrie(eq) == sin(2*x)*cos(x)**2/4
379
+
380
+
381
+ def test_TRpower():
382
+ assert TRpower(1/sin(x)**2) == 1/sin(x)**2
383
+ assert TRpower(cos(x)**3*sin(x/2)**4) == \
384
+ (3*cos(x)/4 + cos(3*x)/4)*(-cos(x)/2 + cos(2*x)/8 + Rational(3, 8))
385
+ for k in range(2, 8):
386
+ assert verify_numerically(sin(x)**k, TRpower(sin(x)**k))
387
+ assert verify_numerically(cos(x)**k, TRpower(cos(x)**k))
388
+
389
+
390
+ def test_hyper_as_trig():
391
+ from sympy.simplify.fu import _osborne, _osbornei
392
+
393
+ eq = sinh(x)**2 + cosh(x)**2
394
+ t, f = hyper_as_trig(eq)
395
+ assert f(fu(t)) == cosh(2*x)
396
+ e, f = hyper_as_trig(tanh(x + y))
397
+ assert f(TR12(e)) == (tanh(x) + tanh(y))/(tanh(x)*tanh(y) + 1)
398
+
399
+ d = Dummy()
400
+ assert _osborne(sinh(x), d) == I*sin(x*d)
401
+ assert _osborne(tanh(x), d) == I*tan(x*d)
402
+ assert _osborne(coth(x), d) == cot(x*d)/I
403
+ assert _osborne(cosh(x), d) == cos(x*d)
404
+ assert _osborne(sech(x), d) == sec(x*d)
405
+ assert _osborne(csch(x), d) == csc(x*d)/I
406
+ for func in (sinh, cosh, tanh, coth, sech, csch):
407
+ h = func(pi)
408
+ assert _osbornei(_osborne(h, d), d) == h
409
+ # /!\ the _osborne functions are not meant to work
410
+ # in the o(i(trig, d), d) direction so we just check
411
+ # that they work as they are supposed to work
412
+ assert _osbornei(cos(x*y + z), y) == cosh(x + z*I)
413
+ assert _osbornei(sin(x*y + z), y) == sinh(x + z*I)/I
414
+ assert _osbornei(tan(x*y + z), y) == tanh(x + z*I)/I
415
+ assert _osbornei(cot(x*y + z), y) == coth(x + z*I)*I
416
+ assert _osbornei(sec(x*y + z), y) == sech(x + z*I)
417
+ assert _osbornei(csc(x*y + z), y) == csch(x + z*I)*I
418
+
419
+
420
+ def test_TR12i():
421
+ ta, tb, tc = [tan(i) for i in (a, b, c)]
422
+ assert TR12i((ta + tb)/(-ta*tb + 1)) == tan(a + b)
423
+ assert TR12i((ta + tb)/(ta*tb - 1)) == -tan(a + b)
424
+ assert TR12i((-ta - tb)/(ta*tb - 1)) == tan(a + b)
425
+ eq = (ta + tb)/(-ta*tb + 1)**2*(-3*ta - 3*tc)/(2*(ta*tc - 1))
426
+ assert TR12i(eq.expand()) == \
427
+ -3*tan(a + b)*tan(a + c)/(tan(a) + tan(b) - 1)/2
428
+ assert TR12i(tan(x)/sin(x)) == tan(x)/sin(x)
429
+ eq = (ta + cos(2))/(-ta*tb + 1)
430
+ assert TR12i(eq) == eq
431
+ eq = (ta + tb + 2)**2/(-ta*tb + 1)
432
+ assert TR12i(eq) == eq
433
+ eq = ta/(-ta*tb + 1)
434
+ assert TR12i(eq) == eq
435
+ eq = (((ta + tb)*(a + 1)).expand())**2/(ta*tb - 1)
436
+ assert TR12i(eq) == -(a + 1)**2*tan(a + b)
437
+
438
+
439
+ def test_TR14():
440
+ eq = (cos(x) - 1)*(cos(x) + 1)
441
+ ans = -sin(x)**2
442
+ assert TR14(eq) == ans
443
+ assert TR14(1/eq) == 1/ans
444
+ assert TR14((cos(x) - 1)**2*(cos(x) + 1)**2) == ans**2
445
+ assert TR14((cos(x) - 1)**2*(cos(x) + 1)**3) == ans**2*(cos(x) + 1)
446
+ assert TR14((cos(x) - 1)**3*(cos(x) + 1)**2) == ans**2*(cos(x) - 1)
447
+ eq = (cos(x) - 1)**y*(cos(x) + 1)**y
448
+ assert TR14(eq) == eq
449
+ eq = (cos(x) - 2)**y*(cos(x) + 1)
450
+ assert TR14(eq) == eq
451
+ eq = (tan(x) - 2)**2*(cos(x) + 1)
452
+ assert TR14(eq) == eq
453
+ i = symbols('i', integer=True)
454
+ assert TR14((cos(x) - 1)**i*(cos(x) + 1)**i) == ans**i
455
+ assert TR14((sin(x) - 1)**i*(sin(x) + 1)**i) == (-cos(x)**2)**i
456
+ # could use extraction in this case
457
+ eq = (cos(x) - 1)**(i + 1)*(cos(x) + 1)**i
458
+ assert TR14(eq) in [(cos(x) - 1)*ans**i, eq]
459
+
460
+ assert TR14((sin(x) - 1)*(sin(x) + 1)) == -cos(x)**2
461
+ p1 = (cos(x) + 1)*(cos(x) - 1)
462
+ p2 = (cos(y) - 1)*2*(cos(y) + 1)
463
+ p3 = (3*(cos(y) - 1))*(3*(cos(y) + 1))
464
+ assert TR14(p1*p2*p3*(x - 1)) == -18*((x - 1)*sin(x)**2*sin(y)**4)
465
+
466
+
467
+ def test_TR15_16_17():
468
+ assert TR15(1 - 1/sin(x)**2) == -cot(x)**2
469
+ assert TR16(1 - 1/cos(x)**2) == -tan(x)**2
470
+ assert TR111(1 - 1/tan(x)**2) == 1 - cot(x)**2
471
+
472
+
473
+ def test_as_f_sign_1():
474
+ assert as_f_sign_1(x + 1) == (1, x, 1)
475
+ assert as_f_sign_1(x - 1) == (1, x, -1)
476
+ assert as_f_sign_1(-x + 1) == (-1, x, -1)
477
+ assert as_f_sign_1(-x - 1) == (-1, x, 1)
478
+ assert as_f_sign_1(2*x + 2) == (2, x, 1)
479
+ assert as_f_sign_1(x*y - y) == (y, x, -1)
480
+ assert as_f_sign_1(-x*y + y) == (-y, x, -1)
481
+
482
+
483
+ def test_issue_25590():
484
+ A = Symbol('A', commutative=False)
485
+ B = Symbol('B', commutative=False)
486
+
487
+ assert TR8(2*cos(x)*sin(x)*B*A) == sin(2*x)*B*A
488
+ assert TR13(tan(2)*tan(3)*B*A) == (-tan(2)/tan(5) - tan(3)/tan(5) + 1)*B*A
489
+
490
+ # XXX The result may not be optimal than
491
+ # sin(2*x)*B*A + cos(x)**2 and may change in the future
492
+ assert (2*cos(x)*sin(x)*B*A + cos(x)**2).simplify() == sin(2*x)*B*A + cos(2*x)/2 + S.One/2
lib/python3.12/site-packages/sympy/simplify/tests/test_function.py ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """ Unit tests for Hyper_Function"""
2
+ from sympy.core import symbols, Dummy, Tuple, S, Rational
3
+ from sympy.functions import hyper
4
+
5
+ from sympy.simplify.hyperexpand import Hyper_Function
6
+
7
+ def test_attrs():
8
+ a, b = symbols('a, b', cls=Dummy)
9
+ f = Hyper_Function([2, a], [b])
10
+ assert f.ap == Tuple(2, a)
11
+ assert f.bq == Tuple(b)
12
+ assert f.args == (Tuple(2, a), Tuple(b))
13
+ assert f.sizes == (2, 1)
14
+
15
+ def test_call():
16
+ a, b, x = symbols('a, b, x', cls=Dummy)
17
+ f = Hyper_Function([2, a], [b])
18
+ assert f(x) == hyper([2, a], [b], x)
19
+
20
+ def test_has():
21
+ a, b, c = symbols('a, b, c', cls=Dummy)
22
+ f = Hyper_Function([2, -a], [b])
23
+ assert f.has(a)
24
+ assert f.has(Tuple(b))
25
+ assert not f.has(c)
26
+
27
+ def test_eq():
28
+ assert Hyper_Function([1], []) == Hyper_Function([1], [])
29
+ assert (Hyper_Function([1], []) != Hyper_Function([1], [])) is False
30
+ assert Hyper_Function([1], []) != Hyper_Function([2], [])
31
+ assert Hyper_Function([1], []) != Hyper_Function([1, 2], [])
32
+ assert Hyper_Function([1], []) != Hyper_Function([1], [2])
33
+
34
+ def test_gamma():
35
+ assert Hyper_Function([2, 3], [-1]).gamma == 0
36
+ assert Hyper_Function([-2, -3], [-1]).gamma == 2
37
+ n = Dummy(integer=True)
38
+ assert Hyper_Function([-1, n, 1], []).gamma == 1
39
+ assert Hyper_Function([-1, -n, 1], []).gamma == 1
40
+ p = Dummy(integer=True, positive=True)
41
+ assert Hyper_Function([-1, p, 1], []).gamma == 1
42
+ assert Hyper_Function([-1, -p, 1], []).gamma == 2
43
+
44
+ def test_suitable_origin():
45
+ assert Hyper_Function((S.Half,), (Rational(3, 2),))._is_suitable_origin() is True
46
+ assert Hyper_Function((S.Half,), (S.Half,))._is_suitable_origin() is False
47
+ assert Hyper_Function((S.Half,), (Rational(-1, 2),))._is_suitable_origin() is False
48
+ assert Hyper_Function((S.Half,), (0,))._is_suitable_origin() is False
49
+ assert Hyper_Function((S.Half,), (-1, 1,))._is_suitable_origin() is False
50
+ assert Hyper_Function((S.Half, 0), (1,))._is_suitable_origin() is False
51
+ assert Hyper_Function((S.Half, 1),
52
+ (2, Rational(-2, 3)))._is_suitable_origin() is True
53
+ assert Hyper_Function((S.Half, 1),
54
+ (2, Rational(-2, 3), Rational(3, 2)))._is_suitable_origin() is True
lib/python3.12/site-packages/sympy/simplify/tests/test_gammasimp.py ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.function import Function
2
+ from sympy.core.numbers import (Rational, pi)
3
+ from sympy.core.singleton import S
4
+ from sympy.core.symbol import symbols
5
+ from sympy.functions.combinatorial.factorials import (rf, binomial, factorial)
6
+ from sympy.functions.elementary.exponential import exp
7
+ from sympy.functions.elementary.miscellaneous import sqrt
8
+ from sympy.functions.elementary.piecewise import Piecewise
9
+ from sympy.functions.elementary.trigonometric import (cos, sin)
10
+ from sympy.functions.special.gamma_functions import gamma
11
+ from sympy.simplify.gammasimp import gammasimp
12
+ from sympy.simplify.powsimp import powsimp
13
+ from sympy.simplify.simplify import simplify
14
+
15
+ from sympy.abc import x, y, n, k
16
+
17
+
18
+ def test_gammasimp():
19
+ R = Rational
20
+
21
+ # was part of test_combsimp_gamma() in test_combsimp.py
22
+ assert gammasimp(gamma(x)) == gamma(x)
23
+ assert gammasimp(gamma(x + 1)/x) == gamma(x)
24
+ assert gammasimp(gamma(x)/(x - 1)) == gamma(x - 1)
25
+ assert gammasimp(x*gamma(x)) == gamma(x + 1)
26
+ assert gammasimp((x + 1)*gamma(x + 1)) == gamma(x + 2)
27
+ assert gammasimp(gamma(x + y)*(x + y)) == gamma(x + y + 1)
28
+ assert gammasimp(x/gamma(x + 1)) == 1/gamma(x)
29
+ assert gammasimp((x + 1)**2/gamma(x + 2)) == (x + 1)/gamma(x + 1)
30
+ assert gammasimp(x*gamma(x) + gamma(x + 3)/(x + 2)) == \
31
+ (x + 2)*gamma(x + 1)
32
+
33
+ assert gammasimp(gamma(2*x)*x) == gamma(2*x + 1)/2
34
+ assert gammasimp(gamma(2*x)/(x - S.Half)) == 2*gamma(2*x - 1)
35
+
36
+ assert gammasimp(gamma(x)*gamma(1 - x)) == pi/sin(pi*x)
37
+ assert gammasimp(gamma(x)*gamma(-x)) == -pi/(x*sin(pi*x))
38
+ assert gammasimp(1/gamma(x + 3)/gamma(1 - x)) == \
39
+ sin(pi*x)/(pi*x*(x + 1)*(x + 2))
40
+
41
+ assert gammasimp(factorial(n + 2)) == gamma(n + 3)
42
+ assert gammasimp(binomial(n, k)) == \
43
+ gamma(n + 1)/(gamma(k + 1)*gamma(-k + n + 1))
44
+
45
+ assert powsimp(gammasimp(
46
+ gamma(x)*gamma(x + S.Half)*gamma(y)/gamma(x + y))) == \
47
+ 2**(-2*x + 1)*sqrt(pi)*gamma(2*x)*gamma(y)/gamma(x + y)
48
+ assert gammasimp(1/gamma(x)/gamma(x - Rational(1, 3))/gamma(x + Rational(1, 3))) == \
49
+ 3**(3*x - Rational(3, 2))/(2*pi*gamma(3*x - 1))
50
+ assert simplify(
51
+ gamma(S.Half + x/2)*gamma(1 + x/2)/gamma(1 + x)/sqrt(pi)*2**x) == 1
52
+ assert gammasimp(gamma(Rational(-1, 4))*gamma(Rational(-3, 4))) == 16*sqrt(2)*pi/3
53
+
54
+ assert powsimp(gammasimp(gamma(2*x)/gamma(x))) == \
55
+ 2**(2*x - 1)*gamma(x + S.Half)/sqrt(pi)
56
+
57
+ # issue 6792
58
+ e = (-gamma(k)*gamma(k + 2) + gamma(k + 1)**2)/gamma(k)**2
59
+ assert gammasimp(e) == -k
60
+ assert gammasimp(1/e) == -1/k
61
+ e = (gamma(x) + gamma(x + 1))/gamma(x)
62
+ assert gammasimp(e) == x + 1
63
+ assert gammasimp(1/e) == 1/(x + 1)
64
+ e = (gamma(x) + gamma(x + 2))*(gamma(x - 1) + gamma(x))/gamma(x)
65
+ assert gammasimp(e) == (x**2 + x + 1)*gamma(x + 1)/(x - 1)
66
+ e = (-gamma(k)*gamma(k + 2) + gamma(k + 1)**2)/gamma(k)**2
67
+ assert gammasimp(e**2) == k**2
68
+ assert gammasimp(e**2/gamma(k + 1)) == k/gamma(k)
69
+ a = R(1, 2) + R(1, 3)
70
+ b = a + R(1, 3)
71
+ assert gammasimp(gamma(2*k)/gamma(k)*gamma(k + a)*gamma(k + b)
72
+ ) == 3*2**(2*k + 1)*3**(-3*k - 2)*sqrt(pi)*gamma(3*k + R(3, 2))/2
73
+
74
+ # issue 9699
75
+ assert gammasimp((x + 1)*factorial(x)/gamma(y)) == gamma(x + 2)/gamma(y)
76
+ assert gammasimp(rf(x + n, k)*binomial(n, k)).simplify() == Piecewise(
77
+ (gamma(n + 1)*gamma(k + n + x)/(gamma(k + 1)*gamma(n + x)*gamma(-k + n + 1)), n > -x),
78
+ ((-1)**k*gamma(n + 1)*gamma(-n - x + 1)/(gamma(k + 1)*gamma(-k + n + 1)*gamma(-k - n - x + 1)), True))
79
+
80
+ A, B = symbols('A B', commutative=False)
81
+ assert gammasimp(e*B*A) == gammasimp(e)*B*A
82
+
83
+ # check iteration
84
+ assert gammasimp(gamma(2*k)/gamma(k)*gamma(-k - R(1, 2))) == (
85
+ -2**(2*k + 1)*sqrt(pi)/(2*((2*k + 1)*cos(pi*k))))
86
+ assert gammasimp(
87
+ gamma(k)*gamma(k + R(1, 3))*gamma(k + R(2, 3))/gamma(k*R(3, 2))) == (
88
+ 3*2**(3*k + 1)*3**(-3*k - S.Half)*sqrt(pi)*gamma(k*R(3, 2) + S.Half)/2)
89
+
90
+ # issue 6153
91
+ assert gammasimp(gamma(Rational(1, 4))/gamma(Rational(5, 4))) == 4
92
+
93
+ # was part of test_combsimp() in test_combsimp.py
94
+ assert gammasimp(binomial(n + 2, k + S.Half)) == gamma(n + 3)/ \
95
+ (gamma(k + R(3, 2))*gamma(-k + n + R(5, 2)))
96
+ assert gammasimp(binomial(n + 2, k + 2.0)) == \
97
+ gamma(n + 3)/(gamma(k + 3.0)*gamma(-k + n + 1))
98
+
99
+ # issue 11548
100
+ assert gammasimp(binomial(0, x)) == sin(pi*x)/(pi*x)
101
+
102
+ e = gamma(n + Rational(1, 3))*gamma(n + R(2, 3))
103
+ assert gammasimp(e) == e
104
+ assert gammasimp(gamma(4*n + S.Half)/gamma(2*n - R(3, 4))) == \
105
+ 2**(4*n - R(5, 2))*(8*n - 3)*gamma(2*n + R(3, 4))/sqrt(pi)
106
+
107
+ i, m = symbols('i m', integer = True)
108
+ e = gamma(exp(i))
109
+ assert gammasimp(e) == e
110
+ e = gamma(m + 3)
111
+ assert gammasimp(e) == e
112
+ e = gamma(m + 1)/(gamma(i + 1)*gamma(-i + m + 1))
113
+ assert gammasimp(e) == e
114
+
115
+ p = symbols("p", integer=True, positive=True)
116
+ assert gammasimp(gamma(-p + 4)) == gamma(-p + 4)
117
+
118
+
119
+ def test_issue_22606():
120
+ fx = Function('f')(x)
121
+ eq = x + gamma(y)
122
+ # seems like ans should be `eq`, not `(x*y + gamma(y + 1))/y`
123
+ ans = gammasimp(eq)
124
+ assert gammasimp(eq.subs(x, fx)).subs(fx, x) == ans
125
+ assert gammasimp(eq.subs(x, cos(x))).subs(cos(x), x) == ans
126
+ assert 1/gammasimp(1/eq) == ans
127
+ assert gammasimp(fx.subs(x, eq)).args[0] == ans
lib/python3.12/site-packages/sympy/simplify/tests/test_hyperexpand.py ADDED
@@ -0,0 +1,1063 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.random import randrange
2
+
3
+ from sympy.simplify.hyperexpand import (ShiftA, ShiftB, UnShiftA, UnShiftB,
4
+ MeijerShiftA, MeijerShiftB, MeijerShiftC, MeijerShiftD,
5
+ MeijerUnShiftA, MeijerUnShiftB, MeijerUnShiftC,
6
+ MeijerUnShiftD,
7
+ ReduceOrder, reduce_order, apply_operators,
8
+ devise_plan, make_derivative_operator, Formula,
9
+ hyperexpand, Hyper_Function, G_Function,
10
+ reduce_order_meijer,
11
+ build_hypergeometric_formula)
12
+ from sympy.concrete.summations import Sum
13
+ from sympy.core.containers import Tuple
14
+ from sympy.core.expr import Expr
15
+ from sympy.core.numbers import I
16
+ from sympy.core.singleton import S
17
+ from sympy.core.symbol import symbols
18
+ from sympy.functions.combinatorial.factorials import binomial
19
+ from sympy.functions.elementary.piecewise import Piecewise
20
+ from sympy.functions.special.hyper import (hyper, meijerg)
21
+ from sympy.abc import z, a, b, c
22
+ from sympy.testing.pytest import XFAIL, raises, slow, tooslow
23
+ from sympy.core.random import verify_numerically as tn
24
+
25
+ from sympy.core.numbers import (Rational, pi)
26
+ from sympy.functions.elementary.exponential import (exp, exp_polar, log)
27
+ from sympy.functions.elementary.hyperbolic import atanh
28
+ from sympy.functions.elementary.miscellaneous import sqrt
29
+ from sympy.functions.elementary.trigonometric import (asin, cos, sin)
30
+ from sympy.functions.special.bessel import besseli
31
+ from sympy.functions.special.error_functions import erf
32
+ from sympy.functions.special.gamma_functions import (gamma, lowergamma)
33
+
34
+
35
+ def test_branch_bug():
36
+ assert hyperexpand(hyper((Rational(-1, 3), S.Half), (Rational(2, 3), Rational(3, 2)), -z)) == \
37
+ -z**S('1/3')*lowergamma(exp_polar(I*pi)/3, z)/5 \
38
+ + sqrt(pi)*erf(sqrt(z))/(5*sqrt(z))
39
+ assert hyperexpand(meijerg([Rational(7, 6), 1], [], [Rational(2, 3)], [Rational(1, 6), 0], z)) == \
40
+ 2*z**S('2/3')*(2*sqrt(pi)*erf(sqrt(z))/sqrt(z) - 2*lowergamma(
41
+ Rational(2, 3), z)/z**S('2/3'))*gamma(Rational(2, 3))/gamma(Rational(5, 3))
42
+
43
+
44
+ def test_hyperexpand():
45
+ # Luke, Y. L. (1969), The Special Functions and Their Approximations,
46
+ # Volume 1, section 6.2
47
+
48
+ assert hyperexpand(hyper([], [], z)) == exp(z)
49
+ assert hyperexpand(hyper([1, 1], [2], -z)*z) == log(1 + z)
50
+ assert hyperexpand(hyper([], [S.Half], -z**2/4)) == cos(z)
51
+ assert hyperexpand(z*hyper([], [S('3/2')], -z**2/4)) == sin(z)
52
+ assert hyperexpand(hyper([S('1/2'), S('1/2')], [S('3/2')], z**2)*z) \
53
+ == asin(z)
54
+ assert isinstance(Sum(binomial(2, z)*z**2, (z, 0, a)).doit(), Expr)
55
+
56
+
57
+ def can_do(ap, bq, numerical=True, div=1, lowerplane=False):
58
+ r = hyperexpand(hyper(ap, bq, z))
59
+ if r.has(hyper):
60
+ return False
61
+ if not numerical:
62
+ return True
63
+ repl = {}
64
+ randsyms = r.free_symbols - {z}
65
+ while randsyms:
66
+ # Only randomly generated parameters are checked.
67
+ for n, ai in enumerate(randsyms):
68
+ repl[ai] = randcplx(n)/div
69
+ if not any(b.is_Integer and b <= 0 for b in Tuple(*bq).subs(repl)):
70
+ break
71
+ [a, b, c, d] = [2, -1, 3, 1]
72
+ if lowerplane:
73
+ [a, b, c, d] = [2, -2, 3, -1]
74
+ return tn(
75
+ hyper(ap, bq, z).subs(repl),
76
+ r.replace(exp_polar, exp).subs(repl),
77
+ z, a=a, b=b, c=c, d=d)
78
+
79
+
80
+ def test_roach():
81
+ # Kelly B. Roach. Meijer G Function Representations.
82
+ # Section "Gallery"
83
+ assert can_do([S.Half], [Rational(9, 2)])
84
+ assert can_do([], [1, Rational(5, 2), 4])
85
+ assert can_do([Rational(-1, 2), 1, 2], [3, 4])
86
+ assert can_do([Rational(1, 3)], [Rational(-2, 3), Rational(-1, 2), S.Half, 1])
87
+ assert can_do([Rational(-3, 2), Rational(-1, 2)], [Rational(-5, 2), 1])
88
+ assert can_do([Rational(-3, 2), ], [Rational(-1, 2), S.Half]) # shine-integral
89
+ assert can_do([Rational(-3, 2), Rational(-1, 2)], [2]) # elliptic integrals
90
+
91
+
92
+ @XFAIL
93
+ def test_roach_fail():
94
+ assert can_do([Rational(-1, 2), 1], [Rational(1, 4), S.Half, Rational(3, 4)]) # PFDD
95
+ assert can_do([Rational(3, 2)], [Rational(5, 2), 5]) # struve function
96
+ assert can_do([Rational(-1, 2), S.Half, 1], [Rational(3, 2), Rational(5, 2)]) # polylog, pfdd
97
+ assert can_do([1, 2, 3], [S.Half, 4]) # XXX ?
98
+ assert can_do([S.Half], [Rational(-1, 3), Rational(-1, 2), Rational(-2, 3)]) # PFDD ?
99
+
100
+ # For the long table tests, see end of file
101
+
102
+
103
+ def test_polynomial():
104
+ from sympy.core.numbers import oo
105
+ assert hyperexpand(hyper([], [-1], z)) is oo
106
+ assert hyperexpand(hyper([-2], [-1], z)) is oo
107
+ assert hyperexpand(hyper([0, 0], [-1], z)) == 1
108
+ assert can_do([-5, -2, randcplx(), randcplx()], [-10, randcplx()])
109
+ assert hyperexpand(hyper((-1, 1), (-2,), z)) == 1 + z/2
110
+
111
+
112
+ def test_hyperexpand_bases():
113
+ assert hyperexpand(hyper([2], [a], z)) == \
114
+ a + z**(-a + 1)*(-a**2 + 3*a + z*(a - 1) - 2)*exp(z)* \
115
+ lowergamma(a - 1, z) - 1
116
+ # TODO [a+1, aRational(-1, 2)], [2*a]
117
+ assert hyperexpand(hyper([1, 2], [3], z)) == -2/z - 2*log(-z + 1)/z**2
118
+ assert hyperexpand(hyper([S.Half, 2], [Rational(3, 2)], z)) == \
119
+ -1/(2*z - 2) + atanh(sqrt(z))/sqrt(z)/2
120
+ assert hyperexpand(hyper([S.Half, S.Half], [Rational(5, 2)], z)) == \
121
+ (-3*z + 3)/4/(z*sqrt(-z + 1)) \
122
+ + (6*z - 3)*asin(sqrt(z))/(4*z**Rational(3, 2))
123
+ assert hyperexpand(hyper([1, 2], [Rational(3, 2)], z)) == -1/(2*z - 2) \
124
+ - asin(sqrt(z))/(sqrt(z)*(2*z - 2)*sqrt(-z + 1))
125
+ assert hyperexpand(hyper([Rational(-1, 2) - 1, 1, 2], [S.Half, 3], z)) == \
126
+ sqrt(z)*(z*Rational(6, 7) - Rational(6, 5))*atanh(sqrt(z)) \
127
+ + (-30*z**2 + 32*z - 6)/35/z - 6*log(-z + 1)/(35*z**2)
128
+ assert hyperexpand(hyper([1 + S.Half, 1, 1], [2, 2], z)) == \
129
+ -4*log(sqrt(-z + 1)/2 + S.Half)/z
130
+ # TODO hyperexpand(hyper([a], [2*a + 1], z))
131
+ # TODO [S.Half, a], [Rational(3, 2), a+1]
132
+ assert hyperexpand(hyper([2], [b, 1], z)) == \
133
+ z**(-b/2 + S.Half)*besseli(b - 1, 2*sqrt(z))*gamma(b) \
134
+ + z**(-b/2 + 1)*besseli(b, 2*sqrt(z))*gamma(b)
135
+ # TODO [a], [a - S.Half, 2*a]
136
+
137
+
138
+ def test_hyperexpand_parametric():
139
+ assert hyperexpand(hyper([a, S.Half + a], [S.Half], z)) \
140
+ == (1 + sqrt(z))**(-2*a)/2 + (1 - sqrt(z))**(-2*a)/2
141
+ assert hyperexpand(hyper([a, Rational(-1, 2) + a], [2*a], z)) \
142
+ == 2**(2*a - 1)*((-z + 1)**S.Half + 1)**(-2*a + 1)
143
+
144
+
145
+ def test_shifted_sum():
146
+ from sympy.simplify.simplify import simplify
147
+ assert simplify(hyperexpand(z**4*hyper([2], [3, S('3/2')], -z**2))) \
148
+ == z*sin(2*z) + (-z**2 + S.Half)*cos(2*z) - S.Half
149
+
150
+
151
+ def _randrat():
152
+ """ Steer clear of integers. """
153
+ return S(randrange(25) + 10)/50
154
+
155
+
156
+ def randcplx(offset=-1):
157
+ """ Polys is not good with real coefficients. """
158
+ return _randrat() + I*_randrat() + I*(1 + offset)
159
+
160
+
161
+ @slow
162
+ def test_formulae():
163
+ from sympy.simplify.hyperexpand import FormulaCollection
164
+ formulae = FormulaCollection().formulae
165
+ for formula in formulae:
166
+ h = formula.func(formula.z)
167
+ rep = {}
168
+ for n, sym in enumerate(formula.symbols):
169
+ rep[sym] = randcplx(n)
170
+
171
+ # NOTE hyperexpand returns truly branched functions. We know we are
172
+ # on the main sheet, but numerical evaluation can still go wrong
173
+ # (e.g. if exp_polar cannot be evalf'd).
174
+ # Just replace all exp_polar by exp, this usually works.
175
+
176
+ # first test if the closed-form is actually correct
177
+ h = h.subs(rep)
178
+ closed_form = formula.closed_form.subs(rep).rewrite('nonrepsmall')
179
+ z = formula.z
180
+ assert tn(h, closed_form.replace(exp_polar, exp), z)
181
+
182
+ # now test the computed matrix
183
+ cl = (formula.C * formula.B)[0].subs(rep).rewrite('nonrepsmall')
184
+ assert tn(closed_form.replace(
185
+ exp_polar, exp), cl.replace(exp_polar, exp), z)
186
+ deriv1 = z*formula.B.applyfunc(lambda t: t.rewrite(
187
+ 'nonrepsmall')).diff(z)
188
+ deriv2 = formula.M * formula.B
189
+ for d1, d2 in zip(deriv1, deriv2):
190
+ assert tn(d1.subs(rep).replace(exp_polar, exp),
191
+ d2.subs(rep).rewrite('nonrepsmall').replace(exp_polar, exp), z)
192
+
193
+
194
+ def test_meijerg_formulae():
195
+ from sympy.simplify.hyperexpand import MeijerFormulaCollection
196
+ formulae = MeijerFormulaCollection().formulae
197
+ for sig in formulae:
198
+ for formula in formulae[sig]:
199
+ g = meijerg(formula.func.an, formula.func.ap,
200
+ formula.func.bm, formula.func.bq,
201
+ formula.z)
202
+ rep = {}
203
+ for sym in formula.symbols:
204
+ rep[sym] = randcplx()
205
+
206
+ # first test if the closed-form is actually correct
207
+ g = g.subs(rep)
208
+ closed_form = formula.closed_form.subs(rep)
209
+ z = formula.z
210
+ assert tn(g, closed_form, z)
211
+
212
+ # now test the computed matrix
213
+ cl = (formula.C * formula.B)[0].subs(rep)
214
+ assert tn(closed_form, cl, z)
215
+ deriv1 = z*formula.B.diff(z)
216
+ deriv2 = formula.M * formula.B
217
+ for d1, d2 in zip(deriv1, deriv2):
218
+ assert tn(d1.subs(rep), d2.subs(rep), z)
219
+
220
+
221
+ def op(f):
222
+ return z*f.diff(z)
223
+
224
+
225
+ def test_plan():
226
+ assert devise_plan(Hyper_Function([0], ()),
227
+ Hyper_Function([0], ()), z) == []
228
+ with raises(ValueError):
229
+ devise_plan(Hyper_Function([1], ()), Hyper_Function((), ()), z)
230
+ with raises(ValueError):
231
+ devise_plan(Hyper_Function([2], [1]), Hyper_Function([2], [2]), z)
232
+ with raises(ValueError):
233
+ devise_plan(Hyper_Function([2], []), Hyper_Function([S("1/2")], []), z)
234
+
235
+ # We cannot use pi/(10000 + n) because polys is insanely slow.
236
+ a1, a2, b1 = (randcplx(n) for n in range(3))
237
+ b1 += 2*I
238
+ h = hyper([a1, a2], [b1], z)
239
+
240
+ h2 = hyper((a1 + 1, a2), [b1], z)
241
+ assert tn(apply_operators(h,
242
+ devise_plan(Hyper_Function((a1 + 1, a2), [b1]),
243
+ Hyper_Function((a1, a2), [b1]), z), op),
244
+ h2, z)
245
+
246
+ h2 = hyper((a1 + 1, a2 - 1), [b1], z)
247
+ assert tn(apply_operators(h,
248
+ devise_plan(Hyper_Function((a1 + 1, a2 - 1), [b1]),
249
+ Hyper_Function((a1, a2), [b1]), z), op),
250
+ h2, z)
251
+
252
+
253
+ def test_plan_derivatives():
254
+ a1, a2, a3 = 1, 2, S('1/2')
255
+ b1, b2 = 3, S('5/2')
256
+ h = Hyper_Function((a1, a2, a3), (b1, b2))
257
+ h2 = Hyper_Function((a1 + 1, a2 + 1, a3 + 2), (b1 + 1, b2 + 1))
258
+ ops = devise_plan(h2, h, z)
259
+ f = Formula(h, z, h(z), [])
260
+ deriv = make_derivative_operator(f.M, z)
261
+ assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2(z), z)
262
+
263
+ h2 = Hyper_Function((a1, a2 - 1, a3 - 2), (b1 - 1, b2 - 1))
264
+ ops = devise_plan(h2, h, z)
265
+ assert tn((apply_operators(f.C, ops, deriv)*f.B)[0], h2(z), z)
266
+
267
+
268
+ def test_reduction_operators():
269
+ a1, a2, b1 = (randcplx(n) for n in range(3))
270
+ h = hyper([a1], [b1], z)
271
+
272
+ assert ReduceOrder(2, 0) is None
273
+ assert ReduceOrder(2, -1) is None
274
+ assert ReduceOrder(1, S('1/2')) is None
275
+
276
+ h2 = hyper((a1, a2), (b1, a2), z)
277
+ assert tn(ReduceOrder(a2, a2).apply(h, op), h2, z)
278
+
279
+ h2 = hyper((a1, a2 + 1), (b1, a2), z)
280
+ assert tn(ReduceOrder(a2 + 1, a2).apply(h, op), h2, z)
281
+
282
+ h2 = hyper((a2 + 4, a1), (b1, a2), z)
283
+ assert tn(ReduceOrder(a2 + 4, a2).apply(h, op), h2, z)
284
+
285
+ # test several step order reduction
286
+ ap = (a2 + 4, a1, b1 + 1)
287
+ bq = (a2, b1, b1)
288
+ func, ops = reduce_order(Hyper_Function(ap, bq))
289
+ assert func.ap == (a1,)
290
+ assert func.bq == (b1,)
291
+ assert tn(apply_operators(h, ops, op), hyper(ap, bq, z), z)
292
+
293
+
294
+ def test_shift_operators():
295
+ a1, a2, b1, b2, b3 = (randcplx(n) for n in range(5))
296
+ h = hyper((a1, a2), (b1, b2, b3), z)
297
+
298
+ raises(ValueError, lambda: ShiftA(0))
299
+ raises(ValueError, lambda: ShiftB(1))
300
+
301
+ assert tn(ShiftA(a1).apply(h, op), hyper((a1 + 1, a2), (b1, b2, b3), z), z)
302
+ assert tn(ShiftA(a2).apply(h, op), hyper((a1, a2 + 1), (b1, b2, b3), z), z)
303
+ assert tn(ShiftB(b1).apply(h, op), hyper((a1, a2), (b1 - 1, b2, b3), z), z)
304
+ assert tn(ShiftB(b2).apply(h, op), hyper((a1, a2), (b1, b2 - 1, b3), z), z)
305
+ assert tn(ShiftB(b3).apply(h, op), hyper((a1, a2), (b1, b2, b3 - 1), z), z)
306
+
307
+
308
+ def test_ushift_operators():
309
+ a1, a2, b1, b2, b3 = (randcplx(n) for n in range(5))
310
+ h = hyper((a1, a2), (b1, b2, b3), z)
311
+
312
+ raises(ValueError, lambda: UnShiftA((1,), (), 0, z))
313
+ raises(ValueError, lambda: UnShiftB((), (-1,), 0, z))
314
+ raises(ValueError, lambda: UnShiftA((1,), (0, -1, 1), 0, z))
315
+ raises(ValueError, lambda: UnShiftB((0, 1), (1,), 0, z))
316
+
317
+ s = UnShiftA((a1, a2), (b1, b2, b3), 0, z)
318
+ assert tn(s.apply(h, op), hyper((a1 - 1, a2), (b1, b2, b3), z), z)
319
+ s = UnShiftA((a1, a2), (b1, b2, b3), 1, z)
320
+ assert tn(s.apply(h, op), hyper((a1, a2 - 1), (b1, b2, b3), z), z)
321
+
322
+ s = UnShiftB((a1, a2), (b1, b2, b3), 0, z)
323
+ assert tn(s.apply(h, op), hyper((a1, a2), (b1 + 1, b2, b3), z), z)
324
+ s = UnShiftB((a1, a2), (b1, b2, b3), 1, z)
325
+ assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2 + 1, b3), z), z)
326
+ s = UnShiftB((a1, a2), (b1, b2, b3), 2, z)
327
+ assert tn(s.apply(h, op), hyper((a1, a2), (b1, b2, b3 + 1), z), z)
328
+
329
+
330
+ def can_do_meijer(a1, a2, b1, b2, numeric=True):
331
+ """
332
+ This helper function tries to hyperexpand() the meijer g-function
333
+ corresponding to the parameters a1, a2, b1, b2.
334
+ It returns False if this expansion still contains g-functions.
335
+ If numeric is True, it also tests the so-obtained formula numerically
336
+ (at random values) and returns False if the test fails.
337
+ Else it returns True.
338
+ """
339
+ from sympy.core.function import expand
340
+ from sympy.functions.elementary.complexes import unpolarify
341
+ r = hyperexpand(meijerg(a1, a2, b1, b2, z))
342
+ if r.has(meijerg):
343
+ return False
344
+ # NOTE hyperexpand() returns a truly branched function, whereas numerical
345
+ # evaluation only works on the main branch. Since we are evaluating on
346
+ # the main branch, this should not be a problem, but expressions like
347
+ # exp_polar(I*pi/2*x)**a are evaluated incorrectly. We thus have to get
348
+ # rid of them. The expand heuristically does this...
349
+ r = unpolarify(expand(r, force=True, power_base=True, power_exp=False,
350
+ mul=False, log=False, multinomial=False, basic=False))
351
+
352
+ if not numeric:
353
+ return True
354
+
355
+ repl = {}
356
+ for n, ai in enumerate(meijerg(a1, a2, b1, b2, z).free_symbols - {z}):
357
+ repl[ai] = randcplx(n)
358
+ return tn(meijerg(a1, a2, b1, b2, z).subs(repl), r.subs(repl), z)
359
+
360
+
361
+ @slow
362
+ def test_meijerg_expand():
363
+ from sympy.simplify.gammasimp import gammasimp
364
+ from sympy.simplify.simplify import simplify
365
+ # from mpmath docs
366
+ assert hyperexpand(meijerg([[], []], [[0], []], -z)) == exp(z)
367
+
368
+ assert hyperexpand(meijerg([[1, 1], []], [[1], [0]], z)) == \
369
+ log(z + 1)
370
+ assert hyperexpand(meijerg([[1, 1], []], [[1], [1]], z)) == \
371
+ z/(z + 1)
372
+ assert hyperexpand(meijerg([[], []], [[S.Half], [0]], (z/2)**2)) \
373
+ == sin(z)/sqrt(pi)
374
+ assert hyperexpand(meijerg([[], []], [[0], [S.Half]], (z/2)**2)) \
375
+ == cos(z)/sqrt(pi)
376
+ assert can_do_meijer([], [a], [a - 1, a - S.Half], [])
377
+ assert can_do_meijer([], [], [a/2], [-a/2], False) # branches...
378
+ assert can_do_meijer([a], [b], [a], [b, a - 1])
379
+
380
+ # wikipedia
381
+ assert hyperexpand(meijerg([1], [], [], [0], z)) == \
382
+ Piecewise((0, abs(z) < 1), (1, abs(1/z) < 1),
383
+ (meijerg([1], [], [], [0], z), True))
384
+ assert hyperexpand(meijerg([], [1], [0], [], z)) == \
385
+ Piecewise((1, abs(z) < 1), (0, abs(1/z) < 1),
386
+ (meijerg([], [1], [0], [], z), True))
387
+
388
+ # The Special Functions and their Approximations
389
+ assert can_do_meijer([], [], [a + b/2], [a, a - b/2, a + S.Half])
390
+ assert can_do_meijer(
391
+ [], [], [a], [b], False) # branches only agree for small z
392
+ assert can_do_meijer([], [S.Half], [a], [-a])
393
+ assert can_do_meijer([], [], [a, b], [])
394
+ assert can_do_meijer([], [], [a, b], [])
395
+ assert can_do_meijer([], [], [a, a + S.Half], [b, b + S.Half])
396
+ assert can_do_meijer([], [], [a, -a], [0, S.Half], False) # dito
397
+ assert can_do_meijer([], [], [a, a + S.Half, b, b + S.Half], [])
398
+ assert can_do_meijer([S.Half], [], [0], [a, -a])
399
+ assert can_do_meijer([S.Half], [], [a], [0, -a], False) # dito
400
+ assert can_do_meijer([], [a - S.Half], [a, b], [a - S.Half], False)
401
+ assert can_do_meijer([], [a + S.Half], [a + b, a - b, a], [], False)
402
+ assert can_do_meijer([a + S.Half], [], [b, 2*a - b, a], [], False)
403
+
404
+ # This for example is actually zero.
405
+ assert can_do_meijer([], [], [], [a, b])
406
+
407
+ # Testing a bug:
408
+ assert hyperexpand(meijerg([0, 2], [], [], [-1, 1], z)) == \
409
+ Piecewise((0, abs(z) < 1),
410
+ (z*(1 - 1/z**2)/2, abs(1/z) < 1),
411
+ (meijerg([0, 2], [], [], [-1, 1], z), True))
412
+
413
+ # Test that the simplest possible answer is returned:
414
+ assert gammasimp(simplify(hyperexpand(
415
+ meijerg([1], [1 - a], [-a/2, -a/2 + S.Half], [], 1/z)))) == \
416
+ -2*sqrt(pi)*(sqrt(z + 1) + 1)**a/a
417
+
418
+ # Test that hyper is returned
419
+ assert hyperexpand(meijerg([1], [], [a], [0, 0], z)) == hyper(
420
+ (a,), (a + 1, a + 1), z*exp_polar(I*pi))*z**a*gamma(a)/gamma(a + 1)**2
421
+
422
+ # Test place option
423
+ f = meijerg(((0, 1), ()), ((S.Half,), (0,)), z**2)
424
+ assert hyperexpand(f) == sqrt(pi)/sqrt(1 + z**(-2))
425
+ assert hyperexpand(f, place=0) == sqrt(pi)*z/sqrt(z**2 + 1)
426
+
427
+
428
+ def test_meijerg_lookup():
429
+ from sympy.functions.special.error_functions import (Ci, Si)
430
+ from sympy.functions.special.gamma_functions import uppergamma
431
+ assert hyperexpand(meijerg([a], [], [b, a], [], z)) == \
432
+ z**b*exp(z)*gamma(-a + b + 1)*uppergamma(a - b, z)
433
+ assert hyperexpand(meijerg([0], [], [0, 0], [], z)) == \
434
+ exp(z)*uppergamma(0, z)
435
+ assert can_do_meijer([a], [], [b, a + 1], [])
436
+ assert can_do_meijer([a], [], [b + 2, a], [])
437
+ assert can_do_meijer([a], [], [b - 2, a], [])
438
+
439
+ assert hyperexpand(meijerg([a], [], [a, a, a - S.Half], [], z)) == \
440
+ -sqrt(pi)*z**(a - S.Half)*(2*cos(2*sqrt(z))*(Si(2*sqrt(z)) - pi/2)
441
+ - 2*sin(2*sqrt(z))*Ci(2*sqrt(z))) == \
442
+ hyperexpand(meijerg([a], [], [a, a - S.Half, a], [], z)) == \
443
+ hyperexpand(meijerg([a], [], [a - S.Half, a, a], [], z))
444
+ assert can_do_meijer([a - 1], [], [a + 2, a - Rational(3, 2), a + 1], [])
445
+
446
+
447
+ @XFAIL
448
+ def test_meijerg_expand_fail():
449
+ # These basically test hyper([], [1/2 - a, 1/2 + 1, 1/2], z),
450
+ # which is *very* messy. But since the meijer g actually yields a
451
+ # sum of bessel functions, things can sometimes be simplified a lot and
452
+ # are then put into tables...
453
+ assert can_do_meijer([], [], [a + S.Half], [a, a - b/2, a + b/2])
454
+ assert can_do_meijer([], [], [0, S.Half], [a, -a])
455
+ assert can_do_meijer([], [], [3*a - S.Half, a, -a - S.Half], [a - S.Half])
456
+ assert can_do_meijer([], [], [0, a - S.Half, -a - S.Half], [S.Half])
457
+ assert can_do_meijer([], [], [a, b + S.Half, b], [2*b - a])
458
+ assert can_do_meijer([], [], [a, b + S.Half, b, 2*b - a])
459
+ assert can_do_meijer([S.Half], [], [-a, a], [0])
460
+
461
+
462
+ @slow
463
+ def test_meijerg():
464
+ # carefully set up the parameters.
465
+ # NOTE: this used to fail sometimes. I believe it is fixed, but if you
466
+ # hit an inexplicable test failure here, please let me know the seed.
467
+ a1, a2 = (randcplx(n) - 5*I - n*I for n in range(2))
468
+ b1, b2 = (randcplx(n) + 5*I + n*I for n in range(2))
469
+ b3, b4, b5, a3, a4, a5 = (randcplx() for n in range(6))
470
+ g = meijerg([a1], [a3, a4], [b1], [b3, b4], z)
471
+
472
+ assert ReduceOrder.meijer_minus(3, 4) is None
473
+ assert ReduceOrder.meijer_plus(4, 3) is None
474
+
475
+ g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2], z)
476
+ assert tn(ReduceOrder.meijer_plus(a2, a2).apply(g, op), g2, z)
477
+
478
+ g2 = meijerg([a1, a2], [a3, a4], [b1], [b3, b4, a2 + 1], z)
479
+ assert tn(ReduceOrder.meijer_plus(a2, a2 + 1).apply(g, op), g2, z)
480
+
481
+ g2 = meijerg([a1, a2 - 1], [a3, a4], [b1], [b3, b4, a2 + 2], z)
482
+ assert tn(ReduceOrder.meijer_plus(a2 - 1, a2 + 2).apply(g, op), g2, z)
483
+
484
+ g2 = meijerg([a1], [a3, a4, b2 - 1], [b1, b2 + 2], [b3, b4], z)
485
+ assert tn(ReduceOrder.meijer_minus(
486
+ b2 + 2, b2 - 1).apply(g, op), g2, z, tol=1e-6)
487
+
488
+ # test several-step reduction
489
+ an = [a1, a2]
490
+ bq = [b3, b4, a2 + 1]
491
+ ap = [a3, a4, b2 - 1]
492
+ bm = [b1, b2 + 1]
493
+ niq, ops = reduce_order_meijer(G_Function(an, ap, bm, bq))
494
+ assert niq.an == (a1,)
495
+ assert set(niq.ap) == {a3, a4}
496
+ assert niq.bm == (b1,)
497
+ assert set(niq.bq) == {b3, b4}
498
+ assert tn(apply_operators(g, ops, op), meijerg(an, ap, bm, bq, z), z)
499
+
500
+
501
+ def test_meijerg_shift_operators():
502
+ # carefully set up the parameters. XXX this still fails sometimes
503
+ a1, a2, a3, a4, a5, b1, b2, b3, b4, b5 = (randcplx(n) for n in range(10))
504
+ g = meijerg([a1], [a3, a4], [b1], [b3, b4], z)
505
+
506
+ assert tn(MeijerShiftA(b1).apply(g, op),
507
+ meijerg([a1], [a3, a4], [b1 + 1], [b3, b4], z), z)
508
+ assert tn(MeijerShiftB(a1).apply(g, op),
509
+ meijerg([a1 - 1], [a3, a4], [b1], [b3, b4], z), z)
510
+ assert tn(MeijerShiftC(b3).apply(g, op),
511
+ meijerg([a1], [a3, a4], [b1], [b3 + 1, b4], z), z)
512
+ assert tn(MeijerShiftD(a3).apply(g, op),
513
+ meijerg([a1], [a3 - 1, a4], [b1], [b3, b4], z), z)
514
+
515
+ s = MeijerUnShiftA([a1], [a3, a4], [b1], [b3, b4], 0, z)
516
+ assert tn(
517
+ s.apply(g, op), meijerg([a1], [a3, a4], [b1 - 1], [b3, b4], z), z)
518
+
519
+ s = MeijerUnShiftC([a1], [a3, a4], [b1], [b3, b4], 0, z)
520
+ assert tn(
521
+ s.apply(g, op), meijerg([a1], [a3, a4], [b1], [b3 - 1, b4], z), z)
522
+
523
+ s = MeijerUnShiftB([a1], [a3, a4], [b1], [b3, b4], 0, z)
524
+ assert tn(
525
+ s.apply(g, op), meijerg([a1 + 1], [a3, a4], [b1], [b3, b4], z), z)
526
+
527
+ s = MeijerUnShiftD([a1], [a3, a4], [b1], [b3, b4], 0, z)
528
+ assert tn(
529
+ s.apply(g, op), meijerg([a1], [a3 + 1, a4], [b1], [b3, b4], z), z)
530
+
531
+
532
+ @slow
533
+ def test_meijerg_confluence():
534
+ def t(m, a, b):
535
+ from sympy.core.sympify import sympify
536
+ a, b = sympify([a, b])
537
+ m_ = m
538
+ m = hyperexpand(m)
539
+ if not m == Piecewise((a, abs(z) < 1), (b, abs(1/z) < 1), (m_, True)):
540
+ return False
541
+ if not (m.args[0].args[0] == a and m.args[1].args[0] == b):
542
+ return False
543
+ z0 = randcplx()/10
544
+ if abs(m.subs(z, z0).n() - a.subs(z, z0).n()).n() > 1e-10:
545
+ return False
546
+ if abs(m.subs(z, 1/z0).n() - b.subs(z, 1/z0).n()).n() > 1e-10:
547
+ return False
548
+ return True
549
+
550
+ assert t(meijerg([], [1, 1], [0, 0], [], z), -log(z), 0)
551
+ assert t(meijerg(
552
+ [], [3, 1], [0, 0], [], z), -z**2/4 + z - log(z)/2 - Rational(3, 4), 0)
553
+ assert t(meijerg([], [3, 1], [-1, 0], [], z),
554
+ z**2/12 - z/2 + log(z)/2 + Rational(1, 4) + 1/(6*z), 0)
555
+ assert t(meijerg([], [1, 1, 1, 1], [0, 0, 0, 0], [], z), -log(z)**3/6, 0)
556
+ assert t(meijerg([1, 1], [], [], [0, 0], z), 0, -log(1/z))
557
+ assert t(meijerg([1, 1], [2, 2], [1, 1], [0, 0], z),
558
+ -z*log(z) + 2*z, -log(1/z) + 2)
559
+ assert t(meijerg([S.Half], [1, 1], [0, 0], [Rational(3, 2)], z), log(z)/2 - 1, 0)
560
+
561
+ def u(an, ap, bm, bq):
562
+ m = meijerg(an, ap, bm, bq, z)
563
+ m2 = hyperexpand(m, allow_hyper=True)
564
+ if m2.has(meijerg) and not (m2.is_Piecewise and len(m2.args) == 3):
565
+ return False
566
+ return tn(m, m2, z)
567
+ assert u([], [1], [0, 0], [])
568
+ assert u([1, 1], [], [], [0])
569
+ assert u([1, 1], [2, 2, 5], [1, 1, 6], [0, 0])
570
+ assert u([1, 1], [2, 2, 5], [1, 1, 6], [0])
571
+
572
+
573
+ def test_meijerg_with_Floats():
574
+ # see issue #10681
575
+ from sympy.polys.domains.realfield import RR
576
+ f = meijerg(((3.0, 1), ()), ((Rational(3, 2),), (0,)), z)
577
+ a = -2.3632718012073
578
+ g = a*z**Rational(3, 2)*hyper((-0.5, Rational(3, 2)), (Rational(5, 2),), z*exp_polar(I*pi))
579
+ assert RR.almosteq((hyperexpand(f)/g).n(), 1.0, 1e-12)
580
+
581
+
582
+ def test_lerchphi():
583
+ from sympy.functions.special.zeta_functions import (lerchphi, polylog)
584
+ from sympy.simplify.gammasimp import gammasimp
585
+ assert hyperexpand(hyper([1, a], [a + 1], z)/a) == lerchphi(z, 1, a)
586
+ assert hyperexpand(
587
+ hyper([1, a, a], [a + 1, a + 1], z)/a**2) == lerchphi(z, 2, a)
588
+ assert hyperexpand(hyper([1, a, a, a], [a + 1, a + 1, a + 1], z)/a**3) == \
589
+ lerchphi(z, 3, a)
590
+ assert hyperexpand(hyper([1] + [a]*10, [a + 1]*10, z)/a**10) == \
591
+ lerchphi(z, 10, a)
592
+ assert gammasimp(hyperexpand(meijerg([0, 1 - a], [], [0],
593
+ [-a], exp_polar(-I*pi)*z))) == lerchphi(z, 1, a)
594
+ assert gammasimp(hyperexpand(meijerg([0, 1 - a, 1 - a], [], [0],
595
+ [-a, -a], exp_polar(-I*pi)*z))) == lerchphi(z, 2, a)
596
+ assert gammasimp(hyperexpand(meijerg([0, 1 - a, 1 - a, 1 - a], [], [0],
597
+ [-a, -a, -a], exp_polar(-I*pi)*z))) == lerchphi(z, 3, a)
598
+
599
+ assert hyperexpand(z*hyper([1, 1], [2], z)) == -log(1 + -z)
600
+ assert hyperexpand(z*hyper([1, 1, 1], [2, 2], z)) == polylog(2, z)
601
+ assert hyperexpand(z*hyper([1, 1, 1, 1], [2, 2, 2], z)) == polylog(3, z)
602
+
603
+ assert hyperexpand(hyper([1, a, 1 + S.Half], [a + 1, S.Half], z)) == \
604
+ -2*a/(z - 1) + (-2*a**2 + a)*lerchphi(z, 1, a)
605
+
606
+ # Now numerical tests. These make sure reductions etc are carried out
607
+ # correctly
608
+
609
+ # a rational function (polylog at negative integer order)
610
+ assert can_do([2, 2, 2], [1, 1])
611
+
612
+ # NOTE these contain log(1-x) etc ... better make sure we have |z| < 1
613
+ # reduction of order for polylog
614
+ assert can_do([1, 1, 1, b + 5], [2, 2, b], div=10)
615
+
616
+ # reduction of order for lerchphi
617
+ # XXX lerchphi in mpmath is flaky
618
+ assert can_do(
619
+ [1, a, a, a, b + 5], [a + 1, a + 1, a + 1, b], numerical=False)
620
+
621
+ # test a bug
622
+ from sympy.functions.elementary.complexes import Abs
623
+ assert hyperexpand(hyper([S.Half, S.Half, S.Half, 1],
624
+ [Rational(3, 2), Rational(3, 2), Rational(3, 2)], Rational(1, 4))) == \
625
+ Abs(-polylog(3, exp_polar(I*pi)/2) + polylog(3, S.Half))
626
+
627
+
628
+ def test_partial_simp():
629
+ # First test that hypergeometric function formulae work.
630
+ a, b, c, d, e = (randcplx() for _ in range(5))
631
+ for func in [Hyper_Function([a, b, c], [d, e]),
632
+ Hyper_Function([], [a, b, c, d, e])]:
633
+ f = build_hypergeometric_formula(func)
634
+ z = f.z
635
+ assert f.closed_form == func(z)
636
+ deriv1 = f.B.diff(z)*z
637
+ deriv2 = f.M*f.B
638
+ for func1, func2 in zip(deriv1, deriv2):
639
+ assert tn(func1, func2, z)
640
+
641
+ # Now test that formulae are partially simplified.
642
+ a, b, z = symbols('a b z')
643
+ assert hyperexpand(hyper([3, a], [1, b], z)) == \
644
+ (-a*b/2 + a*z/2 + 2*a)*hyper([a + 1], [b], z) \
645
+ + (a*b/2 - 2*a + 1)*hyper([a], [b], z)
646
+ assert tn(
647
+ hyperexpand(hyper([3, d], [1, e], z)), hyper([3, d], [1, e], z), z)
648
+ assert hyperexpand(hyper([3], [1, a, b], z)) == \
649
+ hyper((), (a, b), z) \
650
+ + z*hyper((), (a + 1, b), z)/(2*a) \
651
+ - z*(b - 4)*hyper((), (a + 1, b + 1), z)/(2*a*b)
652
+ assert tn(
653
+ hyperexpand(hyper([3], [1, d, e], z)), hyper([3], [1, d, e], z), z)
654
+
655
+
656
+ def test_hyperexpand_special():
657
+ assert hyperexpand(hyper([a, b], [c], 1)) == \
658
+ gamma(c)*gamma(c - a - b)/gamma(c - a)/gamma(c - b)
659
+ assert hyperexpand(hyper([a, b], [1 + a - b], -1)) == \
660
+ gamma(1 + a/2)*gamma(1 + a - b)/gamma(1 + a)/gamma(1 + a/2 - b)
661
+ assert hyperexpand(hyper([a, b], [1 + b - a], -1)) == \
662
+ gamma(1 + b/2)*gamma(1 + b - a)/gamma(1 + b)/gamma(1 + b/2 - a)
663
+ assert hyperexpand(meijerg([1 - z - a/2], [1 - z + a/2], [b/2], [-b/2], 1)) == \
664
+ gamma(1 - 2*z)*gamma(z + a/2 + b/2)/gamma(1 - z + a/2 - b/2) \
665
+ /gamma(1 - z - a/2 + b/2)/gamma(1 - z + a/2 + b/2)
666
+ assert hyperexpand(hyper([a], [b], 0)) == 1
667
+ assert hyper([a], [b], 0) != 0
668
+
669
+
670
+ def test_Mod1_behavior():
671
+ from sympy.core.symbol import Symbol
672
+ from sympy.simplify.simplify import simplify
673
+ n = Symbol('n', integer=True)
674
+ # Note: this should not hang.
675
+ assert simplify(hyperexpand(meijerg([1], [], [n + 1], [0], z))) == \
676
+ lowergamma(n + 1, z)
677
+
678
+
679
+ @slow
680
+ def test_prudnikov_misc():
681
+ assert can_do([1, (3 + I)/2, (3 - I)/2], [Rational(3, 2), 2])
682
+ assert can_do([S.Half, a - 1], [Rational(3, 2), a + 1], lowerplane=True)
683
+ assert can_do([], [b + 1])
684
+ assert can_do([a], [a - 1, b + 1])
685
+
686
+ assert can_do([a], [a - S.Half, 2*a])
687
+ assert can_do([a], [a - S.Half, 2*a + 1])
688
+ assert can_do([a], [a - S.Half, 2*a - 1])
689
+ assert can_do([a], [a + S.Half, 2*a])
690
+ assert can_do([a], [a + S.Half, 2*a + 1])
691
+ assert can_do([a], [a + S.Half, 2*a - 1])
692
+ assert can_do([S.Half], [b, 2 - b])
693
+ assert can_do([S.Half], [b, 3 - b])
694
+ assert can_do([1], [2, b])
695
+
696
+ assert can_do([a, a + S.Half], [2*a, b, 2*a - b + 1])
697
+ assert can_do([a, a + S.Half], [S.Half, 2*a, 2*a + S.Half])
698
+ assert can_do([a], [a + 1], lowerplane=True) # lowergamma
699
+
700
+
701
+ def test_prudnikov_1():
702
+ # A. P. Prudnikov, Yu. A. Brychkov and O. I. Marichev (1990).
703
+ # Integrals and Series: More Special Functions, Vol. 3,.
704
+ # Gordon and Breach Science Publisher
705
+
706
+ # 7.3.1
707
+ assert can_do([a, -a], [S.Half])
708
+ assert can_do([a, 1 - a], [S.Half])
709
+ assert can_do([a, 1 - a], [Rational(3, 2)])
710
+ assert can_do([a, 2 - a], [S.Half])
711
+ assert can_do([a, 2 - a], [Rational(3, 2)])
712
+ assert can_do([a, 2 - a], [Rational(3, 2)])
713
+ assert can_do([a, a + S.Half], [2*a - 1])
714
+ assert can_do([a, a + S.Half], [2*a])
715
+ assert can_do([a, a + S.Half], [2*a + 1])
716
+ assert can_do([a, a + S.Half], [S.Half])
717
+ assert can_do([a, a + S.Half], [Rational(3, 2)])
718
+ assert can_do([a, a/2 + 1], [a/2])
719
+ assert can_do([1, b], [2])
720
+ assert can_do([1, b], [b + 1], numerical=False) # Lerch Phi
721
+ # NOTE: branches are complicated for |z| > 1
722
+
723
+ assert can_do([a], [2*a])
724
+ assert can_do([a], [2*a + 1])
725
+ assert can_do([a], [2*a - 1])
726
+
727
+
728
+ @slow
729
+ def test_prudnikov_2():
730
+ h = S.Half
731
+ assert can_do([-h, -h], [h])
732
+ assert can_do([-h, h], [3*h])
733
+ assert can_do([-h, h], [5*h])
734
+ assert can_do([-h, h], [7*h])
735
+ assert can_do([-h, 1], [h])
736
+
737
+ for p in [-h, h]:
738
+ for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]:
739
+ for m in [-h, h, 3*h, 5*h, 7*h]:
740
+ assert can_do([p, n], [m])
741
+ for n in [1, 2, 3, 4]:
742
+ for m in [1, 2, 3, 4]:
743
+ assert can_do([p, n], [m])
744
+
745
+
746
+ def test_prudnikov_3():
747
+ h = S.Half
748
+ assert can_do([Rational(1, 4), Rational(3, 4)], [h])
749
+ assert can_do([Rational(1, 4), Rational(3, 4)], [3*h])
750
+ assert can_do([Rational(1, 3), Rational(2, 3)], [3*h])
751
+ assert can_do([Rational(3, 4), Rational(5, 4)], [h])
752
+ assert can_do([Rational(3, 4), Rational(5, 4)], [3*h])
753
+
754
+
755
+ @tooslow
756
+ def test_prudnikov_3_slow():
757
+ # XXX: This is marked as tooslow and hence skipped in CI. None of the
758
+ # individual cases below fails or hangs. Some cases are slow and the loops
759
+ # below generate 280 different cases. Is it really necessary to test all
760
+ # 280 cases here?
761
+ h = S.Half
762
+ for p in [1, 2, 3, 4]:
763
+ for n in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4, 9*h]:
764
+ for m in [1, 3*h, 2, 5*h, 3, 7*h, 4]:
765
+ assert can_do([p, m], [n])
766
+
767
+
768
+ @slow
769
+ def test_prudnikov_4():
770
+ h = S.Half
771
+ for p in [3*h, 5*h, 7*h]:
772
+ for n in [-h, h, 3*h, 5*h, 7*h]:
773
+ for m in [3*h, 2, 5*h, 3, 7*h, 4]:
774
+ assert can_do([p, m], [n])
775
+ for n in [1, 2, 3, 4]:
776
+ for m in [2, 3, 4]:
777
+ assert can_do([p, m], [n])
778
+
779
+
780
+ @slow
781
+ def test_prudnikov_5():
782
+ h = S.Half
783
+
784
+ for p in [1, 2, 3]:
785
+ for q in range(p, 4):
786
+ for r in [1, 2, 3]:
787
+ for s in range(r, 4):
788
+ assert can_do([-h, p, q], [r, s])
789
+
790
+ for p in [h, 1, 3*h, 2, 5*h, 3]:
791
+ for q in [h, 3*h, 5*h]:
792
+ for r in [h, 3*h, 5*h]:
793
+ for s in [h, 3*h, 5*h]:
794
+ if s <= q and s <= r:
795
+ assert can_do([-h, p, q], [r, s])
796
+
797
+ for p in [h, 1, 3*h, 2, 5*h, 3]:
798
+ for q in [1, 2, 3]:
799
+ for r in [h, 3*h, 5*h]:
800
+ for s in [1, 2, 3]:
801
+ assert can_do([-h, p, q], [r, s])
802
+
803
+
804
+ @slow
805
+ def test_prudnikov_6():
806
+ h = S.Half
807
+
808
+ for m in [3*h, 5*h]:
809
+ for n in [1, 2, 3]:
810
+ for q in [h, 1, 2]:
811
+ for p in [1, 2, 3]:
812
+ assert can_do([h, q, p], [m, n])
813
+ for q in [1, 2, 3]:
814
+ for p in [3*h, 5*h]:
815
+ assert can_do([h, q, p], [m, n])
816
+
817
+ for q in [1, 2]:
818
+ for p in [1, 2, 3]:
819
+ for m in [1, 2, 3]:
820
+ for n in [1, 2, 3]:
821
+ assert can_do([h, q, p], [m, n])
822
+
823
+ assert can_do([h, h, 5*h], [3*h, 3*h])
824
+ assert can_do([h, 1, 5*h], [3*h, 3*h])
825
+ assert can_do([h, 2, 2], [1, 3])
826
+
827
+ # pages 435 to 457 contain more PFDD and stuff like this
828
+
829
+
830
+ @slow
831
+ def test_prudnikov_7():
832
+ assert can_do([3], [6])
833
+
834
+ h = S.Half
835
+ for n in [h, 3*h, 5*h, 7*h]:
836
+ assert can_do([-h], [n])
837
+ for m in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]: # HERE
838
+ for n in [-h, h, 3*h, 5*h, 7*h, 1, 2, 3, 4]:
839
+ assert can_do([m], [n])
840
+
841
+
842
+ @slow
843
+ def test_prudnikov_8():
844
+ h = S.Half
845
+
846
+ # 7.12.2
847
+ for ai in [1, 2, 3]:
848
+ for bi in [1, 2, 3]:
849
+ for ci in range(1, ai + 1):
850
+ for di in [h, 1, 3*h, 2, 5*h, 3]:
851
+ assert can_do([ai, bi], [ci, di])
852
+ for bi in [3*h, 5*h]:
853
+ for ci in [h, 1, 3*h, 2, 5*h, 3]:
854
+ for di in [1, 2, 3]:
855
+ assert can_do([ai, bi], [ci, di])
856
+
857
+ for ai in [-h, h, 3*h, 5*h]:
858
+ for bi in [1, 2, 3]:
859
+ for ci in [h, 1, 3*h, 2, 5*h, 3]:
860
+ for di in [1, 2, 3]:
861
+ assert can_do([ai, bi], [ci, di])
862
+ for bi in [h, 3*h, 5*h]:
863
+ for ci in [h, 3*h, 5*h, 3]:
864
+ for di in [h, 1, 3*h, 2, 5*h, 3]:
865
+ if ci <= bi:
866
+ assert can_do([ai, bi], [ci, di])
867
+
868
+
869
+ def test_prudnikov_9():
870
+ # 7.13.1 [we have a general formula ... so this is a bit pointless]
871
+ for i in range(9):
872
+ assert can_do([], [(S(i) + 1)/2])
873
+ for i in range(5):
874
+ assert can_do([], [-(2*S(i) + 1)/2])
875
+
876
+
877
+ @slow
878
+ def test_prudnikov_10():
879
+ # 7.14.2
880
+ h = S.Half
881
+ for p in [-h, h, 1, 3*h, 2, 5*h, 3, 7*h, 4]:
882
+ for m in [1, 2, 3, 4]:
883
+ for n in range(m, 5):
884
+ assert can_do([p], [m, n])
885
+
886
+ for p in [1, 2, 3, 4]:
887
+ for n in [h, 3*h, 5*h, 7*h]:
888
+ for m in [1, 2, 3, 4]:
889
+ assert can_do([p], [n, m])
890
+
891
+ for p in [3*h, 5*h, 7*h]:
892
+ for m in [h, 1, 2, 5*h, 3, 7*h, 4]:
893
+ assert can_do([p], [h, m])
894
+ assert can_do([p], [3*h, m])
895
+
896
+ for m in [h, 1, 2, 5*h, 3, 7*h, 4]:
897
+ assert can_do([7*h], [5*h, m])
898
+
899
+ assert can_do([Rational(-1, 2)], [S.Half, S.Half]) # shine-integral shi
900
+
901
+
902
+ def test_prudnikov_11():
903
+ # 7.15
904
+ assert can_do([a, a + S.Half], [2*a, b, 2*a - b])
905
+ assert can_do([a, a + S.Half], [Rational(3, 2), 2*a, 2*a - S.Half])
906
+
907
+ assert can_do([Rational(1, 4), Rational(3, 4)], [S.Half, S.Half, 1])
908
+ assert can_do([Rational(5, 4), Rational(3, 4)], [Rational(3, 2), S.Half, 2])
909
+ assert can_do([Rational(5, 4), Rational(3, 4)], [Rational(3, 2), Rational(3, 2), 1])
910
+ assert can_do([Rational(5, 4), Rational(7, 4)], [Rational(3, 2), Rational(5, 2), 2])
911
+
912
+ assert can_do([1, 1], [Rational(3, 2), 2, 2]) # cosh-integral chi
913
+
914
+
915
+ def test_prudnikov_12():
916
+ # 7.16
917
+ assert can_do(
918
+ [], [a, a + S.Half, 2*a], False) # branches only agree for some z!
919
+ assert can_do([], [a, a + S.Half, 2*a + 1], False) # dito
920
+ assert can_do([], [S.Half, a, a + S.Half])
921
+ assert can_do([], [Rational(3, 2), a, a + S.Half])
922
+
923
+ assert can_do([], [Rational(1, 4), S.Half, Rational(3, 4)])
924
+ assert can_do([], [S.Half, S.Half, 1])
925
+ assert can_do([], [S.Half, Rational(3, 2), 1])
926
+ assert can_do([], [Rational(3, 4), Rational(3, 2), Rational(5, 4)])
927
+ assert can_do([], [1, 1, Rational(3, 2)])
928
+ assert can_do([], [1, 2, Rational(3, 2)])
929
+ assert can_do([], [1, Rational(3, 2), Rational(3, 2)])
930
+ assert can_do([], [Rational(5, 4), Rational(3, 2), Rational(7, 4)])
931
+ assert can_do([], [2, Rational(3, 2), Rational(3, 2)])
932
+
933
+
934
+ @slow
935
+ def test_prudnikov_2F1():
936
+ h = S.Half
937
+ # Elliptic integrals
938
+ for p in [-h, h]:
939
+ for m in [h, 3*h, 5*h, 7*h]:
940
+ for n in [1, 2, 3, 4]:
941
+ assert can_do([p, m], [n])
942
+
943
+
944
+ @XFAIL
945
+ def test_prudnikov_fail_2F1():
946
+ assert can_do([a, b], [b + 1]) # incomplete beta function
947
+ assert can_do([-1, b], [c]) # Poly. also -2, -3 etc
948
+
949
+ # TODO polys
950
+
951
+ # Legendre functions:
952
+ assert can_do([a, b], [a + b + S.Half])
953
+ assert can_do([a, b], [a + b - S.Half])
954
+ assert can_do([a, b], [a + b + Rational(3, 2)])
955
+ assert can_do([a, b], [(a + b + 1)/2])
956
+ assert can_do([a, b], [(a + b)/2 + 1])
957
+ assert can_do([a, b], [a - b + 1])
958
+ assert can_do([a, b], [a - b + 2])
959
+ assert can_do([a, b], [2*b])
960
+ assert can_do([a, b], [S.Half])
961
+ assert can_do([a, b], [Rational(3, 2)])
962
+ assert can_do([a, 1 - a], [c])
963
+ assert can_do([a, 2 - a], [c])
964
+ assert can_do([a, 3 - a], [c])
965
+ assert can_do([a, a + S.Half], [c])
966
+ assert can_do([1, b], [c])
967
+ assert can_do([1, b], [Rational(3, 2)])
968
+
969
+ assert can_do([Rational(1, 4), Rational(3, 4)], [1])
970
+
971
+ # PFDD
972
+ o = S.One
973
+ assert can_do([o/8, 1], [o/8*9])
974
+ assert can_do([o/6, 1], [o/6*7])
975
+ assert can_do([o/6, 1], [o/6*13])
976
+ assert can_do([o/5, 1], [o/5*6])
977
+ assert can_do([o/5, 1], [o/5*11])
978
+ assert can_do([o/4, 1], [o/4*5])
979
+ assert can_do([o/4, 1], [o/4*9])
980
+ assert can_do([o/3, 1], [o/3*4])
981
+ assert can_do([o/3, 1], [o/3*7])
982
+ assert can_do([o/8*3, 1], [o/8*11])
983
+ assert can_do([o/5*2, 1], [o/5*7])
984
+ assert can_do([o/5*2, 1], [o/5*12])
985
+ assert can_do([o/5*3, 1], [o/5*8])
986
+ assert can_do([o/5*3, 1], [o/5*13])
987
+ assert can_do([o/8*5, 1], [o/8*13])
988
+ assert can_do([o/4*3, 1], [o/4*7])
989
+ assert can_do([o/4*3, 1], [o/4*11])
990
+ assert can_do([o/3*2, 1], [o/3*5])
991
+ assert can_do([o/3*2, 1], [o/3*8])
992
+ assert can_do([o/5*4, 1], [o/5*9])
993
+ assert can_do([o/5*4, 1], [o/5*14])
994
+ assert can_do([o/6*5, 1], [o/6*11])
995
+ assert can_do([o/6*5, 1], [o/6*17])
996
+ assert can_do([o/8*7, 1], [o/8*15])
997
+
998
+
999
+ @XFAIL
1000
+ def test_prudnikov_fail_3F2():
1001
+ assert can_do([a, a + Rational(1, 3), a + Rational(2, 3)], [Rational(1, 3), Rational(2, 3)])
1002
+ assert can_do([a, a + Rational(1, 3), a + Rational(2, 3)], [Rational(2, 3), Rational(4, 3)])
1003
+ assert can_do([a, a + Rational(1, 3), a + Rational(2, 3)], [Rational(4, 3), Rational(5, 3)])
1004
+
1005
+ # page 421
1006
+ assert can_do([a, a + Rational(1, 3), a + Rational(2, 3)], [a*Rational(3, 2), (3*a + 1)/2])
1007
+
1008
+ # pages 422 ...
1009
+ assert can_do([Rational(-1, 2), S.Half, S.Half], [1, 1]) # elliptic integrals
1010
+ assert can_do([Rational(-1, 2), S.Half, 1], [Rational(3, 2), Rational(3, 2)])
1011
+ # TODO LOTS more
1012
+
1013
+ # PFDD
1014
+ assert can_do([Rational(1, 8), Rational(3, 8), 1], [Rational(9, 8), Rational(11, 8)])
1015
+ assert can_do([Rational(1, 8), Rational(5, 8), 1], [Rational(9, 8), Rational(13, 8)])
1016
+ assert can_do([Rational(1, 8), Rational(7, 8), 1], [Rational(9, 8), Rational(15, 8)])
1017
+ assert can_do([Rational(1, 6), Rational(1, 3), 1], [Rational(7, 6), Rational(4, 3)])
1018
+ assert can_do([Rational(1, 6), Rational(2, 3), 1], [Rational(7, 6), Rational(5, 3)])
1019
+ assert can_do([Rational(1, 6), Rational(2, 3), 1], [Rational(5, 3), Rational(13, 6)])
1020
+ assert can_do([S.Half, 1, 1], [Rational(1, 4), Rational(3, 4)])
1021
+ # LOTS more
1022
+
1023
+
1024
+ @XFAIL
1025
+ def test_prudnikov_fail_other():
1026
+ # 7.11.2
1027
+
1028
+ # 7.12.1
1029
+ assert can_do([1, a], [b, 1 - 2*a + b]) # ???
1030
+
1031
+ # 7.14.2
1032
+ assert can_do([Rational(-1, 2)], [S.Half, 1]) # struve
1033
+ assert can_do([1], [S.Half, S.Half]) # struve
1034
+ assert can_do([Rational(1, 4)], [S.Half, Rational(5, 4)]) # PFDD
1035
+ assert can_do([Rational(3, 4)], [Rational(3, 2), Rational(7, 4)]) # PFDD
1036
+ assert can_do([1], [Rational(1, 4), Rational(3, 4)]) # PFDD
1037
+ assert can_do([1], [Rational(3, 4), Rational(5, 4)]) # PFDD
1038
+ assert can_do([1], [Rational(5, 4), Rational(7, 4)]) # PFDD
1039
+ # TODO LOTS more
1040
+
1041
+ # 7.15.2
1042
+ assert can_do([S.Half, 1], [Rational(3, 4), Rational(5, 4), Rational(3, 2)]) # PFDD
1043
+ assert can_do([S.Half, 1], [Rational(7, 4), Rational(5, 4), Rational(3, 2)]) # PFDD
1044
+
1045
+ # 7.16.1
1046
+ assert can_do([], [Rational(1, 3), S(2/3)]) # PFDD
1047
+ assert can_do([], [Rational(2, 3), S(4/3)]) # PFDD
1048
+ assert can_do([], [Rational(5, 3), S(4/3)]) # PFDD
1049
+
1050
+ # XXX this does not *evaluate* right??
1051
+ assert can_do([], [a, a + S.Half, 2*a - 1])
1052
+
1053
+
1054
+ def test_bug():
1055
+ h = hyper([-1, 1], [z], -1)
1056
+ assert hyperexpand(h) == (z + 1)/z
1057
+
1058
+
1059
+ def test_omgissue_203():
1060
+ h = hyper((-5, -3, -4), (-6, -6), 1)
1061
+ assert hyperexpand(h) == Rational(1, 30)
1062
+ h = hyper((-6, -7, -5), (-6, -6), 1)
1063
+ assert hyperexpand(h) == Rational(-1, 6)
lib/python3.12/site-packages/sympy/simplify/tests/test_powsimp.py ADDED
@@ -0,0 +1,366 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.function import Function
2
+ from sympy.core.mul import Mul
3
+ from sympy.core.numbers import (E, I, Rational, oo, pi)
4
+ from sympy.core.singleton import S
5
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
6
+ from sympy.functions.elementary.exponential import (exp, log)
7
+ from sympy.functions.elementary.miscellaneous import (root, sqrt)
8
+ from sympy.functions.elementary.trigonometric import sin
9
+ from sympy.functions.special.gamma_functions import gamma
10
+ from sympy.functions.special.hyper import hyper
11
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
12
+ from sympy.simplify.powsimp import (powdenest, powsimp)
13
+ from sympy.simplify.simplify import (signsimp, simplify)
14
+ from sympy.core.symbol import Str
15
+
16
+ from sympy.abc import x, y, z, a, b
17
+
18
+
19
+ def test_powsimp():
20
+ x, y, z, n = symbols('x,y,z,n')
21
+ f = Function('f')
22
+ assert powsimp( 4**x * 2**(-x) * 2**(-x) ) == 1
23
+ assert powsimp( (-4)**x * (-2)**(-x) * 2**(-x) ) == 1
24
+
25
+ assert powsimp(
26
+ f(4**x * 2**(-x) * 2**(-x)) ) == f(4**x * 2**(-x) * 2**(-x))
27
+ assert powsimp( f(4**x * 2**(-x) * 2**(-x)), deep=True ) == f(1)
28
+ assert exp(x)*exp(y) == exp(x)*exp(y)
29
+ assert powsimp(exp(x)*exp(y)) == exp(x + y)
30
+ assert powsimp(exp(x)*exp(y)*2**x*2**y) == (2*E)**(x + y)
31
+ assert powsimp(exp(x)*exp(y)*2**x*2**y, combine='exp') == \
32
+ exp(x + y)*2**(x + y)
33
+ assert powsimp(exp(x)*exp(y)*exp(2)*sin(x) + sin(y) + 2**x*2**y) == \
34
+ exp(2 + x + y)*sin(x) + sin(y) + 2**(x + y)
35
+ assert powsimp(sin(exp(x)*exp(y))) == sin(exp(x)*exp(y))
36
+ assert powsimp(sin(exp(x)*exp(y)), deep=True) == sin(exp(x + y))
37
+ assert powsimp(x**2*x**y) == x**(2 + y)
38
+ # This should remain factored, because 'exp' with deep=True is supposed
39
+ # to act like old automatic exponent combining.
40
+ assert powsimp((1 + E*exp(E))*exp(-E), combine='exp', deep=True) == \
41
+ (1 + exp(1 + E))*exp(-E)
42
+ assert powsimp((1 + E*exp(E))*exp(-E), deep=True) == \
43
+ (1 + exp(1 + E))*exp(-E)
44
+ assert powsimp((1 + E*exp(E))*exp(-E)) == (1 + exp(1 + E))*exp(-E)
45
+ assert powsimp((1 + E*exp(E))*exp(-E), combine='exp') == \
46
+ (1 + exp(1 + E))*exp(-E)
47
+ assert powsimp((1 + E*exp(E))*exp(-E), combine='base') == \
48
+ (1 + E*exp(E))*exp(-E)
49
+ x, y = symbols('x,y', nonnegative=True)
50
+ n = Symbol('n', real=True)
51
+ assert powsimp(y**n * (y/x)**(-n)) == x**n
52
+ assert powsimp(x**(x**(x*y)*y**(x*y))*y**(x**(x*y)*y**(x*y)), deep=True) \
53
+ == (x*y)**(x*y)**(x*y)
54
+ assert powsimp(2**(2**(2*x)*x), deep=False) == 2**(2**(2*x)*x)
55
+ assert powsimp(2**(2**(2*x)*x), deep=True) == 2**(x*4**x)
56
+ assert powsimp(
57
+ exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == \
58
+ exp(-x + exp(-x)*exp(-x*log(x)))
59
+ assert powsimp(
60
+ exp(-x + exp(-x)*exp(-x*log(x))), deep=False, combine='exp') == \
61
+ exp(-x + exp(-x)*exp(-x*log(x)))
62
+ assert powsimp((x + y)/(3*z), deep=False, combine='exp') == (x + y)/(3*z)
63
+ assert powsimp((x/3 + y/3)/z, deep=True, combine='exp') == (x/3 + y/3)/z
64
+ assert powsimp(exp(x)/(1 + exp(x)*exp(y)), deep=True) == \
65
+ exp(x)/(1 + exp(x + y))
66
+ assert powsimp(x*y**(z**x*z**y), deep=True) == x*y**(z**(x + y))
67
+ assert powsimp((z**x*z**y)**x, deep=True) == (z**(x + y))**x
68
+ assert powsimp(x*(z**x*z**y)**x, deep=True) == x*(z**(x + y))**x
69
+ p = symbols('p', positive=True)
70
+ assert powsimp((1/x)**log(2)/x) == (1/x)**(1 + log(2))
71
+ assert powsimp((1/p)**log(2)/p) == p**(-1 - log(2))
72
+
73
+ # coefficient of exponent can only be simplified for positive bases
74
+ assert powsimp(2**(2*x)) == 4**x
75
+ assert powsimp((-1)**(2*x)) == (-1)**(2*x)
76
+ i = symbols('i', integer=True)
77
+ assert powsimp((-1)**(2*i)) == 1
78
+ assert powsimp((-1)**(-x)) != (-1)**x # could be 1/((-1)**x), but is not
79
+ # force=True overrides assumptions
80
+ assert powsimp((-1)**(2*x), force=True) == 1
81
+
82
+ # rational exponents allow combining of negative terms
83
+ w, n, m = symbols('w n m', negative=True)
84
+ e = i/a # not a rational exponent if `a` is unknown
85
+ ex = w**e*n**e*m**e
86
+ assert powsimp(ex) == m**(i/a)*n**(i/a)*w**(i/a)
87
+ e = i/3
88
+ ex = w**e*n**e*m**e
89
+ assert powsimp(ex) == (-1)**i*(-m*n*w)**(i/3)
90
+ e = (3 + i)/i
91
+ ex = w**e*n**e*m**e
92
+ assert powsimp(ex) == (-1)**(3*e)*(-m*n*w)**e
93
+
94
+ eq = x**(a*Rational(2, 3))
95
+ # eq != (x**a)**(2/3) (try x = -1 and a = 3 to see)
96
+ assert powsimp(eq).exp == eq.exp == a*Rational(2, 3)
97
+ # powdenest goes the other direction
98
+ assert powsimp(2**(2*x)) == 4**x
99
+
100
+ assert powsimp(exp(p/2)) == exp(p/2)
101
+
102
+ # issue 6368
103
+ eq = Mul(*[sqrt(Dummy(imaginary=True)) for i in range(3)])
104
+ assert powsimp(eq) == eq and eq.is_Mul
105
+
106
+ assert all(powsimp(e) == e for e in (sqrt(x**a), sqrt(x**2)))
107
+
108
+ # issue 8836
109
+ assert str( powsimp(exp(I*pi/3)*root(-1,3)) ) == '(-1)**(2/3)'
110
+
111
+ # issue 9183
112
+ assert powsimp(-0.1**x) == -0.1**x
113
+
114
+ # issue 10095
115
+ assert powsimp((1/(2*E))**oo) == (exp(-1)/2)**oo
116
+
117
+ # PR 13131
118
+ eq = sin(2*x)**2*sin(2.0*x)**2
119
+ assert powsimp(eq) == eq
120
+
121
+ # issue 14615
122
+ assert powsimp(x**2*y**3*(x*y**2)**Rational(3, 2)
123
+ ) == x*y*(x*y**2)**Rational(5, 2)
124
+
125
+
126
+ def test_powsimp_negated_base():
127
+ assert powsimp((-x + y)/sqrt(x - y)) == -sqrt(x - y)
128
+ assert powsimp((-x + y)*(-z + y)/sqrt(x - y)/sqrt(z - y)) == sqrt(x - y)*sqrt(z - y)
129
+ p = symbols('p', positive=True)
130
+ reps = {p: 2, a: S.Half}
131
+ assert powsimp((-p)**a/p**a).subs(reps) == ((-1)**a).subs(reps)
132
+ assert powsimp((-p)**a*p**a).subs(reps) == ((-p**2)**a).subs(reps)
133
+ n = symbols('n', negative=True)
134
+ reps = {p: -2, a: S.Half}
135
+ assert powsimp((-n)**a/n**a).subs(reps) == (-1)**(-a).subs(a, S.Half)
136
+ assert powsimp((-n)**a*n**a).subs(reps) == ((-n**2)**a).subs(reps)
137
+ # if x is 0 then the lhs is 0**a*oo**a which is not (-1)**a
138
+ eq = (-x)**a/x**a
139
+ assert powsimp(eq) == eq
140
+
141
+
142
+ def test_powsimp_nc():
143
+ x, y, z = symbols('x,y,z')
144
+ A, B, C = symbols('A B C', commutative=False)
145
+
146
+ assert powsimp(A**x*A**y, combine='all') == A**(x + y)
147
+ assert powsimp(A**x*A**y, combine='base') == A**x*A**y
148
+ assert powsimp(A**x*A**y, combine='exp') == A**(x + y)
149
+
150
+ assert powsimp(A**x*B**x, combine='all') == A**x*B**x
151
+ assert powsimp(A**x*B**x, combine='base') == A**x*B**x
152
+ assert powsimp(A**x*B**x, combine='exp') == A**x*B**x
153
+
154
+ assert powsimp(B**x*A**x, combine='all') == B**x*A**x
155
+ assert powsimp(B**x*A**x, combine='base') == B**x*A**x
156
+ assert powsimp(B**x*A**x, combine='exp') == B**x*A**x
157
+
158
+ assert powsimp(A**x*A**y*A**z, combine='all') == A**(x + y + z)
159
+ assert powsimp(A**x*A**y*A**z, combine='base') == A**x*A**y*A**z
160
+ assert powsimp(A**x*A**y*A**z, combine='exp') == A**(x + y + z)
161
+
162
+ assert powsimp(A**x*B**x*C**x, combine='all') == A**x*B**x*C**x
163
+ assert powsimp(A**x*B**x*C**x, combine='base') == A**x*B**x*C**x
164
+ assert powsimp(A**x*B**x*C**x, combine='exp') == A**x*B**x*C**x
165
+
166
+ assert powsimp(B**x*A**x*C**x, combine='all') == B**x*A**x*C**x
167
+ assert powsimp(B**x*A**x*C**x, combine='base') == B**x*A**x*C**x
168
+ assert powsimp(B**x*A**x*C**x, combine='exp') == B**x*A**x*C**x
169
+
170
+
171
+ def test_issue_6440():
172
+ assert powsimp(16*2**a*8**b) == 2**(a + 3*b + 4)
173
+
174
+
175
+ def test_powdenest():
176
+ x, y = symbols('x,y')
177
+ p, q = symbols('p q', positive=True)
178
+ i, j = symbols('i,j', integer=True)
179
+
180
+ assert powdenest(x) == x
181
+ assert powdenest(x + 2*(x**(a*Rational(2, 3)))**(3*x)) == (x + 2*(x**(a*Rational(2, 3)))**(3*x))
182
+ assert powdenest((exp(a*Rational(2, 3)))**(3*x)) # -X-> (exp(a/3))**(6*x)
183
+ assert powdenest((x**(a*Rational(2, 3)))**(3*x)) == ((x**(a*Rational(2, 3)))**(3*x))
184
+ assert powdenest(exp(3*x*log(2))) == 2**(3*x)
185
+ assert powdenest(sqrt(p**2)) == p
186
+ eq = p**(2*i)*q**(4*i)
187
+ assert powdenest(eq) == (p*q**2)**(2*i)
188
+ # -X-> (x**x)**i*(x**x)**j == x**(x*(i + j))
189
+ assert powdenest((x**x)**(i + j))
190
+ assert powdenest(exp(3*y*log(x))) == x**(3*y)
191
+ assert powdenest(exp(y*(log(a) + log(b)))) == (a*b)**y
192
+ assert powdenest(exp(3*(log(a) + log(b)))) == a**3*b**3
193
+ assert powdenest(((x**(2*i))**(3*y))**x) == ((x**(2*i))**(3*y))**x
194
+ assert powdenest(((x**(2*i))**(3*y))**x, force=True) == x**(6*i*x*y)
195
+ assert powdenest(((x**(a*Rational(2, 3)))**(3*y/i))**x) == \
196
+ (((x**(a*Rational(2, 3)))**(3*y/i))**x)
197
+ assert powdenest((x**(2*i)*y**(4*i))**z, force=True) == (x*y**2)**(2*i*z)
198
+ assert powdenest((p**(2*i)*q**(4*i))**j) == (p*q**2)**(2*i*j)
199
+ e = ((p**(2*a))**(3*y))**x
200
+ assert powdenest(e) == e
201
+ e = ((x**2*y**4)**a)**(x*y)
202
+ assert powdenest(e) == e
203
+ e = (((x**2*y**4)**a)**(x*y))**3
204
+ assert powdenest(e) == ((x**2*y**4)**a)**(3*x*y)
205
+ assert powdenest((((x**2*y**4)**a)**(x*y)), force=True) == \
206
+ (x*y**2)**(2*a*x*y)
207
+ assert powdenest((((x**2*y**4)**a)**(x*y))**3, force=True) == \
208
+ (x*y**2)**(6*a*x*y)
209
+ assert powdenest((x**2*y**6)**i) != (x*y**3)**(2*i)
210
+ x, y = symbols('x,y', positive=True)
211
+ assert powdenest((x**2*y**6)**i) == (x*y**3)**(2*i)
212
+
213
+ assert powdenest((x**(i*Rational(2, 3))*y**(i/2))**(2*i)) == (x**Rational(4, 3)*y)**(i**2)
214
+ assert powdenest(sqrt(x**(2*i)*y**(6*i))) == (x*y**3)**i
215
+
216
+ assert powdenest(4**x) == 2**(2*x)
217
+ assert powdenest((4**x)**y) == 2**(2*x*y)
218
+ assert powdenest(4**x*y) == 2**(2*x)*y
219
+
220
+
221
+ def test_powdenest_polar():
222
+ x, y, z = symbols('x y z', polar=True)
223
+ a, b, c = symbols('a b c')
224
+ assert powdenest((x*y*z)**a) == x**a*y**a*z**a
225
+ assert powdenest((x**a*y**b)**c) == x**(a*c)*y**(b*c)
226
+ assert powdenest(((x**a)**b*y**c)**c) == x**(a*b*c)*y**(c**2)
227
+
228
+
229
+ def test_issue_5805():
230
+ arg = ((gamma(x)*hyper((), (), x))*pi)**2
231
+ assert powdenest(arg) == (pi*gamma(x)*hyper((), (), x))**2
232
+ assert arg.is_positive is None
233
+
234
+
235
+ def test_issue_9324_powsimp_on_matrix_symbol():
236
+ M = MatrixSymbol('M', 10, 10)
237
+ expr = powsimp(M, deep=True)
238
+ assert expr == M
239
+ assert expr.args[0] == Str('M')
240
+
241
+
242
+ def test_issue_6367():
243
+ z = -5*sqrt(2)/(2*sqrt(2*sqrt(29) + 29)) + sqrt(-sqrt(29)/29 + S.Half)
244
+ assert Mul(*[powsimp(a) for a in Mul.make_args(z.normal())]) == 0
245
+ assert powsimp(z.normal()) == 0
246
+ assert simplify(z) == 0
247
+ assert powsimp(sqrt(2 + sqrt(3))*sqrt(2 - sqrt(3)) + 1) == 2
248
+ assert powsimp(z) != 0
249
+
250
+
251
+ def test_powsimp_polar():
252
+ from sympy.functions.elementary.complexes import polar_lift
253
+ from sympy.functions.elementary.exponential import exp_polar
254
+ x, y, z = symbols('x y z')
255
+ p, q, r = symbols('p q r', polar=True)
256
+
257
+ assert (polar_lift(-1))**(2*x) == exp_polar(2*pi*I*x)
258
+ assert powsimp(p**x * q**x) == (p*q)**x
259
+ assert p**x * (1/p)**x == 1
260
+ assert (1/p)**x == p**(-x)
261
+
262
+ assert exp_polar(x)*exp_polar(y) == exp_polar(x)*exp_polar(y)
263
+ assert powsimp(exp_polar(x)*exp_polar(y)) == exp_polar(x + y)
264
+ assert powsimp(exp_polar(x)*exp_polar(y)*p**x*p**y) == \
265
+ (p*exp_polar(1))**(x + y)
266
+ assert powsimp(exp_polar(x)*exp_polar(y)*p**x*p**y, combine='exp') == \
267
+ exp_polar(x + y)*p**(x + y)
268
+ assert powsimp(
269
+ exp_polar(x)*exp_polar(y)*exp_polar(2)*sin(x) + sin(y) + p**x*p**y) \
270
+ == p**(x + y) + sin(x)*exp_polar(2 + x + y) + sin(y)
271
+ assert powsimp(sin(exp_polar(x)*exp_polar(y))) == \
272
+ sin(exp_polar(x)*exp_polar(y))
273
+ assert powsimp(sin(exp_polar(x)*exp_polar(y)), deep=True) == \
274
+ sin(exp_polar(x + y))
275
+
276
+
277
+ def test_issue_5728():
278
+ b = x*sqrt(y)
279
+ a = sqrt(b)
280
+ c = sqrt(sqrt(x)*y)
281
+ assert powsimp(a*b) == sqrt(b)**3
282
+ assert powsimp(a*b**2*sqrt(y)) == sqrt(y)*a**5
283
+ assert powsimp(a*x**2*c**3*y) == c**3*a**5
284
+ assert powsimp(a*x*c**3*y**2) == c**7*a
285
+ assert powsimp(x*c**3*y**2) == c**7
286
+ assert powsimp(x*c**3*y) == x*y*c**3
287
+ assert powsimp(sqrt(x)*c**3*y) == c**5
288
+ assert powsimp(sqrt(x)*a**3*sqrt(y)) == sqrt(x)*sqrt(y)*a**3
289
+ assert powsimp(Mul(sqrt(x)*c**3*sqrt(y), y, evaluate=False)) == \
290
+ sqrt(x)*sqrt(y)**3*c**3
291
+ assert powsimp(a**2*a*x**2*y) == a**7
292
+
293
+ # symbolic powers work, too
294
+ b = x**y*y
295
+ a = b*sqrt(b)
296
+ assert a.is_Mul is True
297
+ assert powsimp(a) == sqrt(b)**3
298
+
299
+ # as does exp
300
+ a = x*exp(y*Rational(2, 3))
301
+ assert powsimp(a*sqrt(a)) == sqrt(a)**3
302
+ assert powsimp(a**2*sqrt(a)) == sqrt(a)**5
303
+ assert powsimp(a**2*sqrt(sqrt(a))) == sqrt(sqrt(a))**9
304
+
305
+
306
+ def test_issue_from_PR1599():
307
+ n1, n2, n3, n4 = symbols('n1 n2 n3 n4', negative=True)
308
+ assert (powsimp(sqrt(n1)*sqrt(n2)*sqrt(n3)) ==
309
+ -I*sqrt(-n1)*sqrt(-n2)*sqrt(-n3))
310
+ assert (powsimp(root(n1, 3)*root(n2, 3)*root(n3, 3)*root(n4, 3)) ==
311
+ -(-1)**Rational(1, 3)*
312
+ (-n1)**Rational(1, 3)*(-n2)**Rational(1, 3)*(-n3)**Rational(1, 3)*(-n4)**Rational(1, 3))
313
+
314
+
315
+ def test_issue_10195():
316
+ a = Symbol('a', integer=True)
317
+ l = Symbol('l', even=True, nonzero=True)
318
+ n = Symbol('n', odd=True)
319
+ e_x = (-1)**(n/2 - S.Half) - (-1)**(n*Rational(3, 2) - S.Half)
320
+ assert powsimp((-1)**(l/2)) == I**l
321
+ assert powsimp((-1)**(n/2)) == I**n
322
+ assert powsimp((-1)**(n*Rational(3, 2))) == -I**n
323
+ assert powsimp(e_x) == (-1)**(n/2 - S.Half) + (-1)**(n*Rational(3, 2) +
324
+ S.Half)
325
+ assert powsimp((-1)**(a*Rational(3, 2))) == (-I)**a
326
+
327
+ def test_issue_15709():
328
+ assert powsimp(3**x*Rational(2, 3)) == 2*3**(x-1)
329
+ assert powsimp(2*3**x/3) == 2*3**(x-1)
330
+
331
+
332
+ def test_issue_11981():
333
+ x, y = symbols('x y', commutative=False)
334
+ assert powsimp((x*y)**2 * (y*x)**2) == (x*y)**2 * (y*x)**2
335
+
336
+
337
+ def test_issue_17524():
338
+ a = symbols("a", real=True)
339
+ e = (-1 - a**2)*sqrt(1 + a**2)
340
+ assert signsimp(powsimp(e)) == signsimp(e) == -(a**2 + 1)**(S(3)/2)
341
+
342
+
343
+ def test_issue_19627():
344
+ # if you use force the user must verify
345
+ assert powdenest(sqrt(sin(x)**2), force=True) == sin(x)
346
+ assert powdenest((x**(S.Half/y))**(2*y), force=True) == x
347
+ from sympy.core.function import expand_power_base
348
+ e = 1 - a
349
+ expr = (exp(z/e)*x**(b/e)*y**((1 - b)/e))**e
350
+ assert powdenest(expand_power_base(expr, force=True), force=True
351
+ ) == x**b*y**(1 - b)*exp(z)
352
+
353
+
354
+ def test_issue_22546():
355
+ p1, p2 = symbols('p1, p2', positive=True)
356
+ ref = powsimp(p1**z/p2**z)
357
+ e = z + 1
358
+ ans = ref.subs(z, e)
359
+ assert ans.is_Pow
360
+ assert powsimp(p1**e/p2**e) == ans
361
+ i = symbols('i', integer=True)
362
+ ref = powsimp(x**i/y**i)
363
+ e = i + 1
364
+ ans = ref.subs(i, e)
365
+ assert ans.is_Pow
366
+ assert powsimp(x**e/y**e) == ans
lib/python3.12/site-packages/sympy/simplify/tests/test_radsimp.py ADDED
@@ -0,0 +1,490 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.add import Add
2
+ from sympy.core.function import (Derivative, Function, diff)
3
+ from sympy.core.mul import Mul
4
+ from sympy.core.numbers import (I, Rational)
5
+ from sympy.core.power import Pow
6
+ from sympy.core.singleton import S
7
+ from sympy.core.symbol import (Symbol, Wild, symbols)
8
+ from sympy.functions.elementary.complexes import Abs
9
+ from sympy.functions.elementary.exponential import (exp, log)
10
+ from sympy.functions.elementary.miscellaneous import (root, sqrt)
11
+ from sympy.functions.elementary.trigonometric import (cos, sin)
12
+ from sympy.polys.polytools import factor
13
+ from sympy.series.order import O
14
+ from sympy.simplify.radsimp import (collect, collect_const, fraction, radsimp, rcollect)
15
+
16
+ from sympy.core.expr import unchanged
17
+ from sympy.core.mul import _unevaluated_Mul as umul
18
+ from sympy.simplify.radsimp import (_unevaluated_Add,
19
+ collect_sqrt, fraction_expand, collect_abs)
20
+ from sympy.testing.pytest import raises
21
+
22
+ from sympy.abc import x, y, z, a, b, c, d
23
+
24
+
25
+ def test_radsimp():
26
+ r2 = sqrt(2)
27
+ r3 = sqrt(3)
28
+ r5 = sqrt(5)
29
+ r7 = sqrt(7)
30
+ assert fraction(radsimp(1/r2)) == (sqrt(2), 2)
31
+ assert radsimp(1/(1 + r2)) == \
32
+ -1 + sqrt(2)
33
+ assert radsimp(1/(r2 + r3)) == \
34
+ -sqrt(2) + sqrt(3)
35
+ assert fraction(radsimp(1/(1 + r2 + r3))) == \
36
+ (-sqrt(6) + sqrt(2) + 2, 4)
37
+ assert fraction(radsimp(1/(r2 + r3 + r5))) == \
38
+ (-sqrt(30) + 2*sqrt(3) + 3*sqrt(2), 12)
39
+ assert fraction(radsimp(1/(1 + r2 + r3 + r5))) == (
40
+ (-34*sqrt(10) - 26*sqrt(15) - 55*sqrt(3) - 61*sqrt(2) + 14*sqrt(30) +
41
+ 93 + 46*sqrt(6) + 53*sqrt(5), 71))
42
+ assert fraction(radsimp(1/(r2 + r3 + r5 + r7))) == (
43
+ (-50*sqrt(42) - 133*sqrt(5) - 34*sqrt(70) - 145*sqrt(3) + 22*sqrt(105)
44
+ + 185*sqrt(2) + 62*sqrt(30) + 135*sqrt(7), 215))
45
+ z = radsimp(1/(1 + r2/3 + r3/5 + r5 + r7))
46
+ assert len((3616791619821680643598*z).args) == 16
47
+ assert radsimp(1/z) == 1/z
48
+ assert radsimp(1/z, max_terms=20).expand() == 1 + r2/3 + r3/5 + r5 + r7
49
+ assert radsimp(1/(r2*3)) == \
50
+ sqrt(2)/6
51
+ assert radsimp(1/(r2*a + r3 + r5 + r7)) == (
52
+ (8*sqrt(2)*a**7 - 8*sqrt(7)*a**6 - 8*sqrt(5)*a**6 - 8*sqrt(3)*a**6 -
53
+ 180*sqrt(2)*a**5 + 8*sqrt(30)*a**5 + 8*sqrt(42)*a**5 + 8*sqrt(70)*a**5
54
+ - 24*sqrt(105)*a**4 + 84*sqrt(3)*a**4 + 100*sqrt(5)*a**4 +
55
+ 116*sqrt(7)*a**4 - 72*sqrt(70)*a**3 - 40*sqrt(42)*a**3 -
56
+ 8*sqrt(30)*a**3 + 782*sqrt(2)*a**3 - 462*sqrt(3)*a**2 -
57
+ 302*sqrt(7)*a**2 - 254*sqrt(5)*a**2 + 120*sqrt(105)*a**2 -
58
+ 795*sqrt(2)*a - 62*sqrt(30)*a + 82*sqrt(42)*a + 98*sqrt(70)*a -
59
+ 118*sqrt(105) + 59*sqrt(7) + 295*sqrt(5) + 531*sqrt(3))/(16*a**8 -
60
+ 480*a**6 + 3128*a**4 - 6360*a**2 + 3481))
61
+ assert radsimp(1/(r2*a + r2*b + r3 + r7)) == (
62
+ (sqrt(2)*a*(a + b)**2 - 5*sqrt(2)*a + sqrt(42)*a + sqrt(2)*b*(a +
63
+ b)**2 - 5*sqrt(2)*b + sqrt(42)*b - sqrt(7)*(a + b)**2 - sqrt(3)*(a +
64
+ b)**2 - 2*sqrt(3) + 2*sqrt(7))/(2*a**4 + 8*a**3*b + 12*a**2*b**2 -
65
+ 20*a**2 + 8*a*b**3 - 40*a*b + 2*b**4 - 20*b**2 + 8))
66
+ assert radsimp(1/(r2*a + r2*b + r2*c + r2*d)) == \
67
+ sqrt(2)/(2*a + 2*b + 2*c + 2*d)
68
+ assert radsimp(1/(1 + r2*a + r2*b + r2*c + r2*d)) == (
69
+ (sqrt(2)*a + sqrt(2)*b + sqrt(2)*c + sqrt(2)*d - 1)/(2*a**2 + 4*a*b +
70
+ 4*a*c + 4*a*d + 2*b**2 + 4*b*c + 4*b*d + 2*c**2 + 4*c*d + 2*d**2 - 1))
71
+ assert radsimp((y**2 - x)/(y - sqrt(x))) == \
72
+ sqrt(x) + y
73
+ assert radsimp(-(y**2 - x)/(y - sqrt(x))) == \
74
+ -(sqrt(x) + y)
75
+ assert radsimp(1/(1 - I + a*I)) == \
76
+ (-I*a + 1 + I)/(a**2 - 2*a + 2)
77
+ assert radsimp(1/((-x + y)*(x - sqrt(y)))) == \
78
+ (-x - sqrt(y))/((x - y)*(x**2 - y))
79
+ e = (3 + 3*sqrt(2))*x*(3*x - 3*sqrt(y))
80
+ assert radsimp(e) == x*(3 + 3*sqrt(2))*(3*x - 3*sqrt(y))
81
+ assert radsimp(1/e) == (
82
+ (-9*x + 9*sqrt(2)*x - 9*sqrt(y) + 9*sqrt(2)*sqrt(y))/(9*x*(9*x**2 -
83
+ 9*y)))
84
+ assert radsimp(1 + 1/(1 + sqrt(3))) == \
85
+ Mul(S.Half, -1 + sqrt(3), evaluate=False) + 1
86
+ A = symbols("A", commutative=False)
87
+ assert radsimp(x**2 + sqrt(2)*x**2 - sqrt(2)*x*A) == \
88
+ x**2 + sqrt(2)*x**2 - sqrt(2)*x*A
89
+ assert radsimp(1/sqrt(5 + 2 * sqrt(6))) == -sqrt(2) + sqrt(3)
90
+ assert radsimp(1/sqrt(5 + 2 * sqrt(6))**3) == -(-sqrt(3) + sqrt(2))**3
91
+
92
+ # issue 6532
93
+ assert fraction(radsimp(1/sqrt(x))) == (sqrt(x), x)
94
+ assert fraction(radsimp(1/sqrt(2*x + 3))) == (sqrt(2*x + 3), 2*x + 3)
95
+ assert fraction(radsimp(1/sqrt(2*(x + 3)))) == (sqrt(2*x + 6), 2*x + 6)
96
+
97
+ # issue 5994
98
+ e = S('-(2 + 2*sqrt(2) + 4*2**(1/4))/'
99
+ '(1 + 2**(3/4) + 3*2**(1/4) + 3*sqrt(2))')
100
+ assert radsimp(e).expand() == -2*2**Rational(3, 4) - 2*2**Rational(1, 4) + 2 + 2*sqrt(2)
101
+
102
+ # issue 5986 (modifications to radimp didn't initially recognize this so
103
+ # the test is included here)
104
+ assert radsimp(1/(-sqrt(5)/2 - S.Half + (-sqrt(5)/2 - S.Half)**2)) == 1
105
+
106
+ # from issue 5934
107
+ eq = (
108
+ (-240*sqrt(2)*sqrt(sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) -
109
+ 360*sqrt(2)*sqrt(-8*sqrt(5) + 40)*sqrt(-sqrt(5) + 5) -
110
+ 120*sqrt(10)*sqrt(-8*sqrt(5) + 40)*sqrt(-sqrt(5) + 5) +
111
+ 120*sqrt(2)*sqrt(-sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) +
112
+ 120*sqrt(2)*sqrt(-8*sqrt(5) + 40)*sqrt(sqrt(5) + 5) +
113
+ 120*sqrt(10)*sqrt(-sqrt(5) + 5)*sqrt(8*sqrt(5) + 40) +
114
+ 120*sqrt(10)*sqrt(-8*sqrt(5) + 40)*sqrt(sqrt(5) + 5))/(-36000 -
115
+ 7200*sqrt(5) + (12*sqrt(10)*sqrt(sqrt(5) + 5) +
116
+ 24*sqrt(10)*sqrt(-sqrt(5) + 5))**2))
117
+ assert radsimp(eq) is S.NaN # it's 0/0
118
+
119
+ # work with normal form
120
+ e = 1/sqrt(sqrt(7)/7 + 2*sqrt(2) + 3*sqrt(3) + 5*sqrt(5)) + 3
121
+ assert radsimp(e) == (
122
+ -sqrt(sqrt(7) + 14*sqrt(2) + 21*sqrt(3) +
123
+ 35*sqrt(5))*(-11654899*sqrt(35) - 1577436*sqrt(210) - 1278438*sqrt(15)
124
+ - 1346996*sqrt(10) + 1635060*sqrt(6) + 5709765 + 7539830*sqrt(14) +
125
+ 8291415*sqrt(21))/1300423175 + 3)
126
+
127
+ # obey power rules
128
+ base = sqrt(3) - sqrt(2)
129
+ assert radsimp(1/base**3) == (sqrt(3) + sqrt(2))**3
130
+ assert radsimp(1/(-base)**3) == -(sqrt(2) + sqrt(3))**3
131
+ assert radsimp(1/(-base)**x) == (-base)**(-x)
132
+ assert radsimp(1/base**x) == (sqrt(2) + sqrt(3))**x
133
+ assert radsimp(root(1/(-1 - sqrt(2)), -x)) == (-1)**(-1/x)*(1 + sqrt(2))**(1/x)
134
+
135
+ # recurse
136
+ e = cos(1/(1 + sqrt(2)))
137
+ assert radsimp(e) == cos(-sqrt(2) + 1)
138
+ assert radsimp(e/2) == cos(-sqrt(2) + 1)/2
139
+ assert radsimp(1/e) == 1/cos(-sqrt(2) + 1)
140
+ assert radsimp(2/e) == 2/cos(-sqrt(2) + 1)
141
+ assert fraction(radsimp(e/sqrt(x))) == (sqrt(x)*cos(-sqrt(2)+1), x)
142
+
143
+ # test that symbolic denominators are not processed
144
+ r = 1 + sqrt(2)
145
+ assert radsimp(x/r, symbolic=False) == -x*(-sqrt(2) + 1)
146
+ assert radsimp(x/(y + r), symbolic=False) == x/(y + 1 + sqrt(2))
147
+ assert radsimp(x/(y + r)/r, symbolic=False) == \
148
+ -x*(-sqrt(2) + 1)/(y + 1 + sqrt(2))
149
+
150
+ # issue 7408
151
+ eq = sqrt(x)/sqrt(y)
152
+ assert radsimp(eq) == umul(sqrt(x), sqrt(y), 1/y)
153
+ assert radsimp(eq, symbolic=False) == eq
154
+
155
+ # issue 7498
156
+ assert radsimp(sqrt(x)/sqrt(y)**3) == umul(sqrt(x), sqrt(y**3), 1/y**3)
157
+
158
+ # for coverage
159
+ eq = sqrt(x)/y**2
160
+ assert radsimp(eq) == eq
161
+
162
+
163
+ def test_radsimp_issue_3214():
164
+ c, p = symbols('c p', positive=True)
165
+ s = sqrt(c**2 - p**2)
166
+ b = (c + I*p - s)/(c + I*p + s)
167
+ assert radsimp(b) == -I*(c + I*p - sqrt(c**2 - p**2))**2/(2*c*p)
168
+
169
+
170
+ def test_collect_1():
171
+ """Collect with respect to Symbol"""
172
+ x, y, z, n = symbols('x,y,z,n')
173
+ assert collect(1, x) == 1
174
+ assert collect( x + y*x, x ) == x * (1 + y)
175
+ assert collect( x + x**2, x ) == x + x**2
176
+ assert collect( x**2 + y*x**2, x ) == (x**2)*(1 + y)
177
+ assert collect( x**2 + y*x, x ) == x*y + x**2
178
+ assert collect( 2*x**2 + y*x**2 + 3*x*y, [x] ) == x**2*(2 + y) + 3*x*y
179
+ assert collect( 2*x**2 + y*x**2 + 3*x*y, [y] ) == 2*x**2 + y*(x**2 + 3*x)
180
+
181
+ assert collect( ((1 + y + x)**4).expand(), x) == ((1 + y)**4).expand() + \
182
+ x*(4*(1 + y)**3).expand() + x**2*(6*(1 + y)**2).expand() + \
183
+ x**3*(4*(1 + y)).expand() + x**4
184
+ # symbols can be given as any iterable
185
+ expr = x + y
186
+ assert collect(expr, expr.free_symbols) == expr
187
+ assert collect(x*exp(x) + sin(x)*y + sin(x)*2 + 3*x, x, exact=None
188
+ ) == x*exp(x) + 3*x + (y + 2)*sin(x)
189
+ assert collect(x*exp(x) + sin(x)*y + sin(x)*2 + 3*x + y*x +
190
+ y*x*exp(x), x, exact=None
191
+ ) == x*exp(x)*(y + 1) + (3 + y)*x + (y + 2)*sin(x)
192
+
193
+
194
+ def test_collect_2():
195
+ """Collect with respect to a sum"""
196
+ a, b, x = symbols('a,b,x')
197
+ assert collect(a*(cos(x) + sin(x)) + b*(cos(x) + sin(x)),
198
+ sin(x) + cos(x)) == (a + b)*(cos(x) + sin(x))
199
+
200
+
201
+ def test_collect_3():
202
+ """Collect with respect to a product"""
203
+ a, b, c = symbols('a,b,c')
204
+ f = Function('f')
205
+ x, y, z, n = symbols('x,y,z,n')
206
+
207
+ assert collect(-x/8 + x*y, -x) == x*(y - Rational(1, 8))
208
+
209
+ assert collect( 1 + x*(y**2), x*y ) == 1 + x*(y**2)
210
+ assert collect( x*y + a*x*y, x*y) == x*y*(1 + a)
211
+ assert collect( 1 + x*y + a*x*y, x*y) == 1 + x*y*(1 + a)
212
+ assert collect(a*x*f(x) + b*(x*f(x)), x*f(x)) == x*(a + b)*f(x)
213
+
214
+ assert collect(a*x*log(x) + b*(x*log(x)), x*log(x)) == x*(a + b)*log(x)
215
+ assert collect(a*x**2*log(x)**2 + b*(x*log(x))**2, x*log(x)) == \
216
+ x**2*log(x)**2*(a + b)
217
+
218
+ # with respect to a product of three symbols
219
+ assert collect(y*x*z + a*x*y*z, x*y*z) == (1 + a)*x*y*z
220
+
221
+
222
+ def test_collect_4():
223
+ """Collect with respect to a power"""
224
+ a, b, c, x = symbols('a,b,c,x')
225
+
226
+ assert collect(a*x**c + b*x**c, x**c) == x**c*(a + b)
227
+ # issue 6096: 2 stays with c (unless c is integer or x is positive0
228
+ assert collect(a*x**(2*c) + b*x**(2*c), x**c) == x**(2*c)*(a + b)
229
+
230
+
231
+ def test_collect_5():
232
+ """Collect with respect to a tuple"""
233
+ a, x, y, z, n = symbols('a,x,y,z,n')
234
+ assert collect(x**2*y**4 + z*(x*y**2)**2 + z + a*z, [x*y**2, z]) in [
235
+ z*(1 + a + x**2*y**4) + x**2*y**4,
236
+ z*(1 + a) + x**2*y**4*(1 + z) ]
237
+ assert collect((1 + (x + y) + (x + y)**2).expand(),
238
+ [x, y]) == 1 + y + x*(1 + 2*y) + x**2 + y**2
239
+
240
+
241
+ def test_collect_pr19431():
242
+ """Unevaluated collect with respect to a product"""
243
+ a = symbols('a')
244
+ assert collect(a**2*(a**2 + 1), a**2, evaluate=False)[a**2] == (a**2 + 1)
245
+
246
+
247
+ def test_collect_D():
248
+ D = Derivative
249
+ f = Function('f')
250
+ x, a, b = symbols('x,a,b')
251
+ fx = D(f(x), x)
252
+ fxx = D(f(x), x, x)
253
+
254
+ assert collect(a*fx + b*fx, fx) == (a + b)*fx
255
+ assert collect(a*D(fx, x) + b*D(fx, x), fx) == (a + b)*D(fx, x)
256
+ assert collect(a*fxx + b*fxx, fx) == (a + b)*D(fx, x)
257
+ # issue 4784
258
+ assert collect(5*f(x) + 3*fx, fx) == 5*f(x) + 3*fx
259
+ assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x)) == \
260
+ (x*f(x) + f(x))*D(f(x), x) + f(x)
261
+ assert collect(f(x) + f(x)*diff(f(x), x) + x*diff(f(x), x)*f(x), f(x).diff(x), exact=True) == \
262
+ (x*f(x) + f(x))*D(f(x), x) + f(x)
263
+ assert collect(1/f(x) + 1/f(x)*diff(f(x), x) + x*diff(f(x), x)/f(x), f(x).diff(x), exact=True) == \
264
+ (1/f(x) + x/f(x))*D(f(x), x) + 1/f(x)
265
+ e = (1 + x*fx + fx)/f(x)
266
+ assert collect(e.expand(), fx) == fx*(x/f(x) + 1/f(x)) + 1/f(x)
267
+
268
+
269
+ def test_collect_func():
270
+ f = ((x + a + 1)**3).expand()
271
+
272
+ assert collect(f, x) == a**3 + 3*a**2 + 3*a + x**3 + x**2*(3*a + 3) + \
273
+ x*(3*a**2 + 6*a + 3) + 1
274
+ assert collect(f, x, factor) == x**3 + 3*x**2*(a + 1) + 3*x*(a + 1)**2 + \
275
+ (a + 1)**3
276
+
277
+ assert collect(f, x, evaluate=False) == {
278
+ S.One: a**3 + 3*a**2 + 3*a + 1,
279
+ x: 3*a**2 + 6*a + 3, x**2: 3*a + 3,
280
+ x**3: 1
281
+ }
282
+
283
+ assert collect(f, x, factor, evaluate=False) == {
284
+ S.One: (a + 1)**3, x: 3*(a + 1)**2,
285
+ x**2: umul(S(3), a + 1), x**3: 1}
286
+
287
+
288
+ def test_collect_order():
289
+ a, b, x, t = symbols('a,b,x,t')
290
+
291
+ assert collect(t + t*x + t*x**2 + O(x**3), t) == t*(1 + x + x**2 + O(x**3))
292
+ assert collect(t + t*x + x**2 + O(x**3), t) == \
293
+ t*(1 + x + O(x**3)) + x**2 + O(x**3)
294
+
295
+ f = a*x + b*x + c*x**2 + d*x**2 + O(x**3)
296
+ g = x*(a + b) + x**2*(c + d) + O(x**3)
297
+
298
+ assert collect(f, x) == g
299
+ assert collect(f, x, distribute_order_term=False) == g
300
+
301
+ f = sin(a + b).series(b, 0, 10)
302
+
303
+ assert collect(f, [sin(a), cos(a)]) == \
304
+ sin(a)*cos(b).series(b, 0, 10) + cos(a)*sin(b).series(b, 0, 10)
305
+ assert collect(f, [sin(a), cos(a)], distribute_order_term=False) == \
306
+ sin(a)*cos(b).series(b, 0, 10).removeO() + \
307
+ cos(a)*sin(b).series(b, 0, 10).removeO() + O(b**10)
308
+
309
+
310
+ def test_rcollect():
311
+ assert rcollect((x**2*y + x*y + x + y)/(x + y), y) == \
312
+ (x + y*(1 + x + x**2))/(x + y)
313
+ assert rcollect(sqrt(-((x + 1)*(y + 1))), z) == sqrt(-((x + 1)*(y + 1)))
314
+
315
+
316
+ def test_collect_D_0():
317
+ D = Derivative
318
+ f = Function('f')
319
+ x, a, b = symbols('x,a,b')
320
+ fxx = D(f(x), x, x)
321
+
322
+ assert collect(a*fxx + b*fxx, fxx) == (a + b)*fxx
323
+
324
+
325
+ def test_collect_Wild():
326
+ """Collect with respect to functions with Wild argument"""
327
+ a, b, x, y = symbols('a b x y')
328
+ f = Function('f')
329
+ w1 = Wild('.1')
330
+ w2 = Wild('.2')
331
+ assert collect(f(x) + a*f(x), f(w1)) == (1 + a)*f(x)
332
+ assert collect(f(x, y) + a*f(x, y), f(w1)) == f(x, y) + a*f(x, y)
333
+ assert collect(f(x, y) + a*f(x, y), f(w1, w2)) == (1 + a)*f(x, y)
334
+ assert collect(f(x, y) + a*f(x, y), f(w1, w1)) == f(x, y) + a*f(x, y)
335
+ assert collect(f(x, x) + a*f(x, x), f(w1, w1)) == (1 + a)*f(x, x)
336
+ assert collect(a*(x + 1)**y + (x + 1)**y, w1**y) == (1 + a)*(x + 1)**y
337
+ assert collect(a*(x + 1)**y + (x + 1)**y, w1**b) == \
338
+ a*(x + 1)**y + (x + 1)**y
339
+ assert collect(a*(x + 1)**y + (x + 1)**y, (x + 1)**w2) == \
340
+ (1 + a)*(x + 1)**y
341
+ assert collect(a*(x + 1)**y + (x + 1)**y, w1**w2) == (1 + a)*(x + 1)**y
342
+
343
+
344
+ def test_collect_const():
345
+ # coverage not provided by above tests
346
+ assert collect_const(2*sqrt(3) + 4*a*sqrt(5)) == \
347
+ 2*(2*sqrt(5)*a + sqrt(3)) # let the primitive reabsorb
348
+ assert collect_const(2*sqrt(3) + 4*a*sqrt(5), sqrt(3)) == \
349
+ 2*sqrt(3) + 4*a*sqrt(5)
350
+ assert collect_const(sqrt(2)*(1 + sqrt(2)) + sqrt(3) + x*sqrt(2)) == \
351
+ sqrt(2)*(x + 1 + sqrt(2)) + sqrt(3)
352
+
353
+ # issue 5290
354
+ assert collect_const(2*x + 2*y + 1, 2) == \
355
+ collect_const(2*x + 2*y + 1) == \
356
+ Add(S.One, Mul(2, x + y, evaluate=False), evaluate=False)
357
+ assert collect_const(-y - z) == Mul(-1, y + z, evaluate=False)
358
+ assert collect_const(2*x - 2*y - 2*z, 2) == \
359
+ Mul(2, x - y - z, evaluate=False)
360
+ assert collect_const(2*x - 2*y - 2*z, -2) == \
361
+ _unevaluated_Add(2*x, Mul(-2, y + z, evaluate=False))
362
+
363
+ # this is why the content_primitive is used
364
+ eq = (sqrt(15 + 5*sqrt(2))*x + sqrt(3 + sqrt(2))*y)*2
365
+ assert collect_sqrt(eq + 2) == \
366
+ 2*sqrt(sqrt(2) + 3)*(sqrt(5)*x + y) + 2
367
+
368
+ # issue 16296
369
+ assert collect_const(a + b + x/2 + y/2) == a + b + Mul(S.Half, x + y, evaluate=False)
370
+
371
+
372
+ def test_issue_13143():
373
+ f = Function('f')
374
+ fx = f(x).diff(x)
375
+ e = f(x) + fx + f(x)*fx
376
+ # collect function before derivative
377
+ assert collect(e, Wild('w')) == f(x)*(fx + 1) + fx
378
+ e = f(x) + f(x)*fx + x*fx*f(x)
379
+ assert collect(e, fx) == (x*f(x) + f(x))*fx + f(x)
380
+ assert collect(e, f(x)) == (x*fx + fx + 1)*f(x)
381
+ e = f(x) + fx + f(x)*fx
382
+ assert collect(e, [f(x), fx]) == f(x)*(1 + fx) + fx
383
+ assert collect(e, [fx, f(x)]) == fx*(1 + f(x)) + f(x)
384
+
385
+
386
+ def test_issue_6097():
387
+ assert collect(a*y**(2.0*x) + b*y**(2.0*x), y**x) == (a + b)*(y**x)**2.0
388
+ assert collect(a*2**(2.0*x) + b*2**(2.0*x), 2**x) == (a + b)*(2**x)**2.0
389
+
390
+
391
+ def test_fraction_expand():
392
+ eq = (x + y)*y/x
393
+ assert eq.expand(frac=True) == fraction_expand(eq) == (x*y + y**2)/x
394
+ assert eq.expand() == y + y**2/x
395
+
396
+
397
+ def test_fraction():
398
+ x, y, z = map(Symbol, 'xyz')
399
+ A = Symbol('A', commutative=False)
400
+
401
+ assert fraction(S.Half) == (1, 2)
402
+
403
+ assert fraction(x) == (x, 1)
404
+ assert fraction(1/x) == (1, x)
405
+ assert fraction(x/y) == (x, y)
406
+ assert fraction(x/2) == (x, 2)
407
+
408
+ assert fraction(x*y/z) == (x*y, z)
409
+ assert fraction(x/(y*z)) == (x, y*z)
410
+
411
+ assert fraction(1/y**2) == (1, y**2)
412
+ assert fraction(x/y**2) == (x, y**2)
413
+
414
+ assert fraction((x**2 + 1)/y) == (x**2 + 1, y)
415
+ assert fraction(x*(y + 1)/y**7) == (x*(y + 1), y**7)
416
+
417
+ assert fraction(exp(-x), exact=True) == (exp(-x), 1)
418
+ assert fraction((1/(x + y))/2, exact=True) == (1, Mul(2,(x + y), evaluate=False))
419
+
420
+ assert fraction(x*A/y) == (x*A, y)
421
+ assert fraction(x*A**-1/y) == (x*A**-1, y)
422
+
423
+ n = symbols('n', negative=True)
424
+ assert fraction(exp(n)) == (1, exp(-n))
425
+ assert fraction(exp(-n)) == (exp(-n), 1)
426
+
427
+ p = symbols('p', positive=True)
428
+ assert fraction(exp(-p)*log(p), exact=True) == (exp(-p)*log(p), 1)
429
+
430
+ m = Mul(1, 1, S.Half, evaluate=False)
431
+ assert fraction(m) == (1, 2)
432
+ assert fraction(m, exact=True) == (Mul(1, 1, evaluate=False), 2)
433
+
434
+ m = Mul(1, 1, S.Half, S.Half, Pow(1, -1, evaluate=False), evaluate=False)
435
+ assert fraction(m) == (1, 4)
436
+ assert fraction(m, exact=True) == \
437
+ (Mul(1, 1, evaluate=False), Mul(2, 2, 1, evaluate=False))
438
+
439
+
440
+ def test_issue_5615():
441
+ aA, Re, a, b, D = symbols('aA Re a b D')
442
+ e = ((D**3*a + b*aA**3)/Re).expand()
443
+ assert collect(e, [aA**3/Re, a]) == e
444
+
445
+
446
+ def test_issue_5933():
447
+ from sympy.geometry.polygon import (Polygon, RegularPolygon)
448
+ from sympy.simplify.radsimp import denom
449
+ x = Polygon(*RegularPolygon((0, 0), 1, 5).vertices).centroid.x
450
+ assert abs(denom(x).n()) > 1e-12
451
+ assert abs(denom(radsimp(x))) > 1e-12 # in case simplify didn't handle it
452
+
453
+
454
+ def test_issue_14608():
455
+ a, b = symbols('a b', commutative=False)
456
+ x, y = symbols('x y')
457
+ raises(AttributeError, lambda: collect(a*b + b*a, a))
458
+ assert collect(x*y + y*(x+1), a) == x*y + y*(x+1)
459
+ assert collect(x*y + y*(x+1) + a*b + b*a, y) == y*(2*x + 1) + a*b + b*a
460
+
461
+
462
+ def test_collect_abs():
463
+ s = abs(x) + abs(y)
464
+ assert collect_abs(s) == s
465
+ assert unchanged(Mul, abs(x), abs(y))
466
+ ans = Abs(x*y)
467
+ assert isinstance(ans, Abs)
468
+ assert collect_abs(abs(x)*abs(y)) == ans
469
+ assert collect_abs(1 + exp(abs(x)*abs(y))) == 1 + exp(ans)
470
+
471
+ # See https://github.com/sympy/sympy/issues/12910
472
+ p = Symbol('p', positive=True)
473
+ assert collect_abs(p/abs(1-p)).is_commutative is True
474
+
475
+
476
+ def test_issue_19149():
477
+ eq = exp(3*x/4)
478
+ assert collect(eq, exp(x)) == eq
479
+
480
+ def test_issue_19719():
481
+ a, b = symbols('a, b')
482
+ expr = a**2 * (b + 1) + (7 + 1/b)/a
483
+ collected = collect(expr, (a**2, 1/a), evaluate=False)
484
+ # Would return {_Dummy_20**(-2): b + 1, 1/a: 7 + 1/b} without xreplace
485
+ assert collected == {a**2: b + 1, 1/a: 7 + 1/b}
486
+
487
+
488
+ def test_issue_21355():
489
+ assert radsimp(1/(x + sqrt(x**2))) == 1/(x + sqrt(x**2))
490
+ assert radsimp(1/(x - sqrt(x**2))) == 1/(x - sqrt(x**2))
lib/python3.12/site-packages/sympy/simplify/tests/test_ratsimp.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.numbers import (Rational, pi)
2
+ from sympy.functions.elementary.exponential import log
3
+ from sympy.functions.elementary.miscellaneous import sqrt
4
+ from sympy.functions.special.error_functions import erf
5
+ from sympy.polys.domains import GF
6
+ from sympy.simplify.ratsimp import (ratsimp, ratsimpmodprime)
7
+
8
+ from sympy.abc import x, y, z, t, a, b, c, d, e
9
+
10
+
11
+ def test_ratsimp():
12
+ f, g = 1/x + 1/y, (x + y)/(x*y)
13
+
14
+ assert f != g and ratsimp(f) == g
15
+
16
+ f, g = 1/(1 + 1/x), 1 - 1/(x + 1)
17
+
18
+ assert f != g and ratsimp(f) == g
19
+
20
+ f, g = x/(x + y) + y/(x + y), 1
21
+
22
+ assert f != g and ratsimp(f) == g
23
+
24
+ f, g = -x - y - y**2/(x + y) + x**2/(x + y), -2*y
25
+
26
+ assert f != g and ratsimp(f) == g
27
+
28
+ f = (a*c*x*y + a*c*z - b*d*x*y - b*d*z - b*t*x*y - b*t*x - b*t*z +
29
+ e*x)/(x*y + z)
30
+ G = [a*c - b*d - b*t + (-b*t*x + e*x)/(x*y + z),
31
+ a*c - b*d - b*t - ( b*t*x - e*x)/(x*y + z)]
32
+
33
+ assert f != g and ratsimp(f) in G
34
+
35
+ A = sqrt(pi)
36
+
37
+ B = log(erf(x) - 1)
38
+ C = log(erf(x) + 1)
39
+
40
+ D = 8 - 8*erf(x)
41
+
42
+ f = A*B/D - A*C/D + A*C*erf(x)/D - A*B*erf(x)/D + 2*A/D
43
+
44
+ assert ratsimp(f) == A*B/8 - A*C/8 - A/(4*erf(x) - 4)
45
+
46
+
47
+ def test_ratsimpmodprime():
48
+ a = y**5 + x + y
49
+ b = x - y
50
+ F = [x*y**5 - x - y]
51
+ assert ratsimpmodprime(a/b, F, x, y, order='lex') == \
52
+ (-x**2 - x*y - x - y) / (-x**2 + x*y)
53
+
54
+ a = x + y**2 - 2
55
+ b = x + y**2 - y - 1
56
+ F = [x*y - 1]
57
+ assert ratsimpmodprime(a/b, F, x, y, order='lex') == \
58
+ (1 + y - x)/(y - x)
59
+
60
+ a = 5*x**3 + 21*x**2 + 4*x*y + 23*x + 12*y + 15
61
+ b = 7*x**3 - y*x**2 + 31*x**2 + 2*x*y + 15*y + 37*x + 21
62
+ F = [x**2 + y**2 - 1]
63
+ assert ratsimpmodprime(a/b, F, x, y, order='lex') == \
64
+ (1 + 5*y - 5*x)/(8*y - 6*x)
65
+
66
+ a = x*y - x - 2*y + 4
67
+ b = x + y**2 - 2*y
68
+ F = [x - 2, y - 3]
69
+ assert ratsimpmodprime(a/b, F, x, y, order='lex') == \
70
+ Rational(2, 5)
71
+
72
+ # Test a bug where denominators would be dropped
73
+ assert ratsimpmodprime(x, [y - 2*x], order='lex') == \
74
+ y/2
75
+
76
+ a = (x**5 + 2*x**4 + 2*x**3 + 2*x**2 + x + 2/x + x**(-2))
77
+ assert ratsimpmodprime(a, [x + 1], domain=GF(2)) == 1
78
+ assert ratsimpmodprime(a, [x + 1], domain=GF(3)) == -1
lib/python3.12/site-packages/sympy/simplify/tests/test_rewrite.py ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.numbers import I
2
+ from sympy.core.symbol import symbols
3
+ from sympy.functions.elementary.exponential import exp
4
+ from sympy.functions.elementary.trigonometric import (cos, cot, sin)
5
+ from sympy.testing.pytest import _both_exp_pow
6
+
7
+ x, y, z, n = symbols('x,y,z,n')
8
+
9
+
10
+ @_both_exp_pow
11
+ def test_has():
12
+ assert cot(x).has(x)
13
+ assert cot(x).has(cot)
14
+ assert not cot(x).has(sin)
15
+ assert sin(x).has(x)
16
+ assert sin(x).has(sin)
17
+ assert not sin(x).has(cot)
18
+ assert exp(x).has(exp)
19
+
20
+
21
+ @_both_exp_pow
22
+ def test_sin_exp_rewrite():
23
+ assert sin(x).rewrite(sin, exp) == -I/2*(exp(I*x) - exp(-I*x))
24
+ assert sin(x).rewrite(sin, exp).rewrite(exp, sin) == sin(x)
25
+ assert cos(x).rewrite(cos, exp).rewrite(exp, cos) == cos(x)
26
+ assert (sin(5*y) - sin(
27
+ 2*x)).rewrite(sin, exp).rewrite(exp, sin) == sin(5*y) - sin(2*x)
28
+ assert sin(x + y).rewrite(sin, exp).rewrite(exp, sin) == sin(x + y)
29
+ assert cos(x + y).rewrite(cos, exp).rewrite(exp, cos) == cos(x + y)
30
+ # This next test currently passes... not clear whether it should or not?
31
+ assert cos(x).rewrite(cos, exp).rewrite(exp, sin) == cos(x)
lib/python3.12/site-packages/sympy/simplify/tests/test_simplify.py ADDED
@@ -0,0 +1,1087 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.concrete.summations import Sum
2
+ from sympy.core.add import Add
3
+ from sympy.core.basic import Basic
4
+ from sympy.core.expr import unchanged
5
+ from sympy.core.function import (count_ops, diff, expand, expand_multinomial, Function, Derivative)
6
+ from sympy.core.mul import Mul, _keep_coeff
7
+ from sympy.core import GoldenRatio
8
+ from sympy.core.numbers import (E, Float, I, oo, pi, Rational, zoo)
9
+ from sympy.core.relational import (Eq, Lt, Gt, Ge, Le)
10
+ from sympy.core.singleton import S
11
+ from sympy.core.symbol import (Symbol, symbols)
12
+ from sympy.core.sympify import sympify
13
+ from sympy.functions.combinatorial.factorials import (binomial, factorial)
14
+ from sympy.functions.elementary.complexes import (Abs, sign)
15
+ from sympy.functions.elementary.exponential import (exp, exp_polar, log)
16
+ from sympy.functions.elementary.hyperbolic import (cosh, csch, sinh)
17
+ from sympy.functions.elementary.miscellaneous import sqrt
18
+ from sympy.functions.elementary.piecewise import Piecewise
19
+ from sympy.functions.elementary.trigonometric import (acos, asin, atan, cos, sin, sinc, tan)
20
+ from sympy.functions.special.error_functions import erf
21
+ from sympy.functions.special.gamma_functions import gamma
22
+ from sympy.functions.special.hyper import hyper
23
+ from sympy.functions.special.tensor_functions import KroneckerDelta
24
+ from sympy.geometry.polygon import rad
25
+ from sympy.integrals.integrals import (Integral, integrate)
26
+ from sympy.logic.boolalg import (And, Or)
27
+ from sympy.matrices.dense import (Matrix, eye)
28
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
29
+ from sympy.polys.polytools import (factor, Poly)
30
+ from sympy.simplify.simplify import (besselsimp, hypersimp, inversecombine, logcombine, nsimplify, nthroot, posify, separatevars, signsimp, simplify)
31
+ from sympy.solvers.solvers import solve
32
+
33
+ from sympy.testing.pytest import XFAIL, slow, _both_exp_pow
34
+ from sympy.abc import x, y, z, t, a, b, c, d, e, f, g, h, i, n
35
+
36
+
37
+ def test_issue_7263():
38
+ assert abs((simplify(30.8**2 - 82.5**2 * sin(rad(11.6))**2)).evalf() - \
39
+ 673.447451402970) < 1e-12
40
+
41
+
42
+ def test_factorial_simplify():
43
+ # There are more tests in test_factorials.py.
44
+ x = Symbol('x')
45
+ assert simplify(factorial(x)/x) == gamma(x)
46
+ assert simplify(factorial(factorial(x))) == factorial(factorial(x))
47
+
48
+
49
+ def test_simplify_expr():
50
+ x, y, z, k, n, m, w, s, A = symbols('x,y,z,k,n,m,w,s,A')
51
+ f = Function('f')
52
+
53
+ assert all(simplify(tmp) == tmp for tmp in [I, E, oo, x, -x, -oo, -E, -I])
54
+
55
+ e = 1/x + 1/y
56
+ assert e != (x + y)/(x*y)
57
+ assert simplify(e) == (x + y)/(x*y)
58
+
59
+ e = A**2*s**4/(4*pi*k*m**3)
60
+ assert simplify(e) == e
61
+
62
+ e = (4 + 4*x - 2*(2 + 2*x))/(2 + 2*x)
63
+ assert simplify(e) == 0
64
+
65
+ e = (-4*x*y**2 - 2*y**3 - 2*x**2*y)/(x + y)**2
66
+ assert simplify(e) == -2*y
67
+
68
+ e = -x - y - (x + y)**(-1)*y**2 + (x + y)**(-1)*x**2
69
+ assert simplify(e) == -2*y
70
+
71
+ e = (x + x*y)/x
72
+ assert simplify(e) == 1 + y
73
+
74
+ e = (f(x) + y*f(x))/f(x)
75
+ assert simplify(e) == 1 + y
76
+
77
+ e = (2 * (1/n - cos(n * pi)/n))/pi
78
+ assert simplify(e) == (-cos(pi*n) + 1)/(pi*n)*2
79
+
80
+ e = integrate(1/(x**3 + 1), x).diff(x)
81
+ assert simplify(e) == 1/(x**3 + 1)
82
+
83
+ e = integrate(x/(x**2 + 3*x + 1), x).diff(x)
84
+ assert simplify(e) == x/(x**2 + 3*x + 1)
85
+
86
+ f = Symbol('f')
87
+ A = Matrix([[2*k - m*w**2, -k], [-k, k - m*w**2]]).inv()
88
+ assert simplify((A*Matrix([0, f]))[1] -
89
+ (-f*(2*k - m*w**2)/(k**2 - (k - m*w**2)*(2*k - m*w**2)))) == 0
90
+
91
+ f = -x + y/(z + t) + z*x/(z + t) + z*a/(z + t) + t*x/(z + t)
92
+ assert simplify(f) == (y + a*z)/(z + t)
93
+
94
+ # issue 10347
95
+ expr = -x*(y**2 - 1)*(2*y**2*(x**2 - 1)/(a*(x**2 - y**2)**2) + (x**2 - 1)
96
+ /(a*(x**2 - y**2)))/(a*(x**2 - y**2)) + x*(-2*x**2*sqrt(-x**2*y**2 + x**2
97
+ + y**2 - 1)*sin(z)/(a*(x**2 - y**2)**2) - x**2*sqrt(-x**2*y**2 + x**2 +
98
+ y**2 - 1)*sin(z)/(a*(x**2 - 1)*(x**2 - y**2)) + (x**2*sqrt((-x**2 + 1)*
99
+ (y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*sin(z)/(x**2 - 1) + sqrt(
100
+ (-x**2 + 1)*(y**2 - 1))*(x*(-x*y**2 + x)/sqrt(-x**2*y**2 + x**2 + y**2 -
101
+ 1) + sqrt(-x**2*y**2 + x**2 + y**2 - 1))*sin(z))/(a*sqrt((-x**2 + 1)*(
102
+ y**2 - 1))*(x**2 - y**2)))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*sin(z)/(a*
103
+ (x**2 - y**2)) + x*(-2*x**2*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z)/(a*
104
+ (x**2 - y**2)**2) - x**2*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z)/(a*
105
+ (x**2 - 1)*(x**2 - y**2)) + (x**2*sqrt((-x**2 + 1)*(y**2 - 1))*sqrt(-x**2
106
+ *y**2 + x**2 + y**2 - 1)*cos(z)/(x**2 - 1) + x*sqrt((-x**2 + 1)*(y**2 -
107
+ 1))*(-x*y**2 + x)*cos(z)/sqrt(-x**2*y**2 + x**2 + y**2 - 1) + sqrt((-x**2
108
+ + 1)*(y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z))/(a*sqrt((-x**2
109
+ + 1)*(y**2 - 1))*(x**2 - y**2)))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(
110
+ z)/(a*(x**2 - y**2)) - y*sqrt((-x**2 + 1)*(y**2 - 1))*(-x*y*sqrt(-x**2*
111
+ y**2 + x**2 + y**2 - 1)*sin(z)/(a*(x**2 - y**2)*(y**2 - 1)) + 2*x*y*sqrt(
112
+ -x**2*y**2 + x**2 + y**2 - 1)*sin(z)/(a*(x**2 - y**2)**2) + (x*y*sqrt((
113
+ -x**2 + 1)*(y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*sin(z)/(y**2 -
114
+ 1) + x*sqrt((-x**2 + 1)*(y**2 - 1))*(-x**2*y + y)*sin(z)/sqrt(-x**2*y**2
115
+ + x**2 + y**2 - 1))/(a*sqrt((-x**2 + 1)*(y**2 - 1))*(x**2 - y**2)))*sin(
116
+ z)/(a*(x**2 - y**2)) + y*(x**2 - 1)*(-2*x*y*(x**2 - 1)/(a*(x**2 - y**2)
117
+ **2) + 2*x*y/(a*(x**2 - y**2)))/(a*(x**2 - y**2)) + y*(x**2 - 1)*(y**2 -
118
+ 1)*(-x*y*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z)/(a*(x**2 - y**2)*(y**2
119
+ - 1)) + 2*x*y*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z)/(a*(x**2 - y**2)
120
+ **2) + (x*y*sqrt((-x**2 + 1)*(y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 -
121
+ 1)*cos(z)/(y**2 - 1) + x*sqrt((-x**2 + 1)*(y**2 - 1))*(-x**2*y + y)*cos(
122
+ z)/sqrt(-x**2*y**2 + x**2 + y**2 - 1))/(a*sqrt((-x**2 + 1)*(y**2 - 1)
123
+ )*(x**2 - y**2)))*cos(z)/(a*sqrt((-x**2 + 1)*(y**2 - 1))*(x**2 - y**2)
124
+ ) - x*sqrt((-x**2 + 1)*(y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*sin(
125
+ z)**2/(a**2*(x**2 - 1)*(x**2 - y**2)*(y**2 - 1)) - x*sqrt((-x**2 + 1)*(
126
+ y**2 - 1))*sqrt(-x**2*y**2 + x**2 + y**2 - 1)*cos(z)**2/(a**2*(x**2 - 1)*(
127
+ x**2 - y**2)*(y**2 - 1))
128
+ assert simplify(expr) == 2*x/(a**2*(x**2 - y**2))
129
+
130
+ #issue 17631
131
+ assert simplify('((-1/2)*Boole(True)*Boole(False)-1)*Boole(True)') == \
132
+ Mul(sympify('(2 + Boole(True)*Boole(False))'), sympify('-Boole(True)/2'))
133
+
134
+ A, B = symbols('A,B', commutative=False)
135
+
136
+ assert simplify(A*B - B*A) == A*B - B*A
137
+ assert simplify(A/(1 + y/x)) == x*A/(x + y)
138
+ assert simplify(A*(1/x + 1/y)) == A/x + A/y #(x + y)*A/(x*y)
139
+
140
+ assert simplify(log(2) + log(3)) == log(6)
141
+ assert simplify(log(2*x) - log(2)) == log(x)
142
+
143
+ assert simplify(hyper([], [], x)) == exp(x)
144
+
145
+
146
+ def test_issue_3557():
147
+ f_1 = x*a + y*b + z*c - 1
148
+ f_2 = x*d + y*e + z*f - 1
149
+ f_3 = x*g + y*h + z*i - 1
150
+
151
+ solutions = solve([f_1, f_2, f_3], x, y, z, simplify=False)
152
+
153
+ assert simplify(solutions[y]) == \
154
+ (a*i + c*d + f*g - a*f - c*g - d*i)/ \
155
+ (a*e*i + b*f*g + c*d*h - a*f*h - b*d*i - c*e*g)
156
+
157
+
158
+ def test_simplify_other():
159
+ assert simplify(sin(x)**2 + cos(x)**2) == 1
160
+ assert simplify(gamma(x + 1)/gamma(x)) == x
161
+ assert simplify(sin(x)**2 + cos(x)**2 + factorial(x)/gamma(x)) == 1 + x
162
+ assert simplify(
163
+ Eq(sin(x)**2 + cos(x)**2, factorial(x)/gamma(x))) == Eq(x, 1)
164
+ nc = symbols('nc', commutative=False)
165
+ assert simplify(x + x*nc) == x*(1 + nc)
166
+ # issue 6123
167
+ # f = exp(-I*(k*sqrt(t) + x/(2*sqrt(t)))**2)
168
+ # ans = integrate(f, (k, -oo, oo), conds='none')
169
+ ans = I*(-pi*x*exp(I*pi*Rational(-3, 4) + I*x**2/(4*t))*erf(x*exp(I*pi*Rational(-3, 4))/
170
+ (2*sqrt(t)))/(2*sqrt(t)) + pi*x*exp(I*pi*Rational(-3, 4) + I*x**2/(4*t))/
171
+ (2*sqrt(t)))*exp(-I*x**2/(4*t))/(sqrt(pi)*x) - I*sqrt(pi) * \
172
+ (-erf(x*exp(I*pi/4)/(2*sqrt(t))) + 1)*exp(I*pi/4)/(2*sqrt(t))
173
+ assert simplify(ans) == -(-1)**Rational(3, 4)*sqrt(pi)/sqrt(t)
174
+ # issue 6370
175
+ assert simplify(2**(2 + x)/4) == 2**x
176
+
177
+
178
+ @_both_exp_pow
179
+ def test_simplify_complex():
180
+ cosAsExp = cos(x)._eval_rewrite_as_exp(x)
181
+ tanAsExp = tan(x)._eval_rewrite_as_exp(x)
182
+ assert simplify(cosAsExp*tanAsExp) == sin(x) # issue 4341
183
+
184
+ # issue 10124
185
+ assert simplify(exp(Matrix([[0, -1], [1, 0]]))) == Matrix([[cos(1),
186
+ -sin(1)], [sin(1), cos(1)]])
187
+
188
+
189
+ def test_simplify_ratio():
190
+ # roots of x**3-3*x+5
191
+ roots = ['(1/2 - sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3) + 1/((1/2 - '
192
+ 'sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3))',
193
+ '1/((1/2 + sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3)) + '
194
+ '(1/2 + sqrt(3)*I/2)*(sqrt(21)/2 + 5/2)**(1/3)',
195
+ '-(sqrt(21)/2 + 5/2)**(1/3) - 1/(sqrt(21)/2 + 5/2)**(1/3)']
196
+
197
+ for r in roots:
198
+ r = S(r)
199
+ assert count_ops(simplify(r, ratio=1)) <= count_ops(r)
200
+ # If ratio=oo, simplify() is always applied:
201
+ assert simplify(r, ratio=oo) is not r
202
+
203
+
204
+ def test_simplify_measure():
205
+ measure1 = lambda expr: len(str(expr))
206
+ measure2 = lambda expr: -count_ops(expr)
207
+ # Return the most complicated result
208
+ expr = (x + 1)/(x + sin(x)**2 + cos(x)**2)
209
+ assert measure1(simplify(expr, measure=measure1)) <= measure1(expr)
210
+ assert measure2(simplify(expr, measure=measure2)) <= measure2(expr)
211
+
212
+ expr2 = Eq(sin(x)**2 + cos(x)**2, 1)
213
+ assert measure1(simplify(expr2, measure=measure1)) <= measure1(expr2)
214
+ assert measure2(simplify(expr2, measure=measure2)) <= measure2(expr2)
215
+
216
+
217
+ def test_simplify_rational():
218
+ expr = 2**x*2.**y
219
+ assert simplify(expr, rational = True) == 2**(x+y)
220
+ assert simplify(expr, rational = None) == 2.0**(x+y)
221
+ assert simplify(expr, rational = False) == expr
222
+ assert simplify('0.9 - 0.8 - 0.1', rational = True) == 0
223
+
224
+
225
+ def test_simplify_issue_1308():
226
+ assert simplify(exp(Rational(-1, 2)) + exp(Rational(-3, 2))) == \
227
+ (1 + E)*exp(Rational(-3, 2))
228
+
229
+
230
+ def test_issue_5652():
231
+ assert simplify(E + exp(-E)) == exp(-E) + E
232
+ n = symbols('n', commutative=False)
233
+ assert simplify(n + n**(-n)) == n + n**(-n)
234
+
235
+
236
+ def test_simplify_fail1():
237
+ x = Symbol('x')
238
+ y = Symbol('y')
239
+ e = (x + y)**2/(-4*x*y**2 - 2*y**3 - 2*x**2*y)
240
+ assert simplify(e) == 1 / (-2*y)
241
+
242
+
243
+ def test_nthroot():
244
+ assert nthroot(90 + 34*sqrt(7), 3) == sqrt(7) + 3
245
+ q = 1 + sqrt(2) - 2*sqrt(3) + sqrt(6) + sqrt(7)
246
+ assert nthroot(expand_multinomial(q**3), 3) == q
247
+ assert nthroot(41 + 29*sqrt(2), 5) == 1 + sqrt(2)
248
+ assert nthroot(-41 - 29*sqrt(2), 5) == -1 - sqrt(2)
249
+ expr = 1320*sqrt(10) + 4216 + 2576*sqrt(6) + 1640*sqrt(15)
250
+ assert nthroot(expr, 5) == 1 + sqrt(6) + sqrt(15)
251
+ q = 1 + sqrt(2) + sqrt(3) + sqrt(5)
252
+ assert expand_multinomial(nthroot(expand_multinomial(q**5), 5)) == q
253
+ q = 1 + sqrt(2) + 7*sqrt(6) + 2*sqrt(10)
254
+ assert nthroot(expand_multinomial(q**5), 5, 8) == q
255
+ q = 1 + sqrt(2) - 2*sqrt(3) + 1171*sqrt(6)
256
+ assert nthroot(expand_multinomial(q**3), 3) == q
257
+ assert nthroot(expand_multinomial(q**6), 6) == q
258
+
259
+
260
+ def test_nthroot1():
261
+ q = 1 + sqrt(2) + sqrt(3) + S.One/10**20
262
+ p = expand_multinomial(q**5)
263
+ assert nthroot(p, 5) == q
264
+ q = 1 + sqrt(2) + sqrt(3) + S.One/10**30
265
+ p = expand_multinomial(q**5)
266
+ assert nthroot(p, 5) == q
267
+
268
+
269
+ @_both_exp_pow
270
+ def test_separatevars():
271
+ x, y, z, n = symbols('x,y,z,n')
272
+ assert separatevars(2*n*x*z + 2*x*y*z) == 2*x*z*(n + y)
273
+ assert separatevars(x*z + x*y*z) == x*z*(1 + y)
274
+ assert separatevars(pi*x*z + pi*x*y*z) == pi*x*z*(1 + y)
275
+ assert separatevars(x*y**2*sin(x) + x*sin(x)*sin(y)) == \
276
+ x*(sin(y) + y**2)*sin(x)
277
+ assert separatevars(x*exp(x + y) + x*exp(x)) == x*(1 + exp(y))*exp(x)
278
+ assert separatevars((x*(y + 1))**z).is_Pow # != x**z*(1 + y)**z
279
+ assert separatevars(1 + x + y + x*y) == (x + 1)*(y + 1)
280
+ assert separatevars(y/pi*exp(-(z - x)/cos(n))) == \
281
+ y*exp(x/cos(n))*exp(-z/cos(n))/pi
282
+ assert separatevars((x + y)*(x - y) + y**2 + 2*x + 1) == (x + 1)**2
283
+ # issue 4858
284
+ p = Symbol('p', positive=True)
285
+ assert separatevars(sqrt(p**2 + x*p**2)) == p*sqrt(1 + x)
286
+ assert separatevars(sqrt(y*(p**2 + x*p**2))) == p*sqrt(y*(1 + x))
287
+ assert separatevars(sqrt(y*(p**2 + x*p**2)), force=True) == \
288
+ p*sqrt(y)*sqrt(1 + x)
289
+ # issue 4865
290
+ assert separatevars(sqrt(x*y)).is_Pow
291
+ assert separatevars(sqrt(x*y), force=True) == sqrt(x)*sqrt(y)
292
+ # issue 4957
293
+ # any type sequence for symbols is fine
294
+ assert separatevars(((2*x + 2)*y), dict=True, symbols=()) == \
295
+ {'coeff': 1, x: 2*x + 2, y: y}
296
+ # separable
297
+ assert separatevars(((2*x + 2)*y), dict=True, symbols=[x]) == \
298
+ {'coeff': y, x: 2*x + 2}
299
+ assert separatevars(((2*x + 2)*y), dict=True, symbols=[]) == \
300
+ {'coeff': 1, x: 2*x + 2, y: y}
301
+ assert separatevars(((2*x + 2)*y), dict=True) == \
302
+ {'coeff': 1, x: 2*x + 2, y: y}
303
+ assert separatevars(((2*x + 2)*y), dict=True, symbols=None) == \
304
+ {'coeff': y*(2*x + 2)}
305
+ # not separable
306
+ assert separatevars(3, dict=True) is None
307
+ assert separatevars(2*x + y, dict=True, symbols=()) is None
308
+ assert separatevars(2*x + y, dict=True) is None
309
+ assert separatevars(2*x + y, dict=True, symbols=None) == {'coeff': 2*x + y}
310
+ # issue 4808
311
+ n, m = symbols('n,m', commutative=False)
312
+ assert separatevars(m + n*m) == (1 + n)*m
313
+ assert separatevars(x + x*n) == x*(1 + n)
314
+ # issue 4910
315
+ f = Function('f')
316
+ assert separatevars(f(x) + x*f(x)) == f(x) + x*f(x)
317
+ # a noncommutable object present
318
+ eq = x*(1 + hyper((), (), y*z))
319
+ assert separatevars(eq) == eq
320
+
321
+ s = separatevars(abs(x*y))
322
+ assert s == abs(x)*abs(y) and s.is_Mul
323
+ z = cos(1)**2 + sin(1)**2 - 1
324
+ a = abs(x*z)
325
+ s = separatevars(a)
326
+ assert not a.is_Mul and s.is_Mul and s == abs(x)*abs(z)
327
+ s = separatevars(abs(x*y*z))
328
+ assert s == abs(x)*abs(y)*abs(z)
329
+
330
+ # abs(x+y)/abs(z) would be better but we test this here to
331
+ # see that it doesn't raise
332
+ assert separatevars(abs((x+y)/z)) == abs((x+y)/z)
333
+
334
+
335
+ def test_separatevars_advanced_factor():
336
+ x, y, z = symbols('x,y,z')
337
+ assert separatevars(1 + log(x)*log(y) + log(x) + log(y)) == \
338
+ (log(x) + 1)*(log(y) + 1)
339
+ assert separatevars(1 + x - log(z) - x*log(z) - exp(y)*log(z) -
340
+ x*exp(y)*log(z) + x*exp(y) + exp(y)) == \
341
+ -((x + 1)*(log(z) - 1)*(exp(y) + 1))
342
+ x, y = symbols('x,y', positive=True)
343
+ assert separatevars(1 + log(x**log(y)) + log(x*y)) == \
344
+ (log(x) + 1)*(log(y) + 1)
345
+
346
+
347
+ def test_hypersimp():
348
+ n, k = symbols('n,k', integer=True)
349
+
350
+ assert hypersimp(factorial(k), k) == k + 1
351
+ assert hypersimp(factorial(k**2), k) is None
352
+
353
+ assert hypersimp(1/factorial(k), k) == 1/(k + 1)
354
+
355
+ assert hypersimp(2**k/factorial(k)**2, k) == 2/(k + 1)**2
356
+
357
+ assert hypersimp(binomial(n, k), k) == (n - k)/(k + 1)
358
+ assert hypersimp(binomial(n + 1, k), k) == (n - k + 1)/(k + 1)
359
+
360
+ term = (4*k + 1)*factorial(k)/factorial(2*k + 1)
361
+ assert hypersimp(term, k) == S.Half*((4*k + 5)/(3 + 14*k + 8*k**2))
362
+
363
+ term = 1/((2*k - 1)*factorial(2*k + 1))
364
+ assert hypersimp(term, k) == (k - S.Half)/((k + 1)*(2*k + 1)*(2*k + 3))
365
+
366
+ term = binomial(n, k)*(-1)**k/factorial(k)
367
+ assert hypersimp(term, k) == (k - n)/(k + 1)**2
368
+
369
+
370
+ def test_nsimplify():
371
+ x = Symbol("x")
372
+ assert nsimplify(0) == 0
373
+ assert nsimplify(-1) == -1
374
+ assert nsimplify(1) == 1
375
+ assert nsimplify(1 + x) == 1 + x
376
+ assert nsimplify(2.7) == Rational(27, 10)
377
+ assert nsimplify(1 - GoldenRatio) == (1 - sqrt(5))/2
378
+ assert nsimplify((1 + sqrt(5))/4, [GoldenRatio]) == GoldenRatio/2
379
+ assert nsimplify(2/GoldenRatio, [GoldenRatio]) == 2*GoldenRatio - 2
380
+ assert nsimplify(exp(pi*I*Rational(5, 3), evaluate=False)) == \
381
+ sympify('1/2 - sqrt(3)*I/2')
382
+ assert nsimplify(sin(pi*Rational(3, 5), evaluate=False)) == \
383
+ sympify('sqrt(sqrt(5)/8 + 5/8)')
384
+ assert nsimplify(sqrt(atan('1', evaluate=False))*(2 + I), [pi]) == \
385
+ sqrt(pi) + sqrt(pi)/2*I
386
+ assert nsimplify(2 + exp(2*atan('1/4')*I)) == sympify('49/17 + 8*I/17')
387
+ assert nsimplify(pi, tolerance=0.01) == Rational(22, 7)
388
+ assert nsimplify(pi, tolerance=0.001) == Rational(355, 113)
389
+ assert nsimplify(0.33333, tolerance=1e-4) == Rational(1, 3)
390
+ assert nsimplify(2.0**(1/3.), tolerance=0.001) == Rational(635, 504)
391
+ assert nsimplify(2.0**(1/3.), tolerance=0.001, full=True) == \
392
+ 2**Rational(1, 3)
393
+ assert nsimplify(x + .5, rational=True) == S.Half + x
394
+ assert nsimplify(1/.3 + x, rational=True) == Rational(10, 3) + x
395
+ assert nsimplify(log(3).n(), rational=True) == \
396
+ sympify('109861228866811/100000000000000')
397
+ assert nsimplify(Float(0.272198261287950), [pi, log(2)]) == pi*log(2)/8
398
+ assert nsimplify(Float(0.272198261287950).n(3), [pi, log(2)]) == \
399
+ -pi/4 - log(2) + Rational(7, 4)
400
+ assert nsimplify(x/7.0) == x/7
401
+ assert nsimplify(pi/1e2) == pi/100
402
+ assert nsimplify(pi/1e2, rational=False) == pi/100.0
403
+ assert nsimplify(pi/1e-7) == 10000000*pi
404
+ assert not nsimplify(
405
+ factor(-3.0*z**2*(z**2)**(-2.5) + 3*(z**2)**(-1.5))).atoms(Float)
406
+ e = x**0.0
407
+ assert e.is_Pow and nsimplify(x**0.0) == 1
408
+ assert nsimplify(3.333333, tolerance=0.1, rational=True) == Rational(10, 3)
409
+ assert nsimplify(3.333333, tolerance=0.01, rational=True) == Rational(10, 3)
410
+ assert nsimplify(3.666666, tolerance=0.1, rational=True) == Rational(11, 3)
411
+ assert nsimplify(3.666666, tolerance=0.01, rational=True) == Rational(11, 3)
412
+ assert nsimplify(33, tolerance=10, rational=True) == Rational(33)
413
+ assert nsimplify(33.33, tolerance=10, rational=True) == Rational(30)
414
+ assert nsimplify(37.76, tolerance=10, rational=True) == Rational(40)
415
+ assert nsimplify(-203.1) == Rational(-2031, 10)
416
+ assert nsimplify(.2, tolerance=0) == Rational(1, 5)
417
+ assert nsimplify(-.2, tolerance=0) == Rational(-1, 5)
418
+ assert nsimplify(.2222, tolerance=0) == Rational(1111, 5000)
419
+ assert nsimplify(-.2222, tolerance=0) == Rational(-1111, 5000)
420
+ # issue 7211, PR 4112
421
+ assert nsimplify(S(2e-8)) == Rational(1, 50000000)
422
+ # issue 7322 direct test
423
+ assert nsimplify(1e-42, rational=True) != 0
424
+ # issue 10336
425
+ inf = Float('inf')
426
+ infs = (-oo, oo, inf, -inf)
427
+ for zi in infs:
428
+ ans = sign(zi)*oo
429
+ assert nsimplify(zi) == ans
430
+ assert nsimplify(zi + x) == x + ans
431
+
432
+ assert nsimplify(0.33333333, rational=True, rational_conversion='exact') == Rational(0.33333333)
433
+
434
+ # Make sure nsimplify on expressions uses full precision
435
+ assert nsimplify(pi.evalf(100)*x, rational_conversion='exact').evalf(100) == pi.evalf(100)*x
436
+
437
+
438
+ def test_issue_9448():
439
+ tmp = sympify("1/(1 - (-1)**(2/3) - (-1)**(1/3)) + 1/(1 + (-1)**(2/3) + (-1)**(1/3))")
440
+ assert nsimplify(tmp) == S.Half
441
+
442
+
443
+ def test_extract_minus_sign():
444
+ x = Symbol("x")
445
+ y = Symbol("y")
446
+ a = Symbol("a")
447
+ b = Symbol("b")
448
+ assert simplify(-x/-y) == x/y
449
+ assert simplify(-x/y) == -x/y
450
+ assert simplify(x/y) == x/y
451
+ assert simplify(x/-y) == -x/y
452
+ assert simplify(-x/0) == zoo*x
453
+ assert simplify(Rational(-5, 0)) is zoo
454
+ assert simplify(-a*x/(-y - b)) == a*x/(b + y)
455
+
456
+
457
+ def test_diff():
458
+ x = Symbol("x")
459
+ y = Symbol("y")
460
+ f = Function("f")
461
+ g = Function("g")
462
+ assert simplify(g(x).diff(x)*f(x).diff(x) - f(x).diff(x)*g(x).diff(x)) == 0
463
+ assert simplify(2*f(x)*f(x).diff(x) - diff(f(x)**2, x)) == 0
464
+ assert simplify(diff(1/f(x), x) + f(x).diff(x)/f(x)**2) == 0
465
+ assert simplify(f(x).diff(x, y) - f(x).diff(y, x)) == 0
466
+
467
+
468
+ def test_logcombine_1():
469
+ x, y = symbols("x,y")
470
+ a = Symbol("a")
471
+ z, w = symbols("z,w", positive=True)
472
+ b = Symbol("b", real=True)
473
+ assert logcombine(log(x) + 2*log(y)) == log(x) + 2*log(y)
474
+ assert logcombine(log(x) + 2*log(y), force=True) == log(x*y**2)
475
+ assert logcombine(a*log(w) + log(z)) == a*log(w) + log(z)
476
+ assert logcombine(b*log(z) + b*log(x)) == log(z**b) + b*log(x)
477
+ assert logcombine(b*log(z) - log(w)) == log(z**b/w)
478
+ assert logcombine(log(x)*log(z)) == log(x)*log(z)
479
+ assert logcombine(log(w)*log(x)) == log(w)*log(x)
480
+ assert logcombine(cos(-2*log(z) + b*log(w))) in [cos(log(w**b/z**2)),
481
+ cos(log(z**2/w**b))]
482
+ assert logcombine(log(log(x) - log(y)) - log(z), force=True) == \
483
+ log(log(x/y)/z)
484
+ assert logcombine((2 + I)*log(x), force=True) == (2 + I)*log(x)
485
+ assert logcombine((x**2 + log(x) - log(y))/(x*y), force=True) == \
486
+ (x**2 + log(x/y))/(x*y)
487
+ # the following could also give log(z*x**log(y**2)), what we
488
+ # are testing is that a canonical result is obtained
489
+ assert logcombine(log(x)*2*log(y) + log(z), force=True) == \
490
+ log(z*y**log(x**2))
491
+ assert logcombine((x*y + sqrt(x**4 + y**4) + log(x) - log(y))/(pi*x**Rational(2, 3)*
492
+ sqrt(y)**3), force=True) == (
493
+ x*y + sqrt(x**4 + y**4) + log(x/y))/(pi*x**Rational(2, 3)*y**Rational(3, 2))
494
+ assert logcombine(gamma(-log(x/y))*acos(-log(x/y)), force=True) == \
495
+ acos(-log(x/y))*gamma(-log(x/y))
496
+
497
+ assert logcombine(2*log(z)*log(w)*log(x) + log(z) + log(w)) == \
498
+ log(z**log(w**2))*log(x) + log(w*z)
499
+ assert logcombine(3*log(w) + 3*log(z)) == log(w**3*z**3)
500
+ assert logcombine(x*(y + 1) + log(2) + log(3)) == x*(y + 1) + log(6)
501
+ assert logcombine((x + y)*log(w) + (-x - y)*log(3)) == (x + y)*log(w/3)
502
+ # a single unknown can combine
503
+ assert logcombine(log(x) + log(2)) == log(2*x)
504
+ eq = log(abs(x)) + log(abs(y))
505
+ assert logcombine(eq) == eq
506
+ reps = {x: 0, y: 0}
507
+ assert log(abs(x)*abs(y)).subs(reps) != eq.subs(reps)
508
+
509
+
510
+ def test_logcombine_complex_coeff():
511
+ i = Integral((sin(x**2) + cos(x**3))/x, x)
512
+ assert logcombine(i, force=True) == i
513
+ assert logcombine(i + 2*log(x), force=True) == \
514
+ i + log(x**2)
515
+
516
+
517
+ def test_issue_5950():
518
+ x, y = symbols("x,y", positive=True)
519
+ assert logcombine(log(3) - log(2)) == log(Rational(3,2), evaluate=False)
520
+ assert logcombine(log(x) - log(y)) == log(x/y)
521
+ assert logcombine(log(Rational(3,2), evaluate=False) - log(2)) == \
522
+ log(Rational(3,4), evaluate=False)
523
+
524
+
525
+ def test_posify():
526
+ x = symbols('x')
527
+
528
+ assert str(posify(
529
+ x +
530
+ Symbol('p', positive=True) +
531
+ Symbol('n', negative=True))) == '(_x + n + p, {_x: x})'
532
+
533
+ eq, rep = posify(1/x)
534
+ assert log(eq).expand().subs(rep) == -log(x)
535
+ assert str(posify([x, 1 + x])) == '([_x, _x + 1], {_x: x})'
536
+
537
+ p = symbols('p', positive=True)
538
+ n = symbols('n', negative=True)
539
+ orig = [x, n, p]
540
+ modified, reps = posify(orig)
541
+ assert str(modified) == '[_x, n, p]'
542
+ assert [w.subs(reps) for w in modified] == orig
543
+
544
+ assert str(Integral(posify(1/x + y)[0], (y, 1, 3)).expand()) == \
545
+ 'Integral(1/_x, (y, 1, 3)) + Integral(_y, (y, 1, 3))'
546
+ assert str(Sum(posify(1/x**n)[0], (n,1,3)).expand()) == \
547
+ 'Sum(_x**(-n), (n, 1, 3))'
548
+
549
+ # issue 16438
550
+ k = Symbol('k', finite=True)
551
+ eq, rep = posify(k)
552
+ assert eq.assumptions0 == {'positive': True, 'zero': False, 'imaginary': False,
553
+ 'nonpositive': False, 'commutative': True, 'hermitian': True, 'real': True, 'nonzero': True,
554
+ 'nonnegative': True, 'negative': False, 'complex': True, 'finite': True,
555
+ 'infinite': False, 'extended_real':True, 'extended_negative': False,
556
+ 'extended_nonnegative': True, 'extended_nonpositive': False,
557
+ 'extended_nonzero': True, 'extended_positive': True}
558
+
559
+
560
+ def test_issue_4194():
561
+ # simplify should call cancel
562
+ f = Function('f')
563
+ assert simplify((4*x + 6*f(y))/(2*x + 3*f(y))) == 2
564
+
565
+
566
+ @XFAIL
567
+ def test_simplify_float_vs_integer():
568
+ # Test for issue 4473:
569
+ # https://github.com/sympy/sympy/issues/4473
570
+ assert simplify(x**2.0 - x**2) == 0
571
+ assert simplify(x**2 - x**2.0) == 0
572
+
573
+
574
+ def test_as_content_primitive():
575
+ assert (x/2 + y).as_content_primitive() == (S.Half, x + 2*y)
576
+ assert (x/2 + y).as_content_primitive(clear=False) == (S.One, x/2 + y)
577
+ assert (y*(x/2 + y)).as_content_primitive() == (S.Half, y*(x + 2*y))
578
+ assert (y*(x/2 + y)).as_content_primitive(clear=False) == (S.One, y*(x/2 + y))
579
+
580
+ # although the _as_content_primitive methods do not alter the underlying structure,
581
+ # the as_content_primitive function will touch up the expression and join
582
+ # bases that would otherwise have not been joined.
583
+ assert (x*(2 + 2*x)*(3*x + 3)**2).as_content_primitive() == \
584
+ (18, x*(x + 1)**3)
585
+ assert (2 + 2*x + 2*y*(3 + 3*y)).as_content_primitive() == \
586
+ (2, x + 3*y*(y + 1) + 1)
587
+ assert ((2 + 6*x)**2).as_content_primitive() == \
588
+ (4, (3*x + 1)**2)
589
+ assert ((2 + 6*x)**(2*y)).as_content_primitive() == \
590
+ (1, (_keep_coeff(S(2), (3*x + 1)))**(2*y))
591
+ assert (5 + 10*x + 2*y*(3 + 3*y)).as_content_primitive() == \
592
+ (1, 10*x + 6*y*(y + 1) + 5)
593
+ assert (5*(x*(1 + y)) + 2*x*(3 + 3*y)).as_content_primitive() == \
594
+ (11, x*(y + 1))
595
+ assert ((5*(x*(1 + y)) + 2*x*(3 + 3*y))**2).as_content_primitive() == \
596
+ (121, x**2*(y + 1)**2)
597
+ assert (y**2).as_content_primitive() == \
598
+ (1, y**2)
599
+ assert (S.Infinity).as_content_primitive() == (1, oo)
600
+ eq = x**(2 + y)
601
+ assert (eq).as_content_primitive() == (1, eq)
602
+ assert (S.Half**(2 + x)).as_content_primitive() == (Rational(1, 4), 2**-x)
603
+ assert (Rational(-1, 2)**(2 + x)).as_content_primitive() == \
604
+ (Rational(1, 4), (Rational(-1, 2))**x)
605
+ assert (Rational(-1, 2)**(2 + x)).as_content_primitive() == \
606
+ (Rational(1, 4), Rational(-1, 2)**x)
607
+ assert (4**((1 + y)/2)).as_content_primitive() == (2, 4**(y/2))
608
+ assert (3**((1 + y)/2)).as_content_primitive() == \
609
+ (1, 3**(Mul(S.Half, 1 + y, evaluate=False)))
610
+ assert (5**Rational(3, 4)).as_content_primitive() == (1, 5**Rational(3, 4))
611
+ assert (5**Rational(7, 4)).as_content_primitive() == (5, 5**Rational(3, 4))
612
+ assert Add(z*Rational(5, 7), 0.5*x, y*Rational(3, 2), evaluate=False).as_content_primitive() == \
613
+ (Rational(1, 14), 7.0*x + 21*y + 10*z)
614
+ assert (2**Rational(3, 4) + 2**Rational(1, 4)*sqrt(3)).as_content_primitive(radical=True) == \
615
+ (1, 2**Rational(1, 4)*(sqrt(2) + sqrt(3)))
616
+
617
+
618
+ def test_signsimp():
619
+ e = x*(-x + 1) + x*(x - 1)
620
+ assert signsimp(Eq(e, 0)) is S.true
621
+ assert Abs(x - 1) == Abs(1 - x)
622
+ assert signsimp(y - x) == y - x
623
+ assert signsimp(y - x, evaluate=False) == Mul(-1, x - y, evaluate=False)
624
+
625
+
626
+ def test_besselsimp():
627
+ from sympy.functions.special.bessel import (besseli, besselj, bessely)
628
+ from sympy.integrals.transforms import cosine_transform
629
+ assert besselsimp(exp(-I*pi*y/2)*besseli(y, z*exp_polar(I*pi/2))) == \
630
+ besselj(y, z)
631
+ assert besselsimp(exp(-I*pi*a/2)*besseli(a, 2*sqrt(x)*exp_polar(I*pi/2))) == \
632
+ besselj(a, 2*sqrt(x))
633
+ assert besselsimp(sqrt(2)*sqrt(pi)*x**Rational(1, 4)*exp(I*pi/4)*exp(-I*pi*a/2) *
634
+ besseli(Rational(-1, 2), sqrt(x)*exp_polar(I*pi/2)) *
635
+ besseli(a, sqrt(x)*exp_polar(I*pi/2))/2) == \
636
+ besselj(a, sqrt(x)) * cos(sqrt(x))
637
+ assert besselsimp(besseli(Rational(-1, 2), z)) == \
638
+ sqrt(2)*cosh(z)/(sqrt(pi)*sqrt(z))
639
+ assert besselsimp(besseli(a, z*exp_polar(-I*pi/2))) == \
640
+ exp(-I*pi*a/2)*besselj(a, z)
641
+ assert cosine_transform(1/t*sin(a/t), t, y) == \
642
+ sqrt(2)*sqrt(pi)*besselj(0, 2*sqrt(a)*sqrt(y))/2
643
+
644
+ assert besselsimp(x**2*(a*(-2*besselj(5*I, x) + besselj(-2 + 5*I, x) +
645
+ besselj(2 + 5*I, x)) + b*(-2*bessely(5*I, x) + bessely(-2 + 5*I, x) +
646
+ bessely(2 + 5*I, x)))/4 + x*(a*(besselj(-1 + 5*I, x)/2 - besselj(1 + 5*I, x)/2)
647
+ + b*(bessely(-1 + 5*I, x)/2 - bessely(1 + 5*I, x)/2)) + (x**2 + 25)*(a*besselj(5*I, x)
648
+ + b*bessely(5*I, x))) == 0
649
+
650
+ assert besselsimp(81*x**2*(a*(besselj(Rational(-5, 3), 9*x) - 2*besselj(Rational(1, 3), 9*x) + besselj(Rational(7, 3), 9*x))
651
+ + b*(bessely(Rational(-5, 3), 9*x) - 2*bessely(Rational(1, 3), 9*x) + bessely(Rational(7, 3), 9*x)))/4 + x*(a*(9*besselj(Rational(-2, 3), 9*x)/2
652
+ - 9*besselj(Rational(4, 3), 9*x)/2) + b*(9*bessely(Rational(-2, 3), 9*x)/2 - 9*bessely(Rational(4, 3), 9*x)/2)) +
653
+ (81*x**2 - Rational(1, 9))*(a*besselj(Rational(1, 3), 9*x) + b*bessely(Rational(1, 3), 9*x))) == 0
654
+
655
+ assert besselsimp(besselj(a-1,x) + besselj(a+1, x) - 2*a*besselj(a, x)/x) == 0
656
+
657
+ assert besselsimp(besselj(a-1,x) + besselj(a+1, x) + besselj(a, x)) == (2*a + x)*besselj(a, x)/x
658
+
659
+ assert besselsimp(x**2* besselj(a,x) + x**3*besselj(a+1, x) + besselj(a+2, x)) == \
660
+ 2*a*x*besselj(a + 1, x) + x**3*besselj(a + 1, x) - x**2*besselj(a + 2, x) + 2*x*besselj(a + 1, x) + besselj(a + 2, x)
661
+
662
+ def test_Piecewise():
663
+ e1 = x*(x + y) - y*(x + y)
664
+ e2 = sin(x)**2 + cos(x)**2
665
+ e3 = expand((x + y)*y/x)
666
+ s1 = simplify(e1)
667
+ s2 = simplify(e2)
668
+ s3 = simplify(e3)
669
+ assert simplify(Piecewise((e1, x < e2), (e3, True))) == \
670
+ Piecewise((s1, x < s2), (s3, True))
671
+
672
+
673
+ def test_polymorphism():
674
+ class A(Basic):
675
+ def _eval_simplify(x, **kwargs):
676
+ return S.One
677
+
678
+ a = A(S(5), S(2))
679
+ assert simplify(a) == 1
680
+
681
+
682
+ def test_issue_from_PR1599():
683
+ n1, n2, n3, n4 = symbols('n1 n2 n3 n4', negative=True)
684
+ assert simplify(I*sqrt(n1)) == -sqrt(-n1)
685
+
686
+
687
+ def test_issue_6811():
688
+ eq = (x + 2*y)*(2*x + 2)
689
+ assert simplify(eq) == (x + 1)*(x + 2*y)*2
690
+ # reject the 2-arg Mul -- these are a headache for test writing
691
+ assert simplify(eq.expand()) == \
692
+ 2*x**2 + 4*x*y + 2*x + 4*y
693
+
694
+
695
+ def test_issue_6920():
696
+ e = [cos(x) + I*sin(x), cos(x) - I*sin(x),
697
+ cosh(x) - sinh(x), cosh(x) + sinh(x)]
698
+ ok = [exp(I*x), exp(-I*x), exp(-x), exp(x)]
699
+ # wrap in f to show that the change happens wherever ei occurs
700
+ f = Function('f')
701
+ assert [simplify(f(ei)).args[0] for ei in e] == ok
702
+
703
+
704
+ def test_issue_7001():
705
+ from sympy.abc import r, R
706
+ assert simplify(-(r*Piecewise((pi*Rational(4, 3), r <= R),
707
+ (-8*pi*R**3/(3*r**3), True)) + 2*Piecewise((pi*r*Rational(4, 3), r <= R),
708
+ (4*pi*R**3/(3*r**2), True)))/(4*pi*r)) == \
709
+ Piecewise((-1, r <= R), (0, True))
710
+
711
+
712
+ def test_inequality_no_auto_simplify():
713
+ # no simplify on creation but can be simplified
714
+ lhs = cos(x)**2 + sin(x)**2
715
+ rhs = 2
716
+ e = Lt(lhs, rhs, evaluate=False)
717
+ assert e is not S.true
718
+ assert simplify(e)
719
+
720
+
721
+ def test_issue_9398():
722
+ from sympy.core.numbers import Number
723
+ from sympy.polys.polytools import cancel
724
+ assert cancel(1e-14) != 0
725
+ assert cancel(1e-14*I) != 0
726
+
727
+ assert simplify(1e-14) != 0
728
+ assert simplify(1e-14*I) != 0
729
+
730
+ assert (I*Number(1.)*Number(10)**Number(-14)).simplify() != 0
731
+
732
+ assert cancel(1e-20) != 0
733
+ assert cancel(1e-20*I) != 0
734
+
735
+ assert simplify(1e-20) != 0
736
+ assert simplify(1e-20*I) != 0
737
+
738
+ assert cancel(1e-100) != 0
739
+ assert cancel(1e-100*I) != 0
740
+
741
+ assert simplify(1e-100) != 0
742
+ assert simplify(1e-100*I) != 0
743
+
744
+ f = Float("1e-1000")
745
+ assert cancel(f) != 0
746
+ assert cancel(f*I) != 0
747
+
748
+ assert simplify(f) != 0
749
+ assert simplify(f*I) != 0
750
+
751
+
752
+ def test_issue_9324_simplify():
753
+ M = MatrixSymbol('M', 10, 10)
754
+ e = M[0, 0] + M[5, 4] + 1304
755
+ assert simplify(e) == e
756
+
757
+
758
+ def test_issue_9817_simplify():
759
+ # simplify on trace of substituted explicit quadratic form of matrix
760
+ # expressions (a scalar) should return without errors (AttributeError)
761
+ # See issue #9817 and #9190 for the original bug more discussion on this
762
+ from sympy.matrices.expressions import Identity, trace
763
+ v = MatrixSymbol('v', 3, 1)
764
+ A = MatrixSymbol('A', 3, 3)
765
+ x = Matrix([i + 1 for i in range(3)])
766
+ X = Identity(3)
767
+ quadratic = v.T * A * v
768
+ assert simplify((trace(quadratic.as_explicit())).xreplace({v:x, A:X})) == 14
769
+
770
+
771
+ def test_issue_13474():
772
+ x = Symbol('x')
773
+ assert simplify(x + csch(sinc(1))) == x + csch(sinc(1))
774
+
775
+
776
+ @_both_exp_pow
777
+ def test_simplify_function_inverse():
778
+ # "inverse" attribute does not guarantee that f(g(x)) is x
779
+ # so this simplification should not happen automatically.
780
+ # See issue #12140
781
+ x, y = symbols('x, y')
782
+ g = Function('g')
783
+
784
+ class f(Function):
785
+ def inverse(self, argindex=1):
786
+ return g
787
+
788
+ assert simplify(f(g(x))) == f(g(x))
789
+ assert inversecombine(f(g(x))) == x
790
+ assert simplify(f(g(x)), inverse=True) == x
791
+ assert simplify(f(g(sin(x)**2 + cos(x)**2)), inverse=True) == 1
792
+ assert simplify(f(g(x, y)), inverse=True) == f(g(x, y))
793
+ assert unchanged(asin, sin(x))
794
+ assert simplify(asin(sin(x))) == asin(sin(x))
795
+ assert simplify(2*asin(sin(3*x)), inverse=True) == 6*x
796
+ assert simplify(log(exp(x))) == log(exp(x))
797
+ assert simplify(log(exp(x)), inverse=True) == x
798
+ assert simplify(exp(log(x)), inverse=True) == x
799
+ assert simplify(log(exp(x), 2), inverse=True) == x/log(2)
800
+ assert simplify(log(exp(x), 2, evaluate=False), inverse=True) == x/log(2)
801
+
802
+
803
+ def test_clear_coefficients():
804
+ from sympy.simplify.simplify import clear_coefficients
805
+ assert clear_coefficients(4*y*(6*x + 3)) == (y*(2*x + 1), 0)
806
+ assert clear_coefficients(4*y*(6*x + 3) - 2) == (y*(2*x + 1), Rational(1, 6))
807
+ assert clear_coefficients(4*y*(6*x + 3) - 2, x) == (y*(2*x + 1), x/12 + Rational(1, 6))
808
+ assert clear_coefficients(sqrt(2) - 2) == (sqrt(2), 2)
809
+ assert clear_coefficients(4*sqrt(2) - 2) == (sqrt(2), S.Half)
810
+ assert clear_coefficients(S(3), x) == (0, x - 3)
811
+ assert clear_coefficients(S.Infinity, x) == (S.Infinity, x)
812
+ assert clear_coefficients(-S.Pi, x) == (S.Pi, -x)
813
+ assert clear_coefficients(2 - S.Pi/3, x) == (pi, -3*x + 6)
814
+
815
+ def test_nc_simplify():
816
+ from sympy.simplify.simplify import nc_simplify
817
+ from sympy.matrices.expressions import MatPow, Identity
818
+ from sympy.core import Pow
819
+ from functools import reduce
820
+
821
+ a, b, c, d = symbols('a b c d', commutative = False)
822
+ x = Symbol('x')
823
+ A = MatrixSymbol("A", x, x)
824
+ B = MatrixSymbol("B", x, x)
825
+ C = MatrixSymbol("C", x, x)
826
+ D = MatrixSymbol("D", x, x)
827
+ subst = {a: A, b: B, c: C, d:D}
828
+ funcs = {Add: lambda x,y: x+y, Mul: lambda x,y: x*y }
829
+
830
+ def _to_matrix(expr):
831
+ if expr in subst:
832
+ return subst[expr]
833
+ if isinstance(expr, Pow):
834
+ return MatPow(_to_matrix(expr.args[0]), expr.args[1])
835
+ elif isinstance(expr, (Add, Mul)):
836
+ return reduce(funcs[expr.func],[_to_matrix(a) for a in expr.args])
837
+ else:
838
+ return expr*Identity(x)
839
+
840
+ def _check(expr, simplified, deep=True, matrix=True):
841
+ assert nc_simplify(expr, deep=deep) == simplified
842
+ assert expand(expr) == expand(simplified)
843
+ if matrix:
844
+ m_simp = _to_matrix(simplified).doit(inv_expand=False)
845
+ assert nc_simplify(_to_matrix(expr), deep=deep) == m_simp
846
+
847
+ _check(a*b*a*b*a*b*c*(a*b)**3*c, ((a*b)**3*c)**2)
848
+ _check(a*b*(a*b)**-2*a*b, 1)
849
+ _check(a**2*b*a*b*a*b*(a*b)**-1, a*(a*b)**2, matrix=False)
850
+ _check(b*a*b**2*a*b**2*a*b**2, b*(a*b**2)**3)
851
+ _check(a*b*a**2*b*a**2*b*a**3, (a*b*a)**3*a**2)
852
+ _check(a**2*b*a**4*b*a**4*b*a**2, (a**2*b*a**2)**3)
853
+ _check(a**3*b*a**4*b*a**4*b*a, a**3*(b*a**4)**3*a**-3)
854
+ _check(a*b*a*b + a*b*c*x*a*b*c, (a*b)**2 + x*(a*b*c)**2)
855
+ _check(a*b*a*b*c*a*b*a*b*c, ((a*b)**2*c)**2)
856
+ _check(b**-1*a**-1*(a*b)**2, a*b)
857
+ _check(a**-1*b*c**-1, (c*b**-1*a)**-1)
858
+ expr = a**3*b*a**4*b*a**4*b*a**2*b*a**2*(b*a**2)**2*b*a**2*b*a**2
859
+ for _ in range(10):
860
+ expr *= a*b
861
+ _check(expr, a**3*(b*a**4)**2*(b*a**2)**6*(a*b)**10)
862
+ _check((a*b*a*b)**2, (a*b*a*b)**2, deep=False)
863
+ _check(a*b*(c*d)**2, a*b*(c*d)**2)
864
+ expr = b**-1*(a**-1*b**-1 - a**-1*c*b**-1)**-1*a**-1
865
+ assert nc_simplify(expr) == (1-c)**-1
866
+ # commutative expressions should be returned without an error
867
+ assert nc_simplify(2*x**2) == 2*x**2
868
+
869
+ def test_issue_15965():
870
+ A = Sum(z*x**y, (x, 1, a))
871
+ anew = z*Sum(x**y, (x, 1, a))
872
+ B = Integral(x*y, x)
873
+ bdo = x**2*y/2
874
+ assert simplify(A + B) == anew + bdo
875
+ assert simplify(A) == anew
876
+ assert simplify(B) == bdo
877
+ assert simplify(B, doit=False) == y*Integral(x, x)
878
+
879
+
880
+ def test_issue_17137():
881
+ assert simplify(cos(x)**I) == cos(x)**I
882
+ assert simplify(cos(x)**(2 + 3*I)) == cos(x)**(2 + 3*I)
883
+
884
+
885
+ def test_issue_21869():
886
+ x = Symbol('x', real=True)
887
+ y = Symbol('y', real=True)
888
+ expr = And(Eq(x**2, 4), Le(x, y))
889
+ assert expr.simplify() == expr
890
+
891
+ expr = And(Eq(x**2, 4), Eq(x, 2))
892
+ assert expr.simplify() == Eq(x, 2)
893
+
894
+ expr = And(Eq(x**3, x**2), Eq(x, 1))
895
+ assert expr.simplify() == Eq(x, 1)
896
+
897
+ expr = And(Eq(sin(x), x**2), Eq(x, 0))
898
+ assert expr.simplify() == Eq(x, 0)
899
+
900
+ expr = And(Eq(x**3, x**2), Eq(x, 2))
901
+ assert expr.simplify() == S.false
902
+
903
+ expr = And(Eq(y, x**2), Eq(x, 1))
904
+ assert expr.simplify() == And(Eq(y,1), Eq(x, 1))
905
+
906
+ expr = And(Eq(y**2, 1), Eq(y, x**2), Eq(x, 1))
907
+ assert expr.simplify() == And(Eq(y,1), Eq(x, 1))
908
+
909
+ expr = And(Eq(y**2, 4), Eq(y, 2*x**2), Eq(x, 1))
910
+ assert expr.simplify() == And(Eq(y,2), Eq(x, 1))
911
+
912
+ expr = And(Eq(y**2, 4), Eq(y, x**2), Eq(x, 1))
913
+ assert expr.simplify() == S.false
914
+
915
+
916
+ def test_issue_7971_21740():
917
+ z = Integral(x, (x, 1, 1))
918
+ assert z != 0
919
+ assert simplify(z) is S.Zero
920
+ assert simplify(S.Zero) is S.Zero
921
+ z = simplify(Float(0))
922
+ assert z is not S.Zero and z == 0.0
923
+
924
+
925
+ @slow
926
+ def test_issue_17141_slow():
927
+ # Should not give RecursionError
928
+ assert simplify((2**acos(I+1)**2).rewrite('log')) == 2**((pi + 2*I*log(-1 +
929
+ sqrt(1 - 2*I) + I))**2/4)
930
+
931
+
932
+ def test_issue_17141():
933
+ # Check that there is no RecursionError
934
+ assert simplify(x**(1 / acos(I))) == x**(2/(pi - 2*I*log(1 + sqrt(2))))
935
+ assert simplify(acos(-I)**2*acos(I)**2) == \
936
+ log(1 + sqrt(2))**4 + pi**2*log(1 + sqrt(2))**2/2 + pi**4/16
937
+ assert simplify(2**acos(I)**2) == 2**((pi - 2*I*log(1 + sqrt(2)))**2/4)
938
+ p = 2**acos(I+1)**2
939
+ assert simplify(p) == p
940
+
941
+
942
+ def test_simplify_kroneckerdelta():
943
+ i, j = symbols("i j")
944
+ K = KroneckerDelta
945
+
946
+ assert simplify(K(i, j)) == K(i, j)
947
+ assert simplify(K(0, j)) == K(0, j)
948
+ assert simplify(K(i, 0)) == K(i, 0)
949
+
950
+ assert simplify(K(0, j).rewrite(Piecewise) * K(1, j)) == 0
951
+ assert simplify(K(1, i) + Piecewise((1, Eq(j, 2)), (0, True))) == K(1, i) + K(2, j)
952
+
953
+ # issue 17214
954
+ assert simplify(K(0, j) * K(1, j)) == 0
955
+
956
+ n = Symbol('n', integer=True)
957
+ assert simplify(K(0, n) * K(1, n)) == 0
958
+
959
+ M = Matrix(4, 4, lambda i, j: K(j - i, n) if i <= j else 0)
960
+ assert simplify(M**2) == Matrix([[K(0, n), 0, K(1, n), 0],
961
+ [0, K(0, n), 0, K(1, n)],
962
+ [0, 0, K(0, n), 0],
963
+ [0, 0, 0, K(0, n)]])
964
+ assert simplify(eye(1) * KroneckerDelta(0, n) *
965
+ KroneckerDelta(1, n)) == Matrix([[0]])
966
+
967
+ assert simplify(S.Infinity * KroneckerDelta(0, n) *
968
+ KroneckerDelta(1, n)) is S.NaN
969
+
970
+
971
+ def test_issue_17292():
972
+ assert simplify(abs(x)/abs(x**2)) == 1/abs(x)
973
+ # this is bigger than the issue: check that deep processing works
974
+ assert simplify(5*abs((x**2 - 1)/(x - 1))) == 5*Abs(x + 1)
975
+
976
+
977
+ def test_issue_19822():
978
+ expr = And(Gt(n-2, 1), Gt(n, 1))
979
+ assert simplify(expr) == Gt(n, 3)
980
+
981
+
982
+ def test_issue_18645():
983
+ expr = And(Ge(x, 3), Le(x, 3))
984
+ assert simplify(expr) == Eq(x, 3)
985
+ expr = And(Eq(x, 3), Le(x, 3))
986
+ assert simplify(expr) == Eq(x, 3)
987
+
988
+
989
+ @XFAIL
990
+ def test_issue_18642():
991
+ i = Symbol("i", integer=True)
992
+ n = Symbol("n", integer=True)
993
+ expr = And(Eq(i, 2 * n), Le(i, 2*n -1))
994
+ assert simplify(expr) == S.false
995
+
996
+
997
+ @XFAIL
998
+ def test_issue_18389():
999
+ n = Symbol("n", integer=True)
1000
+ expr = Eq(n, 0) | (n >= 1)
1001
+ assert simplify(expr) == Ge(n, 0)
1002
+
1003
+
1004
+ def test_issue_8373():
1005
+ x = Symbol('x', real=True)
1006
+ assert simplify(Or(x < 1, x >= 1)) == S.true
1007
+
1008
+
1009
+ def test_issue_7950():
1010
+ expr = And(Eq(x, 1), Eq(x, 2))
1011
+ assert simplify(expr) == S.false
1012
+
1013
+
1014
+ def test_issue_22020():
1015
+ expr = I*pi/2 -oo
1016
+ assert simplify(expr) == expr
1017
+ # Used to throw an error
1018
+
1019
+
1020
+ def test_issue_19484():
1021
+ assert simplify(sign(x) * Abs(x)) == x
1022
+
1023
+ e = x + sign(x + x**3)
1024
+ assert simplify(Abs(x + x**3)*e) == x**3 + x*Abs(x**3 + x) + x
1025
+
1026
+ e = x**2 + sign(x**3 + 1)
1027
+ assert simplify(Abs(x**3 + 1) * e) == x**3 + x**2*Abs(x**3 + 1) + 1
1028
+
1029
+ f = Function('f')
1030
+ e = x + sign(x + f(x)**3)
1031
+ assert simplify(Abs(x + f(x)**3) * e) == x*Abs(x + f(x)**3) + x + f(x)**3
1032
+
1033
+
1034
+ def test_issue_23543():
1035
+ # Used to give an error
1036
+ x, y, z = symbols("x y z", commutative=False)
1037
+ assert (x*(y + z/2)).simplify() == x*(2*y + z)/2
1038
+
1039
+
1040
+ def test_issue_11004():
1041
+
1042
+ def f(n):
1043
+ return sqrt(2*pi*n) * (n/E)**n
1044
+
1045
+ def m(n, k):
1046
+ return f(n) / (f(n/k)**k)
1047
+
1048
+ def p(n,k):
1049
+ return m(n, k) / (k**n)
1050
+
1051
+ N, k = symbols('N k')
1052
+ half = Float('0.5', 4)
1053
+ z = log(p(n, k) / p(n, k + 1)).expand(force=True)
1054
+ r = simplify(z.subs(n, N).n(4))
1055
+ assert r == (
1056
+ half*k*log(k)
1057
+ - half*k*log(k + 1)
1058
+ + half*log(N)
1059
+ - half*log(k + 1)
1060
+ + Float(0.9189224, 4)
1061
+ )
1062
+
1063
+
1064
+ def test_issue_19161():
1065
+ polynomial = Poly('x**2').simplify()
1066
+ assert (polynomial-x**2).simplify() == 0
1067
+
1068
+
1069
+ def test_issue_22210():
1070
+ d = Symbol('d', integer=True)
1071
+ expr = 2*Derivative(sin(x), (x, d))
1072
+ assert expr.simplify() == expr
1073
+
1074
+
1075
+ def test_reduce_inverses_nc_pow():
1076
+ x, y = symbols("x y", commutative=True)
1077
+ Z = symbols("Z", commutative=False)
1078
+ assert simplify(2**Z * y**Z) == 2**Z * y**Z
1079
+ assert simplify(x**Z * y**Z) == x**Z * y**Z
1080
+ x, y = symbols("x y", positive=True)
1081
+ assert expand((x*y)**Z) == x**Z * y**Z
1082
+ assert simplify(x**Z * y**Z) == expand((x*y)**Z)
1083
+
1084
+ def test_nc_recursion_coeff():
1085
+ X = symbols("X", commutative = False)
1086
+ assert (2 * cos(pi/3) * X).simplify() == X
1087
+ assert (2.0 * cos(pi/3) * X).simplify() == X
lib/python3.12/site-packages/sympy/simplify/tests/test_sqrtdenest.py ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.mul import Mul
2
+ from sympy.core.numbers import (I, Integer, Rational)
3
+ from sympy.core.symbol import Symbol
4
+ from sympy.functions.elementary.miscellaneous import (root, sqrt)
5
+ from sympy.functions.elementary.trigonometric import cos
6
+ from sympy.integrals.integrals import Integral
7
+ from sympy.simplify.sqrtdenest import sqrtdenest
8
+ from sympy.simplify.sqrtdenest import (
9
+ _subsets as subsets, _sqrt_numeric_denest)
10
+
11
+ r2, r3, r5, r6, r7, r10, r15, r29 = [sqrt(x) for x in (2, 3, 5, 6, 7, 10,
12
+ 15, 29)]
13
+
14
+
15
+ def test_sqrtdenest():
16
+ d = {sqrt(5 + 2 * r6): r2 + r3,
17
+ sqrt(5. + 2 * r6): sqrt(5. + 2 * r6),
18
+ sqrt(5. + 4*sqrt(5 + 2 * r6)): sqrt(5.0 + 4*r2 + 4*r3),
19
+ sqrt(r2): sqrt(r2),
20
+ sqrt(5 + r7): sqrt(5 + r7),
21
+ sqrt(3 + sqrt(5 + 2*r7)):
22
+ 3*r2*(5 + 2*r7)**Rational(1, 4)/(2*sqrt(6 + 3*r7)) +
23
+ r2*sqrt(6 + 3*r7)/(2*(5 + 2*r7)**Rational(1, 4)),
24
+ sqrt(3 + 2*r3): 3**Rational(3, 4)*(r6/2 + 3*r2/2)/3}
25
+ for i in d:
26
+ assert sqrtdenest(i) == d[i], i
27
+
28
+
29
+ def test_sqrtdenest2():
30
+ assert sqrtdenest(sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29))) == \
31
+ r5 + sqrt(11 - 2*r29)
32
+ e = sqrt(-r5 + sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16))
33
+ assert sqrtdenest(e) == root(-2*r29 + 11, 4)
34
+ r = sqrt(1 + r7)
35
+ assert sqrtdenest(sqrt(1 + r)) == sqrt(1 + r)
36
+ e = sqrt(((1 + sqrt(1 + 2*sqrt(3 + r2 + r5)))**2).expand())
37
+ assert sqrtdenest(e) == 1 + sqrt(1 + 2*sqrt(r2 + r5 + 3))
38
+
39
+ assert sqrtdenest(sqrt(5*r3 + 6*r2)) == \
40
+ sqrt(2)*root(3, 4) + root(3, 4)**3
41
+
42
+ assert sqrtdenest(sqrt(((1 + r5 + sqrt(1 + r3))**2).expand())) == \
43
+ 1 + r5 + sqrt(1 + r3)
44
+
45
+ assert sqrtdenest(sqrt(((1 + r5 + r7 + sqrt(1 + r3))**2).expand())) == \
46
+ 1 + sqrt(1 + r3) + r5 + r7
47
+
48
+ e = sqrt(((1 + cos(2) + cos(3) + sqrt(1 + r3))**2).expand())
49
+ assert sqrtdenest(e) == cos(3) + cos(2) + 1 + sqrt(1 + r3)
50
+
51
+ e = sqrt(-2*r10 + 2*r2*sqrt(-2*r10 + 11) + 14)
52
+ assert sqrtdenest(e) == sqrt(-2*r10 - 2*r2 + 4*r5 + 14)
53
+
54
+ # check that the result is not more complicated than the input
55
+ z = sqrt(-2*r29 + cos(2) + 2*sqrt(-10*r29 + 55) + 16)
56
+ assert sqrtdenest(z) == z
57
+
58
+ assert sqrtdenest(sqrt(r6 + sqrt(15))) == sqrt(r6 + sqrt(15))
59
+
60
+ z = sqrt(15 - 2*sqrt(31) + 2*sqrt(55 - 10*r29))
61
+ assert sqrtdenest(z) == z
62
+
63
+
64
+ def test_sqrtdenest_rec():
65
+ assert sqrtdenest(sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 33)) == \
66
+ -r2 + r3 + 2*r7
67
+ assert sqrtdenest(sqrt(-28*r7 - 14*r5 + 4*sqrt(35) + 82)) == \
68
+ -7 + r5 + 2*r7
69
+ assert sqrtdenest(sqrt(6*r2/11 + 2*sqrt(22)/11 + 6*sqrt(11)/11 + 2)) == \
70
+ sqrt(11)*(r2 + 3 + sqrt(11))/11
71
+ assert sqrtdenest(sqrt(468*r3 + 3024*r2 + 2912*r6 + 19735)) == \
72
+ 9*r3 + 26 + 56*r6
73
+ z = sqrt(-490*r3 - 98*sqrt(115) - 98*sqrt(345) - 2107)
74
+ assert sqrtdenest(z) == sqrt(-1)*(7*r5 + 7*r15 + 7*sqrt(23))
75
+ z = sqrt(-4*sqrt(14) - 2*r6 + 4*sqrt(21) + 34)
76
+ assert sqrtdenest(z) == z
77
+ assert sqrtdenest(sqrt(-8*r2 - 2*r5 + 18)) == -r10 + 1 + r2 + r5
78
+ assert sqrtdenest(sqrt(8*r2 + 2*r5 - 18)) == \
79
+ sqrt(-1)*(-r10 + 1 + r2 + r5)
80
+ assert sqrtdenest(sqrt(8*r2/3 + 14*r5/3 + Rational(154, 9))) == \
81
+ -r10/3 + r2 + r5 + 3
82
+ assert sqrtdenest(sqrt(sqrt(2*r6 + 5) + sqrt(2*r7 + 8))) == \
83
+ sqrt(1 + r2 + r3 + r7)
84
+ assert sqrtdenest(sqrt(4*r15 + 8*r5 + 12*r3 + 24)) == 1 + r3 + r5 + r15
85
+
86
+ w = 1 + r2 + r3 + r5 + r7
87
+ assert sqrtdenest(sqrt((w**2).expand())) == w
88
+ z = sqrt((w**2).expand() + 1)
89
+ assert sqrtdenest(z) == z
90
+
91
+ z = sqrt(2*r10 + 6*r2 + 4*r5 + 12 + 10*r15 + 30*r3)
92
+ assert sqrtdenest(z) == z
93
+
94
+
95
+ def test_issue_6241():
96
+ z = sqrt( -320 + 32*sqrt(5) + 64*r15)
97
+ assert sqrtdenest(z) == z
98
+
99
+
100
+ def test_sqrtdenest3():
101
+ z = sqrt(13 - 2*r10 + 2*r2*sqrt(-2*r10 + 11))
102
+ assert sqrtdenest(z) == -1 + r2 + r10
103
+ assert sqrtdenest(z, max_iter=1) == -1 + sqrt(2) + sqrt(10)
104
+ z = sqrt(sqrt(r2 + 2) + 2)
105
+ assert sqrtdenest(z) == z
106
+ assert sqrtdenest(sqrt(-2*r10 + 4*r2*sqrt(-2*r10 + 11) + 20)) == \
107
+ sqrt(-2*r10 - 4*r2 + 8*r5 + 20)
108
+ assert sqrtdenest(sqrt((112 + 70*r2) + (46 + 34*r2)*r5)) == \
109
+ r10 + 5 + 4*r2 + 3*r5
110
+ z = sqrt(5 + sqrt(2*r6 + 5)*sqrt(-2*r29 + 2*sqrt(-10*r29 + 55) + 16))
111
+ r = sqrt(-2*r29 + 11)
112
+ assert sqrtdenest(z) == sqrt(r2*r + r3*r + r10 + r15 + 5)
113
+
114
+ n = sqrt(2*r6/7 + 2*r7/7 + 2*sqrt(42)/7 + 2)
115
+ d = sqrt(16 - 2*r29 + 2*sqrt(55 - 10*r29))
116
+ assert sqrtdenest(n/d) == r7*(1 + r6 + r7)/(Mul(7, (sqrt(-2*r29 + 11) + r5),
117
+ evaluate=False))
118
+
119
+
120
+ def test_sqrtdenest4():
121
+ # see Denest_en.pdf in https://github.com/sympy/sympy/issues/3192
122
+ z = sqrt(8 - r2*sqrt(5 - r5) - sqrt(3)*(1 + r5))
123
+ z1 = sqrtdenest(z)
124
+ c = sqrt(-r5 + 5)
125
+ z1 = ((-r15*c - r3*c + c + r5*c - r6 - r2 + r10 + sqrt(30))/4).expand()
126
+ assert sqrtdenest(z) == z1
127
+
128
+ z = sqrt(2*r2*sqrt(r2 + 2) + 5*r2 + 4*sqrt(r2 + 2) + 8)
129
+ assert sqrtdenest(z) == r2 + sqrt(r2 + 2) + 2
130
+
131
+ w = 2 + r2 + r3 + (1 + r3)*sqrt(2 + r2 + 5*r3)
132
+ z = sqrt((w**2).expand())
133
+ assert sqrtdenest(z) == w.expand()
134
+
135
+
136
+ def test_sqrt_symbolic_denest():
137
+ x = Symbol('x')
138
+ z = sqrt(((1 + sqrt(sqrt(2 + x) + 3))**2).expand())
139
+ assert sqrtdenest(z) == sqrt((1 + sqrt(sqrt(2 + x) + 3))**2)
140
+ z = sqrt(((1 + sqrt(sqrt(2 + cos(1)) + 3))**2).expand())
141
+ assert sqrtdenest(z) == 1 + sqrt(sqrt(2 + cos(1)) + 3)
142
+ z = ((1 + cos(2))**4 + 1).expand()
143
+ assert sqrtdenest(z) == z
144
+ z = sqrt(((1 + sqrt(sqrt(2 + cos(3*x)) + 3))**2 + 1).expand())
145
+ assert sqrtdenest(z) == z
146
+ c = cos(3)
147
+ c2 = c**2
148
+ assert sqrtdenest(sqrt(2*sqrt(1 + r3)*c + c2 + 1 + r3*c2)) == \
149
+ -1 - sqrt(1 + r3)*c
150
+ ra = sqrt(1 + r3)
151
+ z = sqrt(20*ra*sqrt(3 + 3*r3) + 12*r3*ra*sqrt(3 + 3*r3) + 64*r3 + 112)
152
+ assert sqrtdenest(z) == z
153
+
154
+
155
+ def test_issue_5857():
156
+ from sympy.abc import x, y
157
+ z = sqrt(1/(4*r3 + 7) + 1)
158
+ ans = (r2 + r6)/(r3 + 2)
159
+ assert sqrtdenest(z) == ans
160
+ assert sqrtdenest(1 + z) == 1 + ans
161
+ assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \
162
+ Integral(1 + ans, (x, 1, 2))
163
+ assert sqrtdenest(x + sqrt(y)) == x + sqrt(y)
164
+ ans = (r2 + r6)/(r3 + 2)
165
+ assert sqrtdenest(z) == ans
166
+ assert sqrtdenest(1 + z) == 1 + ans
167
+ assert sqrtdenest(Integral(z + 1, (x, 1, 2))) == \
168
+ Integral(1 + ans, (x, 1, 2))
169
+ assert sqrtdenest(x + sqrt(y)) == x + sqrt(y)
170
+
171
+
172
+ def test_subsets():
173
+ assert subsets(1) == [[1]]
174
+ assert subsets(4) == [
175
+ [1, 0, 0, 0], [0, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 0], [1, 0, 1, 0],
176
+ [0, 1, 1, 0], [1, 1, 1, 0], [0, 0, 0, 1], [1, 0, 0, 1], [0, 1, 0, 1],
177
+ [1, 1, 0, 1], [0, 0, 1, 1], [1, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1]]
178
+
179
+
180
+ def test_issue_5653():
181
+ assert sqrtdenest(
182
+ sqrt(2 + sqrt(2 + sqrt(2)))) == sqrt(2 + sqrt(2 + sqrt(2)))
183
+
184
+ def test_issue_12420():
185
+ assert sqrtdenest((3 - sqrt(2)*sqrt(4 + 3*I) + 3*I)/2) == I
186
+ e = 3 - sqrt(2)*sqrt(4 + I) + 3*I
187
+ assert sqrtdenest(e) == e
188
+
189
+ def test_sqrt_ratcomb():
190
+ assert sqrtdenest(sqrt(1 + r3) + sqrt(3 + 3*r3) - sqrt(10 + 6*r3)) == 0
191
+
192
+ def test_issue_18041():
193
+ e = -sqrt(-2 + 2*sqrt(3)*I)
194
+ assert sqrtdenest(e) == -1 - sqrt(3)*I
195
+
196
+ def test_issue_19914():
197
+ a = Integer(-8)
198
+ b = Integer(-1)
199
+ r = Integer(63)
200
+ d2 = a*a - b*b*r
201
+
202
+ assert _sqrt_numeric_denest(a, b, r, d2) == \
203
+ sqrt(14)*I/2 + 3*sqrt(2)*I/2
204
+ assert sqrtdenest(sqrt(-8-sqrt(63))) == sqrt(14)*I/2 + 3*sqrt(2)*I/2
lib/python3.12/site-packages/sympy/simplify/tests/test_trigsimp.py ADDED
@@ -0,0 +1,520 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import product
2
+ from sympy.core.function import (Subs, count_ops, diff, expand)
3
+ from sympy.core.numbers import (E, I, Rational, pi)
4
+ from sympy.core.singleton import S
5
+ from sympy.core.symbol import (Symbol, symbols)
6
+ from sympy.functions.elementary.exponential import (exp, log)
7
+ from sympy.functions.elementary.hyperbolic import (cosh, coth, sinh, tanh)
8
+ from sympy.functions.elementary.miscellaneous import sqrt
9
+ from sympy.functions.elementary.piecewise import Piecewise
10
+ from sympy.functions.elementary.trigonometric import (cos, cot, sin, tan)
11
+ from sympy.functions.elementary.trigonometric import (acos, asin, atan2)
12
+ from sympy.functions.elementary.trigonometric import (asec, acsc)
13
+ from sympy.functions.elementary.trigonometric import (acot, atan)
14
+ from sympy.integrals.integrals import integrate
15
+ from sympy.matrices.dense import Matrix
16
+ from sympy.simplify.simplify import simplify
17
+ from sympy.simplify.trigsimp import (exptrigsimp, trigsimp)
18
+
19
+ from sympy.testing.pytest import XFAIL
20
+
21
+ from sympy.abc import x, y
22
+
23
+
24
+
25
+ def test_trigsimp1():
26
+ x, y = symbols('x,y')
27
+
28
+ assert trigsimp(1 - sin(x)**2) == cos(x)**2
29
+ assert trigsimp(1 - cos(x)**2) == sin(x)**2
30
+ assert trigsimp(sin(x)**2 + cos(x)**2) == 1
31
+ assert trigsimp(1 + tan(x)**2) == 1/cos(x)**2
32
+ assert trigsimp(1/cos(x)**2 - 1) == tan(x)**2
33
+ assert trigsimp(1/cos(x)**2 - tan(x)**2) == 1
34
+ assert trigsimp(1 + cot(x)**2) == 1/sin(x)**2
35
+ assert trigsimp(1/sin(x)**2 - 1) == 1/tan(x)**2
36
+ assert trigsimp(1/sin(x)**2 - cot(x)**2) == 1
37
+
38
+ assert trigsimp(5*cos(x)**2 + 5*sin(x)**2) == 5
39
+ assert trigsimp(5*cos(x/2)**2 + 2*sin(x/2)**2) == 3*cos(x)/2 + Rational(7, 2)
40
+
41
+ assert trigsimp(sin(x)/cos(x)) == tan(x)
42
+ assert trigsimp(2*tan(x)*cos(x)) == 2*sin(x)
43
+ assert trigsimp(cot(x)**3*sin(x)**3) == cos(x)**3
44
+ assert trigsimp(y*tan(x)**2/sin(x)**2) == y/cos(x)**2
45
+ assert trigsimp(cot(x)/cos(x)) == 1/sin(x)
46
+
47
+ assert trigsimp(sin(x + y) + sin(x - y)) == 2*sin(x)*cos(y)
48
+ assert trigsimp(sin(x + y) - sin(x - y)) == 2*sin(y)*cos(x)
49
+ assert trigsimp(cos(x + y) + cos(x - y)) == 2*cos(x)*cos(y)
50
+ assert trigsimp(cos(x + y) - cos(x - y)) == -2*sin(x)*sin(y)
51
+ assert trigsimp(tan(x + y) - tan(x)/(1 - tan(x)*tan(y))) == \
52
+ sin(y)/(-sin(y)*tan(x) + cos(y)) # -tan(y)/(tan(x)*tan(y) - 1)
53
+
54
+ assert trigsimp(sinh(x + y) + sinh(x - y)) == 2*sinh(x)*cosh(y)
55
+ assert trigsimp(sinh(x + y) - sinh(x - y)) == 2*sinh(y)*cosh(x)
56
+ assert trigsimp(cosh(x + y) + cosh(x - y)) == 2*cosh(x)*cosh(y)
57
+ assert trigsimp(cosh(x + y) - cosh(x - y)) == 2*sinh(x)*sinh(y)
58
+ assert trigsimp(tanh(x + y) - tanh(x)/(1 + tanh(x)*tanh(y))) == \
59
+ sinh(y)/(sinh(y)*tanh(x) + cosh(y))
60
+
61
+ assert trigsimp(cos(0.12345)**2 + sin(0.12345)**2) == 1.0
62
+ e = 2*sin(x)**2 + 2*cos(x)**2
63
+ assert trigsimp(log(e)) == log(2)
64
+
65
+
66
+ def test_trigsimp1a():
67
+ assert trigsimp(sin(2)**2*cos(3)*exp(2)/cos(2)**2) == tan(2)**2*cos(3)*exp(2)
68
+ assert trigsimp(tan(2)**2*cos(3)*exp(2)*cos(2)**2) == sin(2)**2*cos(3)*exp(2)
69
+ assert trigsimp(cot(2)*cos(3)*exp(2)*sin(2)) == cos(3)*exp(2)*cos(2)
70
+ assert trigsimp(tan(2)*cos(3)*exp(2)/sin(2)) == cos(3)*exp(2)/cos(2)
71
+ assert trigsimp(cot(2)*cos(3)*exp(2)/cos(2)) == cos(3)*exp(2)/sin(2)
72
+ assert trigsimp(cot(2)*cos(3)*exp(2)*tan(2)) == cos(3)*exp(2)
73
+ assert trigsimp(sinh(2)*cos(3)*exp(2)/cosh(2)) == tanh(2)*cos(3)*exp(2)
74
+ assert trigsimp(tanh(2)*cos(3)*exp(2)*cosh(2)) == sinh(2)*cos(3)*exp(2)
75
+ assert trigsimp(coth(2)*cos(3)*exp(2)*sinh(2)) == cosh(2)*cos(3)*exp(2)
76
+ assert trigsimp(tanh(2)*cos(3)*exp(2)/sinh(2)) == cos(3)*exp(2)/cosh(2)
77
+ assert trigsimp(coth(2)*cos(3)*exp(2)/cosh(2)) == cos(3)*exp(2)/sinh(2)
78
+ assert trigsimp(coth(2)*cos(3)*exp(2)*tanh(2)) == cos(3)*exp(2)
79
+
80
+
81
+ def test_trigsimp2():
82
+ x, y = symbols('x,y')
83
+ assert trigsimp(cos(x)**2*sin(y)**2 + cos(x)**2*cos(y)**2 + sin(x)**2,
84
+ recursive=True) == 1
85
+ assert trigsimp(sin(x)**2*sin(y)**2 + sin(x)**2*cos(y)**2 + cos(x)**2,
86
+ recursive=True) == 1
87
+ assert trigsimp(
88
+ Subs(x, x, sin(y)**2 + cos(y)**2)) == Subs(x, x, 1)
89
+
90
+
91
+ def test_issue_4373():
92
+ x = Symbol("x")
93
+ assert abs(trigsimp(2.0*sin(x)**2 + 2.0*cos(x)**2) - 2.0) < 1e-10
94
+
95
+
96
+ def test_trigsimp3():
97
+ x, y = symbols('x,y')
98
+ assert trigsimp(sin(x)/cos(x)) == tan(x)
99
+ assert trigsimp(sin(x)**2/cos(x)**2) == tan(x)**2
100
+ assert trigsimp(sin(x)**3/cos(x)**3) == tan(x)**3
101
+ assert trigsimp(sin(x)**10/cos(x)**10) == tan(x)**10
102
+
103
+ assert trigsimp(cos(x)/sin(x)) == 1/tan(x)
104
+ assert trigsimp(cos(x)**2/sin(x)**2) == 1/tan(x)**2
105
+ assert trigsimp(cos(x)**10/sin(x)**10) == 1/tan(x)**10
106
+
107
+ assert trigsimp(tan(x)) == trigsimp(sin(x)/cos(x))
108
+
109
+
110
+ def test_issue_4661():
111
+ a, x, y = symbols('a x y')
112
+ eq = -4*sin(x)**4 + 4*cos(x)**4 - 8*cos(x)**2
113
+ assert trigsimp(eq) == -4
114
+ n = sin(x)**6 + 4*sin(x)**4*cos(x)**2 + 5*sin(x)**2*cos(x)**4 + 2*cos(x)**6
115
+ d = -sin(x)**2 - 2*cos(x)**2
116
+ assert simplify(n/d) == -1
117
+ assert trigsimp(-2*cos(x)**2 + cos(x)**4 - sin(x)**4) == -1
118
+ eq = (- sin(x)**3/4)*cos(x) + (cos(x)**3/4)*sin(x) - sin(2*x)*cos(2*x)/8
119
+ assert trigsimp(eq) == 0
120
+
121
+
122
+ def test_issue_4494():
123
+ a, b = symbols('a b')
124
+ eq = sin(a)**2*sin(b)**2 + cos(a)**2*cos(b)**2*tan(a)**2 + cos(a)**2
125
+ assert trigsimp(eq) == 1
126
+
127
+
128
+ def test_issue_5948():
129
+ a, x, y = symbols('a x y')
130
+ assert trigsimp(diff(integrate(cos(x)/sin(x)**7, x), x)) == \
131
+ cos(x)/sin(x)**7
132
+
133
+
134
+ def test_issue_4775():
135
+ a, x, y = symbols('a x y')
136
+ assert trigsimp(sin(x)*cos(y)+cos(x)*sin(y)) == sin(x + y)
137
+ assert trigsimp(sin(x)*cos(y)+cos(x)*sin(y)+3) == sin(x + y) + 3
138
+
139
+
140
+ def test_issue_4280():
141
+ a, x, y = symbols('a x y')
142
+ assert trigsimp(cos(x)**2 + cos(y)**2*sin(x)**2 + sin(y)**2*sin(x)**2) == 1
143
+ assert trigsimp(a**2*sin(x)**2 + a**2*cos(y)**2*cos(x)**2 + a**2*cos(x)**2*sin(y)**2) == a**2
144
+ assert trigsimp(a**2*cos(y)**2*sin(x)**2 + a**2*sin(y)**2*sin(x)**2) == a**2*sin(x)**2
145
+
146
+
147
+ def test_issue_3210():
148
+ eqs = (sin(2)*cos(3) + sin(3)*cos(2),
149
+ -sin(2)*sin(3) + cos(2)*cos(3),
150
+ sin(2)*cos(3) - sin(3)*cos(2),
151
+ sin(2)*sin(3) + cos(2)*cos(3),
152
+ sin(2)*sin(3) + cos(2)*cos(3) + cos(2),
153
+ sinh(2)*cosh(3) + sinh(3)*cosh(2),
154
+ sinh(2)*sinh(3) + cosh(2)*cosh(3),
155
+ )
156
+ assert [trigsimp(e) for e in eqs] == [
157
+ sin(5),
158
+ cos(5),
159
+ -sin(1),
160
+ cos(1),
161
+ cos(1) + cos(2),
162
+ sinh(5),
163
+ cosh(5),
164
+ ]
165
+
166
+
167
+ def test_trigsimp_issues():
168
+ a, x, y = symbols('a x y')
169
+
170
+ # issue 4625 - factor_terms works, too
171
+ assert trigsimp(sin(x)**3 + cos(x)**2*sin(x)) == sin(x)
172
+
173
+ # issue 5948
174
+ assert trigsimp(diff(integrate(cos(x)/sin(x)**3, x), x)) == \
175
+ cos(x)/sin(x)**3
176
+ assert trigsimp(diff(integrate(sin(x)/cos(x)**3, x), x)) == \
177
+ sin(x)/cos(x)**3
178
+
179
+ # check integer exponents
180
+ e = sin(x)**y/cos(x)**y
181
+ assert trigsimp(e) == e
182
+ assert trigsimp(e.subs(y, 2)) == tan(x)**2
183
+ assert trigsimp(e.subs(x, 1)) == tan(1)**y
184
+
185
+ # check for multiple patterns
186
+ assert (cos(x)**2/sin(x)**2*cos(y)**2/sin(y)**2).trigsimp() == \
187
+ 1/tan(x)**2/tan(y)**2
188
+ assert trigsimp(cos(x)/sin(x)*cos(x+y)/sin(x+y)) == \
189
+ 1/(tan(x)*tan(x + y))
190
+
191
+ eq = cos(2)*(cos(3) + 1)**2/(cos(3) - 1)**2
192
+ assert trigsimp(eq) == eq.factor() # factor makes denom (-1 + cos(3))**2
193
+ assert trigsimp(cos(2)*(cos(3) + 1)**2*(cos(3) - 1)**2) == \
194
+ cos(2)*sin(3)**4
195
+
196
+ # issue 6789; this generates an expression that formerly caused
197
+ # trigsimp to hang
198
+ assert cot(x).equals(tan(x)) is False
199
+
200
+ # nan or the unchanged expression is ok, but not sin(1)
201
+ z = cos(x)**2 + sin(x)**2 - 1
202
+ z1 = tan(x)**2 - 1/cot(x)**2
203
+ n = (1 + z1/z)
204
+ assert trigsimp(sin(n)) != sin(1)
205
+ eq = x*(n - 1) - x*n
206
+ assert trigsimp(eq) is S.NaN
207
+ assert trigsimp(eq, recursive=True) is S.NaN
208
+ assert trigsimp(1).is_Integer
209
+
210
+ assert trigsimp(-sin(x)**4 - 2*sin(x)**2*cos(x)**2 - cos(x)**4) == -1
211
+
212
+
213
+ def test_trigsimp_issue_2515():
214
+ x = Symbol('x')
215
+ assert trigsimp(x*cos(x)*tan(x)) == x*sin(x)
216
+ assert trigsimp(-sin(x) + cos(x)*tan(x)) == 0
217
+
218
+
219
+ def test_trigsimp_issue_3826():
220
+ assert trigsimp(tan(2*x).expand(trig=True)) == tan(2*x)
221
+
222
+
223
+ def test_trigsimp_issue_4032():
224
+ n = Symbol('n', integer=True, positive=True)
225
+ assert trigsimp(2**(n/2)*cos(pi*n/4)/2 + 2**(n - 1)/2) == \
226
+ 2**(n/2)*cos(pi*n/4)/2 + 2**n/4
227
+
228
+
229
+ def test_trigsimp_issue_7761():
230
+ assert trigsimp(cosh(pi/4)) == cosh(pi/4)
231
+
232
+
233
+ def test_trigsimp_noncommutative():
234
+ x, y = symbols('x,y')
235
+ A, B = symbols('A,B', commutative=False)
236
+
237
+ assert trigsimp(A - A*sin(x)**2) == A*cos(x)**2
238
+ assert trigsimp(A - A*cos(x)**2) == A*sin(x)**2
239
+ assert trigsimp(A*sin(x)**2 + A*cos(x)**2) == A
240
+ assert trigsimp(A + A*tan(x)**2) == A/cos(x)**2
241
+ assert trigsimp(A/cos(x)**2 - A) == A*tan(x)**2
242
+ assert trigsimp(A/cos(x)**2 - A*tan(x)**2) == A
243
+ assert trigsimp(A + A*cot(x)**2) == A/sin(x)**2
244
+ assert trigsimp(A/sin(x)**2 - A) == A/tan(x)**2
245
+ assert trigsimp(A/sin(x)**2 - A*cot(x)**2) == A
246
+
247
+ assert trigsimp(y*A*cos(x)**2 + y*A*sin(x)**2) == y*A
248
+
249
+ assert trigsimp(A*sin(x)/cos(x)) == A*tan(x)
250
+ assert trigsimp(A*tan(x)*cos(x)) == A*sin(x)
251
+ assert trigsimp(A*cot(x)**3*sin(x)**3) == A*cos(x)**3
252
+ assert trigsimp(y*A*tan(x)**2/sin(x)**2) == y*A/cos(x)**2
253
+ assert trigsimp(A*cot(x)/cos(x)) == A/sin(x)
254
+
255
+ assert trigsimp(A*sin(x + y) + A*sin(x - y)) == 2*A*sin(x)*cos(y)
256
+ assert trigsimp(A*sin(x + y) - A*sin(x - y)) == 2*A*sin(y)*cos(x)
257
+ assert trigsimp(A*cos(x + y) + A*cos(x - y)) == 2*A*cos(x)*cos(y)
258
+ assert trigsimp(A*cos(x + y) - A*cos(x - y)) == -2*A*sin(x)*sin(y)
259
+
260
+ assert trigsimp(A*sinh(x + y) + A*sinh(x - y)) == 2*A*sinh(x)*cosh(y)
261
+ assert trigsimp(A*sinh(x + y) - A*sinh(x - y)) == 2*A*sinh(y)*cosh(x)
262
+ assert trigsimp(A*cosh(x + y) + A*cosh(x - y)) == 2*A*cosh(x)*cosh(y)
263
+ assert trigsimp(A*cosh(x + y) - A*cosh(x - y)) == 2*A*sinh(x)*sinh(y)
264
+
265
+ assert trigsimp(A*cos(0.12345)**2 + A*sin(0.12345)**2) == 1.0*A
266
+
267
+
268
+ def test_hyperbolic_simp():
269
+ x, y = symbols('x,y')
270
+
271
+ assert trigsimp(sinh(x)**2 + 1) == cosh(x)**2
272
+ assert trigsimp(cosh(x)**2 - 1) == sinh(x)**2
273
+ assert trigsimp(cosh(x)**2 - sinh(x)**2) == 1
274
+ assert trigsimp(1 - tanh(x)**2) == 1/cosh(x)**2
275
+ assert trigsimp(1 - 1/cosh(x)**2) == tanh(x)**2
276
+ assert trigsimp(tanh(x)**2 + 1/cosh(x)**2) == 1
277
+ assert trigsimp(coth(x)**2 - 1) == 1/sinh(x)**2
278
+ assert trigsimp(1/sinh(x)**2 + 1) == 1/tanh(x)**2
279
+ assert trigsimp(coth(x)**2 - 1/sinh(x)**2) == 1
280
+
281
+ assert trigsimp(5*cosh(x)**2 - 5*sinh(x)**2) == 5
282
+ assert trigsimp(5*cosh(x/2)**2 - 2*sinh(x/2)**2) == 3*cosh(x)/2 + Rational(7, 2)
283
+
284
+ assert trigsimp(sinh(x)/cosh(x)) == tanh(x)
285
+ assert trigsimp(tanh(x)) == trigsimp(sinh(x)/cosh(x))
286
+ assert trigsimp(cosh(x)/sinh(x)) == 1/tanh(x)
287
+ assert trigsimp(2*tanh(x)*cosh(x)) == 2*sinh(x)
288
+ assert trigsimp(coth(x)**3*sinh(x)**3) == cosh(x)**3
289
+ assert trigsimp(y*tanh(x)**2/sinh(x)**2) == y/cosh(x)**2
290
+ assert trigsimp(coth(x)/cosh(x)) == 1/sinh(x)
291
+
292
+ for a in (pi/6*I, pi/4*I, pi/3*I):
293
+ assert trigsimp(sinh(a)*cosh(x) + cosh(a)*sinh(x)) == sinh(x + a)
294
+ assert trigsimp(-sinh(a)*cosh(x) + cosh(a)*sinh(x)) == sinh(x - a)
295
+
296
+ e = 2*cosh(x)**2 - 2*sinh(x)**2
297
+ assert trigsimp(log(e)) == log(2)
298
+
299
+ # issue 19535:
300
+ assert trigsimp(sqrt(cosh(x)**2 - 1)) == sqrt(sinh(x)**2)
301
+
302
+ assert trigsimp(cosh(x)**2*cosh(y)**2 - cosh(x)**2*sinh(y)**2 - sinh(x)**2,
303
+ recursive=True) == 1
304
+ assert trigsimp(sinh(x)**2*sinh(y)**2 - sinh(x)**2*cosh(y)**2 + cosh(x)**2,
305
+ recursive=True) == 1
306
+
307
+ assert abs(trigsimp(2.0*cosh(x)**2 - 2.0*sinh(x)**2) - 2.0) < 1e-10
308
+
309
+ assert trigsimp(sinh(x)**2/cosh(x)**2) == tanh(x)**2
310
+ assert trigsimp(sinh(x)**3/cosh(x)**3) == tanh(x)**3
311
+ assert trigsimp(sinh(x)**10/cosh(x)**10) == tanh(x)**10
312
+ assert trigsimp(cosh(x)**3/sinh(x)**3) == 1/tanh(x)**3
313
+
314
+ assert trigsimp(cosh(x)/sinh(x)) == 1/tanh(x)
315
+ assert trigsimp(cosh(x)**2/sinh(x)**2) == 1/tanh(x)**2
316
+ assert trigsimp(cosh(x)**10/sinh(x)**10) == 1/tanh(x)**10
317
+
318
+ assert trigsimp(x*cosh(x)*tanh(x)) == x*sinh(x)
319
+ assert trigsimp(-sinh(x) + cosh(x)*tanh(x)) == 0
320
+
321
+ assert tan(x) != 1/cot(x) # cot doesn't auto-simplify
322
+
323
+ assert trigsimp(tan(x) - 1/cot(x)) == 0
324
+ assert trigsimp(3*tanh(x)**7 - 2/coth(x)**7) == tanh(x)**7
325
+
326
+
327
+ def test_trigsimp_groebner():
328
+ from sympy.simplify.trigsimp import trigsimp_groebner
329
+
330
+ c = cos(x)
331
+ s = sin(x)
332
+ ex = (4*s*c + 12*s + 5*c**3 + 21*c**2 + 23*c + 15)/(
333
+ -s*c**2 + 2*s*c + 15*s + 7*c**3 + 31*c**2 + 37*c + 21)
334
+ resnum = (5*s - 5*c + 1)
335
+ resdenom = (8*s - 6*c)
336
+ results = [resnum/resdenom, (-resnum)/(-resdenom)]
337
+ assert trigsimp_groebner(ex) in results
338
+ assert trigsimp_groebner(s/c, hints=[tan]) == tan(x)
339
+ assert trigsimp_groebner(c*s) == c*s
340
+ assert trigsimp((-s + 1)/c + c/(-s + 1),
341
+ method='groebner') == 2/c
342
+ assert trigsimp((-s + 1)/c + c/(-s + 1),
343
+ method='groebner', polynomial=True) == 2/c
344
+
345
+ # Test quick=False works
346
+ assert trigsimp_groebner(ex, hints=[2]) in results
347
+ assert trigsimp_groebner(ex, hints=[int(2)]) in results
348
+
349
+ # test "I"
350
+ assert trigsimp_groebner(sin(I*x)/cos(I*x), hints=[tanh]) == I*tanh(x)
351
+
352
+ # test hyperbolic / sums
353
+ assert trigsimp_groebner((tanh(x)+tanh(y))/(1+tanh(x)*tanh(y)),
354
+ hints=[(tanh, x, y)]) == tanh(x + y)
355
+
356
+
357
+ def test_issue_2827_trigsimp_methods():
358
+ measure1 = lambda expr: len(str(expr))
359
+ measure2 = lambda expr: -count_ops(expr)
360
+ # Return the most complicated result
361
+ expr = (x + 1)/(x + sin(x)**2 + cos(x)**2)
362
+ ans = Matrix([1])
363
+ M = Matrix([expr])
364
+ assert trigsimp(M, method='fu', measure=measure1) == ans
365
+ assert trigsimp(M, method='fu', measure=measure2) != ans
366
+ # all methods should work with Basic expressions even if they
367
+ # aren't Expr
368
+ M = Matrix.eye(1)
369
+ assert all(trigsimp(M, method=m) == M for m in
370
+ 'fu matching groebner old'.split())
371
+ # watch for E in exptrigsimp, not only exp()
372
+ eq = 1/sqrt(E) + E
373
+ assert exptrigsimp(eq) == eq
374
+
375
+ def test_issue_15129_trigsimp_methods():
376
+ t1 = Matrix([sin(Rational(1, 50)), cos(Rational(1, 50)), 0])
377
+ t2 = Matrix([sin(Rational(1, 25)), cos(Rational(1, 25)), 0])
378
+ t3 = Matrix([cos(Rational(1, 25)), sin(Rational(1, 25)), 0])
379
+ r1 = t1.dot(t2)
380
+ r2 = t1.dot(t3)
381
+ assert trigsimp(r1) == cos(Rational(1, 50))
382
+ assert trigsimp(r2) == sin(Rational(3, 50))
383
+
384
+ def test_exptrigsimp():
385
+ def valid(a, b):
386
+ from sympy.core.random import verify_numerically as tn
387
+ if not (tn(a, b) and a == b):
388
+ return False
389
+ return True
390
+
391
+ assert exptrigsimp(exp(x) + exp(-x)) == 2*cosh(x)
392
+ assert exptrigsimp(exp(x) - exp(-x)) == 2*sinh(x)
393
+ assert exptrigsimp((2*exp(x)-2*exp(-x))/(exp(x)+exp(-x))) == 2*tanh(x)
394
+ assert exptrigsimp((2*exp(2*x)-2)/(exp(2*x)+1)) == 2*tanh(x)
395
+ e = [cos(x) + I*sin(x), cos(x) - I*sin(x),
396
+ cosh(x) - sinh(x), cosh(x) + sinh(x)]
397
+ ok = [exp(I*x), exp(-I*x), exp(-x), exp(x)]
398
+ assert all(valid(i, j) for i, j in zip(
399
+ [exptrigsimp(ei) for ei in e], ok))
400
+
401
+ ue = [cos(x) + sin(x), cos(x) - sin(x),
402
+ cosh(x) + I*sinh(x), cosh(x) - I*sinh(x)]
403
+ assert [exptrigsimp(ei) == ei for ei in ue]
404
+
405
+ res = []
406
+ ok = [y*tanh(1), 1/(y*tanh(1)), I*y*tan(1), -I/(y*tan(1)),
407
+ y*tanh(x), 1/(y*tanh(x)), I*y*tan(x), -I/(y*tan(x)),
408
+ y*tanh(1 + I), 1/(y*tanh(1 + I))]
409
+ for a in (1, I, x, I*x, 1 + I):
410
+ w = exp(a)
411
+ eq = y*(w - 1/w)/(w + 1/w)
412
+ res.append(simplify(eq))
413
+ res.append(simplify(1/eq))
414
+ assert all(valid(i, j) for i, j in zip(res, ok))
415
+
416
+ for a in range(1, 3):
417
+ w = exp(a)
418
+ e = w + 1/w
419
+ s = simplify(e)
420
+ assert s == exptrigsimp(e)
421
+ assert valid(s, 2*cosh(a))
422
+ e = w - 1/w
423
+ s = simplify(e)
424
+ assert s == exptrigsimp(e)
425
+ assert valid(s, 2*sinh(a))
426
+
427
+ def test_exptrigsimp_noncommutative():
428
+ a,b = symbols('a b', commutative=False)
429
+ x = Symbol('x', commutative=True)
430
+ assert exp(a + x) == exptrigsimp(exp(a)*exp(x))
431
+ p = exp(a)*exp(b) - exp(b)*exp(a)
432
+ assert p == exptrigsimp(p) != 0
433
+
434
+ def test_powsimp_on_numbers():
435
+ assert 2**(Rational(1, 3) - 2) == 2**Rational(1, 3)/4
436
+
437
+
438
+ @XFAIL
439
+ def test_issue_6811_fail():
440
+ # from doc/src/modules/physics/mechanics/examples.rst, the current `eq`
441
+ # at Line 576 (in different variables) was formerly the equivalent and
442
+ # shorter expression given below...it would be nice to get the short one
443
+ # back again
444
+ xp, y, x, z = symbols('xp, y, x, z')
445
+ eq = 4*(-19*sin(x)*y + 5*sin(3*x)*y + 15*cos(2*x)*z - 21*z)*xp/(9*cos(x) - 5*cos(3*x))
446
+ assert trigsimp(eq) == -2*(2*cos(x)*tan(x)*y + 3*z)*xp/cos(x)
447
+
448
+
449
+ def test_Piecewise():
450
+ e1 = x*(x + y) - y*(x + y)
451
+ e2 = sin(x)**2 + cos(x)**2
452
+ e3 = expand((x + y)*y/x)
453
+ # s1 = simplify(e1)
454
+ s2 = simplify(e2)
455
+ # s3 = simplify(e3)
456
+
457
+ # trigsimp tries not to touch non-trig containing args
458
+ assert trigsimp(Piecewise((e1, e3 < e2), (e3, True))) == \
459
+ Piecewise((e1, e3 < s2), (e3, True))
460
+
461
+
462
+ def test_issue_21594():
463
+ assert simplify(exp(Rational(1,2)) + exp(Rational(-1,2))) == cosh(S.Half)*2
464
+
465
+
466
+ def test_trigsimp_old():
467
+ x, y = symbols('x,y')
468
+
469
+ assert trigsimp(1 - sin(x)**2, old=True) == cos(x)**2
470
+ assert trigsimp(1 - cos(x)**2, old=True) == sin(x)**2
471
+ assert trigsimp(sin(x)**2 + cos(x)**2, old=True) == 1
472
+ assert trigsimp(1 + tan(x)**2, old=True) == 1/cos(x)**2
473
+ assert trigsimp(1/cos(x)**2 - 1, old=True) == tan(x)**2
474
+ assert trigsimp(1/cos(x)**2 - tan(x)**2, old=True) == 1
475
+ assert trigsimp(1 + cot(x)**2, old=True) == 1/sin(x)**2
476
+ assert trigsimp(1/sin(x)**2 - cot(x)**2, old=True) == 1
477
+
478
+ assert trigsimp(5*cos(x)**2 + 5*sin(x)**2, old=True) == 5
479
+
480
+ assert trigsimp(sin(x)/cos(x), old=True) == tan(x)
481
+ assert trigsimp(2*tan(x)*cos(x), old=True) == 2*sin(x)
482
+ assert trigsimp(cot(x)**3*sin(x)**3, old=True) == cos(x)**3
483
+ assert trigsimp(y*tan(x)**2/sin(x)**2, old=True) == y/cos(x)**2
484
+ assert trigsimp(cot(x)/cos(x), old=True) == 1/sin(x)
485
+
486
+ assert trigsimp(sin(x + y) + sin(x - y), old=True) == 2*sin(x)*cos(y)
487
+ assert trigsimp(sin(x + y) - sin(x - y), old=True) == 2*sin(y)*cos(x)
488
+ assert trigsimp(cos(x + y) + cos(x - y), old=True) == 2*cos(x)*cos(y)
489
+ assert trigsimp(cos(x + y) - cos(x - y), old=True) == -2*sin(x)*sin(y)
490
+
491
+ assert trigsimp(sinh(x + y) + sinh(x - y), old=True) == 2*sinh(x)*cosh(y)
492
+ assert trigsimp(sinh(x + y) - sinh(x - y), old=True) == 2*sinh(y)*cosh(x)
493
+ assert trigsimp(cosh(x + y) + cosh(x - y), old=True) == 2*cosh(x)*cosh(y)
494
+ assert trigsimp(cosh(x + y) - cosh(x - y), old=True) == 2*sinh(x)*sinh(y)
495
+
496
+ assert trigsimp(cos(0.12345)**2 + sin(0.12345)**2, old=True) == 1.0
497
+
498
+ assert trigsimp(sin(x)/cos(x), old=True, method='combined') == tan(x)
499
+ assert trigsimp(sin(x)/cos(x), old=True, method='groebner') == sin(x)/cos(x)
500
+ assert trigsimp(sin(x)/cos(x), old=True, method='groebner', hints=[tan]) == tan(x)
501
+
502
+ assert trigsimp(1-sin(sin(x)**2+cos(x)**2)**2, old=True, deep=True) == cos(1)**2
503
+
504
+
505
+ def test_trigsimp_inverse():
506
+ alpha = symbols('alpha')
507
+ s, c = sin(alpha), cos(alpha)
508
+
509
+ for finv in [asin, acos, asec, acsc, atan, acot]:
510
+ f = finv.inverse(None)
511
+ assert alpha == trigsimp(finv(f(alpha)), inverse=True)
512
+
513
+ # test atan2(cos, sin), atan2(sin, cos), etc...
514
+ for a, b in [[c, s], [s, c]]:
515
+ for i, j in product([-1, 1], repeat=2):
516
+ angle = atan2(i*b, j*a)
517
+ angle_inverted = trigsimp(angle, inverse=True)
518
+ assert angle_inverted != angle # assures simplification happened
519
+ assert sin(angle_inverted) == trigsimp(sin(angle))
520
+ assert cos(angle_inverted) == trigsimp(cos(angle))
lib/python3.12/site-packages/sympy/simplify/trigsimp.py ADDED
@@ -0,0 +1,1253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from collections import defaultdict
2
+ from functools import reduce
3
+
4
+ from sympy.core import (sympify, Basic, S, Expr, factor_terms,
5
+ Mul, Add, bottom_up)
6
+ from sympy.core.cache import cacheit
7
+ from sympy.core.function import (count_ops, _mexpand, FunctionClass, expand,
8
+ expand_mul, _coeff_isneg, Derivative)
9
+ from sympy.core.numbers import I, Integer
10
+ from sympy.core.intfunc import igcd
11
+ from sympy.core.sorting import _nodes
12
+ from sympy.core.symbol import Dummy, symbols, Wild
13
+ from sympy.external.gmpy import SYMPY_INTS
14
+ from sympy.functions import sin, cos, exp, cosh, tanh, sinh, tan, cot, coth
15
+ from sympy.functions import atan2
16
+ from sympy.functions.elementary.hyperbolic import HyperbolicFunction
17
+ from sympy.functions.elementary.trigonometric import TrigonometricFunction
18
+ from sympy.polys import Poly, factor, cancel, parallel_poly_from_expr
19
+ from sympy.polys.domains import ZZ
20
+ from sympy.polys.polyerrors import PolificationFailed
21
+ from sympy.polys.polytools import groebner
22
+ from sympy.simplify.cse_main import cse
23
+ from sympy.strategies.core import identity
24
+ from sympy.strategies.tree import greedy
25
+ from sympy.utilities.iterables import iterable
26
+ from sympy.utilities.misc import debug
27
+
28
+ def trigsimp_groebner(expr, hints=[], quick=False, order="grlex",
29
+ polynomial=False):
30
+ """
31
+ Simplify trigonometric expressions using a groebner basis algorithm.
32
+
33
+ Explanation
34
+ ===========
35
+
36
+ This routine takes a fraction involving trigonometric or hyperbolic
37
+ expressions, and tries to simplify it. The primary metric is the
38
+ total degree. Some attempts are made to choose the simplest possible
39
+ expression of the minimal degree, but this is non-rigorous, and also
40
+ very slow (see the ``quick=True`` option).
41
+
42
+ If ``polynomial`` is set to True, instead of simplifying numerator and
43
+ denominator together, this function just brings numerator and denominator
44
+ into a canonical form. This is much faster, but has potentially worse
45
+ results. However, if the input is a polynomial, then the result is
46
+ guaranteed to be an equivalent polynomial of minimal degree.
47
+
48
+ The most important option is hints. Its entries can be any of the
49
+ following:
50
+
51
+ - a natural number
52
+ - a function
53
+ - an iterable of the form (func, var1, var2, ...)
54
+ - anything else, interpreted as a generator
55
+
56
+ A number is used to indicate that the search space should be increased.
57
+ A function is used to indicate that said function is likely to occur in a
58
+ simplified expression.
59
+ An iterable is used indicate that func(var1 + var2 + ...) is likely to
60
+ occur in a simplified .
61
+ An additional generator also indicates that it is likely to occur.
62
+ (See examples below).
63
+
64
+ This routine carries out various computationally intensive algorithms.
65
+ The option ``quick=True`` can be used to suppress one particularly slow
66
+ step (at the expense of potentially more complicated results, but never at
67
+ the expense of increased total degree).
68
+
69
+ Examples
70
+ ========
71
+
72
+ >>> from sympy.abc import x, y
73
+ >>> from sympy import sin, tan, cos, sinh, cosh, tanh
74
+ >>> from sympy.simplify.trigsimp import trigsimp_groebner
75
+
76
+ Suppose you want to simplify ``sin(x)*cos(x)``. Naively, nothing happens:
77
+
78
+ >>> ex = sin(x)*cos(x)
79
+ >>> trigsimp_groebner(ex)
80
+ sin(x)*cos(x)
81
+
82
+ This is because ``trigsimp_groebner`` only looks for a simplification
83
+ involving just ``sin(x)`` and ``cos(x)``. You can tell it to also try
84
+ ``2*x`` by passing ``hints=[2]``:
85
+
86
+ >>> trigsimp_groebner(ex, hints=[2])
87
+ sin(2*x)/2
88
+ >>> trigsimp_groebner(sin(x)**2 - cos(x)**2, hints=[2])
89
+ -cos(2*x)
90
+
91
+ Increasing the search space this way can quickly become expensive. A much
92
+ faster way is to give a specific expression that is likely to occur:
93
+
94
+ >>> trigsimp_groebner(ex, hints=[sin(2*x)])
95
+ sin(2*x)/2
96
+
97
+ Hyperbolic expressions are similarly supported:
98
+
99
+ >>> trigsimp_groebner(sinh(2*x)/sinh(x))
100
+ 2*cosh(x)
101
+
102
+ Note how no hints had to be passed, since the expression already involved
103
+ ``2*x``.
104
+
105
+ The tangent function is also supported. You can either pass ``tan`` in the
106
+ hints, to indicate that tan should be tried whenever cosine or sine are,
107
+ or you can pass a specific generator:
108
+
109
+ >>> trigsimp_groebner(sin(x)/cos(x), hints=[tan])
110
+ tan(x)
111
+ >>> trigsimp_groebner(sinh(x)/cosh(x), hints=[tanh(x)])
112
+ tanh(x)
113
+
114
+ Finally, you can use the iterable form to suggest that angle sum formulae
115
+ should be tried:
116
+
117
+ >>> ex = (tan(x) + tan(y))/(1 - tan(x)*tan(y))
118
+ >>> trigsimp_groebner(ex, hints=[(tan, x, y)])
119
+ tan(x + y)
120
+ """
121
+ # TODO
122
+ # - preprocess by replacing everything by funcs we can handle
123
+ # - optionally use cot instead of tan
124
+ # - more intelligent hinting.
125
+ # For example, if the ideal is small, and we have sin(x), sin(y),
126
+ # add sin(x + y) automatically... ?
127
+ # - algebraic numbers ...
128
+ # - expressions of lowest degree are not distinguished properly
129
+ # e.g. 1 - sin(x)**2
130
+ # - we could try to order the generators intelligently, so as to influence
131
+ # which monomials appear in the quotient basis
132
+
133
+ # THEORY
134
+ # ------
135
+ # Ratsimpmodprime above can be used to "simplify" a rational function
136
+ # modulo a prime ideal. "Simplify" mainly means finding an equivalent
137
+ # expression of lower total degree.
138
+ #
139
+ # We intend to use this to simplify trigonometric functions. To do that,
140
+ # we need to decide (a) which ring to use, and (b) modulo which ideal to
141
+ # simplify. In practice, (a) means settling on a list of "generators"
142
+ # a, b, c, ..., such that the fraction we want to simplify is a rational
143
+ # function in a, b, c, ..., with coefficients in ZZ (integers).
144
+ # (2) means that we have to decide what relations to impose on the
145
+ # generators. There are two practical problems:
146
+ # (1) The ideal has to be *prime* (a technical term).
147
+ # (2) The relations have to be polynomials in the generators.
148
+ #
149
+ # We typically have two kinds of generators:
150
+ # - trigonometric expressions, like sin(x), cos(5*x), etc
151
+ # - "everything else", like gamma(x), pi, etc.
152
+ #
153
+ # Since this function is trigsimp, we will concentrate on what to do with
154
+ # trigonometric expressions. We can also simplify hyperbolic expressions,
155
+ # but the extensions should be clear.
156
+ #
157
+ # One crucial point is that all *other* generators really should behave
158
+ # like indeterminates. In particular if (say) "I" is one of them, then
159
+ # in fact I**2 + 1 = 0 and we may and will compute non-sensical
160
+ # expressions. However, we can work with a dummy and add the relation
161
+ # I**2 + 1 = 0 to our ideal, then substitute back in the end.
162
+ #
163
+ # Now regarding trigonometric generators. We split them into groups,
164
+ # according to the argument of the trigonometric functions. We want to
165
+ # organise this in such a way that most trigonometric identities apply in
166
+ # the same group. For example, given sin(x), cos(2*x) and cos(y), we would
167
+ # group as [sin(x), cos(2*x)] and [cos(y)].
168
+ #
169
+ # Our prime ideal will be built in three steps:
170
+ # (1) For each group, compute a "geometrically prime" ideal of relations.
171
+ # Geometrically prime means that it generates a prime ideal in
172
+ # CC[gens], not just ZZ[gens].
173
+ # (2) Take the union of all the generators of the ideals for all groups.
174
+ # By the geometric primality condition, this is still prime.
175
+ # (3) Add further inter-group relations which preserve primality.
176
+ #
177
+ # Step (1) works as follows. We will isolate common factors in the
178
+ # argument, so that all our generators are of the form sin(n*x), cos(n*x)
179
+ # or tan(n*x), with n an integer. Suppose first there are no tan terms.
180
+ # The ideal [sin(x)**2 + cos(x)**2 - 1] is geometrically prime, since
181
+ # X**2 + Y**2 - 1 is irreducible over CC.
182
+ # Now, if we have a generator sin(n*x), than we can, using trig identities,
183
+ # express sin(n*x) as a polynomial in sin(x) and cos(x). We can add this
184
+ # relation to the ideal, preserving geometric primality, since the quotient
185
+ # ring is unchanged.
186
+ # Thus we have treated all sin and cos terms.
187
+ # For tan(n*x), we add a relation tan(n*x)*cos(n*x) - sin(n*x) = 0.
188
+ # (This requires of course that we already have relations for cos(n*x) and
189
+ # sin(n*x).) It is not obvious, but it seems that this preserves geometric
190
+ # primality.
191
+ # XXX A real proof would be nice. HELP!
192
+ # Sketch that <S**2 + C**2 - 1, C*T - S> is a prime ideal of
193
+ # CC[S, C, T]:
194
+ # - it suffices to show that the projective closure in CP**3 is
195
+ # irreducible
196
+ # - using the half-angle substitutions, we can express sin(x), tan(x),
197
+ # cos(x) as rational functions in tan(x/2)
198
+ # - from this, we get a rational map from CP**1 to our curve
199
+ # - this is a morphism, hence the curve is prime
200
+ #
201
+ # Step (2) is trivial.
202
+ #
203
+ # Step (3) works by adding selected relations of the form
204
+ # sin(x + y) - sin(x)*cos(y) - sin(y)*cos(x), etc. Geometric primality is
205
+ # preserved by the same argument as before.
206
+
207
+ def parse_hints(hints):
208
+ """Split hints into (n, funcs, iterables, gens)."""
209
+ n = 1
210
+ funcs, iterables, gens = [], [], []
211
+ for e in hints:
212
+ if isinstance(e, (SYMPY_INTS, Integer)):
213
+ n = e
214
+ elif isinstance(e, FunctionClass):
215
+ funcs.append(e)
216
+ elif iterable(e):
217
+ iterables.append((e[0], e[1:]))
218
+ # XXX sin(x+2y)?
219
+ # Note: we go through polys so e.g.
220
+ # sin(-x) -> -sin(x) -> sin(x)
221
+ gens.extend(parallel_poly_from_expr(
222
+ [e[0](x) for x in e[1:]] + [e[0](Add(*e[1:]))])[1].gens)
223
+ else:
224
+ gens.append(e)
225
+ return n, funcs, iterables, gens
226
+
227
+ def build_ideal(x, terms):
228
+ """
229
+ Build generators for our ideal. ``Terms`` is an iterable with elements of
230
+ the form (fn, coeff), indicating that we have a generator fn(coeff*x).
231
+
232
+ If any of the terms is trigonometric, sin(x) and cos(x) are guaranteed
233
+ to appear in terms. Similarly for hyperbolic functions. For tan(n*x),
234
+ sin(n*x) and cos(n*x) are guaranteed.
235
+ """
236
+ I = []
237
+ y = Dummy('y')
238
+ for fn, coeff in terms:
239
+ for c, s, t, rel in (
240
+ [cos, sin, tan, cos(x)**2 + sin(x)**2 - 1],
241
+ [cosh, sinh, tanh, cosh(x)**2 - sinh(x)**2 - 1]):
242
+ if coeff == 1 and fn in [c, s]:
243
+ I.append(rel)
244
+ elif fn == t:
245
+ I.append(t(coeff*x)*c(coeff*x) - s(coeff*x))
246
+ elif fn in [c, s]:
247
+ cn = fn(coeff*y).expand(trig=True).subs(y, x)
248
+ I.append(fn(coeff*x) - cn)
249
+ return list(set(I))
250
+
251
+ def analyse_gens(gens, hints):
252
+ """
253
+ Analyse the generators ``gens``, using the hints ``hints``.
254
+
255
+ The meaning of ``hints`` is described in the main docstring.
256
+ Return a new list of generators, and also the ideal we should
257
+ work with.
258
+ """
259
+ # First parse the hints
260
+ n, funcs, iterables, extragens = parse_hints(hints)
261
+ debug('n=%s funcs: %s iterables: %s extragens: %s',
262
+ (funcs, iterables, extragens))
263
+
264
+ # We just add the extragens to gens and analyse them as before
265
+ gens = list(gens)
266
+ gens.extend(extragens)
267
+
268
+ # remove duplicates
269
+ funcs = list(set(funcs))
270
+ iterables = list(set(iterables))
271
+ gens = list(set(gens))
272
+
273
+ # all the functions we can do anything with
274
+ allfuncs = {sin, cos, tan, sinh, cosh, tanh}
275
+ # sin(3*x) -> ((3, x), sin)
276
+ trigterms = [(g.args[0].as_coeff_mul(), g.func) for g in gens
277
+ if g.func in allfuncs]
278
+ # Our list of new generators - start with anything that we cannot
279
+ # work with (i.e. is not a trigonometric term)
280
+ freegens = [g for g in gens if g.func not in allfuncs]
281
+ newgens = []
282
+ trigdict = {}
283
+ for (coeff, var), fn in trigterms:
284
+ trigdict.setdefault(var, []).append((coeff, fn))
285
+ res = [] # the ideal
286
+
287
+ for key, val in trigdict.items():
288
+ # We have now assembeled a dictionary. Its keys are common
289
+ # arguments in trigonometric expressions, and values are lists of
290
+ # pairs (fn, coeff). x0, (fn, coeff) in trigdict means that we
291
+ # need to deal with fn(coeff*x0). We take the rational gcd of the
292
+ # coeffs, call it ``gcd``. We then use x = x0/gcd as "base symbol",
293
+ # all other arguments are integral multiples thereof.
294
+ # We will build an ideal which works with sin(x), cos(x).
295
+ # If hint tan is provided, also work with tan(x). Moreover, if
296
+ # n > 1, also work with sin(k*x) for k <= n, and similarly for cos
297
+ # (and tan if the hint is provided). Finally, any generators which
298
+ # the ideal does not work with but we need to accommodate (either
299
+ # because it was in expr or because it was provided as a hint)
300
+ # we also build into the ideal.
301
+ # This selection process is expressed in the list ``terms``.
302
+ # build_ideal then generates the actual relations in our ideal,
303
+ # from this list.
304
+ fns = [x[1] for x in val]
305
+ val = [x[0] for x in val]
306
+ gcd = reduce(igcd, val)
307
+ terms = [(fn, v/gcd) for (fn, v) in zip(fns, val)]
308
+ fs = set(funcs + fns)
309
+ for c, s, t in ([cos, sin, tan], [cosh, sinh, tanh]):
310
+ if any(x in fs for x in (c, s, t)):
311
+ fs.add(c)
312
+ fs.add(s)
313
+ for fn in fs:
314
+ for k in range(1, n + 1):
315
+ terms.append((fn, k))
316
+ extra = []
317
+ for fn, v in terms:
318
+ if fn == tan:
319
+ extra.append((sin, v))
320
+ extra.append((cos, v))
321
+ if fn in [sin, cos] and tan in fs:
322
+ extra.append((tan, v))
323
+ if fn == tanh:
324
+ extra.append((sinh, v))
325
+ extra.append((cosh, v))
326
+ if fn in [sinh, cosh] and tanh in fs:
327
+ extra.append((tanh, v))
328
+ terms.extend(extra)
329
+ x = gcd*Mul(*key)
330
+ r = build_ideal(x, terms)
331
+ res.extend(r)
332
+ newgens.extend({fn(v*x) for fn, v in terms})
333
+
334
+ # Add generators for compound expressions from iterables
335
+ for fn, args in iterables:
336
+ if fn == tan:
337
+ # Tan expressions are recovered from sin and cos.
338
+ iterables.extend([(sin, args), (cos, args)])
339
+ elif fn == tanh:
340
+ # Tanh expressions are recovered from sihn and cosh.
341
+ iterables.extend([(sinh, args), (cosh, args)])
342
+ else:
343
+ dummys = symbols('d:%i' % len(args), cls=Dummy)
344
+ expr = fn( Add(*dummys)).expand(trig=True).subs(list(zip(dummys, args)))
345
+ res.append(fn(Add(*args)) - expr)
346
+
347
+ if myI in gens:
348
+ res.append(myI**2 + 1)
349
+ freegens.remove(myI)
350
+ newgens.append(myI)
351
+
352
+ return res, freegens, newgens
353
+
354
+ myI = Dummy('I')
355
+ expr = expr.subs(S.ImaginaryUnit, myI)
356
+ subs = [(myI, S.ImaginaryUnit)]
357
+
358
+ num, denom = cancel(expr).as_numer_denom()
359
+ try:
360
+ (pnum, pdenom), opt = parallel_poly_from_expr([num, denom])
361
+ except PolificationFailed:
362
+ return expr
363
+ debug('initial gens:', opt.gens)
364
+ ideal, freegens, gens = analyse_gens(opt.gens, hints)
365
+ debug('ideal:', ideal)
366
+ debug('new gens:', gens, " -- len", len(gens))
367
+ debug('free gens:', freegens, " -- len", len(gens))
368
+ # NOTE we force the domain to be ZZ to stop polys from injecting generators
369
+ # (which is usually a sign of a bug in the way we build the ideal)
370
+ if not gens:
371
+ return expr
372
+ G = groebner(ideal, order=order, gens=gens, domain=ZZ)
373
+ debug('groebner basis:', list(G), " -- len", len(G))
374
+
375
+ # If our fraction is a polynomial in the free generators, simplify all
376
+ # coefficients separately:
377
+
378
+ from sympy.simplify.ratsimp import ratsimpmodprime
379
+
380
+ if freegens and pdenom.has_only_gens(*set(gens).intersection(pdenom.gens)):
381
+ num = Poly(num, gens=gens+freegens).eject(*gens)
382
+ res = []
383
+ for monom, coeff in num.terms():
384
+ ourgens = set(parallel_poly_from_expr([coeff, denom])[1].gens)
385
+ # We compute the transitive closure of all generators that can
386
+ # be reached from our generators through relations in the ideal.
387
+ changed = True
388
+ while changed:
389
+ changed = False
390
+ for p in ideal:
391
+ p = Poly(p)
392
+ if not ourgens.issuperset(p.gens) and \
393
+ not p.has_only_gens(*set(p.gens).difference(ourgens)):
394
+ changed = True
395
+ ourgens.update(p.exclude().gens)
396
+ # NOTE preserve order!
397
+ realgens = [x for x in gens if x in ourgens]
398
+ # The generators of the ideal have now been (implicitly) split
399
+ # into two groups: those involving ourgens and those that don't.
400
+ # Since we took the transitive closure above, these two groups
401
+ # live in subgrings generated by a *disjoint* set of variables.
402
+ # Any sensible groebner basis algorithm will preserve this disjoint
403
+ # structure (i.e. the elements of the groebner basis can be split
404
+ # similarly), and and the two subsets of the groebner basis then
405
+ # form groebner bases by themselves. (For the smaller generating
406
+ # sets, of course.)
407
+ ourG = [g.as_expr() for g in G.polys if
408
+ g.has_only_gens(*ourgens.intersection(g.gens))]
409
+ res.append(Mul(*[a**b for a, b in zip(freegens, monom)]) * \
410
+ ratsimpmodprime(coeff/denom, ourG, order=order,
411
+ gens=realgens, quick=quick, domain=ZZ,
412
+ polynomial=polynomial).subs(subs))
413
+ return Add(*res)
414
+ # NOTE The following is simpler and has less assumptions on the
415
+ # groebner basis algorithm. If the above turns out to be broken,
416
+ # use this.
417
+ return Add(*[Mul(*[a**b for a, b in zip(freegens, monom)]) * \
418
+ ratsimpmodprime(coeff/denom, list(G), order=order,
419
+ gens=gens, quick=quick, domain=ZZ)
420
+ for monom, coeff in num.terms()])
421
+ else:
422
+ return ratsimpmodprime(
423
+ expr, list(G), order=order, gens=freegens+gens,
424
+ quick=quick, domain=ZZ, polynomial=polynomial).subs(subs)
425
+
426
+
427
+ _trigs = (TrigonometricFunction, HyperbolicFunction)
428
+
429
+
430
+ def _trigsimp_inverse(rv):
431
+
432
+ def check_args(x, y):
433
+ try:
434
+ return x.args[0] == y.args[0]
435
+ except IndexError:
436
+ return False
437
+
438
+ def f(rv):
439
+ # for simple functions
440
+ g = getattr(rv, 'inverse', None)
441
+ if (g is not None and isinstance(rv.args[0], g()) and
442
+ isinstance(g()(1), TrigonometricFunction)):
443
+ return rv.args[0].args[0]
444
+
445
+ # for atan2 simplifications, harder because atan2 has 2 args
446
+ if isinstance(rv, atan2):
447
+ y, x = rv.args
448
+ if _coeff_isneg(y):
449
+ return -f(atan2(-y, x))
450
+ elif _coeff_isneg(x):
451
+ return S.Pi - f(atan2(y, -x))
452
+
453
+ if check_args(x, y):
454
+ if isinstance(y, sin) and isinstance(x, cos):
455
+ return x.args[0]
456
+ if isinstance(y, cos) and isinstance(x, sin):
457
+ return S.Pi / 2 - x.args[0]
458
+
459
+ return rv
460
+
461
+ return bottom_up(rv, f)
462
+
463
+
464
+ def trigsimp(expr, inverse=False, **opts):
465
+ """Returns a reduced expression by using known trig identities.
466
+
467
+ Parameters
468
+ ==========
469
+
470
+ inverse : bool, optional
471
+ If ``inverse=True``, it will be assumed that a composition of inverse
472
+ functions, such as sin and asin, can be cancelled in any order.
473
+ For example, ``asin(sin(x))`` will yield ``x`` without checking whether
474
+ x belongs to the set where this relation is true. The default is False.
475
+ Default : True
476
+
477
+ method : string, optional
478
+ Specifies the method to use. Valid choices are:
479
+
480
+ - ``'matching'``, default
481
+ - ``'groebner'``
482
+ - ``'combined'``
483
+ - ``'fu'``
484
+ - ``'old'``
485
+
486
+ If ``'matching'``, simplify the expression recursively by targeting
487
+ common patterns. If ``'groebner'``, apply an experimental groebner
488
+ basis algorithm. In this case further options are forwarded to
489
+ ``trigsimp_groebner``, please refer to
490
+ its docstring. If ``'combined'``, it first runs the groebner basis
491
+ algorithm with small default parameters, then runs the ``'matching'``
492
+ algorithm. If ``'fu'``, run the collection of trigonometric
493
+ transformations described by Fu, et al. (see the
494
+ :py:func:`~sympy.simplify.fu.fu` docstring). If ``'old'``, the original
495
+ SymPy trig simplification function is run.
496
+ opts :
497
+ Optional keyword arguments passed to the method. See each method's
498
+ function docstring for details.
499
+
500
+ Examples
501
+ ========
502
+
503
+ >>> from sympy import trigsimp, sin, cos, log
504
+ >>> from sympy.abc import x
505
+ >>> e = 2*sin(x)**2 + 2*cos(x)**2
506
+ >>> trigsimp(e)
507
+ 2
508
+
509
+ Simplification occurs wherever trigonometric functions are located.
510
+
511
+ >>> trigsimp(log(e))
512
+ log(2)
513
+
514
+ Using ``method='groebner'`` (or ``method='combined'``) might lead to
515
+ greater simplification.
516
+
517
+ The old trigsimp routine can be accessed as with method ``method='old'``.
518
+
519
+ >>> from sympy import coth, tanh
520
+ >>> t = 3*tanh(x)**7 - 2/coth(x)**7
521
+ >>> trigsimp(t, method='old') == t
522
+ True
523
+ >>> trigsimp(t)
524
+ tanh(x)**7
525
+
526
+ """
527
+ from sympy.simplify.fu import fu
528
+
529
+ expr = sympify(expr)
530
+
531
+ _eval_trigsimp = getattr(expr, '_eval_trigsimp', None)
532
+ if _eval_trigsimp is not None:
533
+ return _eval_trigsimp(**opts)
534
+
535
+ old = opts.pop('old', False)
536
+ if not old:
537
+ opts.pop('deep', None)
538
+ opts.pop('recursive', None)
539
+ method = opts.pop('method', 'matching')
540
+ else:
541
+ method = 'old'
542
+
543
+ def groebnersimp(ex, **opts):
544
+ def traverse(e):
545
+ if e.is_Atom:
546
+ return e
547
+ args = [traverse(x) for x in e.args]
548
+ if e.is_Function or e.is_Pow:
549
+ args = [trigsimp_groebner(x, **opts) for x in args]
550
+ return e.func(*args)
551
+ new = traverse(ex)
552
+ if not isinstance(new, Expr):
553
+ return new
554
+ return trigsimp_groebner(new, **opts)
555
+
556
+ trigsimpfunc = {
557
+ 'fu': (lambda x: fu(x, **opts)),
558
+ 'matching': (lambda x: futrig(x)),
559
+ 'groebner': (lambda x: groebnersimp(x, **opts)),
560
+ 'combined': (lambda x: futrig(groebnersimp(x,
561
+ polynomial=True, hints=[2, tan]))),
562
+ 'old': lambda x: trigsimp_old(x, **opts),
563
+ }[method]
564
+
565
+ expr_simplified = trigsimpfunc(expr)
566
+ if inverse:
567
+ expr_simplified = _trigsimp_inverse(expr_simplified)
568
+
569
+ return expr_simplified
570
+
571
+
572
+ def exptrigsimp(expr):
573
+ """
574
+ Simplifies exponential / trigonometric / hyperbolic functions.
575
+
576
+ Examples
577
+ ========
578
+
579
+ >>> from sympy import exptrigsimp, exp, cosh, sinh
580
+ >>> from sympy.abc import z
581
+
582
+ >>> exptrigsimp(exp(z) + exp(-z))
583
+ 2*cosh(z)
584
+ >>> exptrigsimp(cosh(z) - sinh(z))
585
+ exp(-z)
586
+ """
587
+ from sympy.simplify.fu import hyper_as_trig, TR2i
588
+
589
+ def exp_trig(e):
590
+ # select the better of e, and e rewritten in terms of exp or trig
591
+ # functions
592
+ choices = [e]
593
+ if e.has(*_trigs):
594
+ choices.append(e.rewrite(exp))
595
+ choices.append(e.rewrite(cos))
596
+ return min(*choices, key=count_ops)
597
+ newexpr = bottom_up(expr, exp_trig)
598
+
599
+ def f(rv):
600
+ if not rv.is_Mul:
601
+ return rv
602
+ commutative_part, noncommutative_part = rv.args_cnc()
603
+ # Since as_powers_dict loses order information,
604
+ # if there is more than one noncommutative factor,
605
+ # it should only be used to simplify the commutative part.
606
+ if (len(noncommutative_part) > 1):
607
+ return f(Mul(*commutative_part))*Mul(*noncommutative_part)
608
+ rvd = rv.as_powers_dict()
609
+ newd = rvd.copy()
610
+
611
+ def signlog(expr, sign=S.One):
612
+ if expr is S.Exp1:
613
+ return sign, S.One
614
+ elif isinstance(expr, exp) or (expr.is_Pow and expr.base == S.Exp1):
615
+ return sign, expr.exp
616
+ elif sign is S.One:
617
+ return signlog(-expr, sign=-S.One)
618
+ else:
619
+ return None, None
620
+
621
+ ee = rvd[S.Exp1]
622
+ for k in rvd:
623
+ if k.is_Add and len(k.args) == 2:
624
+ # k == c*(1 + sign*E**x)
625
+ c = k.args[0]
626
+ sign, x = signlog(k.args[1]/c)
627
+ if not x:
628
+ continue
629
+ m = rvd[k]
630
+ newd[k] -= m
631
+ if ee == -x*m/2:
632
+ # sinh and cosh
633
+ newd[S.Exp1] -= ee
634
+ ee = 0
635
+ if sign == 1:
636
+ newd[2*c*cosh(x/2)] += m
637
+ else:
638
+ newd[-2*c*sinh(x/2)] += m
639
+ elif newd[1 - sign*S.Exp1**x] == -m:
640
+ # tanh
641
+ del newd[1 - sign*S.Exp1**x]
642
+ if sign == 1:
643
+ newd[-c/tanh(x/2)] += m
644
+ else:
645
+ newd[-c*tanh(x/2)] += m
646
+ else:
647
+ newd[1 + sign*S.Exp1**x] += m
648
+ newd[c] += m
649
+
650
+ return Mul(*[k**newd[k] for k in newd])
651
+ newexpr = bottom_up(newexpr, f)
652
+
653
+ # sin/cos and sinh/cosh ratios to tan and tanh, respectively
654
+ if newexpr.has(HyperbolicFunction):
655
+ e, f = hyper_as_trig(newexpr)
656
+ newexpr = f(TR2i(e))
657
+ if newexpr.has(TrigonometricFunction):
658
+ newexpr = TR2i(newexpr)
659
+
660
+ # can we ever generate an I where there was none previously?
661
+ if not (newexpr.has(I) and not expr.has(I)):
662
+ expr = newexpr
663
+ return expr
664
+
665
+ #-------------------- the old trigsimp routines ---------------------
666
+
667
+ def trigsimp_old(expr, *, first=True, **opts):
668
+ """
669
+ Reduces expression by using known trig identities.
670
+
671
+ Notes
672
+ =====
673
+
674
+ deep:
675
+ - Apply trigsimp inside all objects with arguments
676
+
677
+ recursive:
678
+ - Use common subexpression elimination (cse()) and apply
679
+ trigsimp recursively (this is quite expensive if the
680
+ expression is large)
681
+
682
+ method:
683
+ - Determine the method to use. Valid choices are 'matching' (default),
684
+ 'groebner', 'combined', 'fu' and 'futrig'. If 'matching', simplify the
685
+ expression recursively by pattern matching. If 'groebner', apply an
686
+ experimental groebner basis algorithm. In this case further options
687
+ are forwarded to ``trigsimp_groebner``, please refer to its docstring.
688
+ If 'combined', first run the groebner basis algorithm with small
689
+ default parameters, then run the 'matching' algorithm. 'fu' runs the
690
+ collection of trigonometric transformations described by Fu, et al.
691
+ (see the `fu` docstring) while `futrig` runs a subset of Fu-transforms
692
+ that mimic the behavior of `trigsimp`.
693
+
694
+ compare:
695
+ - show input and output from `trigsimp` and `futrig` when different,
696
+ but returns the `trigsimp` value.
697
+
698
+ Examples
699
+ ========
700
+
701
+ >>> from sympy import trigsimp, sin, cos, log, cot
702
+ >>> from sympy.abc import x
703
+ >>> e = 2*sin(x)**2 + 2*cos(x)**2
704
+ >>> trigsimp(e, old=True)
705
+ 2
706
+ >>> trigsimp(log(e), old=True)
707
+ log(2*sin(x)**2 + 2*cos(x)**2)
708
+ >>> trigsimp(log(e), deep=True, old=True)
709
+ log(2)
710
+
711
+ Using `method="groebner"` (or `"combined"`) can sometimes lead to a lot
712
+ more simplification:
713
+
714
+ >>> e = (-sin(x) + 1)/cos(x) + cos(x)/(-sin(x) + 1)
715
+ >>> trigsimp(e, old=True)
716
+ (1 - sin(x))/cos(x) + cos(x)/(1 - sin(x))
717
+ >>> trigsimp(e, method="groebner", old=True)
718
+ 2/cos(x)
719
+
720
+ >>> trigsimp(1/cot(x)**2, compare=True, old=True)
721
+ futrig: tan(x)**2
722
+ cot(x)**(-2)
723
+
724
+ """
725
+ old = expr
726
+ if first:
727
+ if not expr.has(*_trigs):
728
+ return expr
729
+
730
+ trigsyms = set().union(*[t.free_symbols for t in expr.atoms(*_trigs)])
731
+ if len(trigsyms) > 1:
732
+ from sympy.simplify.simplify import separatevars
733
+
734
+ d = separatevars(expr)
735
+ if d.is_Mul:
736
+ d = separatevars(d, dict=True) or d
737
+ if isinstance(d, dict):
738
+ expr = 1
739
+ for v in d.values():
740
+ # remove hollow factoring
741
+ was = v
742
+ v = expand_mul(v)
743
+ opts['first'] = False
744
+ vnew = trigsimp(v, **opts)
745
+ if vnew == v:
746
+ vnew = was
747
+ expr *= vnew
748
+ old = expr
749
+ else:
750
+ if d.is_Add:
751
+ for s in trigsyms:
752
+ r, e = expr.as_independent(s)
753
+ if r:
754
+ opts['first'] = False
755
+ expr = r + trigsimp(e, **opts)
756
+ if not expr.is_Add:
757
+ break
758
+ old = expr
759
+
760
+ recursive = opts.pop('recursive', False)
761
+ deep = opts.pop('deep', False)
762
+ method = opts.pop('method', 'matching')
763
+
764
+ def groebnersimp(ex, deep, **opts):
765
+ def traverse(e):
766
+ if e.is_Atom:
767
+ return e
768
+ args = [traverse(x) for x in e.args]
769
+ if e.is_Function or e.is_Pow:
770
+ args = [trigsimp_groebner(x, **opts) for x in args]
771
+ return e.func(*args)
772
+ if deep:
773
+ ex = traverse(ex)
774
+ return trigsimp_groebner(ex, **opts)
775
+
776
+ trigsimpfunc = {
777
+ 'matching': (lambda x, d: _trigsimp(x, d)),
778
+ 'groebner': (lambda x, d: groebnersimp(x, d, **opts)),
779
+ 'combined': (lambda x, d: _trigsimp(groebnersimp(x,
780
+ d, polynomial=True, hints=[2, tan]),
781
+ d))
782
+ }[method]
783
+
784
+ if recursive:
785
+ w, g = cse(expr)
786
+ g = trigsimpfunc(g[0], deep)
787
+
788
+ for sub in reversed(w):
789
+ g = g.subs(sub[0], sub[1])
790
+ g = trigsimpfunc(g, deep)
791
+ result = g
792
+ else:
793
+ result = trigsimpfunc(expr, deep)
794
+
795
+ if opts.get('compare', False):
796
+ f = futrig(old)
797
+ if f != result:
798
+ print('\tfutrig:', f)
799
+
800
+ return result
801
+
802
+
803
+ def _dotrig(a, b):
804
+ """Helper to tell whether ``a`` and ``b`` have the same sorts
805
+ of symbols in them -- no need to test hyperbolic patterns against
806
+ expressions that have no hyperbolics in them."""
807
+ return a.func == b.func and (
808
+ a.has(TrigonometricFunction) and b.has(TrigonometricFunction) or
809
+ a.has(HyperbolicFunction) and b.has(HyperbolicFunction))
810
+
811
+
812
+ _trigpat = None
813
+ def _trigpats():
814
+ global _trigpat
815
+ a, b, c = symbols('a b c', cls=Wild)
816
+ d = Wild('d', commutative=False)
817
+
818
+ # for the simplifications like sinh/cosh -> tanh:
819
+ # DO NOT REORDER THE FIRST 14 since these are assumed to be in this
820
+ # order in _match_div_rewrite.
821
+ matchers_division = (
822
+ (a*sin(b)**c/cos(b)**c, a*tan(b)**c, sin(b), cos(b)),
823
+ (a*tan(b)**c*cos(b)**c, a*sin(b)**c, sin(b), cos(b)),
824
+ (a*cot(b)**c*sin(b)**c, a*cos(b)**c, sin(b), cos(b)),
825
+ (a*tan(b)**c/sin(b)**c, a/cos(b)**c, sin(b), cos(b)),
826
+ (a*cot(b)**c/cos(b)**c, a/sin(b)**c, sin(b), cos(b)),
827
+ (a*cot(b)**c*tan(b)**c, a, sin(b), cos(b)),
828
+ (a*(cos(b) + 1)**c*(cos(b) - 1)**c,
829
+ a*(-sin(b)**2)**c, cos(b) + 1, cos(b) - 1),
830
+ (a*(sin(b) + 1)**c*(sin(b) - 1)**c,
831
+ a*(-cos(b)**2)**c, sin(b) + 1, sin(b) - 1),
832
+
833
+ (a*sinh(b)**c/cosh(b)**c, a*tanh(b)**c, S.One, S.One),
834
+ (a*tanh(b)**c*cosh(b)**c, a*sinh(b)**c, S.One, S.One),
835
+ (a*coth(b)**c*sinh(b)**c, a*cosh(b)**c, S.One, S.One),
836
+ (a*tanh(b)**c/sinh(b)**c, a/cosh(b)**c, S.One, S.One),
837
+ (a*coth(b)**c/cosh(b)**c, a/sinh(b)**c, S.One, S.One),
838
+ (a*coth(b)**c*tanh(b)**c, a, S.One, S.One),
839
+
840
+ (c*(tanh(a) + tanh(b))/(1 + tanh(a)*tanh(b)),
841
+ tanh(a + b)*c, S.One, S.One),
842
+ )
843
+
844
+ matchers_add = (
845
+ (c*sin(a)*cos(b) + c*cos(a)*sin(b) + d, sin(a + b)*c + d),
846
+ (c*cos(a)*cos(b) - c*sin(a)*sin(b) + d, cos(a + b)*c + d),
847
+ (c*sin(a)*cos(b) - c*cos(a)*sin(b) + d, sin(a - b)*c + d),
848
+ (c*cos(a)*cos(b) + c*sin(a)*sin(b) + d, cos(a - b)*c + d),
849
+ (c*sinh(a)*cosh(b) + c*sinh(b)*cosh(a) + d, sinh(a + b)*c + d),
850
+ (c*cosh(a)*cosh(b) + c*sinh(a)*sinh(b) + d, cosh(a + b)*c + d),
851
+ )
852
+
853
+ # for cos(x)**2 + sin(x)**2 -> 1
854
+ matchers_identity = (
855
+ (a*sin(b)**2, a - a*cos(b)**2),
856
+ (a*tan(b)**2, a*(1/cos(b))**2 - a),
857
+ (a*cot(b)**2, a*(1/sin(b))**2 - a),
858
+ (a*sin(b + c), a*(sin(b)*cos(c) + sin(c)*cos(b))),
859
+ (a*cos(b + c), a*(cos(b)*cos(c) - sin(b)*sin(c))),
860
+ (a*tan(b + c), a*((tan(b) + tan(c))/(1 - tan(b)*tan(c)))),
861
+
862
+ (a*sinh(b)**2, a*cosh(b)**2 - a),
863
+ (a*tanh(b)**2, a - a*(1/cosh(b))**2),
864
+ (a*coth(b)**2, a + a*(1/sinh(b))**2),
865
+ (a*sinh(b + c), a*(sinh(b)*cosh(c) + sinh(c)*cosh(b))),
866
+ (a*cosh(b + c), a*(cosh(b)*cosh(c) + sinh(b)*sinh(c))),
867
+ (a*tanh(b + c), a*((tanh(b) + tanh(c))/(1 + tanh(b)*tanh(c)))),
868
+
869
+ )
870
+
871
+ # Reduce any lingering artifacts, such as sin(x)**2 changing
872
+ # to 1-cos(x)**2 when sin(x)**2 was "simpler"
873
+ artifacts = (
874
+ (a - a*cos(b)**2 + c, a*sin(b)**2 + c, cos),
875
+ (a - a*(1/cos(b))**2 + c, -a*tan(b)**2 + c, cos),
876
+ (a - a*(1/sin(b))**2 + c, -a*cot(b)**2 + c, sin),
877
+
878
+ (a - a*cosh(b)**2 + c, -a*sinh(b)**2 + c, cosh),
879
+ (a - a*(1/cosh(b))**2 + c, a*tanh(b)**2 + c, cosh),
880
+ (a + a*(1/sinh(b))**2 + c, a*coth(b)**2 + c, sinh),
881
+
882
+ # same as above but with noncommutative prefactor
883
+ (a*d - a*d*cos(b)**2 + c, a*d*sin(b)**2 + c, cos),
884
+ (a*d - a*d*(1/cos(b))**2 + c, -a*d*tan(b)**2 + c, cos),
885
+ (a*d - a*d*(1/sin(b))**2 + c, -a*d*cot(b)**2 + c, sin),
886
+
887
+ (a*d - a*d*cosh(b)**2 + c, -a*d*sinh(b)**2 + c, cosh),
888
+ (a*d - a*d*(1/cosh(b))**2 + c, a*d*tanh(b)**2 + c, cosh),
889
+ (a*d + a*d*(1/sinh(b))**2 + c, a*d*coth(b)**2 + c, sinh),
890
+ )
891
+
892
+ _trigpat = (a, b, c, d, matchers_division, matchers_add,
893
+ matchers_identity, artifacts)
894
+ return _trigpat
895
+
896
+
897
+ def _replace_mul_fpowxgpow(expr, f, g, rexp, h, rexph):
898
+ """Helper for _match_div_rewrite.
899
+
900
+ Replace f(b_)**c_*g(b_)**(rexp(c_)) with h(b)**rexph(c) if f(b_)
901
+ and g(b_) are both positive or if c_ is an integer.
902
+ """
903
+ # assert expr.is_Mul and expr.is_commutative and f != g
904
+ fargs = defaultdict(int)
905
+ gargs = defaultdict(int)
906
+ args = []
907
+ for x in expr.args:
908
+ if x.is_Pow or x.func in (f, g):
909
+ b, e = x.as_base_exp()
910
+ if b.is_positive or e.is_integer:
911
+ if b.func == f:
912
+ fargs[b.args[0]] += e
913
+ continue
914
+ elif b.func == g:
915
+ gargs[b.args[0]] += e
916
+ continue
917
+ args.append(x)
918
+ common = set(fargs) & set(gargs)
919
+ hit = False
920
+ while common:
921
+ key = common.pop()
922
+ fe = fargs.pop(key)
923
+ ge = gargs.pop(key)
924
+ if fe == rexp(ge):
925
+ args.append(h(key)**rexph(fe))
926
+ hit = True
927
+ else:
928
+ fargs[key] = fe
929
+ gargs[key] = ge
930
+ if not hit:
931
+ return expr
932
+ while fargs:
933
+ key, e = fargs.popitem()
934
+ args.append(f(key)**e)
935
+ while gargs:
936
+ key, e = gargs.popitem()
937
+ args.append(g(key)**e)
938
+ return Mul(*args)
939
+
940
+
941
+ _idn = lambda x: x
942
+ _midn = lambda x: -x
943
+ _one = lambda x: S.One
944
+
945
+ def _match_div_rewrite(expr, i):
946
+ """helper for __trigsimp"""
947
+ if i == 0:
948
+ expr = _replace_mul_fpowxgpow(expr, sin, cos,
949
+ _midn, tan, _idn)
950
+ elif i == 1:
951
+ expr = _replace_mul_fpowxgpow(expr, tan, cos,
952
+ _idn, sin, _idn)
953
+ elif i == 2:
954
+ expr = _replace_mul_fpowxgpow(expr, cot, sin,
955
+ _idn, cos, _idn)
956
+ elif i == 3:
957
+ expr = _replace_mul_fpowxgpow(expr, tan, sin,
958
+ _midn, cos, _midn)
959
+ elif i == 4:
960
+ expr = _replace_mul_fpowxgpow(expr, cot, cos,
961
+ _midn, sin, _midn)
962
+ elif i == 5:
963
+ expr = _replace_mul_fpowxgpow(expr, cot, tan,
964
+ _idn, _one, _idn)
965
+ # i in (6, 7) is skipped
966
+ elif i == 8:
967
+ expr = _replace_mul_fpowxgpow(expr, sinh, cosh,
968
+ _midn, tanh, _idn)
969
+ elif i == 9:
970
+ expr = _replace_mul_fpowxgpow(expr, tanh, cosh,
971
+ _idn, sinh, _idn)
972
+ elif i == 10:
973
+ expr = _replace_mul_fpowxgpow(expr, coth, sinh,
974
+ _idn, cosh, _idn)
975
+ elif i == 11:
976
+ expr = _replace_mul_fpowxgpow(expr, tanh, sinh,
977
+ _midn, cosh, _midn)
978
+ elif i == 12:
979
+ expr = _replace_mul_fpowxgpow(expr, coth, cosh,
980
+ _midn, sinh, _midn)
981
+ elif i == 13:
982
+ expr = _replace_mul_fpowxgpow(expr, coth, tanh,
983
+ _idn, _one, _idn)
984
+ else:
985
+ return None
986
+ return expr
987
+
988
+
989
+ def _trigsimp(expr, deep=False):
990
+ # protect the cache from non-trig patterns; we only allow
991
+ # trig patterns to enter the cache
992
+ if expr.has(*_trigs):
993
+ return __trigsimp(expr, deep)
994
+ return expr
995
+
996
+
997
+ @cacheit
998
+ def __trigsimp(expr, deep=False):
999
+ """recursive helper for trigsimp"""
1000
+ from sympy.simplify.fu import TR10i
1001
+
1002
+ if _trigpat is None:
1003
+ _trigpats()
1004
+ a, b, c, d, matchers_division, matchers_add, \
1005
+ matchers_identity, artifacts = _trigpat
1006
+
1007
+ if expr.is_Mul:
1008
+ # do some simplifications like sin/cos -> tan:
1009
+ if not expr.is_commutative:
1010
+ com, nc = expr.args_cnc()
1011
+ expr = _trigsimp(Mul._from_args(com), deep)*Mul._from_args(nc)
1012
+ else:
1013
+ for i, (pattern, simp, ok1, ok2) in enumerate(matchers_division):
1014
+ if not _dotrig(expr, pattern):
1015
+ continue
1016
+
1017
+ newexpr = _match_div_rewrite(expr, i)
1018
+ if newexpr is not None:
1019
+ if newexpr != expr:
1020
+ expr = newexpr
1021
+ break
1022
+ else:
1023
+ continue
1024
+
1025
+ # use SymPy matching instead
1026
+ res = expr.match(pattern)
1027
+ if res and res.get(c, 0):
1028
+ if not res[c].is_integer:
1029
+ ok = ok1.subs(res)
1030
+ if not ok.is_positive:
1031
+ continue
1032
+ ok = ok2.subs(res)
1033
+ if not ok.is_positive:
1034
+ continue
1035
+ # if "a" contains any of trig or hyperbolic funcs with
1036
+ # argument "b" then skip the simplification
1037
+ if any(w.args[0] == res[b] for w in res[a].atoms(
1038
+ TrigonometricFunction, HyperbolicFunction)):
1039
+ continue
1040
+ # simplify and finish:
1041
+ expr = simp.subs(res)
1042
+ break # process below
1043
+
1044
+ if expr.is_Add:
1045
+ args = []
1046
+ for term in expr.args:
1047
+ if not term.is_commutative:
1048
+ com, nc = term.args_cnc()
1049
+ nc = Mul._from_args(nc)
1050
+ term = Mul._from_args(com)
1051
+ else:
1052
+ nc = S.One
1053
+ term = _trigsimp(term, deep)
1054
+ for pattern, result in matchers_identity:
1055
+ res = term.match(pattern)
1056
+ if res is not None:
1057
+ term = result.subs(res)
1058
+ break
1059
+ args.append(term*nc)
1060
+ if args != expr.args:
1061
+ expr = Add(*args)
1062
+ expr = min(expr, expand(expr), key=count_ops)
1063
+ if expr.is_Add:
1064
+ for pattern, result in matchers_add:
1065
+ if not _dotrig(expr, pattern):
1066
+ continue
1067
+ expr = TR10i(expr)
1068
+ if expr.has(HyperbolicFunction):
1069
+ res = expr.match(pattern)
1070
+ # if "d" contains any trig or hyperbolic funcs with
1071
+ # argument "a" or "b" then skip the simplification;
1072
+ # this isn't perfect -- see tests
1073
+ if res is None or not (a in res and b in res) or any(
1074
+ w.args[0] in (res[a], res[b]) for w in res[d].atoms(
1075
+ TrigonometricFunction, HyperbolicFunction)):
1076
+ continue
1077
+ expr = result.subs(res)
1078
+ break
1079
+
1080
+ # Reduce any lingering artifacts, such as sin(x)**2 changing
1081
+ # to 1 - cos(x)**2 when sin(x)**2 was "simpler"
1082
+ for pattern, result, ex in artifacts:
1083
+ if not _dotrig(expr, pattern):
1084
+ continue
1085
+ # Substitute a new wild that excludes some function(s)
1086
+ # to help influence a better match. This is because
1087
+ # sometimes, for example, 'a' would match sec(x)**2
1088
+ a_t = Wild('a', exclude=[ex])
1089
+ pattern = pattern.subs(a, a_t)
1090
+ result = result.subs(a, a_t)
1091
+
1092
+ m = expr.match(pattern)
1093
+ was = None
1094
+ while m and was != expr:
1095
+ was = expr
1096
+ if m[a_t] == 0 or \
1097
+ -m[a_t] in m[c].args or m[a_t] + m[c] == 0:
1098
+ break
1099
+ if d in m and m[a_t]*m[d] + m[c] == 0:
1100
+ break
1101
+ expr = result.subs(m)
1102
+ m = expr.match(pattern)
1103
+ m.setdefault(c, S.Zero)
1104
+
1105
+ elif expr.is_Mul or expr.is_Pow or deep and expr.args:
1106
+ expr = expr.func(*[_trigsimp(a, deep) for a in expr.args])
1107
+
1108
+ try:
1109
+ if not expr.has(*_trigs):
1110
+ raise TypeError
1111
+ e = expr.atoms(exp)
1112
+ new = expr.rewrite(exp, deep=deep)
1113
+ if new == e:
1114
+ raise TypeError
1115
+ fnew = factor(new)
1116
+ if fnew != new:
1117
+ new = min([new, factor(new)], key=count_ops)
1118
+ # if all exp that were introduced disappeared then accept it
1119
+ if not (new.atoms(exp) - e):
1120
+ expr = new
1121
+ except TypeError:
1122
+ pass
1123
+
1124
+ return expr
1125
+ #------------------- end of old trigsimp routines --------------------
1126
+
1127
+
1128
+ def futrig(e, *, hyper=True, **kwargs):
1129
+ """Return simplified ``e`` using Fu-like transformations.
1130
+ This is not the "Fu" algorithm. This is called by default
1131
+ from ``trigsimp``. By default, hyperbolics subexpressions
1132
+ will be simplified, but this can be disabled by setting
1133
+ ``hyper=False``.
1134
+
1135
+ Examples
1136
+ ========
1137
+
1138
+ >>> from sympy import trigsimp, tan, sinh, tanh
1139
+ >>> from sympy.simplify.trigsimp import futrig
1140
+ >>> from sympy.abc import x
1141
+ >>> trigsimp(1/tan(x)**2)
1142
+ tan(x)**(-2)
1143
+
1144
+ >>> futrig(sinh(x)/tanh(x))
1145
+ cosh(x)
1146
+
1147
+ """
1148
+ from sympy.simplify.fu import hyper_as_trig
1149
+
1150
+ e = sympify(e)
1151
+
1152
+ if not isinstance(e, Basic):
1153
+ return e
1154
+
1155
+ if not e.args:
1156
+ return e
1157
+
1158
+ old = e
1159
+ e = bottom_up(e, _futrig)
1160
+
1161
+ if hyper and e.has(HyperbolicFunction):
1162
+ e, f = hyper_as_trig(e)
1163
+ e = f(bottom_up(e, _futrig))
1164
+
1165
+ if e != old and e.is_Mul and e.args[0].is_Rational:
1166
+ # redistribute leading coeff on 2-arg Add
1167
+ e = Mul(*e.as_coeff_Mul())
1168
+ return e
1169
+
1170
+
1171
+ def _futrig(e):
1172
+ """Helper for futrig."""
1173
+ from sympy.simplify.fu import (
1174
+ TR1, TR2, TR3, TR2i, TR10, L, TR10i,
1175
+ TR8, TR6, TR15, TR16, TR111, TR5, TRmorrie, TR11, _TR11, TR14, TR22,
1176
+ TR12)
1177
+
1178
+ if not e.has(TrigonometricFunction):
1179
+ return e
1180
+
1181
+ if e.is_Mul:
1182
+ coeff, e = e.as_independent(TrigonometricFunction)
1183
+ else:
1184
+ coeff = None
1185
+
1186
+ Lops = lambda x: (L(x), x.count_ops(), _nodes(x), len(x.args), x.is_Add)
1187
+ trigs = lambda x: x.has(TrigonometricFunction)
1188
+
1189
+ tree = [identity,
1190
+ (
1191
+ TR3, # canonical angles
1192
+ TR1, # sec-csc -> cos-sin
1193
+ TR12, # expand tan of sum
1194
+ lambda x: _eapply(factor, x, trigs),
1195
+ TR2, # tan-cot -> sin-cos
1196
+ [identity, lambda x: _eapply(_mexpand, x, trigs)],
1197
+ TR2i, # sin-cos ratio -> tan
1198
+ lambda x: _eapply(lambda i: factor(i.normal()), x, trigs),
1199
+ TR14, # factored identities
1200
+ TR5, # sin-pow -> cos_pow
1201
+ TR10, # sin-cos of sums -> sin-cos prod
1202
+ TR11, _TR11, TR6, # reduce double angles and rewrite cos pows
1203
+ lambda x: _eapply(factor, x, trigs),
1204
+ TR14, # factored powers of identities
1205
+ [identity, lambda x: _eapply(_mexpand, x, trigs)],
1206
+ TR10i, # sin-cos products > sin-cos of sums
1207
+ TRmorrie,
1208
+ [identity, TR8], # sin-cos products -> sin-cos of sums
1209
+ [identity, lambda x: TR2i(TR2(x))], # tan -> sin-cos -> tan
1210
+ [
1211
+ lambda x: _eapply(expand_mul, TR5(x), trigs),
1212
+ lambda x: _eapply(
1213
+ expand_mul, TR15(x), trigs)], # pos/neg powers of sin
1214
+ [
1215
+ lambda x: _eapply(expand_mul, TR6(x), trigs),
1216
+ lambda x: _eapply(
1217
+ expand_mul, TR16(x), trigs)], # pos/neg powers of cos
1218
+ TR111, # tan, sin, cos to neg power -> cot, csc, sec
1219
+ [identity, TR2i], # sin-cos ratio to tan
1220
+ [identity, lambda x: _eapply(
1221
+ expand_mul, TR22(x), trigs)], # tan-cot to sec-csc
1222
+ TR1, TR2, TR2i,
1223
+ [identity, lambda x: _eapply(
1224
+ factor_terms, TR12(x), trigs)], # expand tan of sum
1225
+ )]
1226
+ e = greedy(tree, objective=Lops)(e)
1227
+
1228
+ if coeff is not None:
1229
+ e = coeff * e
1230
+
1231
+ return e
1232
+
1233
+
1234
+ def _is_Expr(e):
1235
+ """_eapply helper to tell whether ``e`` and all its args
1236
+ are Exprs."""
1237
+ if isinstance(e, Derivative):
1238
+ return _is_Expr(e.expr)
1239
+ if not isinstance(e, Expr):
1240
+ return False
1241
+ return all(_is_Expr(i) for i in e.args)
1242
+
1243
+
1244
+ def _eapply(func, e, cond=None):
1245
+ """Apply ``func`` to ``e`` if all args are Exprs else only
1246
+ apply it to those args that *are* Exprs."""
1247
+ if not isinstance(e, Expr):
1248
+ return e
1249
+ if _is_Expr(e) or not e.args:
1250
+ return func(e)
1251
+ return e.func(*[
1252
+ _eapply(func, ei) if (cond is None or cond(ei)) else ei
1253
+ for ei in e.args])