5
$\begingroup$

How do I manually escape out of a compiled function without forcing an error and compromising its speed?

Here is an example:

f::zero = "x is zero.";

f = Compile[{{x, _Complex}}, If[x == 0, Return[1/x], x^2], 
"RuntimeOptions" -> {"RuntimeErrorHandler" -> 
  Function[Message[f::zero]; Throw[$Failed]]}];

As you can see, I want f to throw an error when x=0, but in order to do that, I needed to artificially think there actually was an error by returning 1/x. Here is the usage:

Catch[f[0]]

CompiledFunction::cfne : Numeriacal Error encountered; proceeding with uncompiled evaluation. >>
f::zero : x is zero

  1. How do I prevent the kernel from proceeding with uncompiled evaluation of the artificial 1/0 like it says (which I believe compromises speed).

  2. How do I escape out of a CompiledFunction without mimicking an error?

$\endgroup$
5
  • 2
    $\begingroup$ You could use a wrapper function that calls the compiled function and that catches numeric error codes, for instance in this case negative numbers and acts on them. $\endgroup$ Commented Nov 29, 2014 at 13:51
  • $\begingroup$ The uncompiled evaluation on returning an infinite or not-a-number value seems to be hard-wired in the VM. I don't think it's possible to avoid that except as described by Sjoerd. $\endgroup$ Commented Nov 29, 2014 at 14:37
  • $\begingroup$ Ok, it looks like @SjoerdC.deVries 's method works and doesn't compromise the speed too much (at least in the cases I'm working on) $\endgroup$
    – QuantumDot
    Commented Nov 29, 2014 at 17:54
  • $\begingroup$ @SjoerdC.deVries would you kindly turn your comment into an answer. I'll accept and this question will go off the unanswered list. $\endgroup$
    – QuantumDot
    Commented Nov 19, 2015 at 10:30
  • $\begingroup$ @QuantumDot OK, done $\endgroup$ Commented Nov 19, 2015 at 22:30

2 Answers 2

4
$\begingroup$
f::zero = "x is zero.";

f = Compile[{{x, _Complex}}, 
   If[x == 0, Throw[Message[f::zero]; $Failed]; Return[1/x], x^2]];

Catch[f[0]]

Out

Or

f = With[{cf = Compile[{{x, _Complex}}, If[x == 0, Throw[Message[f::zero]; $Failed], x^2]]}, 
     Catch[cf[#]] &];

f[0]

Out

In the case of x == 0 there will be one (thanks to the comment by Oleksandr) MainEvaluate call, but it shouldn't have a more significant impact on the speed than using a wrapper function.

$\endgroup$
4
  • $\begingroup$ Why not return $Failed, rather than throwing it, so the calling function doesn't have to be wrapped in Catch to have good behavior? $\endgroup$
    – m_goldberg
    Commented Nov 20, 2015 at 0:42
  • $\begingroup$ @m_goldberg Because of type inconsistency. $\endgroup$
    – Karsten7
    Commented Nov 20, 2015 at 0:46
  • 2
    $\begingroup$ This is an excellent solution, especially if you're going to have to postprocess the return value anyway. Incidentally, the two calls out to the top level can be made into one by moving Message inside Throw, although I'm not sure that this really matters in practice. $\endgroup$ Commented Nov 20, 2015 at 1:03
  • $\begingroup$ @OleksandrR. Although I couldn't find a measurable speed difference, I do prefer having only one MainEvaluate. Thanks. $\endgroup$
    – Karsten7
    Commented Nov 20, 2015 at 3:16
4
$\begingroup$
ClearAll[f, fc]
f::zero = "x is zero.";

(* compiled part *)
fc =
  Compile[{{x, _Complex}},
   (* First part of the code; can be anything 
      .
      .
   *)
   If[x == 0, (* Error test*)
    {0, 0}, (* Error response, first part is status, second part is dummy *)
    {1, x^2} (* Normal response, first part is status, second part actual result *)
   ]
];

(* wrapper part *)
f[x_] := With[{res = fc[x]}, 
  If[Chop[res[[1]]] == 0, (* check status *)
    Message[f::zero]; $Failed, (* error *)
    res[[2]] (* normal result *)
  ]]

f[0]

Mathematica graphics

f[1]
(* 1. + 0. I *)

or with MichaelE2's suggestion:

ClearAll[f, fc]
f::zero = "x is zero.";

fc =
  Compile[{{x, _Complex}},
   (* First part of the code *)
   If[x == 0, (* Error test*)
    {},(* Error response, empty list *)
    {x^2} (* Normal response, list with result *)
    ]
   ];
f[x_] := With[{res = fc[x]}, If[res == {}, Message[f::zero]; $Failed, First@res]]
$\endgroup$
10
  • $\begingroup$ I might consider indicating the error by returning an empty array and a normal result in extra set of braces: E.g., If[x == 0, {}, {x^2}]. The empty array needs to be of the same rank as the other result. (Just an alternative for folks to choose from.) $\endgroup$
    – Michael E2
    Commented Nov 19, 2015 at 22:41
  • $\begingroup$ @MichaelE2 Good idea. Updated the answer. $\endgroup$ Commented Nov 19, 2015 at 22:58
  • $\begingroup$ Catch and Throw are compilable. Therefore this answer could easily be modified to have an early escape. $\endgroup$
    – Karsten7
    Commented Nov 20, 2015 at 2:51
  • $\begingroup$ @MichaelE2 There will be an extra ArrayReshape when using an empty array. Shouldn't be significant for the speed, though. $\endgroup$
    – Karsten7
    Commented Nov 20, 2015 at 3:02
  • 2
    $\begingroup$ @MichaelE2 the other great advantage of using Throw, and which I think would outweigh any performance differences, is that you can put in it whatever you want--any values generated inside the compiled code; arbitrary top-level code; diagnostic messages... it is a lot more useful than just a numeric flag. $\endgroup$ Commented Nov 20, 2015 at 11:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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