0
$\begingroup$

Something like this, I define an integrand (really big and hairy, simple below)

integrand = z (Exp[-x^2] + y - Cos[y]);
intfunc[z_] := NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}];

intfunc[7.]

NIntegrate::inumrexpr: Expression {2 z,-2 z,0} derived from integrand 2 z (E^-x^2+y-Cos[y]) has evaluated to non-numerical values for all sampling points in the region with boundaries {{0.00350209,4.9965},{-9.29258,999.293}}. >>

NIntegrate::mtdfb: Numerical integration with LevinRule failed. The integration continues with Method -> MultiDimensionalRule. >>

NIntegrate::inumr: The integrand z (E^-x^2+y-Cos[y]) has evaluated to non-numerical values for all sampling points in the region with boundaries {{0,5},{-10,1000}}. >>

How can I do this without typing

intfunc[z_] := 
  NIntegrate[z (Exp[-x^2] + y - Cos[y]), {x, -5, 5}, {y, -10, 1000}];

I'm aware that I could define the integrand as a function, via

ClearAll[integrand, intfunc]
integrand[z_?NumericQ, x_?NumericQ, y_?NumericQ] := 
  z (Exp[-x^2] + y);
intfunc[z_] := 
  NIntegrate[integrand[z, x, y], {x, -5, 5}, {y, -10, 1000}];

and then it will work,

intfunc[7.]
(* 3.5009*10^7 *)

But in the actual integrand I am doing, it seems much slower. Possibly because the extra time to perform the test ?NumericQ? Is that the only way to do this?

$\endgroup$
  • 1
    $\begingroup$ Does this speed things up? intfunc[z0_] := Block[{z = z0}, NIntegrate[ ... ]] $\endgroup$ – march Jan 18 '16 at 17:00
1
$\begingroup$

integrand is a function and functions should be defined and called with explicit arguments. However, there is no need to use PatternTest with its arguments.

integrand[x_, y_, z_] = z (Exp[-x^2] + y - Cos[y]);

Since intfunc employs a numeric technique (NIntegrate) it should be defined with a PatternTest

intfunc[z_?NumericQ] := 
  NIntegrate[integrand[x, y, z], {x, -5, 5}, {y, -10, 1000}];

intfunc[7.]

(*  3.5009*10^7  *)
$\endgroup$
1
$\begingroup$

Sometimes I do it this way:

Block[{NIntegrate, x, y, z}, 
  intfunc[z_] = NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}];
  ];

intfunc[7.]
(*  3.5009*10^7  *)

It's not a great general programmatic way to go, but in the middle of solving a problem, in which I have constructed an expression integrand, this is to me an easy way. Blocking x, y, and z should be unnecessary (or else integrand could not depend on them), but sometimes I'm over-cautious.

Update: Explanation

The problem has three aspects: (1) to get the code for integrand to evaluate (2) before the pattern named z is evaluated (3) without NIntegrate evaluating, all during the evaluation of Set or SetDelayed. I'll say something about the optional ?NumericQ at the end.

There are various ways to address this. One way is to go around the problem entirely and use functions that have all parameters as arguments. This is a better way to write a program. This is the workaround the OP points out in the question (and repeated by Bob Hanlon).

But there are ways to address the problem directly. Block, as in

Block[{NIntegrate}, code]

blocks the evaluation of NIntegrate during the execution of code. Basically, the symbol is temporarily cleared of all its values and attributes. So when the code

intfunc[z_] = NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}];

is evaluated, all the arguments of NIntegrate are evaluated, and the definition becomes

intfunc[z_] = NIntegrate[<fn expr in x,y,z>, {x, -5, 5}, {y, -10, 1000}];

where <fn expr in x,y,z> represents the value of integrand; and that is what intfunc[z_] is set to be equal to.

Another solution is substitution, which I skipped because I thought someone had already suggested it; e.g.,

intfunc[z0_] := NIntegrate[integrand /. z -> z0, {x, -5, 5}, {y, -10, 1000}];

The overhead from ?NumericQ is negligible. It could be quite desirable to use intfunc[z0_?NumericQ]... in the definitions above, especially if intfunc[z] is to be passed to another solver.

Why doesn't this work, with or without NumericQ?

intfunc[z_] := NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}];

Inspecting the DownValues of intfunc will help the explanation:

DownValues[intfunc]
(*  {HoldPattern[intfunc[z_]] :> NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}]}  *)

SetDelayed created a rewrite rule that replaces all instances of the symbol z in the literal, unevaluated expression NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}] by the argument to intfunc. There are no instances of z in that code. So both of these calls,

intfunc[7.]
intfunc[10.]

result in the same thing

NIntegrate[integrand, {x, -5, 5}, {y, -10, 1000}]

which is then evaluated. At this point integrand is evaluated by NIntegrate and the symbolic z shows up. In @march's suggestion

intfunc[z0_] := Block[{z = z0}, NIntegrate[ ... ]]

the value of z would be equal to the value of the argument z0; if numeric, the evaluation of NIntegrate would proceed with no other difficulties except those posed by a pesky integrand.

As a final remark, the OP's actual expression for integrand is certainly pesky. IMO the integrand itself is the only reason that NIntegrate is slow.

$\endgroup$
  • $\begingroup$ You are right that you only have to put NIntegrate in the Block, but I still don't understand how or why this works. It does work though, without the pattern testing overhead of my original method. Thanks for your attention. $\endgroup$ – Jason B. Jan 19 '16 at 14:24
  • $\begingroup$ @JasonB I tried to explain in my update...HTH $\endgroup$ – Michael E2 Jan 19 '16 at 16:26

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.