Replacements on certain locations of expression tree

72 views
Skip to first unread message

Francesco Bonazzi

unread,
Sep 5, 2015, 2:35:35 PM9/5/15
to sympy
Hi,

in Mathematica one can access the expression tree by the [[ ]] operator:


In[1]:= e = x*y + z                                                            

Out[1]= x y + z

In[2]:= e[[1]]                                                                  

Out[2]= x y

In[3]:= e[[1, 1]]                                                              

Out[3]= x


Now one could change the x for a q:


In[4]:= e[[1, 1]] = q                                                          

Out[4]= q

In[5]:= e                                                                      

Out[5]= q y + z                                          


This works by replacing a part in the expression tree (position [[1, 1]]), there is no pattern matching.

I was wondering, is there something like that in SymPy?

Maybe, instead of accessing e.args[0].args[0] (Mathematica's arrays are 1-starting), have some

e.<some method>(0, 0)

and

new_e = e.<some other method>(<new value>, 0, 0)

Is there some easy way to avoid writing a traversal function?

Aaron Meurer

unread,
Sep 5, 2015, 3:00:47 PM9/5/15
to sy...@googlegroups.com
I don't think you need traversal. You just need to rebuild the
expression from the bottom up with the remaining args (remember that
expr == expr.func(*expr.args)).

It would probably be useful to have some utilities functions for
dealing with tuples like (1, 0) meaning expr.args[1].args[0].

Aaron Meurer

>
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sympy+un...@googlegroups.com.
> To post to this group, send email to sy...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sympy.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/sympy/8fc479d2-8131-4c01-9ad3-5618c52588e9%40googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.

Francesco Bonazzi

unread,
Sep 10, 2015, 7:44:10 AM9/10/15
to sympy


On Saturday, 5 September 2015 21:00:47 UTC+2, Aaron Meurer wrote:

I don't think you need traversal. You just need to rebuild the
expression from the bottom up with the remaining args (remember that
expr == expr.func(*expr.args)).


That still requires unpacking the expression tree.

An API like expr.args[2] = new_third_arg_value would violate the object's immutability.

Mathematica's expression trees are not immutable. It has some kind of mechanism by which any modification gets notified to the reference counter, which updates all usage instances.
 
It would probably be useful to have some utilities functions for
dealing with tuples like (1, 0) meaning expr.args[1].args[0].

 Maybe expr.args[1, 2] ?

Some extensions like those used by Mathematica?
Cfr: https://reference.wolfram.com/language/tutorial/PartsOfExpressions.html

That is:
  • Range: expr.args[0, :3] ==> picks 1st arg, then returns its first 3 subargs.
  • Groups: expr.args[(1, 3)] ==> picks 2nd and 4th args.

What about if I add such options on this line:

https://github.com/sympy/sympy/blob/d29f329b38cf3722518b14c0e131b0b1f36b9e37/sympy/core/basic.py#L653


Is it reasonable?

Francesco Bonazzi

unread,
Sep 10, 2015, 7:47:57 AM9/10/15
to sympy

I mean, making expr.args return a proxy of expr._args with such indexing options. Or better add a new method?

Aaron Meurer

unread,
Sep 10, 2015, 4:35:04 PM9/10/15
to sy...@googlegroups.com
I would definitely be careful about the performance and memory usage
implications of making args not a tuple.

What you want to do should definitely be possible without modifying the core.

Aaron Meurer

>
> --
> You received this message because you are subscribed to the Google Groups
> "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to sympy+un...@googlegroups.com.
> To post to this group, send email to sy...@googlegroups.com.
> Visit this group at http://groups.google.com/group/sympy.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/sympy/e5936124-ad08-46e4-ae20-e6cac9fda563%40googlegroups.com.

Francesco Bonazzi

unread,
Sep 25, 2015, 1:12:37 PM9/25/15
to sympy
A possible way is this:
https://gist.github.com/Upabjojr/3718fd700f123fa899ee

Could this be useful if inserted to the API of Basic?

Aaron Meurer

unread,
Sep 26, 2015, 6:34:20 PM9/26/15
to sy...@googlegroups.com
What is PartReplace supposed to do? For me it just returns the replacement.

Aaron Meurer
> https://groups.google.com/d/msgid/sympy/21e5ff28-42f6-4e28-b568-fce93be51c02%40googlegroups.com.

Francesco Bonazzi

unread,
Sep 27, 2015, 5:39:46 PM9/27/15
to sympy


On Sunday, 27 September 2015 00:34:20 UTC+2, Aaron Meurer wrote:
What is PartReplace supposed to do? For me it just returns the replacement.


I updated the gist to make it act as a replacement:

 
In [14]: PartReplace(x*y+z, w, 1,1)
Out[14]: wx + z


This should basically act to replace element at position .args[1].args[1] with w. Of couse I avoided using the syntax expr.args[1].args[1] = w to comply with SymPy's immutability of objects.

In [15]: (x*y+z).args[1].args[1]
Out[15]: y

This gets element at (1,1), or the equivalent form in my gist:

In [18]: Part((x*y+z), 1, 1)
Out[18]: y

I think that Basic could be equipped with these two methods.



Francesco Bonazzi

unread,
Sep 27, 2015, 5:45:14 PM9/27/15
to sympy
It may also support slices, as in:

In [20]: Part(x*y+w*z, slice(None, None, None), 1)
Out[20]: y + z

In [21]: PartReplace(x*y + w*z, o, slice(None, None, None), 1)
Out[21]: ow + ox


Which would be roughly something equivalent to the intended usage of this wrong python expression: expr.args[:].args[1] = o
Reply all
Reply to author
Forward
0 new messages