Suppose I have a very large expression, myEq[x1,x2,x3,x4] in four variables, x1, x2, x3, and x4. (The general problem could of course use any number of variables.) Some (constant) terms do not depend on any variables. Some of the terms depend solely on x1, others solely on x2, ... some on both x1 and x2, others on both x1 and x3, .... and finally some on all four x1 and x2 and x3 and x4. There are, then, $2^4 = 16$ classes of terms based on their minimum variables.

I'd like to perform a four-variable (symbolic) integration of myEq, so I'd like to simplify the process by separating out all terms that depend just on x1 and perform that integration over x1, and likewise for all the terms.

If the terms are all polynomial in the variables, then Collect might be useful. As far as I can see, though, if I use Collect[myEq, x1] (say) I still get terms that involve joint terms, involving, say x1 and x2 as well as x1 and x3 and x4. Moreover, I don't see how Collect is helpful for my problem of non-polynomial terms.

In short, what is the simplest way to take a complicated non-polynomial expression (e.g., trigonometric expression) and break it into parts that depend solely upon each variable and each conjunction of variables?

Minimal example:

myEq[x1,x2,x3,x4] = 5 + Cos[x1] + Sin[x1/2] + Tan[x2] + Cot[x1 x2] + Sin[x1 x2 x3] - Cos[x3] - Tan[x1/x3] + Sin[x2 + x3/x4] - Tan[x2 - x4] + Cot[x1 + x2 + x3 + x4] - Sin[x1 - x2 - x3/x4]

Break this into $2^4$ terms based on the minimum variables.

If there are $n$ variables, then break the expression into $2^n$ such terms.

  • 2
    $\begingroup$ maybe GroupBy[List @@ myEq[x1, x2, x3, x4], Variables[Level[#, {-1}]] &, Total]? $\endgroup$
    – kglr
    Jul 1 '21 at 11:02
  • $\begingroup$ Very nice... especially because it is short and yields sum of terms that depend upon the selected variables. $\endgroup$ Jul 1 '21 at 15:12

You can do the following:

group[expr_Plus] := Values@Reverse@KeySortBy[{Total@# &, # &}]@GroupBy[List @@ expr, Boole@Through[(FreeQ /@ {x4, x3, x2, x1})@#] &]

group[myEq[x1, x2, x3, x4]]
(* {{5}, {Cos[x1], Sin[x1/2]}, {Tan[x2]}, {-Cos[x3]}, {Cot[x1 x2]}, {-Tan[x1/x3]}, {-Tan[x2 - x4]}, {Sin[x1 x2 x3]}, {Sin[x2 + x3/x4]}, {Cot[x1 + x2 + x3 + x4], -Sin[x1 - x2 - x3/x4]}} *)

Essentially, we apply FreeQ to each variable/part combination to check whether that part depends on that variable. Doing this for each variable, we get a four long list of True/False for each part, which can be used for grouping. The rest is just some trickery to get the sorting right (if that's even needed).

  • $\begingroup$ Oh how very very elegant. Perfect. ($\checkmark$) $\endgroup$ Jul 1 '21 at 14:58
groupByVariables = KeySort @ GroupBy[List @@ #, Variables @ Level[#, {-1}] &, Total] &;

groups = groupByVariables @ myEq[x1, x2, x3, x4]
<|{} -> 5,
 {x1} -> Cos[x1] + Sin[x1/2], 
 {x2} -> Tan[x2], 
 {x3} -> -Cos[x3], 
 {x1, x2} -> Cot[x1 x2], 
 {x1, x3} -> -Tan[x1/x3],
 {x2, x4} -> -Tan[x2 - x4], 
 {x1, x2, x3} -> Sin[x1 x2 x3], 
 {x2, x3, x4} -> Sin[x2 + x3/x4], 
 {x1, x2, x3, x4} -> Cot[x1 + x2 + x3 + x4] - Sin[x1 - x2 - x3/x4]|> 
Graph[Join[Keys @ groups, Values @ groups], 
 KeyValueMap[DirectedEdge] @ groups, 
 ImageSize -> Large, 
 VertexCoordinates ->  Join @@ 
    (Thread[{#, Rescale @ Range @ Length @ Keys @ groups}]& /@ {1, 2}), 
 VertexLabels -> {v_ :> Placed["Name", If[MemberQ[Keys @ groups, v], Before, After]]}] 

enter image description here

  • $\begingroup$ Also very valuable and elegant (+1). (I'd expect no less...) $\endgroup$ Jul 1 '21 at 15:44

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.