NIntegrate
does each integral separately
This has been observed before:
NIntegrate piecewise vector function, Nested NIntegrate of vector function. It is also clear from the following BenchmarkPlot
:
int[n_] := Block[{shaxis},
shaxis = Table[1.0*i, {i, 1, n}];
NIntegrate[shaxis/(x^3 + 10), {x, 0, Infinity},
Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule"}]];
BenchmarkPlot[int, # &, 2^Range[4, 11], "IncludeFits" -> True,
TimeConstraint -> 20.`]
I would not know why there has been no interest in developing such functionality for NIntegrate
. NDSolve
has it.
Using NDSolve
instead
NDSolve
uses a local estimate of the error to control the step size. It is not going to be as robust as the global adaptive strategy of NIntegrate
, but it can do a pretty good job. On the OP's test example, it does quite well, but those functions are similar and relatively easy to integrate.
We need to remap the infinite interval {x, 0, Infinity}
to a finite one. The substitution x -> t/(1 - t)
pulls it back to {t, 0, 1}
. As it is not as good at estimating error, I had to fiddle with PrecisionGoal
and AccuracyGoal
to get as accurate a solution as possible. This is a drawback to this method. If you set them higher, NDSolveValue
complains and stops integration before reaching the end. Below we'll compare it with the default NDSolve
settings, NIntegrate
timings and the exact solution.
shaxis = Table[1.0*i, {i, 1, 2046}];
ClearAll[df];
df[t_] = shaxis/(x^3 + 10) Dt[x] /. x -> t/(1 - t) /. Dt[t] -> 1 // Simplify;
ndvals = NDSolveValue[
{a'[t] == df[t], a[0] == ConstantArray[0, Length@shaxis]},
a[1], {t, 0, 1},
PrecisionGoal -> 16, AccuracyGoal -> 11]; // AbsoluteTiming
(* {0.349559, Null} *)
ndvals0 = NDSolveValue[
{a'[t] == df[t], a[0] == ConstantArray[0, Length@shaxis]},
a[1], {t, 0, 1}]; // AbsoluteTiming
(* {0.240336, Null} *)
nivals = NIntegrate[
shaxis/(x^3 + 10), {x, 0, Infinity}]; // AbsoluteTiming
(* {7.49675, Null} *)
(* xzczd's solution *)
NIntegrate[shaxis/(x^3 + 10), {x, 0, Infinity},
Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule",
"SymbolicProcessing" -> 0}]; // AbsoluteTiming
(* {2.29383, Null} *)
exact = shaxis Integrate[1/(x^3 + 10), {x, 0, Infinity}];
With the special precision/accuracy settings, the NDSolve
solutions are a little better than the default NIntegrate
ones. (Again, though, the integrands are not numerically difficult.)
ListPlot[Log10@{Abs[(ndvals - exact)/exact], Abs[(nivals - exact)/exact]}]
The default NDSolve
has 8 digits of precision, which is the default goal:
{Norm[(ndvals0 - exact)]/Norm[exact],
Norm[(ndvals - exact)]/Norm[exact],
Norm[(nivals - exact)]/Norm[exact]}
(* {6.57234*10^-8, 1.2117*10^-13, 4.61459*10^-13} *)
Vector version of NIntegrate
We can easily adapt the implementation of a global adaptive strategy in tutorial/NIntegrateIntegrationStrategies
to handle vector integrands. With a little refactoring, we can make take advantage of the built-in vectorization of Mathematica functions. In this implementation, the integrand needs to be Listable
. On the integrand df
above, it uses four cores (of the 4-core/8-virtual-core i7 on my Mac) typical of Mathematica's vectorized functions.
Of course this circumvents all the other nice features of NIntegrate
, but it might be comparable to the Matlab function (of which I'm ignorant).
res = IStrategyGlobalAdaptive[df, {0, 1}, 10^-8]; // AbsoluteTiming
Norm[(res - exact)]/Norm[exact]
(*
{0.952552, Null}
1.28022*10^-15
*)
Code:
{absc, weights, errweights} = NIntegrate`GaussKronrodRuleData[5, MachinePrecision];
ClearAll[IRuleEstimate, IStrategyGlobalAdaptive];
IRuleEstimate[f_, a_, b_] :=
(b - a) {weights, errweights}.Transpose[f[Rescale[absc, {0, 1}, {a, b}]]];
IStrategyGlobalAdaptive[f_, {aArg_, bArg_}, tol_] /;
MatrixQ[f[N@{aArg, bArg}], NumericQ] :=
Module[{integral, error, regions, r1, r2, a = N[aArg], b = N[bArg], c},
{integral, error} = IRuleEstimate[f, a, b];
(* boundaries, integral, error *)
regions = {{a, b, integral, error}};
While[Norm[error] >= tol*Norm[integral],
(* splitting of the region with the largest error *)
{a, b} = regions[[-1, 1 ;; 2]]; c = (a + b)/2;
(* integration of the left region *)
{integral, error} = IRuleEstimate[f, a, c];
r1 = {a, c, integral, error};
(* integration of the right region *)
{integral, error} = IRuleEstimate[f, c, b];
r2 = {c, b, integral, error};
(* sort the regions: the largest error is last *)
regions = Join[Most[regions], {r1, r2}];
regions = SortBy[regions, Norm@*Last];
(* global integral and error vectors *)
{integral, error} = Total[regions[[All, -2 ;;]]];
];
integral
];
Total@Table[NIntegrate[i/(10 + x^3), {x, 0, Infinity}], {i, laxis}]
? $\endgroup$