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

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 

7 

8 

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)) 

15 

16 Note: This is a General decomposition function. It also decomposes 

17 Polynomials. For only Polynomial decomposition see ``decompose`` in polys. 

18 

19 Examples 

20 ======== 

21 

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] 

34 

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] 

41 

42 

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) 

52 

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 

74 

75 # ===== Convert to Polynomial ===== # 

76 fp = Poly(f) 

77 gens = list(filter(lambda x: symbol in x.free_symbols, fp.gens)) 

78 

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) 

83 

84 # ===== Polynomial decompose() ====== # 

85 try: 

86 return decompose(f) 

87 except ValueError: 

88 return [f] 

89 

90 

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 

97 

98 Note: This is a General composition function. It also composes Polynomials. 

99 For only Polynomial composition see ``compose`` in polys. 

100 

101 Examples 

102 ======== 

103 

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] 

120 

121 foo = g_s[0].subs(symbol, g_s[1]) 

122 

123 if len(g_s) == 2: 

124 return foo 

125 

126 return compogen([foo] + g_s[2:], symbol)