1+ from lpython import S , str
2+ from sympy import Symbol , Pow , sin , oo , pi , E , Mul , Add , oo , log , exp , cos
3+
4+ def mrv (e : S , x : S ) -> list [S ]:
5+ """
6+ Calculate the MRV set of the expression.
7+
8+ Examples
9+ ========
10+
11+ >>> mrv(log(x - log(x))/log(x), x)
12+ {x}
13+
14+ """
15+
16+ if e .is_integer :
17+ empty_list : list [S ] = []
18+ return empty_list
19+ if e == x :
20+ list1 : list [S ] = [x ]
21+ return list1
22+ if e .func == log :
23+ arg0 : S = e .args [0 ]
24+ list2 : list [S ] = mrv (arg0 , x )
25+ return list2
26+ if e .func == Mul or e .func == Add :
27+ a : S = e .args [0 ]
28+ b : S = e .args [1 ]
29+ ans1 : list [S ] = mrv (a , x )
30+ ans2 : list [S ] = mrv (b , x )
31+ list3 : list [S ] = mrv_max (ans1 , ans2 , x )
32+ return list3
33+ if e .func == Pow :
34+ base : S = e .args [0 ]
35+ list4 : list [S ] = mrv (base , x )
36+ return list4
37+ if e .func == sin :
38+ list5 : list [S ] = [x ]
39+ return list5
40+ # elif e.is_Function:
41+ # return reduce(lambda a, b: mrv_max(a, b, x), (mrv(a, x) for a in e.args))
42+ raise NotImplementedError (f"Can't calculate the MRV of { e } ." )
43+
44+ def mrv_max (f : list [S ], g : list [S ], x : S ) -> list [S ]:
45+ """Compute the maximum of two MRV sets.
46+
47+ Examples
48+ ========
49+
50+ >>> mrv_max({log(x)}, {x**5}, x)
51+ {x**5}
52+
53+ """
54+
55+ if len (f ) == 0 :
56+ return g
57+ elif len (g ) == 0 :
58+ return f
59+ # elif f & g:
60+ # return f | g
61+ else :
62+ f1 : S = f [0 ]
63+ g1 : S = g [0 ]
64+ bool1 : bool = f1 == x
65+ bool2 : bool = g1 == x
66+ if bool1 and bool2 :
67+ l : list [S ] = [x ]
68+ return l
69+
70+ def rewrite (e : S , x : S , w : S ) -> S :
71+ """
72+ Rewrites the expression in terms of the MRV subexpression.
73+
74+ Parameters
75+ ==========
76+
77+ e : Expr
78+ an expression
79+ x : Symbol
80+ variable of the `e`
81+ w : Symbol
82+ The symbol which is going to be used for substitution in place
83+ of the MRV in `x` subexpression.
84+
85+ Returns
86+ =======
87+
88+ The rewritten expression
89+
90+ Examples
91+ ========
92+
93+ >>> rewrite(exp(x)*log(x), x, y)
94+ (log(x)/y, -x)
95+
96+ """
97+ Omega : list [S ] = mrv (e , x )
98+ Omega1 : S = Omega [0 ]
99+
100+ if Omega1 == x :
101+ newe : S = e .subs (x , S (1 )/ w )
102+ return newe
103+
104+ def sign (e : S ) -> S :
105+ """
106+ Returns the complex sign of an expression:
107+
108+ Explanation
109+ ===========
110+
111+ If the expression is real the sign will be:
112+
113+ * $1$ if expression is positive
114+ * $0$ if expression is equal to zero
115+ * $-1$ if expression is negative
116+ """
117+
118+ if e .is_positive :
119+ return S (1 )
120+ elif e == S (0 ):
121+ return S (0 )
122+ else :
123+ return S (- 1 )
124+
125+ def signinf (e : S , x : S ) -> S :
126+ """
127+ Determine sign of the expression at the infinity.
128+
129+ Returns
130+ =======
131+
132+ {1, 0, -1}
133+ One or minus one, if `e > 0` or `e < 0` for `x` sufficiently
134+ large and zero if `e` is *constantly* zero for `x\t o\infty`.
135+
136+ """
137+
138+ if not e .has (x ):
139+ return sign (e )
140+ if e == x :
141+ return S (1 )
142+ if e .func == Pow :
143+ base : S = e .args [0 ]
144+ if signinf (base , x ) == S (1 ):
145+ return S (1 )
146+
147+ def leadterm (e : S , x : S ) -> list [S ]:
148+ """
149+ Returns the leading term a*x**b as a list [a, b].
150+ """
151+ if e == sin (x )/ x :
152+ l1 : list [S ] = [S (1 ), S (0 )]
153+ return l1
154+ elif e == S (2 )* sin (x )/ x :
155+ l2 : list [S ] = [S (2 ), S (0 )]
156+ return l2
157+ elif e == sin (S (2 )* x )/ x :
158+ l3 : list [S ] = [S (2 ), S (0 )]
159+ return l3
160+ elif e == sin (x )** S (2 )/ x :
161+ l4 : list [S ] = [S (1 ), S (1 )]
162+ return l4
163+ elif e == sin (x )/ x ** S (2 ):
164+ l5 : list [S ] = [S (1 ), S (- 1 )]
165+ return l5
166+ elif e == sin (x )** S (2 )/ x ** S (2 ):
167+ l6 : list [S ] = [S (1 ), S (0 )]
168+ return l6
169+ elif e == sin (sin (sin (x )))/ sin (x ):
170+ l7 : list [S ] = [S (1 ), S (0 )]
171+ return l7
172+ elif e == S (2 )* log (x + S (1 ))/ x :
173+ l8 : list [S ] = [S (2 ), S (0 )]
174+ return l8
175+ elif e == sin ((log (x + S (1 ))/ x )* x )/ x :
176+ l9 : list [S ] = [S (1 ), S (0 )]
177+ return l9
178+ raise NotImplementedError (f"Can't calculate the leadterm of { e } ." )
179+
180+ def mrv_leadterm (e : S , x : S ) -> list [S ]:
181+ """
182+ Compute the leading term of the series.
183+
184+ Returns
185+ =======
186+
187+ tuple
188+ The leading term `c_0 w^{e_0}` of the series of `e` in terms
189+ of the most rapidly varying subexpression `w` in form of
190+ the pair ``(c0, e0)`` of Expr.
191+
192+ Examples
193+ ========
194+
195+ >>> leadterm(1/exp(-x + exp(-x)) - exp(x), x)
196+ (-1, 0)
197+
198+ """
199+
200+ # w = Dummy('w', real=True, positive=True)
201+ # e = rewrite(e, x, w)
202+ # return e.leadterm(w)
203+ w : S = Symbol ('w' )
204+ newe : S = rewrite (e , x , w )
205+ coeff_exp_list : list [S ] = leadterm (newe , w )
206+
207+ return coeff_exp_list
208+
209+ def limitinf (e : S , x : S ) -> S :
210+ """
211+ Compute the limit of the expression at the infinity.
212+
213+ Examples
214+ ========
215+
216+ >>> limitinf(exp(x)*(exp(1/x - exp(-x)) - exp(1/x)), x)
217+ -1
218+
219+ """
220+
221+ if not e .has (x ):
222+ return e
223+
224+ coeff_exp_list : list [S ] = mrv_leadterm (e , x )
225+ c0 : S = coeff_exp_list [0 ]
226+ e0 : S = coeff_exp_list [1 ]
227+ sig : S = signinf (e0 , x )
228+ if sig == S (1 ):
229+ return S (0 )
230+ if sig == S (- 1 ):
231+ return signinf (c0 , x ) * oo
232+ if sig == S (0 ):
233+ return limitinf (c0 , x )
234+ raise NotImplementedError (f'Result depends on the sign of { sig } .' )
235+
236+ def gruntz (e : S , z : S , z0 : S , dir : str = "+" ) -> S :
237+ """
238+ Compute the limit of e(z) at the point z0 using the Gruntz algorithm.
239+
240+ Explanation
241+ ===========
242+
243+ ``z0`` can be any expression, including oo and -oo.
244+
245+ For ``dir="+"`` (default) it calculates the limit from the right
246+ (z->z0+) and for ``dir="-"`` the limit from the left (z->z0-). For infinite z0
247+ (oo or -oo), the dir argument does not matter.
248+
249+ This algorithm is fully described in the module docstring in the gruntz.py
250+ file. It relies heavily on the series expansion. Most frequently, gruntz()
251+ is only used if the faster limit() function (which uses heuristics) fails.
252+ """
253+
254+ e0 : S
255+ if str (dir ) == "-" :
256+ e0 = e .subs (z , z0 - S (1 )/ z )
257+ elif str (dir ) == "+" :
258+ e0 = e .subs (z , z0 + S (1 )/ z )
259+ else :
260+ raise NotImplementedError ("dir must be '+' or '-'" )
261+
262+ r : S = limitinf (e0 , z )
263+ return r
264+
265+ # test
266+ def test ():
267+ x : S = Symbol ('x' )
268+ print (gruntz (sin (x )/ x , x , S (0 ), "+" ))
269+ print (gruntz (S (2 )* sin (x )/ x , x , S (0 ), "+" ))
270+ print (gruntz (sin (S (2 )* x )/ x , x , S (0 ), "+" ))
271+ print (gruntz (sin (x )** S (2 )/ x , x , S (0 ), "+" ))
272+ print (gruntz (sin (x )/ x ** S (2 ), x , S (0 ), "+" ))
273+ print (gruntz (sin (x )** S (2 )/ x ** S (2 ), x , S (0 ), "+" ))
274+ print (gruntz (sin (sin (sin (x )))/ sin (x ), x , S (0 ), "+" ))
275+ print (gruntz (S (2 )* log (x + S (1 ))/ x , x , S (0 ), "+" ))
276+ print (gruntz (sin ((log (x + S (1 ))/ x )* x )/ x , x , S (0 ), "+" ))
277+
278+ assert gruntz (sin (x )/ x , x , S (0 )) == S (1 )
279+ assert gruntz (S (2 )* sin (x )/ x , x , S (0 )) == S (2 )
280+ assert gruntz (sin (S (2 )* x )/ x , x , S (0 )) == S (2 )
281+ assert gruntz (sin (x )** S (2 )/ x , x , S (0 )) == S (0 )
282+ assert gruntz (sin (x )/ x ** S (2 ), x , S (0 )) == oo
283+ assert gruntz (sin (x )** S (2 )/ x ** S (2 ), x , S (0 )) == S (1 )
284+ assert gruntz (sin (sin (sin (x )))/ sin (x ), x , S (0 )) == S (1 )
285+ assert gruntz (S (2 )* log (x + S (1 ))/ x , x , S (0 )) == S (2 )
286+ assert gruntz (sin ((log (x + S (1 ))/ x )* x )/ x , x , S (0 )) == S (1 )
287+
288+ test ()
0 commit comments