2121 SymbolTrue ,
2222)
2323from mathics .core .convert import from_sympy , sympy_symbol_prefix
24+ from mathics .core .rules import Pattern
2425from mathics .builtin .scoping import dynamic_scoping
2526from mathics .builtin .inference import evaluate_predicate
2627
@@ -68,47 +69,102 @@ def _expand(expr):
6869 if kwargs ["modulus" ] is not None and kwargs ["modulus" ] <= 0 :
6970 return Integer0
7071
72+ target_pat = kwargs .get ("pattern" , None )
73+ if target_pat :
74+ evaluation = kwargs ["evaluation" ]
7175 # A special case for trigonometric functions
7276 if "trig" in kwargs and kwargs ["trig" ]:
73- if expr .has_form ("Sin" , 1 ):
77+ if expr .has_form (
78+ ("Sin" , "Cos" , "Tan" , "Cot" , "Sinh" , "Cosh" , "Tanh" , "Coth" ), 1
79+ ):
80+ head = expr .get_head ()
7481 theta = expr .leaves [0 ]
82+ if (target_pat is not None ) and theta .is_free (target_pat , evaluation ):
83+ return expr
84+ if deep :
85+ theta = _expand (theta )
7586
7687 if theta .has_form ("Plus" , 2 , None ):
7788 x , y = theta .leaves [0 ], Expression ("Plus" , * theta .leaves [1 :])
89+ if head == Symbol ("Sin" ):
90+ a = Expression (
91+ "Times" ,
92+ _expand (Expression ("Sin" , x )),
93+ _expand (Expression ("Cos" , y )),
94+ )
7895
79- a = Expression (
80- "Times" ,
81- _expand (Expression ("Sin" , x )),
82- _expand (Expression ("Cos" , y )),
83- )
84-
85- b = Expression (
86- "Times" ,
87- _expand (Expression ("Cos" , x )),
88- _expand (Expression ("Sin" , y )),
89- )
96+ b = Expression (
97+ "Times" ,
98+ _expand (Expression ("Cos" , x )),
99+ _expand (Expression ("Sin" , y )),
100+ )
101+ return _expand (Expression ("Plus" , a , b ))
102+ elif head == Symbol ("Cos" ):
103+ a = Expression (
104+ "Times" ,
105+ _expand (Expression ("Cos" , x )),
106+ _expand (Expression ("Cos" , y )),
107+ )
90108
91- return Expression ("Plus" , a , b )
109+ b = Expression (
110+ "Times" ,
111+ _expand (Expression ("Sin" , x )),
112+ _expand (Expression ("Sin" , y )),
113+ )
92114
93- elif expr .has_form ("Cos" , 1 ):
94- theta = expr .leaves [0 ]
115+ return _expand (Expression ("Plus" , a , - b ))
116+ elif head == Symbol ("Sinh" ):
117+ a = Expression (
118+ "Times" ,
119+ _expand (Expression ("Sinh" , x )),
120+ _expand (Expression ("Cosh" , y )),
121+ )
95122
96- if theta .has_form ("Plus" , 2 , None ):
97- x , y = theta .leaves [0 ], Expression ("Plus" , * theta .leaves [1 :])
123+ b = Expression (
124+ "Times" ,
125+ _expand (Expression ("Cosh" , x )),
126+ _expand (Expression ("Sinh" , y )),
127+ )
98128
99- a = Expression (
100- "Times" ,
101- _expand (Expression ("Cos" , x )),
102- _expand (Expression ("Cos" , y )),
103- )
129+ return _expand (Expression ("Plus" , a , b ))
130+ elif head == Symbol ("Cosh" ):
131+ a = Expression (
132+ "Times" ,
133+ _expand (Expression ("Cosh" , x )),
134+ _expand (Expression ("Cosh" , y )),
135+ )
104136
105- b = Expression (
106- "Times" ,
107- _expand (Expression ("Sin " , x )),
108- _expand (Expression ("Sin " , y )),
109- )
137+ b = Expression (
138+ "Times" ,
139+ _expand (Expression ("Sinh " , x )),
140+ _expand (Expression ("Sinh " , y )),
141+ )
110142
111- return Expression ("Plus" , a , - b )
143+ return _expand (Expression ("Plus" , a , b ))
144+ elif head == Symbol ("Tan" ):
145+ a = _expand (Expression ("Sin" , theta ))
146+ b = Expression (
147+ "Power" , _expand (Expression ("Cos" , theta )), Integer (- 1 )
148+ )
149+ return _expand (Expression ("Times" , a , b ))
150+ elif head == Symbol ("Cot" ):
151+ a = _expand (Expression ("Cos" , theta ))
152+ b = Expression (
153+ "Power" , _expand (Expression ("Sin" , theta )), Integer (- 1 )
154+ )
155+ return _expand (Expression ("Times" , a , b ))
156+ elif head == Symbol ("Tanh" ):
157+ a = _expand (Expression ("Sinh" , theta ))
158+ b = Expression (
159+ "Power" , _expand (Expression ("Cosh" , theta )), Integer (- 1 )
160+ )
161+ return _expand (Expression ("Times" , a , b ))
162+ elif head == Symbol ("Coth" ):
163+ a = _expand (Expression ("Times" , "Cosh" , theta ))
164+ b = Expression (
165+ "Power" , _expand (Expression ("Sinh" , theta )), Integer (- 1 )
166+ )
167+ return _expand (Expression (a , b ))
112168
113169 sub_exprs = []
114170
@@ -128,6 +184,9 @@ def convert_sympy(expr):
128184 leaves = expr .get_leaves ()
129185 if isinstance (expr , Integer ):
130186 return sympy .Integer (expr .get_int_value ())
187+ if target_pat is not None and not isinstance (expr , Number ):
188+ if expr .is_free (target_pat , evaluation ):
189+ return store_sub_expr (expr )
131190 if expr .has_form ("Power" , 2 ):
132191 # sympy won't expand `(a + b) / x` to `a / x + b / x` if denom is False
133192 # if denom is False we store negative powers to prevent this.
@@ -161,7 +220,13 @@ def unconvert_subexprs(expr):
161220 if not sub_expr .is_atom ():
162221 head = _expand (sub_expr .head ) # also expand head
163222 leaves = sub_expr .get_leaves ()
164- leaves = [_expand (leaf ) for leaf in leaves ]
223+ if target_pat :
224+ leaves = [
225+ leaf if leaf .is_free (target_pat , evaluation ) else _expand (leaf )
226+ for leaf in leaves
227+ ]
228+ else :
229+ leaves = [_expand (leaf ) for leaf in leaves ]
165230 sub_exprs [i ] = Expression (head , * leaves )
166231 else :
167232 # thread over Lists etc.
@@ -170,7 +235,15 @@ def unconvert_subexprs(expr):
170235 for head in threaded_heads :
171236 if sub_expr .has_form (head , None ):
172237 leaves = sub_expr .get_leaves ()
173- leaves = [_expand (leaf ) for leaf in leaves ]
238+ if target_pat :
239+ leaves = [
240+ leaf
241+ if leaf .is_free (target_pat , evaluation )
242+ else _expand (leaf )
243+ for leaf in leaves
244+ ]
245+ else :
246+ leaves = [_expand (leaf ) for leaf in leaves ]
174247 sub_exprs [i ] = Expression (head , * leaves )
175248 break
176249
@@ -721,6 +794,8 @@ class Expand(_Expand):
721794 <dt>'Expand[$expr$]'
722795 <dd>expands out positive integer powers and products of sums in $expr$,
723796 as well as trigonometric identities.
797+ <dt>Expand[$expr$, $target$]
798+ <dd>just expands those parts involving $target$.
724799 </dl>
725800
726801 >> Expand[(x + y) ^ 3]
@@ -743,11 +818,17 @@ class Expand(_Expand):
743818 'Expand' expands trigonometric identities
744819 >> Expand[Sin[x + y], Trig -> True]
745820 = Cos[x] Sin[y] + Cos[y] Sin[x]
821+ >> Expand[Tanh[x + y], Trig -> True]
822+ = Cosh[x] Sinh[y] / (Cosh[x] Cosh[y] + Sinh[x] Sinh[y]) + Cosh[y] Sinh[x] / (Cosh[x] Cosh[y] + Sinh[x] Sinh[y])
746823
747824 'Expand' does not change any other expression.
748825 >> Expand[Sin[x (1 + y)]]
749826 = Sin[x (1 + y)]
750827
828+ Using the second argument, the expression only
829+ expands those subexpressions containing $pat$:
830+ >> Expand[(x+a)^2+(y+a)^2+(x+y)(x+a), y]
831+ = a ^ 2 + 2 a y + x (a + x) + y (a + x) + y ^ 2 + (a + x) ^ 2
751832 'Expand' also works in Galois fields
752833 >> Expand[(1 + a)^12, Modulus -> 3]
753834 = 1 + a ^ 3 + a ^ 9 + a ^ 12
@@ -767,11 +848,28 @@ class Expand(_Expand):
767848 #> (y^2)^(1/2)/(2x+2y)//Expand
768849 = Sqrt[y ^ 2] / (2 x + 2 y)
769850
770- ## This caused a program crash!
851+
771852 #> 2(3+2x)^2/(5+x^2+3x)^3 // Expand
772853 = 24 x / (5 + 3 x + x ^ 2) ^ 3 + 8 x ^ 2 / (5 + 3 x + x ^ 2) ^ 3 + 18 / (5 + 3 x + x ^ 2) ^ 3
773854 """
774855
856+ def apply_patt (self , expr , target , evaluation , options ):
857+ "Expand[expr_, target_, OptionsPattern[Expand]]"
858+
859+ if target .get_head_name () in ("System`Rule" , "System`DelayedRule" ):
860+ optname = target .leaves [0 ].get_name ()
861+ options [optname ] = target .leaves [1 ]
862+ target = None
863+
864+ kwargs = self .convert_options (options , evaluation )
865+ if kwargs is None :
866+ return
867+
868+ if target :
869+ kwargs ["pattern" ] = Pattern .create (target )
870+ kwargs ["evaluation" ] = evaluation
871+ return expand (expr , True , False , ** kwargs )
872+
775873 def apply (self , expr , evaluation , options ):
776874 "Expand[expr_, OptionsPattern[Expand]]"
777875
@@ -816,6 +914,8 @@ class ExpandAll(_Expand):
816914 <dl>
817915 <dt>'ExpandAll[$expr$]'
818916 <dd>expands out negative integer powers and products of sums in $expr$.
917+ <dt>'ExpandAll[$expr$, $target$]'
918+ <dd>just expands those parts involving $target$.
819919 </dl>
820920
821921 >> ExpandAll[(a + b) ^ 2 / (c + d)^2]
@@ -825,15 +925,38 @@ class ExpandAll(_Expand):
825925 >> ExpandAll[(a + Sin[x (1 + y)])^2]
826926 = 2 a Sin[x + x y] + a ^ 2 + Sin[x + x y] ^ 2
827927
928+ >> ExpandAll[Sin[(x+y)^2]]
929+ = Sin[x ^ 2 + 2 x y + y ^ 2]
930+
931+ >> ExpandAll[Sin[(x+y)^2], Trig->True]
932+ = -Sin[x ^ 2] Sin[2 x y] Sin[y ^ 2] + Cos[x ^ 2] Cos[2 x y] Sin[y ^ 2] + Cos[x ^ 2] Cos[y ^ 2] Sin[2 x y] + Cos[2 x y] Cos[y ^ 2] Sin[x ^ 2]
933+
828934 'ExpandAll' also expands heads
829935 >> ExpandAll[((1 + x)(1 + y))[x]]
830936 = (1 + x + y + x y)[x]
831937
832938 'ExpandAll' can also work in finite fields
833939 >> ExpandAll[(1 + a) ^ 6 / (x + y)^3, Modulus -> 3]
834940 = (1 + 2 a ^ 3 + a ^ 6) / (x ^ 3 + y ^ 3)
941+
835942 """
836943
944+ def apply_patt (self , expr , target , evaluation , options ):
945+ "ExpandAll[expr_, target_, OptionsPattern[Expand]]"
946+ if target .get_head_name () in ("System`Rule" , "System`DelayedRule" ):
947+ optname = target .leaves [0 ].get_name ()
948+ options [optname ] = target .leaves [1 ]
949+ target = None
950+
951+ kwargs = self .convert_options (options , evaluation )
952+ if kwargs is None :
953+ return
954+
955+ if target :
956+ kwargs ["pattern" ] = Pattern .create (target )
957+ kwargs ["evaluation" ] = evaluation
958+ return expand (expr , numer = True , denom = True , deep = True , ** kwargs )
959+
837960 def apply (self , expr , evaluation , options ):
838961 "ExpandAll[expr_, OptionsPattern[ExpandAll]]"
839962
0 commit comments