6
$\begingroup$

Consider the following code:

test[x_] := If[1 < x < 2, Message[Test::message]; x^2, x];
NIntegrate[test[x], {x, 0, 3}]

5.33333333333334

I.e. it gives me the result, suppressing all messages emitted by test. OTOH, this code does emit the messages:

N@Sum[test[x] 1/1000, {x, 0, 3, 1/1000}]

Test::message: -- Message text not found --
Test::message: -- Message text not found --
Test::message: -- Message text not found --
General::stop: Further output of Test::message will be suppressed during this calculation.

5.3338335

I'd really like to receive the messages from the integrand, because in Mathematica 11 they allow to see stack trace. How can I stop NIntegrate from suppressing the messages?

$\endgroup$
  • 1
    $\begingroup$ Do you want to trace your function or NIntegrate? $\endgroup$ – Shadowray Jun 2 '17 at 13:44
  • $\begingroup$ @Shadowray I want to trace my function, and only if it generates a message. $\endgroup$ – Ruslan Jun 2 '17 at 14:00
4
$\begingroup$

I finally found a workaround to the message suppression that still allows the subdivision of the interval of integration $[0,3]$ at $x = 1, 2$. You have to protect the message-generating code sufficiently deep so that the symbolic processing takes care of the singularities. Then inside a ?NumericQ protected function, turn on messages again by resetting $Messages.

ClearAll[test, ftrue];
With[{$m = $Messages},
 ftrue[x_?NumericQ] := Block[{$Messages = $m}, Message[Test::message]; x^2];
 ];
test[x_] := If[1 < x < 2, ftrue[x], x];
NIntegrate[test[x], {x, 0, 3}]

Message::msgl: $MessageList $MessageList not a list; reset to {}.
Test::message: -- Message text not found --
Test::message: -- Message text not found --
Test::message: -- Message text not found --
General::stop: Further output of Test::message will be suppressed during this calculation.

Out[56]= 5.33333

The extra message

There's an extraneous Message::msgl that is the fault of NIntegrate. (It doesn't seem to matter or wasn't detected, since messages are turned off.) It seems easy enough just to ignore it, but if that's unacceptable, you could reset $MessageList yourself, or Quiet the message:

If[! ListQ[$MessageList], $MessageList = {}];  (* OR *)
Quiet[Message[Test::message], Message::msgl];

Using this definition of ftrue, you can see the message generation, even though the messages themselves are not printed:

ftrue[x_?NumericQ] := (
   If[Length@$MessageList < 4, Print[$MessageList]];
   Message[Test::message]; x^2);

NIntegrate[test[x], {x, 0, 3}]

$MessageList
{Message::msgl,Test::message}
{Message::msgl,Test::message,Test::message}

Out[69]= 5.33333

A glimpse at the symbolic processing

It may be of interest to inspect how NIntegrate breaks down the integrand. With the OP's test[], it looks like the following. We get three integration regions

NIntegrate[test[x], {x, 0, 3}, IntegrationMonitor :> ((regions = #) &), MaxRecursion -> 0];
Column[regions /. r_NIntegrate`GeneralRule :> Short[r], Dividers -> All]

With my version of test[], the x^2 in the first region in the table (from 1 to 2) is replaced with ftrue[x].

$\endgroup$
5
$\begingroup$

The answer below is for the first version of the question:

I'd really like to receive the messages from the integrand. How can I stop NIntegrate from suppressing the messages?

There are several reasons for this behavior. Generally speaking, NIntegrate pre-evaluates and localizes the integrands in order to facilitate different symbolic pre-processing and numerics algorithms.

If we redefine the integrand with Piecewise we will get the message once, when PiecewiseNIntegrate does the preprocessing:

In[142]:= ClearAll[Test, test]
test[x_] := Piecewise[{{Message[Test::message]; x^2, 1 < x < 2}}, x];
NIntegrate[test[x], {x, 0, 3}]

During evaluation of In[142]:=
 Test::message: -- Message text not found --

Out[144]= 5.33333

Further, we can prevent the symbolic processing with _?NumericQ and use Print or Echo. (Note that the integral converges too slowly.)

ClearAll[Test, test]
test[x_?NumericQ] := 
  If[1 < x < 2, (Echo[Row[{Test::message, " : ", x}]]; x^2), x];
NIntegrate[test[x], {x, 0, 3}, PrecisionGoal -> 2]
(*
Test::message : 1.08055

Test::message : 1.5

Test::message : 1.91945

... 

5.34841 *)

I am not sure what kind of testing you want to do. You might consider using EvaluationMonitor or IntegrationMonitor instead.

$\endgroup$
  • $\begingroup$ The way with Piecewise defies the purpose of this check: the message is emitted when test[x] is called with a symbolic x. And the way with Echo doesn't allow me to see Stack Trace, unlike messages (new feature of Mathematica 11). $\endgroup$ – Ruslan Jun 2 '17 at 13:23
  • $\begingroup$ Right. As I mentioned in my answer you might be better off using EvaluationMonitor. $\endgroup$ – Anton Antonov Jun 2 '17 at 13:37
3
$\begingroup$

Updated to issue a General::stop message

Maybe the following does what you want?

handler[Hold[Message[Test::message], True]] := With[
    {stop = Flatten@Position[$MessageList, HoldForm[General::stop]]},

    Which[
        FreeQ[$MessageList[[stop-1]], HoldForm[Test::message]],
        CellPrint @ Cell[
            BoxData[Internal`MessageTemplate[Test, "message", "Test message", StandardForm]],
            "Message","MSG"
        ];
        If[Count[$MessageList, HoldForm[Test::message]] > 2,
            CellPrint @ Cell[
                BoxData[Internal`MessageTemplate[General, "stop", StringForm[General::stop, HoldForm[Test::message]], StandardForm]],
                "Message", "MSG"
            ]
        ]
    ]
]

test[x_] := If[1<x<2, Message[Test::message];x^2, x];

Internal`HandlerBlock[{"Message", handler}, NIntegrate[test[x], {x,0,3}]]

Test::message: Test message

5.33333

$\endgroup$
  • $\begingroup$ Are you sure this works? The function is called 11 times between x == 1 and x == 2, but I only get one message. Shouldn't there be 3, followed by a General::stop? $\endgroup$ – Michael E2 Jun 2 '17 at 19:19
  • $\begingroup$ @MichaelE2 Do you have lingering definitions, e.g., something like test[x_?NumericQ] := ..? $\endgroup$ – Carl Woll Jun 2 '17 at 19:46
  • $\begingroup$ Also, some more work is needed to enable General::stop to do its thing. $\endgroup$ – Carl Woll Jun 2 '17 at 19:47
  • $\begingroup$ No, I don't think so. Just did it again with a fresh kernel: i.stack.imgur.com/Y1L14.png $\endgroup$ – Michael E2 Jun 2 '17 at 21:19
  • $\begingroup$ @MichaelE2 test is only called once between 1 and 2. test[x] evaluates to If[1<x<2, Message[Test::message];x^2, x] but then stops because x is not numeric. Then, the NIntegrate piecewise code analyzes the If object and converts it into a Piecewise object (which is when the one message gets generated). Then, NIntegrate uses the Piecewise object instead of test. $\endgroup$ – Carl Woll Jun 2 '17 at 21:29

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.