4
$\begingroup$

The built-in function Collect, when applied to more than one variable, will group expressions by the first variable, as here:

 testexpr=1 + 2 x y^3 + a y^4 + b y^4 + y^5 + 2 a x^2 y z - 2 b x^2     
 y z +  a^2 x y^2 z - b^2 x y^2 z + a x y^3 z - b x y^3 z;
 Collect[testexpr,{x,y,z},f]

this gives

 f[1] + y^5 f[1] + x^2 y z f[2 a - 2 b] + y^4 f[a + b] + 
 x (y^3 (f[2] + z f[a - b]) + y^2 z f[a^2 - b^2])

But in my experience it is often more useful not to do this grouping, i.e. to expand the bracket after x, to get the result

f[1] + y^5 f[1] + x y^3 f[2] + x^2 y z f[2 a - 2 b] + 
x y^3 z f[a - b] + y^4 f[a + b] + x y^2 z f[a^2 - b^2] 

One example that i have now is that the x,y,z are complicated expressions of a variable and the arguments of f are polynomials in this variable, and i want to integrate each term separately (as Mathematica has trouble with the full thing, but not the parts), so I really need to have each term separate.

This is one example, but I ran into this problem multiple times. As far as I'm aware there's no standard way to do this, so I've tried writing my own, which is this:

myExpand[expr_] := Replace[expr, 
HoldPattern[a_ (x : Plus[_ ..])] :>(a*# & /@ x) , {0,1}]
myCollect[expr_, vars_, funct_ : (#1 #2&)] := 
FixedPoint[myExpand, Collect[expr, vars, fDummy]] //.
{a_ fDummy[x_] :> fDummy[a, x], a_ fDummy[b_, x_] :> fDummy[a b, x]} /. 
fDummy[x_] :> fDummy[1, x] /. fDummy -> funct

Here myExpand expands terms at the first level, and it is applied with FixedPoint to deal with nested brackets. Then the replacement wraps the variables and the coefficient of each term with a dummy function fDummy, the next replacement adds a 1 as the first argument to the term with no variables and the last replacement applies the function funct.

It will give

myCollect[testexpr, {x, y, z}, f]
f[1, 1] + f[x y^3, 2] + f[y^4, a + b] + f[y^5, 1] + 
f[x^2 y z, 2 a - 2 b] + f[x y^2 z, a^2 - b^2] + f[x y^3 z, a - b]

Perhaps it's also useful to have something slightly more general, where each variable is a different argument (as in f[x,y^3,z,a-b] instead of f[x y^3 z,a-b]), but for my purposes this was enough.

I think this works fine, but I was wondering if anyone has a simpler and/or faster solution.

$\endgroup$
  • 1
    $\begingroup$ Why can't we just apply Expand to the expression after collecting? Perhaps it's not general enough, but Expand@Collect[testexpr, {x,y,z}, f] /. a_ f[x_] :> f[a, x] works for your example. $\endgroup$ – march Jun 8 '15 at 20:51
  • 1
    $\begingroup$ What i want is to expand the expression in powers of the given variables, but not expand each of those terms themselves, which is what your code does. For example to pick certain elements in an expression which you know are not going to simplify with each other, expand in those and simplify their coefficients. $\endgroup$ – Jansen Jun 8 '15 at 20:58
  • $\begingroup$ I see! I will think on this then. $\endgroup$ – march Jun 8 '15 at 21:02
2
$\begingroup$

EDIT

The CoefficientRules method given below won't work if one wants to collect by patterns like Log[_] or _Log (although it will work if the stuff inside the Log is explicitly given). In this case, my instinct is that Collect is then the way to go, and the method supplied by the OP will work pretty well as it is.


CoefficientRules method

Variables together

poly = 1 + 2 x y^3 + a y^4 + b y^4 + y^5 + 2 a x^2 y z 
         - 2 b x^2 y z + a^2 x y^2 z - b^2 x y^2 z + a x y^3 z - b x y^3 z;

Total@KeyValueMap[
  f[Times @@ ({x, y, z}^#1), #2] &, Association@CoefficientRules[poly, {x, y, z}]
 ]
f[1, 1] + f[x y^3, 2] + f[y^4, a + b] + f[y^5, 1] 
        + f[x^2 y z, 2 a - 2 b] + f[x y^2 z, a^2 - b^2] + f[x y^3 z, a - b]

Variables apart

Total@KeyValueMap[
  f[Sequence @@ ({x, y, z}^#1), #2] &, Association@CoefficientRules[poly, {x, y, z}]
 ]
f[1, 1, 1, 1] + f[1, y^4, 1, a + b] + f[1, y^5, 1, 1] + f[x, y^2, z, a^2 - b^2] 
              + f[x, y^3, 1, 2] + f[x, y^3, z, a - b] + f[x^2, y, z, 2 a - 2 b]

Non-Association alternative

I realized that there is a cleaner version using @@@ (although a little slower for some reason):

Total[f[Sequence @@ ({x, y, z}^#1), #2] & @@@ CoefficientRules[poly, {x, y, z}]]

Replace Sequence with Times for the "Variables together" form.

| improve this answer | |
$\endgroup$
  • $\begingroup$ That looks interesting, I have to look at it as I'm not familiar with Association. $\endgroup$ – Jansen Jun 8 '15 at 21:00
  • $\begingroup$ @Jansen, Association was introduced in v10, so you won't have it if you are using an earlier version. $\endgroup$ – Virgil Jun 8 '15 at 21:01
  • $\begingroup$ No I have the latest version, I will have a look. $\endgroup$ – Jansen Jun 8 '15 at 21:04
  • $\begingroup$ Thanks, this is close to what I was looking for, there is one problem though. I didn't emphasise it in my question, but i want to be able to collect also compound expressions and patterns, like you can with Collect, for example {Log[_],ArcTanh[_]}. This doesn't work with CoefficientRules. $\endgroup$ – Jansen Jun 9 '15 at 6:42
  • $\begingroup$ @Jansen, you are right (but although CoefficientRules doesn't work for patterns it will work for more complicated terms, if the whole term is explicitly specified). Given this, I suspect Collect is the way to go and the answer you give in your question is as efficient as anything. If I think of something, I'll add it. $\endgroup$ – Virgil Jun 9 '15 at 18:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.