8

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.

QuantumDot
  • 19,601
  • 7
  • 45
  • 121

2 Answers2

8

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] *)
Greg Hurst
  • 35,921
  • 1
  • 90
  • 136
  • A bit off-topic - what is the . for in the test:

    Which[x-y^2-2. x==0,

    – Doug Kimzey Apr 30 '15 at 13:40
  • @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. – Greg Hurst Apr 30 '15 at 17:49
4

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*)
Ted Ersek
  • 7,124
  • 19
  • 41