# Performance of Apart with complex numbers

Consider the following code

randExpr[] :=
Together@Expand[(a - b - c - I RandomInteger[{10^3, 10^4}] d +
3/(a - b - c - d))^6];
test = randExpr[];
AbsoluteTiming[res1 = Apart[test, a];]


On my system (Fedora 22, Mathematica 10.3, i5-3320M) I get

{12.3737, Null}

Now with the following trivial workaround

myApart[expr_, x_] :=
Block[{i},
If[  FreeQ[expr, Complex],
Apart[expr, x],
Apart[expr /. Complex[0, a_] :> i a, x] /. i -> I
]
];
AbsoluteTiming[res2 = myApart[test, a];]


the output becomes

{0.387141, Null}

and of course

res1 === res2

returns True. I would expect that Apart should be intelligent enough to figure out this particular case automatically, instead of wasting computer time.

Do you think that the current is behavior is worth reporting to the support as a bug, or is it yet something one should expect and tolerate?

I would also be interested in other tricks to make Apart work faster when complex numbers are involved.

P.S. The issue was originally discovered by Rolf Mertig, who provided the given example.

• Symbolic functions tend to become slower and slower every release for simple examples. Why? Because more intelligent operations like the one you're proposing are checked for and added. When it comes to these kinds of things, there's always a tradeoff. – Searke Apr 7 '16 at 16:39
• So, if you want to suggest an improvement, feel free. The developers are always interested in seeing examples like this. But as for calling it a bug... I don't know. – Searke Apr 7 '16 at 16:40
• @Searke This has nothing to do with newer versions. This behaviour has been there since the early nineties, I fixed it (and documented it) in the Apart1 function a long long time ago. I don't know if I mentioned this to WRI (danl) or not, but it is so easy to fix yourself. The great thing about the Mathematica kernel is that you can work around problems quite easily. I am not sure about the fixability of the FrontEnd ... – Rolf Mertig Apr 7 '16 at 18:59
• @RolfMertig Oh that's not what I meant. I didn't mean to imply this slowness was caused by some previous improvement. I mean that adding heuristics that capture and more efficiently handle special cases has to be balanced against the fact that checking for that case adds overhead to the function. This leads to an apparent slowdown of these functions across versions for simple cases. Integrate is a good example of this. For simpler cases, it generally appears to be slower than ever. My point is that adding these kinds of things aren't automatic wins. – Searke Apr 8 '16 at 3:05
• @Searke Additional heuristics is fine as long as can turn it on or off via a dedicated option. Actually, for me Apart is rather an example for a function that didn't receive much love since many MMA versions. In particular it would be nice if it could partial fraction multivariate polynomials, like it was implemented here: github.com/F-Feng/APart – vsht Apr 11 '16 at 9:58

You can always inject some code into the definitions of expressions (such as Apart) which handles the special cases you would like it to treat differently. For example

Unprotect[Apart];

Apart[expr_, x_] := Module[{i},
Apart[expr /. Complex[re_, im_] :> (re + i im), x] /. i -> I
] /; Not[FreeQ[expr, Complex[_, _]]]

Protect[Apart];


will modify the in-built Apart function, such that it does exactly what you want. Specifically, after evaluating the above, you find that

AbsoluteTiming[res3 = Apart[test, a];]
(* {0.789089, Null} *)


is now just as fast as your myApart function.

• Be aware that malformed or nonstandard uses of Complex may lead to infinite recursion with this definition, e.g. Apart[a + Complex, x]. It would be more robust to use ! FreeQ[expr, Complex[_, _]] – Mr.Wizard Jul 1 '17 at 11:29
• @Mr.Wizard: Thanks! I should have seen this. I edited my post accordingly. – JEM_Mosig Jul 1 '17 at 20:08