5

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.

J. M.'s missing motivation
  • 124,525
  • 11
  • 401
  • 574
vsht
  • 3,517
  • 13
  • 23
  • 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 07 '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 07 '16 at 16:40
  • 1
    @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 07 '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 08 '16 at 03:05
  • 1
    @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: https://github.com/F-Feng/APart – vsht Apr 11 '16 at 09:58
  • @vhst Fine for you and other smart people. There are a huge majority using Apart that would find legacy code running more slowly and wouldn't understand. – Searke Apr 11 '16 at 14:24
  • @vhst I would forward any suggestions you have like this to support@wolfram.com and let them know it's a suggestion on how to improve Apart. – Searke Apr 11 '16 at 14:25
  • @Searke Well, by posting this question here I sort of hoped that someone from the developers around would comment on the original issue. Usually I prefer to get some feedback here before contacting the support, since often the behavior that looks like a bug turns out to be expected, after a knowledgeable person explains it. If no one answers in the next couple of days, I'll write to the support. – vsht Apr 11 '16 at 15:14
  • @vhst If you file this as a bug, you'll probably get that reaction you fear. If you write this as a suggestion they will forward it to the developers. – Searke Apr 11 '16 at 15:34

1 Answers1

1

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.

JEM_Mosig
  • 3,003
  • 15
  • 28
  • 3
    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 01 '17 at 11:29
  • @Mr.Wizard: Thanks! I should have seen this. I edited my post accordingly. – JEM_Mosig Jul 01 '17 at 20:08