On Friday, August 17, 2012 5:18:49 PM UTC+3, smichr wrote:
>
> There is an rcollect function that you might look at. 
>

Thanks, I had missed this. However, I tried it now and unless I'm doing 
something wrong, it's not being recursive in the way I meant:

import sympy as sy
s = sy.sympify("a*b*c*d + b*c*d + c*d + d")
sy.rcollect(s, "d", "c", "b", "a")

gives

d*(a*b*c + b*c + c + 1)

whereas I'd like to get, and my code (symutil.recursive_collect("a*b*c*d + 
b*c*d + c*d + d")) produces

d*(1 + c*(1 + b*(1 + a)))

(There was a mistake in my original message where I said "a * b * c * (1 + 
d) ", which obviously is not equivalent with the original. That was quickly 
written by hand out of laziness, and well, I got it wrong. The new, correct 
expression is from actual program output.)


Looking at the source confirms that these two ideas of recursive collection 
are different:

- SymPy's current rcollect() works by processing anything that matches 
is_Add inside the expression tree. Hence, it is recursive in the sense that 
a sum operation at any level in the expression tree will be collected 
locally. Algorithmically, rcollect() uses a depth-first approach.

- My code works by collecting at the top level *first*, and only then 
recurses into the args of the collected expr (doing the same top-level 
collection for them, and recursing, etc.). Atoms are passed through as-is. 
At each level, the current operation is finally rewritten using the 
processed args. This extracts the nested multiplications in the example 
above, because a successful collect() will produce a Mul. Algorithmically, 
recursive_collect() uses a breadth-first approach.


A further difference is that recursive_collect() comes with an (optional) 
automatic symbol list generation mechanism. If no "syms" are given by the 
user, the "syms" list is automatically generated at each level in a way 
such that the operation count of the subexpression is likely to be 
minimized when it is collected.

E.g. when given the expanded expression (please excuse the symbol naming; 
parts of this were automatically generated)

-A*phi_x*u_x - G*phi_y*u_y - __Ux__1__*__Ux__1___x*phi*rho*u_x - 
__Ux__1__*__Uy__1___x*phi*rho*u_y - __Ux__1___y*__Uy__1__*phi*rho*u_x - 
__Uy__1__*__Uy__1___y*phi*rho*u_y - 2*__Ux__1__*__Uy__1__*phi*rho*u_xy - 
phi*rho*u_xx*__Ux__1__**2 - phi*rho*u_yy*__Uy__1__**2

my code using the automatic symbol list produces

phi*rho*(__Ux__1__*(-__Ux__1___x*u_x - __Uy__1___x*u_y - 2*__Uy__1__*u_xy) 
+ __Uy__1__*(-__Ux__1___y*u_x - __Uy__1___y*u_y) - u_xx*__Ux__1__**2 - 
u_yy*__Uy__1__**2) - A*phi_x*u_x - G*phi_y*u_y

which is (by an eyeball metric) at least near optimal regarding operation 
count (at least when collecting is the only allowed simplification).


In conclusion, rcollect() and my suggested code do different things, so I 
would still like to request consideration for whether the new functionality 
would be useful in standard SymPy.

Why I personally think this would be a useful feature, is that the user may 
only want to collect an expression to produce a reduction in operation 
count, without being interested in which symbols should make up syms/vars 
and in which order. Also, a global "optimal" list of syms/vars for this 
purpose may not even exist: different subexpressions may be more 
advantageous to collect using a different ordering of the symbols. The 
automatic syms generation takes care of this. It is not intended to replace 
manually provided syms/vars, but to complement it.


And finally, about rcollect():

- Is there a reason why the call syntax differs from collect()? collect() 
takes in a list of syms, while rcollect() uses Python's *args mechanism for 
the same (and calls them "vars", instead of "syms" like collect()).

- Suggestion for documentation: rcollect() could be mentioned in the 
documentation for collect() to make it easier to find. At least I find it a 
bit confusing that many of the other simplifiers come with a "deep" flag, 
but for collect() the recursive version is a separate function. (Of course, 
technically there is the distinction of descending into function arguments 
vs. recursing into sums, so the difference may be justified.)

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To view this discussion on the web visit 
https://groups.google.com/d/msg/sympy/-/GsXjNHijbrsJ.
To post to this group, send email to sympy@googlegroups.com.
To unsubscribe from this group, send email to 
sympy+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/sympy?hl=en.

Reply via email to