7
$\begingroup$

I have programmed and Compileed a complicated numerical function. The function has singularities (i.e. are infinite) at certain input numerical values. I need to catch this problem in the middle of the evaluation and return a more meaningful Message to the user than the default CompiledFunction::cfne.

I can't quite understand the answer to This Question. Would someone help me the following concrete example?

f = Compile[{{x, _Real}, {y, _Real}}, 
 Log[(x - y^2 - 2. x)^2]/(y x^2 - 2 (x + y) - y^2 + 3.)]

For certain values, there are singularities:

f[-196, 14]

CompiledFunction::cfn:
Numerical error encountered at instruction 7; proceeding with uncompiled evaluation

and also

f[1.5, 0]

CompiledFunction::cfne:
Numerical error encountered; proceeding with uncompiled evaluation.

Power::infy: Infinite expression 1/0. encountered.

I would like to trump these default error messages with my own. Is it possible to do this without compromising the speed of the evaluation??

I feel that using a giant If statement such as:

funcForUser::msg = "Singular point.";
funcForUser[x_, y_] := If[x != y^2, f[x, y], Message[funcForUser::msg]; Undefined]

is a very ugly way to solve the problem. And I also don't have the patience to track down all possible conditions that lead to singularities.

$\endgroup$
8
+50
$\begingroup$

How about using "RuntimeErrorHandler":

f = Compile[{{x, _Real}, {y, _Real}}, 
   Log[(x - y^2 - 2. x)^2]/(y x^2 - 2 (x + y) - y^2 + 3.), 
   "RuntimeOptions" -> {"RuntimeErrorHandler" -> Function[Throw[$Failed]]}
];

Catch[Quiet@f[-196, 15]] // AbsoluteTiming
(* {0.000019, 0.0000116843} *)

Catch[Quiet@f[-196, 14]] // AbsoluteTiming
(* {0.000051, $Failed} *)

Edit

We can even have specific messages too:

(* Copying Ted Ersek's messages *)
f::log="The expression f[`1`,`2`] lead to Log[0.0] which evaluates to -\[Infinity].";
f::div="The expression f[`1`,`2`] lead to division by zero which evaluates to ComplexInfinity.";

f = Compile[{{x, _Real}, {y, _Real}}, 
   Log[(x - y^2 - 2. x)^2]/(y x^2 - 2 (x + y) - y^2 + 3.), 
   "RuntimeOptions" -> {"RuntimeErrorHandler" -> Function[{x,y},
      Which[x-y^2-2. x==0,
        Message[f::log,x,y];
        -\[Infinity],
       y x^2-2 (x+y)-y^2+3.==0,
        Message[f::div,x,y];
        ComplexInfinity,
       True,
        Indeterminate
      ]
    ]}
];

f[-196, 14]
CompiledFunction::cfn: Numerical error encountered at instruction 7; proceeding with uncompiled evaluation. >>
f::log: The expression f[-196,14] lead to Log[0.0] which evaluates to -\[Infinity].
(* -\[Infinity] *)
$\endgroup$
  • $\begingroup$ A bit off-topic - what is the . for in the test: Which[x-y^2-2. x==0, $\endgroup$ – Doug Kimzey Apr 30 '15 at 13:40
  • $\begingroup$ @DougKimzey The . is not necessary, I had just copied OP's code. The difference is 2 represents the integer 2 and 2. (or 2.0) represents the float 2.0. It would have been perfectly fine for OP to use 2 there. $\endgroup$ – Chip Hurst Apr 30 '15 at 17:49
4
$\begingroup$

I use \$MessagePrePrint = StandardForm since without that the real number 1.5 is displayed in a message as 1.5`. However, you might have $MessagePrePrint set to something else. Check is used to control what should happen when a built-in message occurs. Quiet prevents the built-in message from being displayed. I made a pure function (i.e. #1,#2,& notation) to make it a bit faster.

$MessagePrePrint=StandardForm;
f::log="The expression f[`1`,`2`] lead to Log[0.0] which evaluates to 
Indeterminate.";
f::div="The expression f[`1`,`2`] lead to division by zero which evaluates to
ComplexInfinity.";
With[{fc=Compile[{{x,_Real},{y,_Real}},Log[(x-y^2-2. x)^2]/(y x^2-2 (x+y)-y^2+3.)]},
f=Quiet[Check[Check[fc[#1,#2],
Message[f::log,#1,#2];Indeterminate,{CompiledFunction::cfn}],
Message[f::div,#1,#2]ComplexInfinity,{CompiledFunction::cfne,Power::infy}],
{CompiledFunction::cfn,CompiledFunction::cfne,Power::infy}]&];

Now we have

f[-196,14]
f:log : The expression f[-196,14] lead to Log[0.0] which evaluates to Indeterminate.
(*Indeterminate*)

and

f[1.5,0] 
f:div : The expression f[1.5,0] lead to division by zero which evaluates to ComplexInfinity.
(*ComplexInfinity*)
$\endgroup$

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.