Coverage for /usr/lib/python3/dist-packages/sympy/solvers/decompogen.py: 15%
54 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from sympy.core import (Function, Pow, sympify, Expr)
2from sympy.core.relational import Relational
3from sympy.core.singleton import S
4from sympy.polys import Poly, decompose
5from sympy.utilities.misc import func_name
6from sympy.functions.elementary.miscellaneous import Min, Max
9def decompogen(f, symbol):
10 """
11 Computes General functional decomposition of ``f``.
12 Given an expression ``f``, returns a list ``[f_1, f_2, ..., f_n]``,
13 where::
14 f = f_1 o f_2 o ... f_n = f_1(f_2(... f_n))
16 Note: This is a General decomposition function. It also decomposes
17 Polynomials. For only Polynomial decomposition see ``decompose`` in polys.
19 Examples
20 ========
22 >>> from sympy.abc import x
23 >>> from sympy import decompogen, sqrt, sin, cos
24 >>> decompogen(sin(cos(x)), x)
25 [sin(x), cos(x)]
26 >>> decompogen(sin(x)**2 + sin(x) + 1, x)
27 [x**2 + x + 1, sin(x)]
28 >>> decompogen(sqrt(6*x**2 - 5), x)
29 [sqrt(x), 6*x**2 - 5]
30 >>> decompogen(sin(sqrt(cos(x**2 + 1))), x)
31 [sin(x), sqrt(x), cos(x), x**2 + 1]
32 >>> decompogen(x**4 + 2*x**3 - x - 1, x)
33 [x**2 - x - 1, x**2 + x]
35 """
36 f = sympify(f)
37 if not isinstance(f, Expr) or isinstance(f, Relational):
38 raise TypeError('expecting Expr but got: `%s`' % func_name(f))
39 if symbol not in f.free_symbols:
40 return [f]
43 # ===== Simple Functions ===== #
44 if isinstance(f, (Function, Pow)):
45 if f.is_Pow and f.base == S.Exp1:
46 arg = f.exp
47 else:
48 arg = f.args[0]
49 if arg == symbol:
50 return [f]
51 return [f.subs(arg, symbol)] + decompogen(arg, symbol)
53 # ===== Min/Max Functions ===== #
54 if isinstance(f, (Min, Max)):
55 args = list(f.args)
56 d0 = None
57 for i, a in enumerate(args):
58 if not a.has_free(symbol):
59 continue
60 d = decompogen(a, symbol)
61 if len(d) == 1:
62 d = [symbol] + d
63 if d0 is None:
64 d0 = d[1:]
65 elif d[1:] != d0:
66 # decomposition is not the same for each arg:
67 # mark as having no decomposition
68 d = [symbol]
69 break
70 args[i] = d[0]
71 if d[0] == symbol:
72 return [f]
73 return [f.func(*args)] + d0
75 # ===== Convert to Polynomial ===== #
76 fp = Poly(f)
77 gens = list(filter(lambda x: symbol in x.free_symbols, fp.gens))
79 if len(gens) == 1 and gens[0] != symbol:
80 f1 = f.subs(gens[0], symbol)
81 f2 = gens[0]
82 return [f1] + decompogen(f2, symbol)
84 # ===== Polynomial decompose() ====== #
85 try:
86 return decompose(f)
87 except ValueError:
88 return [f]
91def compogen(g_s, symbol):
92 """
93 Returns the composition of functions.
94 Given a list of functions ``g_s``, returns their composition ``f``,
95 where:
96 f = g_1 o g_2 o .. o g_n
98 Note: This is a General composition function. It also composes Polynomials.
99 For only Polynomial composition see ``compose`` in polys.
101 Examples
102 ========
104 >>> from sympy.solvers.decompogen import compogen
105 >>> from sympy.abc import x
106 >>> from sympy import sqrt, sin, cos
107 >>> compogen([sin(x), cos(x)], x)
108 sin(cos(x))
109 >>> compogen([x**2 + x + 1, sin(x)], x)
110 sin(x)**2 + sin(x) + 1
111 >>> compogen([sqrt(x), 6*x**2 - 5], x)
112 sqrt(6*x**2 - 5)
113 >>> compogen([sin(x), sqrt(x), cos(x), x**2 + 1], x)
114 sin(sqrt(cos(x**2 + 1)))
115 >>> compogen([x**2 - x - 1, x**2 + x], x)
116 -x**2 - x + (x**2 + x)**2 - 1
117 """
118 if len(g_s) == 1:
119 return g_s[0]
121 foo = g_s[0].subs(symbol, g_s[1])
123 if len(g_s) == 2:
124 return foo
126 return compogen([foo] + g_s[2:], symbol)