I am using Mathematica for numerical integrals in optics. By accident, I found out that NIntegrate becomes faster by an order of magnitude for a function I'm integrating, if I take the following steps:
Evaluate[]
the function;Compile
the function;- Define a new function that takes only numerical arguments and applies them to the compiled function.
I haven't been able to reproduce this with other functions; only this one so far. I do know that this integrand is highly oscillatory. Here's my minimal working example:
ClearAll["Global`*"];
ClearSystemCache[];
gaussians[x_] :=
0.1 Sum[
Exp[-34000. ((2 m)*0.00004)^2]*1/((Pi*0.00001^2)^0.25)*
Exp[-(x - (2 m)*0.00004)^2/(2*0.00001^2)],
{m, -200, 200}
];
integrand[x_, kx_] := gaussians[x] Exp[-I*kx*x];
numericIntegrand[x_?NumericQ, kx_?NumericQ] := integrand[x, kx]
compiledIntegrand =
Compile[{x,kx}, integrand[x, kx]];
numericalCompiledIntegrand[x_?NumericQ, kx_?NumericQ] :=
compiledIntegrand[x, kx];
compiledEvaluatedIntegrand =
Compile[{x, kx}, Evaluate[integrand[x, kx]]];
numericalCompiledEvaluatedIntegrand[x_?NumericQ, kx_?NumericQ] :=
compiledEvaluatedIntegrand[x, kx];
integral[f_, kx_] :=
NIntegrate[f[x, kx], {x, -0.008, 0.008}, MaxRecursion -> 15(*,
AccuracyGoal\[Rule]12,PrecisionGoal\[Rule]9*)];
ParallelTable[TimeConstrained[
AbsoluteTiming[integral[function, 150000.]],
60, "Exceeded time limit"
],
{function, {integrand, numericIntegrand, compiledIntegrand,
numericalCompiledIntegrand, compiledEvaluatedIntegrand,
numericalCompiledEvaluatedIntegrand}}
]
And the output:
CompiledFunction::cfsa : Argument x at position 1 should be a machine-size real number.
CompiledFunction::cfsa : Argument x at position 1 should be a machine-size real number.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
NIntegrate::slwcon : Numerical integration converging too slowly; suspect one of the following: singularity, value of the integration is 0, highly oscillatory integrand, or WorkingPrecision too small.
{{55.2211, -3.03556*10^-6 +
2.87568*10^-19 I}, "Exceeded time limit", {53.982, -3.03556*10^-6 \
+ 2.87568*10^-19 I}, "Exceeded time limit", {55.0044, -3.03556*10^-6 \
+ 2.87568*10^-19 I}, {4.56948, -3.03556*10^-6 + 2.87568*10^-19 I}}
As you can see from the table output, only this specific sequence of steps gives the speedup I'm referring to. integrand
takes the integrand as is, numericIntegrand
uses a "numerical" version of the integrand (i.e. one that takes only numerical values), compiledIntegrand
uses a compiled version of the integrand, numericalCompiledIntegrand
uses a numerical version of the compiled integrand, compiledEvaluatedIntegrand
uses a compiled version of the integrand with Evaluate[]
applied before compilation, and finally, numericalCompiledEvaluatedIntegrand
applies the steps I outlined above. All of them take over 50 seconds, except for numericalCompiledEvaluatedIntegrand
, which takes less than 5 seconds. What could be the cause of this?
CompiledFunctionTools`CompilePrint[compiledIntegrand]
you will seeMainEvaluate[ Hold[integrand][ R0, R1]]
which means that the integrand is not correctly compiled withourEvaluate
. This is why you cannot get any speed-up from that. $\endgroup$Needs["CCodeGenerator
"]` before you callCompiledFunctionTools`CompilePrint
. $\endgroup$Needs["CCodeGenerator`"]
, but the markup language messed up the command, so you need to put two backticks before and two after the code). Thanks. $\endgroup$